auto import from //depot/cupcake/@135843
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..0b02fdb
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,10 @@
+# the following test is made to detect that we were called
+# through the 'm' or 'mm' build commands. if not, we use the
+# standard QEMU Makefile
+#
+ifeq ($(DEFAULT_GOAL),droid)
+    LOCAL_PATH:= $(call my-dir)
+    include $(LOCAL_PATH)/Makefile.android
+else
+    include Makefile.qemu
+endif
diff --git a/CHANGES.TXT b/CHANGES.TXT
new file mode 100644
index 0000000..0f362b7
--- /dev/null
+++ b/CHANGES.TXT
@@ -0,0 +1,670 @@
+Android Emulator changes:
+=========================
+
+Versions:
+
+   1.0 => SDK M3 release
+   1.1 => SDK M5 release
+   1.2 => Internal release (build 72264)
+   1.3 => Internal release (build 77780)
+   1.4 => Internal release (build 84853)
+   1.5 => SDK 0.9_r1
+   1.6 => SDK 1.0_r1
+   1.7 => SDK 1.0_r2
+   1.8 => SDK 1.1
+   1.9 => (unreleased, planned, named likely to change)
+
+==============================================================================
+Changes between 1.8 and 1.9
+
+IMPORTANT CHANGES:
+
+- Many features have been integrated from upstream QEMU sources, including
+  the new TCG code generator used by the ARM translator. This should result
+  in slightly faster execution speed on all supported platforms.
+
+- The emulator now requires that you specify a virtual device name when
+  starting the emulator, prefixed with the '@' sign. For example, to start
+  the 'foo' virtual device, type:
+
+      emulator @foo
+
+  Each AVD (Android Virtual Device) corresponds to a directory used to store
+  mutable disk images, an optional system image/kernel/sdcard, plus some
+  configuration file(s).
+
+  The command-line tool 'android' that comes with the SDK can be used to
+  create/list/remove virtual devices on your system.
+
+  Note that the '@<name>' form is a convenience shortcut for '-avd <name>'.
+  It is thus possible to place options after the AVD name on your command
+  line, as in:
+
+      emulator @foo -verbose -shell
+
+  Finally, when building the Android platform source tree, an AVD name is not
+  required and 'emulator' will start a new emulator instance exactly as
+  previously.
+
+- A new option '-sysdir <dir>' has been introduced, the interpretation of
+  the '-system' option has changed, and '-image <file>' should now be
+  considered obsolete. In more details:
+
+  * you should now use '-sysdir <dir>' instead of '-system <dir>' to specify
+    the directory where system images will be searched by the emulator
+    on startup.
+
+  * you should now use '-system <file>' to indicate which system.img partition
+    image to use at startup.
+
+  * you should not use '-system <dir>' or '-image <path>' anymore. However,
+    these options are still supported but will print a warning to remind you
+    to change your scripts/habits.
+
+  The change was done to reduce confusion as to what these options provide.
+
+- Options '-noaudio', '-nojni', '-noskin' and 'nocache' are deprecated.
+  You should use '-no-audio', '-no-jni', '-no-skin' and '-no-cache' instead.
+
+- Option 'initdata' is deprecated, you should use '-init-data' instead.
+
+- Hardware emulation is now limited to the corresponding Android Virtual
+  Device's configuration. This means it is now possible to not emulate
+  a touch-screen, trackball, dpad, keyboard, modem, etc...
+
+  Note that in the case of the Android build system, all hardware properties
+  are enabled by default, so this only affects "normal" virtual devices
+  created with the 'android' tool.
+
+- The emulator now supports capturing network packets to a file.
+  You can either use the new -tcpdump <file> command-line option, or use
+  the new console 'network capture start <file>' command (then use
+  'network capture stop' to stop it).
+
+  This captures all ethernet packets on the virtual LAN, so this includes
+  ARP, UDP, TCP, etc... The file is in libpcap format and can be opened with
+  external tools like WireShark for analysis.
+
+OTHER:
+
+- The file in ~/.android/default.keyset was ignored, unless you used
+  '-keyset default' explicitely. It is now loaded automatically when
+  available.
+
+- Environment variable ANDROID_SDK_ROOT can be used to specifiy the location
+  of the SDK installation path.
+
+- Environment variable ANDROID_SDK_HOME can be used to specify the location
+  of the '.android' data directory.
+
+- A new console command 'avd name' can be used to query the name of the
+  virtual device running in the emulator. Note that it will be '<build>'
+  if you run from the Android build system.
+
+  Also, the emulator's window title also displays the AVD name now.
+
+- The option '-memory <size>' has been added. <memory> must be an integer
+  specifying the amount of physical RAM in the emulated device in megabytes.
+  The default value is 96.
+
+- The '-skindir <path>' option now requires that you specify a '-skin <name>'
+  option as well.
+
+- Better handling of Audio on Linux for the EsounD and Alsa backends
+
+- Fullscreen toggle should now work on Windows and OS X. On Linux, the
+  toggle will not switch the display resolution anymore (which resulted
+  in distorted images).
+
+==============================================================================
+Changes between 1.6 and 1.7
+
+IMPORTANT BUG FIXES:
+
+- Properly create ~/.android directory when needed.
+
+- Do not leave temporary files in Android app-specific directory on Win32
+
+- Support for HTTP/HTTPS proxies has been considerably improved and should now
+  "just work" with a lot more HTTP proxies. In case of problem, use the
+  -debug-proxy option to dump debugging data to stderr.
+
+OTHER:
+
+- Trackball emulation has changed. First, the awkward "Control-T" keybinding
+  is gone. Instead, you can now:
+
+     - press 'Delete' to show the trackball and have it disappear as soon
+       as your release the key.
+
+     - press 'F6' to perform a persistent trackball mode toggle.
+
+  Also, trackball emulation is fixed in rotated/landscape mode now.
+
+- New option '-nand-limits <limits>' allows you to send a signal to a remote
+  process when a read or write threshold on flash storage is reached. This is
+  only useful for hardcore Android system hackers.
+
+- Fix emulator build on recent Cygwin releases (the -mno-cygwin headers do not
+  tolerate the _GNU_SOURCE macro definition anymore)
+
+- Fix Win32 emulator to support SD Card images larger than 2 GiB
+
+- The non-Android build system has been completely rewritten to allow building
+  the emulator on Linux x86_64. Also, there is now a single Makefile that
+  drives the build in both Android and non-Android modes.
+
+- '-qemu <other-options>' works again
+
+==============================================================================
+Changes between 1.5 and 1.6
+
+IMPORTANT CHANGES:
+
+- Emulator now saves the user image in <android>/SDK1.0/
+
+OTHER:
+
+- Get rid of EsounD-related freezes on Linux (again)
+
+- Fix the documentation in -help-audio. '-audio list' doesn't work, one
+  needs to call -help-audio-out and -help-audio-in to get the list of valid
+  audio backends
+
+- Fix scrollwheel Dpad emulation in rotated mode. before that, using the
+  scroll-wheel would always generated Dpad Up/Down events, even when in
+  landscape mode.
+
+- Re-enable CPU fault emulation in case of unaligned data access. this was
+  previously disabled because it crashed the emulated kernel in previous
+  releases.
+
+- The emulator no longer prints an obscure warning when it doesn't find
+  the emulator.cfg configuration file in ~/.android.
+
+  'broken configuration file doesn't have a 'window' element'
+
+- Removed a bunch of obsolete options (e.g. -console, -adb-port, etc...)
+
+- Setting the network speed through the console or the -netspeed option will
+  properly modify the connectivity icon on the device.
+
+- Setting the GSM voice registration state to 'roaming' in the console will
+  properly modify the voice icon on the device
+
+==============================================================================
+Changes between 1.4 and 1.5
+
+IMPORTANT BUG FIXES:
+
+- Fix spurious discards of SMS messages when using two emulators.
+
+OTHER:
+
+- Get rid of EsounD-related freezes on Linux (again)
+
+- Fix the documentation in -help-audio. '-audio list' doesn't work; one
+  needs to call -help-audio-out and -help-audio-in to get the list of valid
+  audio backends
+
+- Fix scrollwheel Dpad emulation in rotated mode. before that, using the
+  scroll-wheel would always generated Dpad Up/Down events, even when in
+  landscape mode.
+
+- Re-enable CPU fault emulation in case of unaligned data access. This was
+  previously disabled because it crashed the emulated kernel in previous
+  releases.
+
+==============================================================================
+Changes between 1.3 and 1.4
+
+IMPORTANT BUG FIXES:
+
+- fix for audio-related Linux startup freezes when using the 'esd' and 'alsa'
+  backends
+
+- the number of audio buffers in the Windows backend has been incremented.
+  this gets rid of audio chopiness issues on Vista (and sometimes on XP too)
+
+NEW FEATURES:
+
+NEW CONSOLE COMMANDS:
+
+- new 'geo fix <lontitude> <latitude> [<altitude>]' command allows you to
+  send a simple GPS fix to the emulated system, without the headaches of
+  NMEA 1083 formatting.
+
+OTHER BUG FIXES:
+
+- fixed the -audio, -audio-in and -audio-out options (the <backend> values
+  were sometimes ignored)
+
+REGRESSIONS:
+
+OTHER:
+
+- the transitional '-qemud' option introduced in 1.3 is now gone. its
+  behaviour is now the default.
+
+- use the new '-old-system' option if you need to use a 1.4+ emulator binary
+  with older system images. if you don't use it, GSM and GPS emulation will
+  not work correctly (among other things).
+
+- the obsolete '-oldradio' option is now gone
+
+- on some Unix systems, SIGALRM is blocked by default, so unblock it when
+  creating the alarm timer
+
+- the 'esd' and 'alsa' libraries dump a lot of error messages to the console
+  by default on Linux. these are now disabled unless you use '-debug audio'
+
+- added the '-help-char-devices' help topic that describe the specification
+  of the <device> parameter of options like -serial, -gps, -shell-serial,
+  etc...
+
+KNOWN ISSUES:
+
+- no support for video input
+- no support for mutable SIM Card emulation yet
+- no support for bluetooth
+- no support for WiFi
+
+- on some Linux machines, the emulator might get stuck at startup. this
+  seems to be related to audio input support. try starting with
+  '-audio-in none' or  even '-noaudio' to disable sound, or choose a
+  different audio backend by  defining QEMU_AUDIO_DRV to an appropriate
+  value (read below).
+
+  you can also select different audio backends for both output and input
+  by defining QEMU_AUDIO_OUT_DRV and QEMU_AUDIO_IN_DRV independently.
+
+- on Windows, the emulator takes about 10-15% of the CPU even when the
+  emulated system is idle. this is a known issue related to QEMU's internal
+  event loop and Winsock. this should be fixed in a future emulator release.
+
+- GPS emulation only if you use the '-qemud' option. this is an experimental
+  option that is soon going to be the default. without this option, the
+  emulated system will start but GPS emulation will not work.
+
+  for the record, 'qemud' is a serial port multiplexer that is used to
+  multiplex several communication channels between the emulator and the
+  emulated system, though a single serial port.
+
+==============================================================================
+Changes between 1.2 and 1.3
+
+IMPORTANT BUG FIXES:
+
+NEW FEATURES:
+
+- '-audio-in <backend>' allows you to select the audio input backend from the
+  command line. this is equivalent to defining QEMU_AUDIO_IN_DRV=<backend>
+
+  '-audio-out <backend>' works for the audio output, and '-audio <backend>'
+  will select both input and output at the same time
+
+- '-debug <tags>' has replaced the old '-verbose-<tag1> -verbose-<tag2> ...'
+  debugging option. <tags> is a comma-separated list of debug tags
+  (see -help-debug-tags for a complete list). you can also use the special
+  value 'all' to indicate all debug tags, or prefix a '-' before a tag
+  name to disable it. for example:
+
+    -debug all,-audio
+
+  enables all debugging except audio. '-debug-<tag>' still works though.
+
+  note that while '-verbose-<tag>' is deprecated, '-verbose' is still supported
+  as an alias to '-debug-init'
+
+- '-keyset <file>' allows you to specific the keyset file to use. the default
+  is still ~/.android/default.keyset on Unix. for Windows, use -help-keyset
+  to get its default location (which differs between XP and Vista)
+
+
+NEW CONSOLE COMMANDS:
+
+- the 'geo nmea <sentence>' can be used to send a NMEA 1083 sentence as if
+  it came from an emulated GPS unit. NOTE: this doesn't work unless you
+  also use the '-qemud' option (see KNOWN ISSUES below)
+
+OTHER BUG FIXES:
+
+- severe color artefact issues when scaling the emulator window < 1.0 were
+  fixed.
+
+- fix rare random emulator freezes on Linux by disabling the 'dynticks' timer.
+
+REGRESSIONS:
+
+OTHER:
+
+- the ambiguous '-console' option is now obsolete. use '-shell' instead
+
+- the new '-shell-serial <device>' allows you to specify a device to
+  connect a root shell session to the emulated system.
+
+- the '-debug-kernel' option is now known as '-show-kernel' (the -debug-
+  prefix is reserved for strict emulator debugging features)
+
+- '-adb-port' has been removed from the list of options. similarly
+  '-port <port>' will accept an odd port number, but will print a warning
+  that it is using <port>-1 instead.
+
+- MMX is used on x86 to speed up window rescaling.
+
+- a new '-qemud' option is required to have GPS support work in this
+  SDK (either through '-gps <device>' or the 'geo nmea <sentence>'
+  console command)
+
+  this option is purely experimental and will soon become the default.
+
+KNOWN ISSUES:
+
+- no support for video input
+- no support for mutable SIM Card emulation yet
+- no support for bluetooth
+- no support for WiFi
+
+- on some Linux machines, the emulator might get stuck at startup. this
+  seems to be related to audio input support. try starting with
+  '-audio-in none' or  even '-noaudio' to disable sound, or choose a
+  different audio backend by  defining QEMU_AUDIO_DRV to an appropriate
+  value (read below).
+
+  you can also select different audio backends for both output and input
+  by defining QEMU_AUDIO_OUT_DRV and QEMU_AUDIO_IN_DRV independently.
+
+- on Windows, the emulator takes about 10-15% of the CPU even when the
+  emulated system is idle. this is a known issue related to QEMU's internal
+  event loop and Winsock. this should be fixed in a future emulator release.
+
+- GPS emulation only if you use the '-qemud' option. this is an experimental
+  option that is soon going to be the default. without this option, the
+  emulated system will start but GPS emulation will not work.
+
+  for the record, 'qemud' is a serial port multiplexer that is used to
+  multiplex several communication channels between the emulator and the
+  emulated system, though a single serial port.
+
+==============================================================================
+Changes between 1.1 and 1.2
+
+
+IMPORTANT BUG FIXES:
+
+- fixed a typo that prevented the F9/F10 keyboard shortcuts from working
+  properly, making non-programatically tracing unusable.
+
+- halve the emulator's memory requirements, saving around 130 megabytes
+  of memory by changing the way flash images are accessed (we now use
+  temporary files instead)
+
+- this emulator binary should be 10% to 20% faster than previous ones on
+  the Windows and OS X platforms. for faster boots, you may also want to
+  use the -no-boot-anim option described below to speed up the initial
+  boot sequence as well on slow machines.
+
+- proper rotation support when using Keypad 7/9 to switch between layouts
+  in the default HVGA skin. no need to use Ctrl-PageDown anymore
+
+- the -http-proxy <proxy> option didn't work correctly on Windows (unless
+  you were very lucky).
+
+- general socket handling code on Windows has been significantly improved.
+
+
+NEW FEATURES:
+
+- the console port number of a given emulator instance is now displayed in
+  its window's title bar.
+
+- voice/sms are automatically forwarded to other emulator instances running
+  on the same machine, as long as you use their console port number as the
+  destination phone number.
+
+  for example, if you have two emulator running, the first one will usually
+  use console port 5554, and the second one will use port 5556
+
+  then dialing 5556 on the 1st emulator will generate an incoming call on
+  the 2nd emulator. you can also hold/unhold calls as well.
+
+  this also works when sending SMS messages from one emulator to the other
+
+- the help system has been totally revamped:
+
+   *  -help              prints a summary of all options and help topics
+   *  -help-<option>     prints option-specific help
+   *  -help-<topic>      prints various topical help text
+   *  -help-all          prints *all* help content at once
+
+- the emulator now tries to automatically detect the host time zone and sends
+  it to the emulated system at startup (through the GSM modem). there is also
+  a new '-timezone <timezone>' option to be able to specify a different one.
+
+  IMPORTANT: the <timezone> name must be in zoneinfo format, i.e.
+             Area/Location, human-friendly abbreviations like "PST" or "CET"
+             will not work. examples are:
+
+                 America/Los_Angeles
+                 Europe/Paris
+
+- the emulator can now use up to 4 distinct DNS servers (instead of only one).
+  by default, they are taken from your system's list, which is obtained by
+  calling GetNetworkParams() on Win32, and parsing /etc/resolv.conf on
+  Unix.
+
+- a new '-dns-server <server>' option can be used to specify a comma-separated
+  list of alternative DNS servers to be used by the emulated system, instead of
+  the system's default.
+
+- a new '-scale <fraction>' option allows you to scale the emulator
+  window. <fraction> can be a number between 0.1 and 3.0.
+
+  you can also use '-scale <value>dpi', (e.g. '-scale 110dpi') to indicate the
+  resolution of your host monitor screen. it will be divided by the emulated
+  device's resolution to get an absolute scale.
+
+- a new '-dpi-device <dpi>' option allows you to specific the resolution of
+  the emulated device's screen. Note that this is not required: the default
+  used is 165, which is the average of several prototypes we've been working
+  with.
+
+- add a new '-port <port>' option to specify which port the emulator should
+  bind to for the console, instead of letting it guess. <port> must be an
+  *even* integer between 5554 and 5584 included. the corresponding ADB port
+  will be <port>+1
+
+- [DEPRECATED] add a new '-adb-port <port>' option to specify which port the
+  emulator should bind to, instead of letting it guess. <port> must be an odd
+  integer between 5555 and 5585 included. the corresponding control console
+  will be on <port>-1
+
+  NOTE: -adb-port is deprecated, don't use it, it will probably disappear
+  NOTE2: you cannot use both -port and -adb-port at the same time.
+
+- a new '-no-boot-anim' options tells the emulated system to disable the boot
+  animation. on slow systems, this can *significantly* reduce the time to
+  boot the system in the emulator.
+
+- you can now redefine the emulator's keybinding by writing a 'keyset' file
+  and use '-keyset <filename>' to use it when starting the emulator. use
+  -help-keyset and -help-keyset-file for all details.
+
+  this allows you to use the emulator effectively on keyboards which don't
+  have a keypad, by using different keys..
+
+- you can now toggle between windowed and fullscreen mode at runtime by
+  pressing Alt-Enter (only works on Linux at the moment !!)
+
+- use '-audio-out <backend>' and '-audio-in <backend>' to change the output
+  and input audio backends used by the emulator. see -help-audio-out and
+  -help-audio-in for a list of valid values.
+
+  this is equivalent to setting the QEMU_AUDIO_OUT_DRV and QEMU_AUDIO_IN_DRV
+  environment variables.
+
+  use '-audio <backend>' to set both the input and output backends at the
+  same time. this is equivalent to setting the QEMU_AUDIO_DRV environment
+  variable.
+
+
+NEW CONSOLE COMMANDS:
+
+- the new 'power' command can be used to control the power/battery state of
+  the emulated device.
+
+- the new 'event send' command can be used to send simulated hardware events
+  to the Android Linux kernel. each event must be in the form
+  <type>:<code>:<value> where:
+
+    <type>  is either an integer or a corresponding string alias
+            (use "event types" to see a list of aliases)
+
+    <code>  is either an integer or a corresponding string alias
+            that depends on the value of <type> (use "event codes <type>"
+            to see a list of these aliases)
+
+    <value> is an integer
+
+  NOTE: Be warned that it is very easy to confuse the kernel about the state
+        of emulated hardware by sending the wrong event. An *excellent*
+        knowledge of the Linux kernel internals is encouraged before playing
+        with "event send".
+
+- the new 'event text <textMessage>' command can be used to simulate
+  keypresses of small text messages, where <textMessage> is an utf-8 string.
+
+- the new 'avd stop' and 'avd start' command can be used to stop/start the
+  emulation. you can also use 'avd status' to query the current state.
+
+- the new 'window scale <scale>' command allows you to change the scale of
+  the emulator window dynamically. <scale> is either an integer followed by
+  the 'dpi' suffix (e.g. '120dpi') or a real number between 0.1 and 3.0.
+
+  in the first case, <scale> specifies your monitor dpi; in the second one,
+  the new window scale itself.
+
+
+OTHER BUG FIXES:
+
+- in case of SDL_Init() failure, print the SDL error message.
+- disable networking code's logging to /tmp/slirp.log
+- the emulator now works with 2GB SD Card files
+- the emulator doesn't prevent the screensaver to kick in on OS X anymore
+- the -onion and -onion-alpha options now work properly
+- a second emulator instance trying to use the same SD Card instance than a
+  first one will no longer crash
+- it's now possible to properly start the emulator in the background on all
+  Unix shells (e.g. "emulator &") without being interrupted/stopped by a
+  SIGTTIN or SIGTTOU signal.
+- fixed a bug in the SMS emulation that happened when using GSM 7-bit escaped
+  characters, i.e. anything in the following: [|]~\{}^
+- fixed a small regression where -data <foo> would fail if the file <foo>
+  did not exist.
+
+
+REGRESSIONS:
+
+- the -flash-keys options doesn't work anymore
+
+
+KNOWN ISSUES:
+
+- no support for video input
+- no support for mutable SIM Card emulation yet
+- no support for bluetooth
+- no support for WiFi
+
+- on some Linux machines, the emulator might get stuck at startup. this
+  seems to be related to audio input support. try starting with
+  '-audio-in none' or even '-noaudio' to disable sound, or choose a different
+  audio backend by defining QEMU_AUDIO_DRV to an appropriate value
+  (read below).
+
+  you can also select different audio backends for both output and input
+  by defining QEMU_AUDIO_OUT_DRV and QEMU_AUDIO_IN_DRV independently.
+
+- on Windows, the emulator takes about 10-15% of the CPU even when the
+  emulated system is idle. this is a known issue related to QEMU's internal
+  event loop and Winsock. this should be fixed in a future emulator release.
+
+OTHER:
+
+- you can now use -debug-<component> and/or -debug-no-<component> to
+  enable or disable the debug messages of a given emulator component. this
+  can be very useful for troubleshooting. for all details, use -help-debug
+  and -help-debug-tags
+
+- you can also use '-debug <tags>' where <tags> is a comma-separated list
+  of component names, optionally prefixed by a single '-'. see -help-debug
+  and -help-debug-tags for all details
+
+- you can now define the ANDROID_VERBOSE environment variable as a list
+  of "debug" items (each <item> corresponds to a -debug-<item> option).
+  for example, defining:
+
+     ANDROID_VERBOSE=socket,keys
+
+  is equivalent to using "-debug socket,keys" when invoking the emulator
+
+- as a special case, -debug-slirp enables logging of the router/firewall
+  operations to a temporary file (e.g. /tmp/android/slirp.log). you can
+  also specify a logging bitmask with the ANDROID_SLIRP_LOGMASK environment
+  variable (the default is a mask of 7).
+
+- removed many obsolete / unused source files from the repository. also
+  performed a rather heavy cleanup of the sources to make them somewhat
+  more manageable.
+
+- integrate dynticks support from upstream QEMU depot. this only allows one
+  to provide more precise timing accuracy in the guest under Linux.
+  (NOTE: disabled in the source code, since it seems that it freezes
+  the emulator sometimes)
+
+- audio input is now working on OS X, Windows and Linux. on Linux, there
+  are four different backends supported: EsounD, ALSA, OSS and SDL. they
+  are accessed through dlopen/dlsym, which means that the emulator binary
+  will run on any system.
+
+  you can specify a given backend by defining the QEMU_AUDIO_DRV environment
+  variable to one of these values:
+
+    alsa
+    esd
+    sdl
+    oss
+    none
+
+  note that the "sdl" audio backend is the most compatible, but doesn't
+  support audio input at all !!
+
+- a new option '-cpu-delay <delay>' can be used to slow down the CPU
+  emulation. the <delay> is an integer between 0 and 1000. note that it
+  doesn't necessarily scale linearly with effective performance.
+
+  the delay process is not exactly deterministic. this is just a hack that
+  may disappear or be completely re-implemented in the future
+
+- some new "gsm" and "sms" subcommands were added to the control console.
+  they are used internally by the voice/sms auto-forwarder and are probably
+  not very useful to typical developers
+
+- some code has been added to support save/restore of the AVD state to/from
+  a file. however this is not properly tested yet, and requires that you
+  use exactly the same options and disk images when reloading the AVD state.
+
+- added a new -cache <file> option to specify the cache partition image
+  file. the default is to use a temporary file instead
+
+- added a new -report-console <socket> option to be able to report the
+  automatically assigned console port to a remote third-party (e.g. a
+  script) before starting the emulation. see the output of -help for all
+  the details
+
+- (only useful to Android engineers)
+  the audio sub-system is now compiled in its own static library (called
+  libqemu-audio.a), which gets copied to the Android "prebuilt/Linux/qemu"
+  directory. this is done to avoid forcing all developers to install various
+  development packages on Linux, as well as all build servers. there is also
+  now a script named "distrib/update-audio.sh" which will update the depot
+  file automatically for you: call it whenever you change the audio sources.
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644
index 0000000..223ede7
--- /dev/null
+++ b/COPYING.LIB
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/Changelog b/Changelog
new file mode 100644
index 0000000..58f3e5e
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,395 @@
+version 0.8.2:
+
+  - ACPI support
+  - PC VGA BIOS fixes
+  - switch to OpenBios for SPARC targets (Blue Swirl)
+  - VNC server fixes
+  - MIPS FPU support (Marius Groeger)
+  - Solaris/SPARC host support (Ben Taylor)
+  - PPC breakpoints and single stepping (Jason Wessel)
+  - USB updates (Paul Brook)
+  - UDP/TCP/telnet character devices (Jason Wessel)
+  - Windows sparse file support (Frediano Ziglio)
+  - RTL8139 NIC TCP segmentation offloading (Igor Kovalenko)
+  - PCNET NIC support (Antony T Curtis)
+  - Support for variable frequency host CPUs
+  - Workaround for win32 SMP hosts
+  - Support for AMD Flash memories (Jocelyn Mayer)
+  - Audio capture to WAV files support (malc)
+
+version 0.8.1:
+
+  - USB tablet support (Brad Campbell, Anthony Liguori)
+  - win32 host serial support (Kazu)
+  - PC speaker support (Joachim Henke)
+  - IDE LBA48 support (Jens Axboe)
+  - SSE3 support
+  - Solaris port (Ben Taylor)
+  - Preliminary SH4 target (Samuel Tardieu)
+  - VNC server (Anthony Liguori)
+  - slirp fixes (Ed Swierk et al.)
+  - USB fixes
+  - ARM Versatile Platform Baseboard emulation (Paul Brook)
+
+version 0.8.0:
+
+  - ARM system emulation: Arm Integrator/CP board with an arm1026ej-s
+    cpu (Paul Brook)
+  - SMP support
+  - Mac OS X cocoa improvements (Mike Kronenberg)
+  - Mac OS X CoreAudio driver (Mike Kronenberg)
+  - DirectSound driver (malc)
+  - ALSA audio driver (malc)
+  - new audio options: '-soundhw' and '-audio-help' (malc)
+  - ES1370 PCI audio device (malc)
+  - Initial USB support
+  - Linux host serial port access
+  - Linux host low level parallel port access
+  - New network emulation code supporting VLANs.
+  - MIPS and MIPSel User Linux emulation
+  - MIPS fixes to boot Linux (Daniel Jacobowitz)
+  - NX bit support
+  - Initial SPARC SMP support (Blue Swirl)
+  - Major overhaul of the virtual FAT driver for read/write support
+    (Johannes Schindelin)
+
+version 0.7.2:
+  
+  - x86_64 fixes (Win2000 and Linux 2.6 boot in 32 bit)
+  - merge self modifying code handling in dirty ram page mecanism.
+  - MIPS fixes (Ralf Baechle)
+  - better user net performances
+
+version 0.7.1:
+
+  - read-only Virtual FAT support (Johannes Schindelin)
+  - Windows 2000 install disk full hack (original idea from Vladimir
+    N. Oleynik)
+  - VMDK disk image creation (Filip Navara)
+  - SPARC64 progress (Blue Swirl)
+  - initial MIPS support (Jocelyn mayer)
+  - MIPS improvements (Ralf Baechle)
+  - 64 bit fixes in user networking (initial patch by Gwenole Beauchesne)
+  - IOAPIC support (Filip Navara)
+
+version 0.7.0:
+
+  - better BIOS translation and HDD geometry auto-detection
+  - user mode networking bug fix
+  - undocumented FPU ops support
+  - Cirrus VGA: support for 1280x1024x[8,15,16] modes
+  - 'pidfile' option
+  - .dmg disk image format support (Johannes Schindelin)
+  - keymaps support (initial patch by Johannes Schindelin)
+  - big endian ARM support (Lennert Buytenhek)
+  - added generic 64 bit target support
+  - x86_64 target support
+  - initial APIC support
+  - MMX/SSE/SSE2/PNI support
+  - PC parallel port support (Mark Jonckheere)
+  - initial SPARC64 support (Blue Swirl)
+  - SPARC target boots Linux (Blue Swirl)
+  - armv5te user mode support (Paul Brook)
+  - ARM VFP support (Paul Brook)
+  - ARM "Angel" semihosting syscalls (Paul Brook)
+  - user mode gdb stub support (Paul Brook)
+  - Samba 3 support
+  - initial Cocoa support (Pierre d'Herbemont)
+  - generic FPU emulation code
+  - Virtual PC read-only disk image support (Alex Beregszaszi)
+
+version 0.6.1:
+
+  - Mac OS X port (Pierre d'Herbemont)
+  - Virtual console support
+  - Better monitor line edition
+  - New block device layer 
+  - New 'qcow' growable disk image support with AES encryption and
+    transparent decompression
+  - VMware 3 and 4 read-only disk image support (untested)
+  - Support for up to 4 serial ports
+  - TFTP server support (Magnus Damm)
+  - Port redirection support in user mode networking
+  - Support for not executable data sections
+  - Compressed loop disk image support (Johannes Schindelin)
+  - Level triggered IRQ fix (aka NE2000 PCI performance fix) (Steve
+    Wormley)
+  - Fixed Fedora Core 2 problems (now you can run qemu without any
+    LD_ASSUME_KERNEL tricks on FC2)
+  - DHCP fix for Windows (accept DHCPREQUEST alone)
+  - SPARC system emulation (Blue Swirl)
+  - Automatic Samba configuration for host file access from Windows.
+  - '-loadvm' and '-full-screen' options
+  - ne2000 savevm support (Johannes Schindelin)
+  - Ctrl-Alt is now the default grab key. Ctrl-Alt-[0-9] switches to
+    the virtual consoles.
+  - BIOS floppy fix for NT4 (Mike Nordell, Derek Fawcus, Volker Ruppert)
+  - Floppy fixes for NT4 and NT5 (Mike Nordell)
+  - NT4 IDE fixes (Ben Pfaf, Mike Nordell)
+  - SDL Audio support and SB16 fixes (malc)
+  - ENTER instruction bug fix (initial patch by Stefan Kisdaroczi)
+  - VGA font change fix
+  - VGA read-only CRTC register fix
+
+version 0.6.0:
+
+  - minimalist FPU exception support (NetBSD FPU probe fix)
+  - cr0.ET fix (Win95 boot)
+  - *BSD port (Markus Niemisto)
+  - I/O access fix (signaled by Mark Jonckheere)
+  - IDE drives serial number fix (Mike Nordell)
+  - int13 CDROM BIOS fix (aka Solaris x86 install CD fix)
+  - int15, ah=86 BIOS fix (aka Solaris x86 hardware probe hang up fix)
+  - BSR/BSF "undefined behaviour" fix
+  - vmdk2raw: convert VMware disk images to raw images
+  - PCI support
+  - NE2K PCI support
+  - dummy VGA PCI support
+  - VGA font selection fix (Daniel Serpell)
+  - PIC reset fix (Hidemi KAWAI)
+  - PIC spurious irq support (aka Solaris install bug)
+  - added '-localtime' option
+  - Cirrus CL-GD54xx VGA support (initial patch by Makoto Suzuki (suzu))
+  - APM and system shutdown support
+  - Fixed system reset
+  - Support for other PC BIOSes
+  - Initial PowerMac hardware emulation
+  - PowerMac/PREP OpenFirmware compatible BIOS (Jocelyn Mayer)
+  - initial IDE BMDMA support (needed for Darwin x86)
+  - Set the default memory size for PC emulation to 128 MB
+
+version 0.5.5:
+
+  - SDL full screen support (initial patch by malc)
+  - VGA support on PowerPC PREP
+  - VBE fixes (Matthew Mastracci)
+  - PIT fixes (aka Win98 hardware probe and "VGA slowness" bug)
+  - IDE master only fixes (aka Win98 CD-ROM probe bug)
+  - ARM load/store half word fix (Ulrich Hecht)
+  - FDC fixes for Win98
+
+version 0.5.4:
+  
+  - qemu-fast fixes
+  - BIOS area protection fix (aka EMM386.EXE fix) (Mike Nordell)
+  - keyboard/mouse fix (Mike Nordell)
+  - IDE fixes (Linux did not recognized slave drivers)
+  - VM86 EIP masking fix (aka NT5 install fix) (Mike Nordell)
+  - QEMU can now boot a PowerPC Linux kernel (Jocelyn Mayer)
+  - User mode network stack
+  - imul imm8 fix + 0x82 opcode support (Hidemi KAWAI)
+  - precise self modifying code (aka BeOS install bug)
+
+version 0.5.3:
+
+  - added Bochs VESA VBE support
+  - VGA memory map mode 3 access fix (OS/2 install fix)
+  - IDE fixes (Jens Axboe)
+  - CPU interrupt fixes
+  - fixed various TLB invalidation cases (NT install)
+  - fixed cr0.WP semantics (XP install)
+  - direct chaining support for SPARC and PowerPC (faster)
+  - ARM NWFPE support (initial patch by Ulrich Hecht)
+  - added specific x86 to x86 translator (close to native performance
+    in qemu-i386 and qemu-fast)
+  - shm syscalls support (Paul McKerras)
+  - added accurate CR0.MP/ME/TS emulation
+  - fixed DMA memory write access (Win95 boot floppy fix)
+  - graphical x86 linux loader
+  - command line monitor 
+  - generic removable device support
+  - support of CD-ROM change
+  - multiple network interface support
+  - initial x86-64 host support (Gwenole Beauchesne)
+  - lret to outer priviledge fix (OS/2 install fix)
+  - task switch fixes (SkyOS boot)
+  - VM save/restore commands
+  - new timer API
+  - more precise RTC emulation (periodic timers + time updates)
+  - Win32 port (initial patch by Kazu)
+
+version 0.5.2:
+
+  - improved soft MMU speed (assembly functions and specializing)
+  - improved multitasking speed by avoiding flushing TBs when
+    switching tasks
+  - improved qemu-fast speed
+  - improved self modifying code handling (big performance gain in
+    softmmu mode).
+  - fixed IO checking
+  - fixed CD-ROM detection (win98 install CD)
+  - fixed addseg real mode bug (GRUB boot fix)
+  - added ROM memory support (win98 boot)
+  - fixed 'call Ev' in case of paging exception
+  - updated the script 'qemu-binfmt-conf.sh' to use QEMU automagically
+    when launching executables for the supported target CPUs.
+  - PowerPC system emulation update (Jocelyn Mayer)
+  - PC floppy emulation and DMA fixes (Jocelyn Mayer)
+  - polled mode for PIC (Jocelyn Mayer)
+  - fixed PTE dirty bit handling
+  - fixed xadd same reg bug
+  - fixed cmpxchg exception safeness
+  - access to virtual memory in gdb stub
+  - task gate and NT flag fixes
+  - eflags optimisation fix for string operations
+
+version 0.5.1:
+  
+  - float access fixes when using soft mmu
+  - PC emulation support on PowerPC
+  - A20 support
+  - IDE CD-ROM emulation
+  - ARM fixes (Ulrich Hecht)
+  - SB16 emulation (malc)
+  - IRET and INT fixes in VM86 mode with IOPL=3
+  - Port I/Os use TSS io map
+  - Full task switching/task gate support
+  - added verr, verw, arpl, fcmovxx
+  - PowerPC target support (Jocelyn Mayer)
+  - Major SPARC target fixes (dynamically linked programs begin to work)
+
+version 0.5.0:
+  
+  - full hardware level VGA emulation
+  - graphical display with SDL
+  - added PS/2 mouse and keyboard emulation
+  - popw (%esp) fix
+  - mov to/from segment data width fix
+  - added real mode support
+  - added Bochs BIOS and LGPL'ed VGA BIOS loader in qemu
+  - m68k host port (Richard Zidlicky)
+  - partial soft MMU support for memory mapped I/Os
+  - multi-target build
+  - fixed: no error code in hardware interrupts
+  - fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
+  - correct single stepping thru string operations
+  - preliminary SPARC target support (Thomas M. Ogrisegg)
+  - tun-fd option (Rusty Russell)
+  - automatic IDE geometry detection
+  - renamed 'vl' to qemu[-fast] and user qemu to qemu-{cpu}.
+  - added man page
+  - added full soft mmu mode to launch unpatched OSes.
+
+version 0.4.3:
+
+  - x86 exception fix in case of nop instruction.
+  - gcc 3.2.2 bug workaround (RedHat 9 fix)
+  - sparc and Alpha host fixes
+  - many ARM target fixes: 'ls' and 'bash' can be launched.
+
+version 0.4.2:
+
+ - many exception handling fixes (can compile a Linux kernel inside vl)
+ - IDE emulation support
+ - initial GDB stub support
+ - deferred update support for disk images (Rusty Russell)
+ - accept User Mode Linux Copy On Write disk images
+ - SMP kernels can at least be booted
+
+version 0.4.1:
+  
+ - more accurate timer support in vl.
+ - more reliable NE2000 probe in vl.
+ - added 2.5.66 kernel in vl-test.
+ - added VLTMPDIR environment variable in vl.
+
+version 0.4:
+
+ - initial support for ring 0 x86 processor emulation
+ - fixed signal handling for correct dosemu DPMI emulation
+ - fast x86 MMU emulation with mmap()
+ - fixed popl (%esp) case
+ - Linux kernel can be executed by QEMU with the 'vl' command.
+
+version 0.3:
+
+ - initial support for ARM emulation
+ - added fnsave, frstor, fnstenv, fldenv FPU instructions
+ - added FPU register save in signal emulation
+ - initial ARM port
+ - Sparc and Alpha ports work on the regression test
+ - generic ioctl number conversion
+ - fixed ioctl type conversion
+
+version 0.2:
+
+ - PowerPC disassembly and ELF symbols output (Rusty Russell)
+ - flock support (Rusty Russell)
+ - ugetrlimit support (Rusty Russell)
+ - fstat64 fix (Rusty Russell)
+ - initial Alpha port (Falk Hueffner)
+ - initial IA64 port (Matt Wilson)
+ - initial Sparc and Sparc64 port (David S. Miller)
+ - added HLT instruction
+ - LRET instruction fix.
+ - added GPF generation for I/Os.
+ - added INT3 and TF flag support.
+ - SHL instruction C flag fix.
+ - mmap emulation for host page size > 4KB
+ - self-modifying code support
+ - better VM86 support (dosemu works on non trivial programs)
+ - precise exception support (EIP is computed correctly in most cases)
+ - more precise LDT/GDT/IDT emulation
+ - faster segment load in vm86 mode
+ - direct chaining of basic blocks (faster emulation)
+
+version 0.1.6:
+
+ - automatic library search system. QEMU can now work with unpatched
+   ELF dynamic loader and libc (Rusty Russell).
+ - ISO C warning fixes (Alistair Strachan)
+ - first self-virtualizable version (works only as long as the
+   translation cache is not flushed)
+ - RH9 fixes
+
+version 0.1.5:
+
+ - ppc64 support + personality() patch (Rusty Russell)
+ - first Alpha CPU patches (Falk Hueffner)
+ - removed bfd.h dependancy
+ - fixed shrd, shld, idivl and divl on PowerPC.
+ - fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).
+
+version 0.1.4:
+
+ - more accurate VM86 emulation (can launch small DOS 16 bit
+   executables in wine).
+ - fixed push/pop fs/gs
+ - added iret instruction.
+ - added times() syscall and SIOCATMARK ioctl.
+
+version 0.1.3:
+
+ - S390 support (Ulrich Weigand)
+ - glibc 2.3.x compile fix (Ulrich Weigand)
+ - socketcall endian fix (Ulrich Weigand)
+ - struct sockaddr endian fix (Ulrich Weigand)
+ - sendmsg/recvmsg endian fix (Ulrich Weigand)
+ - execve endian fix (Ulrich Weigand)
+ - fdset endian fix (Ulrich Weigand)
+ - partial setsockopt syscall support (Ulrich Weigand)
+ - more accurate pushf/popf emulation
+ - first partial vm86() syscall support (can be used with runcom example).
+ - added bound, cmpxchg8b, cpuid instructions
+ - added 16 bit addressing support/override for string operations
+ - poll() fix
+ 
+version 0.1.2:
+
+ - compile fixes
+ - xlat instruction
+ - xchg instruction memory lock
+ - added simple vm86 example (not working with QEMU yet). The 54 byte
+   DOS executable 'pi_10.com' program was released by Bertram
+   Felgenhauer (more information at http://www.boo.net/~jasonp/pipage.html).
+
+version 0.1.1:
+
+ - glibc 2.2 compilation fixes
+ - added -s and -L options
+ - binary distribution of x86 glibc and wine
+ - big endian fixes in ELF loader and getdents.
+
+version 0.1:
+
+ - initial public release.
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..95b64e1
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,119 @@
+This package contains the sources to the Android emulator program.
+
+Supported Development Platforms:
+--------------------------------
+
+The Android emulator can be built on the following platforms:
+
+    - Linux 32-bits
+    - Linux 64-bits (*only* generates 32-bit emulator binary)
+    - Darwin x86
+    - Darwin ppc  (experimental only)
+    - Windows x86  (through Cygwin only)
+
+Note that development on 64-bit versions of Darwin and Windows is
+not supported. The 32-bit emulator binary should run normally on
+these platforms though.
+
+The Windows emulator binary is built using the -no-cygwin option
+and thus doesn't depend on CYGWIN.DLL being installed on your system.
+
+It is possible to hack the android-configure.sh script to build
+a 64-bit emulator binary on Linux. Unfortunately the resulting
+program will crash pretty soon during emulation. This problem is
+due to the way the emulator works and cannot be easily fixed at
+the moment.
+
+Supported Compilers:
+--------------------
+
+The Android emulator is a heavy fork of QEMU 0.8.2, and as such,
+can only be built properly with a small number of compilers. Moreover,
+which compiler can be used depends on your platform.
+
+The following table sums up the compilers that are *known* to produce
+correct output:
+
+  Linux x86:              gcc-3.4.6
+  Linux x86 and x86_64:   gcc-4.2.3
+  Darwin x86:             gcc-4.0.1 (build 5341)
+  Darwin ppc:             gcc-3.3   (build 1819)
+
+Use any other compiler at your own risks ! A 'bad binary' usually
+results in the VM crashing either immediately or after a few seconds.
+
+Note that on Darwin, the *build* number of your compiler *is* important.
+Some builds of gcc-4.0.1 are known to generate bad binaries on Darwin x86,
+so your own fails to build an executable that works correctly.
+You can find the sources to the required gcc here:
+
+
+We distribute a file named distrib/build_gcc_qemu_darwin.sh which can be
+used as a replacement for the Apple-provided build_gcc.sh that comes with
+their gcc distribution.
+
+
+Building the emulator with the Android build system:
+----------------------------------------------------
+
+Ensure that you have properly configured your build by running the
+envsetup.sh script and using the appropriate 'lunch' command.
+
+Then type: 
+
+     m emulator
+
+This will rebuild the emulator and place it in an adequate location.
+Simply type 'emulator' to start it with the currently built system
+image.
+
+
+Building the emulator without the Android build system:
+-------------------------------------------------------
+
+You can also build the emulator as a stand-alone program, by following
+these simple steps:
+
+  1/ First, build Android's patched libSDL as a static library,
+     this can be done as:
+
+        cd $TOP/extlibs/libsdl-1.2.12
+        ./android-configure --prefix=<PATH>
+        make
+        make install
+
+    Where $TOP is the path of your open-source Android source tree, and
+    where <PATH> is any path of your chosing where the library will
+    be copied to by the 'make install' command. For example, you
+    can use $HOME/android-sdl
+
+  2/ Configure the emulator with android-configure.sh, as in:
+
+        cd $TOP/tools/qemu
+        ./android-configure.sh --sdl-config=<PATH>
+        make
+
+     Where <PATH> is the same path you used with the --prefix option
+     when building the SDL library
+
+The emulator binary is located into objs/emulator, you can strip it and
+copy it to any location of your choosing.
+
+
+Creating an emulator source distribution package:
+-------------------------------------------------
+
+We provide a script to build a tar.gz package file that contains all the
+sources required to rebuild the emulator (i.e. it includes the patched SDL
+sources as well) plus a handy script to automate the rebuild.
+
+Simply invoke:
+
+    cd $TOP/tools/qemu
+    distrib/make-distrib.sh
+
+This script will create a tar.gz file under /tmp/android-package and will
+print its location when it completes.
+
+To rebuild the corresponding emulator, un-tar-gz the package, and run
+the 'rebuild.sh' script.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..bfc9a1f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,12 @@
+The following points clarify the QEMU licenses:
+
+1) The QEMU virtual CPU core library (libqemu.a) and the QEMU PC
+   system emulator are released under the GNU Lesser General Public
+   License.
+
+2) The Linux user mode QEMU emulator is released under the GNU General
+   Public License.
+
+3) QEMU is a trademark of Fabrice Bellard.
+
+Fabrice Bellard.
\ No newline at end of file
diff --git a/MODULE_LICENSE_GPL b/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_GPL
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f39a14c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,73 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# this is a set of definitions that allow the usage of Makefile.android
+# even if we're not using the Android build system.
+#
+
+BUILD_SYSTEM := android/build
+OBJS_DIR     := objs
+CONFIG_MAKE  := $(OBJS_DIR)/config.make
+CONFIG_H     := $(OBJS_DIR)/config-host.h
+
+ifeq ($(wildcard $(CONFIG_MAKE)),)
+    $(error "The configuration file '$(CONFIG_MAKE)' doesnt' exist, please run the "rebuilt.sh" script)
+endif
+
+include $(CONFIG_MAKE)
+include $(BUILD_SYSTEM)/definitions.make
+
+VPATH := $(OBJS_DIR)
+VPATH += :$(SRC_PATH)/android/config
+VPATH += :$(SRC_PATH):$(SRC_PATH)/target-$(TARGET_ARCH)
+
+.PHONY: all libraries executables clean clean-config clean-objs-dir \
+        clean-executables clean-libraries
+
+CLEAR_VARS                := $(BUILD_SYSTEM)/clear_vars.make
+BUILD_HOST_EXECUTABLE     := $(BUILD_SYSTEM)/host_executable.make
+BUILD_HOST_STATIC_LIBRARY := $(BUILD_SYSTEM)/host_static_library.make
+
+DEPENDENCY_DIRS :=
+
+all: libraries executables
+EXECUTABLES :=
+LIBRARIES   :=
+
+SDL_CONFIG ?= $(PREBUILT)/sdl/bin/sdl-config
+SDL_LIBS   := $(filter %.a,$(shell $(SDL_CONFIG) --static-libs))
+$(foreach lib,$(SDL_LIBS), \
+    $(eval $(call copy-prebuilt-lib,$(lib))) \
+)
+
+clean: clean-intermediates
+
+distclean: clean clean-config
+
+# let's roll
+include Makefile.android
+
+libraries: $(LIBRARIES)
+executables: $(EXECUTABLES)
+
+clean-intermediates:
+	rm -rf $(OBJS_DIR)/intermediates $(EXECUTABLES) $(LIBRARIES)
+
+clean-config:
+	rm -f $(CONFIG_MAKE) $(CONFIG_H)
+
+# include dependency information
+DEPENDENCY_DIRS := $(sort $(DEPENDENCY_DIRS))
+-include $(wildcard $(DEPENDENCY_DIRS:%=%/*.d))
\ No newline at end of file
diff --git a/Makefile.android b/Makefile.android
new file mode 100644
index 0000000..35fcad8
--- /dev/null
+++ b/Makefile.android
@@ -0,0 +1,538 @@
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_PATH:= $(call my-dir)
+
+# determine the location of platform-specific directories
+#
+CONFIG_DIRS     := \
+    $(LOCAL_PATH)/android/config \
+    $(LOCAL_PATH)/android/config/$(HOST_PREBUILT_TAG)
+
+CONFIG_INCLUDES := $(CONFIG_DIRS:%=-I%)
+
+MY_CFLAGS := $(CONFIG_INCLUDES) -O2 -g \
+             -fno-PIC \
+             -falign-functions=0 \
+             -fomit-frame-pointer \
+
+MY_LDFLAGS :=
+
+# this is needed to build the emulator on 64-bit Linux systems
+ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
+  MY_CFLAGS += -Wa,--32
+endif
+
+ifeq ($(HOST_OS),freebsd)
+  MY_CFLAGS += -Wa,--32 -I /usr/local/include
+endif
+
+ifeq ($(HOST_OS),windows)
+  MY_CFLAGS += -D_WIN32 -mno-cygwin
+  # we need Win32 features  that are available since Windows 2000 Professional/Server (NT 5.0)
+  MY_CFLAGS += -DWINVER=0x501
+endif
+
+ifeq ($(HOST_ARCH),ppc)
+    MY_CFLAGS += -D__powerpc__
+endif
+
+ifeq ($(HOST_OS),darwin)
+    MY_CFLAGS += -mdynamic-no-pic
+endif
+MY_CC := $(HOST_CC)
+
+# BUILD_STANDALONE_EMULATOR is only defined when building with
+# the android-rebuild.sh script. The script will also provide
+# adequate values for HOST_CC
+#
+ifneq ($(BUILD_STANDALONE_EMULATOR),true)
+
+  ifneq ($(USE_CCACHE),)
+    MY_CC := prebuilt/$(HOST_PREBUILT_TAG)/ccache/ccache $(MY_CC)
+  endif
+endif
+
+
+ifneq ($(combo_target)$(TARGET_SIMULATOR),HOST_true)
+  ifneq ($(HOST_ARCH),x86_64)
+    MY_CFLAGS  += -m32
+    MY_LDFLAGS += -m32
+  endif
+endif
+
+include $(CLEAR_VARS)
+
+###########################################################
+# Zlib configuration
+#
+ZLIB_DIR := distrib/zlib-1.2.3
+include $(LOCAL_PATH)/$(ZLIB_DIR)/sources.make
+
+###########################################################
+# Libpng configuration
+#
+LIBPNG_DIR := distrib/libpng-1.2.19
+include $(LOCAL_PATH)/$(LIBPNG_DIR)/sources.make
+
+###############################################################################
+# build the TCG code generator
+#
+include $(CLEAR_VARS)
+
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_CC                        := $(MY_CC)
+LOCAL_CFLAGS                    := $(MY_CFLAGS) $(LOCAL_CFLAGS)
+LOCAL_LDFLAGS                   := $(MY_LDFLAGS)
+LOCAL_MODULE                    := emulator-tcg
+
+TCG_TARGET := $(HOST_ARCH)
+ifeq ($(TCG_TARGET),x86)
+  TCG_TARGET := i386
+endif
+
+TCG_CFLAGS := -I$(LOCAL_PATH)/tcg -I$(LOCAL_PATH)/tcg/$(TCG_TARGET)
+
+LOCAL_CFLAGS += $(TCG_CFLAGS) \
+                -I$(LOCAL_PATH)/target-arm \
+                -I$(LOCAL_PATH)/fpu \
+
+LOCAL_SRC_FILES := \
+    tcg/tcg.c \
+    tcg/tcg-dyngen.c \
+    tcg/tcg-runtime.c \
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+##############################################################################
+# build the HW emulation support
+#
+include $(CLEAR_VARS)
+
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_CC                        := $(MY_CC)
+LOCAL_MODULE                    := emulator-hw
+
+HW_CFLAGS    := -I$(LOCAL_PATH)/hw
+
+LOCAL_CFLAGS := $(MY_CFLAGS) $(LOCAL_CFLAGS)
+LOCAL_CFLAGS += -I$(LOCAL_PATH)/target-arm -I$(LOCAL_PATH)/fpu $(HW_CFLAGS)
+LOCAL_CFLAGS += $(ZLIB_CFLAGS) -I$(LOCAL_PATH)/$(ZLIB_DIR)
+
+HW_SOURCES := \
+    android_arm.c \
+    arm_pic.c \
+    cdrom.c \
+    dma.c \
+    irq.c \
+    goldfish_audio.c \
+    goldfish_battery.c \
+    goldfish_device.c \
+    goldfish_events_device.c \
+    goldfish_fb.c \
+    goldfish_interrupt.c \
+    goldfish_memlog.c \
+    goldfish_mmc.c \
+    goldfish_nand.c  \
+    goldfish_switch.c \
+    goldfish_timer.c \
+    goldfish_trace.c \
+    goldfish_tty.c \
+    pci.c \
+    scsi-disk.c \
+    smc91c111.c \
+    usb-hid.c \
+    usb-hub.c \
+    usb-msd.c \
+    usb-ohci.c \
+    usb.c \
+
+LOCAL_SRC_FILES += $(HW_SOURCES:%=hw/%)
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+##############################################################################
+# build the ARM-specific emulation engine sources
+#
+include $(CLEAR_VARS)
+
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_CC                        := $(MY_CC)
+LOCAL_MODULE                    := emulator-arm
+LOCAL_LDFLAGS                   := $(MY_LDFLAGS)
+LOCAL_CFLAGS                    := $(MY_CFLAGS) $(LOCAL_CFLAGS)
+LOCAL_STATIC_LIBRARIES          := emulator-hw
+
+LOCAL_CFLAGS := -fno-PIC -fomit-frame-pointer -Wno-sign-compare
+LOCAL_CFLAGS := $(MY_CFLAGS) $(LOCAL_CFLAGS)
+
+LOCAL_CFLAGS += -I$(LOCAL_PATH) \
+                -I$(LOCAL_PATH)/target-arm \
+                -I$(LOCAL_PATH)/fpu \
+                $(TCG_CFLAGS) \
+                $(HW_CFLAGS) \
+
+ifeq ($(HOST_ARCH),ppc)
+    LOCAL_CFLAGS += -D__powerpc__
+endif
+
+LOCAL_SRC_FILES += exec.c cpu-exec.c  \
+                   target-arm/op_helper.c \
+                   target-arm/iwmmxt_helper.c \
+                   target-arm/neon_helper.c \
+                   target-arm/helper.c \
+                   target-arm/translate.c \
+                   target-arm/machine.c \
+                   translate-all.c \
+                   hw/armv7m.c \
+                   hw/armv7m_nvic.c \
+                   arm-semi.c \
+                   trace.c \
+                   varint.c \
+                   dcache.c \
+
+LOCAL_SRC_FILES += fpu/softfloat.c
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+##############################################################################
+# SDL-related definitions
+#
+
+SDL_CONFIG ?= prebuilt/$(HOST_PREBUILT_TAG)/sdl/bin/sdl-config
+SDL_CFLAGS := $(shell $(SDL_CONFIG) --cflags)
+
+# We need to filter out the _GNU_SOURCE variable because it breaks recent
+# releases of Cygwin when using the -mno-cygwin option. Moreover, we don't
+# need this macro at all to build the Android emulator.
+SDL_CFLAGS := $(filter-out -D_GNU_SOURCE=1,$(SDL_CFLAGS))
+SDL_LDLIBS := $(filter-out %.a %.lib,$(shell $(SDL_CONFIG) --static-libs))
+
+
+##############################################################################
+# determine audio sources, build the prebuilt audio-library if needed
+#
+
+# determine AUDIO sources based on current configuration
+#
+AUDIO_SOURCES := audio.c noaudio.c wavaudio.c sdlaudio.c wavcapture.c mixeng.c
+AUDIO_CFLAGS  := -I$(LOCAL_PATH)/audio -DHAS_AUDIO
+AUDIO_LDLIBS  :=
+
+ifeq ($(HOST_OS),darwin)
+  CONFIG_COREAUDIO ?= yes
+endif
+
+ifeq ($(HOST_OS),windows)
+  CONFIG_WINAUDIO ?= yes
+endif
+
+ifeq ($(HOST_OS),linux)
+  CONFIG_OSS  ?= yes
+  CONFIG_ALSA ?= yes
+  CONFIG_ESD  ?= yes
+endif
+
+ifeq ($(HOST_OS),freebsd)
+  CONFIG_OSS ?= yes
+endif
+
+ifeq ($(CONFIG_COREAUDIO),yes)
+  AUDIO_SOURCES += coreaudio.c
+  AUDIO_CFLAGS  += -DCONFIG_COREAUDIO
+  AUDIO_LDLIBS  += -Wl,-framework,CoreAudio
+endif
+
+ifeq ($(CONFIG_WINAUDIO),yes)
+  AUDIO_SOURCES += winaudio.c
+  AUDIO_CFLAGS  += -DCONFIG_WINAUDIO
+endif
+
+ifeq ($(CONFIG_ALSA),yes)
+  AUDIO_SOURCES += alsaaudio.c audio_pt_int.c
+  AUDIO_CFLAGS  += -DCONFIG_ALSA
+endif
+
+ifeq ($(CONFIG_ESD),yes)
+  AUDIO_SOURCES += esdaudio.c
+  AUDIO_CFLAGS  += -DCONFIG_ESD
+endif
+
+ifeq ($(CONFIG_OSS),yes)
+  AUDIO_SOURCES += ossaudio.c
+  AUDIO_CFLAGS  += -DCONFIG_OSS
+endif
+
+AUDIO_SOURCES := $(AUDIO_SOURCES:%=audio/%)
+
+# determine whether we're going to use the prebuilt
+# audio library (this is useful on Linux to avoid requiring
+# all sound-related development packages to be installed on
+# the build and developer machines).
+#
+# note that you can define BUILD_QEMU_AUDIO_LIB to true
+# in your environment to force recompilation.
+#
+QEMU_AUDIO_LIB :=
+
+ifneq ($(BUILD_STANDALONE_EMULATOR),true)
+  QEMU_AUDIO_LIB := $(wildcard \
+	prebuilt/$(HOST_PREBUILT_TAG)/emulator/libqemu-audio.a)
+endif
+
+ifeq ($(BUILD_QEMU_AUDIO_LIB),true)
+  include $(CLEAR_VARS)
+  LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+  LOCAL_CC                        := $(MY_CC)
+  LOCAL_MODULE                    := libqemu-audio
+  LOCAL_LDFLAGS                   := $(MY_LDFLAGS)
+
+  LOCAL_CFLAGS := -Wno-sign-compare \
+                  -fno-strict-aliasing -W -Wall -Wno-unused-parameter \
+                  -I$(LOCAL_PATH) \
+                  -I$(LOCAL_PATH)/target-arm \
+                  -I$(LOCAL_PATH)/fpu \
+
+  LOCAL_CFLAGS := $(MY_CFLAGS) $(LOCAL_CFLAGS) $(AUDIO_CFLAGS)
+
+  LOCAL_CFLAGS += $(SDL_CFLAGS)
+
+  LOCAL_SRC_FILES += $(AUDIO_SOURCES)
+
+  include $(BUILD_HOST_STATIC_LIBRARY)
+  QEMU_AUDIO_LIB := $(LOCAL_BUILT_MODULE)
+
+endif  # !QEMU_AUDIO_LIB
+
+##############################################################################
+# now build the emulator itself
+#
+include $(CLEAR_VARS)
+
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_CC                        := $(MY_CC)
+LOCAL_MODULE                    := emulator
+LOCAL_STATIC_LIBRARIES          := emulator-hw emulator-arm emulator-tcg
+LOCAL_LDFLAGS                   := $(MY_LDFLAGS)
+
+# don't remove the -fno-strict-aliasing, or you'll break things
+# (e.g. slirp2/network support)
+#
+LOCAL_CFLAGS := -fno-PIC -fomit-frame-pointer -Wno-sign-compare \
+                -fno-strict-aliasing -g -W -Wall -Wno-unused-parameter
+
+LOCAL_CFLAGS := $(MY_CFLAGS) $(LOCAL_CFLAGS)
+
+# add the build ID to the default macro definitions
+LOCAL_CFLAGS += -DANDROID_BUILD_ID="$(strip $(BUILD_ID))-$(strip $(BUILD_NUMBER))"
+
+# include the Zlib sources
+#
+LOCAL_SRC_FILES += $(ZLIB_SOURCES)
+LOCAL_CFLAGS    += $(ZLIB_CFLAGS) -I$(LOCAL_PATH)/$(ZLIB_DIR)
+
+# include the Libpng sources
+#
+LOCAL_SRC_FILES += $(LIBPNG_SOURCES)
+LOCAL_CFLAGS    += $(LIBPNG_CFLAGS) -I$(LOCAL_PATH)/$(LIBPNG_DIR)
+
+LOCAL_CFLAGS    += -I$(LOCAL_PATH)/ \
+                   -I$(LOCAL_PATH)/target-arm \
+                   -I$(LOCAL_PATH)/fpu \
+                   $(TCG_CFLAGS) \
+                   $(HW_CFLAGS) \
+
+# include telephony stuff
+#
+TELEPHONY_SOURCES := android_modem.c modem_driver.c gsm.c sim_card.c sysdeps_qemu.c sms.c remote_call.c
+LOCAL_SRC_FILES += $(TELEPHONY_SOURCES:%=telephony/%)
+LOCAL_CFLAGS    += -I$(LOCAL_PATH)/telephony
+
+# include sound support source files. we first try to see if we have a prebuilt audio
+# library. if not, we build things the "hard" way.
+#
+# note that to generate the prebuilt audio library, you should do the following:
+#
+#   cd tools/qemu
+#   ./android-rebuild.sh
+#   distrib/update-audio.sh
+#
+ifeq ($(QEMU_AUDIO_LIB),)
+  LOCAL_SRC_FILES += $(AUDIO_SOURCES)
+endif  # !QEMU_AUDIO_LIB
+
+LOCAL_CFLAGS  += $(AUDIO_CFLAGS)
+LOCAL_LDLIBS  += $(AUDIO_LDLIBS)
+
+# include slirp2 code, i.e. the user-level networking stuff
+#
+SLIRP_SOURCES := bootp.c     cksum.c      debug.c  if.c     ip_icmp.c  ip_input.c   ip_output.c  \
+                 mbuf.c      misc.c       sbuf.c   slirp.c  socket.c   tcp_input.c  tcp_output.c \
+                 tcp_subr.c  tcp_timer.c  tftp.c   udp.c
+
+LOCAL_SRC_FILES += $(SLIRP_SOURCES:%=slirp2/%)
+LOCAL_CFLAGS    += -I$(LOCAL_PATH)/slirp2
+
+# socket proxy support
+#
+PROXY_SOURCES := \
+    proxy_common.c \
+    proxy_http.c \
+    proxy_http_connector.c \
+    proxy_http_rewriter.c \
+
+LOCAL_SRC_FILES += $(PROXY_SOURCES:%=proxy/%)
+LOCAL_CFLAGS    += -I$(LOCAL_PATH)/proxy
+
+# the linux-user sources, I doubt we really need these
+#
+#LINUX_SOURCES := main.c elfload.c mmap.c signal.c path.c syscall.c
+#LOCAL_SRC_FILES += $(LINUX_SOURCES:%=linux-user/%)
+
+# the skin support sources
+#
+SKIN_SOURCES := rect.c \
+                region.c \
+                image.c \
+                trackball.c \
+                keyboard.c \
+                keyset.c \
+                file.c \
+                window.c \
+                scaler.c \
+                composer.c \
+                surface.c \
+
+LOCAL_SRC_FILES += $(SKIN_SOURCES:%=android/skin/%)
+#LOCAL_CFLAGS    += -I$(LOCAL_PATH)/skin
+
+ifeq ($(HOST_ARCH),x86)
+# enable MMX code for our skin scaler
+LOCAL_CFLAGS += -DUSE_MMX=1 -mmmx
+endif
+
+# include other sources
+#
+VL_SOURCES := vl.c osdep.c cutils.c \
+              block.c readline.c monitor.c console.c loader.c sockets.c \
+              block-qcow.c aes.c d3des.c block-cloop.c block-dmg.c block-vvfat.c \
+              block-qcow2.c block-cow.c \
+              cbuffer.c \
+              gdbstub.c usb-linux.c \
+              vnc.c disas.c arm-dis.c \
+              shaper.c charpipe.c loadpng.c \
+              framebuffer.c \
+              tcpdump.c \
+              android/charmap.c \
+              android/cmdline-option.c \
+              android/config.c \
+              android/console.c \
+              android/gps.c \
+              android/help.c \
+              android/hw-control.c \
+              android/hw-events.c \
+              android/hw-kmsg.c \
+              android/main.c \
+              android/qemud.c \
+              android/resource.c \
+              android/user-config.c \
+              android/utils/bufprint.c \
+              android/utils/debug.c \
+              android/utils/dirscanner.c \
+              android/utils/display.c \
+              android/utils/ini.c \
+              android/utils/filelock.c \
+              android/utils/misc.c \
+              android/utils/path.c \
+              android/utils/reflist.c \
+              android/utils/stralloc.c \
+              android/utils/system.c \
+              android/utils/tempfile.c \
+              android/utils/timezone.c \
+              android/avd/hw-config.c \
+              android/avd/info.c \
+
+# we need to add a Quartz-specific file
+ifeq ($(HOST_OS),darwin)
+  # Alas, the Android build system doesn't know how to deal
+  # with Objective C sources yet.
+  ifeq ($(BUILD_STANDALONE_EMULATOR),true)
+    VL_SOURCES += android/utils/display-quartz.m
+  else
+    LOCAL_CFLAGS += -DCONFIG_NO_COCOA
+  endif
+endif
+
+VL_SOURCES += hw/arm_boot.c \
+              hw/android_arm.c \
+
+ifeq ($(HOST_OS),windows)
+  VL_SOURCES += block-raw-win32.c
+else
+  VL_SOURCES += block-raw-posix.c
+endif
+
+ifeq ($(HOST_OS),linux)
+    LOCAL_LDLIBS += -lX11
+endif
+
+ifeq ($(HOST_ARCH),x86)
+    VL_SOURCES += i386-dis.c
+endif
+ifeq ($(HOST_ARCH),x86_64)
+    VL_SOURCES += i386-dis.c
+endif
+ifeq ($(HOST_ARCH),ppc)
+    VL_SOURCES += ppc-dis.c
+endif
+
+ifeq ($(HOST_OS),windows)
+  #VL_SOURCES   += tap-win32.c
+  LOCAL_LDLIBS += -mno-cygwin -mwindows -mconsole
+endif
+
+LOCAL_SRC_FILES += $(VL_SOURCES)
+
+ifeq ($(HOST_OS),linux)
+  LOCAL_LDLIBS += -lutil -lrt
+endif
+
+# add SDL-specific flags
+#
+LOCAL_CFLAGS += $(SDL_CFLAGS)
+LOCAL_LDLIBS += $(SDL_LDLIBS)
+LOCAL_STATIC_LIBRARIES += libSDL libSDLmain
+LOCAL_STATIC_LIBRARIES += libSDL libSDLmain
+
+# on Windows, link the icon file as well into the executable
+# unfortunately, our build system doesn't help us much, so we need
+# to use some weird pathnames to make this work...
+#
+ifeq ($(HOST_OS),windows)
+INTERMEDIATE     := $(call intermediates-dir-for,EXECUTABLES,$(LOCAL_MODULE),true)
+ANDROID_ICON_OBJ := android_icon.o
+ANDROID_ICON_PATH := $(LOCAL_PATH)/images
+$(ANDROID_ICON_PATH)/$(ANDROID_ICON_OBJ): $(ANDROID_ICON_PATH)/android_icon.rc
+	windres $< -I $(ANDROID_ICON_PATH) -o $@
+
+# seems to be the only way to add an object file that was not generated from
+# a C/C++/Java source file to our build system. and very unfortunately,
+# $(TOPDIR)/$(LOCALPATH) will always be prepended to this value, which forces
+# us to put the object file in the source directory...
+#
+LOCAL_PREBUILT_OBJ_FILES += images/$(ANDROID_ICON_OBJ)
+endif
+
+# other flags
+LOCAL_CFLAGS += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
+LOCAL_LDLIBS += -lm -lpthread
+
+ifeq ($(HOST_OS),windows)
+    LOCAL_LDLIBS += -lwinmm -lws2_32 -liphlpapi
+endif
+
+LOCAL_LDLIBS += $(QEMU_AUDIO_LIB)
+
+LOCAL_MODULE := emulator
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif  # TARGET_ARCH == arm
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..e77696a
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,339 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644
index 0000000..44ddc25
--- /dev/null
+++ b/README
@@ -0,0 +1,23 @@
+This package contains the sources to the Android emulator program.
+
+This program emulates a virtual ARM board that can be used to run Android
+system images on a typical developer machine. To do so, you'll need additionnal
+files provided with the public Android Software Development Kit (SDK).
+
+To download them, go to http://code.google.com/android/
+
+Emulator-specific documentation is available at the following page:
+
+    http://code.google.com/android/reference/emulator.html
+
+Please read the INSTALL file to see how you can rebuild the emulator, or
+build a source distribution package tarball.
+
+Read the CHANGES.TXT file to see what important changes were added since
+the last release.
+
+Note: This program is distributed under the terms of the GNU General Public
+      License, which exact licensing conditions are available in the COPYING
+      file found within this package.
+
+- Android Emulator Team
diff --git a/a.out.h b/a.out.h
new file mode 100644
index 0000000..2d35ebf
--- /dev/null
+++ b/a.out.h
@@ -0,0 +1,431 @@
+/* a.out.h
+
+   Copyright 1997, 1998, 1999, 2001 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _A_OUT_H_
+#define _A_OUT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define COFF_IMAGE_WITH_PE
+#define COFF_LONG_SECTION_NAMES
+
+/*** coff information for Intel 386/486.  */
+
+
+/********************** FILE HEADER **********************/
+
+struct external_filehdr {
+  short f_magic;	/* magic number			*/
+  short f_nscns;	/* number of sections		*/
+  host_ulong f_timdat;	/* time & date stamp		*/
+  host_ulong f_symptr;	/* file pointer to symtab	*/
+  host_ulong f_nsyms;	/* number of symtab entries	*/
+  short f_opthdr;	/* sizeof(optional hdr)		*/
+  short f_flags;	/* flags			*/
+};
+
+/* Bits for f_flags:
+ *	F_RELFLG	relocation info stripped from file
+ *	F_EXEC		file is executable (no unresolved external references)
+ *	F_LNNO		line numbers stripped from file
+ *	F_LSYMS		local symbols stripped from file
+ *	F_AR32WR	file has byte ordering of an AR32WR machine (e.g. vax)
+ */
+
+#define F_RELFLG	(0x0001)
+#define F_EXEC		(0x0002)
+#define F_LNNO		(0x0004)
+#define F_LSYMS		(0x0008)
+
+
+
+#define	I386MAGIC	0x14c
+#define I386PTXMAGIC	0x154
+#define I386AIXMAGIC	0x175
+
+/* This is Lynx's all-platform magic number for executables. */
+
+#define LYNXCOFFMAGIC	0415
+
+#define I386BADMAG(x) (((x).f_magic != I386MAGIC) \
+		       && (x).f_magic != I386AIXMAGIC \
+		       && (x).f_magic != I386PTXMAGIC \
+		       && (x).f_magic != LYNXCOFFMAGIC)
+
+#define	FILHDR	struct external_filehdr
+#define	FILHSZ	20
+
+
+/********************** AOUT "OPTIONAL HEADER"=
+ **********************/
+
+
+typedef struct
+{
+  unsigned short magic;		/* type of file				*/
+  unsigned short vstamp;	/* version stamp			*/
+  host_ulong	tsize;		/* text size in bytes, padded to FW bdry*/
+  host_ulong	dsize;		/* initialized data "  "		*/
+  host_ulong	bsize;		/* uninitialized data "   "		*/
+  host_ulong	entry;		/* entry pt.				*/
+  host_ulong text_start;	/* base of text used for this file */
+  host_ulong data_start;	/* base of data used for this file=
+ */
+}
+AOUTHDR;
+
+#define AOUTSZ 28
+#define AOUTHDRSZ 28
+
+#define OMAGIC          0404    /* object files, eg as output */
+#define ZMAGIC          0413    /* demand load format, eg normal ld output */
+#define STMAGIC		0401	/* target shlib */
+#define SHMAGIC		0443	/* host   shlib */
+
+
+/* define some NT default values */
+/*  #define NT_IMAGE_BASE        0x400000 moved to internal.h */
+#define NT_SECTION_ALIGNMENT 0x1000
+#define NT_FILE_ALIGNMENT    0x200
+#define NT_DEF_RESERVE       0x100000
+#define NT_DEF_COMMIT        0x1000
+
+/********************** SECTION HEADER **********************/
+
+
+struct external_scnhdr {
+  char		s_name[8];	/* section name			*/
+  host_ulong	s_paddr;	/* physical address, offset
+				   of last addr in scn */
+  host_ulong	s_vaddr;	/* virtual address		*/
+  host_ulong	s_size;		/* section size			*/
+  host_ulong	s_scnptr;	/* file ptr to raw data for section */
+  host_ulong	s_relptr;	/* file ptr to relocation	*/
+  host_ulong	s_lnnoptr;	/* file ptr to line numbers	*/
+  unsigned short s_nreloc;	/* number of relocation entries	*/
+  unsigned short s_nlnno;	/* number of line number entries*/
+  host_ulong	s_flags;	/* flags			*/
+};
+
+#define	SCNHDR	struct external_scnhdr
+#define	SCNHSZ	40
+
+/*
+ * names of "special" sections
+ */
+#define _TEXT	".text"
+#define _DATA	".data"
+#define _BSS	".bss"
+#define _COMMENT ".comment"
+#define _LIB ".lib"
+
+/********************** LINE NUMBERS **********************/
+
+/* 1 line number entry for every "breakpointable" source line in a section.
+ * Line numbers are grouped on a per function basis; first entry in a function
+ * grouping will have l_lnno = 0 and in place of physical address will be the
+ * symbol table index of the function name.
+ */
+struct external_lineno {
+  union {
+    host_ulong l_symndx; /* function name symbol index, iff l_lnno 0 */
+    host_ulong l_paddr;	/* (physical) address of line number	*/
+  } l_addr;
+  unsigned short l_lnno;	/* line number		*/
+};
+
+#define	LINENO	struct external_lineno
+#define	LINESZ	6
+
+/********************** SYMBOLS **********************/
+
+#define E_SYMNMLEN	8	/* # characters in a symbol name	*/
+#define E_FILNMLEN	14	/* # characters in a file name		*/
+#define E_DIMNUM	4	/* # array dimensions in auxiliary entry */
+
+struct __attribute__((packed)) external_syment
+{
+  union {
+    char e_name[E_SYMNMLEN];
+    struct {
+      host_ulong e_zeroes;
+      host_ulong e_offset;
+    } e;
+  } e;
+  host_ulong e_value;
+  unsigned short e_scnum;
+  unsigned short e_type;
+  char e_sclass[1];
+  char e_numaux[1];
+};
+
+#define N_BTMASK	(0xf)
+#define N_TMASK		(0x30)
+#define N_BTSHFT	(4)
+#define N_TSHIFT	(2)
+
+union external_auxent {
+  struct {
+    host_ulong x_tagndx;	/* str, un, or enum tag indx */
+    union {
+      struct {
+	unsigned short  x_lnno; /* declaration line number */
+	unsigned short  x_size; /* str/union/array size */
+      } x_lnsz;
+      host_ulong x_fsize;	/* size of function */
+    } x_misc;
+    union {
+      struct {			/* if ISFCN, tag, or .bb */
+	host_ulong x_lnnoptr;/* ptr to fcn line # */
+	host_ulong x_endndx;	/* entry ndx past block end */
+      } x_fcn;
+      struct {			/* if ISARY, up to 4 dimen. */
+	char x_dimen[E_DIMNUM][2];
+      } x_ary;
+    } x_fcnary;
+    unsigned short x_tvndx;	/* tv index */
+  } x_sym;
+
+  union {
+    char x_fname[E_FILNMLEN];
+    struct {
+      host_ulong x_zeroes;
+      host_ulong x_offset;
+    } x_n;
+  } x_file;
+
+  struct {
+    host_ulong x_scnlen;	/* section length */
+    unsigned short x_nreloc;	/* # relocation entries */
+    unsigned short x_nlinno;	/* # line numbers */
+    host_ulong x_checksum;	/* section COMDAT checksum */
+    unsigned short x_associated;/* COMDAT associated section index */
+    char x_comdat[1];		/* COMDAT selection number */
+  } x_scn;
+
+  struct {
+    host_ulong x_tvfill;	/* tv fill value */
+    unsigned short x_tvlen;	/* length of .tv */
+    char x_tvran[2][2];		/* tv range */
+  } x_tv;	/* info about .tv section (in auxent of symbol .tv)) */
+
+};
+
+#define	SYMENT	struct external_syment
+#define	SYMESZ	18
+#define	AUXENT	union external_auxent
+#define	AUXESZ	18
+
+#define _ETEXT	"etext"
+
+/********************** RELOCATION DIRECTIVES **********************/
+
+struct external_reloc {
+  char r_vaddr[4];
+  char r_symndx[4];
+  char r_type[2];
+};
+
+#define RELOC struct external_reloc
+#define RELSZ 10
+
+/* end of coff/i386.h */
+
+/* PE COFF header information */
+
+#ifndef _PE_H
+#define _PE_H
+
+/* NT specific file attributes */
+#define IMAGE_FILE_RELOCS_STRIPPED           0x0001
+#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002
+#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004
+#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008
+#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080
+#define IMAGE_FILE_32BIT_MACHINE             0x0100
+#define IMAGE_FILE_DEBUG_STRIPPED            0x0200
+#define IMAGE_FILE_SYSTEM                    0x1000
+#define IMAGE_FILE_DLL                       0x2000
+#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000
+
+/* additional flags to be set for section headers to allow the NT loader to
+   read and write to the section data (to replace the addresses of data in
+   dlls for one thing); also to execute the section in .text's case=
+ */
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_EXECUTE     0x20000000
+#define IMAGE_SCN_MEM_READ        0x40000000
+#define IMAGE_SCN_MEM_WRITE       0x80000000
+
+/*
+ * Section characteristics added for ppc-nt
+ */
+
+#define IMAGE_SCN_TYPE_NO_PAD                0x00000008  /* Reserved.  */
+
+#define IMAGE_SCN_CNT_CODE                   0x00000020  /* Section contains code. */
+#define IMAGE_SCN_CNT_INITIALIZED_DATA       0x00000040  /* Section contains initialized data. */
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA     0x00000080  /* Section contains uninitialized data. */
+
+#define IMAGE_SCN_LNK_OTHER                  0x00000100  /* Reserved.  */
+#define IMAGE_SCN_LNK_INFO                   0x00000200  /* Section contains comments or some other type of information. */
+#define IMAGE_SCN_LNK_REMOVE                 0x00000800  /* Section contents will not become part of image. */
+#define IMAGE_SCN_LNK_COMDAT                 0x00001000  /* Section contents comdat. */
+
+#define IMAGE_SCN_MEM_FARDATA                0x00008000
+
+#define IMAGE_SCN_MEM_PURGEABLE              0x00020000
+#define IMAGE_SCN_MEM_16BIT                  0x00020000
+#define IMAGE_SCN_MEM_LOCKED                 0x00040000
+#define IMAGE_SCN_MEM_PRELOAD                0x00080000
+
+#define IMAGE_SCN_ALIGN_1BYTES               0x00100000
+#define IMAGE_SCN_ALIGN_2BYTES               0x00200000
+#define IMAGE_SCN_ALIGN_4BYTES               0x00300000
+#define IMAGE_SCN_ALIGN_8BYTES               0x00400000
+#define IMAGE_SCN_ALIGN_16BYTES              0x00500000  /* Default alignment if no others are specified. */
+#define IMAGE_SCN_ALIGN_32BYTES              0x00600000
+#define IMAGE_SCN_ALIGN_64BYTES              0x00700000
+
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL            0x01000000  /* Section contains extended relocations. */
+#define IMAGE_SCN_MEM_NOT_CACHED             0x04000000  /* Section is not cachable.               */
+#define IMAGE_SCN_MEM_NOT_PAGED              0x08000000  /* Section is not pageable.               */
+#define IMAGE_SCN_MEM_SHARED                 0x10000000  /* Section is shareable.                  */
+
+/* COMDAT selection codes.  */
+
+#define IMAGE_COMDAT_SELECT_NODUPLICATES     (1) /* Warn if duplicates.  */
+#define IMAGE_COMDAT_SELECT_ANY		     (2) /* No warning.  */
+#define IMAGE_COMDAT_SELECT_SAME_SIZE	     (3) /* Warn if different size.  */
+#define IMAGE_COMDAT_SELECT_EXACT_MATCH	     (4) /* Warn if different.  */
+#define IMAGE_COMDAT_SELECT_ASSOCIATIVE	     (5) /* Base on other section.  */
+
+/* Magic values that are true for all dos/nt implementations */
+#define DOSMAGIC       0x5a4d
+#define NT_SIGNATURE   0x00004550
+
+/* NT allows long filenames, we want to accommodate this.  This may break
+     some of the bfd functions */
+#undef  FILNMLEN
+#define FILNMLEN	18	/* # characters in a file name		*/
+
+
+#ifdef COFF_IMAGE_WITH_PE
+/* The filehdr is only weired in images */
+
+#undef FILHDR
+struct external_PE_filehdr
+{
+  /* DOS header fields */
+  unsigned short e_magic;	/* Magic number, 0x5a4d */
+  unsigned short e_cblp;	/* Bytes on last page of file, 0x90 */
+  unsigned short e_cp;		/* Pages in file, 0x3 */
+  unsigned short e_crlc;	/* Relocations, 0x0 */
+  unsigned short e_cparhdr;	/* Size of header in paragraphs, 0x4 */
+  unsigned short e_minalloc;	/* Minimum extra paragraphs needed, 0x0 */
+  unsigned short e_maxalloc;	/* Maximum extra paragraphs needed, 0xFFFF */
+  unsigned short e_ss;		/* Initial (relative) SS value, 0x0 */
+  unsigned short e_sp;		/* Initial SP value, 0xb8 */
+  unsigned short e_csum;	/* Checksum, 0x0 */
+  unsigned short e_ip;		/* Initial IP value, 0x0 */
+  unsigned short e_cs;		/* Initial (relative) CS value, 0x0 */
+  unsigned short e_lfarlc;	/* File address of relocation table, 0x40 */
+  unsigned short e_ovno;	/* Overlay number, 0x0 */
+  char e_res[4][2];		/* Reserved words, all 0x0 */
+  unsigned short e_oemid;	/* OEM identifier (for e_oeminfo), 0x0 */
+  unsigned short e_oeminfo;	/* OEM information; e_oemid specific, 0x0 */
+  char e_res2[10][2];		/* Reserved words, all 0x0 */
+  host_ulong e_lfanew;	/* File address of new exe header, 0x80 */
+  char dos_message[16][4];	/* other stuff, always follow DOS header */
+  unsigned int nt_signature;	/* required NT signature, 0x4550 */
+
+  /* From standard header */
+
+  unsigned short f_magic;	/* magic number			*/
+  unsigned short f_nscns;	/* number of sections		*/
+  host_ulong f_timdat;	/* time & date stamp		*/
+  host_ulong f_symptr;	/* file pointer to symtab	*/
+  host_ulong f_nsyms;	/* number of symtab entries	*/
+  unsigned short f_opthdr;	/* sizeof(optional hdr)		*/
+  unsigned short f_flags;	/* flags			*/
+};
+
+
+#define FILHDR struct external_PE_filehdr
+#undef FILHSZ
+#define FILHSZ 152
+
+#endif
+
+typedef struct
+{
+  unsigned short magic;		/* type of file				*/
+  unsigned short vstamp;	/* version stamp			*/
+  host_ulong	tsize;		/* text size in bytes, padded to FW bdry*/
+  host_ulong	dsize;		/* initialized data "  "		*/
+  host_ulong	bsize;		/* uninitialized data "   "		*/
+  host_ulong	entry;		/* entry pt.				*/
+  host_ulong text_start;	/* base of text used for this file */
+  host_ulong data_start;	/* base of all data used for this file */
+
+  /* NT extra fields; see internal.h for descriptions */
+  host_ulong  ImageBase;
+  host_ulong  SectionAlignment;
+  host_ulong  FileAlignment;
+  unsigned short  MajorOperatingSystemVersion;
+  unsigned short  MinorOperatingSystemVersion;
+  unsigned short  MajorImageVersion;
+  unsigned short  MinorImageVersion;
+  unsigned short  MajorSubsystemVersion;
+  unsigned short  MinorSubsystemVersion;
+  char  Reserved1[4];
+  host_ulong  SizeOfImage;
+  host_ulong  SizeOfHeaders;
+  host_ulong  CheckSum;
+  unsigned short Subsystem;
+  unsigned short DllCharacteristics;
+  host_ulong  SizeOfStackReserve;
+  host_ulong  SizeOfStackCommit;
+  host_ulong  SizeOfHeapReserve;
+  host_ulong  SizeOfHeapCommit;
+  host_ulong  LoaderFlags;
+  host_ulong  NumberOfRvaAndSizes;
+  /* IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; */
+  char  DataDirectory[16][2][4]; /* 16 entries, 2 elements/entry, 4 chars */
+
+} PEAOUTHDR;
+
+
+#undef AOUTSZ
+#define AOUTSZ (AOUTHDRSZ + 196)
+
+#undef  E_FILNMLEN
+#define E_FILNMLEN	18	/* # characters in a file name		*/
+#endif
+
+/* end of coff/pe.h */
+
+#define DT_NON		(0)	/* no derived type */
+#define DT_PTR		(1)	/* pointer */
+#define DT_FCN		(2)	/* function */
+#define DT_ARY		(3)	/* array */
+
+#define ISPTR(x)	(((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
+#define ISFCN(x)	(((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
+#define ISARY(x)	(((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _A_OUT_H_ */
+
diff --git a/aes.c b/aes.c
new file mode 100644
index 0000000..3700894
--- /dev/null
+++ b/aes.c
@@ -0,0 +1,1320 @@
+/**
+ *
+ * aes.c - integrated in QEMU by Fabrice Bellard from the OpenSSL project.
+ */
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "qemu-common.h"
+#include "aes.h"
+
+#ifndef NDEBUG
+#define NDEBUG
+#endif
+
+#include <assert.h>
+
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+
+#define MAXKC   (256/32)
+#define MAXKB   (256/8)
+#define MAXNR   14
+
+/* This controls loop-unrolling in aes_core.c */
+#undef FULL_UNROLL
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+static const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+static const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+static const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+static const u32 Te3[256] = {
+
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+static const u32 Te4[256] = {
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+static const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+static const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+static const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+static const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+static const u32 Td4[256] = {
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+static const u32 rcon[] = {
+	0x01000000, 0x02000000, 0x04000000, 0x08000000,
+	0x10000000, 0x20000000, 0x40000000, 0x80000000,
+	0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+
+/**
+ * Expand the cipher key into the encryption key schedule.
+ */
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+			AES_KEY *key) {
+
+	u32 *rk;
+   	int i = 0;
+	u32 temp;
+
+	if (!userKey || !key)
+		return -1;
+	if (bits != 128 && bits != 192 && bits != 256)
+		return -2;
+
+	rk = key->rd_key;
+
+	if (bits==128)
+		key->rounds = 10;
+	else if (bits==192)
+		key->rounds = 12;
+	else
+		key->rounds = 14;
+
+	rk[0] = GETU32(userKey     );
+	rk[1] = GETU32(userKey +  4);
+	rk[2] = GETU32(userKey +  8);
+	rk[3] = GETU32(userKey + 12);
+	if (bits == 128) {
+		while (1) {
+			temp  = rk[3];
+			rk[4] = rk[0] ^
+				(Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp >> 24)       ] & 0x000000ff) ^
+				rcon[i];
+			rk[5] = rk[1] ^ rk[4];
+			rk[6] = rk[2] ^ rk[5];
+			rk[7] = rk[3] ^ rk[6];
+			if (++i == 10) {
+				return 0;
+			}
+			rk += 4;
+		}
+	}
+	rk[4] = GETU32(userKey + 16);
+	rk[5] = GETU32(userKey + 20);
+	if (bits == 192) {
+		while (1) {
+			temp = rk[ 5];
+			rk[ 6] = rk[ 0] ^
+				(Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp >> 24)       ] & 0x000000ff) ^
+				rcon[i];
+			rk[ 7] = rk[ 1] ^ rk[ 6];
+			rk[ 8] = rk[ 2] ^ rk[ 7];
+			rk[ 9] = rk[ 3] ^ rk[ 8];
+			if (++i == 8) {
+				return 0;
+			}
+			rk[10] = rk[ 4] ^ rk[ 9];
+			rk[11] = rk[ 5] ^ rk[10];
+			rk += 6;
+		}
+	}
+	rk[6] = GETU32(userKey + 24);
+	rk[7] = GETU32(userKey + 28);
+	if (bits == 256) {
+		while (1) {
+			temp = rk[ 7];
+			rk[ 8] = rk[ 0] ^
+				(Te4[(temp >> 16) & 0xff] & 0xff000000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp      ) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp >> 24)       ] & 0x000000ff) ^
+				rcon[i];
+			rk[ 9] = rk[ 1] ^ rk[ 8];
+			rk[10] = rk[ 2] ^ rk[ 9];
+			rk[11] = rk[ 3] ^ rk[10];
+			if (++i == 7) {
+				return 0;
+			}
+			temp = rk[11];
+			rk[12] = rk[ 4] ^
+				(Te4[(temp >> 24)       ] & 0xff000000) ^
+				(Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^
+				(Te4[(temp >>  8) & 0xff] & 0x0000ff00) ^
+				(Te4[(temp      ) & 0xff] & 0x000000ff);
+			rk[13] = rk[ 5] ^ rk[12];
+			rk[14] = rk[ 6] ^ rk[13];
+			rk[15] = rk[ 7] ^ rk[14];
+
+			rk += 8;
+        	}
+	}
+	return 0;
+}
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ */
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+			 AES_KEY *key) {
+
+        u32 *rk;
+	int i, j, status;
+	u32 temp;
+
+	/* first, start with an encryption schedule */
+	status = AES_set_encrypt_key(userKey, bits, key);
+	if (status < 0)
+		return status;
+
+	rk = key->rd_key;
+
+	/* invert the order of the round keys: */
+	for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {
+		temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+		temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+		temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+		temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+	}
+	/* apply the inverse MixColumn transform to all round keys but the first and the last: */
+	for (i = 1; i < (key->rounds); i++) {
+		rk += 4;
+		rk[0] =
+			Td0[Te4[(rk[0] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[0] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[0]      ) & 0xff] & 0xff];
+		rk[1] =
+			Td0[Te4[(rk[1] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[1] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[1]      ) & 0xff] & 0xff];
+		rk[2] =
+			Td0[Te4[(rk[2] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[2] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[2]      ) & 0xff] & 0xff];
+		rk[3] =
+			Td0[Te4[(rk[3] >> 24)       ] & 0xff] ^
+			Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^
+			Td2[Te4[(rk[3] >>  8) & 0xff] & 0xff] ^
+			Td3[Te4[(rk[3]      ) & 0xff] & 0xff];
+	}
+	return 0;
+}
+
+#ifndef AES_ASM
+/*
+ * Encrypt a single block
+ * in and out can overlap
+ */
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+		 const AES_KEY *key) {
+
+	const u32 *rk;
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	assert(in && out && key);
+	rk = key->rd_key;
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+	s0 = GETU32(in     ) ^ rk[0];
+	s1 = GETU32(in +  4) ^ rk[1];
+	s2 = GETU32(in +  8) ^ rk[2];
+	s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+	/* round 1: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];
+   	/* round 2: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];
+	/* round 3: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];
+   	/* round 4: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];
+	/* round 5: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];
+   	/* round 6: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];
+	/* round 7: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];
+   	/* round 8: */
+   	s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];
+   	s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];
+   	s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];
+   	s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];
+	/* round 9: */
+   	t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];
+   	t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];
+   	t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];
+   	t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];
+        s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];
+        s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];
+        s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];
+        t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];
+        t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];
+        t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >>  8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];
+            s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >>  8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];
+            s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >>  8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];
+            s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >>  8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >>  8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];
+            t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >>  8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];
+            t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >>  8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];
+            t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >>  8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];
+        }
+    }
+    rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Te0[(s0 >> 24)       ] ^
+            Te1[(s1 >> 16) & 0xff] ^
+            Te2[(s2 >>  8) & 0xff] ^
+            Te3[(s3      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Te0[(s1 >> 24)       ] ^
+            Te1[(s2 >> 16) & 0xff] ^
+            Te2[(s3 >>  8) & 0xff] ^
+            Te3[(s0      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Te0[(s2 >> 24)       ] ^
+            Te1[(s3 >> 16) & 0xff] ^
+            Te2[(s0 >>  8) & 0xff] ^
+            Te3[(s1      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Te0[(s3 >> 24)       ] ^
+            Te1[(s0 >> 16) & 0xff] ^
+            Te2[(s1 >>  8) & 0xff] ^
+            Te3[(s2      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Te0[(t0 >> 24)       ] ^
+            Te1[(t1 >> 16) & 0xff] ^
+            Te2[(t2 >>  8) & 0xff] ^
+            Te3[(t3      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Te0[(t1 >> 24)       ] ^
+            Te1[(t2 >> 16) & 0xff] ^
+            Te2[(t3 >>  8) & 0xff] ^
+            Te3[(t0      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Te0[(t2 >> 24)       ] ^
+            Te1[(t3 >> 16) & 0xff] ^
+            Te2[(t0 >>  8) & 0xff] ^
+            Te3[(t1      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Te0[(t3 >> 24)       ] ^
+            Te1[(t0 >> 16) & 0xff] ^
+            Te2[(t1 >>  8) & 0xff] ^
+            Te3[(t2      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+	s0 =
+		(Te4[(t0 >> 24)       ] & 0xff000000) ^
+		(Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t3      ) & 0xff] & 0x000000ff) ^
+		rk[0];
+	PUTU32(out     , s0);
+	s1 =
+		(Te4[(t1 >> 24)       ] & 0xff000000) ^
+		(Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t0      ) & 0xff] & 0x000000ff) ^
+		rk[1];
+	PUTU32(out +  4, s1);
+	s2 =
+		(Te4[(t2 >> 24)       ] & 0xff000000) ^
+		(Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t1      ) & 0xff] & 0x000000ff) ^
+		rk[2];
+	PUTU32(out +  8, s2);
+	s3 =
+		(Te4[(t3 >> 24)       ] & 0xff000000) ^
+		(Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+		(Te4[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+		(Te4[(t2      ) & 0xff] & 0x000000ff) ^
+		rk[3];
+	PUTU32(out + 12, s3);
+}
+
+/*
+ * Decrypt a single block
+ * in and out can overlap
+ */
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+		 const AES_KEY *key) {
+
+	const u32 *rk;
+	u32 s0, s1, s2, s3, t0, t1, t2, t3;
+#ifndef FULL_UNROLL
+	int r;
+#endif /* ?FULL_UNROLL */
+
+	assert(in && out && key);
+	rk = key->rd_key;
+
+	/*
+	 * map byte array block to cipher state
+	 * and add initial round key:
+	 */
+    s0 = GETU32(in     ) ^ rk[0];
+    s1 = GETU32(in +  4) ^ rk[1];
+    s2 = GETU32(in +  8) ^ rk[2];
+    s3 = GETU32(in + 12) ^ rk[3];
+#ifdef FULL_UNROLL
+    /* round 1: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];
+    /* round 2: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];
+    /* round 3: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];
+    /* round 4: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];
+    /* round 5: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];
+    /* round 6: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];
+    /* round 7: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];
+    /* round 8: */
+    s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];
+    s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];
+    s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];
+    s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];
+    /* round 9: */
+    t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];
+    t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];
+    t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];
+    t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];
+    if (key->rounds > 10) {
+        /* round 10: */
+        s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];
+        s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];
+        s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];
+        s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];
+        /* round 11: */
+        t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];
+        t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];
+        t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];
+        t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];
+        if (key->rounds > 12) {
+            /* round 12: */
+            s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >>  8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];
+            s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >>  8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];
+            s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >>  8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];
+            s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >>  8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];
+            /* round 13: */
+            t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >>  8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];
+            t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >>  8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];
+            t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >>  8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];
+            t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >>  8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];
+        }
+    }
+	rk += key->rounds << 2;
+#else  /* !FULL_UNROLL */
+    /*
+     * Nr - 1 full rounds:
+     */
+    r = key->rounds >> 1;
+    for (;;) {
+        t0 =
+            Td0[(s0 >> 24)       ] ^
+            Td1[(s3 >> 16) & 0xff] ^
+            Td2[(s2 >>  8) & 0xff] ^
+            Td3[(s1      ) & 0xff] ^
+            rk[4];
+        t1 =
+            Td0[(s1 >> 24)       ] ^
+            Td1[(s0 >> 16) & 0xff] ^
+            Td2[(s3 >>  8) & 0xff] ^
+            Td3[(s2      ) & 0xff] ^
+            rk[5];
+        t2 =
+            Td0[(s2 >> 24)       ] ^
+            Td1[(s1 >> 16) & 0xff] ^
+            Td2[(s0 >>  8) & 0xff] ^
+            Td3[(s3      ) & 0xff] ^
+            rk[6];
+        t3 =
+            Td0[(s3 >> 24)       ] ^
+            Td1[(s2 >> 16) & 0xff] ^
+            Td2[(s1 >>  8) & 0xff] ^
+            Td3[(s0      ) & 0xff] ^
+            rk[7];
+
+        rk += 8;
+        if (--r == 0) {
+            break;
+        }
+
+        s0 =
+            Td0[(t0 >> 24)       ] ^
+            Td1[(t3 >> 16) & 0xff] ^
+            Td2[(t2 >>  8) & 0xff] ^
+            Td3[(t1      ) & 0xff] ^
+            rk[0];
+        s1 =
+            Td0[(t1 >> 24)       ] ^
+            Td1[(t0 >> 16) & 0xff] ^
+            Td2[(t3 >>  8) & 0xff] ^
+            Td3[(t2      ) & 0xff] ^
+            rk[1];
+        s2 =
+            Td0[(t2 >> 24)       ] ^
+            Td1[(t1 >> 16) & 0xff] ^
+            Td2[(t0 >>  8) & 0xff] ^
+            Td3[(t3      ) & 0xff] ^
+            rk[2];
+        s3 =
+            Td0[(t3 >> 24)       ] ^
+            Td1[(t2 >> 16) & 0xff] ^
+            Td2[(t1 >>  8) & 0xff] ^
+            Td3[(t0      ) & 0xff] ^
+            rk[3];
+    }
+#endif /* ?FULL_UNROLL */
+    /*
+	 * apply last round and
+	 * map cipher state to byte array block:
+	 */
+   	s0 =
+   		(Td4[(t0 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t2 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t1      ) & 0xff] & 0x000000ff) ^
+   		rk[0];
+	PUTU32(out     , s0);
+   	s1 =
+   		(Td4[(t1 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t3 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t2      ) & 0xff] & 0x000000ff) ^
+   		rk[1];
+	PUTU32(out +  4, s1);
+   	s2 =
+   		(Td4[(t2 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t0 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t3      ) & 0xff] & 0x000000ff) ^
+   		rk[2];
+	PUTU32(out +  8, s2);
+   	s3 =
+   		(Td4[(t3 >> 24)       ] & 0xff000000) ^
+   		(Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^
+   		(Td4[(t1 >>  8) & 0xff] & 0x0000ff00) ^
+   		(Td4[(t0      ) & 0xff] & 0x000000ff) ^
+   		rk[3];
+	PUTU32(out + 12, s3);
+}
+
+#endif /* AES_ASM */
+
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+		     const unsigned long length, const AES_KEY *key,
+		     unsigned char *ivec, const int enc)
+{
+
+	unsigned long n;
+	unsigned long len = length;
+	unsigned char tmp[AES_BLOCK_SIZE];
+
+	assert(in && out && key && ivec);
+
+	if (enc) {
+		while (len >= AES_BLOCK_SIZE) {
+			for(n=0; n < AES_BLOCK_SIZE; ++n)
+				tmp[n] = in[n] ^ ivec[n];
+			AES_encrypt(tmp, out, key);
+			memcpy(ivec, out, AES_BLOCK_SIZE);
+			len -= AES_BLOCK_SIZE;
+			in += AES_BLOCK_SIZE;
+			out += AES_BLOCK_SIZE;
+		}
+		if (len) {
+			for(n=0; n < len; ++n)
+				tmp[n] = in[n] ^ ivec[n];
+			for(n=len; n < AES_BLOCK_SIZE; ++n)
+				tmp[n] = ivec[n];
+			AES_encrypt(tmp, tmp, key);
+			memcpy(out, tmp, AES_BLOCK_SIZE);
+			memcpy(ivec, tmp, AES_BLOCK_SIZE);
+		}
+	} else {
+		while (len >= AES_BLOCK_SIZE) {
+			memcpy(tmp, in, AES_BLOCK_SIZE);
+			AES_decrypt(in, out, key);
+			for(n=0; n < AES_BLOCK_SIZE; ++n)
+				out[n] ^= ivec[n];
+			memcpy(ivec, tmp, AES_BLOCK_SIZE);
+			len -= AES_BLOCK_SIZE;
+			in += AES_BLOCK_SIZE;
+			out += AES_BLOCK_SIZE;
+		}
+		if (len) {
+			memcpy(tmp, in, AES_BLOCK_SIZE);
+			AES_decrypt(tmp, tmp, key);
+			for(n=0; n < len; ++n)
+				out[n] = tmp[n] ^ ivec[n];
+			memcpy(ivec, tmp, AES_BLOCK_SIZE);
+		}
+	}
+}
diff --git a/aes.h b/aes.h
new file mode 100644
index 0000000..a0167eb
--- /dev/null
+++ b/aes.h
@@ -0,0 +1,26 @@
+#ifndef QEMU_AES_H
+#define QEMU_AES_H
+
+#define AES_MAXNR 14
+#define AES_BLOCK_SIZE 16
+
+struct aes_key_st {
+    uint32_t rd_key[4 *(AES_MAXNR + 1)];
+    int rounds;
+};
+typedef struct aes_key_st AES_KEY;
+
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,
+	AES_KEY *key);
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,
+	AES_KEY *key);
+
+void AES_encrypt(const unsigned char *in, unsigned char *out,
+	const AES_KEY *key);
+void AES_decrypt(const unsigned char *in, unsigned char *out,
+	const AES_KEY *key);
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
+		     const unsigned long length, const AES_KEY *key,
+		     unsigned char *ivec, const int enc);
+
+#endif
diff --git a/alpha.ld b/alpha.ld
new file mode 100644
index 0000000..0975443
--- /dev/null
+++ b/alpha.ld
@@ -0,0 +1,128 @@
+OUTPUT_FORMAT("elf64-alpha", "elf64-alpha",
+	      "elf64-alpha")
+OUTPUT_ARCH(alpha)
+ENTRY(__start)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp     : { *(.interp) 	}
+  .hash          : { *(.hash)		}
+  .dynsym        : { *(.dynsym)		}
+  .dynstr        : { *(.dynstr)		}
+  .gnu.version   : { *(.gnu.version)	}
+  .gnu.version_d   : { *(.gnu.version_d)	}
+  .gnu.version_r   : { *(.gnu.version_r)	}
+  .rel.text      :
+    { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+  .rela.text     :
+    { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+  .rel.data      :
+    { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+  .rela.data     :
+    { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+  .rel.rodata    :
+    { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+  .rela.rodata   :
+    { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+  .rel.got       : { *(.rel.got)		}
+  .rela.got      : { *(.rela.got)		}
+  .rel.ctors     : { *(.rel.ctors)	}
+  .rela.ctors    : { *(.rela.ctors)	}
+  .rel.dtors     : { *(.rel.dtors)	}
+  .rela.dtors    : { *(.rela.dtors)	}
+  .rel.init      : { *(.rel.init)	}
+  .rela.init     : { *(.rela.init)	}
+  .rel.fini      : { *(.rel.fini)	}
+  .rela.fini     : { *(.rela.fini)	}
+  .rel.bss       : { *(.rel.bss)		}
+  .rela.bss      : { *(.rela.bss)		}
+  .rel.plt       : { *(.rel.plt)		}
+  .rela.plt      : { *(.rela.plt)		}
+  .init          : { *(.init)	} =0x47ff041f
+  .text      :
+  {
+    *(.text)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.gnu.linkonce.t*)
+  } =0x47ff041f
+  _etext = .;
+  PROVIDE (etext = .);
+  .fini      : { *(.fini)    } =0x47ff041f
+  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
+  .rodata1   : { *(.rodata1) }
+  .reginfo : { *(.reginfo) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x100000) + (. & (0x100000 - 1));
+  .data    :
+  {
+    *(.data)
+    *(.gnu.linkonce.d*)
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  .ctors         :
+  {
+    *(.ctors)
+  }
+  .dtors         :
+  {
+    *(.dtors)
+  }
+  .plt      : { *(.plt)	}
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  _edata  =  .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+  }
+  _end = . ;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* These must appear regardless of  .  */
+}
diff --git a/android-configure.sh b/android-configure.sh
new file mode 100755
index 0000000..c5e60ef
--- /dev/null
+++ b/android-configure.sh
@@ -0,0 +1,646 @@
+#!/bin/bash
+#
+# this script is used to rebuild the Android emulator from sources
+# in the current directory. It also contains logic to speed up the
+# rebuild if it detects that you're using the Android build system
+#
+# in this case, it will use prebuilt binaries for the compiler,
+# the audio library and the SDL library. You can disable this
+# by using the --no-prebuilt-libs and --cc=<compiler> options
+#
+#
+# here's the list of environment variables you can define before
+# calling this script to control it (besides options):
+#
+#
+
+# first, let's see which system we're running this on
+cd `dirname $0`
+PROGNAME=`basename $0`
+
+# this function will be used to execute commands and eventually
+# dump them if VERBOSE is 'yes'
+VERBOSE=yes
+VERBOSE2=no
+
+function log()
+{
+    if [ "$VERBOSE" = "yes" ] ; then
+        echo "$1"
+    fi
+}
+
+function log2()
+{
+    if [ "$VERBOSE2" = "yes" ] ; then
+        echo "$1"
+    fi
+}
+
+function execute()
+{
+    log2 "Running: $*"
+    $*
+}
+
+function compile()
+{
+    log2 "Object     : $CC -o $TMPO -c $CFLAGS $TMPC"
+    $CC -o $TMPO -c $CFLAGS $TMPC 2> $TMPL
+}
+
+function link()
+{
+    log2 "Link      : $LD $LDFLAGS -o $TMPE $TMPO"
+    $LD $LDFLAGS -o $TMPE $TMPO 2> $TMPL
+}
+
+function compile-exec-run()
+{
+    log2 "RunExec    : $CC -o $TMPE $CFLAGS $TMPC"
+    compile
+    if [ $? != 0 ] ; then
+        echo "Failure to compile test program"
+        cat $TMPL
+        exit 1
+    fi
+    link
+    if [ $? != 0 ] ; then
+        echo "Failure to link test program"
+        cat $TMPL
+        exit 1
+    fi
+    $TMPE
+}
+
+OS=`uname -s`
+CPU=`uname -m`
+EXE=""
+case "$CPU" in
+    i?86) CPU=x86
+    ;;
+esac
+
+case "$OS" in
+    Darwin)
+        OS=darwin-$CPU
+        ;;
+    Linux)
+        # note that building on x86_64 is handled later
+        OS=linux-$CPU
+        ;;
+    *_NT-*)
+        OS=windows
+        EXE=.exe
+        ;;
+esac
+
+# Are we running in the Android build system ?
+unset TOP
+if [ -n "$ANDROID_PRODUCT_OUT" ] ; then
+    TOP=`cd $ANDROID_PRODUCT_OUT/../../../.. && pwd`
+    log "TOP found at $TOP"
+    # $TOP/config/envsetup.make is for the old tree layout
+    # $TOP/build/envsetup.sh is for the new one
+    ANDROID_CONFIG_MK=$TOP/config/envsetup.make
+    if [ ! -f $ANDROID_CONFIG_MK ] ; then
+        ANDROID_CONFIG_MK=$TOP/build/core/config.mk
+    fi
+    if [ ! -f $ANDROID_CONFIG_MK ] ; then
+        echo "Cannot find build system root (TOP)"
+        echo "defaulting to non-Android build"
+        unset TOP
+    fi
+fi
+
+# normalize the TOP variable, we don't want any trailing /
+IN_ANDROID_BUILD=no
+if [ -n "$TOP" ] ; then
+    TOPDIR=`dirname $TOP`
+    if [ "$TOPDIR" != "." ] ; then
+        TOP=$TOPDIR/`basename $TOP`
+    fi
+    IN_ANDROID_BUILD=yes
+    log "In Android Build"
+fi
+
+# Parse options
+OPTION_TARGETS=""
+OPTION_DEBUG=no
+OPTION_IGNORE_AUDIO=no
+OPTION_NO_PREBUILTS=no
+OPTION_TRY_64=no
+OPTION_HELP=no
+
+if [ -z "$CC" ] ; then
+  CC=gcc
+fi
+
+for opt do
+  optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
+  case "$opt" in
+  --help|-h|-\?) OPTION_HELP=yes
+  ;;
+  --verbose)
+    if [ "$VERBOSE" = "yes" ] ; then
+        VERBOSE2=yes
+    else
+        VERBOSE=yes
+    fi
+  ;;
+  --install=*) OPTION_TARGETS="$TARGETS $optarg";
+  ;;
+  --sdl-config=*) SDL_CONFIG=$optarg
+  ;;
+  --cc=*) CC="$optarg" ; HOSTCC=$CC
+  ;;
+  --no-strip) OPTION_NO_STRIP=yes
+  ;;
+  --debug) OPTION_DEBUG=yes
+  ;;
+  --ignore-audio) OPTION_IGNORE_AUDIO=yes
+  ;;
+  --no-prebuilts) OPTION_NO_PREBUILTS=yes
+  ;;
+  --try-64) OPTION_TRY_64=yes
+  ;;
+  *)
+    echo "unknown option '$opt', use --help"
+    exit 1
+  esac
+done
+
+# Print the help message
+#
+if [ "$OPTION_HELP" = "yes" ] ; then
+    cat << EOF
+
+Usage: rebuild.sh [options]
+Options: [defaults in brackets after descriptions]
+EOF
+    echo "Standard options:"
+    echo "  --help                   print this message"
+    echo "  --install=FILEPATH       copy emulator executable to FILEPATH [$TARGETS]"
+    echo "  --cc=PATH                specify C compiler [$CC]"
+    echo "  --sdl-config=FILE        use specific sdl-config script [$SDL_CONFIG]"
+    echo "  --no-strip               do not strip emulator executable"
+    echo "  --debug                  enable debug (-O0 -g) build"
+    echo "  --ignore-audio           ignore audio messages (may build sound-less emulator)"
+    echo "  --no-prebuilts           do not use prebuilt libraries and compiler"
+    echo "  --try-64                 try to build a 64-bit executable (may crash)"
+    echo "  --verbose                verbose configuration"
+    echo ""
+    exit 1
+fi
+
+# Various probes are going to need to run a small C program
+TMPC=/tmp/android-qemu-$$.c
+TMPO=/tmp/android-qemu-$$.o
+TMPE=/tmp/android-qemu-$$$EXE
+TMPL=/tmp/android-qemu-$$.log
+
+function clean-exit ()
+{
+    rm -f $TMPC $TMPO $TMPL $TMPE
+    exit 1
+}
+
+# Adjust a few things when we're building within the Android build
+# system:
+#    - locate prebuilt directory
+#    - locate and use prebuilt libraries
+#    - copy the new binary to the correct location
+#
+if [ "$OPTION_NO_PREBUILTS" = "yes" ] ; then
+    IN_ANDROID_BUILD=no
+fi
+
+if [ "$IN_ANDROID_BUILD" = "yes" ] ; then
+
+    # Get the value of a build variable as an absolute path.
+    function get_abs_build_var()
+    {
+       (cd $TOP && CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make -f $ANDROID_CONFIG_MK dumpvar-abs-$1)
+    }
+
+    # locate prebuilt directory
+    PREBUILT_HOST_TAG=$OS
+    case $OS in
+        linux-*)
+            # Linux is a special case because in the old tree layout
+            # we simply used 'Linux' as the prebuilt host tag, but
+            # are now using "linux-x86" in the new layout
+            # check which one should be used
+            #
+            if [ -d $TOP/prebuilt/Linux ] ; then
+                PREBUILT_HOST_TAG=Linux
+            fi
+            ;;
+    esac
+    PREBUILT=$TOP/prebuilt/$PREBUILT_HOST_TAG
+    if [ ! -d $PREBUILT ] ; then
+        # this can happen when building on x86_64
+        case $OS in
+            linux-x86_64)
+                PREBUILT_HOST_TAG=linux-x86
+                PREBUILT=$TOP/prebuilt/$PREBUILT_HOST_TAG
+                log "Forcing usage of 32-bit prebuilts"
+                ;;
+            *)
+        esac
+        if [ ! -d $PREBUILT ] ; then
+            echo "Can't find the prebuilt directory $PREBUILT in Android build"
+            exit 1
+        fi
+    fi
+    log "Prebuilt   : PREBUILT=$PREBUILT"
+
+    # use ccache if USE_CCACHE is defined and the corresponding
+    # binary is available.
+    #
+    # note: located in PREBUILT/ccache/ccache in the new tree layout
+    #       located in PREBUILT/ccache in the old one
+    #
+    if [ -n "$USE_CCACHE" ] ; then
+        CCACHE="$PREBUILT/ccache/ccache$EXE"
+        if [ ! -f $CCACHE ] ; then
+            CCACHE="$PREBUILT/ccache$EXE"
+        fi
+        if [ -f $CCACHE ] ; then
+            CC="$CCACHE $CC"
+        fi
+        log "Prebuilt   : CCACHE=$CCACHE"
+    fi
+
+    # if the user didn't specify a sdl-config script, get the prebuilt one
+    if [ -z "$SDL_CONFIG" -a "$OPTION_NO_PREBUILTS" = "no" ] ; then
+        # always use our own static libSDL by default
+        SDL_CONFIG=$PREBUILT/sdl/bin/sdl-config
+        log "Prebuilt   : SDL_CONFIG=$SDL_CONFIG"
+    fi
+
+    # finally ensure that our new binary is copied to the 'out'
+    # subdirectory as 'emulator'
+    HOST_BIN=$(get_abs_build_var HOST_OUT_EXECUTABLES)
+    if [ -n "$HOST_BIN" ] ; then
+        TARGETS="$TARGETS $HOST_BIN/emulator$EXE"
+        log "Targets    : TARGETS=$TARGETS"
+    fi
+fi  # IN_ANDROID_BUILD = no
+
+
+####
+####  Compiler checks
+####
+####
+if [ -z "$CC" ] ; then
+    CC=gcc
+    if [ $CPU = "powerpc" ] ; then
+        CC=gcc-3.3
+    fi
+fi
+
+cat > $TMPC <<EOF
+int main(void) {}
+EOF
+
+if [ -z "$LD" ] ; then
+    LD=$CC
+fi
+
+# we only support generating 32-bit binaris on 64-bit systems.
+# And we may need to add a -Wa,--32 to CFLAGS to let the assembler
+# generate 32-bit binaries on Linux x86_64.
+#
+if [ "$OPTION_TRY_64" != "yes" ] ; then
+    if [ "$CPU" = "x86_64" -o "$CPU" = "amd64" ] ; then
+        log "Check32Bits: Forcing generation of 32-bit binaries (--try-64 to disable)"
+        CPU="i386"
+        case $OS in
+            linux-*)
+                OS=linux-x86
+                ;;
+            darwin-*)
+                OS=darwin-x86
+                ;;
+        esac
+        CFLAGS="$CFLAGS -m32"
+        LDFLAGS="$LDFLAGS -m32"
+        compile
+        if [ $? != 0 ] ; then
+            CFLAGS="$CFLAGS -Wa,--32"
+        fi
+        # check that the compiler can link 32-bit executables
+        # if not, try the host linker
+        link
+        if [ $? != 0 ] ; then
+            OLD_LD=$LD
+            LD=gcc
+            compile
+            link
+            if [ $? != 0 ] ; then
+                log "not using gcc for LD"
+                LD=$OLD_LD
+            fi
+        fi
+    fi
+fi
+
+compile
+if [ $? != 0 ] ; then
+    echo "C compiler doesn't seem to work:"
+    cat $TMPL
+    clean-exit
+fi
+log "CC         : compiler check ok ($CC)"
+
+# on 64-bit systems, some of our prebuilt compilers are not
+# capable of linking 32-bit executables properly
+#
+link
+if [ $? != 0 ] ; then
+    echo "Linker doesn't seem to work:"
+    cat $TMPL
+    clean-exit
+fi
+log "LD         : linker check ok ($LD)"
+
+###
+###  SDL Probe
+###
+
+# For now, we require an external libSDL library, if SDL_CONFIG is not
+# defined, try to grab it from the environment
+#
+if [ -z "$SDL_CONFIG" ] ; then
+    SDL_CONFIG=`which sdl-config`
+    if [ $? != 0 ] ; then
+        echo "Please ensure that you have the emulator's patched libSDL"
+        echo "built somewhere and point to its sdl-config script either"
+        echo "with the SDL_CONFIG env. variable, or the --sdl-config=<script>"
+        echo "option."
+        clean-exit
+    fi
+fi
+
+# check that we can link statically with the library.
+#
+SDL_CFLAGS=`$SDL_CONFIG --cflags`
+SDL_LIBS=`$SDL_CONFIG --static-libs`
+
+# quick hack, remove the -D_GNU_SOURCE=1 of some SDL Cflags
+# since they break recent Mingw releases
+SDL_CFLAGS=`echo $SDL_CFLAGS | sed -e s/-D_GNU_SOURCE=1//g`
+
+log "SDL-probe  : SDL_CFLAGS = $SDL_CFLAGS"
+log "SDL-probe  : SDL_LIBS   = $SDL_LIBS"
+
+OLD_CFLAGS=$CFLAGS
+OLD_LDFLAGS=$LDFLAGS
+
+CFLAGS="$CFLAGS $SDL_CFLAGS"
+LDFLAGS="$LDFLAGS $SDL_LIBS"
+
+cat > $TMPC << EOF
+#include <SDL.h>
+#undef main
+int main( void ) {
+   return SDL_Init (SDL_INIT_VIDEO); 
+}
+EOF
+compile
+if [ $? != 0 ] ; then
+    echo "You provided an explicit sdl-config script, but the corresponding library"
+    echo "cannot be statically linked with the Android emulator directly."
+    echo "Error message:"
+    cat $TMPL
+    clean-exit
+fi
+log "SDL-probe  : static linking ok"
+
+# now, let's check that the SDL library has the special functions
+# we added to our own sources
+#
+cat > $TMPC << EOF
+#include <SDL.h>
+#undef main
+int main( void ) {
+    int  x, y;
+    SDL_WM_GetPos(&x, &y);
+    SDL_WM_SetPos(x, y);
+    return SDL_Init (SDL_INIT_VIDEO); 
+}
+EOF
+compile
+if [ $? != 0 ] ; then
+    echo "You provided an explicit sdl-config script in SDL_CONFIG, but the"
+    echo "corresponding library doesn't have the patches required to link"
+    echo "with the Android emulator. Unsetting SDL_CONFIG will use the"
+    echo "sources bundled with the emulator instead"
+    echo "Error:"
+    cat $TMPL
+    clean-exit
+fi
+
+log "SDL-probe  : extra features ok"
+rm -f $TMPL $TMPC $TMPE
+
+CFLAGS=$OLD_CFLAGS
+LDFLAGS=$OLD_LDFLAGS
+
+
+###
+###  Audio subsystems probes
+###
+PROBE_COREAUDIO=no
+PROBE_ALSA=no
+PROBE_OSS=no
+PROBE_ESD=no
+PROBE_WINAUDIO=no
+
+case "$OS" in
+    darwin*) PROBE_COREAUDIO=yes;
+    ;;
+    linux-*) PROBE_ALSA=yes; PROBE_OSS=yes; PROBE_ESD=yes;
+    ;;
+    windows) PROBE_WINAUDIO=yes
+    ;;
+esac
+
+ORG_CFLAGS=$CFLAGS
+ORG_LDFLAGS=$LDFLAGS
+
+if [ "$PROBE_ESD" = yes ] ; then
+    CFLAGS="$ORG_CFLAGS"
+    LDFLAGS="$ORG_LDFLAGS -ldl"
+    cp -f android/config/check-esd.c $TMPC
+    compile && link && $TMPE
+    if [ $? = 0 ] ; then
+        log "AudioProbe : ESD seems to be usable on this system"
+    else
+        if [ "$OPTION_IGNORE_AUDIO" = no ] ; then
+            echo "the EsounD development files do not seem to be installed on this system"
+            echo "Are you missing the libesd-dev package ?"
+            echo "Correct the errors below and try again:"
+            cat $TMPL
+            clean-exit
+        fi
+        PROBE_ESD=no
+        log "AudioProbe : ESD seems to be UNUSABLE on this system !!"
+    fi
+fi
+
+if [ "$PROBE_ALSA" = yes ] ; then
+    CFLAGS="$ORG_CFLAGS"
+    LDFLAGS="$ORG_CFLAGS -ldl"
+    cp -f android/config/check-alsa.c $TMPC
+    compile && link && $TMPE
+    if [ $? = 0 ] ; then
+        log "AudioProbe : ALSA seems to be usable on this system"
+    else
+        if [ "$OPTION_IGNORE_AUDIO" = no ] ; then
+            echo "the ALSA development files do not seem to be installed on this system"
+            echo "Are you missing the libasound-dev package ?"
+            echo "Correct the erros below and try again"
+            cat $TMPL
+            clean-exit
+        fi
+        PROBE_ALSA=no
+        log "AudioProbe : ALSA seems to be UNUSABLE on this system !!"
+    fi
+fi
+
+CFLAGS=$ORG_CFLAGS
+LDFLAGS=$ORG_LDFLAGS
+
+# create the objs directory that is going to contain all generated files
+# including the configuration ones
+#
+mkdir -p objs
+
+###
+###  Compiler probe
+###
+
+####
+####  Host system probe
+####
+
+# because the previous version could be read-only
+rm -f $TMPC
+
+# check host endianess
+#
+HOST_BIGENDIAN=no
+cat > $TMPC << EOF
+#include <inttypes.h>
+int main(int argc, char ** argv){
+        volatile uint32_t i=0x01234567;
+        return (*((uint8_t*)(&i))) == 0x67;
+}
+EOF
+compile-exec-run && HOST_BIGENDIAN=yes
+log "Host       : HOST_BIGENDIAN=$HOST_BIGENDIAN"
+
+# check size of host long bits
+HOST_LONGBITS=32
+cat > $TMPC << EOF
+int main(void) {
+        return sizeof(void*)*8;
+}
+EOF
+compile-exec-run
+HOST_LONGBITS=$?
+log "Host       : HOST_LONGBITS=$HOST_LONGBITS"
+
+# check whether we have <byteswap.h>
+#
+HAVE_BYTESWAP_H=yes
+cat > $TMPC << EOF
+#include <byteswap.h>
+EOF
+compile
+if [ $? != 0 ] ; then
+    HAVE_BYTESWAP_H=no
+fi
+log "Host       : HAVE_BYTESWAP_H=$HAVE_BYTESWAP_H"
+
+# Build the config.make file
+#
+rm -rf objs
+mkdir -p objs
+config_mk=objs/config.make
+echo "# This file was autogenerated by $PROGNAME" > $config_mk
+echo "TARGET_ARCH := arm" >> $config_mk
+case $OS in
+    linux-*) HOST_OS=linux
+    ;;
+    darwin-*) HOST_OS=darwin
+    ;;
+    *) HOST_OS=$OS
+esac
+echo "OS          := $OS" >> $config_mk
+echo "HOST_OS     := $HOST_OS" >> $config_mk
+case $CPU in
+	i?86) HOST_ARCH=x86
+	;;
+	amd64) HOST_ARCH=x86_64
+	;;
+	powerpc) HOST_ARCH=ppc
+	;;
+	*) HOST_ARCH=$CPU
+esac
+echo "HOST_ARCH         := $HOST_ARCH" >> $config_mk
+PWD=`pwd`
+echo "SRC_PATH          := $PWD" >> $config_mk
+echo "CC                := $CC" >> $config_mk
+echo "HOST_CC           := $CC" >> $config_mk
+echo "LD                := $LD" >> $config_mk
+echo "NO_PREBUILT       := $OPTION_NO_PREBUILTS" >> $config_mk
+echo "HOST_PREBUILT_TAG := $PREBUILT_HOST_TAG" >> $config_mk
+echo "PREBUILT          := $PREBUILT" >> $config_mk
+echo "CFLAGS            := $CFLAGS" >> $config_mk
+echo "LDFLAGS           := $LDFLAGS" >> $config_mk
+echo "SDL_CONFIG         := $SDL_CONFIG" >> $config_mk
+echo "CONFIG_COREAUDIO  := $PROBE_COREAUDIO" >> $config_mk
+echo "CONFIG_WINAUDIO   := $PROBE_WINAUDIO" >> $config_mk
+echo "CONFIG_ESD        := $PROBE_ESD" >> $config_mk
+echo "CONFIG_ALSA       := $PROBE_ALSA" >> $config_mk
+echo "CONFIG_OSS        := $PROBE_OSS" >> $config_mk
+echo "" >> $config_mk
+echo "BUILD_STANDALONE_EMULATOR := true" >> $config_mk
+echo "HOST_PREBUILT_TAG         := $PREBUILT_HOST_TAG" >> $config_mk
+
+log "Generate   : $config_mk"
+
+# Build the config-host.h file
+#
+config_h=objs/config-host.h
+echo "/* This file was autogenerated by '$PROGNAME' */" > $config_h
+echo "#define CONFIG_QEMU_SHAREDIR   \"/usr/local/share/qemu\"" >> $config_h
+echo "#define HOST_LONG_BITS  $HOST_LONGBITS" >> $config_h
+if [ "$HAVE_BYTESWAP_H" = "yes" ] ; then
+  echo "#define HAVE_BYTESWAP_H 1" >> $config_h
+fi
+echo "#define CONFIG_GDBSTUB  1" >> $config_h
+echo "#define CONFIG_SLIRP    1" >> $config_h
+echo "#define CONFIG_SKINS    1" >> $config_h
+# the -nand-limits options can only work on non-windows systems
+if [ "$OS" != "windows" ] ; then
+    echo "#define CONFIG_NAND_LIMITS  1" >> $config_h
+fi
+echo "#define QEMU_VERSION    \"0.8.2\"" >> $config_h
+case "$CPU" in
+    i386) HOST_CPU=I386
+    ;;
+    powerpc) HOST_CPU=PPC
+    ;;
+    x86_64|amd64) HOST_CPU=X86_64
+    ;;
+    *) HOST_CPU=$CPU
+    ;;
+esac
+echo "#define HOST_$HOST_CPU    1" >> $config_h
+log "Generate   : $config_h"
+
+echo "Ready to go. Type 'make' to build emulator"
diff --git a/android-rebuild.sh b/android-rebuild.sh
new file mode 100755
index 0000000..d488f69
--- /dev/null
+++ b/android-rebuild.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# this script is used to rebuild all QEMU binaries for the host
+# platforms.
+#
+# assume that the device tree is in TOP
+#
+
+cd `dirname $0`
+./android-configure.sh $* && \
+make -j4 && \
+echo "Done. !!"
diff --git a/android/android.h b/android/android.h
new file mode 100644
index 0000000..71a20f6
--- /dev/null
+++ b/android/android.h
@@ -0,0 +1,100 @@
+/* Copyright (C) 2007 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef  _qemu_android_h
+#define  _qemu_android_h
+
+#define  ANDROID_VERSION_MAJOR  1
+#define  ANDROID_VERSION_MINOR  9
+
+#define  CONFIG_SHAPER  1
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/** in vl.c */
+
+/* emulated network up/down speeds, expressed in bits/seconds */
+extern double   qemu_net_upload_speed;
+extern double   qemu_net_download_speed;
+
+/* emulated network min-max latency, expressed in ms */
+extern int      qemu_net_min_latency;
+extern int      qemu_net_max_latency;
+
+/* global flag, when true, network is disabled */
+extern int      qemu_net_disable;
+
+/* list of supported network speed names and values in bits/seconds */
+typedef struct {
+    const char*  name;
+    const char*  display;
+    int          upload;
+    int          download;
+} NetworkSpeed;
+
+extern const NetworkSpeed   android_netspeeds[];
+
+/* list of supported network latency names and min-max values in ms */
+typedef struct {
+    const char*  name;
+    const char*  display;
+    int          min_ms;
+    int          max_ms;
+} NetworkLatency;
+
+extern const NetworkLatency  android_netdelays[];
+
+/* enable/disable interrupt polling mode. the emulator will always use 100%
+ * of host CPU time, but will get high-quality time measurments. this is
+ * required for the tracing mode unless you can bear 10ms granularities
+ */
+extern void  qemu_polling_enable(void);
+extern void  qemu_polling_disable(void);
+
+/**in hw/goldfish_fb.c */
+
+/* framebuffer dimensions in pixels, note these can change dynamically */
+extern int  android_framebuffer_w;
+extern int  android_framebuffer_h;
+/* framebuffer dimensions in mm */
+extern int  android_framebuffer_phys_w;
+extern int  android_framebuffer_phys_h;
+
+/* framebuffer rotation, relative to device */
+typedef enum {
+    ANDROID_ROTATION_0 = 0,
+    ANDROID_ROTATION_90,
+    ANDROID_ROTATION_180,
+    ANDROID_ROTATION_270
+} AndroidRotation;
+
+extern AndroidRotation  android_framebuffer_rotation;
+
+/**  in android_main.c */
+
+/* this is the port used for the control console in this emulator instance.
+ * starts at 5554, with increments of 2 */
+extern int   android_base_port;
+
+/* parses a network speed parameter and sets qemu_net_upload_speed and
+ * qemu_net_download_speed accordingly. returns -1 on failure, 0 on success */
+extern int   android_parse_network_speed(const char*  speed);
+
+/* parse a network delay parameter and sets qemu_net_min/max_latency
+ * accordingly. returns -1 on error, 0 on success */
+extern int   android_parse_network_latency(const char*  delay);
+
+extern void  android_emulation_setup( void );
+extern void  android_emulation_teardown( void );
+
+#endif /* _qemu_android_h */
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
new file mode 100644
index 0000000..f2293d1
--- /dev/null
+++ b/android/avd/hardware-properties.ini
@@ -0,0 +1,158 @@
+# This file describes the properties of a given virtual device configuration file.
+#
+# Note: Most top-level properties are boolean that control whether a feature is
+#       present or not. Sub-features that depend on it are ignored if their
+#       parent is set to 'false' or 'no'
+#
+# This file is parsed by 'android/tools/gen-hw-config.py' to generate
+# 'android/avd/hw-config-defs.h'. The latter is a special header containing
+# macro statements that is used several times:
+#
+#  - once to define the fields of the AndroidHwConfig structure
+#    (see android/avd/hw-config.h)
+#
+#  - once to implement the hardware configuration loader
+#    (see android/avd/hw-config.h)
+#
+# Hopefully, this file should also be read by a virtual device creation
+# tool/wizard to provide a nice user interface (hence the presence of
+# the 'abstract' and 'description' keys which are not currently used)
+#
+#
+# NOTE: if you remove items from this file, be sure that you do not break
+#       the emulator build.
+#
+
+# Ram size
+name        = hw.ramSize
+type        = integer
+default     = 96
+abstract    = Device ram size
+description = The amount of physical RAM on the device, in megabytes.
+
+# Touch screen support
+name        = hw.touchScreen
+type        = boolean
+default     = yes
+abstract    = Touch-screen support
+description = Whether there is a touch screen or not on the device.
+
+# Trackball support
+name        = hw.trackBall
+type        = boolean
+default     = yes
+abstract    = Track-ball support
+description = Whether there is a trackball on the device.
+
+# Keyboard support (qwerty/azerty)
+name        = hw.keyboard
+type        = boolean
+default     = yes
+abstract    = Keyboard support
+description = Whether the device has a QWERTY keyboard.
+
+# DPad keys
+name        = hw.dPad
+type        = boolean
+default     = yes
+abstract    = DPad support
+description = Whether the device has DPad keys
+
+# GSM Modem support
+name        = hw.gsmModem
+type        = boolean
+default     = yes
+abstract    = GSM modem support
+description = Whether there is a GSM modem in the device.
+
+# Wifi support
+name        = hw.wifi
+type        = boolean
+default     = no
+abstract    = Wifi support
+description = Whether the device has a Wifi chipset.
+
+# Bluetooth support
+name        = hw.bluetooth
+type        = boolean
+default     = no
+abstract    = Bluetooth support
+description = Whether the device has a Bluetooth chipset.
+
+# Camera support
+name        = hw.camera
+type        = boolean
+default     = no
+abstract    = Camera support
+description = Whether the device has a camera.
+
+name        = hw.camera.maxHorizontalPixels
+type        = integer
+default     = 640
+abstract    = Maximum horizontal camera pixels
+
+name        = hw.camera.maxVerticalPixels
+type        = integer
+default     = 480
+abstract    = Maximum vertical camera pixels
+
+# GPS support
+name        = hw.gps
+type        = boolean
+default     = no
+abstract    = GPS support
+description = Whether there is a GPS in the device.
+
+# Accelerometer
+name        = hw.accelerometer
+type        = boolean
+default     = no
+abstract    = Accelerometer support
+description = Whether there is an accelerometer in the device.
+
+# Battery
+name        = hw.battery
+type        = boolean
+default     = yes
+abstract    = Battery support
+description = Whether the device can run on a battery.
+
+# Audio input
+name        = hw.audioInput
+type        = boolean
+default     = yes
+abstract    = Audio recording support
+description = Whether the device can record audio
+
+# Audio output
+name        = hw.audioOutput
+type        = boolean
+default     = yes
+abstract    = Audio playback support
+description = Whether the device can play audio
+
+# Compass
+name        = hw.compass
+type        = boolean
+default     = no
+abstract    = Compass support
+description = Whether there is a compass in the device.
+
+# SDCard support
+name        = hw.sdCard
+type        = boolean
+default     = yes
+abstract    = SD Card support
+description = Whether the device supports insertion/removal of virtual SD Cards.
+
+# Cache partition
+name        = disk.cachePartition
+type        = boolean
+default     = yes
+abstract    = Cache partition support
+description = Whether we use a /cache partition on the device.
+
+name        = disk.cachePartition.size
+type        = diskSize
+abstract    = Cache partition size
+default     = 66MB
diff --git a/android/avd/hw-config-defs.h b/android/avd/hw-config-defs.h
new file mode 100644
index 0000000..5c3b9ab
--- /dev/null
+++ b/android/avd/hw-config-defs.h
@@ -0,0 +1,165 @@
+/* this file is automatically generated from 'hardware-properties.ini'
+ * DO NOT EDIT IT. To re-generate it, use android/tools/gen-hw-config.py'
+ */
+#ifndef HWCFG_INT
+#error  HWCFG_INT not defined
+#endif
+#ifndef HWCFG_BOOL
+#error  HWCFG_BOOL not defined
+#endif
+#ifndef HWCFG_DISKSIZE
+#error  HWCFG_DISKSIZE not defined
+#endif
+#ifndef HWCFG_STRING
+#error  HWCFG_STRING not defined
+#endif
+#ifndef HWCFG_DOUBLE
+#error  HWCFG_DOUBLE not defined
+#endif
+
+HWCFG_INT(
+  hw_ramSize,
+  "hw.ramSize",
+  96,
+  "Device ram size",
+  "The amount of physical RAM on the device, in megabytes.")
+
+HWCFG_BOOL(
+  hw_touchScreen,
+  "hw.touchScreen",
+  "yes",
+  "Touch-screen support",
+  "Whether there is a touch screen or not on the device.")
+
+HWCFG_BOOL(
+  hw_trackBall,
+  "hw.trackBall",
+  "yes",
+  "Track-ball support",
+  "Whether there is a trackball on the device.")
+
+HWCFG_BOOL(
+  hw_keyboard,
+  "hw.keyboard",
+  "yes",
+  "Keyboard support",
+  "Whether the device has a QWERTY keyboard.")
+
+HWCFG_BOOL(
+  hw_dPad,
+  "hw.dPad",
+  "yes",
+  "DPad support",
+  "Whether the device has DPad keys")
+
+HWCFG_BOOL(
+  hw_gsmModem,
+  "hw.gsmModem",
+  "yes",
+  "GSM modem support",
+  "Whether there is a GSM modem in the device.")
+
+HWCFG_BOOL(
+  hw_wifi,
+  "hw.wifi",
+  "no",
+  "Wifi support",
+  "Whether the device has a Wifi chipset.")
+
+HWCFG_BOOL(
+  hw_bluetooth,
+  "hw.bluetooth",
+  "no",
+  "Bluetooth support",
+  "Whether the device has a Bluetooth chipset.")
+
+HWCFG_BOOL(
+  hw_camera,
+  "hw.camera",
+  "no",
+  "Camera support",
+  "Whether the device has a camera.")
+
+HWCFG_INT(
+  hw_camera_maxHorizontalPixels,
+  "hw.camera.maxHorizontalPixels",
+  640,
+  "Maximum horizontal camera pixels",
+  "")
+
+HWCFG_INT(
+  hw_camera_maxVerticalPixels,
+  "hw.camera.maxVerticalPixels",
+  480,
+  "Maximum vertical camera pixels",
+  "")
+
+HWCFG_BOOL(
+  hw_gps,
+  "hw.gps",
+  "no",
+  "GPS support",
+  "Whether there is a GPS in the device.")
+
+HWCFG_BOOL(
+  hw_accelerometer,
+  "hw.accelerometer",
+  "no",
+  "Accelerometer support",
+  "Whether there is an accelerometer in the device.")
+
+HWCFG_BOOL(
+  hw_battery,
+  "hw.battery",
+  "yes",
+  "Battery support",
+  "Whether the device can run on a battery.")
+
+HWCFG_BOOL(
+  hw_audioInput,
+  "hw.audioInput",
+  "yes",
+  "Audio recording support",
+  "Whether the device can record audio")
+
+HWCFG_BOOL(
+  hw_audioOutput,
+  "hw.audioOutput",
+  "yes",
+  "Audio playback support",
+  "Whether the device can play audio")
+
+HWCFG_BOOL(
+  hw_compass,
+  "hw.compass",
+  "no",
+  "Compass support",
+  "Whether there is a compass in the device.")
+
+HWCFG_BOOL(
+  hw_sdCard,
+  "hw.sdCard",
+  "yes",
+  "SD Card support",
+  "Whether the device supports insertion/removal of virtual SD Cards.")
+
+HWCFG_BOOL(
+  disk_cachePartition,
+  "disk.cachePartition",
+  "yes",
+  "Cache partition support",
+  "Whether we use a /cache partition on the device.")
+
+HWCFG_DISKSIZE(
+  disk_cachePartition_size,
+  "disk.cachePartition.size",
+  "66MB",
+  "Cache partition size",
+  "")
+
+#undef HWCFG_INT
+#undef HWCFG_BOOL
+#undef HWCFG_DISKSIZE
+#undef HWCFG_STRING
+#undef HWCFG_DOUBLE
+/* end of auto-generated file */
diff --git a/android/avd/hw-config.c b/android/avd/hw-config.c
new file mode 100644
index 0000000..2362b59
--- /dev/null
+++ b/android/avd/hw-config.c
@@ -0,0 +1,39 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/avd/hw-config.h"
+#include "android/utils/ini.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+/* the global variable containing the hardware config for this device */
+AndroidHwConfig   android_hw[1];
+
+int
+androidHwConfig_read( AndroidHwConfig*  config,
+                      IniFile*          ini )
+{
+    if (ini == NULL)
+        return -1;
+
+    /* use the magic of macros to implement the hardware configuration loaded */
+
+#define   HWCFG_BOOL(n,s,d,a,t)       config->n = iniFile_getBoolean(ini, s, d);
+#define   HWCFG_INT(n,s,d,a,t)        config->n = iniFile_getInteger(ini, s, d);
+#define   HWCFG_STRING(n,s,d,a,t)     config->n = iniFile_getString(ini, s, d);
+#define   HWCFG_DOUBLE(n,s,d,a,t)     config->n = iniFile_getDouble(ini, s, d);
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   config->n = iniFile_getDiskSize(ini, s, d);
+
+#include "android/avd/hw-config-defs.h"
+
+    return 0;
+}
diff --git a/android/avd/hw-config.h b/android/avd/hw-config.h
new file mode 100644
index 0000000..05eb828
--- /dev/null
+++ b/android/avd/hw-config.h
@@ -0,0 +1,46 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_AVD_HW_CONFIG_H
+#define _ANDROID_AVD_HW_CONFIG_H
+
+#include <stdint.h>
+#include "android/utils/ini.h"
+
+typedef char      hw_bool_t;
+typedef int       hw_int_t;
+typedef int64_t   hw_disksize_t;
+typedef char*     hw_string_t;
+typedef double    hw_double_t;
+
+/* these macros are used to define the fields of AndroidHwConfig
+ * declared below
+ */
+#define   HWCFG_BOOL(n,s,d,a,t)       hw_bool_t      n;
+#define   HWCFG_INT(n,s,d,a,t)        hw_int_t       n;
+#define   HWCFG_STRING(n,s,d,a,t)     hw_string_t    n;
+#define   HWCFG_DOUBLE(n,s,d,a,t)     hw_double_t    n;
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   hw_disksize_t  n;
+
+typedef struct {
+#include "android/avd/hw-config-defs.h"
+} AndroidHwConfig;
+
+/* reads a hardware configuration file from disk.
+ * returns -1 if the file could not be read, or 0 in case of success.
+ *
+ * note that default values are written to hwConfig if the configuration
+ * file doesn't have the corresponding hardware properties.
+ */
+int  androidHwConfig_read( AndroidHwConfig*  hwConfig,
+                           IniFile*          configFile );
+
+#endif /* _ANDROID_AVD_HW_CONFIG_H */
diff --git a/android/avd/info.c b/android/avd/info.c
new file mode 100644
index 0000000..230ddaa
--- /dev/null
+++ b/android/avd/info.c
@@ -0,0 +1,1865 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/avd/info.h"
+#include "android/utils/path.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/filelock.h"
+#include "android/utils/tempfile.h"
+#include "android/utils/debug.h"
+#include "android/utils/dirscanner.h"
+#include <ctype.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+/* define this to 1 to support obsolete platform/add-on
+ * specific parsing and path searching as was used temporarily
+ * in post-1.1 / pre-cupcake SDKs.
+ *
+ * the corresponding code should be removed soon.
+ */
+#define  SUPPORT_PLATFORM_OR_ADDON  1
+
+/* global variables - see android/globals.h */
+AvdInfoParams   android_avdParams[1];
+AvdInfo*        android_avdInfo;
+
+/* for debugging */
+#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
+#define  DD(...)  VERBOSE_PRINT(avd_config,__VA_ARGS__)
+
+/* technical note on how all of this is supposed to work:
+ *
+ * Each AVD corresponds to a "content directory" that is used to
+ * store persistent disk images and configuration files. Most remarkable
+ * are:
+ *
+ * - a "config.ini" file used to hold configuration information for the
+ *   AVD
+ *
+ * - mandatory user data image ("userdata-qemu.img") and cache image
+ *   ("cache.img")
+ *
+ * - optional mutable system image ("system-qemu.img"), kernel image
+ *   ("kernel-qemu") and read-only ramdisk ("ramdisk.img")
+ *
+ * When starting up an AVD, the emulator looks for relevant disk images
+ * in the content directory. If it doesn't find a given image there, it
+ * will try to search in the list of system directories listed in the
+ * 'config.ini' file through one of the following (key,value) pairs:
+ *
+ *    images.sysdir.1 = <first search path>
+ *    images.sysdir.2 = <second search path>
+ *
+ * The search paths can be absolute, or relative to the root SDK installation
+ * path (which is determined from the emulator program's location, or from the
+ * ANDROID_SDK_ROOT environment variable).
+ *
+ * Individual image disk search patch can be over-riden on the command-line
+ * with one of the usual options.
+ */
+
+#if SUPPORT_PLATFORM_OR_ADDON
+/*
+ * BELOW IS THE DOCUMENTATION FOR AN OBSOLETE SCHEME THAT USED TO BE MORE
+ * COMPLEX TO IMPLEMENT, AND EXPOSED TOO MUCH SDK INTERNALS WITHIN THE
+ * EMULATOR SOURCE CODE.
+ *
+ * THE CORRESPONDING CODE IS STILL THERE FOR LEGACY SUPPORT REASON BUT WILL
+ * SOON BE REMOVED FOR SIMPLIFICATION REASONS.
+ *
+ * we assume the following SDK layout:
+ *
+ *  SDK/
+ *    tools/
+ *      emulator[.exe]
+ *      libs/
+ *        hardware-properties.ini
+ *        ...
+ *
+ *    platforms/
+ *      <platform1>/
+ *        build.prop
+ *        images/
+ *          <default kernel/disk images>
+ *        skins/
+ *          default/    --> default skin
+ *            layout
+ *            <skin bitmaps>
+ *          <skin2>/    --> another skin
+ *            layout
+ *            <skin bitmaps>
+ *          <skin3>/    --> skin alias to <skin2>
+ *            alias-<skin2>
+ *
+ *      <platform2>/
+ *        build.prop
+ *        images/
+ *          <other default kernel/disk images>
+ *
+ *    add-ons/
+ *      <partner1>/
+ *        manifest.ini
+ *        images/
+ *          <replacement disk images>
+ *
+ *      <partner2>/
+ *        manifest.ini
+ *        <replacement disk images>
+ *        hardware.ini
+ *        skins/
+ *           default/
+ *              layout
+ *              <skin bitmaps>
+ *           <skin2>/
+ *              layout
+ *              <skin bitmaps>
+ *
+ *
+ * we define a 'platform' as a directory that provides a complete
+ * set of disk/kernel images, some skins, as well as a build.prop
+ * file.
+ *
+ * we define an 'addon' as a directory that provides additionnal
+ * or replacement files related to a given existing platform.
+ * each add-on provides at the minimum a 'manifest.ini' file
+ * that describes it (see below).
+ *
+ * important notes:
+ *
+ * - the build.prop file of a given platform directory contains
+ *   a line that reads 'ro.build.version.sdk=<version>' where
+ *   <version> is an integer corresponding to the corresponding
+ *   official API version number as defined by Android.
+ *
+ *   each platform provided with the SDK must have a unique
+ *   version number.
+ *
+ * - the manifest.ini of a given addon must contain lines
+ *   that include:
+ *
+ *      name=<addOnName>
+ *      vendor=<vendorName>
+ *      api=<version>
+ *
+ *   where <version> is used to identify the platform the add-on
+ *   refers to. Note that the platform's directory name is
+ *   irrelevant to the matching algorithm.
+ *
+ *   each addon available must have a unique
+ *   <vendor>:<name>:<sdk> triplet
+ *
+ * - an add-on can provide a hardware.ini file. If present, this
+ *   is used to force the hardware setting of any virtual device
+ *   built from the add-on.
+ *
+ * - the file in SDK/tools/lib/hardware-properties.ini declares which
+ *   hardware properties are supported by the emulator binary.
+ *   these can appear in the config.ini file of a given virtual
+ *   device, or the hardware.ini of a given add-on.
+ *
+ * normally, a virtual device corresponds to:
+ *
+ *  - a root configuration file, placed in ~/.android/avd/<foo>.ini
+ *    where <foo> is the name of the virtual device.
+ *
+ *  - a "content" directory, which contains disk images for the
+ *    virtual device (e.g. at a minimum, the userdata.img file)
+ *    plus some configuration information.
+ *
+ *  - the root config file must have at least two lines like:
+ *
+ *      path=<pathToContentDirectory>
+ *      target=<targetAddonOrPlatform>
+ *
+ *    the 'path' value must point to the location of
+ *    the virtual device's content directory. By default, this
+ *    should be ~/.android/avd/<foo>/, though the user should be
+ *    able to choose an alternative path at creation time.
+ *
+ *    the 'target' value can be one of:
+ *
+ *        android-<version>
+ *        <vendor>:<name>:<version>
+ *
+ *    the first form is used to refer to a given platform.
+ *    the second form is used to refer to a unique add-on.
+ *    in both forms, <version> must be an integer that
+ *    matches one of the available platforms.
+ *
+ *    <vendor>:<name>:<version> must match the triplet of one
+ *    of the available add-ons
+ *
+ *    if the target value is incorrect, or if the content path
+ *    is invalid, the emulator will abort with an error.
+ *
+ * - the content directory shall contain a 'config.ini' that
+ *   contains hardware properties for the virtual device
+ *   (as defined by SDK/tools/lib/hardware-properties.ini), as
+ *   well as additional lines like:
+ *
+ *      sdcard=<pathToDefaultSDCard>
+ *      skin=<defaultSkinName>
+ *      options=<additionalEmulatorStartupOptions>
+ *
+ *
+ *  Finally, to find the skin to be used with a given virtual
+ *  device, the following logic is used:
+ *
+ *  - if no skin name has been manually specified on
+ *    the command line, or in the config.ini file,
+ *    look in $CONTENT/skin/layout and use it if available.
+ *
+ *  - otherwise, set SKINNAME to 'default' if not manually
+ *    specified, and look for $ADDON/skins/$SKINNAME/layout
+ *    and use it if available
+ *
+ *  - otherwise, look for $PLATFORM/skins/$SKINNAME/layout
+ *    and use it if available.
+ *
+ *  - otherwise, look for $PLATFORM/skins/$SKINNAME/alias-<other>.
+ *    if a file exist by that name, look at $PLATFORM/skins/<other>/layout
+ *    and use it if available. Aliases are not recursives :-)
+ */
+
+/*  now, things get a little bit more complicated when working
+ *  within the Android build system. In this mode, which can be
+ *  detected by looking at the definition of the ANDROID_PRODUCT_OUT
+ *  environment variable, we're going to simply pick the image files
+ *  from the out directory, or from $BUILDROOT/prebuilt
+ */
+
+/* the name of the $SDKROOT subdirectory that contains all platforms */
+#  define  PLATFORMS_SUBDIR  "platforms"
+
+/* the name of the $SDKROOT subdirectory that contains add-ons */
+#  define  ADDONS_SUBDIR     "add-ons"
+
+#endif /* SUPPORT_PLATFORM_OR_ADDON */
+
+/* this is the subdirectory of $HOME/.android where all
+ * root configuration files (and default content directories)
+ * are located.
+ */
+#define  ANDROID_AVD_DIR    "avd"
+
+/* the prefix of config.ini keys that will be used for search directories
+ * of system images.
+ */
+#define  SEARCH_PREFIX   "images.sysdir."
+
+/* the maximum number of search path keys we're going to read from the
+ * config.ini file
+ */
+#define  MAX_SEARCH_PATHS  2
+
+/* the config.ini key that will be used to indicate the full relative
+ * path to the skin directory (including the skin name).
+ *
+ * If SUPPORT_PLATFORM_OR_ADDON is defined, then this can also be
+ * the name of a skin, without any path, and platform/add-on directories
+ * will be searched for it.
+ */
+#define  SKIN_PATH       "skin"
+
+/* default skin name */
+#define  SKIN_DEFAULT    "HVGA"
+
+/* certain disk image files are mounted read/write by the emulator
+ * to ensure that several emulators referencing the same files
+ * do not corrupt these files, we need to lock them and respond
+ * to collision depending on the image type.
+ *
+ * the enumeration below is used to record information about
+ * each image file path.
+ *
+ * READONLY means that the file will be mounted read-only
+ * and this doesn't need to be locked. must be first in list
+ *
+ * MUSTLOCK means that the file should be locked before
+ * being mounted by the emulator
+ *
+ * TEMPORARY means that the file has been copied to a
+ * temporary image, which can be mounted read/write
+ * but doesn't require locking.
+ */
+typedef enum {
+    IMAGE_STATE_READONLY,     /* unlocked */
+    IMAGE_STATE_MUSTLOCK,     /* must be locked */
+    IMAGE_STATE_LOCKED,       /* locked */
+    IMAGE_STATE_LOCKED_EMPTY, /* locked and empty */
+    IMAGE_STATE_TEMPORARY,    /* copied to temp file (no lock needed) */
+} AvdImageState;
+
+struct AvdInfo {
+    /* for the Android build system case */
+    char      inAndroidBuild;
+    char*     androidOut;
+    char*     androidBuildRoot;
+
+    /* for the normal virtual device case */
+    char*     deviceName;
+    char*     sdkRootPath;
+    char      sdkRootPathFromEnv;
+    char*     searchPaths[ MAX_SEARCH_PATHS ];
+    int       numSearchPaths;
+#if SUPPORT_PLATFORM_OR_ADDON
+    int       platformVersion;
+    char*     platformPath;
+    char*     addonTarget;
+    char*     addonPath;
+#endif
+    char*     contentPath;
+    IniFile*  rootIni;      /* root <foo>.ini file */
+    IniFile*  configIni;    /* virtual device's config.ini */
+
+    /* for both */
+    char*     skinName;     /* skin name */
+    char*     skinDirPath;  /* skin directory */
+
+    /* image files */
+    char*     imagePath [ AVD_IMAGE_MAX ];
+    char      imageState[ AVD_IMAGE_MAX ];
+};
+
+
+void
+avdInfo_free( AvdInfo*  i )
+{
+    if (i) {
+        int  nn;
+
+        for (nn = 0; nn < AVD_IMAGE_MAX; nn++)
+            AFREE(i->imagePath[nn]);
+
+        AFREE(i->skinName);
+        AFREE(i->skinDirPath);
+
+        for (nn = 0; nn < i->numSearchPaths; nn++)
+            AFREE(i->searchPaths[nn]);
+
+        i->numSearchPaths = 0;
+
+        if (i->configIni) {
+            iniFile_free(i->configIni);
+            i->configIni = NULL;
+        }
+
+        if (i->rootIni) {
+            iniFile_free(i->rootIni);
+            i->rootIni = NULL;
+        }
+
+        AFREE(i->contentPath);
+        AFREE(i->sdkRootPath);
+
+        if (i->inAndroidBuild) {
+            AFREE(i->androidOut);
+            AFREE(i->androidBuildRoot);
+        } else {
+#if SUPPORT_PLATFORM_OR_ADDON
+            AFREE(i->platformPath);
+            AFREE(i->addonTarget);
+            AFREE(i->addonPath);
+#endif
+        }
+
+        AFREE(i->deviceName);
+        AFREE(i);
+    }
+}
+
+/* list of default file names for each supported image file type */
+static const char*  const  _imageFileNames[ AVD_IMAGE_MAX ] = {
+#define  _AVD_IMG(x,y,z)  y,
+    AVD_IMAGE_LIST
+#undef _AVD_IMG
+};
+
+/* list of short text description for each supported image file type */
+static const char*  const _imageFileText[ AVD_IMAGE_MAX ] = {
+#define  _AVD_IMG(x,y,z)  z,
+    AVD_IMAGE_LIST
+#undef _AVD_IMG
+};
+
+/***************************************************************
+ ***************************************************************
+ *****
+ *****    NORMAL VIRTUAL DEVICE SUPPORT
+ *****
+ *****/
+
+/* compute path to the root SDK directory
+ * assume we are in $SDKROOT/tools/emulator[.exe]
+ */
+static int
+_getSdkRoot( AvdInfo*  i )
+{
+    const char*  env;
+    char         temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+#define  SDK_ROOT_ENV  "ANDROID_SDK_ROOT"
+
+    env = getenv(SDK_ROOT_ENV);
+    if (env != NULL && env[0] != 0) {
+        if (path_exists(env)) {
+            D("found " SDK_ROOT_ENV ": %s", env);
+            i->sdkRootPath = ASTRDUP(env);
+            i->sdkRootPathFromEnv = 1;
+            return 0;
+        }
+        D(SDK_ROOT_ENV " points to unknown directory: %s", env);
+    }
+
+    (void) bufprint_app_dir(temp, end);
+
+    i->sdkRootPath = path_parent(temp, 1);
+    if (i->sdkRootPath == NULL) {
+        derror("can't find root of SDK directory");
+        return -1;
+    }
+    D("found SDK root at %s", i->sdkRootPath);
+    return 0;
+}
+
+static void
+_getSearchPaths( AvdInfo*  i )
+{
+    char  temp[PATH_MAX], *p = temp, *end= p+sizeof temp;
+    int   nn, count = 0;
+
+
+
+    for (nn = 0; nn < MAX_SEARCH_PATHS; nn++) {
+        char*  path;
+
+        p = bufprint(temp, end, "%s%d", SEARCH_PREFIX, nn+1 );
+        if (p >= end)
+            continue;
+
+        path = iniFile_getString( i->configIni, temp );
+        if (path != NULL) {
+            DD("    found image search path: %s", path);
+            if (!path_is_absolute(path)) {
+                p = bufprint(temp, end, "%s/%s", i->sdkRootPath, path);
+                AFREE(path);
+                path = ASTRDUP(temp);
+            }
+            i->searchPaths[count++] = path;
+        }
+    }
+
+    i->numSearchPaths = count;
+    if (count == 0)
+        DD("no search paths found in this AVD's config.ini");
+    else
+        DD("found a total of %d search paths for this AVD", count);
+}
+
+#if SUPPORT_PLATFORM_OR_ADDON
+/* returns the full path of the platform subdirectory
+ * corresponding to a given API version
+ */
+static char*
+_findPlatformByVersion( const char*  sdkRoot, int  version )
+{
+    char         temp[PATH_MAX], *p=temp, *end=p+sizeof temp;
+    char*        subdir = NULL;
+    DirScanner*  scanner;
+
+    DD("> %s(%s,%d)", __FUNCTION__, sdkRoot, version);
+    p = bufprint(temp, end, "%s/%s", sdkRoot, PLATFORMS_SUBDIR);
+    if (p >= end) {
+        DD("! path too long");
+        return NULL;
+    }
+
+    scanner = dirScanner_new(temp);
+    if (scanner == NULL) {
+        DD("! cannot scan path %s: %s", temp, strerror(errno));
+        return NULL;
+    }
+
+    for (;;) {
+        IniFile*  ini;
+        int       apiVersion;
+
+        subdir = (char*) dirScanner_nextFull(scanner);
+        if (subdir == NULL)
+            break;
+
+        /* look for a file named "build.prop */
+        p = bufprint(temp, end, "%s/build.prop", subdir);
+        if (p >= end)
+            continue;
+
+        if (!path_exists(temp)) {
+            DD("! no file at %s", temp);
+            continue;
+        }
+
+        ini = iniFile_newFromFile(temp);
+        if (ini == NULL)
+            continue;
+
+        apiVersion = iniFile_getInteger(ini, "ro.build.version.sdk", -1);
+        iniFile_free(ini);
+
+        DD("! found %s (version %d)", temp, apiVersion);
+
+        if (apiVersion == version) {
+            /* Bingo */
+            subdir = ASTRDUP(subdir);
+            break;
+        }
+    }
+
+    if (!subdir) {
+        DD("< didn't found anything");
+    }
+
+    dirScanner_free(scanner);
+    return subdir;
+}
+
+/* returns the full path of the addon corresponding to a given target,
+ * or NULL if not found. on success, *pversion will contain the SDK
+ * version number
+ */
+static char*
+_findAddonByTarget( const char*  sdkRoot, const char*  target, int  *pversion )
+{
+    char*  targetCopy    = ASTRDUP(target);
+    char*  targetVendor  = NULL;
+    char*  targetName    = NULL;
+    int    targetVersion = -1;
+
+    char         temp[PATH_MAX];
+    char*        p;
+    char*        end;
+    DirScanner*  scanner;
+    char*        subdir;
+
+    DD("> %s(%s,%s)", __FUNCTION__, sdkRoot, target);
+
+    /* extract triplet from target string */
+    targetVendor = targetCopy;
+
+    p = strchr(targetVendor, ':');
+    if (p == NULL) {
+        DD("< missing first column separator");
+        goto FAIL;
+    }
+    *p         = 0;
+    targetName = p + 1;
+    p          = strchr(targetName, ':');
+    if (p == NULL) {
+        DD("< missing second column separator");
+        goto FAIL;
+    }
+    *p++ = 0;
+
+    targetVersion = atoi(p);
+
+    if (targetVersion == 0) {
+        DD("< invalid version number");
+        goto FAIL;
+    }
+    /* now scan addons directory */
+    p   = temp;
+    end = p + sizeof temp;
+
+    p = bufprint(p, end, "%s/%s", sdkRoot, ADDONS_SUBDIR);
+    if (p >= end) {
+        DD("< add-on path too long");
+        goto FAIL;
+    }
+    scanner = dirScanner_new(temp);
+    if (scanner == NULL) {
+        DD("< cannot scan add-on path %s: %s", temp, strerror(errno));
+        goto FAIL;
+    }
+    for (;;) {
+        IniFile*     ini;
+        const char*  vendor;
+        const char*  name;
+        int          version;
+        int          matches;
+
+        subdir = (char*) dirScanner_nextFull(scanner);
+        if (subdir == NULL)
+            break;
+
+        /* try to open the manifest.ini file */
+        p = bufprint(temp, end, "%s/manifest.ini", subdir);
+        if (p >= end)
+            continue;
+
+        ini = iniFile_newFromFile(temp);
+        if (ini == NULL)
+            continue;
+
+        DD("! scanning manifest.ini in %s", temp);
+
+        /* find the vendor, name and version */
+        vendor  = iniFile_getValue(ini,  "vendor");
+        name    = iniFile_getValue(ini,  "name");
+        version = iniFile_getInteger(ini, "api", -1);
+
+        matches = 0;
+
+        matches += (version == targetVersion);
+        matches += (vendor && !strcmp(vendor, targetVendor));
+        matches += (name   && !strcmp(name, targetName));
+
+        DD("! matches=%d vendor=[%s] name=[%s] version=%d",
+           matches,
+           vendor ? vendor : "<NULL>",
+           name ? name : "<NULL>",
+           version);
+
+        iniFile_free(ini);
+
+        if (matches == 3) {
+            /* bingo */
+            *pversion = version;
+            subdir    = ASTRDUP(subdir);
+            break;
+        }
+    }
+
+    dirScanner_free(scanner);
+
+    DD("< returning %s", subdir ? subdir : "<NULL>");
+    return subdir;
+
+FAIL:
+    AFREE(targetCopy);
+    return NULL;
+}
+#endif /* SUPPORT_PLATFORM_OR_ADDON */
+
+static int
+_checkAvdName( const char*  name )
+{
+    int  len  = strlen(name);
+    int  len2 = strspn(name, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                             "abcdefghijklmnopqrstuvwxyz"
+                             "0123456789_.-");
+    return (len == len2);
+}
+
+/* parse the root config .ini file. it is located in
+ * ~/.android/avd/<name>.ini or Windows equivalent
+ */
+static int
+_getRootIni( AvdInfo*  i )
+{
+    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+    p = bufprint_config_path(temp, end);
+    p = bufprint(p, end, "/" ANDROID_AVD_DIR "/%s.ini", i->deviceName);
+    if (p >= end) {
+        derror("device name too long");
+        return -1;
+    }
+
+    i->rootIni = iniFile_newFromFile(temp);
+    if (i->rootIni == NULL) {
+        derror("unknown virtual device name: '%s'", i->deviceName);
+        return -1;
+    }
+    D("root virtual device file at %s", temp);
+    return 0;
+}
+
+/* the .ini variable name that points to the content directory
+ * in a root AVD ini file. This is required */
+#   define  ROOT_PATH_KEY    "path"
+
+static int
+_getContentPath( AvdInfo*  i )
+{
+    i->contentPath = iniFile_getString(i->rootIni, ROOT_PATH_KEY);
+
+    if (i->contentPath == NULL) {
+        derror("bad config: %s",
+               "virtual device file lacks a "ROOT_PATH_KEY" entry");
+        return -1;
+    }
+    D("virtual device content at %s", i->contentPath);
+    return 0;
+}
+
+#if SUPPORT_PLATFORM_OR_ADDON
+#   define  ROOT_TARGET_KEY  "target"
+
+/* retrieve the content path and target from the root .ini file */
+static int
+_getTarget( AvdInfo*  i )
+{
+    i->addonTarget = iniFile_getString(i->rootIni, ROOT_TARGET_KEY);
+
+    if (i->addonTarget == NULL) {
+        derror("bad config: %s",
+               "virtual device file lacks a "ROOT_TARGET_KEY" entry");
+        return -1;
+    }
+
+    D("virtual device target is %s", i->addonTarget);
+
+    if (!strncmp(i->addonTarget, "android-", 8)) {  /* target is platform */
+        char*        end;
+        const char*  versionString = i->addonTarget+8;
+        int          version = (int) strtol(versionString, &end, 10);
+        if (*end != 0 || version <= 0) {
+            derror("bad config: invalid platform version: '%s'", versionString);
+            return -1;
+        }
+        i->platformVersion = version;
+        i->platformPath    = _findPlatformByVersion(i->sdkRootPath, 
+                                                    version);
+        if (i->platformPath == NULL) {
+            derror("bad config: unknown platform version: '%d'", version);
+            return -1;
+        }
+    }
+    else  /* target is add-on */
+    {
+        i->addonPath = _findAddonByTarget(i->sdkRootPath, i->addonTarget,
+                                          &i->platformVersion);
+        if (i->addonPath == NULL) {
+            derror("bad config: %s",
+                   "unknown add-on target: '%s'", i->addonTarget);
+            return -1;
+        }
+
+        i->platformPath = _findPlatformByVersion(i->sdkRootPath, 
+                                                 i->platformVersion);
+        if (i->platformPath == NULL) {
+            derror("bad config: %s",
+                   "unknown add-on platform version: '%d'", i->platformVersion);
+            return -1;
+        }
+        D("virtual device add-on path: %s", i->addonPath);
+    }
+    D("virtual device platform path: %s",   i->platformPath);
+    D("virtual device platform version %d", i->platformVersion);
+    return 0;
+}
+#endif /* SUPPORT_PLATFORM_OR_ADDON */
+
+/* find and parse the config.ini file from the content directory */
+static int
+_getConfigIni(AvdInfo*  i)
+{
+    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+    p = bufprint(p, end, "%s/config.ini", i->contentPath);
+    if (p >= end) {
+        derror("can't access virtual device content directory");
+        return -1;
+    }
+
+#if 1   /* XXX: TODO: remove this in the future */
+    /* for now, allow a non-existing config.ini */
+    if (!path_exists(temp)) {
+        D("virtual device has no config file - no problem");
+        return 0;
+    }
+#endif
+
+    i->configIni = iniFile_newFromFile(temp);
+    if (i->configIni == NULL) {
+        derror("bad config: %s",
+               "virtual device directory lacks config.ini");
+        return -1;
+    }
+    D("virtual device config file: %s", temp);
+    return 0;
+}
+
+/***************************************************************
+ ***************************************************************
+ *****
+ *****    KERNEL/DISK IMAGE LOADER
+ *****
+ *****/
+
+/* a structure used to handle the loading of
+ * kernel/disk images.
+ */
+typedef struct {
+    AvdInfo*        info;
+    AvdInfoParams*  params;
+    AvdImageType    id;
+    const char*     imageFile;
+    const char*     imageText;
+    char**          pPath;
+    char*           pState;
+    char            temp[PATH_MAX];
+} ImageLoader;
+
+static void
+imageLoader_init( ImageLoader*  l, AvdInfo*  info, AvdInfoParams*  params )
+{
+    memset(l, 0, sizeof(*l));
+    l->info    = info;
+    l->params  = params;
+}
+
+/* set the type of the image to load */
+static void
+imageLoader_set( ImageLoader*  l, AvdImageType  id )
+{
+    l->id        = id;
+    l->imageFile = _imageFileNames[id];
+    l->imageText = _imageFileText[id];
+    l->pPath     = &l->info->imagePath[id];
+    l->pState    = &l->info->imageState[id];
+
+    l->pState[0] = IMAGE_STATE_READONLY;
+}
+
+/* change the image path */
+static char*
+imageLoader_setPath( ImageLoader*  l, const char*  path )
+{
+    path = path ? ASTRDUP(path) : NULL;
+
+    AFREE(l->pPath[0]);
+    l->pPath[0] = (char*) path;
+
+    return (char*) path;
+}
+
+static char*
+imageLoader_extractPath( ImageLoader*  l )
+{
+    char*  result = l->pPath[0];
+    l->pPath[0] = NULL;
+    return result;
+}
+
+/* flags used when loading images */
+enum {
+    IMAGE_REQUIRED          = (1<<0),  /* image is required */
+    IMAGE_SEARCH_SDK        = (1<<1),  /* search image in SDK */
+    IMAGE_EMPTY_IF_MISSING  = (1<<2),  /* create empty file if missing */
+    IMAGE_DONT_LOCK         = (1<<4),  /* don't try to lock image */
+    IMAGE_IGNORE_IF_LOCKED  = (1<<5),  /* ignore file if it's locked */
+};
+
+#define  IMAGE_OPTIONAL  0
+
+/* find an image from the SDK search directories.
+ * returns the full path or NULL if the file could not be found.
+ *
+ * note: this stores the result in the image's path as well
+ */
+static char*
+imageLoader_lookupSdk( ImageLoader*  l  )
+{
+    AvdInfo*     i     = l->info;
+    const char*  image = l->imageFile;
+    char*        temp  = l->temp, *p = temp, *end = p + sizeof(l->temp);
+
+    do {
+        /* try the search paths */
+        int  nn;
+
+        for (nn = 0; nn < i->numSearchPaths; nn++) {
+            const char* searchDir = i->searchPaths[nn];
+
+            p = bufprint(temp, end, "%s/%s", searchDir, image);
+            if (p < end && path_exists(temp)) {
+                DD("found %s in search dir: %s", image, searchDir);
+                goto FOUND;
+            }
+            DD("    no %s in search dir: %s", image, searchDir);
+        }
+
+#if SUPPORT_PLATFORM_OR_ADDON
+        /* try the add-on directory, if any */
+        if (i->addonPath != NULL) {
+            p = bufprint(temp, end, "%s/images/%s", i->addonPath, image);
+            if (p < end && path_exists(temp)) {
+                DD("found %s in add-on dir:", image, i->addonPath);
+                break;
+            }
+            DD("    no %s in add-on dir: ", image, i->addonPath);
+        }
+
+        /* or try the platform directory */
+        p = bufprint(temp, end, "%s/images/%s",
+                     i->platformPath, image);
+        if (p < end && path_exists(temp)) {
+            DD("found %s in platform dir:", image, i->platformPath);
+            break;
+        }
+        DD("    no %s in platform dir: ", image, i->platformPath);
+#endif
+        return NULL;
+
+    } while (0);
+
+FOUND:
+    l->pState[0] = IMAGE_STATE_READONLY;
+
+    return imageLoader_setPath(l, temp);
+}
+
+/* search for a file in the content directory.
+ * returns NULL if the file cannot be found.
+ *
+ * note that this formats l->temp with the file's path
+ * allowing you to retrieve it if the function returns NULL
+ */
+static char*
+imageLoader_lookupContent( ImageLoader*  l )
+{
+    AvdInfo*  i     = l->info;
+    char*     temp  = l->temp, *p = temp, *end = p + sizeof(l->temp);
+
+    p = bufprint(temp, end, "%s/%s", i->contentPath, l->imageFile);
+    if (p >= end) {
+        derror("content directory path too long");
+        exit(2);
+    }
+    if (!path_exists(temp)) {
+        DD("    no %s in content directory", l->imageFile);
+        return NULL;
+    }
+    DD("found %s in content directory", l->imageFile);
+
+    /* assume content image files must be locked */
+    l->pState[0] = IMAGE_STATE_MUSTLOCK;
+
+    return imageLoader_setPath(l, temp);
+}
+
+/* lock a file image depending on its state and user flags
+ * note that this clears l->pPath[0] if the lock could not
+ * be acquired and that IMAGE_IGNORE_IF_LOCKED is used.
+ */
+static void
+imageLoader_lock( ImageLoader*  l, unsigned  flags )
+{
+    const char*  path = l->pPath[0];
+
+    if (flags & IMAGE_DONT_LOCK)
+        return;
+
+    if (l->pState[0] != IMAGE_STATE_MUSTLOCK)
+        return;
+
+    D("    locking %s image at %s", l->imageText, path);
+
+    if (filelock_create(path) != NULL) {
+        /* succesful lock */
+        l->pState[0] = IMAGE_STATE_LOCKED;
+        return;
+    }
+
+    if (flags & IMAGE_IGNORE_IF_LOCKED) {
+        dwarning("ignoring locked %s image at %s", l->imageText, path);
+        imageLoader_setPath(l, NULL);
+        return;
+    }
+
+    derror("the %s image is used by another emulator. aborting",
+            l->imageText);
+    exit(2);
+}
+
+/* make a file image empty, this may require locking */
+static void
+imageLoader_empty( ImageLoader*  l, unsigned  flags )
+{
+    const char*  path;
+
+    imageLoader_lock(l, flags);
+
+    path = l->pPath[0];
+    if (path == NULL)  /* failed to lock, caller will handle it */
+        return;
+
+    if (path_empty_file(path) < 0) {
+        derror("could not create %s image at %s: %s",
+                l->imageText, path, strerror(errno));
+        exit(2);
+    }
+    l->pState[0] = IMAGE_STATE_LOCKED_EMPTY;
+}
+
+
+/* copy image file from a given source 
+ * assumes locking is needed.
+ */
+static void
+imageLoader_copyFrom( ImageLoader*  l, const char*  srcPath )
+{
+    const char*  dstPath = NULL;
+
+    /* find destination file */
+    if (l->params) {
+        dstPath = l->params->forcePaths[l->id];
+    }
+    if (!dstPath) {
+        imageLoader_lookupContent(l);
+        dstPath = l->temp;
+    }
+
+    /* lock destination */
+    imageLoader_setPath(l, dstPath);
+    l->pState[0] = IMAGE_STATE_MUSTLOCK;
+    imageLoader_lock(l, 0);
+
+    /* make the copy */
+    if (path_copy_file(dstPath, srcPath) < 0) {
+        derror("can't initialize %s image from SDK: %s: %s",
+               l->imageText, dstPath, strerror(errno));
+        exit(2);
+    }
+}
+
+/* this will load and eventually lock and image file, depending
+ * on the flags being used. on exit, this function udpates
+ * l->pState[0] and l->pPath[0]
+ *
+ * returns the path to the file. Note that it returns NULL
+ * only if the file was optional and could not be found.
+ *
+ * if the file is required and missing, the function aborts
+ * the program.
+ */
+static char*
+imageLoader_load( ImageLoader*    l,
+                  unsigned        flags )
+{
+    const char*  path = NULL;
+
+    /* first, check user-provided path */
+    path = l->params->forcePaths[l->id];
+    if (path != NULL) {
+        imageLoader_setPath(l, path);
+        if (path_exists(path)) {
+            DD("found user-provided %s image: %s", l->imageText, l->imageFile);
+            goto EXIT;
+        }
+        D("user-provided %s image does not exist: %s",
+          l->imageText, path);
+
+        /* if the file is required, abort */
+        if (flags & IMAGE_REQUIRED) {
+            derror("user-provided %s image at %s doesn't exist",
+                    l->imageText, path);
+            exit(2);
+        }
+    }
+    else {
+        const char*  contentFile;
+
+        /* second, look in the content directory */
+        path = imageLoader_lookupContent(l);
+        if (path) goto EXIT;
+
+        contentFile = ASTRDUP(l->temp);
+
+        /* it's not there */
+        if (flags & IMAGE_SEARCH_SDK) {
+            /* third, look in the SDK directory */
+            path = imageLoader_lookupSdk(l);
+            if (path) {
+                AFREE((char*)contentFile);
+                goto EXIT;
+            }
+        }
+        DD("found no %s image (%s)", l->imageText, l->imageFile);
+
+        /* if the file is required, abort */
+        if (flags & IMAGE_REQUIRED) {
+            AvdInfo*  i = l->info;
+
+            derror("could not find required %s image (%s).", 
+                   l->imageText, l->imageFile);
+
+            if (i->inAndroidBuild) {
+                dprint( "Did you build everything ?" );
+            } else if (!i->sdkRootPathFromEnv) {
+                dprint( "Maybe defining %s to point to a valid SDK "
+                        "installation path might help ?", SDK_ROOT_ENV );
+            } else {
+                dprint( "Your %s is probably wrong: %s", SDK_ROOT_ENV,
+                        i->sdkRootPath );
+            }
+            exit(2);
+        }
+
+        path = imageLoader_setPath(l, contentFile);
+        AFREE((char*)contentFile);
+    }
+
+    /* otherwise, do we need to create it ? */
+    if (flags & IMAGE_EMPTY_IF_MISSING) {
+        imageLoader_empty(l, flags);
+        return l->pPath[0];
+    }
+    return NULL;
+
+EXIT:
+    imageLoader_lock(l, flags);
+    return l->pPath[0];
+}
+
+
+
+/* find the correct path of all image files we're going to need
+ * and lock the files that need it.
+ */
+static int
+_getImagePaths(AvdInfo*  i, AvdInfoParams*  params )
+{
+    int   wipeData  = (params->flags & AVDINFO_WIPE_DATA) != 0;
+    int   wipeCache = (params->flags & AVDINFO_WIPE_CACHE) != 0;
+    int   noCache   = (params->flags & AVDINFO_NO_CACHE) != 0;
+    int   noSdCard  = (params->flags & AVDINFO_NO_SDCARD) != 0;
+
+    ImageLoader  l[1];
+
+    imageLoader_init(l, i, params);
+
+    /* pick up the kernel and ramdisk image files - these don't
+     * need a specific handling.
+     */
+    imageLoader_set ( l, AVD_IMAGE_KERNEL );
+    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK );
+
+    imageLoader_set ( l, AVD_IMAGE_RAMDISK );
+    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK | IMAGE_DONT_LOCK );
+
+    /* the system image
+     *
+     * if there is one in the content directory just lock
+     * and use it.
+     */
+    imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
+    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_SEARCH_SDK );
+
+    /* the data partition - this one is special because if it
+     * is missing, we need to copy the initial image file into it.
+     *
+     * first, try to see if it is in the content directory
+     * (or the user-provided path)
+     */
+    imageLoader_set( l, AVD_IMAGE_USERDATA );
+    if ( !imageLoader_load( l, IMAGE_OPTIONAL |
+                               IMAGE_EMPTY_IF_MISSING |
+                               IMAGE_DONT_LOCK ) )
+    {
+        /* it's not, we're going to initialize it. simply
+         * forcing a data wipe should be enough */
+        D("initializing new data partition image: %s", l->pPath[0]);
+        wipeData = 1;
+    }
+
+    if (wipeData) {
+        /* find SDK source file */
+        const char*  srcPath;
+
+        if (imageLoader_lookupSdk(l)) {
+            derror("can't locate initial %s image in SDK",
+                l->imageText);
+            exit(2);
+        }
+        srcPath = imageLoader_extractPath(l);
+
+        imageLoader_copyFrom( l, srcPath );
+        AFREE((char*) srcPath);
+    }
+    else
+    {
+        /* lock the data partition image */
+        l->pState[0] = IMAGE_STATE_MUSTLOCK;
+        imageLoader_lock( l, 0 );
+    }
+
+    /* the cache partition: unless the user doesn't want one,
+     * we're going to create it in the content directory
+     */
+    if (!noCache) {
+        imageLoader_set (l, AVD_IMAGE_CACHE);
+        imageLoader_load(l, IMAGE_OPTIONAL |
+                            IMAGE_EMPTY_IF_MISSING );
+
+        if (wipeCache) {
+            if (path_empty_file(l->pPath[0]) < 0) {
+                derror("cannot wipe %s image at %s: %s",
+                       l->imageText, l->pPath[0],
+                       strerror(errno));
+                exit(2);
+            }
+        }
+    }
+
+    /* the SD Card image. unless the user doesn't want to, we're
+     * going to mount it if available. Note that if the image is
+     * already used, we must ignore it.
+     */
+    if (!noSdCard) {
+        imageLoader_set (l, AVD_IMAGE_SDCARD);
+        imageLoader_load(l, IMAGE_OPTIONAL |
+                            IMAGE_IGNORE_IF_LOCKED);
+
+        /* if the file was not found, ignore it */
+        if (l->pPath[0] && !path_exists(l->pPath[0])) 
+        {
+            D("ignoring non-existing %s at %s: %s",
+              l->imageText, l->pPath[0], strerror(errno));
+
+            /* if the user provided the SD Card path by hand,
+             * warn him. */
+            if (params->forcePaths[AVD_IMAGE_SDCARD] != NULL)
+                dwarning("ignoring non-existing SD Card image");
+
+            imageLoader_setPath(l, NULL);
+        }
+    }
+
+    return 0;
+}
+
+/* check that a given directory contains a valid skin.
+ * returns 1 on success, 0 on failure.
+ */
+static int
+_checkSkinPath( const char*  skinPath )
+{
+    char  temp[MAX_PATH], *p=temp, *end=p+sizeof(temp);
+
+    /* for now, if it has a 'layout' file, it is a valid skin path */
+    p = bufprint(temp, end, "%s/layout", skinPath);
+    if (p >= end || !path_exists(temp))
+        return 0;
+
+    return 1;
+}
+
+/* check that there is a skin named 'skinName' listed from 'skinDirRoot'
+ * this returns 1 on success, 0 on failure
+ * on success, the 'temp' buffer will get the path containing the real
+ * skin directory (after alias expansion), including the skin name.
+ */
+static int
+_checkSkinDir( char*        temp,
+               char*        end,
+               const char*  skinDirRoot,
+               const char*  skinName )
+{
+    DirScanner*  scanner;
+    char        *p;
+    int          result;
+
+    p = bufprint(temp, end, "%s/skins/%s",
+                 skinDirRoot, skinName);
+
+    if (p >= end || !path_exists(temp)) {
+        DD("    ignore bad skin directory %s", temp);
+        return 0;
+    }
+
+    /* first, is this a normal skin directory ? */
+    if (_checkSkinPath(temp)) {
+        /* yes */
+        DD("    found skin directory: %s", temp);
+        return 1;
+    }
+
+    /* second, is it an alias to another skin ? */
+    *p      = 0;
+    result  = 0;
+    scanner = dirScanner_new(temp);
+    if (scanner != NULL) {
+        for (;;) {
+            const char*  file = dirScanner_next(scanner);
+
+            if (file == NULL)
+                break;
+
+            if (strncmp(file, "alias-", 6) || file[6] == 0)
+                continue;
+
+            p = bufprint(temp, end, "%s/skins/%s",
+                            skinDirRoot, file+6);
+
+            if (p < end && _checkSkinPath(temp)) {
+                /* yes, it's an alias */
+                DD("    skin alias '%s' points to skin directory: %s",
+                   file+6, temp);
+                result = 1;
+                break;
+            }
+        }
+        dirScanner_free(scanner);
+    }
+    return result;
+}
+
+/* try to see if the skin name leads to a magic skin or skin path directly
+ * returns 1 on success, 0 on error.
+ * on success, this sets up 'skinDirPath' and 'skinName' in the AvdInfo.
+ */
+static int
+_getSkinPathFromName( AvdInfo*  i, const char*  skinName )
+{
+    char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+    /* if the skin name has the format 'NNNNxNNN' where
+    * NNN is a decimal value, then this is a 'magic' skin
+    * name that doesn't require a skin directory
+    */
+    if (isdigit(skinName[0])) {
+        int  width, height;
+        if (sscanf(skinName, "%dx%d", &width, &height) == 2) {
+            D("'magic' skin format detected: %s", skinName);
+            i->skinName    = ASTRDUP(skinName);
+            i->skinDirPath = NULL;
+            return 1;
+        }
+    }
+
+    /* is the skin name a direct path to the skin directory ? */
+    if (_checkSkinPath(skinName)) {
+        goto FOUND_IT;
+    }
+
+    /* is the skin name a relative path from the SDK root ? */
+    p = bufprint(temp, end, "%s/%s", i->sdkRootPath, skinName);
+    if (p < end && _checkSkinPath(temp)) {
+        skinName = temp;
+        goto FOUND_IT;
+    }
+
+    /* nope */
+    return 0;
+
+FOUND_IT:
+    if (path_split(skinName, &i->skinDirPath, &i->skinName) < 0) {
+        derror("malformed skin name: %s", skinName);
+        exit(2);
+    }
+    D("found skin '%s' in directory: %s", i->skinName, i->skinDirPath);
+    return 1;
+}
+
+/* return 0 on success, -1 on error */
+static int
+_getSkin( AvdInfo*  i, AvdInfoParams*  params )
+{
+    char*  skinName;
+    char   temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+    char   explicitSkin = 1;
+
+    /* this function is used to compute the 'skinName' and 'skinDirPath'
+     * fields of the AvdInfo.
+     */
+
+    /* processing here is a bit tricky, so here's how it happens
+     *
+     * - command-line option '-skin <name>' can be used to specify the
+     *   name of a skin, to override the AVD settings.
+     *
+     * - skins are searched from <dir>/../skins for each <dir> in the
+     *   images search list, unless a '-skindir <path>' option has been
+     *   provided on the command-line
+     *
+     * - otherwise, the config.ini can also contain a SKIN_PATH key that
+     *   shall  give the full path to the skin directory, either relative
+     *   to the SDK root, or an absolute path.
+     *
+     * - skin names like '320x480' corresponds to "magic skins" that
+     *   simply display a framebuffer, without any ornaments of the
+     *   corresponding size. They do not correspond to any real skin
+     *   directory / files and are handled later. But they must be
+     *   recognized here and report a NULL skindir.
+     */
+    if (params->skinName) {
+        skinName = ASTRDUP(params->skinName);
+    } else {
+        skinName = iniFile_getString( i->configIni, SKIN_PATH );
+        explicitSkin = 0;
+    }
+
+    /* first, check that the skin name is not magic or a direct
+     * directory path
+     */
+    if (skinName != NULL && _getSkinPathFromName(i, skinName)) {
+        AFREE(skinName);
+        return 0;
+    }
+
+    /* if not, the default skinName is "HVGA" */
+    if (skinName == NULL) {
+        skinName = ASTRDUP(SKIN_DEFAULT);
+        explicitSkin = 0;
+    }
+
+    i->skinName = skinName;
+
+    /* now try to find the skin directory for that name -
+     * first try the content directory */
+    do {
+        /* if there is a single 'skin' directory in
+         * the content directory, assume that's what the
+         * user wants,  unless an explicit name was given
+         */
+        if (!explicitSkin) {
+            p = bufprint(temp, end, "%s/skin", i->contentPath);
+            if (p < end && _checkSkinPath(temp)) {
+                D("using skin content from %s", temp);
+                AFREE(i->skinName);
+                i->skinName    = ASTRDUP("skin");
+                i->skinDirPath = ASTRDUP(i->contentPath);
+                return 0;
+            }
+        }
+
+        /* look in content directory */
+        if (_checkSkinDir(temp, end, i->contentPath, skinName))
+            break;
+
+        /* look in the search paths. For each <dir> in the list,
+         * look the skins in <dir>/.. */
+        {
+            int  nn;
+            for (nn = 0; nn < i->numSearchPaths; nn++) {
+                char*  parentDir = path_parent(i->searchPaths[nn], 1);
+                int    ret;
+                if (parentDir == NULL)
+                    continue;
+                ret=_checkSkinDir(temp, end, parentDir, skinName);
+                AFREE(parentDir);
+                if (ret)
+                  break;
+            }
+            if (nn < i->numSearchPaths)
+                break;
+        }
+
+#if SUPPORT_PLATFORM_OR_ADDON
+        /* look in the add-on directory, if any */
+        if (i->addonPath && 
+            _checkSkinDir(temp, end, i->addonPath, skinName))
+            break;
+
+        /* look in the platforms directory */
+        if (_checkSkinDir(temp, end, i->platformPath, skinName))
+            break;
+#endif
+        /* didn't find it */
+        if (explicitSkin) {
+            derror("could not find directory for skin '%s',"
+                   " please use a different name", skinName);
+            exit(2);
+        } else {
+            dwarning("no skin directory matched '%s', so reverted to default",
+                     skinName);
+            AFREE(i->skinName);
+            params->skinName = SKIN_DEFAULT;
+            return _getSkin(i, params);
+        }
+
+        return -1;
+
+    } while (0);
+
+    /* separate skin name from parent directory. the skin name
+     * returned in 'temp' might be different from the original
+     * one due to alias expansion so strip it.
+     */
+    AFREE(i->skinName);
+
+    if (path_split(temp, &i->skinDirPath, &i->skinName) < 0) {
+        derror("weird skin path: %s", temp);
+        return -1;
+    }
+    DD("found skin '%s' in directory: %s", i->skinName, i->skinDirPath);
+    return 0;
+}
+
+
+AvdInfo*
+avdInfo_new( const char*  name, AvdInfoParams*  params )
+{
+    AvdInfo*  i;
+
+    if (name == NULL)
+        return NULL;
+
+    if (!_checkAvdName(name)) {
+        derror("virtual device name contains invalid characters");
+        exit(1);
+    }
+
+    ANEW0(i);
+    i->deviceName = ASTRDUP(name);
+
+    if ( _getSdkRoot(i)     < 0 ||
+         _getRootIni(i)     < 0 ||
+         _getContentPath(i) < 0 ||
+         _getConfigIni(i)   < 0 )
+        goto FAIL;
+
+    /* look for image search paths. handle post 1.1/pre cupcake
+     * obsolete SDKs.
+     */
+    _getSearchPaths(i);
+
+    if (i->numSearchPaths == 0) {
+#if SUPPORT_PLATFORM_OR_ADDON
+        /* no search paths, look for platform/add-on */
+        if (_getTarget(i) < 0)
+            goto FAIL;
+#endif
+    }
+
+    /* don't need this anymore */
+    iniFile_free(i->rootIni);
+    i->rootIni = NULL;
+
+    if ( _getImagePaths(i, params) < 0 ||
+         _getSkin      (i, params) < 0 )
+        goto FAIL;
+
+    return i;
+
+FAIL:
+    avdInfo_free(i);
+    return NULL;
+}
+
+/***************************************************************
+ ***************************************************************
+ *****
+ *****    ANDROID BUILD SUPPORT
+ *****
+ *****    The code below corresponds to the case where we're
+ *****    starting the emulator inside the Android build
+ *****    system. The main differences are that:
+ *****
+ *****    - the $ANDROID_PRODUCT_OUT directory is used as the
+ *****      content file.
+ *****
+ *****    - built images must not be modified by the emulator,
+ *****      so system.img must be copied to a temporary file
+ *****      and userdata.img must be copied to userdata-qemu.img
+ *****      if the latter doesn't exist.
+ *****
+ *****    - the kernel and default skin directory are taken from
+ *****      prebuilt
+ *****
+ *****    - there is no root .ini file, or any config.ini in
+ *****      the content directory, no SDK platform version
+ *****      and no add-on to consider.
+ *****/
+
+/* used to fake a config.ini located in the content directory */
+static int
+_getBuildConfigIni( AvdInfo*  i )
+{
+    /* a blank file is ok at the moment */
+    i->configIni = iniFile_newFromMemory( "", 0 );
+    return 0;
+}
+
+static int
+_getBuildImagePaths( AvdInfo*  i, AvdInfoParams*  params )
+{
+    int   wipeData  = (params->flags & AVDINFO_WIPE_DATA) != 0;
+    int   noCache   = (params->flags & AVDINFO_NO_CACHE) != 0;
+    int   noSdCard  = (params->flags & AVDINFO_NO_SDCARD) != 0;
+
+    char         temp[PATH_MAX], *p=temp, *end=p+sizeof temp;
+    char*        srcData;
+    ImageLoader  l[1];
+
+    imageLoader_init(l, i, params);
+
+    /** load the kernel image
+     **/
+
+    /* if it is not in the out directory, get it from prebuilt
+     */
+    imageLoader_set ( l, AVD_IMAGE_KERNEL );
+
+    if ( !imageLoader_load( l, IMAGE_OPTIONAL |
+                               IMAGE_DONT_LOCK ) )
+    {
+#define  PREBUILT_KERNEL_PATH   "prebuilt/android-arm/kernel/kernel-qemu"
+        p = bufprint(temp, end, "%s/%s", i->androidBuildRoot,
+                        PREBUILT_KERNEL_PATH);
+        if (p >= end || !path_exists(temp)) {
+            derror("bad workspace: cannot find prebuilt kernel in: %s", temp);
+            exit(1);
+        }
+        imageLoader_setPath(l, temp);
+    }
+
+    /** load the data partition. note that we use userdata-qemu.img
+     ** since we don't want to modify userdata.img at all
+     **/
+    imageLoader_set ( l, AVD_IMAGE_USERDATA );
+    imageLoader_load( l, IMAGE_OPTIONAL | IMAGE_DONT_LOCK );
+
+    /* get the path of the source file, and check that it actually exists
+     * if the user didn't provide an explicit data file
+     */
+    srcData = imageLoader_extractPath(l);
+    if (srcData == NULL && params->forcePaths[AVD_IMAGE_USERDATA] == NULL) {
+        derror("There is no %s image in your build directory. Please make a full build",
+                l->imageText, l->imageFile);
+        exit(2);
+    }
+
+    /* get the path of the target file */
+    l->imageFile = "userdata-qemu.img";
+    imageLoader_load( l, IMAGE_OPTIONAL |
+                         IMAGE_EMPTY_IF_MISSING |
+                         IMAGE_IGNORE_IF_LOCKED );
+
+    /* force a data wipe if we just created the image */
+    if (l->pState[0] == IMAGE_STATE_LOCKED_EMPTY)
+        wipeData = 1;
+
+    /* if the image was already locked, create a temp file
+     * then force a data wipe.
+     */
+    if (l->pPath[0] == NULL) {
+        TempFile*  temp = tempfile_create();
+        imageLoader_setPath(l, tempfile_path(temp));
+        dwarning( "Another emulator is running. user data changes will *NOT* be saved");
+        wipeData = 1;
+    }
+
+    /* in the case of a data wipe, copy userdata.img into
+     * the destination */
+    if (wipeData) {
+        if (srcData == NULL || !path_exists(srcData)) {
+            derror("There is no %s image in your build directory. Please make a full build",
+                   l->imageText, _imageFileNames[l->id]);
+            exit(2);
+        }
+        if (path_copy_file( l->pPath[0], srcData ) < 0) {
+            derror("could not initialize %s image from %s: %s",
+                   l->imageText, temp, strerror(errno));
+            exit(2);
+        }
+    }
+
+    AFREE(srcData);
+
+    /** load the ramdisk image
+     **/
+    imageLoader_set ( l, AVD_IMAGE_RAMDISK );
+    imageLoader_load( l, IMAGE_REQUIRED |
+                         IMAGE_DONT_LOCK );
+
+    /** load the system image. read-only. the caller must
+     ** take care of checking the state
+     **/
+    imageLoader_set ( l, AVD_IMAGE_INITSYSTEM );
+    imageLoader_load( l, IMAGE_REQUIRED | IMAGE_DONT_LOCK );
+
+    /* force the system image to read-only status */
+    l->pState[0] = IMAGE_STATE_READONLY;
+
+    /** cache partition handling
+     **/
+    if (!noCache) {
+        imageLoader_set (l, AVD_IMAGE_CACHE);
+
+        /* if the user provided one cache image, lock & use it */
+        if ( params->forcePaths[l->id] != NULL ) {
+            imageLoader_load(l, IMAGE_REQUIRED | 
+                                IMAGE_IGNORE_IF_LOCKED);
+        }
+    }
+
+    /** SD Card image
+     **/
+    if (!noSdCard) {
+        imageLoader_set (l, AVD_IMAGE_SDCARD);
+        imageLoader_load(l, IMAGE_OPTIONAL | IMAGE_IGNORE_IF_LOCKED);
+    }
+
+    return 0;
+}
+
+static int
+_getBuildSkin( AvdInfo*  i, AvdInfoParams*  params )
+{
+    /* the (current) default skin name for our build system */
+    const char*  skinName = params->skinName;
+    const char*  skinDir  = params->skinRootPath;
+    char         temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+    char*        q;
+
+    if (!skinName) {
+        /* the (current) default skin name for the build system */
+        skinName = SKIN_DEFAULT;
+        DD("selecting default skin name '%s'", skinName);
+    }
+
+    i->skinName = ASTRDUP(skinName);
+
+    if (!skinDir) {
+
+#define  PREBUILT_SKINS_DIR  "development/emulator/skins"
+
+        /* the (current) default skin directory */
+        p = bufprint( temp, end, "%s/%s",
+                      i->androidBuildRoot, PREBUILT_SKINS_DIR );
+    } else {
+        p = bufprint( temp, end, "%s", skinDir );
+    }
+
+    q  = bufprint(p, end, "/%s/layout", skinName);
+    if (q >= end || !path_exists(temp)) {
+        DD("skin content directory does not exist: %s", temp);
+        if (skinDir)
+            dwarning("could not find valid skin '%s' in %s:\n",
+                     skinName, temp);
+        return -1;
+    }
+    *p = 0;
+    DD("found skin path: %s", temp);
+    i->skinDirPath = ASTRDUP(temp);
+
+    return 0;
+}
+
+AvdInfo*
+avdInfo_newForAndroidBuild( const char*     androidBuildRoot,
+                            const char*     androidOut,
+                            AvdInfoParams*  params )
+{
+    AvdInfo*  i;
+
+    ANEW0(i);
+
+    i->inAndroidBuild   = 1;
+    i->androidBuildRoot = ASTRDUP(androidBuildRoot);
+    i->androidOut       = ASTRDUP(androidOut);
+    i->contentPath      = ASTRDUP(androidOut);
+
+    /* TODO: find a way to provide better information from the build files */
+    i->deviceName = ASTRDUP("<build>");
+
+    if (_getBuildConfigIni(i)          < 0 ||
+        _getBuildImagePaths(i, params) < 0 )
+        goto FAIL;
+
+    /* we don't need to fail if there is no valid skin */
+    _getBuildSkin(i, params);
+
+    return i;
+
+FAIL:
+    avdInfo_free(i);
+    return NULL;
+}
+
+const char*
+avdInfo_getName( AvdInfo*  i )
+{
+    return i ? i->deviceName : NULL;
+}
+
+const char*
+avdInfo_getImageFile( AvdInfo*  i, AvdImageType  imageType )
+{
+    if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX)
+        return NULL;
+
+    return i->imagePath[imageType];
+}
+
+int
+avdInfo_isImageReadOnly( AvdInfo*  i, AvdImageType  imageType )
+{
+    if (i == NULL || (unsigned)imageType >= AVD_IMAGE_MAX)
+        return 1;
+
+    return (i->imageState[imageType] == IMAGE_STATE_READONLY);
+}
+
+const char*
+avdInfo_getSkinName( AvdInfo*  i )
+{
+    return i->skinName;
+}
+
+const char*
+avdInfo_getSkinDir ( AvdInfo*  i )
+{
+    return i->skinDirPath;
+}
+
+int
+avdInfo_getHwConfig( AvdInfo*  i, AndroidHwConfig*  hw )
+{
+    IniFile*   ini = i->configIni;
+    int        ret;
+
+    if (ini == NULL)
+        ini = iniFile_newFromMemory("", 0);
+
+    ret = androidHwConfig_read(hw, ini);
+
+    if (ini != i->configIni)
+        iniFile_free(ini);
+
+    return ret;
+}
+
+const char*
+avdInfo_getContentPath( AvdInfo*  i )
+{
+    return i->contentPath;
+}
+
+int
+avdInfo_inAndroidBuild( AvdInfo*  i )
+{
+    return i->inAndroidBuild;
+}
+
+char*
+avdInfo_getTracePath( AvdInfo*  i, const char*  traceName )
+{
+    char   tmp[MAX_PATH], *p=tmp, *end=p + sizeof(tmp);
+
+    if (i == NULL || traceName == NULL || traceName[0] == 0)
+        return NULL;
+
+    if (i->inAndroidBuild) {
+        p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s",
+                      i->androidOut, traceName );
+    } else {
+        p = bufprint( p, end, "%s" PATH_SEP "traces" PATH_SEP "%s",
+                      i->contentPath, traceName );
+    }
+    return ASTRDUP(tmp);
+}
diff --git a/android/avd/info.h b/android/avd/info.h
new file mode 100644
index 0000000..6cd97dc
--- /dev/null
+++ b/android/avd/info.h
@@ -0,0 +1,171 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef ANDROID_AVD_INFO_H
+#define ANDROID_AVD_INFO_H
+
+#include "android/utils/ini.h"
+#include "android/avd/hw-config.h"
+
+/* An Android Virtual Device (AVD for short) corresponds to a
+ * directory containing all kernel/disk images for a given virtual
+ * device, as well as information about its hardware capabilities,
+ * SDK version number, skin, etc...
+ *
+ * Each AVD has a human-readable name and is backed by a root
+ * configuration file and a content directory. For example, an
+ *  AVD named 'foo' will correspond to the following:
+ *
+ *  - a root configuration file named ~/.android/avd/foo.ini
+ *    describing where the AVD's content can be found
+ *
+ *  - a content directory like ~/.android/avd/foo/ containing all
+ *    disk image and configuration files for the virtual device.
+ *
+ * the 'foo.ini' file should contain at least one line of the form:
+ *
+ *    rootPath=<content-path>
+ *
+ * it may also contain other lines that cache stuff found in the
+ * content directory, like hardware properties or SDK version number.
+ *
+ * it is possible to move the content directory by updating the foo.ini
+ * file to point to the new location. This can be interesting when your
+ * $HOME directory is located on a network share or in a roaming profile
+ * (Windows), given that the content directory of a single virtual device
+ * can easily use more than 100MB of data.
+ *
+ */
+
+/* a macro used to define the list of disk images managed by the
+ * implementation. This macro will be expanded several times with
+ * varying definitions of _AVD_IMG
+ */
+#define  AVD_IMAGE_LIST \
+    _AVD_IMG(KERNEL,"kernel-qemu","kernel") \
+    _AVD_IMG(RAMDISK,"ramdisk.img","ramdisk") \
+    _AVD_IMG(INITSYSTEM,"system.img","init system") \
+    _AVD_IMG(INITDATA,"userdata.img","init data") \
+    _AVD_IMG(USERSYSTEM,"system-qemu.img","user system") \
+    _AVD_IMG(USERDATA,"userdata-qemu.img", "user data") \
+    _AVD_IMG(CACHE,"cache.img","cache") \
+    _AVD_IMG(SDCARD,"sdcard.img","SD Card") \
+
+/* define the enumared values corresponding to each AVD image type
+ * examples are: AVD_IMAGE_KERNEL, AVD_IMAGE_SYSTEM, etc..
+ */
+#define _AVD_IMG(x,y,z)   AVD_IMAGE_##x ,
+typedef enum {
+    AVD_IMAGE_LIST
+    AVD_IMAGE_MAX /* do not remove */
+} AvdImageType;
+#undef  _AVD_IMG
+
+/* AvdInfo is an opaque structure used to model the information
+ * corresponding to a given AVD instance
+ */
+typedef struct AvdInfo  AvdInfo;
+
+/* various flags used when creating an AvdInfo object */
+typedef enum {
+    /* use to force a data wipe */
+    AVDINFO_WIPE_DATA = (1 << 0),
+    /* use to ignore the cache partition */
+    AVDINFO_NO_CACHE  = (1 << 1),
+    /* use to wipe cache partition, ignored if NO_CACHE is set */
+    AVDINFO_WIPE_CACHE = (1 << 2),
+    /* use to ignore ignore SDCard image (default or provided) */
+    AVDINFO_NO_SDCARD = (1 << 3),
+    /* use to wipe the system image with new initial values */
+    AVDINFO_WIPE_SYSTEM = (1 << 4),
+} AvdFlags;
+
+typedef struct {
+    unsigned     flags;
+    const char*  skinName;
+    const char*  skinRootPath;
+    const char*  forcePaths[AVD_IMAGE_MAX];
+} AvdInfoParams;
+
+/* Creates a new AvdInfo object from a name. Returns NULL if name is NULL
+ * or contains characters that are not part of the following list:
+ * letters, digits, underscores, dashes and periods
+ */
+AvdInfo*  avdInfo_new( const char*  name, AvdInfoParams*  params );
+
+/* A special function used to setup an AvdInfo for use when starting
+ * the emulator from the Android build system. In this specific instance
+ * we're going to create temporary files to hold all writable image
+ * files, and activate all hardware features by default
+ *
+ * 'androidBuildRoot' must be the absolute path to the root of the
+ * Android build system (i.e. the 'android' directory)
+ *
+ * 'androidOut' must be the target-specific out directory where
+ * disk images will be looked for.
+ */
+AvdInfo*  avdInfo_newForAndroidBuild( const char*     androidBuildRoot,
+                                      const char*     androidOut,
+                                      AvdInfoParams*  params );
+
+/* Frees an AvdInfo object and the corresponding strings that may be
+ * returned by its getXXX() methods
+ */
+void        avdInfo_free( AvdInfo*  i );
+
+/* Return the name of the Android Virtual Device
+ */
+const char*  avdInfo_getName( AvdInfo*  i );
+
+/* Try to find the path of a given image file, returns NULL
+ * if the corresponding file could not be found. the string
+ * belongs to the AvdInfo object.
+ */
+const char*  avdInfo_getImageFile( AvdInfo*  i, AvdImageType  imageType );
+
+/* Returns 1 if the corresponding image file is read-only
+ */
+int          avdInfo_isImageReadOnly( AvdInfo*  i, AvdImageType  imageType );
+
+/* lock an image file if it is writable. returns 0 on success, or -1
+ * otherwise. note that if the file is read-only, it doesn't need to
+ * be locked and the function will return success.
+ */
+int          avdInfo_lockImageFile( AvdInfo*  i, AvdImageType  imageType, int  abortOnError);
+
+/* Manually set the path of a given image file. */
+void         avdInfo_setImageFile( AvdInfo*  i, AvdImageType  imageType, const char*  imagePath );
+
+/* Returns the path of the skin directory */
+/* the string belongs to the AvdInfo object */
+const char*  avdInfo_getSkinPath( AvdInfo*  i );
+
+/* Returns the name of the virtual device's skin */
+const char*  avdInfo_getSkinName( AvdInfo*  i );
+
+/* Returns the root skin directory for this device */
+const char*  avdInfo_getSkinDir ( AvdInfo*  i );
+
+/* Returns the content path of the virtual device */
+const char*  avdInfo_getContentPath( AvdInfo*  i );
+
+/* Returns TRUE iff in the Android build system */
+int          avdInfo_inAndroidBuild( AvdInfo*  i );
+
+/* Reads the AVD's hardware configuration into 'hw'. returns -1 on error, 0 otherwise */
+int          avdInfo_getHwConfig( AvdInfo*  i, AndroidHwConfig*  hw );
+
+/* Returns a *copy* of the path used to store trace 'foo'. result must be freed by caller */
+char*        avdInfo_getTracePath( AvdInfo*  i, const char*  traceName );
+
+/* */
+
+#endif /* ANDROID_AVD_INFO_H */
diff --git a/android/build/binary.make b/android/build/binary.make
new file mode 100644
index 0000000..1c75d52
--- /dev/null
+++ b/android/build/binary.make
@@ -0,0 +1,34 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# definitions shared by host_executable.make and host_static_library.make
+#
+
+# the directory where we're going to place our object files
+LOCAL_OBJS_DIR  := $(call intermediates-dir-for,EXECUTABLES,$(LOCAL_MODULE))
+LOCAL_OBJECTS   :=
+LOCAL_CC        ?= $(CC)
+LOCAL_C_SOURCES := $(filter  %.c,$(LOCAL_SRC_FILES))
+LOCAL_OBJC_SOURCES := $(filter %.m,$(LOCAL_SRC_FILES))
+
+$(foreach src,$(LOCAL_C_SOURCES), \
+    $(eval $(call compile-c-source,$(src))) \
+)
+
+$(foreach src,$(LOCAL_OBJC_SOURCES), \
+    $(eval $(call compile-objc-source,$(src))) \
+)
+
+CLEAN_OBJS_DIRS += $(LOCAL_OBJS_DIR)
diff --git a/android/build/clear_vars.make b/android/build/clear_vars.make
new file mode 100644
index 0000000..a9289b0
--- /dev/null
+++ b/android/build/clear_vars.make
@@ -0,0 +1,30 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# called multiple times to clear variables used to define a given 'module'
+#
+LOCAL_NO_DEFAULT_COMPILER_FLAGS:=
+LOCAL_CC :=
+LOCAL_CXX :=
+LOCAL_CFLAGS :=
+LOCAL_LDFLAGS :=
+LOCAL_LDLIBS :=
+LOCAL_SRC_FILES :=
+LOCAL_MODULE :=
+LOCAL_MODULE_PATH:=
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_BUILT_MODULE :=
+LOCAL_PREBUILT_OBJ_FILES :=
+
diff --git a/android/build/definitions.make b/android/build/definitions.make
new file mode 100644
index 0000000..cd03f89
--- /dev/null
+++ b/android/build/definitions.make
@@ -0,0 +1,109 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# shared definitions
+ifeq ($(strip $(SHOW)),)
+define pretty
+@echo $1
+endef
+hide := @
+else
+define pretty
+endef
+hide := 
+endif
+
+define my-dir
+.
+endef
+
+# return the directory containing the intermediate files for a given
+# kind of executable
+# $1 = type (EXECUTABLES or STATIC_LIBRARIES) 
+# $2 = module name
+# $3 = ignored
+#
+define intermediates-dir-for
+$(OBJS_DIR)/intermediates/$(2)
+endef
+
+# Generate the full path of a given static library
+define library-path
+$(OBJS_DIR)/$(1).a
+endef
+
+define executable-path
+$(OBJS_DIR)/$(1)$(EXE)
+endef
+
+# Compile a C source file
+#
+define  compile-c-source
+SRC:=$(1)
+OBJ:=$$(LOCAL_OBJS_DIR)/$$(SRC:%.c=%.o)
+LOCAL_OBJECTS += $$(OBJ)
+DEPENDENCY_DIRS += $$(dir $$(OBJ))
+$$(OBJ): PRIVATE_CFLAGS := $$(CFLAGS) $$(LOCAL_CFLAGS) -I$$(LOCAL_PATH) -I$$(OBJS_DIR)
+$$(OBJ): PRIVATE_CC     := $$(LOCAL_CC)
+$$(OBJ): PRIVATE_OBJ    := $$(OBJ)
+$$(OBJ): PRIVATE_MODULE := $$(LOCAL_MODULE)
+$$(OBJ): PRIVATE_SRC    := $$(SRC_PATH)/$$(SRC)
+$$(OBJ): PRIVATE_SRC0   := $$(SRC)
+$$(OBJ): $$(SRC_PATH)/$$(SRC)
+	@mkdir -p $$(dir $$(PRIVATE_OBJ))
+	@echo "Compile: $$(PRIVATE_MODULE) <= $$(PRIVATE_SRC0)"
+	$(hide) $$(PRIVATE_CC) $$(PRIVATE_CFLAGS) -c -o $$(PRIVATE_OBJ) -MMD -MP -MF $$(PRIVATE_OBJ).d.tmp $$(PRIVATE_SRC)
+	$(hide) $$(SRC_PATH)/android/build/mkdeps.sh $$(PRIVATE_OBJ) $$(PRIVATE_OBJ).d.tmp $$(PRIVATE_OBJ).d
+endef
+
+# Compile an Objective-C source file
+#
+define  compile-objc-source
+SRC:=$(1)
+OBJ:=$$(LOCAL_OBJS_DIR)/$$(SRC:%.m=%.o)
+LOCAL_OBJECTS += $$(OBJ)
+DEPENDENCY_DIRS += $$(dir $$(OBJ))
+$$(OBJ): PRIVATE_CFLAGS := $$(CFLAGS) $$(LOCAL_CFLAGS) -I$$(LOCAL_PATH) -I$$(OBJS_DIR)
+$$(OBJ): PRIVATE_CC     := $$(LOCAL_CC)
+$$(OBJ): PRIVATE_OBJ    := $$(OBJ)
+$$(OBJ): PRIVATE_MODULE := $$(LOCAL_MODULE)
+$$(OBJ): PRIVATE_SRC    := $$(SRC_PATH)/$$(SRC)
+$$(OBJ): PRIVATE_SRC0   := $$(SRC)
+$$(OBJ): $$(SRC_PATH)/$$(SRC)
+	@mkdir -p $$(dir $$(PRIVATE_OBJ))
+	@echo "Compile: $$(PRIVATE_MODULE) <= $$(PRIVATE_SRC0)"
+	$(hide) $$(PRIVATE_CC) $$(PRIVATE_CFLAGS) -c -o $$(PRIVATE_OBJ) -MMD -MP -MF $$(PRIVATE_OBJ).d.tmp $$(PRIVATE_SRC)
+	$(hide) $$(SRC_PATH)/android/build/mkdeps.sh $$(PRIVATE_OBJ) $$(PRIVATE_OBJ).d.tmp $$(PRIVATE_OBJ).d
+endef
+
+# for now, we only use prebuilt SDL libraries, so copy them
+define copy-prebuilt-lib
+_SRC := $(1)
+_SRC1 := $$(notdir $$(_SRC))
+_DST := $$(OBJS_DIR)/$$(_SRC1)
+LIBRARIES += $$(_DST)
+$$(_DST): PRIVATE_DST := $$(_DST)
+$$(_DST): PRIVATE_SRC := $$(_SRC)
+$$(_DST): $$(_SRC)
+	@mkdir -p $$(dir $$(PRIVATE_DST))
+	@echo "Prebuilt: $$(PRIVATE_DST)"
+	$(hide) cp -f $$(PRIVATE_SRC) $$(PRIVATE_DST)
+endef
+
+define  create-dir
+$(1):
+	mkdir -p $(1)
+endef
+
diff --git a/android/build/getdir.make b/android/build/getdir.make
new file mode 100644
index 0000000..a4dadd3
--- /dev/null
+++ b/android/build/getdir.make
@@ -0,0 +1,19 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# used to return in 'dir' the name of the current operating system
+# we really get the value from the configuration script
+#
+dir := $(HOST_OS)
diff --git a/android/build/host_executable.make b/android/build/host_executable.make
new file mode 100644
index 0000000..62f4762
--- /dev/null
+++ b/android/build/host_executable.make
@@ -0,0 +1,34 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# first, call a library containing all object files
+LOCAL_BUILT_MODULE := $(call executable-path,$(LOCAL_MODULE))
+LOCAL_CC ?= $(CC)
+include $(BUILD_SYSTEM)/binary.make
+
+LOCAL_LDLIBS := $(foreach lib,$(LOCAL_STATIC_LIBRARIES),$(call library-path,$(lib))) $(LOCAL_LDLIBS)
+
+$(LOCAL_BUILT_MODULE): PRIVATE_LDFLAGS := $(LDFLAGS) $(LOCAL_LDFLAGS)
+$(LOCAL_BUILT_MODULE): PRIVATE_LDLIBS  := $(LOCAL_LDLIBS)
+$(LOCAL_BUILT_MODULE): PRIVATE_OBJS    := $(LOCAL_OBJECTS)
+
+$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
+	@ mkdir -p $(dir $@)
+	@ echo "Executable: $@"
+	$(hide) $(LD) $(PRIVATE_LDFLAGS) -o $@ $(PRIVATE_LIBRARY) $(PRIVATE_OBJS) $(PRIVATE_LDLIBS)
+
+EXECUTABLES += $(LOCAL_BUILT_MODULE)
+$(LOCAL_BUILT_MODULE): $(foreach lib,$(LOCAL_STATIC_LIBRARIES),$(call library-path,$(lib)))
+
diff --git a/android/build/host_static_library.make b/android/build/host_static_library.make
new file mode 100644
index 0000000..3de5a99
--- /dev/null
+++ b/android/build/host_static_library.make
@@ -0,0 +1,35 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# build a host executable, the name of the final executable should be
+# put in LOCAL_BUILT_MODULE for use by the caller
+#
+
+#$(info STATIC_LIBRARY SRCS=$(LOCAL_SRC_FILES))
+LOCAL_BUILT_MODULE := $(call library-path,$(LOCAL_MODULE))
+LOCAL_CC ?= $(CC)
+include $(BUILD_SYSTEM)/binary.make
+
+LOCAL_AR ?= $(AR)
+ARFLAGS := crs
+
+$(LOCAL_BUILT_MODULE): PRIVATE_AR := $(LOCAL_AR)
+$(LOCAL_BUILT_MODULE): PRIVATE_OBJECTS := $(LOCAL_OBJECTS)
+$(LOCAL_BUILT_MODULE): $(LOCAL_OBJECTS)
+	@mkdir -p $(dir $@)
+	@echo "Library: $@"
+	$(hide) $(PRIVATE_AR) $(ARFLAGS) $@ $(PRIVATE_OBJECTS)
+
+LIBRARIES += $(LOCAL_BUILT_MODULE)
diff --git a/android/build/mkdeps.sh b/android/build/mkdeps.sh
new file mode 100755
index 0000000..abecec7
--- /dev/null
+++ b/android/build/mkdeps.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# This script is used to transform the dependency files generated by GCC
+# For example, a typical .d file will have a line like:
+#
+#    source.o: /full/path/to/source.c other.h headers.h
+#    ...
+#
+# the script is used to replace 'source.o' to a full path, as in
+#
+#    objs/intermediates/emulator/source.o: /full/path/to/source.c other.h headers.h
+#
+# parameters
+#
+# $1: object file (full path)
+# $2: source dependency file to modify (erased on success)
+# $3: target source dependency file
+#
+
+# quote the object path. we change a single '.' into
+# a '\.' since this will be parsed by sed.
+#
+OBJECT=`echo $1 | sed -e s/\\\\./\\\\\\\\./g`
+#echo OBJECT=$OBJECT
+
+OBJ_NAME=`basename $OBJECT`
+#echo OBJ_NAME=$OBJ_NAME
+
+# we replace $OBJ_NAME with $OBJECT only if $OBJ_NAME starts the line
+# that's because some versions of GCC (e.g. 4.2.3) already produce
+# a correct dependency line with the full path to the object file.
+# In this case, we don't want to touch anything
+#
+cat $2 | sed -e s%^$OBJ_NAME%$OBJECT%g > $3 && rm -f $2
+
+
+
diff --git a/android/charmap.c b/android/charmap.c
new file mode 100644
index 0000000..c8ed2d6
--- /dev/null
+++ b/android/charmap.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/charmap.h"
+
+/* the following is automatically generated by the 'gen-charmap.py' script
+ * do not touch. the generation command was:
+ *   gen-charmap.py qwerty.kcm qwerty2.kcm
+ */
+
+static const AKeyEntry  _qwerty_keys[] =
+{
+   /* keycode                   base   caps    fn  caps+fn   number */
+
+    { kKeyCodeA             ,   'a',   'A',   '#',   0x00,   '#' },
+    { kKeyCodeB             ,   'b',   'B',   '<',   0x00,   '<' },
+    { kKeyCodeC             ,   'c',   'C',   '9', 0x00E7,   '9' },
+    { kKeyCodeD             ,   'd',   'D',   '5',   0x00,   '5' },
+    { kKeyCodeE             ,   'e',   'E',   '2', 0x0301,   '2' },
+    { kKeyCodeF             ,   'f',   'F',   '6', 0x00A5,   '6' },
+    { kKeyCodeG             ,   'g',   'G',   '-',    '_',   '-' },
+    { kKeyCodeH             ,   'h',   'H',   '[',    '{',   '[' },
+    { kKeyCodeI             ,   'i',   'I',   '$', 0x0302,   '$' },
+    { kKeyCodeJ             ,   'j',   'J',   ']',    '}',   ']' },
+    { kKeyCodeK             ,   'k',   'K',   '"',    '~',   '"' },
+    { kKeyCodeL             ,   'l',   'L',  '\'',    '`',  '\'' },
+    { kKeyCodeM             ,   'm',   'M',   '!',   0x00,   '!' },
+    { kKeyCodeN             ,   'n',   'N',   '>', 0x0303,   '>' },
+    { kKeyCodeO             ,   'o',   'O',   '(',   0x00,   '(' },
+    { kKeyCodeP             ,   'p',   'P',   ')',   0x00,   ')' },
+    { kKeyCodeQ             ,   'q',   'Q',   '*', 0x0300,   '*' },
+    { kKeyCodeR             ,   'r',   'R',   '3', 0x20AC,   '3' },
+    { kKeyCodeS             ,   's',   'S',   '4', 0x00DF,   '4' },
+    { kKeyCodeT             ,   't',   'T',   '+', 0x00A3,   '+' },
+    { kKeyCodeU             ,   'u',   'U',   '&', 0x0308,   '&' },
+    { kKeyCodeV             ,   'v',   'V',   '=',    '^',   '=' },
+    { kKeyCodeW             ,   'w',   'W',   '1',   0x00,   '1' },
+    { kKeyCodeX             ,   'x',   'X',   '8',   0x00,   '8' },
+    { kKeyCodeY             ,   'y',   'Y',   '%', 0x00A1,   '%' },
+    { kKeyCodeZ             ,   'z',   'Z',   '7',   0x00,   '7' },
+    { kKeyCodeComma         ,   ',',   ';',   ';',    '|',   ',' },
+    { kKeyCodePeriod        ,   '.',   ':',   ':', 0x2026,   '.' },
+    { kKeyCodeAt            ,   '@',   '0',   '0', 0x2022,   '0' },
+    { kKeyCodeSlash         ,   '/',   '?',   '?',   '\\',   '/' },
+    { kKeyCodeSpace         ,  0x20,  0x20,   0x9,    0x9,  0x20 },
+    { kKeyCodeNewline       ,   0xa,   0xa,   0xa,    0xa,   0xa },
+    { kKeyCodeTab           ,   0x9,   0x9,   0x9,    0x9,   0x9 },
+    { kKeyCode0             ,   '0',   ')',   '0',    ')',   '0' },
+    { kKeyCode1             ,   '1',   '!',   '1',    '!',   '1' },
+    { kKeyCode2             ,   '2',   '@',   '2',    '@',   '2' },
+    { kKeyCode3             ,   '3',   '#',   '3',    '#',   '3' },
+    { kKeyCode4             ,   '4',   '$',   '4',    '$',   '4' },
+    { kKeyCode5             ,   '5',   '%',   '5',    '%',   '5' },
+    { kKeyCode6             ,   '6',   '^',   '6',    '^',   '6' },
+    { kKeyCode7             ,   '7',   '&',   '7',    '&',   '7' },
+    { kKeyCode8             ,   '8',   '*',   '8',    '*',   '8' },
+    { kKeyCode9             ,   '9',   '(',   '9',    '(',   '9' },
+    { kKeyCodeGrave         ,   '`',   '~',   '`',    '~',   '`' },
+    { kKeyCodeMinus         ,   '-',   '_',   '-',    '_',   '-' },
+    { kKeyCodeEquals        ,   '=',   '+',   '=',    '+',   '=' },
+    { kKeyCodeLeftBracket   ,   '[',   '{',   '[',    '{',   '[' },
+    { kKeyCodeRightBracket  ,   ']',   '}',   ']',    '}',   ']' },
+    { kKeyCodeBackslash     ,  '\\',   '|',  '\\',    '|',  '\\' },
+    { kKeyCodeSemicolon     ,   ';',   ':',   ';',    ':',   ';' },
+    { kKeyCodeApostrophe    ,  '\'',   '"',  '\'',    '"',  '\'' },
+};
+
+static const AKeyCharmap  _qwerty_charmap =
+{
+    _qwerty_keys,
+    51,
+    "qwerty"
+};
+
+static const AKeyEntry  _qwerty2_keys[] =
+{
+   /* keycode                   base   caps    fn  caps+fn   number */
+
+    { kKeyCodeA             ,   'a',   'A',   'a',    'A',   'a' },
+    { kKeyCodeB             ,   'b',   'B',   'b',    'B',   'b' },
+    { kKeyCodeC             ,   'c',   'C', 0x00e7, 0x00E7,   'c' },
+    { kKeyCodeD             ,   'd',   'D',  '\'',   '\'',  '\'' },
+    { kKeyCodeE             ,   'e',   'E',   '"', 0x0301,   '"' },
+    { kKeyCodeF             ,   'f',   'F',   '[',    '[',   '[' },
+    { kKeyCodeG             ,   'g',   'G',   ']',    ']',   ']' },
+    { kKeyCodeH             ,   'h',   'H',   '<',    '<',   '<' },
+    { kKeyCodeI             ,   'i',   'I',   '-', 0x0302,   '-' },
+    { kKeyCodeJ             ,   'j',   'J',   '>',    '>',   '>' },
+    { kKeyCodeK             ,   'k',   'K',   ';',    '~',   ';' },
+    { kKeyCodeL             ,   'l',   'L',   ':',    '`',   ':' },
+    { kKeyCodeM             ,   'm',   'M',   '%',   0x00,   '%' },
+    { kKeyCodeN             ,   'n',   'N',  0x00, 0x0303,   'n' },
+    { kKeyCodeO             ,   'o',   'O',   '+',    '+',   '+' },
+    { kKeyCodeP             ,   'p',   'P',   '=', 0x00A5,   '=' },
+    { kKeyCodeQ             ,   'q',   'Q',   '|', 0x0300,   '|' },
+    { kKeyCodeR             ,   'r',   'R',   '`', 0x20AC,   '`' },
+    { kKeyCodeS             ,   's',   'S',  '\\', 0x00DF,  '\\' },
+    { kKeyCodeT             ,   't',   'T',   '{', 0x00A3,   '}' },
+    { kKeyCodeU             ,   'u',   'U',   '_', 0x0308,   '_' },
+    { kKeyCodeV             ,   'v',   'V',   'v',    'V',   'v' },
+    { kKeyCodeW             ,   'w',   'W',   '~',    '~',   '~' },
+    { kKeyCodeX             ,   'x',   'X',   'x',    'X',   'x' },
+    { kKeyCodeY             ,   'y',   'Y',   '}', 0x00A1,   '}' },
+    { kKeyCodeZ             ,   'z',   'Z',   'z',    'Z',   'z' },
+    { kKeyCodeComma         ,   ',',   '<',   ',',    ',',   ',' },
+    { kKeyCodePeriod        ,   '.',   '>',   '.', 0x2026,   '.' },
+    { kKeyCodeAt            ,   '@',   '@',   '@', 0x2022,   '@' },
+    { kKeyCodeSlash         ,   '/',   '?',   '?',    '?',   '/' },
+    { kKeyCodeSpace         ,  0x20,  0x20,   0x9,    0x9,  0x20 },
+    { kKeyCodeNewline       ,   0xa,   0xa,   0xa,    0xa,   0xa },
+    { kKeyCode0             ,   '0',   ')',   ')',    ')',   '0' },
+    { kKeyCode1             ,   '1',   '!',   '!',    '!',   '1' },
+    { kKeyCode2             ,   '2',   '@',   '@',    '@',   '2' },
+    { kKeyCode3             ,   '3',   '#',   '#',    '#',   '3' },
+    { kKeyCode4             ,   '4',   '$',   '$',    '$',   '4' },
+    { kKeyCode5             ,   '5',   '%',   '%',    '%',   '5' },
+    { kKeyCode6             ,   '6',   '^',   '^',    '^',   '6' },
+    { kKeyCode7             ,   '7',   '&',   '&',    '&',   '7' },
+    { kKeyCode8             ,   '8',   '*',   '*',    '*',   '8' },
+    { kKeyCode9             ,   '9',   '(',   '(',    '(',   '9' },
+    { kKeyCodeTab           ,   0x9,   0x9,   0x9,    0x9,   0x9 },
+    { kKeyCodeGrave         ,   '`',   '~',   '`',    '~',   '`' },
+    { kKeyCodeMinus         ,   '-',   '_',   '-',    '_',   '-' },
+    { kKeyCodeEquals        ,   '=',   '+',   '=',    '+',   '=' },
+    { kKeyCodeLeftBracket   ,   '[',   '{',   '[',    '{',   '[' },
+    { kKeyCodeRightBracket  ,   ']',   '}',   ']',    '}',   ']' },
+    { kKeyCodeBackslash     ,  '\\',   '|',  '\\',    '|',  '\\' },
+    { kKeyCodeSemicolon     ,   ';',   ':',   ';',    ':',   ';' },
+    { kKeyCodeApostrophe    ,  '\'',   '"',  '\'',    '"',  '\'' },
+};
+
+static const AKeyCharmap  _qwerty2_charmap =
+{
+    _qwerty2_keys,
+    51,
+    "qwerty2"
+};
+
+const AKeyCharmap*  android_charmaps[2] = { &_qwerty_charmap , &_qwerty2_charmap };
+const int           android_charmap_count = 2;
diff --git a/android/charmap.h b/android/charmap.h
new file mode 100644
index 0000000..f300e68
--- /dev/null
+++ b/android/charmap.h
@@ -0,0 +1,133 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_charmap_h
+#define _android_charmap_h
+
+#include "linux_keycodes.h"
+
+/* Keep it consistent with linux/input.h */
+typedef enum {
+    kKeyCodeSoftLeft                = KEY_SOFT1,
+    kKeyCodeSoftRight               = KEY_SOFT2,
+    kKeyCodeHome                    = KEY_HOME,
+    kKeyCodeBack                    = KEY_BACK,
+    kKeyCodeCall                    = KEY_SEND,
+    kKeyCodeEndCall                 = KEY_END,
+    kKeyCode0                       = KEY_0,
+    kKeyCode1                       = KEY_1,
+    kKeyCode2                       = KEY_2,
+    kKeyCode3                       = KEY_3,
+    kKeyCode4                       = KEY_4,
+    kKeyCode5                       = KEY_5,
+    kKeyCode6                       = KEY_6,
+    kKeyCode7                       = KEY_7,
+    kKeyCode8                       = KEY_8,
+    kKeyCode9                       = KEY_9,
+    kKeyCodeStar                    = KEY_STAR,
+    kKeyCodePound                   = KEY_SHARP,
+    kKeyCodeDpadUp                  = KEY_UP,
+    kKeyCodeDpadDown                = KEY_DOWN,
+    kKeyCodeDpadLeft                = KEY_LEFT,
+    kKeyCodeDpadRight               = KEY_RIGHT,
+    kKeyCodeDpadCenter              = KEY_CENTER,
+    kKeyCodeVolumeUp                = KEY_VOLUMEUP,
+    kKeyCodeVolumeDown              = KEY_VOLUMEDOWN,
+    kKeyCodePower                   = KEY_POWER,
+    kKeyCodeCamera                  = KEY_CAMERA,
+    kKeyCodeClear                   = KEY_CLEAR,
+    kKeyCodeA                       = KEY_A,
+    kKeyCodeB                       = KEY_B,
+    kKeyCodeC                       = KEY_C,
+    kKeyCodeD                       = KEY_D,
+    kKeyCodeE                       = KEY_E,
+    kKeyCodeF                       = KEY_F,
+    kKeyCodeG                       = KEY_G,
+    kKeyCodeH                       = KEY_H,
+    kKeyCodeI                       = KEY_I,
+    kKeyCodeJ                       = KEY_J,
+    kKeyCodeK                       = KEY_K,
+    kKeyCodeL                       = KEY_L,
+    kKeyCodeM                       = KEY_M,
+    kKeyCodeN                       = KEY_N,
+    kKeyCodeO                       = KEY_O,
+    kKeyCodeP                       = KEY_P,
+    kKeyCodeQ                       = KEY_Q,
+    kKeyCodeR                       = KEY_R,
+    kKeyCodeS                       = KEY_S,
+    kKeyCodeT                       = KEY_T,
+    kKeyCodeU                       = KEY_U,
+    kKeyCodeV                       = KEY_V,
+    kKeyCodeW                       = KEY_W,
+    kKeyCodeX                       = KEY_X,
+    kKeyCodeY                       = KEY_Y,
+    kKeyCodeZ                       = KEY_Z,
+
+    kKeyCodeComma                   = KEY_COMMA,
+    kKeyCodePeriod                  = KEY_DOT,
+    kKeyCodeAltLeft                 = KEY_LEFTALT,
+    kKeyCodeAltRight                = KEY_RIGHTALT,
+    kKeyCodeCapLeft                 = KEY_LEFTSHIFT,
+    kKeyCodeCapRight                = KEY_RIGHTSHIFT,
+    kKeyCodeTab                     = KEY_TAB,
+    kKeyCodeSpace                   = KEY_SPACE,
+    kKeyCodeSym                     = KEY_COMPOSE,
+    kKeyCodeExplorer                = KEY_WWW,
+    kKeyCodeEnvelope                = KEY_MAIL,
+    kKeyCodeNewline                 = KEY_ENTER,
+    kKeyCodeDel                     = KEY_BACKSPACE,
+    kKeyCodeGrave                   = 399,
+    kKeyCodeMinus                   = KEY_MINUS,
+    kKeyCodeEquals                  = KEY_EQUAL,
+    kKeyCodeLeftBracket             = KEY_LEFTBRACE,
+    kKeyCodeRightBracket            = KEY_RIGHTBRACE,
+    kKeyCodeBackslash               = KEY_BACKSLASH,
+    kKeyCodeSemicolon               = KEY_SEMICOLON,
+    kKeyCodeApostrophe              = KEY_APOSTROPHE,
+    kKeyCodeSlash                   = KEY_SLASH,
+    kKeyCodeAt                      = KEY_EMAIL,
+    kKeyCodeNum                     = KEY_NUM,
+    kKeyCodeHeadsetHook             = KEY_HEADSETHOOK,
+    kKeyCodeFocus                   = KEY_FOCUS,
+    kKeyCodePlus                    = KEY_PLUS,
+    kKeyCodeMenu                    = KEY_MENU,
+    kKeyCodeNotification            = KEY_NOTIFICATION,
+    kKeyCodeSearch                  = KEY_SEARCH,
+
+    kKeyCodeBtnMouse                = BTN_MOUSE,
+
+    kKeyCodeOrientation0   = 77,
+    kKeyCodeOrientation90  = 78,
+    kKeyCodeOrientation180 = 79,
+    kKeyCodeOrientation270 = 80
+} AndroidKeyCode;
+
+
+/* this defines a structure used to describe an Android keyboard charmap */
+typedef struct AKeyEntry {
+    unsigned short  code;
+    unsigned short  base;
+    unsigned short  caps;
+    unsigned short  fn;
+    unsigned short  caps_fn;
+    unsigned short  number;
+} AKeyEntry;
+
+typedef struct {
+    const AKeyEntry*  entries;
+    int               num_entries;
+    char              name[ 32 ];
+} AKeyCharmap;
+
+extern const int           android_charmap_count;
+extern const AKeyCharmap*  android_charmaps[];
+
+#endif /* _android_charmap_h */
diff --git a/android/cmdline-option.c b/android/cmdline-option.c
new file mode 100644
index 0000000..b773d9d
--- /dev/null
+++ b/android/cmdline-option.c
@@ -0,0 +1,255 @@
+#include "android/cmdline-option.h"
+#include "android/utils/debug.h"
+#include "android/utils/misc.h"
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#define  _VERBOSE_TAG(x,y)   { #x, VERBOSE_##x, y },
+static const struct { const char*  name; int  flag; const char*  text; }
+debug_tags[] = {
+    VERBOSE_TAG_LIST
+    { 0, 0, 0 }
+};
+
+static void  parse_debug_tags( const char*  tags );
+void  parse_env_debug_tags( void );
+
+
+typedef struct {
+    const char*  name;
+    int          var_offset;
+    int          var_is_param;
+    int          var_is_config;
+} OptionInfo;
+
+#define  OPTION(_name,_type,_config)  \
+    { #_name, offsetof(AndroidOptions,_name), _type, _config },
+
+
+static const OptionInfo  option_keys[] = {
+#define  OPT_PARAM(_name,_template,_descr)  OPTION(_name,1,0)
+#define  OPT_FLAG(_name,_descr)             OPTION(_name,0,0)
+#define  CFG_PARAM(_name,_template,_descr)  OPTION(_name,1,1)
+#define  CFG_FLAG(_name,_descr)             OPTION(_name,0,1)
+#include "android/cmdline-options.h"
+    { NULL, 0, 0, 0 }
+};
+
+int
+android_parse_options( int  *pargc, char**  *pargv, AndroidOptions*  opt )
+{
+    int     nargs = *pargc-1;
+    char**  aread = *pargv+1;
+    char**  awrite = aread;
+
+    memset( opt, 0, sizeof *opt );
+
+    while (nargs > 0) {
+        char*  arg;
+        char   arg2_tab[64], *arg2 = arg2_tab;
+        int    nn;
+
+        /* process @<name> as a special exception meaning
+         * '-avd <name>'
+         */
+        if (aread[0][0] == '@') {
+            opt->avd = aread[0]+1;
+            nargs--;
+            aread++;
+            continue;
+        }
+
+        /* anything that isn't an option past this points
+         * exits the loop
+         */
+        if (aread[0][0] != '-') {
+            break;
+        }
+
+        arg = aread[0]+1;
+
+        /* an option cannot contain an underscore */
+        if (strchr(arg, '_') != NULL) {
+            break;
+        }
+
+        nargs--;
+        aread++;
+
+        /* for backwards compatibility with previous versions */
+        if (!strcmp(arg, "verbose")) {
+            arg = "debug-init";
+        }
+
+        /* special handing for -debug <tags> */
+        if (!strcmp(arg, "debug")) {
+            if (nargs == 0) {
+                derror( "-debug must be followed by tags (see -help-verbose)\n");
+                exit(1);
+            }
+            nargs--;
+            parse_debug_tags(*aread++);
+            continue;
+        }
+
+        /* NOTE: variable tables map option names to values
+         * (e.g. field offsets into the AndroidOptions structure).
+         *
+         * however, the names stored in the table used underscores
+         * instead of dashes. this means that the command-line option
+         * '-foo-bar' will be associated to the name 'foo_bar' in
+         * this table, and will point to the field 'foo_bar' or
+         * AndroidOptions.
+         *
+         * as such, before comparing the current option to the
+         * content of the table, we're going to translate dashes
+         * into underscores.
+         */
+        arg2 = arg2_tab;
+        buffer_translate_char( arg2_tab, sizeof(arg2_tab),
+                               arg, '-', '_');
+
+        /* special handling for -debug-<tag> and -debug-no-<tag> */
+        if (!memcmp(arg2, "debug_", 6)) {
+            int            remove = 0;
+            unsigned long  mask   = 0;
+            arg2 += 6;
+            if (!memcmp(arg2, "no_", 3)) {
+                arg2  += 3;
+                remove = 1;
+            }
+            if (!strcmp(arg2, "all")) {
+                mask = ~0;
+            }
+            for (nn = 0; debug_tags[nn].name; nn++) {
+                if (!strcmp(arg2, debug_tags[nn].name)) {
+                    mask = (1UL << debug_tags[nn].flag);
+                    break;
+                }
+            }
+            if (remove)
+                android_verbose &= ~mask;
+            else
+                android_verbose |= mask;
+            continue;
+        }
+
+        /* look into our table of options
+         *
+         */
+        {
+            const OptionInfo*  oo = option_keys;
+
+            for ( ; oo->name; oo++ ) {
+                if ( !strcmp( oo->name, arg2 ) ) {
+                    void*  field = (char*)opt + oo->var_offset;
+
+                    if (oo->var_is_param) {
+                        /* parameter option */
+                        if (nargs == 0) {
+                            derror( "-%s must be followed by parameter (see -help-%s)",
+                                    arg, arg );
+                            exit(1);
+                        }
+                        nargs--;
+                        ((char**)field)[0] = *aread++;
+                    } else {
+                        /* flag option */
+                        ((int*)field)[0] = 1;
+                    }
+                    break;
+                }
+            }
+
+            if (oo->name == NULL) {  /* unknown option ? */
+                nargs++;
+                aread--;
+                break;
+            }
+        }
+    }
+
+    /* copy remaining parameters, if any, to command line */
+    *pargc = nargs + 1;
+
+    while (nargs > 0) {
+        awrite[0] = aread[0];
+        awrite ++;
+        aread  ++;
+        nargs  --;
+    }
+
+    awrite[0] = NULL;
+
+    return 0;
+}
+
+
+
+/* special handling of -debug option and tags */
+#define  ENV_DEBUG   "ANDROID_DEBUG"
+
+static void
+parse_debug_tags( const char*  tags )
+{
+    char*        x;
+    char*        y;
+    char*        x0;
+
+    if (tags == NULL)
+        return;
+
+    x = x0 = strdup(tags);
+    while (*x) {
+        y = strchr(x, ',');
+        if (y == NULL)
+            y = x + strlen(x);
+        else
+            *y++ = 0;
+
+        if (y > x+1) {
+            int  nn, remove = 0;
+            unsigned mask = 0;
+
+            if (x[0] == '-') {
+                remove = 1;
+                x += 1;
+            }
+
+            if (!strcmp( "all", x ))
+                mask = ~0;
+            else {
+                char  temp[32];
+                buffer_translate_char(temp, sizeof temp, x, '-', '_');
+
+                for (nn = 0; debug_tags[nn].name != NULL; nn++) {
+                    if ( !strcmp( debug_tags[nn].name, temp ) ) {
+                        mask |= (1 << debug_tags[nn].flag);
+                        break;
+                    }
+                }
+            }
+
+            if (mask == 0)
+                dprint( "ignoring unknown " ENV_DEBUG " item '%s'", x );
+            else {
+                if (remove)
+                    android_verbose &= ~mask;
+                else
+                    android_verbose |= mask;
+            }
+        }
+        x = y;
+    }
+
+    free(x0);
+}
+
+void
+parse_env_debug_tags( void )
+{
+    const char*  env = getenv( ENV_DEBUG );
+    parse_debug_tags( env );
+}
+
diff --git a/android/cmdline-option.h b/android/cmdline-option.h
new file mode 100644
index 0000000..b87144d
--- /dev/null
+++ b/android/cmdline-option.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_OPTION_H
+#define _ANDROID_OPTION_H
+
+/* define a structure that will hold all option variables
+ */
+typedef struct {
+#define OPT_PARAM(n,t,d)   char*  n;
+#define OPT_FLAG(n,d)      int    n;
+#include "android/cmdline-options.h"
+} AndroidOptions;
+
+
+/* parse command-line arguments options and remove them from (argc,argv)
+ * 'opt' will be set to the content of parsed options
+ * returns 0 on success, -1 on error (unknown option)
+ */
+extern int
+android_parse_options( int  *pargc, char**  *pargv, AndroidOptions*  opt );
+
+/* name of default keyset file */
+#define  KEYSET_FILE    "default.keyset"
+
+/* the default device DPI if none is specified by the skin
+ */
+#define  DEFAULT_DEVICE_DPI  165
+
+/* default network settings for emulator */
+#define  DEFAULT_NETSPEED  "full"
+#define  DEFAULT_NETDELAY  "none"
+
+#endif /* _ANDROID_OPTION_H */
diff --git a/android/cmdline-options.h b/android/cmdline-options.h
new file mode 100644
index 0000000..e38b081
--- /dev/null
+++ b/android/cmdline-options.h
@@ -0,0 +1,137 @@
+/* this header file can be included several times by the same source code.
+ * it contains the list of support command-line options for the Android
+ * emulator program
+ */
+#ifndef OPT_PARAM
+#error OPT_PARAM is not defined
+#endif
+#ifndef OPT_FLAG
+#error OPT_FLAG is not defined
+#endif
+#ifndef CFG_PARAM
+#define CFG_PARAM  OPT_PARAM
+#endif
+#ifndef CFG_FLAG
+#define CFG_FLAG   OPT_FLAG
+#endif
+
+/* required to ensure that the CONFIG_XXX macros are properly defined */
+//XXX#include "config.h"
+
+/* Some options acts like flags, while others must be followed by a parameter
+ * string. Nothing really new here.
+ *
+ * Some options correspond to AVD (Android Virtual Device) configuration
+ * and will be ignored if you start the emulator with the -avd <name> flag.
+ *
+ * However, if you use them with -avd-create <name>, these options will be
+ * recorded into the new AVD directory. Once an AVD is created, there is no
+ * way to change these options.
+ *
+ * Several macros are used to define options:
+ *
+ *    OPT_FLAG( name, "description" )
+ *       used to define a non-config flag option.
+ *       * 'name' is the option suffix that must follow the dash (-)
+ *          as well as the name of an integer variable whose value will
+ *          be 1 if the flag is used, or 0 otherwise.
+ *       * "description" is a short description string that will be
+ *         displayed by 'emulator -help'.
+ *
+ *    OPT_PARAM( name, "<param>", "description" )
+ *       used to define a non-config parameter option
+ *        * 'name' will point to a char* variable (NULL if option is unused)
+ *        * "<param>" is a template for the parameter displayed by the help
+ *        * 'varname' is the name of a char* variable that will point
+ *          to the parameter string, if any, or will be NULL otherwise.
+ *
+ *    CFG_FLAG( name, "description" )
+ *        used to define a configuration-specific flag option.
+ *
+ *    CFG_PARAM( name, "<param>", "description" )
+ *        used to define a configuration-specific parameter option.
+ *
+ * NOTE: Keep in mind that optio names are converted by translating
+ *       dashes into underscore.
+ *
+ *       This means that '-some-option' is equivalent to '-some_option'
+ *       and will be backed by a variable name 'some_option'
+ *
+ */
+
+CFG_PARAM( sysdir,  "<dir>",  "search for system disk images in <dir>" )
+CFG_PARAM( system,  "<file>", "read initial system image from <file>" )
+CFG_PARAM( datadir, "<dir>",  "write user data into <dir>" )
+CFG_PARAM( kernel,  "<file>", "use specific emulated kernel" )
+CFG_PARAM( ramdisk, "<file>", "ramdisk image (default <system>/ramdisk.img" )
+CFG_PARAM( image,   "<file>", "obsolete, use -system <file> instead" )
+CFG_PARAM( init_data, "<file>", "initial data image (default <system>/userdata.img" )
+CFG_PARAM( initdata, "<file>", "same as '-init-data <file>'" )
+CFG_PARAM( data,     "<file>", "data image (default <datadir>/userdata-qemu.img" )
+CFG_PARAM( cache,    "<file>", "cache partition image (default is temporary file)" )
+CFG_FLAG ( no_cache, "disable the cache partition" )
+CFG_FLAG ( nocache,  "same as -no-cache" )
+OPT_PARAM( sdcard, "<file>", "SD card image (default <system>/sdcard.img")
+OPT_FLAG ( wipe_data, "reset the use data image (copy it from initdata)" )
+CFG_PARAM( avd, "<name>", "use a specific android virtual device" )
+CFG_PARAM( skindir, "<dir>", "search skins in <dir> (default <system>/skins)" )
+CFG_PARAM( skin, "<name>", "select a given skin" )
+CFG_FLAG ( no_skin, "don't use any emulator skin" )
+CFG_FLAG ( noskin, "same as -no-skin" )
+CFG_PARAM( memory, "<size>", "physical RAM size in MBs" )
+
+OPT_PARAM( netspeed, "<speed>", "maximum network download/upload speeds" )
+OPT_PARAM( netdelay, "<delay>", "network latency emulation" )
+OPT_FLAG ( netfast, "disable network shaping" )
+
+OPT_PARAM( trace, "<name>", "enable code profiling (F9 to start)" )
+OPT_FLAG ( show_kernel, "display kernel messages" )
+OPT_FLAG ( shell, "enable root shell on current terminal" )
+OPT_FLAG ( no_jni, "disable JNI checks in the Dalvik runtime" )
+OPT_FLAG ( nojni, "same as -no-jni" )
+OPT_PARAM( logcat, "<tags>", "enable logcat output with given tags" )
+
+OPT_FLAG ( no_audio, "disable audio support" )
+OPT_FLAG ( noaudio,  "same as -no-audio" )
+OPT_PARAM( audio,    "<backend>", "use specific audio backend" )
+OPT_PARAM( audio_in, "<backend>", "use specific audio input backend" )
+OPT_PARAM( audio_out,"<backend>", "use specific audio output backend" )
+
+OPT_FLAG ( raw_keys, "disable Unicode keyboard reverse-mapping" )
+OPT_PARAM( radio, "<device>", "redirect radio modem interface to character device" )
+OPT_PARAM( port, "<port>", "TCP port that will be used for the console" )
+OPT_PARAM( ports, "<consoleport>,<adbport>", "TCP ports used for the console and adb bridge" )
+OPT_PARAM( onion, "<image>", "use overlay PNG image over screen" )
+OPT_PARAM( onion_alpha, "<%age>", "specify onion-skin translucency" )
+OPT_PARAM( onion_rotation, "0|1|2|3", "specify onion-skin rotation" )
+
+OPT_PARAM( scale, "<scale>", "scale emulator window" )
+OPT_PARAM( dpi_device, "<dpi>", "specify device's resolution in dpi (default "
+            STRINGIFY(DEFAULT_DEVICE_DPI) ")" )
+
+OPT_PARAM( http_proxy, "<proxy>", "make TCP connections through a HTTP/HTTPS proxy" )
+OPT_PARAM( timezone, "<timezone>", "use this timezone instead of the host's default" )
+OPT_PARAM( dns_server, "<servers>", "use this DNS server(s) in the emulated system" )
+OPT_PARAM( cpu_delay, "<cpudelay>", "throttle CPU emulation" )
+OPT_FLAG ( no_boot_anim, "disable animation for faster boot" )
+
+OPT_FLAG( no_window, "disable graphical window display" )
+OPT_FLAG( version, "display emulator version number" )
+
+OPT_PARAM( report_console, "<socket>", "report console port to remote socket" )
+OPT_PARAM( gps, "<device>", "redirect NMEA GPS to character device" )
+OPT_PARAM( keyset, "<name>", "specify keyset file name" )
+OPT_PARAM( shell_serial, "<device>", "specific character device for root shell" )
+OPT_FLAG ( old_system, "support old (pre 1.4) system images" )
+OPT_PARAM( tcpdump, "<file>", "capture network packets to file" )
+
+#ifdef CONFIG_NAND_LIMITS
+OPT_PARAM( nand_limits, "<nlimits>", "enforce NAND/Flash read/write thresholds" )
+#endif
+
+OPT_PARAM( bootchart, "<timeout>", "enable bootcharting")
+
+#undef CFG_FLAG
+#undef CFG_PARAM
+#undef OPT_FLAG
+#undef OPT_PARAM
diff --git a/android/config.c b/android/config.c
new file mode 100644
index 0000000..36fab11
--- /dev/null
+++ b/android/config.c
@@ -0,0 +1,467 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "android/config.h"
+#include "android/utils/path.h"
+
+AConfig*
+aconfig_node(const char *name, const char *value)
+{
+    AConfig *n;
+
+    n = (AConfig*) calloc(sizeof(AConfig), 1);
+    n->name = name ? name : "";
+    n->value = value ? value : "";
+
+    return n;
+}
+
+static AConfig*
+_aconfig_find(AConfig *root, const char *name, int create)
+{
+    AConfig *node;
+
+    for(node = root->first_child; node; node = node->next) {
+        if(!strcmp(node->name, name)) return node;
+    }
+
+    if(create) {
+        node = (AConfig*) calloc(sizeof(AConfig), 1);
+        node->name = name;
+        node->value = "";
+
+        if(root->last_child) {
+            root->last_child->next = node;
+        } else {
+            root->first_child = node;
+        }
+        root->last_child = node;
+    }
+
+    return node;
+}
+
+AConfig*
+aconfig_find(AConfig *root, const char *name)
+{
+    return _aconfig_find(root, name, 0);
+}
+
+int
+aconfig_bool(AConfig *root, const char *name, int _default)
+{
+    AConfig *n = _aconfig_find(root, name, 0);
+    if(n == 0) {
+        return _default;
+    } else {
+        switch(n->value[0]){
+        case 'y':
+        case 'Y':
+        case '1':
+            return 1;
+        default:
+            return 0;
+        }
+    }
+}
+
+unsigned
+aconfig_unsigned(AConfig *root, const char *name, unsigned _default)
+{
+    AConfig *n = _aconfig_find(root, name, 0);
+    if(n == 0) {
+        return _default;
+    } else {
+        return strtoul(n->value, 0, 0);
+    }
+}
+
+int
+aconfig_int(AConfig *root, const char *name, int _default)
+{
+    AConfig *n = _aconfig_find(root, name, 0);
+    if(n == 0) {
+        return _default;
+    } else {
+        return strtol(n->value, 0, 0);
+    }
+}
+
+
+const char*
+aconfig_str(AConfig *root, const char *name, const char *_default)
+{
+    AConfig *n = _aconfig_find(root, name, 0);
+    if(n == 0) {
+        return _default;
+    } else {
+        return n->value;
+    }
+}
+
+void
+aconfig_set(AConfig *root, const char *name, const char *value)
+{
+    AConfig *node = _aconfig_find(root, name, 1);
+    node->value = value;
+}
+
+#define T_EOF 0
+#define T_TEXT 1
+#define T_DOT 2
+#define T_OBRACE 3
+#define T_CBRACE 4
+
+typedef struct
+{
+    char *data;
+    char *text;
+    int len;
+    char next;
+} cstate;
+
+
+static int _lex(cstate *cs, int value)
+{
+    char c;
+    char *s;
+    char *data;
+
+    data = cs->data;
+
+    if(cs->next != 0) {
+        c = cs->next;
+        cs->next = 0;
+        goto got_c;
+    }
+
+restart:
+    for(;;) {
+        c = *data++;
+    got_c:
+        if(isspace(c)) continue;
+
+        switch(c) {
+        case 0:
+            return T_EOF;
+
+        /* a sharp sign (#) starts a line comment and everything
+         * behind that is ignored until the end of line
+         */
+        case '#':
+            for(;;) {
+                switch(*data) {
+                case 0:
+                    cs->data = data;
+                    return T_EOF;
+                case '\n':
+                    cs->data = data + 1;
+                    goto restart;
+                default:
+                    data++;
+                }
+            }
+            break;
+
+        case '.':
+            cs->data = data;
+            return T_DOT;
+
+        case '{':
+            cs->data = data;
+            return T_OBRACE;
+
+        case '}':
+            cs->data = data;
+            return T_CBRACE;
+
+        default:
+            s = data - 1;
+
+            if(value) {
+               /* if we're looking for a value, then take anything 
+                * until the end of line. note that sharp signs do
+                * not start comments then. the result will be stripped
+                * from trailing whitespace.
+                */
+                for(;;) {
+                    if(*data == 0) {
+                        cs->data = data;
+                        break;
+                    }
+                    if(*data == '\n') {
+                        cs->data = data + 1;
+                        *data-- = 0;
+                        break;
+                    }
+                    data++;
+                }
+
+                    /* strip trailing whitespace */
+                while(data > s){
+                    if(!isspace(*data)) break;
+                    *data-- = 0;
+                }
+
+                goto got_text;
+            } else {
+               /* looking for a key name. we stop at whitspace,
+                * EOF, of T_DOT/T_OBRACE/T_CBRACE characters.
+                * note that the name can include sharp signs
+                */
+                for(;;) {
+                    if(isspace(*data)) {
+                        *data = 0;
+                        cs->data = data + 1;
+                        goto got_text;
+                    }
+                    switch(*data) {
+                    case 0:
+                        cs->data = data;
+                        goto got_text;
+                    case '.':
+                    case '{':
+                    case '}':
+                        cs->next = *data;
+                        *data = 0;
+                        cs->data = data + 1;
+                        goto got_text;
+                    default:
+                        data++;
+                    }
+                }
+            }
+        }
+    }
+
+got_text:
+    cs->text = s;
+    return T_TEXT;
+}
+
+#if 0
+char *TOKENNAMES[] = { "EOF", "TEXT", "DOT", "OBRACE", "CBRACE" };
+
+static int lex(cstate *cs, int value)
+{
+    int tok = _lex(cs, value);
+    printf("TOKEN(%d) %s %s\n", value, TOKENNAMES[tok],
+           tok == T_TEXT ? cs->text : "");
+    return tok;
+}
+#else
+#define lex(cs,v) _lex(cs,v)
+#endif
+
+static int parse_expr(cstate *cs, AConfig *node);
+
+static int
+parse_block(cstate *cs, AConfig *node)
+{
+    for(;;){
+        switch(lex(cs, 0)){
+        case T_TEXT:
+            if(parse_expr(cs, node)) return -1;
+            continue;
+
+        case T_CBRACE:
+            return 0;
+
+        default:
+            return -1;
+        }
+    }
+}
+
+static int
+parse_expr(cstate *cs, AConfig *node)
+{
+        /* last token was T_TEXT */
+    node = _aconfig_find(node, cs->text, 1);
+
+    for(;;) {
+        switch(lex(cs, 1)) {
+        case T_DOT:
+            if(lex(cs, 0) != T_TEXT) return -1;
+            node = _aconfig_find(node, cs->text, 1);
+            continue;
+
+        case T_TEXT:
+            node->value = cs->text;
+            return 0;
+
+        case T_OBRACE:
+            return parse_block(cs, node);
+
+        default:
+            return -1;
+        }
+    }
+}
+
+void
+aconfig_load(AConfig *root, char *data)
+{
+    if(data != 0) {
+        cstate cs;
+        cs.data = data;
+        cs.next = 0;
+
+        for(;;) {
+            switch(lex(&cs, 0)){
+            case T_TEXT:
+                if(parse_expr(&cs, root)) return;
+                break;
+            default:
+                return;
+            }
+        }
+    }
+}
+
+int
+aconfig_load_file(AConfig *root, const char *fn)
+{
+    char *data;
+    data = path_load_file(fn, NULL);
+    if (data == NULL)
+        return -1;
+
+    aconfig_load(root, data);
+    return 0;
+}
+
+
+typedef struct
+{
+    char   buff[1024];
+    char*  p;
+    char*  end;
+    int    fd;
+} Writer;
+
+static int
+writer_init( Writer*  w, const char*  fn )
+{
+    w->p   = w->buff;
+    w->end = w->buff + sizeof(w->buff);
+
+    w->fd  = creat( fn, 0755 );
+    if (w->fd < 0)
+        return -1;
+
+#ifdef _WIN32
+    _setmode( w->fd, _O_BINARY );
+#endif
+    return 0;
+}
+
+static void
+writer_write( Writer*  w, const char*  src, int  len )
+{
+    while (len > 0) {
+        int  avail = w->end - w->p;
+
+        if (avail > len)
+            avail = len;
+
+        memcpy( w->p, src, avail );
+        src += avail;
+        len -= avail;
+
+        w->p += avail;
+        if (w->p == w->end) {
+            write( w->fd, w->buff, w->p - w->buff );
+            w->p = w->buff;
+        }
+    }
+}
+
+static void
+writer_done( Writer*  w )
+{
+    if (w->p > w->buff)
+        write( w->fd, w->buff, w->p - w->buff );
+    close( w->fd );
+}
+
+static void
+writer_margin( Writer*  w, int  margin)
+{
+    static const char  spaces[10] = "          ";
+    while (margin >= 10) {
+        writer_write(w,spaces,10);
+        margin -= 10;
+    }
+    if (margin > 0)
+        writer_write(w,spaces,margin);
+}
+
+static void
+writer_c(Writer*  w, char  c)
+{
+    writer_write(w, &c, 1);
+}
+
+static void
+writer_str(Writer*  w, const char*  str)
+{
+    writer_write(w, str, strlen(str));
+}
+
+static void
+writer_node(Writer*  w, AConfig*  node, int  margin)
+{
+    writer_margin(w,margin);
+    writer_str(w, node->name);
+    writer_c(w,' ');
+
+    if (node->value[0]) {
+        writer_str(w, node->value);
+        writer_c(w,'\n');
+    }
+    else
+    {
+        AConfig*  child;
+
+        writer_c(w, '{');
+        writer_c(w, '\n');
+
+        for (child = node->first_child; child; child = child->next)
+            writer_node(w,child,margin+4);
+
+        writer_margin(w,margin);
+        writer_c(w,'}');
+        writer_c(w,'\n');
+    }
+}
+
+int
+aconfig_save_file(AConfig *root, const char *fn)
+{
+    AConfig*  child;
+    Writer    w[1];
+
+    if (writer_init(w,fn) < 0)
+        return -1;
+
+    for (child = root->first_child; child; child = child->next)
+        writer_node(w,child,0);
+
+    writer_done(w);
+    return 0;
+}
diff --git a/android/config.h b/android/config.h
new file mode 100644
index 0000000..5e8b048
--- /dev/null
+++ b/android/config.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef ANDROID_CONFIG_H
+#define ANDROID_CONFIG_H
+
+/** ANDROID CONFIGURATION FILE SUPPORT
+ **
+ ** A configuration file is loaded as a simplre tree of (key,value)
+ ** pairs. keys and values are simple strings
+ **/
+typedef struct AConfig  AConfig;
+
+struct AConfig
+{
+    AConfig*     next;
+    AConfig*     first_child;
+    AConfig*     last_child;
+    const char*  name;
+    const char*  value;
+};
+
+/* parse a text string into a config node tree */
+extern void   aconfig_load(AConfig*  root, char*  data);
+
+/* parse a file into a config node tree, return 0 in case of success, -1 otherwise */
+extern int    aconfig_load_file(AConfig*  root, const char*  path);
+
+/* save a config node tree into a file, return 0 in case of success, -1 otherwise */
+extern int    aconfig_save_file(AConfig*  root, const char* path);
+
+/* create a single config node */
+extern AConfig*  aconfig_node(const char *name, const char *value);
+
+/* locate a named child of a config node */
+extern AConfig*  aconfig_find(AConfig *root, const char *name);
+
+/* add a named child to a config node (or modify it if it already exists) */
+extern void      aconfig_set(AConfig *root, const char *name, const char *value);
+
+
+/* look up a child by name and return its value, eventually converted
+ * into a boolean or integer */
+extern int          aconfig_bool    (AConfig *root, const char *name, int _default);
+extern unsigned     aconfig_unsigned(AConfig *root, const char *name, unsigned _default);
+extern int          aconfig_int     (AConfig *root, const char *name, int _default);
+extern const char*  aconfig_str     (AConfig *root, const char *name, const char *_default);
+
+#endif /* ANDROID_CONFIG_H */
diff --git a/android/config/Linux/config-host.h b/android/config/Linux/config-host.h
new file mode 100644
index 0000000..90defbd
--- /dev/null
+++ b/android/config/Linux/config-host.h
@@ -0,0 +1,10 @@
+/* Automatically generated by configure - do not modify */
+#define CONFIG_QEMU_SHAREDIR "/usr/local/share/qemu"
+#define HOST_I386 1
+#define HOST_LONG_BITS 32
+#define HAVE_BYTESWAP_H 1
+#define CONFIG_GDBSTUB 1
+#define CONFIG_SLIRP 1
+#define QEMU_VERSION "0.8.2"
+#define CONFIG_SKINS 1
+#define CONFIG_UNAME_RELEASE ""
diff --git a/android/config/check-alsa.c b/android/config/check-alsa.c
new file mode 100644
index 0000000..4ab2945
--- /dev/null
+++ b/android/config/check-alsa.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <dlfcn.h>
+#include <stdio.h>
+#include <alsa/asoundlib.h>
+
+#define  D(...)  fprintf(stderr,__VA_ARGS__)
+#define  STRINGIFY(x)  _STRINGIFY(x)
+#define _STRINGIFY(x)  #x
+
+#define  DYN_SYMBOLS   \
+    DYN_FUNCTION(size_t,snd_pcm_sw_params_sizeof,(void))    \
+    DYN_FUNCTION(int,snd_pcm_hw_params_current,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \
+    DYN_FUNCTION(int,snd_pcm_sw_params_set_start_threshold,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val))  \
+    DYN_FUNCTION(int,snd_pcm_sw_params,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params))  \
+    DYN_FUNCTION(int,snd_pcm_sw_params_current,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)) \
+    DYN_FUNCTION(size_t,snd_pcm_hw_params_sizeof,(void))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_any,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_access,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_format,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_rate_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_channels_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_buffer_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_get_buffer_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val))  \
+    DYN_FUNCTION(int,snd_pcm_prepare,(snd_pcm_t *pcm))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_get_period_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_get_period_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_period_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_get_buffer_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_buffer_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val))  \
+    DYN_FUNCTION(int,snd_pcm_hw_params_set_period_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir))  \
+    DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_avail_update,(snd_pcm_t *pcm)) \
+    DYN_FUNCTION(int,snd_pcm_drop,(snd_pcm_t *pcm))  \
+    DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_writei,(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size))  \
+    DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_readi,(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size))  \
+    DYN_FUNCTION(snd_pcm_state_t,snd_pcm_state,(snd_pcm_t *pcm))  \
+    DYN_FUNCTION(const char*,snd_strerror,(int errnum)) \
+    DYN_FUNCTION(int,snd_pcm_open,(snd_pcm_t **pcm, const char *name,snd_pcm_stream_t stream, int mode)) \
+    DYN_FUNCTION(int,snd_pcm_close,(snd_pcm_t *pcm))  \
+
+
+
+/* define pointers to library functions we're going to use */
+#define  DYN_FUNCTION(ret,name,sig)   \
+    static ret  (*func_ ## name)sig;
+
+DYN_SYMBOLS
+
+#undef  DYN_FUNCTION
+
+#define func_snd_pcm_hw_params_alloca(ptr) \
+    do { assert(ptr); *ptr = (snd_pcm_hw_params_t *) alloca(func_snd_pcm_hw_params_sizeof()); memset(*ptr, 0, func_snd_pcm_hw_params_sizeof()); } while (0)
+
+#define func_snd_pcm_sw_params_alloca(ptr) \
+    do { assert(ptr); *ptr = (snd_pcm_sw_params_t *) alloca(func_snd_pcm_sw_params_sizeof()); memset(*ptr, 0, func_snd_pcm_sw_params_sizeof()); } while (0)
+
+static void*  alsa_lib;
+
+int  main(void)
+{
+    int  result = 1;
+
+    alsa_lib = dlopen( "libasound.so", RTLD_NOW );
+    if (alsa_lib == NULL)
+        alsa_lib = dlopen( "libasound.so.2", RTLD_NOW );
+
+    if (alsa_lib == NULL) {
+        D("could not find libasound on this system\n");
+        return 1;
+    }
+
+#undef  DYN_FUNCTION
+#define DYN_FUNCTION(ret,name,sig)                                               \
+    do {                                                                         \
+        (func_ ##name) = dlsym( alsa_lib, STRINGIFY(name) );                     \
+        if ((func_##name) == NULL) {                                             \
+            D("could not find %s in libasound\n", STRINGIFY(name)); \
+            goto Fail;                                                           \
+        }                                                                        \
+    } while (0);
+
+    DYN_SYMBOLS
+
+    result = 0;
+    goto Exit;
+
+Fail:
+    D("failed to open library\n");
+
+Exit:
+    dlclose(alsa_lib);
+    return result;
+}
diff --git a/android/config/check-esd.c b/android/config/check-esd.c
new file mode 100644
index 0000000..a8eb11b
--- /dev/null
+++ b/android/config/check-esd.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/* this file is used to test that we can use libesd with lazy dynamic linking */
+
+#include <esd.h>
+#include <dlfcn.h>
+#include <stdio.h>
+
+#define  D(...)  fprintf(stderr,__VA_ARGS__)
+#define  STRINGIFY(x)  _STRINGIFY(x)
+#define _STRINGIFY(x)  #x
+
+#define  ESD_SYMBOLS   \
+    ESD_FUNCTION(int,esd_play_stream,(esd_format_t,int,const char*,const char*))   \
+    ESD_FUNCTION(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
+    ESD_FUNCTION(int,esd_open_sound,( const char *host )) \
+    ESD_FUNCTION(int,esd_close,(int)) \
+
+/* define pointers to library functions we're going to use */
+#define  ESD_FUNCTION(ret,name,sig)   \
+    static ret  (*func_ ## name)sig;
+
+ESD_SYMBOLS
+
+#undef  ESD_FUNCTION
+static void*    esd_lib;
+
+int main( void ) 
+{
+    int  fd;
+
+    esd_lib = dlopen( "libesd.so", RTLD_NOW );
+    if (esd_lib == NULL)
+        esd_lib = dlopen( "libesd.so.0", RTLD_NOW );
+
+    if (esd_lib == NULL) {
+        D("could not find libesd on this system");
+        return 1;
+    }
+
+#undef  ESD_FUNCTION
+#define ESD_FUNCTION(ret,name,sig)                                               \
+    do {                                                                         \
+        (func_ ##name) = dlsym( esd_lib, STRINGIFY(name) );                      \
+        if ((func_##name) == NULL) {                                             \
+            D("could not find %s in libesd\n", STRINGIFY(name));   \
+            return 1;                                               \
+        }                                                                        \
+    } while (0);
+
+    ESD_SYMBOLS
+
+    return 0;
+}
diff --git a/android/config/config.h b/android/config/config.h
new file mode 100644
index 0000000..be83607
--- /dev/null
+++ b/android/config/config.h
@@ -0,0 +1,14 @@
+/* Automatically generated by configure - do not modify */
+#include "config-host.h"
+#define CONFIG_QEMU_PREFIX "/usr/gnemul/qemu-arm"
+#define TARGET_ARCH "arm"
+#define TARGET_ARM 1
+#define CONFIG_TRACE 1
+#define CONFIG_NAND 1
+#define CONFIG_SHAPER 1
+#define CONFIG_SOFTMMU 1
+#define CONFIG_SOFTFLOAT 1
+#define CONFIG_SDL 1
+#ifndef _WIN32
+#define CONFIG_NAND_LIMITS 1
+#endif
diff --git a/android/config/darwin-ppc/config-host.h b/android/config/darwin-ppc/config-host.h
new file mode 100644
index 0000000..cbd43d1
--- /dev/null
+++ b/android/config/darwin-ppc/config-host.h
@@ -0,0 +1,13 @@
+/* Automatically generated by configure - do not modify */
+#define CONFIG_QEMU_SHAREDIR "/usr/local/share/qemu"
+#define HOST_PPC 1
+#define HOST_LONG_BITS 32
+#define CONFIG_DARWIN 1
+#define CONFIG_GDBSTUB 1
+#define CONFIG_SLIRP 1
+#define QEMU_VERSION "0.8.2"
+#define O_LARGEFILE 0
+#define MAP_ANONYMOUS MAP_ANON
+#define _BSD 1
+#define CONFIG_SKINS 1
+#define CONFIG_UNAME_RELEASE ""
diff --git a/android/config/darwin-x86/config-host.h b/android/config/darwin-x86/config-host.h
new file mode 100644
index 0000000..aaf0195
--- /dev/null
+++ b/android/config/darwin-x86/config-host.h
@@ -0,0 +1,13 @@
+/* Automatically generated by configure - do not modify */
+#define CONFIG_QEMU_SHAREDIR "/usr/local/share/qemu"
+#define HOST_I386 1
+#define HOST_LONG_BITS 32
+#define CONFIG_DARWIN 1
+#define CONFIG_GDBSTUB 1
+#define CONFIG_SLIRP 1
+#define QEMU_VERSION "0.8.2"
+#define O_LARGEFILE 0
+#define MAP_ANONYMOUS MAP_ANON
+#define _BSD 1
+#define CONFIG_SKINS 1
+#define CONFIG_UNAME_RELEASE ""
diff --git a/android/config/linux-x86/config-host.h b/android/config/linux-x86/config-host.h
new file mode 100644
index 0000000..90defbd
--- /dev/null
+++ b/android/config/linux-x86/config-host.h
@@ -0,0 +1,10 @@
+/* Automatically generated by configure - do not modify */
+#define CONFIG_QEMU_SHAREDIR "/usr/local/share/qemu"
+#define HOST_I386 1
+#define HOST_LONG_BITS 32
+#define HAVE_BYTESWAP_H 1
+#define CONFIG_GDBSTUB 1
+#define CONFIG_SLIRP 1
+#define QEMU_VERSION "0.8.2"
+#define CONFIG_SKINS 1
+#define CONFIG_UNAME_RELEASE ""
diff --git a/android/config/windows/config-host.h b/android/config/windows/config-host.h
new file mode 100644
index 0000000..8f9e0d9
--- /dev/null
+++ b/android/config/windows/config-host.h
@@ -0,0 +1,10 @@
+/* Automatically generated by configure - do not modify */
+#define CONFIG_QEMU_SHAREDIR "/usr/local/share/qemu"
+#define HOST_I386 1
+#define HOST_LONG_BITS 32
+#define CONFIG_WIN32 1
+#define CONFIG_GDBSTUB 1
+#define CONFIG_SLIRP 1
+#define QEMU_VERSION "0.8.2"
+#define CONFIG_SKINS 1
+#define CONFIG_UNAME_RELEASE ""
diff --git a/android/console.c b/android/console.c
new file mode 100644
index 0000000..3a769c1
--- /dev/null
+++ b/android/console.c
@@ -0,0 +1,2190 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+/*
+ *  Android emulator control console
+ *
+ *  this console is enabled automatically at emulator startup, on port 5554 by default,
+ *  unless some other emulator is already running. See (android_emulation_start in android_sdl.c
+ *  for details)
+ *
+ *  you can telnet to the console, then use commands like 'help' or others to dynamically
+ *  change emulator settings.
+ *
+ */
+
+#include "sockets.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "android/android.h"
+#include "sockets.h"
+#include "cpu.h"
+#include "hw/goldfish_device.h"
+#include "hw/power_supply.h"
+#include "shaper.h"
+#include "modem_driver.h"
+#include "android/gps.h"
+#include "android/globals.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/debug.h"
+#include "android/utils/stralloc.h"
+#include "tcpdump.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "android/hw-events.h"
+#include "android/skin/keyboard.h"
+
+#if defined(CONFIG_SLIRP)
+#include "libslirp.h"
+#endif
+
+/* set to 1 to use the i/o and event functions
+ * defined in "telephony/sysdeps.h"
+ */
+#define  USE_SYSDEPS  0
+
+#include "sysdeps.h"
+
+#define  DEBUG  1
+
+#if 1
+#  define  D_ACTIVE   VERBOSE_CHECK(console)
+#else
+#  define  D_ACTIVE   DEBUG
+#endif
+
+#if DEBUG
+#  define  D(x)   do { if (D_ACTIVE) ( printf x , fflush(stdout) ); } while (0)
+#else
+#  define  D(x)   do{}while(0)
+#endif
+
+extern int  slirp_inited;  /* in vl.c */
+
+typedef struct ControlGlobalRec_*  ControlGlobal;
+
+typedef struct ControlClientRec_*  ControlClient;
+
+typedef struct {
+    int           host_port;
+    int           host_udp;
+    unsigned int  guest_ip;
+    int           guest_port;
+} RedirRec, *Redir;
+
+
+#if USE_SYSDEPS
+typedef SysChannel  Socket;
+#else /* !USE_SYSDEPS */
+typedef int         Socket;
+#endif /* !USE_SYSDEPS */
+
+
+typedef struct ControlClientRec_
+{
+    struct ControlClientRec_*  next;       /* next client in list           */
+    Socket                     sock;       /* socket used for communication */
+    ControlGlobal              global;
+    char                       finished;
+    char                       buff[ 4096 ];
+    int                        buff_len;
+
+} ControlClientRec;
+
+
+typedef struct ControlGlobalRec_
+{
+    /* listening socket */
+    Socket    listen_fd;
+
+    /* the list of current clients */
+    ControlClient   clients;
+
+    /* the list of redirections currently active */
+    Redir     redirs;
+    int       num_redirs;
+    int       max_redirs;
+
+} ControlGlobalRec;
+
+
+static int
+control_global_add_redir( ControlGlobal  global,
+                          int            host_port,
+                          int            host_udp,
+                          unsigned int   guest_ip,
+                          int            guest_port )
+{
+    Redir  redir;
+
+    if (global->num_redirs >= global->max_redirs)
+    {
+        int  old_max = global->max_redirs;
+        int  new_max = old_max + (old_max >> 1) + 4;
+
+        Redir  new_redirs = realloc( global->redirs, new_max*sizeof(global->redirs[0]) );
+        if (new_redirs == NULL)
+            return -1;
+
+        global->redirs     = new_redirs;
+        global->max_redirs = new_max;
+    }
+
+    redir = &global->redirs[ global->num_redirs++ ];
+
+    redir->host_port  = host_port;
+    redir->host_udp   = host_udp;
+    redir->guest_ip   = guest_ip;
+    redir->guest_port = guest_port;
+
+    return 0;
+}
+
+static int
+control_global_del_redir( ControlGlobal  global,
+                          int            host_port,
+                          int            host_udp )
+{
+    int  nn;
+
+    for (nn = 0; nn < global->num_redirs; nn++)
+    {
+        Redir  redir = &global->redirs[nn];
+
+        if ( redir->host_port == host_port &&
+             redir->host_udp  == host_udp  )
+        {
+            memmove( redir, redir + 1, ((global->num_redirs - nn)-1)*sizeof(*redir) );
+            global->num_redirs -= 1;
+            return 0;
+        }
+    }
+    /* we didn't find it */
+    return -1;
+}
+
+static void
+control_client_destroy( ControlClient  client )
+{
+    ControlGlobal  global = client->global;
+    ControlClient  *pnode = &global->clients;
+
+    D(( "destroying control client %p\n", client ));
+
+#if USE_SYSDEPS
+    sys_channel_on( client->sock, 0, NULL, NULL );
+#else
+    qemu_set_fd_handler( client->sock, NULL, NULL, NULL );
+#endif
+
+    for ( ;; ) {
+        ControlClient  node = *pnode;
+        if ( node == NULL )
+            break;
+        if ( node == client ) {
+            *pnode     = node->next;
+            node->next = NULL;
+            break;
+        }
+        pnode = &node->next;
+    }
+
+#if USE_SYSDEPS
+    sys_channel_close( client->sock );
+    client->sock = NULL;
+#else
+    socket_close( client->sock );
+    client->sock = -1;
+#endif
+
+    free( client );
+}
+
+static void  control_client_read( void*  _client );  /* forward */
+
+
+static void  control_control_write( ControlClient  client, const char*  buff, int  len )
+{
+    int ret;
+
+    if (len < 0)
+        len = strlen(buff);
+
+    while (len > 0) {
+#if USE_SYSDEPS
+        ret = sys_channel_write( client->sock, buff, len );
+#else
+        ret = socket_send( client->sock, buff, len);
+#endif
+        if (ret < 0) {
+            if (errno != EINTR && errno != EWOULDBLOCK)
+                return;
+        } else {
+            buff += ret;
+            len  -= ret;
+        }
+    }
+}
+
+static void  control_write( ControlClient  client, const char*  format, ... )
+{
+    static char  temp[1024];
+    va_list      args;
+
+    va_start(args, format);
+    vsnprintf( temp, sizeof(temp), format, args );
+    va_end(args);
+
+    temp[ sizeof(temp)-1 ] = 0;
+
+    control_control_write( client, temp, -1 );
+}
+
+
+static ControlClient
+control_client_create( Socket         socket,
+                       ControlGlobal  global )
+{
+    ControlClient  client = calloc( sizeof(*client), 1 );
+
+    if (client) {
+#if !USE_SYSDEPS
+        socket_set_nodelay( socket );
+        socket_set_nonblock( socket );
+#endif
+        client->finished = 0;
+        client->global  = global;
+        client->sock    = socket;
+        client->next    = global->clients;
+        global->clients = client;
+
+#if USE_SYSDEPS
+        sys_channel_on( socket, SYS_EVENT_READ,
+                        (SysChannelCallback) control_client_read,
+                        client );
+#else
+        qemu_set_fd_handler( socket, control_client_read, NULL, client );
+#endif
+    }
+    return client;
+}
+
+typedef const struct CommandDefRec_  *CommandDef;
+
+typedef struct CommandDefRec_ {
+    const char*  names;
+    const char*  abstract;
+    const char*  description;
+    void        (*descriptor)( ControlClient  client );
+    int         (*handler)( ControlClient  client, char* args );
+    CommandDef   subcommands;   /* if handler is NULL */
+
+} CommandDefRec;
+
+static const CommandDefRec   main_commands[];  /* forward */
+
+static CommandDef
+find_command( char*  input, CommandDef  commands, char*  *pend, char*  *pargs )
+{
+    int    nn;
+    char*  args = strchr(input, ' ');
+
+    if (args != NULL) {
+        while (*args == ' ')
+            args++;
+
+        if (args[0] == 0)
+            args = NULL;
+    }
+
+    for (nn = 0; commands[nn].names != NULL; nn++)
+    {
+        const char*  name = commands[nn].names;
+        const char*  sep;
+
+        do {
+            int  len, c;
+
+            sep = strchr( name, '|' );
+            if (sep)
+                len = sep - name;
+            else
+                len = strlen(name);
+
+            c = input[len];
+            if ( !memcmp( name, input, len ) && (c == ' ' || c == 0) ) {
+                *pend  = input + len;
+                *pargs = args;
+                return &commands[nn];
+            }
+
+            if (sep)
+                name = sep + 1;
+
+        } while (sep != NULL && *name);
+    }
+    /* NOTE: don't touch *pend and *pargs if no command is found */
+    return NULL;
+}
+
+static void
+dump_help( ControlClient  client,
+           CommandDef     cmd,
+           const char*    prefix )
+{
+    if (cmd->description) {
+        control_write( client, "%s", cmd->description );
+    } else if (cmd->descriptor) {
+        cmd->descriptor( client );
+    } else
+        control_write( client, "%s\r\n", cmd->abstract );
+
+    if (cmd->subcommands) {
+        cmd = cmd->subcommands;
+        control_write( client, "\r\navailable sub-commands:\r\n" );
+        for ( ; cmd->names != NULL; cmd++ ) {
+            control_write( client, "   %s %-15s  %s\r\n", prefix, cmd->names, cmd->abstract );
+        }
+        control_write( client, "\r\n" );
+    }
+}
+
+static void
+control_client_do_command( ControlClient  client )
+{
+    char*       line     = client->buff;
+    char*       args     = NULL;
+    CommandDef  commands = main_commands;
+    char*       cmdend   = client->buff;
+    CommandDef  cmd      = find_command( line, commands, &cmdend, &args );
+
+    if (cmd == NULL) {
+        control_write( client, "KO: unknown command, try 'help'\r\n" );
+        return;
+    }
+
+    for (;;) {
+        CommandDef  subcmd;
+
+        if (cmd->handler) {
+            if ( !cmd->handler( client, args ) )
+                control_write( client, "OK\r\n" );
+            break;
+        }
+
+        /* no handler means we should have sub-commands */
+        if (cmd->subcommands == NULL) {
+            control_write( client, "KO: internal error: buggy command table for '%.*s'\r\n",
+                           cmdend - client->buff, client->buff );
+            break;
+        }
+
+        /* we need a sub-command here */
+        if ( !args ) {
+            dump_help( client, cmd, "" );
+            control_write( client, "KO: missing sub-command\r\n" );
+            break;
+        }
+
+        line     = args;
+        commands = cmd->subcommands;
+        subcmd   = find_command( line, commands, &cmdend, &args );
+        if (subcmd == NULL) {
+            dump_help( client, cmd, "" );
+            control_write( client, "KO:  bad sub-command\r\n" );
+            break;
+        }
+        cmd = subcmd;
+    }
+}
+
+/* implement the 'help' command */
+static int
+do_help( ControlClient  client, char*  args )
+{
+    char*       line;
+    char*       start = args;
+    char*       end   = start;
+    CommandDef  cmd = main_commands;
+
+    /* without arguments, simply dump all commands */
+    if (args == NULL) {
+        control_write( client, "Android console command help:\r\n\r\n" );
+        for ( ; cmd->names != NULL; cmd++ ) {
+            control_write( client, "    %-15s  %s\r\n", cmd->names, cmd->abstract );
+        }
+        control_write( client, "\r\ntry 'help <command>' for command-specific help\r\n" );
+        return 0;
+    }
+
+    /* with an argument, find the corresponding command */
+    for (;;) {
+        CommandDef  subcmd;
+
+        line    = args;
+        subcmd  = find_command( line, cmd, &end, &args );
+        if (subcmd == NULL) {
+            control_write( client, "try one of these instead:\r\n\r\n" );
+            for ( ; cmd->names != NULL; cmd++ ) {
+                control_write( client, "    %.*s %s\r\n",
+                              end - start, start, cmd->names );
+            }
+            control_write( client, "\r\nKO: unknown command\r\n" );
+            return -1;
+        }
+
+        if ( !args || !subcmd->subcommands ) {
+            dump_help( client, subcmd, start );
+            return 0;
+        }
+        cmd = subcmd->subcommands;
+    }
+}
+
+
+static void
+control_client_read_byte( ControlClient  client, unsigned char  ch )
+{
+    if (ch == '\r')
+    {
+        /* filter them out */
+    }
+    else if (ch == '\n')
+    {
+        client->buff[ client->buff_len ] = 0;
+        control_client_do_command( client );
+        if (client->finished)
+            return;
+
+        client->buff_len = 0;
+    }
+    else
+    {
+        if (client->buff_len >= sizeof(client->buff)-1)
+            client->buff_len = 0;
+
+        client->buff[ client->buff_len++ ] = ch;
+    }
+}
+
+static void
+control_client_read( void*  _client )
+{
+    ControlClient  client = _client;
+    unsigned char  buf[4096];
+    int            size;
+
+    D(( "in control_client read: " ));
+#if USE_SYSDEPS
+    size = sys_channel_read( client->sock, buf, sizeof(buf) );
+#else
+    size = socket_recv( client->sock, buf, sizeof(buf) );
+#endif
+    if (size < 0) {
+        D(( "size < 0, exiting with %d: %s\n", errno, errno_str ));
+		if (errno != EWOULDBLOCK && errno != EINTR)
+			control_client_destroy( client );
+        return;
+    }
+
+    if (size == 0) {
+        /* end of connection */
+        D(( "end of connection detected !!\n" ));
+        control_client_destroy( client );
+    }
+    else {
+        int  nn;
+#ifdef _WIN32
+#  if DEBUG
+        char  temp[16];
+        int   count = size > sizeof(temp)-1 ? sizeof(temp)-1 : size;
+        for (nn = 0; nn < count; nn++) {
+                int  c = buf[nn];
+                if (c == '\n')
+                        temp[nn] = '!';
+            else if (c < 32)
+                        temp[nn] = '.';
+                else
+                    temp[nn] = (char)c;
+        }
+        temp[nn] = 0;
+        D(( "received %d bytes: %s\n", size, temp ));
+#  endif
+#else
+        D(( "received %.*s\n", size, buf ));
+#endif
+        for (nn = 0; nn < size; nn++) {
+            control_client_read_byte( client, buf[nn] );
+            if (client->finished) {
+                control_client_destroy(client);
+                return;
+            }
+        }
+    }
+}
+
+
+/* this function is called on each new client connection */
+static void
+control_global_accept( void*  _global )
+{
+    ControlGlobal       global = _global;
+    ControlClient       client;
+    Socket              fd;
+
+    D(( "control_global_accept: just in (fd=%p)\n", (void*)global->listen_fd ));
+
+#if USE_SYSDEPS
+    fd = sys_channel_create_tcp_handler( global->listen_fd );
+    if (fd == NULL) {
+        perror("accept");
+        return;
+    }
+#else
+    for(;;) {
+        fd = socket_accept( global->listen_fd, NULL );
+        if (fd < 0 && errno != EINTR) {
+            D(( "problem in accept: %d: %s\n", errno, errno_str ));
+            perror("accept");
+            return;
+        } else if (fd >= 0) {
+            break;
+        }
+        D(( "relooping in accept()\n" ));
+    }
+
+    socket_set_xreuseaddr( fd );
+#endif
+
+    D(( "control_global_accept: creating new client\n" ));
+    client = control_client_create( fd, global );
+    if (client) {
+        D(( "control_global_accept: new client %p\n", client ));
+        control_write( client, "Android Console: type 'help' for a list of commands\r\n" );
+        control_write( client, "OK\r\n" );
+    }
+}
+
+
+static int
+control_global_init( ControlGlobal  global,
+                     int            control_port )
+{
+    Socket  fd;
+#if !USE_SYSDEPS
+    int     ret;
+    SockAddress  sockaddr;
+#endif
+
+    memset( global, 0, sizeof(*global) );
+
+    sys_main_init();
+
+#if USE_SYSDEPS
+    fd = sys_channel_create_tcp_server( control_port );
+    if (fd == NULL) {
+        return -1;
+    }
+
+    D(("global fd=%p\n", fd));
+
+    global->listen_fd = fd;
+    sys_channel_on( fd, SYS_EVENT_READ,
+                    (SysChannelCallback) control_global_accept,
+                    global );
+#else
+    fd = socket_create_inet( SOCKET_STREAM );
+    if (fd < 0) {
+        perror("socket");
+        return -1;
+    }
+
+    socket_set_xreuseaddr( fd );
+
+    sock_address_init_inet( &sockaddr, SOCK_ADDRESS_INET_LOOPBACK, control_port );
+
+    ret = socket_bind(fd, &sockaddr );
+    if (ret < 0) {
+        perror("bind");
+        socket_close( fd );
+        return -1;
+    }
+
+    ret = socket_listen(fd, 0);
+    if (ret < 0) {
+        perror("listen");
+        socket_close( fd );
+        return -1;
+    }
+
+    socket_set_nonblock(fd);
+
+    global->listen_fd = fd;
+
+    qemu_set_fd_handler( fd, control_global_accept, NULL, global );
+#endif
+    return 0;
+}
+
+
+
+static int
+do_quit( ControlClient  client, char*  args )
+{
+    client->finished = 1;
+    return -1;
+}
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                        N E T W O R K   S E T T I N G S                          ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+do_network_status( ControlClient  client, char*  args )
+{
+    control_write( client, "Current network status:\r\n" );
+
+    control_write( client, "  download speed:   %8d bits/s (%.1f KB/s)\r\n",
+                   (long)qemu_net_download_speed, qemu_net_download_speed/8192. );
+
+    control_write( client, "  upload speed:     %8d bits/s (%.1f KB/s)\r\n",
+                   (long)qemu_net_upload_speed, qemu_net_upload_speed/8192. );
+
+    control_write( client, "  minimum latency:  %ld ms\r\n", qemu_net_min_latency );
+    control_write( client, "  maximum latency:  %ld ms\r\n", qemu_net_max_latency );
+    return 0;
+}
+
+static void
+dump_network_speeds( ControlClient  client )
+{
+    const NetworkSpeed*  speed = android_netspeeds;
+    const char* const  format = "  %-8s %s\r\n";
+    for ( ; speed->name; speed++ ) {
+        control_write( client, format, speed->name, speed->display );
+    }
+    control_write( client, format, "<num>", "selects both upload and download speed" );
+    control_write( client, format, "<up>:<down>", "select individual upload/download speeds" );
+}
+
+
+static int
+do_network_speed( ControlClient  client, char*  args )
+{
+    if ( !args ) {
+        control_write( client, "KO: missing <speed> argument, see 'help network speed'\r\n" );
+        return -1;
+    }
+    if ( android_parse_network_speed( args ) < 0 ) {
+        control_write( client, "KO: invalid <speed> argument, see 'help network speed' for valid values\r\n" );
+        return -1;
+    }
+
+    netshaper_set_rate( slirp_shaper_in,  qemu_net_download_speed );
+    netshaper_set_rate( slirp_shaper_out, qemu_net_upload_speed );
+
+    if (android_modem) {
+        amodem_set_data_network_type( android_modem, 
+                                    android_parse_network_type( args ) );
+    }
+    return 0;
+}
+
+static void
+describe_network_speed( ControlClient  client )
+{
+    control_write( client,
+                   "'network speed <speed>' allows you to dynamically change the speed of the emulated\r\n"
+                   "network on the device, where <speed> is one of the following:\r\n\r\n" );
+    dump_network_speeds( client );
+}
+
+static int
+do_network_delay( ControlClient  client, char*  args )
+{
+    if ( !args ) {
+        control_write( client, "KO: missing <delay> argument, see 'help network delay'\r\n" );
+        return -1;
+    }
+    if ( android_parse_network_latency( args ) < 0 ) {
+        control_write( client, "KO: invalid <delay> argument, see 'help network delay' for valid values\r\n" );
+        return -1;
+    }
+    netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency );
+    return 0;
+}
+
+static void
+describe_network_delay( ControlClient  client )
+{
+    control_write( client,
+                   "'network delay <latency>' allows you to dynamically change the latency of the emulated\r\n"
+                   "network on the device, where <latency> is one of the following:\r\n\r\n" );
+    /* XXX: TODO */
+}
+
+static int
+do_network_capture_start( ControlClient  client, char*  args )
+{
+    if ( !args ) {
+        control_write( client, "KO: missing <file> argument, see 'help network capture start'\r\n" );
+        return -1;
+    }
+    if ( qemu_tcpdump_start(args) < 0) {
+        control_write( client, "KO: could not start capture: %s", strerror(errno) );
+        return -1;
+    }
+    return 0;
+}
+
+static int
+do_network_capture_stop( ControlClient  client, char*  args )
+{
+    /* no need to return an error here */
+    qemu_tcpdump_stop();
+    return 0;
+}
+
+static const CommandDefRec  network_capture_commands[] =
+{
+    { "start", "start network capture",
+      "'network capture start <file>' starts a new capture of network packets\r\n"
+      "into a specific <file>. This will stop any capture already in progress.\r\n"
+      "the capture file can later be analyzed by tools like WireShark. It uses\r\n"
+      "the libpcap file format.\r\n\r\n"
+      "you can stop the capture anytime with 'network capture stop'\r\n", NULL,
+      do_network_capture_start, NULL },
+
+    { "stop", "stop network capture",
+      "'network capture stop' stops a currently running packet capture, if any.\r\n"
+      "you can start one with 'network capture start <file>'\r\n", NULL,
+      do_network_capture_stop, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+static const CommandDefRec  network_commands[] =
+{
+    { "status", "dump network status", NULL, NULL,
+       do_network_status, NULL },
+
+    { "speed", "change network speed", NULL, describe_network_speed,
+      do_network_speed, NULL },
+
+    { "delay", "change network latency", NULL, describe_network_delay,
+       do_network_delay, NULL },
+
+    { "capture", "dump network packets to file",
+      "allows to start/stop capture of network packets to a file for later analysis\r\n", NULL,
+      NULL, network_capture_commands },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                       P O R T   R E D I R E C T I O N S                         ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+do_redir_list( ControlClient  client, char*  args )
+{
+    ControlGlobal  global = client->global;
+
+    if (global->num_redirs == 0)
+        control_write( client, "no active redirections\r\n" );
+    else {
+        int  nn;
+        for (nn = 0; nn < global->num_redirs; nn++) {
+            Redir  redir = &global->redirs[nn];
+            control_write( client, "%s:%-5d => %-5d\r\n",
+                          redir->host_udp ? "udp" : "tcp",
+                          redir->host_port,
+                          redir->guest_port );
+        }
+    }
+    return 0;
+}
+
+/* parse a protocol:port specification */
+static int
+redir_parse_proto_port( char*  args, int  *pport, int  *pproto )
+{
+    int  proto = -1;
+    int  len   = 0;
+    char*  end;
+
+    if ( !memcmp( args, "tcp:", 4 ) ) {
+        proto = 0;
+        len   = 4;
+    }
+    else if ( !memcmp( args, "udp:", 4 ) ) {
+        proto = 1;
+        len   = 4;
+    }
+    else
+        return 0;
+
+    args   += len;
+    *pproto = proto;
+    *pport  = strtol( args, &end, 10 );
+    if (end == args)
+        return 0;
+
+    len += end - args;
+    return len;
+}
+
+static int
+redir_parse_guest_port( char*  arg, int  *pport )
+{
+    char*  end;
+
+    *pport = strtoul( arg, &end, 10 );
+    if (end == arg)
+        return 0;
+
+    return end - arg;
+}
+
+static Redir
+redir_find( ControlGlobal  global, int  port, int  isudp )
+{
+    int  nn;
+
+    for (nn = 0; nn < global->num_redirs; nn++) {
+        Redir  redir = &global->redirs[nn];
+
+        if (redir->host_port == port && redir->host_udp == isudp)
+            return redir;
+    }
+    return NULL;
+}
+
+
+static int
+do_redir_add( ControlClient  client, char*  args )
+{
+    int       len, host_proto, host_port, guest_port;
+    uint32_t  guest_ip;
+    Redir     redir;
+
+    if ( !args )
+        goto BadFormat;
+
+    if (!slirp_inited) {
+        slirp_inited = 1;
+        slirp_init();
+    }
+
+    len = redir_parse_proto_port( args, &host_port, &host_proto );
+    if (len == 0 || args[len] != ':')
+        goto BadFormat;
+
+    args += len + 1;
+    len = redir_parse_guest_port( args, &guest_port );
+    if (len == 0 || args[len] != 0)
+        goto BadFormat;
+
+    redir = redir_find( client->global, host_port, host_proto );
+    if ( redir != NULL ) {
+        control_write( client, "KO: host port already active, use 'redir del' to remove first\r\n" );
+        return -1;
+    }
+
+    if (!inet_strtoip("10.0.2.15", &guest_ip)) {
+        control_write( client, "KO: unexpected internal failure when resolving 10.0.2.15\r\n" );
+        return -1;
+    }
+
+    D(("pattern hport=%d gport=%d proto=%d\n", host_port, guest_port, host_proto ));
+    if ( control_global_add_redir( client->global, host_port, host_proto,
+                                   guest_ip, guest_port ) < 0 )
+    {
+        control_write( client, "KO: not enough memory to allocate redirection\r\n" );
+        return -1;
+    }
+
+    if (slirp_redir(host_proto, host_port, guest_ip, guest_port) < 0) {
+        control_write( client, "KO: can't setup redirection, port probably used by another program on host\r\n" );
+        control_global_del_redir( client->global, host_port, host_proto );
+        return -1;
+    }
+
+    return 0;
+
+BadFormat:
+    control_write( client, "KO: bad redirection format, try (tcp|udp):hostport:guestport\r\n", -1 );
+    return -1;
+}
+
+
+static int
+do_redir_del( ControlClient  client, char*  args )
+{
+    int    len, proto, port;
+    Redir  redir;
+
+    if ( !args )
+        goto BadFormat;
+    len = redir_parse_proto_port( args, &port, &proto );
+    if ( len == 0 || args[len] != 0 )
+        goto BadFormat;
+
+    redir = redir_find( client->global, port, proto );
+    if (redir == NULL) {
+        control_write( client, "KO: can't remove unknown redirection (%s:%d)\r\n",
+                        proto ? "udp" : "tcp", port );
+        return -1;
+    }
+
+    slirp_unredir( redir->host_udp, redir->host_port );
+    control_global_del_redir( client->global, port, proto );\
+
+    return 0;
+
+BadFormat:
+    control_write( client, "KO: bad redirection format, try (tcp|udp):hostport\r\n" );
+    return -1;
+}
+
+static const CommandDefRec  redir_commands[] =
+{
+    { "list", "list current redirections",
+    "list current port redirections. use 'redir add' and 'redir del' to add and remove them\r\n", NULL,
+    do_redir_list, NULL },
+
+    { "add",  "add new redirection",
+    "add a new port redirection, arguments must be:\r\n\r\n"
+            "  redir add <protocol>:<host-port>:<guest-port>\r\n\r\n"
+            "where:   <protocol>     is either 'tcp' or 'udp'\r\n"
+            "         <host-port>    a number indicating which port on the host to open\r\n"
+            "         <guest-port>   a number indicating which port to route to on the device\r\n"
+            "\r\nas an example, 'redir  tcp:5000:6000' will allow any packets sent to\r\n"
+            "the host's TCP port 5000 to be routed to TCP port 6000 of the emulated device\r\n", NULL,
+    do_redir_add, NULL },
+
+    { "del",  "remove existing redirection",
+    "remove a port redirecion that was created with 'redir add', arguments must be:\r\n\r\n"
+            "  redir  del <protocol>:<host-port>\r\n\r\n"
+            "see the 'help redir add' for the meaning of <protocol> and <host-port>\r\n", NULL,
+    do_redir_del, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                           G S M   M O D E M                                     ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static const struct {
+    const char*         name;
+    const char*         display;
+    ARegistrationState  state;
+} _gsm_states[] = {
+    { "unregistered",  "no network available", A_REGISTRATION_UNREGISTERED },
+    { "home",          "on local network, non-roaming", A_REGISTRATION_HOME },
+    { "roaming",       "on roaming network", A_REGISTRATION_ROAMING },
+    { "searching",     "searching networks", A_REGISTRATION_SEARCHING },
+    { "denied",        "emergency calls only", A_REGISTRATION_DENIED },
+    { "off",           "same as 'unregistered'", A_REGISTRATION_UNREGISTERED },
+    { "on",            "same as 'home'", A_REGISTRATION_HOME },
+    { NULL, NULL, A_REGISTRATION_UNREGISTERED }
+};
+
+static const char*
+gsm_state_to_string( ARegistrationState  state )
+{
+    int  nn;
+    for (nn = 0; _gsm_states[nn].name != NULL; nn++) {
+        if (state == _gsm_states[nn].state)
+            return _gsm_states[nn].name;
+    }
+    return "<unknown>";
+}
+
+static int
+do_gsm_status( ControlClient  client, char*  args )
+{
+    if (args) {
+        control_write( client, "KO: no argument required\r\n" );
+        return -1;
+    }
+    if (!android_modem) {
+        control_write( client, "KO: modem emulation not running\r\n" );
+        return -1;
+    }
+    control_write( client, "gsm voice state: %s\r\n",
+                   gsm_state_to_string(
+                       amodem_get_voice_registration(android_modem) ) );
+    control_write( client, "gsm data state:  %s\r\n",
+                   gsm_state_to_string(
+                       amodem_get_data_registration(android_modem) ) );
+    return 0;
+}
+
+
+static void
+help_gsm_data( ControlClient  client )
+{
+    int  nn;
+    control_write( client,
+            "the 'gsm data <state>' allows you to change the state of your GPRS connection\r\n"
+            "valid values for <state> are the following:\r\n\r\n" );
+    for (nn = 0; ; nn++) {
+        const char*         name    = _gsm_states[nn].name;
+        const char*         display = _gsm_states[nn].display;
+
+        if (!name)
+            break;
+
+        control_write( client, "  %-15s %s\r\n", name, display );
+    }
+    control_write( client, "\r\n" );
+}
+
+
+static int
+do_gsm_data( ControlClient  client, char*  args )
+{
+    int  nn;
+
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm data <state>'\r\n" );
+        return -1;
+    }
+
+    for (nn = 0; ; nn++) {
+        const char*         name    = _gsm_states[nn].name;
+        ARegistrationState  state   = _gsm_states[nn].state;
+
+        if (!name)
+            break;
+
+        if ( !strcmp( args, name ) ) {
+            if (!android_modem) {
+                control_write( client, "KO: modem emulation not running\r\n" );
+                return -1;
+            }
+            amodem_set_data_registration( android_modem, state );
+            qemu_net_disable = (state != A_REGISTRATION_HOME    &&
+                                state != A_REGISTRATION_ROAMING );
+            return 0;
+        }
+    }
+    control_write( client, "KO: bad GSM data state name, try 'help gsm data' for list of valid values\r\n" );
+    return -1;
+}
+
+static void
+help_gsm_voice( ControlClient  client )
+{
+    int  nn;
+    control_write( client,
+            "the 'gsm voice <state>' allows you to change the state of your GPRS connection\r\n"
+            "valid values for <state> are the following:\r\n\r\n" );
+    for (nn = 0; ; nn++) {
+        const char*         name    = _gsm_states[nn].name;
+        const char*         display = _gsm_states[nn].display;
+
+        if (!name)
+            break;
+
+        control_write( client, "  %-15s %s\r\n", name, display );
+    }
+    control_write( client, "\r\n" );
+}
+
+
+static int
+do_gsm_voice( ControlClient  client, char*  args )
+{
+    int  nn;
+
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm voice <state>'\r\n" );
+        return -1;
+    }
+
+    for (nn = 0; ; nn++) {
+        const char*         name    = _gsm_states[nn].name;
+        ARegistrationState  state   = _gsm_states[nn].state;
+
+        if (!name)
+            break;
+
+        if ( !strcmp( args, name ) ) {
+            if (!android_modem) {
+                control_write( client, "KO: modem emulation not running\r\n" );
+                return -1;
+            }
+            amodem_set_voice_registration( android_modem, state );
+            return 0;
+        }
+    }
+    control_write( client, "KO: bad GSM data state name, try 'help gsm voice' for list of valid values\r\n" );
+    return -1;
+}
+
+
+static int
+gsm_check_number( char*  args )
+{
+    int  nn;
+
+    for (nn = 0; args[nn] != 0; nn++) {
+        int  c = args[nn];
+        if ( !isdigit(c) && c != '+' && c != '#' ) {
+            return -1;
+        }
+    }
+    if (nn == 0)
+        return -1;
+
+    return 0;
+}
+
+static int
+do_gsm_call( ControlClient  client, char*  args )
+{
+    /* check that we have a phone number made of digits */
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
+        return -1;
+    }
+
+    if (gsm_check_number(args)) {
+        control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
+        return -1;
+    }
+
+    if (!android_modem) {
+        control_write( client, "KO: modem emulation not running\r\n" );
+        return -1;
+    }
+    amodem_add_inbound_call( android_modem, args );
+    return 0;
+}
+
+static int
+do_gsm_cancel( ControlClient  client, char*  args )
+{
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm call <phonenumber>'\r\n" );
+        return -1;
+    }
+    if (gsm_check_number(args)) {
+        control_write( client, "KO: bad phone number format, use digits, # and + only\r\n" );
+        return -1;
+    }
+    if (!android_modem) {
+        control_write( client, "KO: modem emulation not running\r\n" );
+        return -1;
+    }
+    if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
+        control_write( client, "KO: could not cancel this number\r\n" );
+        return -1;
+    }
+    return 0;
+}
+
+
+static const char*
+call_state_to_string( ACallState  state )
+{
+    switch (state) {
+        case A_CALL_ACTIVE:   return "active";
+        case A_CALL_HELD:     return "held";
+        case A_CALL_ALERTING: return "ringing";
+        case A_CALL_WAITING:  return "waiting";
+        case A_CALL_INCOMING: return "incoming";
+        default: return "unknown";
+    }
+}
+
+static int
+do_gsm_list( ControlClient  client, char*  args )
+{
+    /* check that we have a phone number made of digits */
+    int   count = amodem_get_call_count( android_modem );
+    int   nn;
+    for (nn = 0; nn < count; nn++) {
+        ACall        call = amodem_get_call( android_modem, nn );
+        const char*  dir;
+
+        if (call == NULL)
+            continue;
+
+        if (call->dir == A_CALL_OUTBOUND)
+            dir = "outbound to ";
+         else
+            dir = "inbound from";
+
+        control_write( client, "%s %-10s : %s\r\n", dir,
+                       call->number, call_state_to_string(call->state) );
+    }
+    return 0;
+}
+
+static int
+do_gsm_busy( ControlClient  client, char*  args )
+{
+    ACall  call;
+
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm busy <phonenumber>'\r\n" );
+        return -1;
+    }
+    call = amodem_find_call_by_number( android_modem, args );
+    if (call == NULL || call->dir != A_CALL_OUTBOUND) {
+        control_write( client, "KO: no current outbound call to number '%s' (call %p)\r\n", args, call );
+        return -1;
+    }
+    if ( amodem_disconnect_call( android_modem, args ) < 0 ) {
+        control_write( client, "KO: could not cancel this number\r\n" );
+        return -1;
+    }
+    return 0;
+}
+
+static int
+do_gsm_hold( ControlClient  client, char*  args )
+{
+    ACall  call;
+
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm out hold <phonenumber>'\r\n" );
+        return -1;
+    }
+    call = amodem_find_call_by_number( android_modem, args );
+    if (call == NULL) {
+        control_write( client, "KO: no current call to/from number '%s'\r\n", args );
+        return -1;
+    }
+    if ( amodem_update_call( android_modem, args, A_CALL_HELD ) < 0 ) {
+        control_write( client, "KO: could put this call on hold\r\n" );
+        return -1;
+    }
+    return 0;
+}
+
+
+static int
+do_gsm_accept( ControlClient  client, char*  args )
+{
+    ACall  call;
+
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'gsm accept <phonenumber>'\r\n" );
+        return -1;
+    }
+    call = amodem_find_call_by_number( android_modem, args );
+    if (call == NULL) {
+        control_write( client, "KO: no current call to/from number '%s'\r\n", args );
+        return -1;
+    }
+    if ( amodem_update_call( android_modem, args, A_CALL_ACTIVE ) < 0 ) {
+        control_write( client, "KO: could not activate this call\r\n" );
+        return -1;
+    }
+    return 0;
+}
+
+
+#if 0
+static const CommandDefRec  gsm_in_commands[] =
+{
+    { "new", "create a new 'waiting' inbound call",
+    "'gsm in create <phonenumber>' creates a new inbound phone call, placed in\r\n"
+    "the 'waiting' state by default, until the system answers/holds/closes it\r\n", NULL
+    do_gsm_in_create, NULL },
+
+    { "hold", "change the state of an oubtound call to 'held'",
+    "change the state of an outbound call to 'held'. this is only possible\r\n"
+    "if the call in the 'waiting' or 'active' state\r\n", NULL,
+    do_gsm_out_hold, NULL },
+
+    { "accept", "change the state of an outbound call to 'active'",
+    "change the state of an outbound call to 'active'. this is only possible\r\n"
+    "if the call is in the 'waiting' or 'held' state\r\n", NULL,
+    do_gsm_out_accept, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+#endif
+
+
+static const CommandDefRec  gsm_commands[] =
+{
+    { "list", "list current phone calls",
+    "'gsm list' lists all inbound and outbound calls and their state\r\n", NULL,
+    do_gsm_list, NULL },
+
+    { "call", "create inbound phone call",
+    "'gsm call <phonenumber>' allows you to simulate a new inbound call\r\n", NULL,
+    do_gsm_call, NULL },
+
+    { "busy", "close waiting outbound call as busy",
+    "'gsm busy <remoteNumber>' closes an outbound call, reporting\r\n"
+    "the remote phone as busy. only possible if the call is 'waiting'.\r\n", NULL,
+    do_gsm_busy, NULL },
+
+    { "hold", "change the state of an oubtound call to 'held'",
+    "'gsm hold <remoteNumber>' change the state of a call to 'held'. this is only possible\r\n"
+    "if the call in the 'waiting' or 'active' state\r\n", NULL,
+    do_gsm_hold, NULL },
+
+    { "accept", "change the state of an outbound call to 'active'",
+    "'gsm accept <remoteNumber>' change the state of a call to 'active'. this is only possible\r\n"
+    "if the call is in the 'waiting' or 'held' state\r\n", NULL,
+    do_gsm_accept, NULL },
+
+    { "cancel", "disconnect an inbound or outbound phone call",
+    "'gsm cancel <phonenumber>' allows you to simulate the end of an inbound or outbound call\r\n", NULL,
+    do_gsm_cancel, NULL },
+
+    { "data", "modify data connection state", NULL, help_gsm_data,
+    do_gsm_data, NULL },
+
+    { "voice", "modify voice connection state", NULL, help_gsm_voice,
+    do_gsm_voice, NULL },
+
+    { "status", "display GSM status",
+    "'gsm status' displays the current state of the GSM emulation\r\n", NULL,
+    do_gsm_status, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                           S M S   C O M M A N D                                 ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+do_sms_send( ControlClient  client, char*  args )
+{
+    char*          p;
+    int            textlen;
+    SmsAddressRec  sender;
+    SmsPDU*        pdus;
+    int            nn;
+
+    /* check that we have a phone number made of digits */
+    if (!args) {
+    MissingArgument:
+        control_write( client, "KO: missing argument, try 'sms send <phonenumber> <text message>'\r\n" );
+        return -1;
+    }
+    p = strchr( args, ' ' );
+    if (!p) {
+        goto MissingArgument;
+    }
+
+    if ( sms_address_from_str( &sender, args, p - args ) < 0 ) {
+        control_write( client, "KO: bad phone number format, must be [+](0-9)*\r\n" );
+        return -1;
+    }
+
+
+    /* un-secape message text into proper utf-8 (conversion happens in-site) */
+    p      += 1;
+    textlen = strlen(p);
+    textlen = sms_utf8_from_message_str( p, textlen, (unsigned char*)p, textlen );
+    if (textlen < 0) {
+        control_write( client, "message must be utf8 and can use the following escapes:\r\n"
+                       "    \\n      for a newline\r\n"
+                       "    \\xNN    where NN are two hexadecimal numbers\r\n"
+                       "    \\uNNNN  where NNNN are four hexadecimal numbers\r\n"
+                       "    \\\\     to send a '\\' character\r\n\r\n"
+                       "    anything else is an error\r\n"
+                       "KO: badly formatted text\r\n" );
+        return -1;
+    }
+
+    if (!android_modem) {
+        control_write( client, "KO: modem emulation not running\r\n" );
+        return -1;
+    }
+
+    /* create a list of SMS PDUs, then send them */
+    pdus = smspdu_create_deliver_utf8( (cbytes_t)p, textlen, &sender, NULL );
+    if (pdus == NULL) {
+        control_write( client, "KO: internal error when creating SMS-DELIVER PDUs\n" );
+        return -1;
+    }
+
+    for (nn = 0; pdus[nn] != NULL; nn++)
+        amodem_receive_sms( android_modem, pdus[nn] );
+
+    smspdu_free_list( pdus );
+    return 0;
+}
+
+static int
+do_sms_sendpdu( ControlClient  client, char*  args )
+{
+    SmsPDU  pdu;
+
+    /* check that we have a phone number made of digits */
+    if (!args) {
+        control_write( client, "KO: missing argument, try 'sms sendpdu <hexstring>'\r\n" );
+        return -1;
+    }
+
+    if (!android_modem) {
+        control_write( client, "KO: modem emulation not running\r\n" );
+        return -1;
+    }
+
+    pdu = smspdu_create_from_hex( args, strlen(args) );
+    if (pdu == NULL) {
+        control_write( client, "KO: badly formatted <hexstring>\r\n" );
+        return -1;
+    }
+
+    amodem_receive_sms( android_modem, pdu );
+    smspdu_free( pdu );
+    return 0;
+}
+
+static const CommandDefRec  sms_commands[] =
+{
+    { "send", "send inbound SMS text message",
+    "'sms send <phonenumber> <message>' allows you to simulate a new inbound sms message\r\n", NULL,
+    do_sms_send, NULL },
+
+    { "pdu", "send inbound SMS PDU",
+    "'sms pdu <hexstring>' allows you to simulate a new inbound sms PDU\r\n"
+    "(used internally when one emulator sends SMS messages to another instance).\r\n"
+    "you probably don't want to play with this at all\r\n", NULL,
+    do_sms_sendpdu, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+static void
+do_control_write(void* data, const char* string)
+{
+    control_write((ControlClient)data, string);
+}
+
+static int
+do_power_display( ControlClient client, char*  args )
+{
+    goldfish_battery_display(do_control_write, client);
+    return 0;
+}
+
+static int
+do_ac_state( ControlClient  client, char*  args )
+{
+    if (args) {
+        if (strcasecmp(args, "on") == 0) {
+            goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 1);
+            return 0;
+        }
+        if (strcasecmp(args, "off") == 0) {
+            goldfish_battery_set_prop(1, POWER_SUPPLY_PROP_ONLINE, 0);
+            return 0;
+        }
+    }
+
+    control_write( client, "KO: Usage: \"ac on\" or \"ac off\"\n" );
+    return -1;
+}
+
+static int
+do_battery_status( ControlClient  client, char*  args )
+{
+    if (args) {
+        if (strcasecmp(args, "unknown") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_UNKNOWN);
+            return 0;
+        }
+        if (strcasecmp(args, "charging") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_CHARGING);
+            return 0;
+        }
+        if (strcasecmp(args, "discharging") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_DISCHARGING);
+            return 0;
+        }
+        if (strcasecmp(args, "not-charging") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_NOT_CHARGING);
+            return 0;
+        }
+        if (strcasecmp(args, "full") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_STATUS_FULL);
+            return 0;
+        }
+    }
+
+    control_write( client, "KO: Usage: \"status unknown|charging|discharging|not-charging|full\"\n" );
+    return -1;
+}
+
+static int
+do_battery_present( ControlClient  client, char*  args )
+{
+    if (args) {
+        if (strcasecmp(args, "true") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 1);
+            return 0;
+        }
+        if (strcasecmp(args, "false") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_PRESENT, 0);
+            return 0;
+        }
+    }
+
+    control_write( client, "KO: Usage: \"present true\" or \"present false\"\n" );
+    return -1;
+}
+
+static int
+do_battery_health( ControlClient  client, char*  args )
+{
+    if (args) {
+        if (strcasecmp(args, "unknown") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNKNOWN);
+            return 0;
+        }
+        if (strcasecmp(args, "good") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_GOOD);
+            return 0;
+        }
+        if (strcasecmp(args, "overheat") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERHEAT);
+            return 0;
+        }
+        if (strcasecmp(args, "dead") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_DEAD);
+            return 0;
+        }
+        if (strcasecmp(args, "overvoltage") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_OVERVOLTAGE);
+            return 0;
+        }
+        if (strcasecmp(args, "failure") == 0) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_HEALTH_UNSPEC_FAILURE);
+            return 0;
+        }
+    }
+
+    control_write( client, "KO: Usage: \"health unknown|good|overheat|dead|overvoltage|failure\"\n" );
+    return -1;
+}
+
+static int
+do_battery_capacity( ControlClient  client, char*  args )
+{
+    if (args) {
+        int capacity;
+
+        if (sscanf(args, "%d", &capacity) == 1 && capacity >= 0 && capacity <= 100) {
+            goldfish_battery_set_prop(0, POWER_SUPPLY_PROP_CAPACITY, capacity);
+            return 0;
+        }
+    }
+
+    control_write( client, "KO: Usage: \"capacity <percentage>\"\n" );
+    return -1;
+}
+
+
+static const CommandDefRec  power_commands[] =
+{
+    { "display", "display battery and charger state",
+    "display battery and charger state\r\n", NULL,
+    do_power_display, NULL },
+
+    { "ac", "set AC charging state",
+    "'ac on|off' allows you to set the AC charging state to on or off\r\n", NULL,
+    do_ac_state, NULL },
+
+    { "status", "set battery status",
+    "'status unknown|charging|discharging|not-charging|full' allows you to set battery status\r\n", NULL,
+    do_battery_status, NULL },
+
+    { "present", "set battery present state",
+    "'present true|false' allows you to set battery present state to true or false\r\n", NULL,
+    do_battery_present, NULL },
+
+    { "health", "set battery health state",
+    "'health unknown|good|overheat|dead|overvoltage|failure' allows you to set battery health state\r\n", NULL,
+    do_battery_health, NULL },
+
+    { "capacity", "set battery capacity state",
+    "'capacity <percentage>' allows you to set battery capacity to a value 0 - 100\r\n", NULL,
+    do_battery_capacity, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                         E  V  E  N  T   C O M M A N D S                         ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+
+static int
+do_event_send( ControlClient  client, char*  args )
+{
+    char*   p;
+
+    if (!args) {
+        control_write( client, "KO: Usage: event send <type>:<code>:<value> ...\r\n" );
+        return -1;
+    }
+
+    p = args;
+    while (*p) {
+        char*  q;
+        int    type, code, value, ret;
+
+        p += strspn( args, " \t" );  /* skip spaces */
+        if (*p == 0)
+            break;
+
+        q  = p + strcspn( p, " \t" );
+
+        if (q == p)
+            break;
+
+        ret = android_event_from_str( p, &type, &code, &value );
+        if (ret < 0) {
+            if (ret == -1) {
+                control_write( client,
+                               "KO: invalid event type in '%.*s', try 'event list types' for valid values\r\n",
+                               q-p, p );
+            } else if (ret == -2) {
+                control_write( client,
+                               "KO: invalid event code in '%.*s', try 'event list codes <type>' for valid values\r\n",
+                               q-p, p );
+            } else {
+                control_write( client,
+                               "KO: invalid event value in '%.*s', must be an integer\r\n",
+                               q-p, p);
+            }
+            return -1;
+        }
+
+        kbd_generic_event( type, code, value );
+        p = q;
+    }
+    return 0;
+}
+
+static int
+do_event_types( ControlClient  client, char*  args )
+{
+    int  count = android_event_get_type_count();
+    int  nn;
+
+    control_write( client, "event <type> can be an integer or one of the following aliases\r\n" );
+    for (nn = 0; nn < count; nn++) {
+        char  tmp[16];
+        char* p = tmp;
+        char* end = p + sizeof(tmp);
+        int   count2 = android_event_get_code_count( nn );;
+
+        p = android_event_bufprint_type_str( p, end, nn );
+
+        control_write( client, "    %-8s", tmp );
+        if (count2 > 0)
+            control_write( client, "  (%d code aliases)", count2 );
+
+        control_write( client, "\r\n" );
+    }
+    return 0;
+}
+
+static int
+do_event_codes( ControlClient  client, char*  args )
+{
+    int  count;
+    int  nn, type, dummy;
+
+    if (!args) {
+        control_write( client, "KO: argument missing, try 'event codes <type>'\r\n" );
+        return -1;
+    }
+
+    if ( android_event_from_str( args, &type, &dummy, &dummy ) < 0 ) {
+        control_write( client, "KO: bad argument, see 'event types' for valid values\r\n" );
+        return -1;
+    }
+
+    count = android_event_get_code_count( type );
+    if (count == 0) {
+        control_write( client, "no code aliases defined for this type\r\n" );
+    } else {
+        control_write( client, "type '%s' accepts the following <code> aliases:\r\n",
+                       args );
+        for (nn = 0; nn < count; nn++) {
+            char  temp[20], *p = temp, *end = p + sizeof(temp);
+            android_event_bufprint_code_str( p, end, type, nn );
+            control_write( client, "    %-12s\r\n", temp );
+        }
+    }
+
+    return 0;
+}
+
+static __inline__ int
+utf8_next( unsigned char* *pp, unsigned char*  end )
+{
+    unsigned char*  p      = *pp;
+    int             result = -1;
+
+    if (p < end) {
+        int  c= *p++;
+        if (c >= 128) {
+            if ((c & 0xe0) == 0xc0)
+                c &= 0x1f;
+            else if ((c & 0xf0) == 0xe0)
+                c &= 0x0f;
+            else
+                c &= 0x07;
+
+            while (p < end && (p[0] & 0xc0) == 0x80) {
+                c = (c << 6) | (p[0] & 0x3f);
+            }
+        }
+        result = c;
+        *pp    = p;
+    }
+    return result;
+}
+
+static int
+do_event_text( ControlClient  client, char*  args )
+{
+    SkinKeyboard*   keyboard;
+    unsigned char*  p   = (unsigned char*) args;
+    unsigned char*  end = p + strlen(args);
+    int             textlen;
+
+    if (!args) {
+        control_write( client, "KO: argument missing, try 'event text <message>'\r\n" );
+        return -1;
+    }
+    keyboard = android_emulator_get_keyboard();
+    if (keyboard == NULL) {
+        control_write( client, "KO: no keyboard active in current device layout/config\r\n" );
+        return -1;
+    }
+
+    /* un-secape message text into proper utf-8 (conversion happens in-site) */
+    textlen = strlen((char*)p);
+    textlen = sms_utf8_from_message_str( args, textlen, (unsigned char*)p, textlen );
+    if (textlen < 0) {
+        control_write( client, "message must be utf8 and can use the following escapes:\r\n"
+                       "    \\n      for a newline\r\n"
+                       "    \\xNN    where NN are two hexadecimal numbers\r\n"
+                       "    \\uNNNN  where NNNN are four hexadecimal numbers\r\n"
+                       "    \\\\     to send a '\\' character\r\n\r\n"
+                       "    anything else is an error\r\n"
+                       "KO: badly formatted text\r\n" );
+        return -1;
+    }
+
+    end = p + textlen;
+    while (p < end) {
+        int  c = utf8_next( &p, end );
+        if (c <= 0)
+            break;
+
+        skin_keyboard_process_unicode_event( keyboard, (unsigned)c, 1 );
+        skin_keyboard_process_unicode_event( keyboard, (unsigned)c, 0 );
+        skin_keyboard_flush( keyboard );
+    }
+
+    return 0;
+}
+
+static const CommandDefRec  event_commands[] =
+{
+    { "send", "send a series of events to the kernel",
+    "'event send <type>:<code>:<value> ...' allows your to send one or more hardware events\r\n"
+    "to the Android kernel. you can use text names or integers for <type> and <code>\r\n", NULL,
+    do_event_send, NULL },
+
+    { "types", "list all <type> aliases",
+    "'event types' list all <type> string aliases supported by the 'event' subcommands\r\n",
+    NULL, do_event_types, NULL },
+
+    { "codes", "list all <code> aliases for a given <type>",
+    "'event codes <type>' lists all <code> string aliases for a given event <type>\r\n",
+    NULL, do_event_codes, NULL },
+
+    { "text", "simulate keystrokes from a given text",
+    "'event text <message>' allows you to simulate keypresses to generate a given text\r\n"
+    "message. <message> must be an utf-8 string. Unicode points will be reverse-mapped\r\n"
+    "according to the current device keyboard. unsupported characters will be discarded\r\n"
+    "silently\r\n", NULL, do_event_text, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                               V M   C O M M A N D S                             ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+do_avd_stop( ControlClient  client, char*  args )
+{
+    if (!vm_running) {
+        control_write( client, "KO: virtual device already stopped\r\n" );
+        return -1;
+    }
+    vm_stop(EXCP_INTERRUPT);
+    return 0;
+}
+
+static int
+do_avd_start( ControlClient  client, char*  args )
+{
+    if (vm_running) {
+        control_write( client, "KO: virtual device already running\r\n" );
+        return -1;
+    }
+    vm_start();
+    return 0;
+}
+
+static int
+do_avd_status( ControlClient  client, char*  args )
+{
+    control_write( client, "virtual device is %s\r\n", vm_running ? "running" : "stopped" );
+    return 0;
+}
+
+static int
+do_avd_name( ControlClient  client, char*  args )
+{
+    control_write( client, "%s\r\n", avdInfo_getName(android_avdInfo) );
+    return 0;
+}
+
+static const CommandDefRec  vm_commands[] =
+{
+    { "stop", "stop the virtual device",
+    "'avd stop' stops the virtual device immediately, use 'avd start' to continue execution\r\n",
+    NULL, do_avd_stop, NULL },
+
+    { "start", "start/restart the virtual device",
+    "'avd start' will start or continue the virtual device, use 'avd stop' to stop it\r\n",
+    NULL, do_avd_start, NULL },
+
+    { "status", "query virtual device status",
+    "'avd status' will indicate wether the virtual device is running or not\r\n",
+    NULL, do_avd_status, NULL },
+
+    { "name", "query virtual device name",
+    "'avd name' will return the name of this virtual device\r\n",
+    NULL, do_avd_name, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                             G E O   C O M M A N D S                             ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+do_geo_nmea( ControlClient  client, char*  args )
+{
+    if (!args) {
+        control_write( client, "KO: NMEA sentence missing, try 'help geo nmea'\r\n" );
+        return -1;
+    }
+    if (!android_gps_cs) {
+        control_write( client, "KO: no GPS emulation in this virtual device\r\n" );
+        return -1;
+    }
+    android_gps_send_nmea( args );
+    return 0;
+}
+
+static int
+do_geo_fix( ControlClient  client, char*  args )
+{
+#define  MAX_GEO_PARAMS  3
+    char*   p = args;
+    int     n_params = 0;
+    double  params[ MAX_GEO_PARAMS ];
+
+    static  int     last_time = 0;
+    static  double  last_altitude = 0.;
+
+    if (!p)
+        p = "";
+
+    /* tokenize */
+    while (*p) {
+        char*   end;
+        double  val = strtod( p, &end );
+
+        if (end == p) {
+            control_write( client, "KO: argument '%s' is not a number\n", p );
+            return -1;
+        }
+
+        params[n_params++] = val;
+        if (n_params >= MAX_GEO_PARAMS)
+            break;
+
+        p = end;
+        while (*p && (p[0] == ' ' || p[0] == '\t'))
+            p += 1;
+    }
+
+    /* sanity check */
+    if (n_params < 2) {
+        control_write( client, "KO: not enough arguments: see 'help geo fix' for details\r\n" );
+        return -1;
+    }
+
+    /* generate an NMEA sentence for this fix */
+    {
+        STRALLOC_DEFINE(s);
+        double   val;
+        int      deg, min;
+        char     hemi;
+
+        /* first, the time */
+        stralloc_add_format( s, "$GPGGA,%06d", last_time );
+        last_time ++;
+
+        /* then the latitude */
+        hemi = 'N';
+        val  = params[1];
+        if (val < 0) {
+            hemi = 'S';
+            val  = -val;
+        }
+        deg = (int) val;
+        min = 60*(val - deg);
+        val = val - min/60.;
+        stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)(val * 10000), hemi );
+
+        /* the longitude */
+        hemi = 'E';
+        val  = params[0];
+        if (val < 0) {
+            hemi = 'W';
+            val  = -val;
+        }
+        deg = (int) val;
+        min = 60*(val - deg);
+        val = val - min/60.;
+        stralloc_add_format( s, ",%02d%02d.%04d,%c", deg, min, (int)(val * 10000), hemi );
+
+        /* bogus fix quality, empty satellite count and dilutions */
+        stralloc_add_str( s, ",1,,,," );
+
+        /* optional altitude */
+        if (n_params >= 3) {
+            stralloc_add_format( s, "%.1g", params[2] );
+            last_altitude = params[2];
+        } else {
+            stralloc_add_str( s, "," );
+        }
+        /* bogus rest and checksum */
+        stralloc_add_str( s, ",,,*47" );
+
+        /* send it, then free */
+        android_gps_send_nmea( stralloc_cstr(s) );
+        stralloc_reset( s );
+    }
+    return 0;
+}
+
+static const CommandDefRec  geo_commands[] =
+{
+    { "nmea", "send an GPS NMEA sentence",
+    "'geo nema <sentence>' sends a NMEA 0183 sentence to the emulated device, as\r\n"
+    "if it came from an emulated GPS modem. <sentence> must begin with '$GP'. only\r\n"
+    "'$GPGGA' and '$GPRCM' sentences are supported at the moment.\r\n",
+    NULL, do_geo_nmea, NULL },
+
+    { "fix", "send a simple GPS fix",
+    "'geo fix <longitude> <latitude> [<altitude>]' allows you to send a\r\n"
+    "simple GPS fix to the emulated system. the parameters are:\r\n\r\n"
+    "  <longitude>   longitude, in decimal degrees\r\n"
+    "  <latitude>    latitude, in decimal degrees\r\n"
+    "  <altitude>    optional altitude in meters\r\n"
+    "\r\n",
+    NULL, do_geo_fix, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                           M A I N   C O M M A N D S                             ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+extern void  android_emulator_set_window_scale( double, int );
+
+static int
+do_window_scale( ControlClient  client, char*  args )
+{
+    double  scale;
+    int     is_dpi = 0;
+    char*   end;
+
+    if (!args) {
+        control_write( client, "KO: argument missing, try 'window scale <scale>'\r\n" );
+        return -1;
+    }
+
+    scale = strtol( args, &end, 10 );
+    if (end > args && !memcmp( end, "dpi", 4 )) {
+        is_dpi = 1;
+    }
+    else {
+        scale = strtod( args, &end );
+        if (end == args || end[0]) {
+            control_write( client, "KO: argument <scale> must be a real number, or an integer followed by 'dpi'\r\n" );
+            return -1;
+        }
+    }
+
+    android_emulator_set_window_scale( scale, is_dpi );
+    return 0;
+}
+
+static const CommandDefRec  window_commands[] =
+{
+    { "scale", "change the window scale",
+    "'window scale <scale>' allows you to change the scale of the emulator window at runtime\r\n"
+    "<scale> must be either a real number between 0.1 and 3.0, or an integer followed by\r\n"
+    "the 'dpi' prefix (as in '120dpi')\r\n",
+    NULL, do_window_scale, NULL },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+/********************************************************************************************/
+/********************************************************************************************/
+/*****                                                                                 ******/
+/*****                           M A I N   C O M M A N D S                             ******/
+/*****                                                                                 ******/
+/********************************************************************************************/
+/********************************************************************************************/
+
+static int
+do_kill( ControlClient  client, char*  args )
+{
+	control_write( client, "OK: killing emulator, bye bye\r\n" );
+	exit(0);
+}
+
+static const CommandDefRec   main_commands[] =
+{
+    { "help|h|?", "print a list of commands", NULL, NULL, do_help, NULL },
+
+    { "event", "simulate hardware events",
+    "allows you to send fake hardware events to the kernel\r\n", NULL,
+    NULL, event_commands },
+
+    { "geo", "Geo-location commands",
+      "allows you to change Geo-related settings, or to send GPS NMEA sentences\r\n", NULL,
+      NULL, geo_commands },
+
+    { "gsm", "GSM related commands",
+      "allows you to change GSM-related settings, or to make a new inbound phone call\r\n", NULL,
+      NULL, gsm_commands },
+
+    { "kill", "kill the emulator instance", NULL, NULL,
+	  do_kill, NULL },
+
+    { "network", "manage network settings",
+      "allows you to manage the settings related to the network data connection of the\r\n"
+      "emulated device.\r\n", NULL,
+      NULL, network_commands },
+
+    { "power", "power related commands",
+      "allows to change battery and AC power status\r\n", NULL,
+      NULL, power_commands },
+
+    { "quit|exit", "quit control session", NULL, NULL,
+      do_quit, NULL },
+
+    { "redir",    "manage port redirections",
+      "allows you to add, list and remove UDP and/or PORT redirection from the host to the device\r\n"
+      "as an example, 'redir  tcp:5000:6000' will route any packet sent to the host's TCP port 5000\r\n"
+      "to TCP port 6000 of the emulated device\r\n", NULL,
+      NULL, redir_commands },
+
+    { "sms", "SMS related commands",
+      "allows you to simulate an inbound SMS\r\n", NULL,
+      NULL, sms_commands },
+
+    { "avd", "manager virtual device state",
+    "allows to change (e.g. start/stop) the virtual device state\r\n", NULL,
+    NULL, vm_commands },
+
+    { "window", "manage emulator window",
+    "allows you to modify the emulator window\r\n", NULL,
+    NULL, window_commands },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL }
+};
+
+
+static ControlGlobalRec  _g_global;
+
+int
+control_console_start( int  port )
+{
+    return control_global_init( &_g_global, port );
+}
diff --git a/android/globals.h b/android/globals.h
new file mode 100644
index 0000000..b26663d
--- /dev/null
+++ b/android/globals.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_GLOBALS_H
+#define _ANDROID_GLOBALS_H
+
+#include "android/avd/info.h"
+#include "android/avd/hw-config.h"
+
+/* this structure is setup when loading the virtual device
+ * after that, you can read the 'flags' field to determine
+ * wether a data or cache wipe has been in effect.
+ */
+extern AvdInfoParams     android_avdParams[1];
+
+/* a pointer to the android virtual device information
+ * object, which can be queried for the paths of various
+ * image files or the skin
+ */
+extern AvdInfo*          android_avdInfo;
+
+/* the hardware configuration for this specific virtual device */
+extern AndroidHwConfig   android_hw[1];
+
+#endif /* _ANDROID_GLOBALS_H */
diff --git a/android/gps.c b/android/gps.c
new file mode 100644
index 0000000..be68cfc
--- /dev/null
+++ b/android/gps.c
@@ -0,0 +1,37 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/gps.h"
+#include "android/utils/debug.h"
+#include "qemu-char.h"
+
+CharDriverState*   android_gps_cs;
+
+#define  D(...)  VERBOSE_PRINT(gps,__VA_ARGS__)
+
+void
+android_gps_send_nmea( const char*  sentence )
+{
+    if (sentence == NULL)
+        return;
+
+    D("sending '%s'", sentence);
+
+    if (android_gps_cs == NULL) {
+        D("missing GPS channel, ignored");
+        return;
+    }
+
+    qemu_chr_write( android_gps_cs, (const void*)sentence, strlen(sentence) );
+    qemu_chr_write( android_gps_cs, (const void*)"\n", 1 );
+}
+
+
diff --git a/android/gps.h b/android/gps.h
new file mode 100644
index 0000000..33ecece
--- /dev/null
+++ b/android/gps.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_gps_h
+#define _android_gps_h
+
+#include "qemu-common.h"
+
+/* this is the internal character driver used to communicate with the
+ * emulated GPS unit. see qemu_chr_open() in vl.c */
+extern CharDriverState*  android_gps_cs;
+
+extern void  android_gps_send_nmea( const char*  sentence );
+
+#endif /* _android_gps_h */
diff --git a/android/help.c b/android/help.c
new file mode 100644
index 0000000..92531d9
--- /dev/null
+++ b/android/help.c
@@ -0,0 +1,1452 @@
+#include "android/help.h"
+#include "android/cmdline-option.h"
+#include "android/utils/path.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/debug.h"
+#include "android/utils/misc.h"
+#include "android/skin/keyset.h"
+#include "android/android.h"
+#include <stdint.h>
+#include "audio/audio.h"
+#include <string.h>
+#include <stdlib.h>
+
+/* XXX: TODO: put most of the help stuff in auto-generated files */
+
+#define  PRINTF(...)  stralloc_add_format(out,__VA_ARGS__)
+
+static void
+help_virtual_device( stralloc_t*  out )
+{
+    PRINTF(
+    "  An Android Virtual Device (AVD) models a single virtual\n"
+    "  device running the Android platform that has, at least, its own\n"
+    "  kernel, system image and data partition.\n\n"
+
+    "  Only one emulator process can run a given AVD at a time, but\n"
+    "  you can create several AVDs and run them concurrently.\n\n"
+
+    "  You can invoke a given AVD at startup using either '-avd <name>'\n"
+    "  or '@<name>', both forms being equivalent. For example, to launch\n"
+    "  the AVD named 'foo', type:\n\n"
+
+    "      emulator @foo\n\n"
+
+    "  The 'android' helper tool can be used to manage virtual devices.\n"
+    "  For example:\n\n"
+
+    "    android avd   -- creates a new virtual device.\n"
+    "    android list  -- list all virtual devices available.\n\n"
+
+    "  Each AVD really corresponds to a content directory which stores\n"
+    "  persistent and writable disk images as well as configuration files.\n"
+
+    "  Each AVD must be created against an existing SDK platform or add-on.\n"
+    "  For more information on this topic, see -help-sdk-images.\n\n"
+
+    "  SPECIAL NOTE: in the case where you are *not* using the emulator\n"
+    "  with the Android SDK, but with the Android build system, you will\n"
+    "  need to define the ANDROID_PRODUCT_OUT variable in your environment.\n"
+    "  See -help-build-images for the details.\n"
+    );
+}
+
+
+static void
+help_sdk_images( stralloc_t*  out )
+{
+    PRINTF(
+    "  The Android SDK now supports multiple versions of the Android platform.\n"
+    "  Each SDK 'platform' corresponds to:\n\n"
+
+    "    - a given version of the Android API.\n"
+    "    - a set of corresponding system image files.\n"
+    "    - build and configuration properties.\n"
+    "    - an android.jar file used when building your application.\n"
+    "    - skins.\n\n"
+
+    "  The Android SDK also supports the concept of 'add-ons'. Each add-on is\n"
+    "  based on an existing platform, and provides replacement or additional\n"
+    "  image files, android.jar, hardware configuration options and/or skins.\n\n"
+
+    "  The purpose of add-ons is to allow vendors to provide their own customized\n"
+    "  system images and APIs without needing to package a complete SDK.\n\n"
+
+    "  Before using the SDK, you need to create an Android Virtual Device (AVD)\n"
+    "  (see -help-virtual-device for details). Each AVD is created in reference\n"
+    "  to a given SDK platform *or* add-on, and will search the corresponding\n"
+    "  directories for system image files, in the following order:\n\n" 
+
+    "    - in the AVD's content directory.\n"
+    "    - in the AVD's SDK add-on directory, if any.\n"
+    "    - in the AVD's SDK platform directory, if any.\n\n"
+
+    "  The image files are documented in -help-disk-images. By default, an AVD\n"
+    "  content directory will contain the following persistent image files:\n\n"
+
+    "     userdata-qemu.img     - the /data partition image file\n"
+    "     cache.img             - the /cache partition image file\n\n"
+
+    "  You can use -wipe-data to re-initialize the /data partition to its factory\n"
+    "  defaults. This will erase all user settings for the virtual device.\n\n"
+    );
+}
+
+static void
+help_build_images( stralloc_t*  out )
+{
+    PRINTF(
+    "  The emulator detects that you are working from the Android build system\n"
+    "  by looking at the ANDROID_PRODUCT_OUT variable in your environment.\n\n"
+
+    "  If it is defined, it should point to the product-specific directory that\n"
+    "  contains the generated system images.\n"
+
+    "  In this case, the emulator will look by default for the following image\n"
+    "  files there:\n\n"
+
+    "    - system.img   : the *initial* system image.\n"
+    "    - ramdisk.img  : the ramdisk image used to boot the system.\n"
+    "    - userdata.img : the *initial* user data image (see below).\n"
+    "    - kernel-qemu  : the emulator-specific Linux kernel image.\n\n"
+
+    "  If the kernel image is not found in the out directory, then it is searched\n"
+    "  in <build-root>/prebuilt/android-arm/kernel/.\n\n"
+
+    "  Skins will be looked in <build-root>/development/emulator/skins/\n\n"
+
+    "  You can use the -sysdir, -system, -kernel, -ramdisk, -datadir, -data options\n"
+    "  to specify different search directories or specific image files. You can\n"
+    "  also use the -cache and -sdcard options to indicate specific cache partition\n"
+    "  and SD Card image files.\n\n"
+
+    "  For more details, see the corresponding -help-<option> section.\n\n"
+
+    "  Note that the following behaviour is specific to 'build mode':\n\n"
+
+    "  - the *initial* system image is copied to a temporary file which is\n"
+    "    automatically removed when the emulator exits. There is thus no way to\n"
+    "    make persistent changes to this image through the emulator, even if\n"
+    "    you use the '-image <file>' option.\n\n"
+
+    "  - unless you use the '-cache <file>' option, the cache partition image\n"
+    "    is backed by a temporary file that is initially empty and destroyed on\n"
+    "    program exit.\n\n"
+
+    "  SPECIAL NOTE: If you are using the emulator with the Android SDK, the\n"
+    "  information above doesn't apply. See -help-sdk-images for more details.\n"
+    );
+}
+
+static void
+help_disk_images( stralloc_t*  out )
+{
+    char  datadir[256];
+
+    bufprint_config_path( datadir, datadir + sizeof(datadir) );
+
+    PRINTF(
+    "  The emulator needs several key image files to run appropriately.\n"
+    "  Their exact location depends on whether you're using the emulator\n"
+    "  from the Android SDK, or not (more details below).\n\n"
+
+    "  The minimal required image files are the following:\n\n"
+
+    "    kernel-qemu      the emulator-specific Linux kernel image\n"
+    "    ramdisk.img      the ramdisk image used to boot the system\n"
+    "    system.img       the *initial* system image\n"
+    "    userdata.img     the *initial* data partition image\n\n"
+
+    "  It will also use the following writable image files:\n\n"
+
+    "    userdata-qemu.img  the persistent data partition image\n"
+    "    system-qemu.img    an *optional* persistent system image\n"
+    "    cache.img          an *optional* cache partition image\n"
+    "    sdcard.img         an *optional* SD Card partition image\n\n"
+
+    "  If you use a virtual device, its content directory should store\n"
+    "  all writable images, and read-only ones will be found from the\n"
+    "  corresponding platform/add-on directories. See -help-sdk-images\n"
+    "  for more details.\n\n"
+
+    "  If you are building from the Android build system, you should\n"
+    "  have ANDROID_PRODUCT_OUT defined in your environment, and the\n"
+    "  emulator shall be able to pick-up the right image files automatically.\n"
+    "  See -help-build-images for more details.\n\n"
+
+    "  If you're neither using the SDK or the Android build system, you\n"
+    "  can still run the emulator by explicitely providing the paths to\n"
+    "  *all* required disk images through a combination of the following\n"
+    "  options: -sysdir, -datadir, -kernel, -ramdisk, -system, -data, -cache\n"
+    "  and -sdcard\n\n"
+
+    "  The actual logic being that the emulator should be able to find all\n"
+    "  images from the options you give it.\n\n"
+
+    "  For more detail, see the corresponding -help-<option> entry.\n\n"
+
+    "  Other related options are:\n\n"
+
+    "      -init-data   Specify an alernative *initial* user data image\n\n"
+
+    "      -wipe-data   Copy the content of the *initial* user data image\n"
+    "                   (userdata.img) into the writable one (userdata-qemu.img)\n\n"
+
+    "      -no-cache    do not use a cache partition, even if one is\n"
+    "                   available.\n\n"
+    ,
+    datadir );
+}
+
+static void
+help_keys(stralloc_t*  out)
+{
+    int  pass, maxw = 0;
+
+    stralloc_add_str( out, "  When running the emulator, use the following keypresses:\n\n");
+
+    if (!android_keyset)
+        android_keyset = skin_keyset_new_from_text( skin_keyset_get_default() );
+
+    for (pass = 0; pass < 2; pass++) {
+        SkinKeyCommand  cmd;
+
+        for (cmd = SKIN_KEY_COMMAND_NONE+1; cmd < SKIN_KEY_COMMAND_MAX; cmd++)
+        {
+            SkinKeyBinding  bindings[ SKIN_KEY_COMMAND_MAX_BINDINGS ];
+            int             n, count, len;
+            char            temp[32], *p = temp, *end = p + sizeof(temp);
+
+            count = skin_keyset_get_bindings( android_keyset, cmd, bindings );
+            if (count <= 0)
+                continue;
+
+            for (n = 0; n < count; n++) {
+                p = bufprint(p, end, "%s%s", (n == 0) ? "" : ", ",
+                            skin_key_symmod_to_str( bindings[n].sym, bindings[n].mod ) );
+            }
+
+            if (pass == 0) {
+                len = strlen(temp);
+                if (len > maxw)
+                    maxw = len;
+            } else {
+                PRINTF( "    %-*s  %s\n", maxw, temp, skin_key_command_description(cmd) );
+            }
+        }
+    }
+    PRINTF( "\n" );
+    PRINTF( "  note that NumLock must be deactivated for keypad keys to work\n\n" );
+}
+
+
+static void
+help_environment(stralloc_t*  out)
+{
+    PRINTF(
+    "  The Android emulator looks at various environment variables when it starts:\n\n"
+
+    "  If ANDROID_LOG_TAGS is defined, it will be used as in '-logcat <tags>'.\n\n"
+
+    "  If 'http_proxy' is defined, it will be used as in '-http-proxy <proxy>'.\n\n"
+
+    "  If ANDROID_VERBOSE is defined, it can contain a comma-separated list of\n"
+    "  verbose items. for example:\n\n"
+
+    "      ANDROID_VERBOSE=socket,radio\n\n"
+
+    "  is equivalent to using the '-verbose -verbose-socket -verbose-radio'\n"
+    "  options together. unsupported items will be ignored.\n\n"
+
+    "  If ANDROID_LOG_TAGS is defined, it will be used as in '-logcat <tags>'.\n\n"
+
+    "  If ANDROID_SDK_HOME is defined, it indicates the path of the '.android'\n"
+    "  directory which contains the SDK user data (Android Virtual Devices,\n"
+    "  DDMS preferences, key stores, etc.).\n\n"
+
+    "  If ANDROID_SDK_ROOT is defined, it indicates the path of the SDK\n"
+    "  installation directory.\n\n"
+
+    );
+}
+
+
+static void
+help_keyset_file(stralloc_t*  out)
+{
+    int           n, count;
+    const char**  strings;
+    char          temp[MAX_PATH];
+
+    PRINTF(
+    "  on startup, the emulator looks for 'keyset' file that contains the\n"
+    "  configuration of key-bindings to use. the default location on this\n"
+    "  system is:\n\n"
+    );
+
+    bufprint_config_file( temp, temp+sizeof(temp), KEYSET_FILE );
+    PRINTF( "    %s\n\n", temp );
+
+    PRINTF(
+    "  if the file doesn't exist, the emulator writes one containing factory\n"
+    "  defaults. you are then free to modify it to suit specific needs.\n\n"
+    "  this file shall contain a list of text lines in the following format:\n\n"
+
+    "    <command> [<modifiers>]<key>\n\n"
+
+    "  where <command> is an emulator-specific command name, i.e. one of:\n\n"
+    );
+
+    count   = SKIN_KEY_COMMAND_MAX-1;
+    strings = calloc( count, sizeof(char*) );
+    for (n = 0; n < count; n++)
+        strings[n] = skin_key_command_to_str(n+1);
+
+    stralloc_tabular( out, strings, count, "    ", 80-8 );
+    free(strings);
+
+    PRINTF(
+    "\n"
+    "  <modifers> is an optional list of <modifier> elements (without separators)\n"
+    "  which can be one of:\n\n"
+
+    "    Ctrl-     Left Control Key\n"
+    "    Shift-    Left Shift Key\n"
+    "    Alt-      Left Alt key\n"
+    "    RCtrl-    Right Control Key\n"
+    "    RShift-   Right Shift Key\n"
+    "    RAlt-     Right Alt key (a.k.a AltGr)\n"
+    "\n"
+    "  finally <key> is a QWERTY-specific keyboard symbol which can be one of:\n\n"
+    );
+    count   = skin_keysym_str_count();
+    strings = calloc( count, sizeof(char*) );
+    for (n = 0; n < count; n++)
+        strings[n] = skin_keysym_str(n);
+
+    stralloc_tabular( out, strings, count, "    ", 80-8 );
+    free(strings);
+
+    PRINTF(
+    "\n"
+    "  case is not significant, and a single command can be associated to up\n"
+    "  to %d different keys. to bind a command to multiple keys, use commas to\n"
+    "  separate them. here are some examples:\n\n",
+    SKIN_KEY_COMMAND_MAX_BINDINGS );
+
+    PRINTF(
+    "    TOGGLE_NETWORK      F8                # toggle the network on/off\n"
+    "    CHANGE_LAYOUT_PREV  Keypad_7,Ctrl-J   # switch to a previous skin layout\n"
+    "\n"
+    );
+}
+
+
+static void
+help_debug_tags(stralloc_t*  out)
+{
+    int  n;
+
+#define  _VERBOSE_TAG(x,y)   { #x, VERBOSE_##x, y },
+    static const struct { const char*  name; int  flag; const char*  text; }
+    verbose_options[] = {
+        VERBOSE_TAG_LIST
+        { 0, 0, 0 }
+    };
+#undef _VERBOSE_TAG
+
+    PRINTF(
+    "  the '-debug <tags>' option can be used to enable or disable debug\n"
+    "  messages from specific parts of the emulator. <tags> must be a list\n"
+    "  (separated by space/comma/column) of <component> names, which can be one of:\n\n"
+    );
+
+    for (n = 0; n < VERBOSE_MAX; n++)
+        PRINTF( "    %-12s    %s\n", verbose_options[n].name, verbose_options[n].text );
+    PRINTF( "    %-12s    %s\n", "all", "all components together\n" );
+
+    PRINTF(
+    "\n"
+    "  each <component> can be prefixed with a single '-' to indicate the disabling\n"
+    "  of its debug messages. for example:\n\n"
+
+    "    -debug all,-socket,-keys\n\n"
+
+    "  enables all debug messages, except the ones related to network sockets\n"
+    "  and key bindings/presses\n\n"
+    );
+}
+
+static void
+help_char_devices(stralloc_t*  out)
+{
+    PRINTF(
+    "  various emulation options take a <device> specification that can be used to\n"
+    "  specify something to hook to an emulated device or communication channel.\n"
+    "  here is the list of supported <device> specifications:\n\n"
+
+    "      stdio\n"
+    "          standard input/output. this may be subject to character\n"
+    "          translation (e.g. LN <=> CR/LF)\n\n"
+
+    "      COM<n>   [Windows only]\n"
+    "          where <n> is a digit. host serial communication port.\n\n"
+
+    "      pipe:<filename>\n"
+    "          named pipe <filename>\n\n"
+
+    "      file:<filename>\n"
+    "          write output to <filename>, no input can be read\n\n"
+
+    "      pty  [Linux only]\n"
+    "          pseudo TTY (a new PTY is automatically allocated)\n\n"
+
+    "      /dev/<file>  [Unix only]\n"
+    "          host char device file, e.g. /dev/ttyS0. may require root access\n\n"
+
+    "      /dev/parport<N>  [Linux only]\n"
+    "          use host parallel port. may require root access\n\n"
+
+    "      unix:<path>[,server][,nowait]]     [Unix only]\n"
+    "          use a Unix domain socket. if you use the 'server' option, then\n"
+    "          the emulator will create the socket and wait for a client to\n"
+    "          connect before continuing, unless you also use 'nowait'\n\n"
+
+    "      tcp:[<host>]:<port>[,server][,nowait][,nodelay]\n"
+    "          use a TCP socket. 'host' is set to localhost by default. if you\n"
+    "          use the 'server' option will bind the port and wait for a client\n"
+    "          to connect before continuing, unless you also use 'nowait'. the\n"
+    "          'nodelay' option disables the TCP Nagle algorithm\n\n"
+
+    "      telnet:[<host>]:<port>[,server][,nowait][,nodelay]\n"
+    "          similar to 'tcp:' but uses the telnet protocol instead of raw TCP\n\n"
+
+    "      udp:[<remote_host>]:<remote_port>[@[<src_ip>]:<src_port>]\n"
+    "          send output to a remote UDP server. if 'remote_host' is no\n"
+    "          specified it will default to '0.0.0.0'. you can also receive input\n"
+    "          through UDP by specifying a source address after the optional '@'.\n\n"
+
+    "      fdpair:<fd1>,<fd2>  [Unix only]\n"
+    "          redirection input and output to a pair of pre-opened file\n"
+    "          descriptors. this is mostly useful for scripts and other\n"
+    "          programmatic launches of the emulator.\n\n"
+
+    "      none\n"
+    "          no device connected\n\n"
+
+    "      null\n"
+    "          the null device (a.k.a /dev/null on Unix, or NUL on Win32)\n\n"
+
+    "  NOTE: these correspond to the <device> parameter of the QEMU -serial option\n"
+    "        as described on http://bellard.org/qemu/qemu-doc.html#SEC10\n\n"
+    );
+}
+
+static void
+help_avd(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-avd <name>' to start the emulator program with a given avd,\n"
+    "  where <name> must correspond to the name of one of the\n"
+    "  Android Virtual Devices available on your host.\n\n"
+
+    "  As a special convenience, using '@<name>' is equivalent to using\n"
+    "  '-avd <name>'.\n\n"
+
+    "  For more information about virtual devices, see -help-virtual-device.\n"
+    );
+}
+
+static void
+help_sysdir(stralloc_t*  out)
+{
+    char   systemdir[MAX_PATH];
+    char   *p = systemdir, *end = p + sizeof(systemdir);
+
+    p = bufprint_app_dir( p, end );
+    p = bufprint( p, end, PATH_SEP "lib" PATH_SEP "images" );
+
+    PRINTF(
+    "  use '-sysdir <dir>' to specify a directory where system read-only\n"
+    "  image files will be searched. on this system, the default directory is:\n\n"
+    "      %s\n\n", systemdir );
+
+    PRINTF(
+    "  see '-help-disk-images' for more information about disk image files\n\n" );
+}
+
+static void
+help_datadir(stralloc_t*  out)
+{
+    char  datadir[MAX_PATH];
+
+    bufprint_config_path(datadir, datadir + sizeof(datadir));
+
+    PRINTF(
+    "  use '-datadir <dir>' to specify a directory where writable image files\n"
+    "  will be searched. on this system, the default directory is:\n\n"
+    "      %s\n\n", datadir );
+
+    PRINTF(
+    "  see '-help-disk-images' for more information about disk image files\n\n" );
+}
+
+static void
+help_kernel(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-kernel <file>' to specify a Linux kernel image to be run.\n"
+    "  the default image is 'kernel-qemu' from the system directory.\n\n"
+
+    "  you can use '-debug-kernel' to see debug messages from the kernel\n"
+    "  to the terminal\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_ramdisk(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-ramdisk <file>' to specify a Linux ramdisk boot image to be run in\n"
+    "  the emulator. the default image is 'ramdisk.img' from the system directory.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_system(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-system <file>' to specify the intial system image that will be loaded.\n"
+    "  the default image is 'system.img' from the system directory.\n\n"
+
+    "  NOTE: In previous releases of the Android SDK, this option was named '-image'.\n"
+    "        And using '-system <path>' was equivalent to using '-sysdir <path>' now.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_image(stralloc_t*  out)
+{
+    PRINTF(
+    "  This option is obsolete, you should use '-system <file>' instead to point\n"
+    "  to the initial system image.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_init_data(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-init-data <file>' to specify an *init* /data partition file.\n"
+    "  it is only used when creating a new writable /data image file, or\n"
+    "  when you use '-wipe-data' to reset it. the default is 'userdata.img'\n"
+    "  from the system directory.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_data(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-data <file>' to specify a different /data partition image file.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_wipe_data(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-wipe-data' to reset your /data partition image to its factory\n"
+    "  defaults. this removes all installed applications and settings.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_cache(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-cache <file>' to specify a /cache partition image. if <file> does\n"
+    "  not exist, it will be created empty. by default, the cache partition is\n"
+    "  backed by a temporary file that is deleted when the emulator exits.\n"
+    "  using the -cache option allows it to be persistent.\n\n"
+
+    "  the '-no-cache' option can be used to disable the cache partition.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_no_cache(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-no-cache' to disable the cache partition in the emulated system.\n"
+    "  the cache partition is optional, but when available, is used by the browser\n"
+    "  to cache web pages and images\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_sdcard(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-sdcard <file>' to specify a SD Card image file that will be attached\n"
+    "  to the emulator. By default, the 'sdcard.img' file is searched in the data\n"
+    "  directory.\n\n"
+
+    "  if the file does not exist, the emulator will still start, but without an\n"
+    "  attached SD Card.\n\n"
+
+    "  see '-help-disk-images' for more information about disk image files\n\n"
+    );
+}
+
+static void
+help_skindir(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-skindir <dir>' to specify a directory that will be used to search\n"
+    "  for emulator skins. each skin must be a subdirectory of <dir>. by default\n"
+    "  the emulator will look in the 'skins' sub-directory of the system directory\n\n"
+
+    "  the '-skin <name>' option is required when -skindir is used.\n"
+    );
+}
+
+static void
+help_skin(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-skin <skin>' to specify an emulator skin, each skin corresponds to\n"
+    "  the visual appearance of a given device, including buttons and keyboards,\n"
+    "  and is stored as subdirectory <skin> of the skin root directory\n"
+    "  (see '-help-skindir')\n\n" );
+
+    PRINTF(
+    "  note that <skin> can also be '<width>x<height>' (e.g. '320x480') to\n"
+    "  specify an exact framebuffer size, without any visual ornaments.\n\n" );
+}
+
+/* default network settings for emulator */
+#define  DEFAULT_NETSPEED  "full"
+#define  DEFAULT_NETDELAY  "none"
+
+static void
+help_shaper(stralloc_t*  out)
+{
+    int  n;
+
+    PRINTF(
+    "  the Android emulator supports network throttling, i.e. slower network\n"
+    "  bandwidth as well as higher connection latencies. this is done either through\n"
+    "  skin configuration, or with '-netspeed <speed>' and '-netdelay <delay>'.\n\n"
+
+    "  the format of -netspeed is one of the following (numbers are kbits/s):\n\n" );
+
+    for (n = 0; android_netspeeds[n].name != NULL; n++) {
+        PRINTF( "    -netspeed %-12s %-15s  (up: %.1f, down: %.1f)\n",
+                        android_netspeeds[n].name,
+                        android_netspeeds[n].display,
+                        android_netspeeds[n].upload/1000.,
+                        android_netspeeds[n].download/1000. );
+    }
+    PRINTF( "\n" );
+    PRINTF( "    -netspeed %-12s %s", "<num>", "select both upload and download speed\n");
+    PRINTF( "    -netspeed %-12s %s", "<up>:<down>", "select individual up and down speed\n");
+
+    PRINTF( "\n  The format of -netdelay is one of the following (numbers are msec):\n\n" );
+    for (n = 0; android_netdelays[n].name != NULL; n++) {
+        PRINTF( "    -netdelay %-10s   %-15s  (min %d, max %d)\n",
+                        android_netdelays[n].name, android_netdelays[n].display,
+                        android_netdelays[n].min_ms, android_netdelays[n].max_ms );
+    }
+    PRINTF( "    -netdelay %-10s   %s", "<num>", "select exact latency\n");
+    PRINTF( "    -netdelay %-10s   %s", "<min>:<max>", "select min and max latencies\n\n");
+
+    PRINTF( "  the emulator uses the following defaults:\n\n" );
+    PRINTF( "    Default network speed   is '%s'\n",   DEFAULT_NETSPEED);
+    PRINTF( "    Default network latency is '%s'\n\n", DEFAULT_NETDELAY);
+}
+
+static void
+help_http_proxy(stralloc_t*  out)
+{
+    PRINTF(
+    "  the Android emulator allows you to redirect all TCP connections through\n"
+    "  a HTTP/HTTPS proxy. this can be enabled by using the '-http-proxy <proxy>'\n"
+    "  option, or by defining the 'http_proxy' environment variable.\n\n"
+
+    "  <proxy> can be one of the following:\n\n"
+    "    http://<server>:<port>\n"
+    "    http://<username>:<password>@<server>:<port>\n\n"
+
+    "  the 'http://' prefix can be omitted. If '-http-proxy <proxy>' is not used,\n"
+    "  the 'http_proxy' environment variable is looked up and any value matching\n"
+    "  the <proxy> format will be used automatically\n\n" );
+}
+
+static void
+help_report_console(stralloc_t*  out)
+{
+    PRINTF(
+    "  the '-report-console <socket>' option can be used to report the\n"
+    "  automatically-assigned console port number to a remote third-party\n"
+    "  before starting the emulation. <socket> must be in one of these\n"
+    "  formats:\n\n"
+
+    "      tcp:<port>[,server][,max=<seconds>]\n"
+    "      unix:<path>[,server][,max=<seconds>]\n"
+    "\n"
+    "  if the 'server' option is used, the emulator opens a server socket\n"
+    "  and waits for an incoming connection to it. by default, it will instead\n"
+    "  try to make a normal client connection to the socket, and, in case of\n"
+    "  failure, will repeat this operation every second for 10 seconds.\n"
+    "  the 'max=<seconds>' option can be used to modify the timeout\n\n"
+
+    "  when the connection is established, the emulator sends its console port\n"
+    "  number as text to the remote third-party, then closes the connection and\n"
+    "  starts the emulation as usual. *any* failure in the process described here\n"
+    "  will result in the emulator aborting immediately\n\n"
+
+    "  as an example, here's a small Unix shell script that starts the emulator in\n"
+    "  the background and waits for its port number with the help of the 'netcat'\n"
+    "  utility:\n\n"
+
+    "      MYPORT=5000\n"
+    "      emulator -no-window -report-console tcp:$MYPORT &\n"
+    "      CONSOLEPORT=`nc -l localhost $MYPORT`\n"
+    "\n"
+    );
+}
+
+static void
+help_dpi_device(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-dpi-device <dpi>' to specify the screen resolution of the emulated\n"
+    "  device. <dpi> must be an integer between 72 and 1000. the default is taken\n"
+    "  from the skin, if available, or uses the contant value %d (an average of\n"
+    "  several prototypes used during Android development).\n\n", DEFAULT_DEVICE_DPI );
+
+    PRINTF(
+    "  the device resolution can also used to rescale the emulator window with\n"
+    "  the '-scale' option (see -help-scale)\n\n"
+    );
+}
+
+static void
+help_audio(stralloc_t*  out)
+{
+    PRINTF(
+    "  the '-audio <backend>' option allows you to select a specific backend\n"
+    "  to be used to both play and record audio in the Android emulator.\n\n"
+
+    "  this is equivalent to calling both '-audio-in <backend>' and\n"
+    "  '-audio-out <backend>' at the same time.\n\n"
+
+    "  use '-help-audio-out' to see a list of valid output <backend> values.\n"
+    "  use '-help-audio-in'  to see a list of valid input <backend> values.\n"
+    "  use '-audio none' to disable audio completely.\n\n"
+    );
+}
+
+static void
+help_audio_out(stralloc_t*  out)
+{
+    int  nn;
+
+    PRINTF(
+        "  the  '-audio-out <backend>' option allows you to select a specific\n"
+        "  backend to play audio in the Android emulator. this is mostly useful\n"
+        "  on Linux\n\n"
+
+        "  on this system, output <backend> can be one of the following:\n\n"
+    );
+    for ( nn = 0; ; nn++ ) {
+        const char*  descr;
+        const char*  name = audio_get_backend_name( 0, nn, &descr );
+        if (name == NULL)
+            break;
+        PRINTF( "    %-10s %s\n", name, descr );
+    }
+    PRINTF( "\n" );
+}
+
+static void
+help_audio_in(stralloc_t*  out)
+{
+    int  nn;
+
+    PRINTF(
+        "  the  '-audio-in <backend>' option allows you to select a specific\n"
+        "  backend to play audio in the Android emulator. this is mostly useful\n"
+        "  on Linux\n\n"
+
+        "  IMPORTANT NOTE:\n"
+        "     on some Linux systems, broken Esd/ALSA/driver implementations will\n"
+        "     make your emulator freeze and become totally unresponsive when\n"
+        "     using audio recording. the only way to avoid this is to use\n"
+        "     '-audio-in none' to disable it\n\n"
+
+        "  on this system, input <backend> can be one of:\n\n"
+    );
+    for ( nn = 0; ; nn++ ) {
+        const char*  descr;
+        const char*  name = audio_get_backend_name( 1, nn, &descr );
+        if (name == NULL)
+            break;
+        PRINTF( "    %-10s %s\n", name, descr );
+    }
+    PRINTF( "\n" );
+}
+
+
+static void
+help_scale(stralloc_t*  out)
+{
+    PRINTF(
+    "  the '-scale <scale>' option is used to scale the emulator window to\n"
+    "  something that better fits the physical dimensions of a real device. this\n"
+    "  can be *very* useful to check that your UI isn't too small to be usable\n"
+    "  on a real device.\n\n"
+
+    "  there are three supported formats for <scale>:\n\n"
+
+    "  * if <scale> is a real number (between 0.1 and 3.0) it is used as a\n"
+    "    scaling factor for the emulator's window.\n\n"
+
+    "  * if <scale> is an integer followed by the suffix 'dpi' (e.g. '110dpi'),\n"
+    "    then it is interpreted as the resolution of your monitor screen. this\n"
+    "    will be divided by the emulated device's resolution to get an absolute\n"
+    "    scale. (see -help-dpi-device for details).\n\n"
+
+    "  * finally, if <scale> is the keyword 'auto', the emulator tries to guess\n"
+    "    your monitor's resolution and automatically adjusts its window\n"
+    "    accordingly\n\n"
+
+    "    NOTE: this process is *very* unreliable, depending on your OS, video\n"
+    "          driver issues and other random system parameters\n\n"
+
+    "  the emulator's scale can be changed anytime at runtime through the control\n"
+    "  console. see the help for the 'window scale' command for details\n\n" );
+}
+
+static void
+help_trace(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-trace <name>' to start the emulator with runtime code profiling support\n"
+    "  profiling itself will not be enabled unless you press F9 to activate it, or\n"
+    "  the executed code turns it on programmatically.\n\n"
+
+    "  trace information is stored in directory <name>, several files are created\n"
+    "  there, that can later be used with the 'traceview' program that comes with\n"
+    "  the Android SDK for analysis.\n\n"
+
+    "  note that execution will be slightly slower when enabling code profiling,\n"
+    "  this is a necessary requirement of the operations being performed to record\n"
+    "  the execution trace. this slowdown should not affect your system until you\n"
+    "  enable the profiling though...\n\n"
+    );
+}
+
+static void
+help_show_kernel(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-show-kernel' to redirect debug messages from the kernel to the current\n"
+    "  terminal. this is useful to check that the boot process works correctly.\n\n"
+    );
+}
+
+static void
+help_shell(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-shell' to create a root shell console on the current terminal.\n"
+    "  this is unlike the 'adb shell' command for the following reasons:\n\n"
+
+    "  * this is a *root* shell that allows you to modify many parts of the system\n"
+    "  * this works even if the ADB daemon in the emulated system is broken\n"
+    "  * pressing Ctrl-C will stop the emulator, instead of the shell.\n\n"
+    "  See also '-shell-serial'.\n\n" );
+}
+
+static void
+help_shell_serial(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-shell-serial <device>' instead of '-shell' to open a root shell\n"
+    "  to the emulated system, while specifying an external communication\n"
+    "  channel / host device.\n\n"
+
+    "  '-shell-serial stdio' is identical to '-shell', while you can use\n"
+    "  '-shell-serial tcp::4444,server,nowait' to talk to the shell over local\n"
+    "  TCP port 4444.  '-shell-serial fdpair:3:6' would let a parent process\n"
+    "  talk to the shell using fds 3 and 6.\n\n"
+
+    "  see -help-char-devices for a list of available <device> specifications.\n\n"
+    "  NOTE: you can have only one shell per emulator instance at the moment\n\n"
+    );
+}
+
+static void
+help_logcat(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-logcat <tags>' to redirect log messages from the emulated system to\n"
+    "  the current terminal. <tags> is a list of space/comma-separated log filters\n"
+    "  where each filter has the following format:\n\n"
+
+    "     <componentName>:<logLevel>\n\n"
+
+    "  where <componentName> is either '*' or the name of a given component,\n"
+    "  and <logLevel> is one of the following letters:\n\n"
+
+    "      v          verbose level\n"
+    "      d          debug level\n"
+    "      i          informative log level\n"
+    "      w          warning log level\n"
+    "      e          error log level\n"
+    "      s          silent log level\n\n"
+
+    "  for example, the following only displays messages from the 'GSM' component\n"
+    "  that are at least at the informative level:\n\n"
+
+    "    -logcat '*:s GSM:i'\n\n"
+
+    "  if '-logcat <tags>' is not used, the emulator looks for ANDROID_LOG_TAGS\n"
+    "  in the environment. if it is defined, its value must match the <tags>\n"
+    "  format and will be used to redirect log messages to the terminal.\n\n"
+
+    "  note that this doesn't prevent you from redirecting the same, or other,\n"
+    "  log messages through the ADB or DDMS tools too.\n\n");
+}
+
+static void
+help_no_audio(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-no-audio' to disable all audio support in the emulator. this may be\n"
+    "  unfortunately be necessary in some cases:\n\n"
+
+    "  * at least two users have reported that their Windows machine rebooted\n"
+    "    instantly unless they used this option when starting the emulator.\n"
+    "    it is very likely that the problem comes from buggy audio drivers.\n\n"
+
+    "  * on some Linux machines, the emulator might get stuck at startup with\n"
+    "    audio support enabled. this problem is hard to reproduce, but seems to\n"
+    "    be related too to flaky ALSA / audio driver support.\n\n"
+
+    "  on Linux, another option is to try to change the default audio backend\n"
+    "  used by the emulator. you can do that by setting the QEMU_AUDIO_DRV\n"
+    "  environment variables to one of the following values:\n\n"
+
+    "    alsa        (use the ALSA backend)\n"
+    "    esd         (use the EsounD backend)\n"
+    "    sdl         (use the SDL audio backend, no audio input supported)\n"
+    "    oss         (use the OSS backend)\n"
+    "    none        (do not support audio)\n"
+    "\n"
+    "  the very brave can also try to use distinct backends for audio input\n"
+    "  and audio outputs, this is possible by selecting one of the above values\n"
+    "  into the QEMU_AUDIO_OUT_DRV and QEMU_AUDIO_IN_DRV environment variables.\n\n"
+    );
+}
+
+static void
+help_raw_keys(stralloc_t*  out)
+{
+    PRINTF(
+    "  this option is deprecated because one can do the same using Ctrl-K\n"
+    "  at runtime (this keypress toggles between unicode/raw keyboard modes)\n\n"
+
+    "  by default, the emulator tries to reverse-map the characters you type on\n"
+    "  your keyboard to device-specific key presses whenever possible. this is\n"
+    "  done to make the emulator usable with a non-QWERTY keyboard.\n\n"
+
+    "  however, this also means that single keypresses like Shift or Alt are not\n"
+    "  passed to the emulated device. the '-raw-keys' option disables the reverse\n"
+    "  mapping. it should only be used when using a QWERTY keyboard on your machine\n"
+
+    "  (should only be useful to Android system hackers, e.g. when implementing a\n"
+    "  new input method).\n\n"
+    );
+}
+
+static void
+help_radio(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-radio <device>' to redirect the GSM modem emulation to an external\n"
+    "  character device or program. this bypasses the emulator's internal modem\n"
+    "  and should only be used for testing.\n\n"
+
+    "  see '-help-char-devices' for the format of <device>\n\n"
+
+    "  the data exchanged with the external device/program are GSM AT commands\n\n"
+
+    "  note that, when running in the emulator, the Android GSM stack only supports\n"
+    "  a *very* basic subset of the GSM protocol. trying to link the emulator to\n"
+    "  a real GSM modem is very likely to not work properly.\n\n"
+    );
+}
+
+
+static void
+help_port(stralloc_t*  out)
+{
+    PRINTF(
+    "  at startup, the emulator tries to bind its control console at a free port\n"
+    "  starting from 5554, in increments of two (i.e. 5554, then 5556, 5558, etc..)\n"
+    "  this allows several emulator instances to run concurrently on the same\n"
+    "  machine, each one using a different console port number.\n\n"
+
+    "  use '-port <port>' to force an emulator instance to use a given console port\n\n"
+
+    "  note that <port> must be an *even* integer between 5554 and 5584 included.\n"
+    "  <port>+1 must also be free and will be reserved for ADB. if any of these\n"
+    "  ports is already used, the emulator will fail to start.\n\n" );
+}
+
+static void
+help_ports(stralloc_t*  out)
+{
+    PRINTF(
+    "  the '-ports <consoleport>,<adbport>' option allows you to explicitely set\n"
+    "  the TCP ports used by the emulator to implement its control console and\n"
+    "  communicate with the ADB tool.\n\n"
+
+    "  This is a very special option that should probably *not* be used by typical\n"
+    "  developers using the Android SDK (use '-port <port>' instead), because the\n"
+    "  corresponding instance is probably not going to be seen from adb/DDMS. Its\n"
+    "  purpose is to use the emulator in very specific network configurations.\n\n"
+
+    "    <consoleport> is the TCP port used to bind the control console\n"
+    "    <adbport> is the TCP port used to bind the ADB local transport/tunnel.\n\n"
+
+    "  If both ports aren't available on startup, the emulator will exit.\n\n");
+}
+
+
+static void
+help_onion(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-onion <file>' to specify a PNG image file that will be displayed on\n"
+    "  top of the emulated framebuffer with translucency. this can be useful to\n"
+    "  check that UI elements are correctly positioned with regards to a reference\n"
+    "  graphics specification.\n\n"
+
+    "  the default translucency is 50%%, but you can use '-onion-alpha <%%age>' to\n"
+    "  select a different one, or even use keypresses at runtime to alter it\n"
+    "  (see -help-keys for details)\n\n"
+
+    "  finally, the onion image can be rotated (see -help-onion-rotate)\n\n"
+    );
+}
+
+static void
+help_onion_alpha(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-onion-alpha <percent>' to change the translucency level of the onion\n"
+    "  image that is going to be displayed on top of the framebuffer (see also\n"
+    "  -help-onion). the default is 50%%.\n\n"
+
+    "  <percent> must be an integer between 0 and 100.\n\n"
+
+    "  you can also change the translucency dynamically (see -help-keys)\n\n"
+    );
+}
+
+static void
+help_onion_rotation(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-onion-rotation <rotation>' to change the rotation of the onion\n"
+    "  image loaded through '-onion <file>'. valid values for <rotation> are:\n\n"
+
+    "   0        no rotation\n"
+    "   1        90  degrees clockwise\n"
+    "   2        180 degrees\n"
+    "   3        270 degrees clockwise\n\n"
+    );
+}
+
+
+static void
+help_timezone(stralloc_t*  out)
+{
+    PRINTF(
+    "  by default, the emulator tries to detect your current timezone to report\n"
+    "  it to the emulated system. use the '-timezone <timezone>' option to choose\n"
+    "  a different timezone, or if the automatic detection doesn't work correctly.\n\n"
+
+    "  VERY IMPORTANT NOTE:\n\n"
+    "  the <timezone> value must be in zoneinfo format, i.e. it should look like\n"
+    "  Area/Location or even Area/SubArea/Location. valid examples are:\n\n"
+
+    "    America/Los_Angeles\n"
+    "    Europe/Paris\n\n"
+
+    "  using a human-friendly abbreviation like 'PST' or 'CET' will not work, as\n"
+    "  well as using values that are not defined by the zoneinfo database.\n\n"
+
+    "  NOTE: unfortunately, this will not work on M5 and older SDK releases\n\n"
+    );
+}
+
+
+static void
+help_dns_server(stralloc_t*  out)
+{
+    PRINTF(
+    "  by default, the emulator tries to detect the DNS servers you're using and\n"
+    "  will setup special aliases in the emulated firewall network to allow the\n"
+    "  Android system to connect directly to them. use '-dns-server <servers>' to\n"
+    "  select a different list of DNS servers to be used.\n\n"
+
+    "  <servers> must be a comma-separated list of up to 4 DNS server names or\n"
+    "  IP addresses.\n\n"
+
+    "  NOTE: on M5 and older SDK releases, only the first server in the list will\n"
+    "        be used.\n\n"
+    );
+}
+
+
+static void
+help_cpu_delay(stralloc_t*  out)
+{
+    PRINTF(
+    "  this option is purely experimental, probably doesn't work as you would\n"
+    "  expect, and may even disappear in a later emulator release.\n\n"
+
+    "  use '-cpu-delay <delay>' to throttle CPU emulation. this may be useful\n"
+    "  to detect weird race conditions that only happen on 'lower' CPUs. note\n"
+    "  that <delay> is a unit-less integer that doesn't even scale linearly\n"
+    "  to observable slowdowns. use trial and error to find something that\n"
+    "  suits you, the 'correct' machine is very probably dependent on your\n"
+    "  host CPU and memory anyway...\n\n"
+    );
+}
+
+
+static void
+help_no_boot_anim(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-no-boot-anim' to disable the boot animation (red bouncing ball) when\n"
+    "  starting the emulator. on slow machines, this can surprisingly speed up the\n"
+    "  boot sequence in tremendous ways.\n\n"
+
+    "  NOTE: unfortunately, this will not work on M5 and older SDK releases\n\n"
+    );
+}
+
+
+static void
+help_gps(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-gps <device>' to emulate an NMEA-compatible GPS unit connected to\n"
+    "  an external character device or socket. the format of <device> is the same\n"
+    "  than the one used for '-radio <device>' (see -help-char-devices for details)\n\n"
+    );
+}
+
+
+static void
+help_keyset(stralloc_t*  out)
+{
+    char  temp[256];
+
+    PRINTF(
+    "  use '-keyset <name>' to specify a different keyset file name to use when\n"
+    "  starting the emulator. a keyset file contains a list of key bindings used\n"
+    "  to control the emulator with the host keyboard.\n\n"
+
+    "  by default, the emulator looks for the following file:\n\n"
+    );
+
+    bufprint_config_file(temp, temp+sizeof(temp), KEYSET_FILE);
+    PRINTF(
+    "    %s\n\n", temp );
+
+    bufprint_config_path(temp, temp+sizeof(temp));
+    PRINTF(
+    "  however, if -keyset is used, then the emulator does the following:\n\n"
+
+    "  - first, if <name> doesn't have an extension, then the '.keyset' suffix\n"
+    "    is appended to it (e.g. \"foo\" => \"foo.keyset\"),\n\n"
+
+    "  - then, the emulator searches for a file named <name> in the following\n"
+    "    directories:\n\n"
+
+    "     * the emulator configuration directory: %s\n"
+    "     * the 'keysets' subdirectory of <systemdir>, if any\n"
+    "     * the 'keysets' subdirectory of the program location, if any\n\n",
+    temp );
+
+    PRINTF(
+    "  if no corresponding file is found, a default set of key bindings is used.\n\n"
+    "  use '-help-keys' to list the default key bindings.\n"
+    "  use '-help-keyset-file' to learn more about the format of keyset files.\n"
+    "\n"
+    );
+}
+
+static void
+help_old_system(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-old-system' if you want to use a recent emulator binary to run\n"
+    "  an old version of the Android SDK system images. Here, 'old' means anything\n"
+    "  older than version 1.4 of the emulator.\n\n"
+
+    "  NOTE: using '-old-system' with recent system images is likely to not work\n"
+    "        properly, though you may not notice it immediately (e.g. failure to\n"
+    "        start the emulated GPS hardware)\n\n"
+    );
+}
+
+#ifdef CONFIG_NAND_LIMITS
+static void
+help_nand_limits(stralloc_t*  out)
+{
+    PRINTF(
+    "  use '-nand-limits <limits>' to enable a debugging feature that sends a\n"
+    "  signal to an external process once a read and/or write limit is achieved\n"
+    "  in the emulated system. the format of <limits> is the following:\n\n"
+
+    "     pid=<number>,signal=<number>,[reads=<threshold>][,writes=<threshold>]\n\n"
+
+    "  where 'pid' is the target process identifier, 'signal' the number of the\n"
+    "  target signal. the read and/or write threshold'reads' are a number optionally\n"
+    "  followed by a K, M or G suffix, corresponding to the number of bytes to be\n"
+    "  read or written before the signal is sent.\n\n"
+    );
+}
+#endif /* CONFIG_NAND_LIMITS */
+
+static void
+help_bootchart(stralloc_t  *out)
+{
+    PRINTF(
+    "  some Android system images have a modified 'init' system that  integrates\n"
+    "  a bootcharting facility (see http://www.bootchart.org/). You can pass a\n"
+    "  bootcharting period to the system with the following:\n\n"
+
+    "    -bootchart <timeout>\n\n"
+
+    "  where 'timeout' is a period expressed in seconds. Note that this won't do\n"
+    "  anything if your init doesn't have bootcharting activated.\n\n"
+    );
+}
+
+static void
+help_tcpdump(stralloc_t  *out)
+{
+    PRINTF(
+    "  use the -tcpdump <file> option to start capturing all network packets\n"
+    "  that are sent through the emulator's virtual Ethernet LAN. You can later\n"
+    "  use tools like WireShark to analyze the traffic and understand what\n"
+    "  really happens.\n\n"
+
+    "  note that this captures all Ethernet packets, and is not limited to TCP\n"
+    "  connections.\n\n"
+
+    "  you can also start/stop the packet capture dynamically through the console;\n"
+    "  see the 'network capture start' and 'network capture stop' commands for\n"
+    "  details.\n\n"
+    );
+}
+
+#define  help_no_skin   NULL
+#define  help_netspeed  help_shaper
+#define  help_netdelay  help_shaper
+#define  help_netfast   help_shaper
+
+#define  help_noaudio      NULL
+#define  help_noskin       NULL
+#define  help_nocache      NULL
+#define  help_no_jni       NULL
+#define  help_nojni        NULL
+#define  help_initdata     NULL
+#define  help_no_window    NULL
+#define  help_version      NULL
+#define  help_memory       NULL
+
+typedef struct {
+    const char*  name;
+    const char*  template;
+    const char*  descr;
+    void (*func)(stralloc_t*);
+} OptionHelp;
+
+static const OptionHelp    option_help[] = {
+#define  OPT_FLAG(_name,_descr)             { STRINGIFY(_name), NULL, _descr, help_##_name },
+#define  OPT_PARAM(_name,_template,_descr)  { STRINGIFY(_name), _template, _descr, help_##_name },
+#include "android/cmdline-options.h"
+    { NULL, NULL, NULL, NULL }
+};
+
+typedef struct {
+    const char*  name;
+    const char*  desc;
+    void (*func)(stralloc_t*);
+} TopicHelp;
+
+
+static const TopicHelp    topic_help[] = {
+    { "disk-images",  "about disk images",      help_disk_images },
+    { "keys",         "supported key bindings", help_keys },
+    { "debug-tags",   "debug tags for -debug <tags>", help_debug_tags },
+    { "char-devices", "character <device> specification", help_char_devices },
+    { "environment",  "environment variables",  help_environment },
+    { "keyset-file",  "key bindings configuration file", help_keyset_file },
+    { "virtual-device", "virtual device management", help_virtual_device },
+    { "sdk-images",   "about disk images when using the SDK", help_sdk_images },
+    { "build-images", "about disk images when building Android", help_build_images },
+    { NULL, NULL, NULL }
+};
+
+int
+android_help_for_option( const char*  option, stralloc_t*  out )
+{
+    OptionHelp const*  oo;
+    char               temp[32];
+
+    /* the names in the option_help table use underscore instead
+     * of dashes, so create a tranlated copy of the option name
+     * before scanning the table for matches
+     */
+    buffer_translate_char( temp, sizeof temp, option, '-', '_' );
+
+    for ( oo = option_help; oo->name != NULL; oo++ ) {
+        if ( !strcmp(oo->name, temp) ) {
+            if (oo->func)
+                oo->func(out);
+            else
+                stralloc_add_str(out, oo->descr);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+
+int
+android_help_for_topic( const char*  topic, stralloc_t*  out )
+{
+    const TopicHelp*  tt;
+
+    for ( tt = topic_help; tt->name != NULL; tt++ ) {
+        if ( !strcmp(tt->name, topic) ) {
+            tt->func(out);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+
+extern void  
+android_help_list_options( stralloc_t*  out )
+{
+    const OptionHelp*  oo;
+    const TopicHelp*   tt;
+    int                maxwidth = 0;
+
+    for ( oo = option_help; oo->name != NULL; oo++ ) {
+        int  width = strlen(oo->name);
+        if (oo->template != NULL)
+            width += strlen(oo->template);
+        if (width > maxwidth)
+            maxwidth = width;
+    }
+
+    for (oo = option_help; oo->name != NULL; oo++) {
+        char  temp[32];
+        /* the names in the option_help table use underscores instead
+         * of dashes, so create a translated copy of the option's name
+         */
+        buffer_translate_char(temp, sizeof temp, oo->name, '_', '-');
+
+        stralloc_add_format( out, "    -%s %-*s %s\n",
+            temp,
+            (int)(maxwidth - strlen(oo->name)),
+            oo->template ? oo->template : "",
+            oo->descr );
+    }
+
+    PRINTF( "\n" );
+    PRINTF( "     %-*s  %s\n", maxwidth, "-qemu args...",    "pass arguments to qemu");
+    PRINTF( "     %-*s  %s\n", maxwidth, "-qemu -h", "display qemu help");
+    PRINTF( "\n" );
+    PRINTF( "     %-*s  %s\n", maxwidth, "-verbose",       "same as '-debug-init'");
+    PRINTF( "     %-*s  %s\n", maxwidth, "-debug <tags>",  "enable/disable debug messages");
+    PRINTF( "     %-*s  %s\n", maxwidth, "-debug-<tag>",   "enable specific debug messages");
+    PRINTF( "     %-*s  %s\n", maxwidth, "-debug-no-<tag>","disable specific debug messages");
+    PRINTF( "\n" );
+    PRINTF( "     %-*s  %s\n", maxwidth, "-help",    "print this help");
+    PRINTF( "     %-*s  %s\n", maxwidth, "-help-<option>", "print option-specific help");
+    PRINTF( "\n" );
+
+    for (tt = topic_help; tt->name != NULL; tt += 1) {
+        char    help[32];
+        snprintf(help, sizeof(help), "-help-%s", tt->name);
+        PRINTF( "     %-*s  %s\n", maxwidth, help, tt->desc );
+    }
+    PRINTF( "     %-*s  %s\n", maxwidth, "-help-all", "prints all help content");
+    PRINTF( "\n");
+}
+
+
+void
+android_help_main( stralloc_t*  out )
+{
+    stralloc_add_str(out, "Android Emulator usage: emulator [options] [-qemu args]\n");
+    stralloc_add_str(out, "  options:\n" );
+
+    android_help_list_options(out);
+
+    /*printf( "%.*s", out->n, out->s );*/
+}
+
+
+void
+android_help_all( stralloc_t*  out )
+{
+    const OptionHelp*  oo;
+    const TopicHelp*   tt;
+
+    for (oo = option_help; oo->name != NULL; oo++) {
+        PRINTF( "========= help for option -%s:\n\n", oo->name );
+        android_help_for_option( oo->name, out );
+    }
+
+    for (tt = topic_help; tt->name != NULL; tt++) {
+        PRINTF( "========= help for -help-%s\n\n", tt->name );
+        android_help_for_topic( tt->name, out );
+    }
+    PRINTF( "========= top-level help\n\n" );
+    android_help_main(out);
+}
diff --git a/android/help.h b/android/help.h
new file mode 100644
index 0000000..94feca1
--- /dev/null
+++ b/android/help.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_HELP_H
+#define _ANDROID_HELP_H
+
+#include "android/utils/stralloc.h"
+
+/* appends the list of options with a small description to a dynamic string */
+extern void  android_help_list_options( stralloc_t*  out );
+
+/* output main help screen into a single dynamic string */
+extern void  android_help_main( stralloc_t*  out );
+
+/* output all help into a single dynamic string */
+extern void  android_help_all( stralloc_t*  out );
+
+/* appends the help for a given command-line option into a dynamic string
+ * returns 0 on success, or -1 on error (i.e. unknown option)
+ */
+extern int  android_help_for_option( const char*  option, stralloc_t*  out );
+
+/* appends the help for a given help topic into a dynamic string
+ * returns 0 on success, or -1 on error (i.e. unknown topic)
+ */
+extern int  android_help_for_topic( const char*  topic, stralloc_t*  out );
+
+#endif /* _ANDROID_HELP_H */
diff --git a/android/hw-control.c b/android/hw-control.c
new file mode 100644
index 0000000..681e85b
--- /dev/null
+++ b/android/hw-control.c
@@ -0,0 +1,204 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+/* this file implements the support of the new 'hardware control'
+ * qemud communication channel, which is used by libs/hardware on
+ * the system image to communicate with the emulator program for
+ * emulating the following:
+ *
+ *   - power management
+ *   - led(s) brightness
+ *   - vibrator
+ *   - flashlight
+ */
+#include "android/hw-control.h"
+#include "cbuffer.h"
+#include "android/qemud.h"
+#include "android/utils/misc.h"
+#include "android/utils/debug.h"
+#include "qemu-char.h"
+#include <stdio.h>
+#include <string.h>
+
+#define  D(...)  VERBOSE_PRINT(hw_control,__VA_ARGS__)
+
+/* define T_ACTIVE to 1 to debug transport communications */
+#define  T_ACTIVE  0
+
+#if T_ACTIVE
+#define  T(...)  VERBOSE_PRINT(hw_control,__VA_ARGS__)
+#else
+#define  T(...)   ((void)0)
+#endif
+
+static void*                  hw_control_client;
+static AndroidHwControlFuncs  hw_control_funcs;
+
+#define  BUFFER_SIZE  512
+
+typedef struct {
+    CharDriverState*  cs;
+    int               overflow;
+    int               wanted;
+    CBuffer           input[1];
+    char              input_0[ BUFFER_SIZE ];
+    /* note: 1 more byte to zero-terminate the query */
+    char              query[ BUFFER_SIZE+1 ];
+} HwControl;
+
+/* forward */
+static void  hw_control_do_query( HwControl*  h,
+                                  uint8_t*    query,
+                                  int         querylen );
+
+static void
+hw_control_init( HwControl*  h, CharDriverState*  cs )
+{
+    h->cs       = cs;
+    h->overflow = 0;
+    h->wanted   = 0;
+    cbuffer_reset( h->input,  h->input_0,  sizeof h->input_0 );
+}
+
+static int
+hw_control_can_read( void*  _hw )
+{
+    HwControl*  h = _hw;
+    return cbuffer_write_avail( h->input );
+}
+
+static void
+hw_control_read( void*  _hw, const uint8_t*  data, int  len )
+{
+    HwControl*  h     = _hw;
+    CBuffer*    input = h->input;
+
+    T("%s: %4d '%.*s'", __FUNCTION__, len, len, data);
+
+    cbuffer_write( input, data, len );
+
+    while ( input->count > 0 )
+    {
+        /* skip over unwanted data, if any */
+        while (h->overflow > 0) {
+            uint8_t*  dummy;
+            int       avail = cbuffer_read_peek( input, &dummy );
+
+            if (avail == 0)
+                return;
+
+            if (avail > h->overflow)
+                avail = h->overflow;
+
+            cbuffer_read_step( input, avail );
+            h->overflow -= avail;
+        }
+
+        /* all incoming messages are made of a 4-byte hexchar sequence giving */
+        /* the length of the following payload                                */
+        if (h->wanted == 0)
+        {
+            char  header[4];
+            int   len;
+
+            if (input->count < 4)
+                return;
+
+            cbuffer_read( input, header, 4 );
+            len = hex2int( (uint8_t*)header, 4 );
+            if (len >= 0) {
+                /* if the message is too long, skip it */
+                if (len > input->size) {
+                    T("%s: skipping oversized message (%d > %d)",
+                    __FUNCTION__, len, input->size);
+                    h->overflow = len;
+                } else {
+                    T("%s: waiting for %d bytes", __FUNCTION__, len);
+                    h->wanted = len;
+                }
+            }
+        }
+        else
+        {
+            if (input->count < h->wanted)
+                break;
+
+            cbuffer_read( input, h->query, h->wanted );
+            h->query[h->wanted] = 0;
+            hw_control_do_query( h, (uint8_t*)h->query, h->wanted );
+            h->wanted = 0;
+        }
+    }
+}
+
+
+static uint8_t*
+if_starts_with( uint8_t*  buf, int buflen, const char*  prefix )
+{
+    int  prefixlen = strlen(prefix);
+
+    if (buflen < prefixlen || memcmp(buf, prefix, prefixlen))
+        return NULL;
+
+    return (uint8_t*)buf + prefixlen;
+}
+
+
+static void
+hw_control_do_query( HwControl*  h,
+                     uint8_t*    query,
+                     int         querylen )
+{
+    uint8_t*   q;
+
+    D("%s: query %4d '%.*s'", __FUNCTION__, querylen, querylen, query );
+
+    q = if_starts_with( query, querylen, "power:light:brightness:" );
+    if (q != NULL) {
+        if (hw_control_funcs.light_brightness) {
+            char*  qq = strchr((const char*)q, ':');
+            int    value;
+            if (qq == NULL) {
+                D("%s: badly formatted", __FUNCTION__ );
+                return;
+            }
+            *qq++ = 0;
+            value = atoi(qq);
+            hw_control_funcs.light_brightness( hw_control_client, (char*)q, value );
+        }
+        return;
+    }
+}
+
+
+void
+android_hw_control_init( void*  opaque, const AndroidHwControlFuncs*  funcs )
+{
+    static CharDriverState*   hw_control_cs;
+    static HwControl          hwstate[1];
+
+    if (hw_control_cs == NULL) {
+        CharDriverState*  cs;
+        if ( android_qemud_get_channel( ANDROID_QEMUD_CONTROL, &cs ) < 0 ) {
+            derror( "could not create hardware control charpipe" );
+            exit(1);
+        }
+
+        hw_control_cs = cs;
+        hw_control_init( hwstate, cs );
+        qemu_chr_add_handlers( cs, hw_control_can_read, hw_control_read, NULL, hwstate );
+
+        D("%s: hw-control char pipe initialized", __FUNCTION__);
+    }
+    hw_control_client = opaque;
+    hw_control_funcs  = funcs[0];
+}
diff --git a/android/hw-control.h b/android/hw-control.h
new file mode 100644
index 0000000..6e9874e
--- /dev/null
+++ b/android/hw-control.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_hw_control_h
+#define _android_hw_control_h
+
+#include "qemu-common.h"
+
+/* a callback function called when the system wants to change the brightness
+ * of a given light. 'light' is a string which can be one of:
+ * 'lcd_backlight', 'button_backlight' or 'Keyboard_backlight'
+ *
+ * brightness is an integer (acceptable range are 0..255), however the
+ * default is around 105, and we probably don't want to dim the emulator's
+ * output at that level.
+ */
+typedef void  (*AndroidHwLightBrightnessFunc)( void*       opaque,
+                                               const char* light,
+                                               int         brightness );
+
+/* used to record a hw control 'client' */
+typedef struct {
+    AndroidHwLightBrightnessFunc  light_brightness;
+} AndroidHwControlFuncs;
+
+/* used to initialize the hardware control support */
+extern void  android_hw_control_init( void*                         opaque,
+                                      const AndroidHwControlFuncs*  funcs );
+
+#endif /* _android_hw_control_h */
diff --git a/android/hw-events.c b/android/hw-events.c
new file mode 100644
index 0000000..e72440c
--- /dev/null
+++ b/android/hw-events.c
@@ -0,0 +1,223 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/hw-events.h"
+#include "android/utils/bufprint.h"
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+    const char*  name;
+    int          value;
+} PairRec;
+
+#define  EV_TYPE(n,v)   { STRINGIFY(n), (v) },
+#define  BTN_CODE(n,v)  { STRINGIFY(n), (v) },
+#define  REL_CODE(n,v)  { STRINGIFY(n), (v) },
+#define  ABS_CODE(n,v)  { STRINGIFY(n), (v) },
+
+static const PairRec  _ev_types_tab[] =
+{
+    EVENT_TYPE_LIST
+    { NULL, 0 }
+};
+
+static const PairRec  _btn_codes_list[] =
+{
+    EVENT_BTN_LIST
+    { NULL, 0 }
+};
+
+static const PairRec  _rel_codes_list[] =
+{
+    EVENT_REL_LIST
+    { NULL, 0 }
+};
+
+static const PairRec  _abs_codes_list[] =
+{
+    EVENT_ABS_LIST
+    { NULL, 0 }
+};
+
+#undef EV_TYPE
+#undef BTN_CODE
+#undef REL_CODE
+#undef ABS_CODE
+
+static int
+count_list( const PairRec*  list )
+{
+    int  nn = 0;
+    while (list[nn].name != NULL)
+        nn += 1;
+
+    return nn;
+}
+
+static int
+scan_list( const PairRec*  list,
+           const char*     prefix,
+           const char*     name,
+           int             namelen )
+{
+    int   len;
+
+    if (namelen <= 0)
+        return -1;
+
+    len = strlen(prefix);
+    if (namelen <= len)
+        return -1;
+    if ( memcmp( name, prefix, len ) != 0 )
+        return -1;
+
+    name    += len;
+    namelen -= len;
+
+    for ( ; list->name != NULL; list += 1 )
+    {
+        if ( memcmp( list->name, name, namelen ) == 0 && list->name[namelen] == 0 )
+            return list->value;
+    }
+    return -1;
+}
+
+
+typedef struct {
+    int             type;
+    const char*     prefix;
+    const PairRec*  pairs;
+} TypeListRec;
+
+typedef const TypeListRec*  TypeList;
+
+static const TypeListRec  _types_list[] =
+{
+    { EV_KEY, "BTN_", _btn_codes_list },
+    { EV_REL, "REL_", _rel_codes_list },
+    { EV_ABS, "ABS_", _abs_codes_list },
+    { -1, NULL, NULL }
+};
+
+
+static TypeList
+find_type_list( int  type )
+{
+    TypeList  list = _types_list;
+
+    for ( ; list->type >= 0; list += 1 )
+        if (list->type == type)
+            return list;
+
+    return NULL;
+}
+
+
+int
+android_event_from_str( const char*  name,
+                        int         *ptype,
+                        int         *pcode,
+                        int         *pvalue )
+{
+    const char*  p;
+    const char*  pend;
+    const char*  q;
+    TypeList     list;
+    char*        end;
+
+    *ptype  = 0;
+    *pcode  = 0;
+    *pvalue = 0;
+
+    p    = name;
+    pend = p + strcspn(p, " \t");
+    q    = strchr(p, ':');
+    if (q == NULL || q > pend)
+        q = pend;
+
+    *ptype = scan_list( _ev_types_tab, "EV_", p, q-p );
+    if (*ptype < 0) {
+        *ptype = (int) strtol( p, &end, 0 );
+        if (end != q)
+            return -1;
+    }
+
+    if (*q != ':')
+        return 0;
+
+    p = q + 1;
+    q = strchr(p, ':');
+    if (q == NULL || q > pend)
+        q = pend;
+
+    list = find_type_list( *ptype );
+
+    *pcode = -1;
+    if (list != NULL) {
+        *pcode = scan_list( list->pairs, list->prefix, p, q-p );
+    }
+    if (*pcode < 0) {
+        *pcode = (int) strtol( p, &end, 0 );
+        if (end != q)
+            return -2;
+    }
+
+    if (*q != ':')
+        return 0;
+
+    p = q + 1;
+    q = strchr(p, ':');
+    if (q == NULL || q > pend)
+        q = pend;
+
+    *pvalue = (int)strtol( p, &end, 0 );
+    if (end != q)
+        return -3;
+
+    return 0;
+}
+
+int
+android_event_get_type_count( void )
+{
+    return count_list( _ev_types_tab );
+}
+
+char*
+android_event_bufprint_type_str( char*  buff, char*  end, int  type_index )
+{
+    return bufprint( buff, end, "EV_%s", _ev_types_tab[type_index].name );
+}
+
+/* returns the list of valid event code string aliases for a given event type */
+int
+android_event_get_code_count( int  type )
+{
+    TypeList  list = find_type_list(type);
+
+    if (list == NULL)
+        return 0;
+
+    return count_list( list->pairs );
+}
+
+char*
+android_event_bufprint_code_str( char*  buff, char*  end, int  type, int  code_index )
+{
+    TypeList  list = find_type_list(type);
+
+    if (list == NULL)
+        return buff;
+
+    return bufprint( buff, end, "%s%s", list->prefix, list->pairs[code_index].name );
+}
+
diff --git a/android/hw-events.h b/android/hw-events.h
new file mode 100644
index 0000000..74100b8
--- /dev/null
+++ b/android/hw-events.h
@@ -0,0 +1,184 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_HW_EVENTS_H
+#define _ANDROID_HW_EVENTS_H
+
+#include "android/utils/system.h"
+
+/* from the Linux kernel */
+
+#define  EVENT_TYPE_LIST  \
+  EV_TYPE(SYN,0x00)   \
+  EV_TYPE(KEY,0x01)   \
+  EV_TYPE(REL,0x02)   \
+  EV_TYPE(ABS,0x03)   \
+  EV_TYPE(MSC,0x04)   \
+  EV_TYPE(SW, 0x05)   \
+  EV_TYPE(LED,0x11)   \
+  EV_TYPE(SND,0x12)   \
+  EV_TYPE(REP,0x14)   \
+  EV_TYPE(FF, 0x15)   \
+  EV_TYPE(PWR,0x16)   \
+  EV_TYPE(FF_STATUS,0x17)  \
+  EV_TYPE(MAX,0x1f)
+
+#undef  EV_TYPE
+#define EV_TYPE(n,v)    GLUE(EV_,n) = v,
+typedef enum {
+    EVENT_TYPE_LIST
+} EventType;
+#undef  EV_TYPE
+
+#define  EVENT_BTN_LIST  \
+    BTN_CODE(MISC,0x100)  \
+    BTN_CODE(0,0x100)     \
+    BTN_CODE(1,0x101)     \
+    BTN_CODE(2,0x102)     \
+    BTN_CODE(3,0x103)     \
+    BTN_CODE(4,0x104)     \
+    BTN_CODE(5,0x105)     \
+    BTN_CODE(6,0x106)     \
+    BTN_CODE(7,0x107)     \
+    BTN_CODE(8,0x108)     \
+    BTN_CODE(9,0x109)     \
+    \
+    BTN_CODE(MOUSE,  0x110)  \
+    BTN_CODE(LEFT,   0x110)  \
+    BTN_CODE(RIGHT,  0x111)  \
+    BTN_CODE(MIDDLE, 0x112)  \
+    BTN_CODE(SIDE,   0x113)  \
+    BTN_CODE(EXTRA,  0x114)  \
+    BTN_CODE(FORWARD,0x115)  \
+    BTN_CODE(BACK,   0x116)  \
+    BTN_CODE(TASK,   0x117)  \
+    \
+    BTN_CODE(JOYSTICK,0x120)  \
+    BTN_CODE(TRIGGER, 0x120)  \
+    BTN_CODE(THUMB,   0x121)  \
+    BTN_CODE(THUMB2,  0x122)  \
+    BTN_CODE(TOP,     0x123)  \
+    BTN_CODE(TOP2,    0x124)  \
+    BTN_CODE(PINKIE,  0x125)  \
+    BTN_CODE(BASE,    0x126)  \
+    BTN_CODE(BASE2,   0x127)  \
+    BTN_CODE(BASE3,   0x128)  \
+    BTN_CODE(BASE4,   0x129)  \
+    BTN_CODE(BASE5,   0x12a)  \
+    BTN_CODE(BASE6,   0x12b)  \
+    BTN_CODE(DEAD,    0x12f)  \
+    \
+    BTN_CODE(GAMEPAD,  0x130)  \
+    BTN_CODE(A,        0x130)  \
+    BTN_CODE(B,        0x131)  \
+    BTN_CODE(C,        0x132)  \
+    BTN_CODE(X,        0x133)  \
+    BTN_CODE(Y,        0x134)  \
+    BTN_CODE(Z,        0x135)  \
+    BTN_CODE(TL,       0x136)  \
+    BTN_CODE(TR,       0x137)  \
+    BTN_CODE(TL2,      0x138)  \
+    BTN_CODE(TR2,      0x139)  \
+    BTN_CODE(SELECT,   0x13a)  \
+    BTN_CODE(START,    0x13b)  \
+    BTN_CODE(MODE,     0x13c)  \
+    BTN_CODE(THUMBL,   0x13d)  \
+    BTN_CODE(THUMBR,   0x13e)  \
+    \
+    BTN_CODE(DIGI,            0x140)  \
+    BTN_CODE(TOOL_PEN,        0x140)  \
+    BTN_CODE(TOOL_RUBBER,     0x141)  \
+    BTN_CODE(TOOL_BRUSH,      0x142)  \
+    BTN_CODE(TOOL_PENCIL,     0x143)  \
+    BTN_CODE(TOOL_AIRBRUSH,   0x144)  \
+    BTN_CODE(TOOL_FINGER,     0x145)  \
+    BTN_CODE(TOOL_MOUSE,      0x146)  \
+    BTN_CODE(TOOL_LENS,       0x147)  \
+    BTN_CODE(TOUCH,           0x14a)  \
+    BTN_CODE(STYLUS,          0x14b)  \
+    BTN_CODE(STYLUS2,         0x14c)  \
+    BTN_CODE(TOOL_DOUBLETAP,  0x14d)  \
+    BTN_CODE(TOOL_TRIPLETAP,  0x14e)  \
+    \
+    BTN_CODE(WHEEL,  0x150)      \
+    BTN_CODE(GEAR_DOWN,  0x150)  \
+    BTN_CODE(GEAR_UP,    0x150)
+
+#undef  BTN_CODE
+#define BTN_CODE(n,v)   GLUE(BTN_,n) = v,
+typedef enum {
+    EVENT_BTN_LIST
+} EventBtnCode;
+#undef  BTN_CODE
+
+#define  EVENT_REL_LIST \
+    REL_CODE(X,  0x00)  \
+    REL_CODE(Y,  0x01)
+
+#define  REL_CODE(n,v)  GLUE(REL_,n) = v,
+typedef enum {
+    EVENT_REL_LIST
+} EventRelCode;
+#undef  REL_CODE
+
+#define  EVENT_ABS_LIST  \
+    ABS_CODE(X,        0x00)  \
+    ABS_CODE(Y,        0x01)  \
+    ABS_CODE(Z,        0x02)  \
+    ABS_CODE(RX,       0x03)  \
+    ABS_CODE(RY,       0x04)  \
+    ABS_CODE(RZ,       0x05)  \
+    ABS_CODE(THROTTLE, 0x06)  \
+    ABS_CODE(RUDDER,   0x07)  \
+    ABS_CODE(WHEEL,    0x08)  \
+    ABS_CODE(GAS,      0x09)  \
+    ABS_CODE(BRAKE,    0x0a)  \
+    ABS_CODE(HAT0X,    0x10)  \
+    ABS_CODE(HAT0Y,    0x11)  \
+    ABS_CODE(HAT1X,    0x12)  \
+    ABS_CODE(HAT1Y,    0x13)  \
+    ABS_CODE(HAT2X,    0x14)  \
+    ABS_CODE(HAT2Y,    0x15)  \
+    ABS_CODE(HAT3X,    0x16)  \
+    ABS_CODE(HAT3Y,    0x17)  \
+    ABS_CODE(PRESSURE, 0x18)  \
+    ABS_CODE(DISTANCE, 0x19)  \
+    ABS_CODE(TILT_X,   0x1a)  \
+    ABS_CODE(TILT_Y,   0x1b)  \
+    ABS_CODE(TOOL_WIDTH, 0x1c)  \
+    ABS_CODE(VOLUME,     0x20)  \
+    ABS_CODE(MISC,       0x28)  \
+    ABS_CODE(MAX,        0x3f)
+
+#define  ABS_CODE(n,v)  GLUE(ABS_,n) = v,
+
+typedef enum {
+    EVENT_ABS_LIST
+} EventAbsCode;
+#undef  ABS_CODE
+
+/* convert an event string specification like <type>:<code>:<value>
+ * into three integers. returns 0 on success, or -1 in case of error
+ */
+extern int   android_event_from_str( const char*  name,
+                                     int         *ptype,
+                                     int         *pcode,
+                                     int         *pvalue );
+
+/* returns the list of valid event type string aliases */
+extern int    android_event_get_type_count( void );
+extern char*  android_event_bufprint_type_str( char*  buff, char*  end, int  type_index );
+
+/* returns the list of valid event code string aliases for a given event type */
+extern int    android_event_get_code_count( int  type );
+extern char*  android_event_bufprint_code_str( char*  buff, char*  end, int  type, int  code_index );
+
+#endif /* _ANDROID_HW_EVENTS_H */
diff --git a/android/hw-kmsg.c b/android/hw-kmsg.c
new file mode 100644
index 0000000..987943b
--- /dev/null
+++ b/android/hw-kmsg.c
@@ -0,0 +1,70 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/hw-kmsg.h"
+#include "qemu-char.h"
+#include "charpipe.h"
+#include "android/utils/debug.h"
+
+static CharDriverState*  android_kmsg_cs;
+
+typedef struct {
+    CharDriverState*  cs;
+    AndroidKmsgFlags  flags;
+} KernelLog;
+
+static int
+kernel_log_can_read( void*  opaque )
+{
+    return 1024;
+}
+
+static void
+kernel_log_read( void*  opaque, const uint8_t*  from, int  len )
+{
+    KernelLog*  k = opaque;
+
+    if (k->flags & ANDROID_KMSG_PRINT_MESSAGES)
+        printf( "%.*s", len, (const char*)from );
+
+    /* XXXX: TODO: save messages into in-memory buffer for later retrieval */
+}
+
+static void
+kernel_log_init( KernelLog*  k, AndroidKmsgFlags  flags )
+{
+    if ( qemu_chr_open_charpipe( &k->cs, &android_kmsg_cs ) < 0 ) {
+        derror( "could not create kernel log charpipe" );
+        exit(1);
+    }
+
+    qemu_chr_add_handlers( k->cs, kernel_log_can_read, kernel_log_read, NULL, k );
+
+    k->flags = flags;
+}
+
+static KernelLog  _kernel_log[1];
+
+void
+android_kmsg_init( AndroidKmsgFlags  flags )
+{
+    if (_kernel_log->cs == NULL)
+        kernel_log_init( _kernel_log, flags );
+}
+
+
+CharDriverState*  android_kmsg_get_cs( void )
+{
+    if (android_kmsg_cs == NULL) {
+        android_kmsg_init(0);
+    }
+    return android_kmsg_cs;
+}
diff --git a/android/hw-kmsg.h b/android/hw-kmsg.h
new file mode 100644
index 0000000..51d6731
--- /dev/null
+++ b/android/hw-kmsg.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_kmsg_h
+#define _android_kmsg_h
+
+#include "qemu-common.h"
+
+/* this chardriver is used to read the kernel messages coming
+ * from the first serial port (i.e. /dev/ttyS0) and store them
+ * in memory for later...
+ */
+
+typedef enum {
+    ANDROID_KMSG_SAVE_MESSAGES  = (1 << 0),
+    ANDROID_KMSG_PRINT_MESSAGES = (1 << 1),
+} AndroidKmsgFlags;
+
+extern void  android_kmsg_init( AndroidKmsgFlags  flags );
+
+extern CharDriverState*  android_kmsg_get_cs( void );
+
+#endif /* _android_kmsg_h */
diff --git a/android/icons.h b/android/icons.h
new file mode 100644
index 0000000..e4ec861
--- /dev/null
+++ b/android/icons.h
@@ -0,0 +1,984 @@
+/* Copyright (C) 2007 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+/* automatically generated, do not touch */
+
+static const unsigned char _data_android_icon_16_png[460] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 16,  0,  0,  0, 16,  8,  6,  0,  0,  0, 31,243,255,
+     97,  0,  0,  1,147, 73, 68, 65, 84, 56,141,173, 82, 77, 75, 66,
+     65, 20, 61, 79,173,199,148,125, 64,209, 35,112, 97,145, 84,219,
+     30,210,  7,173,162, 64,168, 77,235,  8,132, 86,110,251, 39,109,
+     94,109, 10,177, 40,104, 47, 73, 59,169, 80,136, 90, 68,144,182,
+      8, 18,162, 44, 42,165,184,111, 20,229,182,200, 39,126,188,146,
+    160,  3,  3,119,152,115,207, 61, 51,115,192,204,104, 92, 68, 82,
+     39,146, 61, 53,123, 65, 36,167,237,184,182,205,145,228, 12,231,
+     62, 94,152, 72,222, 17,201,251,143,207, 28, 71,146, 51,108, 39,
+    226, 66, 51,  2,139,227,135, 56, 78,175, 65,150,222,134,152,203,
+     80, 93,189,  8,140, 70,  0, 32,  0, 32, 81,199,110,152, 62,250,
+    244,126,203,225,196, 36, 19, 73, 54,226, 94, 54,226, 94, 38,146,
+    188,115, 54,193, 15,175, 87, 77, 46, 28,150,144,105, 22,102,  1,
+    132, 82,217, 61, 80, 49,139,240,249, 88,117, 72,248,124, 12,178,
+    244,134,235,199,109,  0,  8,153,102, 97,193, 58, 83,152, 25,166,
+     89,152,205,126, 94,156,104,110,221,230, 70, 63, 98, 73,  8, 53,
+    106,189, 65, 40,253,124,128,163,155, 21,  4,253,169,186,233,181,
+      8,250, 83,216,191,244, 99,160, 75,199,188,111, 43,  4, 32,234,
+    176,101,254,  1,255, 38, 80, 86,224,132,162, 56, 91, 54, 40,138,
+      3,202,119, 91, 25, 64, 53,  7,155,186,103,125,117,164,127,185,
+    165,192,156,207, 64,103,155,  6,  0,155, 85,  7, 66,168,137,142,
+    118,205, 51,216, 61,181,209, 74, 64,115,235,187,110,213, 51, 44,
+    132, 26,  3,208, 28, 36, 34,105,228,205, 12, 27,113, 47,231,205,
+     12,215,214, 68, 50,242, 99,144, 42, 78,210,  0,162,191, 24,136,
+      9,161,214, 69,217,238, 23, 46, 74, 69,  7,134,251,150, 80,144,
+     69,  0,168,173, 19,141,100,133,153,155, 20,172, 88,163,242, 80,
+     86, 45,132,122,218,200,253,  2, 34,145, 32,131,249,218,106,138,
+      0,  0,  0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+#ifdef CONFIG_DARWIN
+static const unsigned char _data_android_icon_256_png[13369] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  1,  0,  0,  0,  1,  0,  8,  6,  0,  0,  0, 92,114,168,
+    102,  0,  0, 32,  0, 73, 68, 65, 84,120,156,237,157,119,124, 84,
+     85,222,255, 63, 51,119,122, 50,105, 36,164,144, 10, 36,  4,144,
+    162, 16, 69, 17, 68,  4, 17, 69,220, 93,  5,101,125,118,117,197,
+    125, 22,244,121, 30, 93, 86,244,167,235,174,143,186,174,171,176,
+    214, 69,121,118,215,222,176, 23,176, 32,138, 65,164,  8,  2, 73,
+     32, 16,106,122, 47,147, 73,153,126,231,254,254,184,153,100,106,
+    114,238,100,250, 61,239,215,107, 94, 19,134,115,238, 57,183,124,
+     63,247,123,234, 87,194,113, 28, 40, 20,138, 56,145,134,187,  2,
+     20, 10, 37,124, 80,  1,160, 80, 68,140, 44,208,  7, 52, 26,205,
+    129, 62,100,196,163,209,168, 36,129, 56,142,193, 96, 18, 85,123,
+    140, 94, 55,255, 81,171,149,  1, 57,142, 36,208,125,  0, 98, 18,
+    128, 97, 30, 96, 41,120,113,101,  6,190, 37,  0,216,129,143, 13,
+    128, 29,  0, 55,240,241, 74,172, 62,212,195, 92, 51,  9,134,174,
+     23, 51,240,225,192, 95, 47,199,181,115, 92, 55,175,196,234, 53,
+    243, 70,160,  4,128, 54,  1,252, 64,163, 81, 73,188, 60,200, 12,
+      0, 21,128,132, 39,159,220, 48,169,186,186,230,209,174, 46,221,
+    238,254,126, 67,131,193, 96,234,236,233,233, 61,209,210,210,250,
+    246,161, 67,135, 86,  1, 72,  4,160,  1, 32,  7,127, 15, 36,  3,
+     31,151, 50, 66,112, 42, 33,197,203, 57, 73,193, 95,  3, 13,128,
+    196, 67,135, 14,173,106,105,105,125,187,167,167,247,132,193, 96,
+    234,236,239, 55, 52,116,117,233,118, 87, 87,215, 60,250,228,147,
+     27,138,  0, 36,  0, 80,131,191,214, 94,175, 89, 44, 94,183, 96,
+     66, 61,  0,129,120,121,192, 36,224,223, 90,154,150,150,214,151,
+    180,218,132,107, 36, 18,201, 72,242,204,153,205,166,170,147, 39,
+     79, 62, 60,103,206, 69,219,  0, 88,192,191,225,  6,255,223,241,
+     71,172,188,213,220,174,155,  4,188,241, 43,203,202,202, 87,228,
+    231, 23, 60,160, 80, 40, 38,194,205,160,221,225, 56,206,220,219,
+    219,243,121, 70, 70,250,237,  0, 12,224,189,  3,199,245,113,185,
+     78,177,114,221,124, 65,155,  0, 97,192,199, 27, 76,249,209, 71,
+     31, 95,176,120,241,226, 15, 25, 70, 54, 86,232, 49,219,219,219,
+     62,202,203,203, 93, 13,192,  8, 31, 15,116,180, 63,204, 94,140,
+    159,  1,160,105,110,110,125, 45, 49, 49,241, 58,161,199,179, 90,
+     45,237, 95,127,189,253,250, 21, 43, 86, 28,  6, 96,198, 80,115,
+     74, 52, 34, 64,  5, 32, 12,184, 61,200, 82,  0,170,250,250,134,
+     39,198,140, 73, 93,139, 81, 52,167,140, 70, 99,237,152, 49,201,
+    179,  0,244,129, 23,  1,192,237,129,142,214,135,217,139,241,203,
+      0,196,245,244,244,150,203,100,242, 28,255,143,204,217, 91, 90,
+     90,254, 57,126,124,193,122,  0, 38,120, 17,129,104,189,102, 36,
+    208, 62,128, 16,227,229, 65,150,151,151, 87,220, 54,102, 76,234,
+    157, 24,229,117, 84,171,213,121,157,157, 93, 21,  0,226,192,191,
+     29, 29,101, 12,150, 25,141,109, 91, 31,111,126,245,232,141, 31,
+      0, 36,210,140,140,204, 53,223,125, 87,122, 39,  0,  5,134,174,
+     87, 84, 95,179, 80, 67,  5, 64, 56,142,246,107,220,196,137,133,
+     79,  4,234,160,106,181, 38,235,228,201, 83,239, 98,168,147,203,
+     81, 86, 44, 60,196,131,109,254,166,166,230,215, 71,111,252, 67,
+    204,158, 61,251, 97,  0,169,112,125,150, 99,225,154,133,  4, 42,
+      0,194,145,  0, 80,233,116,221,223, 75, 36, 18,117, 32, 15,156,
+    147,147,187,232,163,143, 62, 94, 13,222, 77,246,120,160,163,233,
+    141,230, 84, 87,199, 55,115,224,192,193, 27,147,146,146,151,  7,
+    178, 28,134,145, 41,155,155, 91,118,130, 31,129,113,140,168, 80,
+      8,161,  2, 32, 28,201,182,109,159,207, 82, 42, 85, 83,130,113,
+    240,203, 47,191,252,127,193,191,209,124, 14,117,  5,163,220, 64,
+    226, 99,136, 52,110,242,228, 41, 27,131, 81, 94, 98, 98,210,132,
+    135, 31,126,100, 41,232,243, 44,152,128,207,  4, 20,202,171,  7,
+    139,195, 93,  5, 33, 72,  0,200,166, 79,159,113,115,176, 10, 80,
+     42, 85,218,234,234,154,207, 10, 10,242, 47,  7, 63,212,229, 49,
+     60, 24, 69,215,204,225,250,171,218,219, 59,183, 49, 12,147, 20,
+    172,130, 22, 45, 90,180,234,161,135,254,188, 29,124, 71,170,  3,
+     46,210,175,213,173, 37, 85, 97, 45,159, 42,166,112,100, 90,109,
+    252,165,193, 44, 32, 61, 61, 99,198,  7, 31,124,176, 22, 67,179,
+      8, 93, 58,184,238,184,172, 38, 98,189,  0,167,186, 57,234, 43,
+     59,120,240,167,155,227,226,226, 46, 14,102,185,121,121,249, 51,
+    192, 79,176,138,216,107, 19,137, 80,  1, 16,142, 76,161, 80, 20,
+    144, 36,236,232,175,192, 87, 85,191,194,  7, 21,139,112,164,241,
+    121, 65,133, 44, 92,184,232,255,129,111, 10, 56, 68,192,133, 72,
+     20,  1, 47,117,146,  2,136, 47, 46, 46,126, 92,200,113, 14, 53,
+     60,133, 15, 42, 22,225,171,170, 95,161,163,191,130, 40, 79, 66,
+     66, 66, 38,128,160,121, 24,177, 74,216,155,  0,209,128,243, 91,
+    237, 79,127,250, 83,186, 68, 34, 85,141,148,135,181,155,240,205,
+    169, 53, 48,217,186,  0,  0,229, 77,155, 96,231,172,152,149,189,
+    142,168, 76,149, 74,165,173,174,174,249,180, 96,168, 41,224, 60,
+    166,205, 57,234,245,194,174,252,193,223, 67, 45, 10,195,148,237,
+    236,250,127,198, 48, 50, 98,195, 60,212,240, 20,142, 54,255, 19,
+      0,208,103,110,192, 55,167,214, 96,197,140,157, 96, 70,184,228,
+     50,153, 76,185, 96,193,130,156,210,210,210, 42, 56,205,  7,112,
+    191, 70, 20, 87,168,  0,  8, 67,114,227,141, 55, 93, 78,146, 80,
+    111,170, 30, 52,126,  7,199, 90, 94, 66,110,210, 66,164,197,207,
+     36, 42,204,209, 20,184,225,134, 27,158,  3,255, 64,219,157,254,
+    123,240,  1,119,175,163,151,127, 15,247,113, 28,203,125, 34,141,
+    163, 60, 95,198,195,249, 16, 28,191, 93,255,246,190, 50, 28,107,
+    121,201,229, 55,147,173, 11,122, 83, 53, 82, 52,147, 71,204,127,
+    235,173,183, 94, 90, 90, 90,186, 19,174,253, 38,148, 97,160,  2,
+     32, 12,238,221,119,183,124,119,255,253,127, 28, 49, 97,162,170,
+      0, 10, 70, 11, 11,219, 59,148,153, 99,241, 67,245,253,184,238,
+    188,207, 32,149,200,137, 10, 28,104, 10,188, 13,160, 13, 67, 43,
+      9,  1,222,200, 56,167,191, 25,240,111, 93, 41,134, 86,211, 73,
+    211,210,210,228,159,127,254,197, 34,133, 66,145,234, 56,102,124,
+    124,124,102, 92, 92,156, 75,239, 88,127,127,127, 85, 95, 95, 95,
+    179,227,223, 70,163,177, 99,249,242,107,191,109,111,111,183, 12,
+    148,105,135,235,170, 60,111, 43, 26,  7,135,252,  0,104, 39, 77,
+     34,119,253,237,156, 21, 63, 84,223, 15,142,115,181, 93,  5,163,
+     69,162,138,168,197,133,205,155,255,239, 16,  0, 37,248,181, 21,
+     20,  2,168,  0,  8,131,123,244,209, 71,219,238,187,239,126,147,
+     84, 58,188, 79,202, 72, 85,152,157,179, 30,123,107,254,236,242,
+    187,222, 84,141,195, 13,207, 96,118,206,122,162,  2, 85, 42,149,
+    246,220,185,234, 79,198,143, 47,184,  2,124, 83, 96,176,  8,167,
+    143,124,223,190,125, 87,101,103,231, 92,166, 80, 40,115,149, 74,
+    197, 68,153, 76,150, 42,145, 72, 53, 18,137,132,232, 30, 39, 38,
+     38,121,204,201,175,173,173,  7,199,113, 54,187,221,110, 96, 89,
+     91,135,201,100, 58,107, 54,155,235,154,154, 26, 75, 47,190,248,
+    226,237,  0,172,224, 69,201,177,100, 23,224, 69, 64,221,214,214,
+    254,153, 76, 70,238,250, 31,110,120,  6,122, 83,181,199,239,179,
+    115,214,143,232,254,  3,128,213,106,181,236,223,191, 79,  7,126,
+     77,  5,133, 16, 42,  0,194,177, 89,173,150,106,165, 82, 53,162,
+     79, 90,148,182, 18, 53, 93, 95,161,169,103,175,203,239,149,173,
+    175, 34, 47,121, 49,113, 83, 32, 35, 35,115,198,150, 45,239,174,
+    189,233,166, 27,255,  9,  0,119,223,253,251,180,223,253,238,119,
+    191, 76, 77, 77, 93,160, 84,170,138,101, 50, 89, 50,130,212,251,
+     45,145, 72,100, 12,195, 36, 48, 12,147,160, 80, 40,199,  3, 64,
+     90,218,216,213,  6,131,137,179,217,108, 58,179,217, 84,213,209,
+    209, 81,250,210, 75,255,126,107,227,198,141,109,  0,240,195, 15,
+    123,110,140,143,215,206, 33, 45,163,189,175, 12,149,173,175,122,
+    252,158,149,112,  9,138,210, 86, 18, 29, 67,175,215, 55,129, 31,
+      2,180,143,148,150, 50, 68,216, 23,  3, 69,250, 56, 45,224, 49,
+    180,165,172,169,169,125,110,236,216,244,219, 73,242,246, 91,154,
+    241,201,209,107, 96,181, 27, 92,126, 79, 84, 21,  8,106, 10, 24,
+    141,134, 62,171,213,218, 28, 23, 23,159,195, 48,204,200,175,196,
+     48,192,178,172,201,100, 50,214,202,229,138, 12,133, 66,145, 72,
+    146,199,206, 89,241,233,177,229, 30,111,127,185, 84,131,159, 77,
+    251, 28,113,138, 76,162,178, 15, 30, 60,240,217,101,151,205,255,
+    127,  0,206,194,181,137,130, 72,238,  4,244,119, 30,  0, 93, 12,
+     20, 62,216,159,126, 58,248, 14,105,226, 56, 69, 38, 46,204,125,
+    192,227,119, 71, 83,128, 20,181, 90, 19,159,144,144, 88, 24,169,
+    198, 15,  0, 12,195,168,226,226,226, 39,145, 26, 63,224,219,245,
+    191, 48,247,  1, 98,227,  7,128, 45, 91,222,249, 26, 64, 15,113,
+      6, 10,  0, 42,  0, 68,184,189, 65,236, 55,220,112,195, 97,163,
+    209,120,134, 52,127, 97,218, 13,200, 74,184,196,227,247,202,214,
+     87,209,222, 87, 22,136, 42, 70, 37, 29,253, 21, 62, 93,255,194,
+    180, 27,136,143,211,213,213, 89,255,226,139, 47, 86,  1,112,244,
+    184, 14,222,175, 72,126,251, 71,  2, 84,  0,132,195,  1, 48,109,
+    216,240,228,207,237,118, 59,241,112,211,220,130,199,160, 96,180,
+    174,  7, 26, 24, 21,176,115,214, 64,215, 49,226,177,115, 86,236,
+     62,119,159,215, 94,255,185,  5,143, 17, 31,135,101, 89,118,197,
+    138, 27,238,  7,208,  2,190,247,159, 26,188,  0,168,  0,248,  7,
+    251,183,191, 61, 94,215,209,209,254, 17,105,134, 56, 69, 38, 74,
+    114,238,243,248, 93,104, 83, 32, 86,240,229,250,151,228,220, 39,
+    200,245, 63,118,236,104,233,190,125,251,106,  1,212,130, 26,191,
+     96,168,  0,  8,195,121,178,140, 37, 63, 63,239,206,254,254,190,
+     58,210,204,180, 41,192, 19, 40,215,191,179,179,179,249,226,139,
+    231,252, 29, 64,  3,248, 97, 72,111, 19,154, 40,195, 64,  5,192,
+     63, 56,240, 15,156, 97,221,186,223, 47,181,219,237,182,145, 50,
+     56,184,180,224,113, 81, 55,  5,  2,229,250,219,108, 54,246,170,
+    171,174,188,  7, 64, 35,128,118, 80,195,247, 11, 58, 15, 96,116,
+    216,222,120,227,141,166,135, 31,126,228,227,140,140,204, 21, 36,
+     25, 52,138,116,148,228,220,135, 61, 53, 15,186,252, 46,116,130,
+    144, 16,244,166,106,216,216,161, 97, 72, 51,171, 71,159,185,193,
+     37, 77,188, 50, 27, 74,102,168,243, 94,198,104,136,103,224,  9,
+     33, 80,174,255,209,163, 21,223, 87, 86, 86, 54,129,119,253,135,
+    141, 23, 64,241, 13, 21,  0,255,112,105, 10,140, 31, 95,240, 95,
+    109,109,237, 23,197,199,107,115, 73, 50, 23,166,221,128, 26,221,
+    118, 52,234,119,187,252, 46,116,130,144, 51,122, 83, 53,116,198,
+     83, 48, 88, 90,209,109, 60,131, 94,115, 29, 12,150, 86,175,198,
+     38,132, 68, 85,  1, 52,138,116,104,149,185, 72, 82, 79,132, 70,
+    145,142,100,117,145, 95,226, 16, 40,215,191,163,163,189,121,238,
+    220, 75,158,  2, 80,143,161,157,148,169,  7,224,  7,116, 34,144,
+      0,188,172,117,119,204,189, 87,172, 88,177, 50,255,229,151, 95,
+     57,204, 48, 12,145,168, 26, 44,173,248,228,216, 50,151,181,  2,
+      0,217,  4, 33,214,110, 66, 91, 95, 25,218,251,202,208,214, 95,
+    134,246,190, 35, 48,219,244,126,157,147,191, 40,101,137, 72,139,
+     63, 31, 99,227,102, 34, 45,126, 38,198,198,207, 28,118,202,174,
+    175,  9, 63, 10, 70,139,159,157,183, 13, 26, 69, 58, 81,185, 86,
+    171,149,189,232,162, 11,127, 83, 85,117,162, 18,192, 25, 12, 77,
+     67,246, 88,159, 16, 13, 67,128,225,158,  8, 68, 61,128,192,192,
+    189,255,254,123,157, 15, 61,244,208, 23,227,199, 79, 32,218,243,
+     78,163, 72,199,133,185, 15,224,135,234,251, 93,126,247,213, 20,
+    232, 50,156, 64,125,247, 78, 52,232,119, 71, 68,135,161,217,166,
+     71, 67,119, 41, 26,186, 75,  7,127, 75,139,159,137,236,196,121,
+    200, 73, 90,232,177,122,111, 56,215,159,212,248,  1,224,208,161,
+    159,246, 86, 85,157,232,  4,223,241, 23,241,  6, 30,233, 80, 15,
+    128, 16, 47,235,221, 29, 30,128, 28, 64,252,185,115,213,255, 72,
+     79,207,184, 65, 34,145,  8,154,147,191,227,212,111, 61,154,  2,
+     18,  9,131,107, 38,191,  3,169, 68,142,234,174, 47,113,182,243,
+     83, 24, 44,173,163, 61,133,144,162, 81,164, 99,194,152,235, 80,
+    144,178, 20, 28,103,199,182, 19, 43, 61, 58,254,198, 37,206,195,
+    226,162,127,  9, 58, 46,199,113, 56,115,230,244, 87, 51,102, 76,
+    255, 45,248,185,255,142,168, 74, 94, 87, 40, 70,186, 23, 16,110,
+     15,128, 10,  0,  1, 94,140, 31, 24, 88,133,119,193,  5, 23,140,
+    249,230,155,157,187, 84, 42,255,122,204,124, 53,  5, 24,169, 10,
+    172,221,228, 95,133, 35, 12,111,231, 34,212,245,119,167,183,183,
+    167,225,150, 91,110, 89,252,213, 87, 95, 54,131,143, 14,228, 24,
+      6,116, 44,  6,138, 10, 17,  8,183,  0,208, 97,192, 17, 24,198,
+    248, 85, 71,142,148,221,182,123,247,158, 51,254, 26, 63, 48,212,
+     20,112, 39, 86,140, 31,240,126, 46, 23,230, 62,224,183,241,  3,
+    128, 86,155,144,253,254,251, 31, 28,221,187,119,223,111,224, 25,
+     75,193,249, 59, 34,183, 79,139, 20,168,  0, 12,195, 48,198,175,
+    169,171,171,223, 60,105, 82,241,243, 18,137, 68, 49,154, 50,186,
+     12, 39, 80,215,189,115, 52,135,136, 74,234,186,119,162,203,112,
+     98, 84,199, 96, 24, 70, 54,115,230,249, 79,159, 62,125,102, 51,
+    248,  8,195,206,155,168,  2, 84,  4, 70,132, 54,  1,124,224,195,
+    248,101,  0,226, 58, 59,117,223,170,213,234,243, 71,115,252, 46,
+    195,  9,148, 53,189,128, 58,221,142,209, 28, 38,234,201, 77, 94,
+    140,153, 89,119, 16,109,249, 53, 28, 61, 61,250,163, 25, 25,233,
+     87,128,239, 23,176,194,115,123, 51,  0,145,215, 28,  8,119, 19,
+    128, 10,128, 23,124,116,248,201,  0,104,245,250,222, 50,185, 92,
+     62,206,223, 99,155,109,122,252,212,176,  1,167,219, 63, 24,109,
+     53, 99,138,194,180, 27, 48, 59,123, 61,148, 50,226,149,196, 30,
+    152, 76,198,214,148,148,228, 25,224, 87,  5, 90,225, 58, 65, 40,
+     34, 69, 32,220,  2, 64,155,  0,110,248, 48,126,121,105,233,174,
+     43,251,250, 12,213,254, 26, 63,199,177,168,108,121,  5, 31, 86,
+     44,162,198,239,133,211,237, 31,224,195,138, 69,168,108,121,197,
+     99,180,128, 20,149, 74,157,222,211,211,123,110,251,246,175,175,
+      4, 31, 48,212, 57, 84, 24,109, 14,120,129, 10,128, 19,195, 24,
+    255,226,217,179, 75,182, 72,165,210,120,127,142,171, 55, 85,227,
+    243, 19,171,112,176,254,  9,143,222,126,202, 16, 22,182, 23,  7,
+    235,159,192,231, 39, 86,249, 61,131, 81, 38,147,107, 46,185,228,
+    146, 45, 31,126,248,209,213,160, 34, 48, 34, 84,  0,  6,240,213,
+    230, 47, 45,221,181,184,164,164,228, 93,169, 84,170,241,231,184,
+     21, 77,155,241,233,177,229,196,  1, 46, 40,252,148,225, 79,143,
+     45, 71, 69,211,102,191,242, 51,140, 76,121,229,149, 75,222,252,
+    248,227, 79,150,130,159,167, 65, 59,  6,125, 64,  5,192, 19,231,
+     14, 63,237,236,217,179,223,146, 72,164,130,163,  0, 27,173, 29,
+    248,250,228,109, 56,220,248,140, 40, 86,249,  5, 26, 59,103,197,
+    225,198,103,176,253,228,173, 48, 90, 59,  4,231,103, 24, 70,177,
+    112,225,194, 87,  1,140,193, 48, 34, 32,118,168,  0,192,235,155,
+    128,  1, 16,175,215,247,150, 75,165,110,107,119,  9,104,238,217,
+    143,207, 42,127,230,177, 27, 48, 69, 56,142,107,217,220,179, 95,
+    112, 94,185, 92, 17,215,209,209,121,  8,128, 22,158, 67,132,  0,
+    168, 23, 32,122,  1,240, 17,210, 74,211,213,165,219, 41,151,203,
+    179,132, 30,239, 68,219, 91,248,250,212,106,191,222, 90, 20,239,
+     24,173, 29,248,250,212,106,156,104,123, 75,112, 94,141, 38, 46,
+    173,161,161,105, 23,128, 56,184,134, 92,167, 77,  1,136, 92,  0,
+    124,197,179,171,173,173,123, 90,165, 82,207, 16,122,188,159,234,
+     55,224,199,218, 71,253,238,197,166,248,134,227, 88,252, 88,251,
+     40, 14,214, 63, 33, 56,111, 74, 74, 74,113, 89, 89,249, 11,  0,
+     84, 24,234, 20,164, 34,  0,145, 11,128, 19,142,155, 47, 63,114,
+    164,236,214,180,180,177,191, 17,146,217,206, 89,177,235,236, 58,
+    143,184,118,148,192, 83,217,242, 10,118,157, 93, 39,184, 95,165,
+    168,104,210,202,175,190,218,190, 22,252,200,  0,237, 15, 24, 64,
+    180,  2,224,171,221, 95, 84, 52,105,163,144,227,216, 57, 43, 74,
+    207,174, 67,117,215, 23,129,171, 28,101, 88,170,187,190,192,119,
+    103,238, 18, 44,  2,115,231, 94,250, 72, 97, 97, 97, 54, 60, 59,
+      5,  1,136,211, 11, 16,165,  0,120,113,253, 25,  0,113, 58,157,
+    126,175, 68, 34, 33,158, 98,101,231,172,248,238,204, 93,  1,157,
+    206, 27,175,204, 70,126,242, 18,140,137,155, 26,176, 99,134,155,
+     49,113, 83,145,159,188,  4,241,202,236,128, 29,179,190,123, 39,
+    190, 61,125,135, 32, 17, 96, 24, 70,190,123,247,158, 29,160,253,
+      1,131,136,125, 67, 16,199,205, 87, 52, 52, 52,254, 67,169, 84,
+     78, 20,146,249,135,234,251, 81, 31,160,133, 60, 83, 51,126,131,
+    233,153,107, 60,166,194,158,235,220,134,  3,117,127,245,  8, 53,
+     30,233,168,100, 41,184, 48,247,  1,140, 31,179,204,229,119,179,
+     77,143,138,230,205,168,108,121,101,212,101, 52,234,119,227,135,
+    234,251, 49,127, 60,185,211,150,144,144,144,125,236, 88,229, 43,
+    231,157, 55,245,102,120,134, 17,143,152, 41,194,161, 66,116, 30,
+    128, 55,215,255,142, 59,238,200, 74, 78, 78,185, 81,200,113, 14,
+    214, 63,129,115,157,219, 70, 93, 31,149, 44,  5,215, 76,222,130,
+    146,156,251,188,206,131, 31, 63,102, 25,126, 62,237, 75,140, 75,
+    156, 55,234,178, 66,197,184,196,121,248,217,180,109, 30,198, 15,
+    240, 91,137,149,228,220,135,107, 38,111,129, 74,150, 50,234,178,
+    206,117,110, 19,220, 49,152,159, 95,112,213,162, 69,139, 39, 99,
+    168, 41,224,130,152,188,  0,209,  9,128, 19, 14,215, 95,243,216,
+     99,143,127, 73, 26, 70, 27,  0,142,183,190, 30,144, 55, 24,  0,
+     44,152,248,236,136,155,128, 42,101,137, 88, 56,241,121, 36, 40,
+    137,246, 28, 13, 43,  9,202, 92, 44,152,248,236,136,198,157, 22,
+     63, 19,151, 23, 62, 31,144, 50, 43, 91, 94,193,241,214,215,137,
+    211, 75,165, 82,230,205, 55,223,124, 15,252,252,  0,175, 77,  1,
+    177, 32, 42,  1,112,219,212, 19,  0,228,245,245,141,207, 40,149,
+    202,  9,164,199,104,237, 59,132,159,234, 55,  4,164, 62,  5, 41,
+     87, 35, 67, 91, 66,148,150,145,170,130,178,101,120,160,153,157,
+    179, 30,114,194, 89,211,233,241,179, 80,144,114,117, 64,202,253,
+    169,126,  3, 90,251, 14, 17,167, 79, 72, 72,204, 41, 47,175,120,
+      1, 62, 70,  5,196,226,  5,136, 74,  0,220, 96,  0,104,146,147,
+    147,174, 39,205, 96,180,118,160,212,143,222,103, 95, 76, 24, 67,
+    180,127,232, 32, 57, 73, 11, 71,181, 92, 54,216, 40,101,137,200,
+     73, 90, 40, 40,143,208,107,224, 11, 59,103, 69,233,153,187,  4,
+     77,192,202,203,203, 95, 10, 32, 11, 62,102,  9,138,  1,209,  8,
+    128,151, 45,189,149,237,237, 29,159, 72,165, 12,241, 10,191, 31,
+    170,239, 15,232, 12,191,100,205, 36, 65,233, 37, 18,  6, 73, 42,
+     65,253,148, 33, 37, 73, 53, 17, 18,  9, 51,114, 66, 39,132, 94,
+    131,225, 48, 90, 59,176,251,220,189,196,233, 21, 10,133,250,220,
+    185,234,183,  0, 40,225,218, 12, 16,141, 23, 32, 26,  1,112,131,
+    249,247,191,255, 61, 53, 46, 46,126, 46,105,134,211,237, 31,120,
+    236,222, 59, 90,100,126, 44, 48,148, 49,126, 45, 74, 12,  9,254,
+    212,205,159,107, 48, 28, 77, 61,123,  5,237,183,144,145,145,121,
+    193, 93,119,221, 53, 15, 67, 94,128,168, 16,133,  0,120,121,251,
+     43,174,189,118,249,227,164,249,251, 45,205, 56, 80,247,215,128,
+    215,203,104,109,247, 35, 79,228,174, 49,240,167,110,254, 92,131,
+    145, 56, 80,247, 87,244, 91,154,137,211,175, 89,179,246, 33,240,
+    115,  3, 68,231,  5,136, 66,  0,220,144,  2, 80,199,199,107,231,
+    147,102,216, 87,251, 48,172,118,195,200,  9,  5, 34,116,181, 96,
+    191,165,121,212, 27,105,  6,147, 46,195,  9, 65,134,  7,  8,191,
+      6, 36, 88,237,  6,236,171,125,152, 56,125, 78, 78,238, 76,240,
+    125,  1,142, 17,  1,209, 32, 70,  1,144,215,215, 55,108,148, 16,
+     54, 86,221,163,223,  4,146,227,173,175, 11,234, 80, 20, 50,212,
+     21, 46,132,212,209,206, 89,131,118, 78, 66,238,155, 84, 42,101,
+     14, 28, 56,248, 24, 60, 71,  4, 98,158,152, 23,  0, 55,247, 95,
+     10, 64,149,156,156,242, 11,146,188, 28,199,226,112,227,179, 65,
+    171, 91,159,185,129,184,105,209,220,179, 31, 39, 90,223, 12, 90,
+     93,  2,197,137,214, 55,137,215,238, 31,168,251,171, 71,148,226,
+     64,114,184,241, 89,226,149,153, 69, 69, 69, 11,  1,100,192,203,
+    106,193, 88,110,  6,196,188,  0,184, 33,219,187,119,223,114,210,
+    189,253,106,187,191,  9,186,203, 93,213,246, 14,246,214,252,121,
+    216, 64, 32, 53,186,237,216, 41,112,222,123,184,176,115, 86,236,
+     60,125,199,176,139,163, 88,187,  9,123,107,254,140,170,182,119,
+    130, 90,151, 46,195,  9,212,118,127, 67,148, 86,161, 80,170, 55,
+    111,254,191, 21, 16,217,144,160, 88,214,  2, 12,110,240, 89, 84,
+     52,233, 46,210, 76,254,238, 73, 39,148, 83,237,239,161, 81,191,
+     27, 69,105, 55, 32, 43, 97, 46,100,140,  6,118,187, 21,221,166,
+     51, 56,219,241,105,212,237, 44,100,181, 27,176,235,236, 58,156,
+    110,255,  0, 19, 82,175, 67,146,106, 34,164, 82, 57,108,172,  1,
+     77, 61,123,112,170,253,  3,193,125,  5,254, 82,209,180, 25,249,
+    201, 75,136,210, 46, 93,186,244, 22,  0, 47,  3,232, 28,248,201,
+     33,  2, 49,187, 70, 32,166,227,  2,120,113,255,147,250,251,141,
+     45, 36,237,255,134,238, 82,124,115,122, 77,208,234, 70,  9, 29,
+    139, 10, 55, 35, 59,105,193,136,233,236,118,187, 61, 62, 94, 83,
+      2,224,  4,248,176,227, 65, 15, 55, 78,227,  2,132, 14, 89, 89,
+     89,249, 42,210,206,191,170,246, 45,193,174, 15, 37, 68,144,222,
+     75,169, 84, 42,125,227,141, 55,110,130,136,154,  1, 98, 17,  0,
+      9,  0, 89, 86, 86,214, 10,146,196,  6, 75,107,192, 39,253, 80,
+    194, 71,163,126, 55,113,120,245,146,146, 11, 23,  3, 72,128, 72,
+    118, 12, 18,131,  0, 12, 46,252, 81,171,201,246,249, 59,219,249,
+     41,221,215, 47,134,224, 56, 22,103, 58, 62, 38, 74,155,158,158,
+     94,  8, 32, 25, 34,217, 54, 76, 12,  2,  0,240,231,169, 96, 24,
+     89,  2, 73,226, 64,172,243,167, 68, 22,164, 91,182, 41,149,170,
+     56,  0, 41,112, 29, 14,140, 89, 98, 86,  0,220,198,110,153,178,
+    178,242,255, 32,201,215, 99,174,131,206,120, 42, 72,181,162,132,
+     11,157,241, 20,122,204,117, 68,105, 95,127,253,141,235,225,101,
+    109, 64, 44,206,  7,136, 89,  1,112, 66,  2, 64,150,153,153, 73,
+    180,240,188, 73,191, 39,200,213,161,132, 11,210,123, 59,107,214,
+    172,185,224,251,  1,128, 24,247,  2, 98, 93,  0,  6,195,124,169,
+    213,234,105, 36, 25,162,109,204,157, 66, 14,233,189, 29, 59, 54,
+    125,  2,128, 68,184, 26,126, 76,138, 64,172, 11,  0,192,159,163,
+     92, 38,147,141, 33, 73,220,222,119, 36,200,213,161,132, 11,210,
+    123,171,209,168, 19,  1, 36, 65,  4,253,  0, 98, 16,  0,201,103,
+    159,109,157, 13, 72, 70, 60, 87,131,165, 53,162,151,219, 82, 70,
+    135,209,218, 65, 52, 28, 40,145, 72, 37,235,215,223, 59, 11, 34,
+    176,143,168,154, 10,236,103, 39, 12,115,222,121,231,121,110, 79,
+    235,133, 78, 67,165, 31,135,167, 68, 19,157,134, 74,104, 20,233,
+     35,166, 91,178,100,201,101, 27, 54, 60,249, 14,  0,151,  5, 24,
+     66,158,193, 96,204, 28, 12, 52, 17, 41,  0,  2, 46,178,123, 58,
+    206,237,255, 36,  0, 24,141, 70,115, 30,201,193,244,166,106,194,
+     98, 41,209,138,222, 84,141, 28,130,116, 89, 89, 89,227,  1,196,
+      3,232,133,107, 51,192,253, 25,115,198,197,224,125, 61,199,145,
+     36, 12, 17, 35,  0,195, 24,189,227,226, 75,193,215, 87,  6,126,
+    227,  6,105, 70, 70,  6,  3,  0, 45, 45, 45,142,121,219, 54,240,
+    193, 30,216,129,127, 75,  1, 48, 10,133, 34,147,164, 14,253,230,
+    166,209,156,  2, 37, 10, 32,189,199, 90,173,118, 12,  0, 13,134,
+    158, 63,199,176, 32, 51,240,183, 52, 35, 35, 67,  6,  0, 45, 45,
+     45,142,231,205,241,236, 57,158, 67,103, 67, 31,252,219,249, 89,
+    191,213, 16,222,133, 70, 97, 23,  0, 47,134,239,188, 22, 91, 62,
+    240, 81, 28, 57, 82,118,195,184,113,227,150,171, 84,234,153, 12,
+     35, 77,150, 72,164,170,161, 44,156,205,110,231,140,102,179,233,
+    116,119,119,247,206,119,223,125,247,213,  7, 30,184,191, 25,252,
+     77, 80, 49,140, 44,149,164, 46,189,150,198, 64,156, 18, 37,130,
+     33,189,199,106,181, 58,  9,188,  7, 16,  7, 64,242,151,191, 60,
+    150,190,106,213,170, 91,146,146,146, 22, 42,149,170, 66,169, 84,
+    162,  6,134, 98, 73,112,156,221,196,178,108,151,193, 96, 56, 82,
+     95, 95,255, 73, 73,201,236, 79,192, 55, 31, 28, 31, 14,188, 72,
+     12,102,  1,  0,141, 70, 37,  1,  0,131,193, 20, 22, 33,  8,219,
+    106, 64,199,137, 59,215,  5, 67, 10,171,250,234,171,237, 23, 77,
+    159, 62,125,181, 86,155,112, 37,195, 48,201, 66,235,193,113,118,
+    179,209,104, 60,117,252,248,241, 45,231,159,127,254, 31, 25, 70,
+     54,226,238,147,159,159,184,  9,237,125,101, 66,139,162, 68, 17,
+    105,241, 51,113,205,228,145, 23,  7,153,205,102,243,231,159,111,
+    125,241,202, 43,151, 92,161,209,196, 21, 73,165, 82,161,203,239,
+     56,155,205,166,235,233,209,127, 83, 94, 94,254,210, 53,215, 92,
+    253, 35,  0, 51,134, 86, 25, 14,166,115,206, 68, 42,  4,129, 90,
+     13, 24, 22,  1,112, 51,126,199,223, 12,  0, 85,101,229,241,223,
+    101,103,103,223, 37,151, 43,  2, 22, 73,146,101, 89, 43,195, 48,
+    242,145,210,125,122,108, 57,157,  5, 24,227, 36,171,139,112,221,
+    121,159,141,152,206,106,181,218,228,114,121,192, 60,100,139,197,
+    220, 80, 95, 95,247,220,180,105,211,254,  9,192,136,161,184,132,
+    156,219, 55,145,  8,  4, 74,  0, 66,222,  4,240, 98,252, 14, 87,
+     63, 94,167,211,239, 86, 42,149,  1,223, 32,128,196,248,  1,  4,
+    101,227, 79, 74,100, 65,122,143,  3,105,252,  0,160, 80, 40,179,
+     39, 76, 40,124,178,179,179,235,246, 49, 99, 82,230,  1,232,  3,
+    223, 52,112,236, 57, 32,129, 83,179, 32, 84, 77,130,144,142,115,
+    122, 49,126,  6,128,250,216,177,202,181,253,253,134,250, 96, 24,
+    191, 16,130,185, 63, 29, 37, 50,  8,247, 61, 86,171, 53, 69,189,
+    189,253,117,101,101,229,107,  1,168,225, 35, 54,161,151, 38,114,
+     80,  8, 89, 19,192,139,241,203,  0,104,154,154,154, 95, 79, 74,
+     74,190, 54,160,149,160, 80,162,128,246,246,182, 47,243,242,114,
+    255,  3,128,  1,124,223,  0,231,244,  1,224,187, 57, 16, 85,125,
+      0, 62,222,252,241,221,221, 61,  7, 20, 10, 69,228,198,186,162,
+     80,130,140,193,208, 95,147,154, 58,166,  4,252,124,  3,199,208,
+    225,136, 34, 16, 53, 91,130,249, 48,254, 56,157,174,251,  7,106,
+    252, 20,177,163,209,196,229,183,182,182, 31,  4, 63,220, 24,242,
+    230, 64, 40,251,  0,  6,247,229,111,108,108,126, 69,169, 84, 77,
+      9, 97,217, 20, 74,196,162,213,106,243, 79,159, 62,243, 33,  0,
+     21,188,196, 37,  8, 38, 65, 21,  0, 39,229, 26, 92,150, 91, 81,
+    113,244,183,201,201,201, 63, 11,102,185, 20, 74,180, 49,110, 92,
+    246,130, 29, 59,118,220, 13,126, 68,204, 99,152, 60, 88, 94, 64,
+     80,251,  0,220,  4,128,  1,144,216,223,111,168,119,157,197, 71,
+    134,213,110, 64,179,126, 15,170,187,190, 68,143,185, 22, 58,195,
+    169,193, 64, 25,  9,202, 92,104, 20,233,200, 74,152,139,188,148,
+     37, 72, 84, 21,248, 85,247, 96,110, 81, 78,137, 28,252,221,138,
+     91,111,170, 70,109,215,118, 52,245,236,129,193,210, 58,184,195,
+    144, 84, 34, 71,178,166,  8,137,170,  2,228, 39, 47, 65,102,226,
+     92,200,253,136,122,204,178, 54,139, 86, 27, 95, 12,160, 25, 94,
+     58,  5,157,251,  2, 34,190, 19,208,205,248,165,  0, 52, 58,157,
+    254, 71,161, 67,125, 22,182, 23, 71, 26,158,193,169,142, 15,135,
+    141,158,227, 76, 90,252, 76, 92, 48,238,110,100, 38,204, 17, 82,
+     20, 21,  0,145, 32, 84,  0,154,123,246,227,112,227, 51,196,179,
+     68, 25,169, 10, 69,169,215,227,252,236,187,161, 96,180,130,202,
+    210,235,187,171, 51, 51, 51,102,  1,232,135,107, 92,  2, 23, 17,
+    136,154, 78,192,  1,152,242,242,138,219,132, 26,127,125,247, 78,
+    124, 88,177,  8, 39,218,222, 34, 54,126,  0,104,239, 43,195,246,
+    147,183,226,251,115,247,208,201, 61, 20,191,177,218, 13,248,254,
+    220, 61,216,126,242, 86, 65, 83,196, 89,187,  9, 39,218,222,194,
+    135, 21,139, 80,223,189, 83, 80,153,137,137, 73,  5, 91,183,110,
+     93,  7,207,166, 64, 80,  8,182,  0, 12,118,252,229,229,229,253,
+     94, 72,198,242,166, 77,248,246,244, 29, 48,219,244,126, 23,126,
+    174,115, 27,182, 85, 94, 31,178, 48, 84,148,216,161,223,210,140,
+    109,149,215,143,106,135,104,179, 77,143,111, 79,223,129,242,166,
+     77,130,242, 93,120,225, 69,183,195,115,103,226,160,136, 65, 80,
+      4,192,173,195, 66,250,221,119,165,151, 43, 20,202, 92,210,252,
+    199, 90, 94,194,145,198,231,  3, 82, 23,189,169, 26,219,171,110,
+    129,133,237, 13,200,241, 40,177,143,133,237,197,246,170, 91,  2,
+    182, 63,196,145,198,231,  5,197,153,212,106, 19, 50,158,121,230,
+    217,101, 24, 26, 22, 28, 36,208,157,129,161,104,  2,200,167, 78,
+    157, 74, 28,144,179,161,187, 20,135, 26,158, 10,104,  5,122,204,
+    117, 40, 61,115, 23, 13,246, 65, 25, 17,142, 99, 81,122,230, 46,
+    226, 45,196, 73, 57,220,248, 12,234,116, 59,136,211, 47, 93,186,
+    244,118,240,203,145,131, 58, 28, 24, 76,  1, 24, 92,232,163, 86,
+    107, 46, 32,201, 96,182,233,177,187,250,190,160, 24,106, 83,207,
+     94, 28,107,121, 41,224,199,165,196, 22,199, 90, 94, 10,218,206,
+    208,123,106, 30, 36,110,210,166,167,167, 23,  3, 72,133,107,116,
+    162,128, 11, 65, 40,250,  0, 20, 12,195, 16, 69,228, 41,111,218,
+     52,170, 54,255, 72, 84, 52,109,134,201,214, 21,180,227, 83,162,
+     27,147,173, 43,168, 33,225,205, 54, 61,113,127,128, 66,161,212,
+      0, 24,131,161,126,128,160, 16,108,  1,144, 85, 84, 28, 37,138,
+    200, 99,182,233,113,178,253,189,160, 86,198,106, 55,224, 68,235,
+     91, 65, 45,131, 18,189,156,104,125, 43,232,163, 70, 39,219,223,
+     35,126,201,109,217,242,238,141,  8,114,164,226,128, 11,128, 91,
+     39,  5,147,158,158,126, 13, 73,190, 90,221,118, 65, 67,125,254,
+    114,182,243,211,160,151, 65,137, 78, 66,241,108,176,118, 19,106,
+    117,219,137,210,206,152, 49,227, 18,184, 70, 42,  6, 16,216,142,
+    192, 96,247,  1,200, 73, 35,242, 52,132, 40, 28,119,159,185,129,
+    238,254, 75,241,160,219,120, 38,100,123,  5,212,119,151, 18,165,
+     75, 75, 27, 59, 30,124,164, 98, 32,202,250,  0,  6,183,249, 34,
+    141,200,211,209, 95, 17,164,170,132,183, 44, 74,116, 16,202,152,
+     16,164,101,169, 84, 42, 45, 60, 61,128,168, 25,  6,148,252,230,
+     55,183, 37,145, 68,228,177,115, 86,162,136, 45,129, 34,220,187,
+    194, 80, 34,143, 80, 62, 19,  6, 75,235,224, 58,150,225,144, 74,
+    165,210,169, 83,167,166, 33,136, 29,129, 65, 21,128, 37, 75,174,
+     28, 71,146, 48,212,147,116, 44,108, 95, 72,203,163, 68, 62, 86,
+     54,180, 83,198, 73,159,249,139, 46,186, 40, 11,174, 67,129,  1,
+     37,168,  2,144,144,144, 64,180,157, 55,107, 39,219, 74, 60, 80,
+    132,162,179,145, 18, 93,216, 66,188,102,132,244,153, 79, 79, 79,
+    215, 34,136,235,  2, 98, 62,248, 33,133, 18,205,216,237,156, 99,
+     61, 77, 80,160,  2, 64,161, 68, 62, 81, 57, 17, 40,102, 99,170,
+     83, 40,161,195,101,191,142,168,234,  3, 64,128,247, 26,161, 80,
+    196, 76, 84,245,  1,208,183, 63,133, 18, 56,162,114, 30,  0,133,
+     66,137,112,168,  0, 80, 40, 34,134, 10,  0,133, 18,193,112, 28,
+     23,212,230, 52, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,
+      0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,
+      1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12,
+     21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196,
+     80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68,
+     12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,
+    196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160, 80,
+     68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10,
+     69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160,
+     80, 68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0,
+     10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,
+    160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,
+      0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,
+      1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12,
+     21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 37,130,
+    145, 72, 36, 92, 48,143, 79,  5,128, 66, 17, 49, 84,  0, 40, 20,
+     17, 19, 44,  1,  8,170,219, 66,161,136, 12,206,199,223,163, 38,
+    152, 30,  0,215,210,210,220, 78,146, 80, 38,213,  4,177, 26,225,
+     47,143, 18,249, 68,234, 51, 88, 91, 91,219,  5,222,232,131,242,
+     82, 13,170,  0,124,255,253,247,109, 36,  9,229, 76,104, 47,190,
+    130,209,134,180, 60, 74,228, 19,234,103,144,180,188,178,178, 50,
+     61,  0,251,192, 63,  3, 46,  2, 65, 21,128,215, 94,123, 77,207,
+    113, 28, 59, 98, 37, 36,114,196, 43,179,131, 88, 21, 87,180,170,
+    220,144,149, 69,137, 14, 18, 84,  5, 33, 43, 43, 94,153, 13,169,
+     68, 62, 98, 58,150,101,237, 85, 85, 85,189,  0,172,193,170, 75,
+    176, 59,  1,109, 86,171,181,147, 36, 97,170,102,106,144,171, 50,
+     68, 90,220,244,144,149, 69,137, 14, 66,249, 76,144, 62,235,  6,
+    131, 65, 15,192,132, 40,109,  2,  0,128,205, 96,232,175, 32, 73,
+     56, 46,113, 94,144,171,194,147,160,204, 13,169,183, 65,137, 14,
+    226,149,217, 72, 80,134,198, 51, 36,125,214, 91, 91, 91,206,  0,
+    232, 65, 16, 59,213,131,218,  4,  0, 96,107,104,104,248,156, 36,
+    113, 94,202, 18,200, 67,208, 17, 51, 49,245, 23, 65, 47,131, 18,
+    157,132,226,217,144, 75, 53,200, 75, 89, 66,148,246,199, 31,247,
+    239,  5,160,119,250, 41,170,250,  0,  0,128,189,240,194,146,119,
+     72, 18, 42, 24, 45,166,101,254,103, 80, 43,163, 81,164, 99,114,
+    198,175,131, 90,  6, 37,122,153,156,241,107,104, 20,233, 65, 45,
+     99, 90,230,127, 18,117, 66,115, 28,135,223,254,246,183,223, 96,
+    200,  3,136,170, 38,  0,231,244,109,179,217,172,250,225, 18, 59,
+      8,246, 13,152,158,185, 38, 36, 94,  6, 37, 58,145, 75, 53,152,
+    158,185, 38,104,199, 23,242,  2, 50,153,140,125,224, 59,255,250,
+     17,173,243,  0,  6, 62,214,190,190,190, 67, 36, 25,228, 82, 13,
+    230,143,223, 72,212, 67, 42,148,156,164,133, 40, 30,187, 42,224,
+    199,165,196, 22,197, 99, 87, 33, 39,105, 97,192,143, 43,145, 48,
+    152,155,255, 23,226, 23, 80, 83, 83,211, 73,  0, 58,184,190, 76,
+     35,191,  9, 96, 48,152,220, 43,105, 61,120,240,192,115,164,249,
+     51,180, 37,152,147,247,167,128,214, 41, 89, 93,132,249, 19, 54,
+      6,244,152,148,216,101,254,132,141, 72, 86, 23,  5,244,152, 37,
+     57,247,  9,234,232,126,253,245,215,223,197,144,  0,184,216,148,
+     23, 27,243,155, 96,247,  1,112,  0,216,235,174,187,110,183,209,
+    104, 56, 71,154,169, 40,109, 37, 46, 45,120, 60, 32,158, 64,134,
+    182,  4, 87, 21,191, 65, 93,127, 10, 49,114,169,  6, 87, 21,191,
+    129, 12,109,201,168,143, 37,149,200,113,105,193,227,152,146, 78,
+    222,247,212,217,217,217,184, 97,195,147,229,  0, 58, 49, 36,  0,
+     81,213,  7,224,140, 29,128,121,223,190,125,235,133,100,154,152,
+    250,115, 44, 41,126,213,239, 33, 59,137,132,193,121, 25,171,113,
+    229,164,151,161,148, 37,250,117, 12,138,120, 81,202, 18,113,229,
+    164,151,113, 94,198,106, 72, 36,140, 95,199,136, 87,102,227,202,
+     73, 47, 99, 98,234,207,  5,229,219,180,233,249, 77,  0,154,  1,
+     24, 17, 68,227,  7,  0,  9,199,  5,246,216, 70,163, 25, 26,141,
+     74,226, 56, 62,120,145, 97,  0,104,218,218,218,119,197,199,107,
+    167,  9, 57,158,157,179,226,100,251,123, 40,107,124, 14,102, 27,
+     81, 95, 34,114,147, 23,227,130,113,119, 33, 73, 61, 81, 80,221,
+     95, 61, 88, 44, 40, 61, 37, 58,185,181,164, 74, 80,122,189,169,
+     26,135, 26,158, 66,157,110,  7, 81,122,165, 44, 17, 51,199,253,
+     15, 38,165,173, 20,236,197, 54, 55, 55,157,157, 48, 97,252, 29,
+      0,126,  2, 96,  0,255,  2,117,124, 56,128,111,  2,168,213, 74,
+     65,199,245, 69, 40,  4,192, 33,  2, 50,  0,  9,122,125,239, 57,
+    185, 92,238,151, 63,222,212,179, 23, 77,250, 61,104,238,221, 15,
+      0,232,236,175, 68,156, 34, 19, 42,121, 10, 52,242,116,228, 37,
+     47,198,184,196,121, 80,203, 83,253, 92,168,210,238,  0,  0, 19,
+    244, 73, 68, 65, 84,170, 59, 21,  0,113, 32, 84,  0, 28,152,108,
+     93,104,232,222,133, 90,221, 14, 24,172,173, 48, 89,187,208,111,
+    105,198,152, 56,126,102, 95,166,118, 14,178, 18,231, 34, 43,225,
+     18,191,142,111, 54,155,140,201,201, 73, 43,  0,156,  3, 80,  3,
+     79,227, 15,184,  0,200,  2,114, 20, 55, 12,  6, 19,231, 36,  2,
+    142,138,179,  0, 12,229,229,101,235,102,207, 46,217,236,207,113,
+    179, 18, 46,241,251,226, 82, 40,163, 69, 37, 75,193,196,212,159,
+     11,118,233, 73,121,237,181,215,254,  1,160,  3, 64, 29,124,180,
+    253,  3,217,  1,  8,132,166, 19,208,241,205,  1,176,206,159, 63,
+    239,189,186,186, 90,162,201, 65, 20,138, 88,168,168, 40,223,117,
+    247,221,119,125,  3,160, 26,174,111,252,168,239,  4,  4, 92,189,
+      0, 83,113,241,164,187,116,186,174, 35, 33, 42,155, 66,137,104,
+    154,155,155,206,206,153,115,209,223,  0,156,  6,208,  7, 47,110,
+    127,176,  8,154,  0, 56,185, 42,238, 94,  0, 11,160,127,220,184,
+    172,171,218,218, 90,127, 10, 86,249, 20, 74, 52, 80, 83, 83,125,
+    116,194,132,241,255, 13,160, 22, 64, 43, 60,223,254,128, 83,219,
+     63,208,229,135,218,  3,112,124,108,  0,250,242,243,243,174, 62,
+    121,178,234,189, 16,213,129, 66,137, 40, 14, 30, 60,176,125,202,
+    148,201,247,  2, 56, 11, 94,  0,220,223,252,209,235,  1,  0, 94,
+     21,203, 67,  4,206, 63,127,230,218,143, 63,254,240,191,204,102,
+    147, 33,152,117,161, 80, 34,  5,163,209,104,120,250,233,167,254,
+    114,217,101,243, 55,  2,168,  2,208,  0,239,198, 63, 72, 48,222,
+    254, 64,144, 70,  1,124,192,129, 31, 18,116, 62, 17, 22,128,241,
+    230,155,111,126,  3,192,151,213,213,181, 91,211,211,211,167,132,
+    176, 78, 20, 74, 72,105,108,108, 56, 89, 88, 56,113, 61,120,119,
+    191, 26,252, 98, 31, 95,198, 31,244,205,117,131,222,  4,112, 83,
+     46,111, 61,155, 44,  0, 11,128,182,130,130,188,  5,127,252,227,
+    253, 55,117,116,116,212,  5,178, 14, 54,155,205, 62,114, 42, 10,
+    101,  8,155,205, 26,208,103,166,173,173,173,238,246,219,111,251,
+    239,194,194,137,255,  3,190,179,239, 56,124, 27,191,139,  7, 16,
+    172,183, 63, 16,162, 62,128, 97, 68,192,238,244,109,  5,208,247,
+    244,211, 79,127,153,155,155,125,145, 70,163,186,248,199, 31,247,
+    111,237,234,234,108, 98, 89,118,196,125,  5,221, 49, 26,141,134,
+    170,170, 19, 63, 61,246,216,163,127,151, 72, 36, 35,103,160, 80,
+     92,144,224,207,127,254,211, 19,199,143, 87, 30, 50, 26, 13,130,
+    155,167, 54,155,141,237,232,232,104,218,183,111,239, 54,141, 70,
+    117, 93,126,126,238,237,111,191,253,118, 41,128,  3,  0,234,193,
+    191,248, 88,184,218, 64, 72,141, 31,  8,210, 76, 64, 95, 56, 77,
+     14,  2,248,230,128,227,219,249, 35, 29,248,102,  0,200,  1,140,
+      5,144,188,102,205,154, 11, 86,174,188,113,233,132,  9, 19,166,
+     39, 37, 37,101,112, 28,160, 80, 40, 20, 44,203,218, 89,150,181,
+     89,173, 22, 99, 75, 75, 75,205,225,195,135,247,223,114,203,175,
+    191,  2, 47, 40, 61,  0,108,125,125,253, 63, 72,165,204,136, 98,
+     71,103,  2,138,  3,146,153,128, 44,107,179,107,181,241,215,  3,
+    168,  4,144,  8, 32,233,245,215,223,184,254,130, 11, 46,152,147,
+    158,158, 94, 32,151, 43,212, 12,195,200, 24,134,145, 90, 44, 22,
+     11,  0,116,119,235, 90,206,156, 57,115,244,237,183,223,250,246,
+    229,151, 95, 62, 13,126, 42,175, 14, 64, 23,134, 22,246,248, 50,
+    120, 15,183,127, 56,227,143,232,169,192,195, 65, 40,  2,238, 31,
+      6,188, 48,168,  0, 36,  1, 72,  0,160, 85,169, 84,140,201,100,
+     82,128,111, 66, 88,192,111,160,216,  3,126, 27,165,254,129,188,
+    137,125,125,134,179, 82,169,116,196, 21, 29, 84,  0,196,  1,153,
+      0,176,118,173, 54,238, 58,  0,251, 49,100,172, 82,  0, 90,240,
+    130,160,  5,160, 80, 42,149, 10,179,217,204, 96,200,157,239,  6,
+    208, 59,240, 49,195,183,177,251,109,252, 64,224,  4, 32,148,157,
+    128,  0,188, 78, 19,134,211,223,206, 70, 15,167,191,237,  3,223,
+     86,240, 19, 37,  0, 64, 98, 50,153,220, 15,239,126,209,164,224,
+    221, 44, 10,197, 31, 28, 46,186,227,219, 14,254, 37,227,  8,120,
+     35, 49,155, 61, 94,120,238,243, 94,124, 25,186,207,206,190, 96,
+    187,253,206,132, 92,  0,  0, 15, 17,  0, 92, 71,  8,156,133,192,
+    241, 55,224, 41, 12,190,112,206,195,  1, 96,105, 23,  0,197, 79,
+     28,157,212,206,  2,224,248,125,164,103,208,241,237,107, 66,156,
+    123, 90,  0,161, 53,126, 32, 76,  2,  0, 12,157,168, 15,111,192,
+    241,111,231, 97, 67, 18,227,119,198,145,206, 78,158,133, 66,113,
+    193,238,229, 67,106,160,222,222,238, 62,135,247, 66,109,248, 14,
+    194, 38,  0, 14, 94,216,149,207,  1,192, 29,151,213,184,123,  4,
+    142,111,119,129, 16, 42,  0, 20,138,191,184,143, 88,249, 35,  0,
+    190,254, 13, 32,124,134,239, 32,236,  2,224,192, 33,  4,128, 79,
+     49,240,245,111,111,184, 47, 69,166, 80,  0,  0,113,138, 76,162,
+    116, 78,205,198,225,218,242,126,225,252,172,135,155,136, 17,  0,
+    103,134,187, 64,110,226,224, 11, 95,237, 45, 10,133,144,193,199,
+    204, 47,227,143, 36, 35, 31,142,136, 20,128,225, 32,185,176,110,
+     34, 65,236,182, 37, 40,115,209, 99, 14,232, 36, 68, 74,132,193,
+     16,110,209,197,241,227,227, 94,163,242, 70,139,113,147, 16,170,
+    213,128,225,196,206,178,108,223,200,201,  0,173, 42, 47,216,117,
+    161,132, 25,210,123,108,177,152, 13,224, 23,172,197,140,177,123,
+     35,214,  5,128,  3,192,154, 76,198,179, 36,137, 83,105,212,224,
+    152,135,244, 30,119,117,117, 53,130, 31,243,119, 16,147, 66, 16,
+    235,  2,  0,  0, 86,157, 78,247, 45, 73,194,188,228, 69,193,174,
+     11, 37,204,144,222,227,227,199,143, 31, 70,144,227,242, 69,  2,
+    177, 44,  0,142,155,102,127,246,217,103, 95, 37,201,144,162,153,
+    140,236,164,  5, 65,171, 16, 37,188,100, 39, 45, 64,138,102, 50,
+     81,218,135, 30,122,104, 27, 92, 35,243,  2, 49, 40,  4,177, 44,
+      0, 14,236, 47,188,176,169,157,101, 89,143,121,195,222,152,149,
+    189,206,239, 64, 16,148,200, 69, 34, 97, 48, 43,123, 29, 81, 90,
+    139,197, 98, 41, 43, 59,162,  3, 63,159, 63,230,140,222,153,152,
+     20,  0,167, 94, 90,135,251,102,179, 88,204, 53, 36,121,147,213,
+     69,184, 40,247,193, 96, 85,141, 18, 38, 46,202,125,144, 56,222,
+    159, 94,223,221, 12,126,205,137, 99, 29,201, 96, 51, 32,150, 70,
+      0,128, 24, 21,  0, 47, 88,154,154,154,136,247, 30, 44, 30,187,
+     10,211, 50,255, 51,152,245,161,132,144,233, 89,107,  4, 69,134,
+    254,241,199, 31,191,133,143,192,156,177,134, 24,  4,128,  3, 96,
+    155, 54,237,188, 77, 54,155,173,159, 52,211,172,236,117,184,180,
+    224,113, 26, 84, 52,138,145, 75, 53,184,180,224,113, 92, 48,238,
+    110,226, 60,102,179,201,184,114,229,138, 15, 16,130,192,156,145,
+    128, 24,  4,  0,224, 39,116, 24,187,186, 58, 63, 20,146,105, 98,
+    234,207,241,139,233, 95,163,120,236, 42, 42,  4, 81,132, 92,170,
+     65,241,216, 85,248,197,244,175,  5, 71,241, 57,118,236,216, 46,
+    240, 27,120,232, 17,227,198, 15, 68,225, 76, 64,129, 56, 47, 32,
+    178,230,231,231,253,161,171, 75,183, 88,165, 82,147, 77,  8,  7,
+    160,150,167, 98, 78,222, 67,184, 48,247,  1, 52,233,247, 64,103,
+     60,133, 67, 13, 79,  5,167,182,148, 81, 49, 43,123, 29,146,213,
+     69,200, 74,156,235, 87,104,121,189,190,187, 99,222,188, 75,159,
+      5, 31,153,215,219,250,253,152, 35,214,  5,192,129, 99, 93,183,
+    225,249,231,159, 91,254,135, 63,172,223, 79,178, 67,144, 51, 82,
+    137, 28,217, 73, 11,144,157,180, 64,112,255, 64,168,119, 26,242,
+     55,248,229, 72,196,202,121,120,131,101, 89,251,175,127,253,171,
+    245,224,141,191,  3, 34,112,255,  1,241, 52,  1,128,129,190,128,
+    135, 30,122,232, 84,117,245,185, 77,225,174, 12, 37,178,216,181,
+    171,244,253, 29, 59,118,156,133,103,108,190,152, 38,102,  5,192,
+    203, 80,160,227, 99,153, 54,237,188, 71,116, 58, 93,121,216, 42,
+     71,137, 40, 26, 27, 27, 79, 46, 91,118,205, 43,224,195,114, 91,
+    225, 99,  5, 96,172, 13,  1,  2, 49, 44,  0, 62, 24,108, 10,140,
+     27,151,121,165,193,208,223, 20,238, 10, 81,194,139, 78,167,107,
+     41, 44,156,240,123,  0, 53,224,135,254,132,110,252, 17,213,196,
+    180,  0, 12,227,  5,176,  0,250, 83, 83,199,156,223,210,210,114,
+     40, 92,245,163,132,151,154,154,234,163,227,198,101,222,  6,160,
+     17,252, 94,253, 62,  3,116,196,226,219, 31,136,113,  1,240,130,
+    243, 77,181,  2,232, 27, 63, 62,255,170,253,251,247, 61,203,178,
+     54, 91,120,171, 70,  9, 21, 54,155,205,182,109,219,214, 87,167,
+     76,153,188, 30,124, 96,206, 51, 24, 33, 54, 95,172, 34, 38,  1,
+    112,223,156,145,  3,191,222,187,127,225,194,203,255,119,193,130,
+    203,102,119,119,235, 26,194, 83, 53, 74,168,232,232,232,104,154,
+     50,165,248,151, 43, 87,174,120,  9, 64,  5,188,  7,230,116, 16,
+    243, 34, 16,243,  2,224,230,186,121,115,239, 88,  0,166, 67,135,
+     14,157,203,202,202,156,189,115,231,183,127,239,233,209,119,132,
+    161,170,148, 32,162,215,119,119,124,252,241, 71,255,204,205,205,
+     94,221,208,208, 80,  5,160, 28,252,100,159, 17, 99,243,197,170,
+    251, 15,136,100, 30,192, 11,187,242, 57,167,109,194,188,221, 76,
+     59,248,200, 66,236,178,101,215,252,  5,192,166, 59,239,188,115,
+    238, 47,127,121,243,205,185,185,185,231, 37, 36, 36,166,203,229,
+    114,225, 51, 75, 40, 97,195,106,181, 88,187,187,245,109,117,117,
+     53,199, 94,124,241,197,173,111,191,253,246, 89,240, 17,121,155,
+    193,175,242, 27,206,240, 69, 97,252,128, 72,  4,  0, 24, 86,  4,
+     56,184,  6, 34,177,  3,104,217,180,105,211,167,155, 54,109,250,
+     26, 64, 10,128,132,107,175, 93, 94,116,199, 29,107,175,203,205,
+    205,155,164,213,106, 83,237,118, 78, 18, 31, 31,151,164,209,196,
+    169, 66,122, 34,  1,194,110,183,163,185,185,169, 13,  0, 50, 51,
+    179,198, 74,165,209,233, 12,246,245,245,153,123,123,123,186,165,
+     82, 41,215,219,219,219, 81, 87, 87,119,234,217,103,159,217,190,
+     99,199,142, 70,240,225,186,186,193,191,233,117,224,251,125,134,
+     11,198, 41, 42,227,  7, 68, 36,  0,  0,145, 39,  0,184,134, 33,
+    179,129, 95, 22, 42,217,186,245,179,147, 91,183,126,246, 53,248,
+    216,132,113,  0, 82,186,186,186,183,  6,187,206,193,164,176,112,
+    226, 61,  0,244,125,125,134, 79,195, 93, 23,127,145, 72, 36,220,
+    132,  9,227,127,  7,126,254,190, 13,188,209, 59,226, 67,218, 64,
+     22,151,207, 89,  0,  0,136,195,248,  1, 17,244,  1,184,227,165,
+     79,192,241,237, 30,  1,134,117,251, 88,192,139, 65, 19,248,  9,
+     35,167, 24, 70, 26,208, 24,242, 97,192,  8,224,156, 68, 34,137,
+    218,135,125, 32,244,187, 17,192,201,129, 79, 45,248,169,188,102,
+    240,  2,224,126, 31,125, 69,250, 17,157,241,  3, 34, 20,  0,192,
+    103,199,160,227,111,231,  7,195,151, 24,216,248, 79,244,  6, 31,
+     26,  8,124,225, 56,151,168,101, 64,  0,236, 24,188, 39,195, 26,
+    189,251,253,  5, 68,232,246, 59, 35,170, 38,128, 51, 94, 66,146,
+    185,223,120,231,149,132,142,111,231, 88,133,118, 73, 84, 71, 29,
+    149,  0, 67,  6, 17,237,184, 27, 58,139, 97,220,123,120,222,107,
+    209, 25,190,  3,209, 10,128,131, 17, 98, 19,194,237, 55,231,240,
+    229,177,240,192, 56, 71,188,141, 74,  6, 52,216, 91, 19,142,104,
+     76, 95,172,134,239, 64,244,  2,224,224,133, 93,249,220,218,249,
+    213,  0,  0,137,235,171,221,215,176, 97,148,123,  0,  0, 98,192,
+      3, 24,232,191,240,230,222,251,156,205, 39,118,163,119, 70,148,
+    125,  0, 35,193,249,  0, 62,198,138,163,152, 88, 59, 15,151,207,
+     48,247,145, 50,  0, 21,  0,113, 67,141, 65,228, 80,  1,160, 80,
+     68, 12, 21,  0, 10, 69,196, 80,  1,160, 80, 68, 12, 21,  0, 10,
+     69,196, 80,  1,160, 80, 68, 12, 21,  0, 10, 69,196, 80,  1,160,
+     80, 68, 12, 21,  0, 10, 69,196, 80,  1,160,196,202,100,160, 88,
+     57,143,144, 66,  5,128, 66, 17, 49, 84,  0, 40, 81,206,224,178,
+    102,138, 31, 80,  1,160, 80,227, 17, 49, 84,  0,252,135,227, 56,
+     46,138,119,211,137,141, 85,113,118,187,221,121, 57,115, 76,156,
+     83, 40,161,251,  1, 56,241,226,247,  5, 66,146,115, 28,199, 89,
+     72, 18,230, 38, 47, 70,157,110,135,127,149, 18, 72,130, 50,151,
+     40,221,128,225,176,  0,236, 28,199,177, 18,137,100,196,103, 33,
+     65,153,139, 30,115,221, 40,107, 72, 70,110,242, 98,162,116,118,
+     59,107,197,208, 14, 64, 46,  8,188,159,162,132,122,  0,254,193,
+      1, 96,173, 86, 11, 81,112,209, 68, 85,232, 30, 68,173, 42,143,
+     40,157,217,108, 49, 98, 32, 22,130,205,102,235, 11,228,177,  3,
+     65,146,122,  2, 81, 58,189, 94,223,  6,215,125, 13,169, 23, 32,
+      0, 42,  0,254, 99,211,233,116,123, 73, 18,142,141,159, 25,236,
+    186,  8, 46,171,163,163,189, 30,252,110,186,172,193,208,127, 58,
+    144,199, 14,  4,105,113,211,137,210, 85, 87,159,171,  4,191, 21,
+     56, 64,141, 95, 48, 84,  0,132,227,120,200,216,111,190,249,230,
+    109,146, 12,233,218, 18, 48,210,208,196, 15, 25,151, 56,143, 40,
+    221,241,227,149, 63,129,223, 59,223,218,222,222,190, 51,144,199,
+     30, 45,140, 84,133,116,109,  9, 81,218,127,253,235, 95,219,193,
+     71,250, 17, 85, 76,191, 64, 65,  5,192,127,236,107,215,174, 57,
+    105,183,219,173, 35, 37, 84, 48, 90,228, 39, 47,  9,122,133,146,
+    213, 69, 72, 37,124,115,222,115,207, 61, 91, 49, 16, 60,227,222,
+    123,215,191, 70,146, 39, 53,110, 58,146,213, 69,163,168, 33, 25,
+    249,201, 75,160, 96,180, 35,166,179,217,172,182,247,222,123,175,
+     14,124, 32, 16,106,244,126, 64,  5, 64,  0, 78,155, 73, 14, 70,
+     23,182, 88, 44,245, 36,121,103,140,187, 19, 82, 73,112,195, 11,
+    206,202, 94, 71,148,206, 98, 49,155,207,157, 59,215,135,129, 24,
+    121,219,183,111,215,177, 44,107, 12,100, 25,254, 34,149,200, 49,
+     99,220,157, 68,105,245,250,158, 54,240,238,191, 67,132,  7,247,
+     56,164, 27,127,146, 65,  5, 96,116, 88,123,122,244,  7, 72, 18,
+     38, 40,115, 49, 41,109,101,208, 42,146, 30, 63, 11,217, 73, 11,
+    136,210,234,116,221, 77, 24, 10,144,201,  1,176,153, 76,198,106,
+    146,188,217, 73, 11,144, 65,232,158,251,195,164,180,149,196, 35,
+     25, 13, 13,245,149,224,189,152, 88,217,220, 52,228, 80,  1,240,
+     31, 14,  0,123,244,232,209,119, 72, 51,204,206, 89,143, 20,205,
+    228,128, 87, 68, 37, 75,193,101, 19,158, 34, 78, 95, 81, 81,190,
+      7,124,176, 76,135,225, 88,218,218,218, 62, 39,205, 63,127,252,
+     70,168,100, 41,130,235, 57, 18, 41,154,201,152,157,179,158, 56,
+    253, 71, 31,125,180,  3,174,238, 63, 21,  1,129, 80,  1, 24, 29,
+    236,181,215, 46,219, 99,177, 88, 58, 73, 18, 51, 82, 21, 22, 21,
+    110, 14,168,241, 72, 37,114, 92, 81,248,  2, 52,138,116,162,244,
+     44,203,218,175,187,110,249, 59, 24, 18,  0, 59,  0,235,212,169,
+     83,158,226, 56,142, 37, 57,134, 70,145,142, 43, 10, 95,  8,104,
+    147, 70, 37, 75,193,162,194,205,196,157,165,125,125,125,250, 13,
+     27,158, 60, 10,234,  1,140, 10, 42,  0,254, 49, 24, 30, 12,128,
+    185,185,185,233, 85,210,140, 26, 69, 58,150, 77,121, 47, 32,157,
+    105,106,121, 42,150, 20,191,138, 52,  1,195,115,245,245,117, 71,
+    193,  7, 57,213,195, 53,160,134,177,167, 71, 79, 52,172,  9,  0,
+    105,241, 51,177,180,248, 13,168,229,169,  2,107,237, 73,178,186,
+      8, 87, 79,217, 66, 44, 98,  0,176,127,255,190, 47,  0,180,129,
+     31,202,  4,168, 23,224, 23, 84,  0,252,199,241,160, 89, 39, 79,
+     46,254,155,209,104, 36,154, 20,  4,  0,241,202,108, 92, 61,101,
+     11,198,143, 89,230,119,225, 25,218, 18, 44,155,242, 62,210,227,
+    103, 17,231,177, 90,173,214, 41, 83, 38, 63,  8,160, 25,158, 17,
+    116, 44,153,153, 25, 43, 89,150, 37,154,221,  8,240, 34,176,108,
+    202,251,163,234, 19, 24, 63,102, 25,174,158,178,133,184,221, 15,
+      0,221,221,186,246,229,203,175,125, 29,252,121,248,138,255, 71,
+     33,128, 10,192,232, 24,124,123,214,212, 84, 63, 39, 36,163, 92,
+    170,193,252,241, 27,113,237,148, 15,145,153, 48,135, 56, 95,178,
+    186,  8,139, 10, 55,227,170,226, 55, 16,167,200, 20, 84,217, 51,
+    103, 78, 31,  0,239,250, 55,193, 51,146, 14, 11,192,168,215,119,
+    127, 33,228,152,113,138, 76, 92, 85,252,  6, 22, 21,110, 22,228,
+    213,100, 38,204,193,178, 41,239, 97,254,248,141,144, 75, 53, 66,
+    138,196,142, 29, 59, 62,  0,127, 14,206,238, 63, 53,126, 63,144,
+      4, 58, 82,146,209,104, 22,148,254,213,131,197,  1, 45, 63, 20,
+     12,  4, 18,117,254, 48,  0,226,154,154,154,119, 36, 37, 37, 95,
+    224,207, 49, 13,150, 86,212,117,239, 68,107,239, 65,244,152,107,
+     97,182,233,193,218, 77,208, 40,210,161,145,167, 35, 45,110, 58,
+    242, 82,150,248, 61,173,184,187,187,187, 61, 43, 43,227, 22,  0,
+     39, 49,228,  1, 56,135,201,150,128, 95, 27, 18,223,209,209,121,
+     68,163,137, 27,231, 79, 57,122, 83, 53,106,187,182,163,189,191,
+      2,  6,107, 43, 12,150, 86, 48, 82, 21,148,178, 68, 36, 40,243,
+     48, 86, 59, 11,121, 73,139,  4,185,251,206, 52, 52,212,159, 40,
+     42, 42,252, 61,128, 35,224, 59,  0, 61,226,  1, 70,211, 16,224,
+    173, 37, 85,126,229, 83,171,149,  1, 41,159, 46,  6,242, 31,231,
+    135,204, 14,192,152,149,149,121,117,119,119,207, 41,133, 66,145,
+     32,244, 96, 26, 69, 58,138,199,174, 66,241,216, 85,129,171,225,
+      0, 44,203,178, 55,222,184,226, 30,240,111,205, 22,248,142,160,
+    107,  3,208,255,224,131,127,188,114,227,198,167,202,165, 82,169,
+    224,231, 35, 81, 85,128,233, 89,107,  2, 81,109, 15,140, 70, 67,
+     95, 81, 81,225, 61,  0,106,193,247, 99,196, 98,188,198,144, 18,
+    118, 15, 32, 26,209,104, 84,142,176,192,206, 94,128, 20,128,226,
+    139, 47,190, 92,118,217,101, 11,222,150, 68, 80,232,224,119,222,
+    121,251,249,213,171,111,123, 15, 64, 57,248, 73, 51,238,111,127,
+      7,142,243, 80,238,221,187,111,237,204,153,231, 63, 17,218,154,
+    250,198,110,183,115, 79, 62,249,196, 67,143, 60,242,240,151,  0,
+     78, 96, 96, 37, 35,188, 68,  3, 54, 24, 76, 49, 47,  4,129,242,
+      0,168,  0,248,137, 23, 17,144, 14,124,212,223,127,191,123,205,
+    236,217, 37,143,135,173,114, 78,236,219,183,119,235, 21, 87, 44,
+    124, 30, 64,  5,124,184,204,  3, 73,221,155, 52,234,163, 71, 43,
+    159,153, 48, 97,194,175, 67, 95,107, 79,  6, 68,236, 35,  0, 71,
+      1, 24, 48,180,  4,216,197,147, 17,131,241,  3,129, 19,  0,218,
+      9, 24, 56, 28, 15,163,105,254,252,121,255,119,232,208, 79,127,
+      9,119, 40,234, 31,127,220,255,197, 21, 87, 44,252,  7,128, 42,
+     12,205,252,243,102,252,112,251,141,  5, 96,156, 54,109,234,186,
+    211,167, 79,189, 25,218, 90,187,194,113, 28,247,233,167,159,188,
+    188,122,245,109, 91,193,191,249, 13,240,222,124,161,248,  1, 21,
+      0, 63,113,122,211,184,183, 65,237,  0,140,243,230, 93,250,247,
+     47,190,216,246, 59,179,217,108,  8,117,221,108, 54, 27,251,238,
+    187, 91, 54, 95,126,249,130,103,193, 27, 77, 39, 60, 13,127,164,
+    143, 29,128, 97,198,140,233,119,237,221,187,231, 17,150,101,137,
+     38,  9,  5, 18,163,209,104,120,250,233,191, 63,186,106,213, 77,
+    111,130,127,243, 59, 60, 24,175,109,127,177,188,253,  3,  9,109,
+      2,140,  2,167,102,  0,224,217, 31, 32,  5, 32,  7,144, 86, 83,
+     83,187,109,236,216,244,192,207,  1,246, 66,119,183,174,117,233,
+    210,171,254, 80, 94, 94, 94,  7,224, 20, 60,223,252,206,109,127,
+    111,125,  0,142,111, 41,134,154,  3,202,235,175,191,126,226,166,
+     77, 47,110, 77, 72, 72,200,  8,246, 57,  0, 64, 99, 99,195,233,
+    194,194,137,235,192, 79,246,169,  6,191,232,199,155,219, 63,120,
+     14, 98, 18,  0,218,  7, 16, 33,184,245,  5, 56,190,157, 13, 72,
+      6, 32,110,207,158,189,127,157, 52,169,120,133, 70,163, 17, 60,
+     66, 64,130,197, 98,177,158, 58,117,114,255,133, 23,150,252, 13,
+     64, 35,120,163,177,129,220,248, 29,120, 19,  1,135,152,197, 31,
+     59, 86,249,122, 78, 78,238,124,185, 92,174,  8,198,121,244,247,
+    247,245, 28, 56,112,224,171,107,174,185,250, 21,240,189,253,117,
+     62,206,193,229, 60,196,100,252,  0, 21,128,136,193,139, 23,224,
+    248,118,127,139, 42,  0,100,148,149,149, 63, 91, 80, 80,176, 64,
+     46, 87,  4,196,128, 88,150,181,215,213,213, 30,157, 58,117,202,
+    159, 48, 52,201,167, 21,158,237,125,199,230,153, 94,223,154, 62,
+    188, 25, 56,157,131, 67,204,148,  0,178, 79,157, 58,253,114, 86,
+    214,184,243,165, 82,105, 64,154,145, 22,139,197, 82, 85,117, 98,
+    223,156, 57, 23,109,  0,255,214,111, 30, 56,159, 17,141,223,249,
+     60,196,  2, 21,128,  8, 98,  4, 17,112,247,  6, 20,  0, 50, 55,
+    109,218,116,253,194,133, 87,252, 34, 45,109,236,120,181, 90,173,
+     21, 50,106,104, 54,155, 76, 93, 93, 93, 77,165,165,223,125,181,
+    122,245,234,175,192,187,249,205,224,223,252,190, 12,102, 68,163,
+     17,120, 30, 74,  0,121,111,190,249,230,175, 46,185,100,238,181,
+    201,201, 41,227,148, 74, 37,241,182, 71, 28,199,193,104, 52,244,
+    182,181,181,215,110,219,246,217,231,247,222,123,239,247,  0,186,
+    193,207, 83,104, 27,205,121,136,  1, 42,  0, 17,  6,129,241,184,
+    247, 15,200,  0,196,  3, 72,  3,144,244,194, 11, 47, 46,187,248,
+    226,139,231,101,102,102, 77, 84,169, 84,241, 12,195,200, 36, 18,
+    137,196,102,179, 89,173, 86,139,177,189,189,163,161,188,188,236,
+    224,218,181,107,182,117,119,119,155,192,119,136,117,  3,232,128,
+    235,218,126,159,157,100, 32, 48, 26, 63,206, 67, 14, 32,  9, 64,
+     90, 90, 90, 90,202, 63,254,177,233,198, 25, 51,102,148,140, 25,
+     51, 38, 71, 46, 87,168,101, 50,153,156,227, 56,142,101, 89,155,
+    201,100,236,107,106,106, 58,179,111,223,190,125,119,222,121,199,
+     78,240,115, 18,116,224,167,244,118,194,181,135,127, 84,231, 17,
+    235, 80,  1,136, 64,220,140,  7,112,117,165,125, 25,145,227,155,
+      1,144,224,244, 81, 13,252,  6,240,187,247,154,193, 27,189, 30,
+    174,  6,239,236,226, 15,103, 44,196, 70, 51,140,  8,120, 59, 15,
+    169,219,223, 14, 65, 72,  0, 47,112,138,129,243,224,  6,206,195,
+    232,116, 30,253,  4,231,224,247,121,196, 50, 84,  0, 34, 20, 31,
+     34,224,254,237,237,  3, 47,127, 59,227,205, 16,134, 51, 22,175,
+    157,125,164, 70, 51,194,121,144,158,203, 72,231, 65,122, 46,126,
+    159, 71,172, 66,  5, 32,194, 33, 20,  2,199,247, 72, 70,227,192,
+    151, 81,140,104, 48,128,112,163,241,114, 14,222,234, 56, 92,253,
+    133,156,199,112,231, 54,136,216, 13,223,  1, 21,128, 40, 96,  4,
+      3,114,254,219,219,111,190,224,188,252,237,237,183, 65, 70,107,
+     52,177,114, 30,177,  4, 21,128, 40,131,192,136,134,251,205, 25,
+    111, 55,204,235, 77, 12,180,193,248, 56,  7, 32,136,231, 65,141,
+    222, 59, 84,  0,162,152, 97, 12,105,212,132,202, 96, 98,225, 28,
+    162, 25, 42,  0, 49,202, 72,134, 21, 45,198, 17, 43,231, 17,169,
+      4, 74,  0,254, 63, 34, 58,182, 52, 79,174,223,189,  0,  0,  0,
+      0, 73, 69, 78, 68,174, 66, 96,130
+};
+#endif /* CONFIG_DARWIN */
+
+static const unsigned char _data_android_icon_32_png[1321] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 32,  0,  0,  0, 32,  8,  6,  0,  0,  0,115,122,122,
+    244,  0,  0,  4,240, 73, 68, 65, 84, 88,133,237, 87, 91,108, 20,
+     85, 24,254,102,167, 51,237,238, 78,183,237,178,180,182,213,165,
+    110, 98, 35, 18,181, 41, 41, 18,185, 52,  1,251,162,  4, 98,212,
+    180, 38, 74,170,132,  7,170, 38, 26, 31, 20,130, 24,130,196,128,
+    190, 24,181, 38,120, 71,177, 82,110,106,212,  4,154,  0, 69,170,
+     98, 85,104,161,  8,180,118,183, 40,236,165,179,219,153,189,204,
+    206,238,204,244,248, 80,102,178,211,237,110,161, 53,242,226,151,
+     76,246, 59,255,249,207,255,127,123,230,159,115,161,  8, 33,  0,
+      0,138,162,144,  7, 84, 40,196, 47,103, 89,118,105, 58,157, 62,
+     89, 94,238, 58, 33, 73, 50,201,116,176,217,138, 44, 60, 31,121,
+    154, 97,152, 37,138,162,244,184, 92,206,143, 36, 73, 30,159, 28,
+    200,102, 43, 50, 56, 33,  4,212,117,  8,160, 68, 49,246,  5,195,
+     48, 45, 25,  3,131,137, 68,226, 89,139,197, 82, 14,  0,170,170,
+    122,237,118,251, 86,154,166, 27,116, 31, 77,211,122,139,139,237,
+    139,219,187,107, 76, 34,218, 26,125, 55, 38, 32, 20,226, 27, 57,
+    142, 59,158, 75, 93, 62, 72,146,180,190,115,160,254,131,124,  2,
+     44,211,  5, 97, 89,118,169,206,207,250,119, 33,154,186,156,215,
+    191,199,183,217,224,170,170,172,152, 46,254,180,  2,100, 89,246,
+    233,252,206,138, 39,208,119,229, 93, 16,162,129, 79,244,227,210,
+    104, 39, 46,141,118,226,106,244, 71,164, 84, 17,231,131,187,225,
+    113,174, 50,198,198,227,  9,170,173,209,151,183,184, 10,242,117,
+    242,124,228, 65,155,205,246,185,222, 38, 68, 67, 74, 19,241,217,
+    111,117, 24, 39, 74,150, 63, 87,120, 43, 86,222,209,110,180,171,
+    170,170, 90,  4, 33, 58, 84, 90,234,216,210,222, 93, 67,178,  6,
+     32,255, 12, 80, 69, 69, 69, 70, 52, 33, 57,132,175,206,173,194,
+     85,177,  7, 52,197, 76, 57, 64,213, 36,124, 51,240, 48, 46,132,
+     58, 12, 27,203,178,155, 71, 71,195, 15,228, 74, 50,165,  0, 66,
+      8, 34, 17,225,113,139,197, 50, 15,  0,164,116, 16,135, 47,182,
+     66, 74,  7,177,110, 73, 63,230, 87, 54,103,141,185,171, 98, 45,
+     30,187,247, 40,  8,209,240,243,200, 86,248,198, 14, 27,125,133,
+    133,133,175,111, 88,238,197,134,229,222,172,113, 89,175, 96,120,
+    216,231, 22,132,104, 27, 77,211,166,226, 75, 42, 60,  0,224,148,
+    119, 39, 46,  6, 15,101,  5,250, 51,252, 53, 34,210, 31, 70,251,
+    212,200, 54,184, 75, 87,192, 66, 49, 40, 40, 40, 88, 40,  8,209,
+    183,210,233,116, 23,128,239,  0, 24,175,195,244, 25, 10, 66,116,
+     19,203,178,219, 39,  7,239,236,107,132,148, 14, 78, 53, 89,121,
+    241,208,252, 47, 49,151,171, 51,217, 84, 85,253,214,225,224, 86,
+      3, 32,166,117, 32, 28, 30,107,176,217,108,191,220,112,150, 25,
+     64,150,229, 86,167,179,244, 83,147,128,104, 52,254, 49,195, 48,
+    173,  0,240,183,112, 28,231,  2, 31,162,204, 90,139,  6,247,203,
+    216,253,235,221, 51, 74,212,218,112,  1,222,200,247, 24,  8,124,
+    130,170,146,251, 81, 95,253, 60,  0,128, 16,146,176,219,173,197,
+    132, 16, 98, 20, 33, 77,211, 75,116,222,251,215, 14,  4, 98,189,
+    240,199,126,130, 37, 71,197, 95, 47,250,174,190,  3, 62,209,143,
+    203, 99, 71, 12, 27, 69, 81,246, 64, 32, 88, 15,152,191,  2,171,
+     78, 68,121,162, 90,133,228,240,172,146,103,198,152, 28,203,239,
+     15,184, 38, 11,248, 79,145, 74,201,244, 77, 21,160,105, 26,117,
+     83,  5,232,248, 95, 64,166,128,164, 78, 74,173, 30,211,239,108,
+    144, 43,150, 36, 37,227, 38,  1,154,166,245,232,188,225,182,141,
+    168,116, 44, 70,117, 73, 35,  8,209,102, 37,160,190,250,  5, 84,
+    112, 11,225,113,174, 54,108,170,170,202, 77, 77, 43, 79,  3, 25,
+    155,145,162, 40,237,250, 74, 88, 93,178, 12,213, 37,203,102,149,
+     88,135,187,172,  9,238,178, 38,147,237,216,177,163, 59,  0,152,
+    103,192,229,114,246,170,170,250,210,191,146, 53, 15,188, 94,239,
+    137, 53,107, 86,191,135,107, 59,162,169,  8, 29, 14,110,231,249,
+    243,  3,110, 65, 24,123, 67, 16,198,250,117,251, 76,102,195,237,
+    108, 52,181,207,156, 57,189,119,207,158, 61,235, 23, 44,152,223,
+     12, 32,172,219,167, 58, 21, 83,152,184,  7,188,202,113,220, 22,
+    221, 40,202, 94, 12,241,135,112,214,191, 11,  0, 96,101, 92,104,
+    174, 59,  9,  0, 56,233,221,136, 33,126,226,140, 80,193, 45,196,
+     34,247, 38,148,217,106,141,125, 36,153, 76,134,231,204, 41,171,
+      5, 16,  3, 48,126,237, 33,185, 78,197,  4,192,120, 60, 30,127,
+    155, 16,114, 69, 55,150, 20,221, 14, 59, 91,105, 56,101,110, 82,
+     86,102,174,193,153,130, 98,204,177, 47, 48,245,119,117, 29,217,
+     14, 64,108,239,174, 81,  0,104,200, 56,144,228, 92,  7, 60,158,
+     26, 62, 24, 12,214,137,162,240, 90, 46,159,233, 16, 10,133,134,
+     59, 58, 58,158,107,105,105,126, 31, 19,255, 58, 11,121, 23, 34,
+    143,167,134,103, 24,214,111, 56, 83,180,209, 71,153,120,206, 48,
+    193,117,235,158,218,  5, 64,154,201,169, 24,  0, 64,  8, 49,214,
+    135,185, 92, 29,104,203,196,221,174,210,177,216,240,185,165,120,
+    145,193, 43,139,239, 51,120, 32,224,255, 29,128, 50,249,122,150,
+    137,235,186, 27, 38, 18, 73,129,162, 40,  7,  0,164, 84, 17,177,
+    212,  8, 92,246,123, 76, 78, 73,133,135,172, 70, 80,102,173, 53,
+    108,251,247,239,123, 36, 62,239,149,131,153,126, 55,124, 53,  3,
+     64, 82,169,212, 51,122,163,176,160, 36, 43, 57, 48,241, 85,100,
+     38, 31, 28, 28,236, 90,187,246,201,227,211, 71, 39,  4,250, 44,
+    228,  1, 21, 14,143, 61, 42,138,177,139,146, 36, 19, 73,146, 73,
+     60, 46, 41,  7, 14, 28,124,179,187,251,135, 23,  5, 33,234,211,
+    237,162, 24, 75,236,221,219,185, 13, 64, 57,  0, 90,146,100,100,
+     62,147,115,255,  3, 56,204, 60,122,104, 43, 57,236,  0,  0,  0,
+      0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+
+static const FileEntry  _file_entries[] =
+{
+
+    { "android_icon_16.png", _data_android_icon_16_png, 460 },
+#ifdef CONFIG_DARWIN
+    { "android_icon_256.png", _data_android_icon_256_png, 13369 },
+#endif
+    { "android_icon_32.png", _data_android_icon_32_png, 1321 },
+    { NULL, NULL, 0 }
+};
diff --git a/android/main.c b/android/main.c
new file mode 100644
index 0000000..c366a9e
--- /dev/null
+++ b/android/main.c
@@ -0,0 +1,2836 @@
+/* Copyright (C) 2006-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/time.h>
+#ifdef _WIN32
+#include <process.h>
+#endif
+#include "libslirp.h"
+#include "sockets.h"
+
+#include "android/android.h"
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "console.h"
+
+#include <SDL.h>
+#include <SDL_syswm.h>
+
+#include "math.h"
+
+#include "android/charmap.h"
+#include "modem_driver.h"
+#include "shaper.h"
+#include "proxy_http.h"
+
+#include "android/utils/debug.h"
+#include "android/resource.h"
+#include "android/config.h"
+#include "android/config/config.h"
+
+#include "android/skin/image.h"
+#include "android/skin/trackball.h"
+#include "android/skin/keyboard.h"
+#include "android/skin/file.h"
+#include "android/skin/window.h"
+#include "android/skin/keyset.h"
+
+#include "android/gps.h"
+#include "android/qemud.h"
+#include "android/hw-kmsg.h"
+#include "android/hw-control.h"
+#include "android/user-config.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/dirscanner.h"
+#include "android/utils/path.h"
+#include "android/utils/timezone.h"
+#include "android/utils/display.h"
+
+#include "android/cmdline-option.h"
+#include "android/help.h"
+#include "hw/goldfish_nand.h"
+
+#include "android/globals.h"
+#include "tcpdump.h"
+
+/* in vl.c */
+extern void  qemu_help(int  code);
+
+#include "framebuffer.h"
+AndroidRotation  android_framebuffer_rotation;
+
+#define  STRINGIFY(x)   _STRINGIFY(x)
+#define  _STRINGIFY(x)  #x
+
+#define  VERSION_STRING  STRINGIFY(ANDROID_VERSION_MAJOR)"."STRINGIFY(ANDROID_VERSION_MINOR)
+
+#define  KEYSET_FILE    "default.keyset"
+SkinKeyset*      android_keyset;
+
+#define  D(...)  do {  if (VERBOSE_CHECK(init)) dprint(__VA_ARGS__); } while (0)
+
+extern int  control_console_start( int  port );  /* in control.c */
+
+extern int qemu_milli_needed;
+
+/* the default device DPI if none is specified by the skin
+ */
+#define  DEFAULT_DEVICE_DPI  165
+
+static const AKeyCharmap*  android_charmap;
+
+int    android_base_port;
+
+#if 0
+static int  opts->flashkeys;      /* forward */
+#endif
+
+static void  handle_key_command( void*  opaque, SkinKeyCommand  command, int  param );
+
+#ifdef CONFIG_TRACE
+extern void  start_tracing(void);
+extern void  stop_tracing(void);
+#endif
+
+unsigned long   android_verbose;
+
+int   qemu_cpu_delay = 0;
+int   qemu_cpu_delay_count;
+
+/***********************************************************************/
+/***********************************************************************/
+/*****                                                             *****/
+/*****            U T I L I T Y   R O U T I N E S                  *****/
+/*****                                                             *****/
+/***********************************************************************/
+/***********************************************************************/
+
+/*** APPLICATION DIRECTORY
+ *** Where are we ?
+ ***/
+
+const char*  get_app_dir(void)
+{
+    char  buffer[1024];
+    char* p   = buffer;
+    char* end = p + sizeof(buffer);
+    p = bufprint_app_dir(p, end);
+    if (p >= end)
+        return NULL;
+
+    return strdup(buffer);
+}
+
+/***  CONFIGURATION
+ ***/
+
+static AUserConfig*  userConfig;
+
+void
+emulator_config_init( void )
+{
+    userConfig = auserConfig_new( android_avdInfo );
+}
+
+/* only call this function on normal exits, so that ^C doesn't save the configuration */
+void
+emulator_config_done( void )
+{
+    int  win_x, win_y;
+
+    if (!userConfig) {
+        D("no user configuration?");
+        return;
+    }
+
+    SDL_WM_GetPos( &win_x, &win_y );
+    auserConfig_setWindowPos(userConfig, win_x, win_y);
+    auserConfig_save(userConfig);
+}
+
+void *loadpng(const char *fn, unsigned *_width, unsigned *_height);
+void *readpng(const unsigned char*  base, size_t  size, unsigned *_width, unsigned *_height);
+
+#ifdef CONFIG_DARWIN
+#  define  ANDROID_ICON_PNG  "android_icon_256.png"
+#else
+#  define  ANDROID_ICON_PNG  "android_icon_16.png"
+#endif
+
+static void
+sdl_set_window_icon( void )
+{
+    static int  window_icon_set;
+
+    if (!window_icon_set)
+    {
+#ifdef _WIN32
+        HANDLE         handle = GetModuleHandle( NULL );
+        HICON          icon   = LoadIcon( handle, MAKEINTRESOURCE(1) );
+        SDL_SysWMinfo  wminfo;
+
+        SDL_GetWMInfo(&wminfo);
+
+        SetClassLong( wminfo.window, GCL_HICON, (LONG)icon );
+#else  /* !_WIN32 */
+        unsigned              icon_w, icon_h;
+        size_t                icon_bytes;
+        const unsigned char*  icon_data;
+        void*                 icon_pixels;
+
+        window_icon_set = 1;
+
+        icon_data = android_icon_find( ANDROID_ICON_PNG, &icon_bytes );
+        if ( !icon_data )
+            return;
+
+        icon_pixels = readpng( icon_data, icon_bytes, &icon_w, &icon_h );
+        if ( !icon_pixels )
+            return;
+
+       /* the data is loaded into memory as RGBA bytes by libpng. we want to manage
+        * the values as 32-bit ARGB pixels, so swap the bytes accordingly depending
+        * on our CPU endianess
+        */
+        {
+            unsigned*  d     = icon_pixels;
+            unsigned*  d_end = d + icon_w*icon_h;
+
+            for ( ; d < d_end; d++ ) {
+                unsigned  pix = d[0];
+#if WORDS_BIGENDIAN
+                /* R,G,B,A read as RGBA => ARGB */
+                pix = ((pix >> 8) & 0xffffff) | (pix << 24);
+#else
+                /* R,G,B,A read as ABGR => ARGB */
+                pix = (pix & 0xff00ff00) | ((pix >> 16) & 0xff) | ((pix & 0xff) << 16);
+#endif
+                d[0] = pix;
+            }
+        }
+
+        SDL_Surface* icon = sdl_surface_from_argb32( icon_pixels, icon_w, icon_h );
+        if (icon != NULL) {
+            SDL_WM_SetIcon(icon, NULL);
+            SDL_FreeSurface(icon);
+            free( icon_pixels );
+        }
+#endif	/* !_WIN32 */
+    }
+}
+
+
+/***********************************************************************/
+/***********************************************************************/
+/*****                                                             *****/
+/*****            S K I N   I M A G E S                            *****/
+/*****                                                             *****/
+/***********************************************************************/
+/***********************************************************************/
+
+void send_key_event(unsigned code, unsigned down)
+{
+    if(code == 0) {
+        return;
+    }
+    if (VERBOSE_CHECK(keys))
+        printf(">> KEY [0x%03x,%s]\n", (code & 0x1ff), down ? "down" : " up " );
+    kbd_put_keycode((code & 0x1ff) | (down ? 0x200 : 0));
+}
+
+
+
+typedef struct {
+    AConfig*       aconfig;
+    SkinFile*      layout_file;
+    SkinLayout*    layout;
+    SkinKeyboard*  keyboard;
+    SkinWindow*    window;
+    int            win_x;
+    int            win_y;
+    int            show_trackball;
+    SkinTrackBall* trackball;
+    int            lcd_brightness;
+    SkinImage*     onion;
+    SkinRotation   onion_rotation;
+    int            onion_alpha;
+
+    AndroidOptions  opts[1];  /* copy of options */
+} QEmulator;
+
+static QEmulator   qemulator[1];
+
+static void
+qemulator_done( QEmulator*  emulator )
+{
+    if (emulator->window) {
+        skin_window_free(emulator->window);
+        emulator->window = NULL;
+    }
+    if (emulator->trackball) {
+        skin_trackball_destroy(emulator->trackball);
+        emulator->trackball = NULL;
+    }
+    if (emulator->keyboard) {
+        skin_keyboard_free(emulator->keyboard);
+        emulator->keyboard = NULL;
+    }
+    emulator->layout = NULL;
+    if (emulator->layout_file) {
+        skin_file_free(emulator->layout_file);
+        emulator->layout_file = NULL;
+    }
+}
+
+
+static void
+qemulator_setup( QEmulator*  emulator );
+
+static void
+qemulator_fb_update( void*   _emulator, int  x, int  y, int  w, int  h )
+{
+    QEmulator*  emulator = _emulator;
+
+    if (emulator->window)
+        skin_window_update_display( emulator->window, x, y, w, h );
+}
+
+static void
+qemulator_fb_rotate( void*  _emulator, int  rotation )
+{
+    QEmulator*     emulator = _emulator;
+
+    qemulator_setup( emulator );
+}
+
+
+
+static int
+qemulator_init( QEmulator*       emulator,
+                AConfig*         aconfig,
+                const char*      basepath,
+                int              x,
+                int              y,
+                AndroidOptions*  opts )
+{
+    emulator->aconfig     = aconfig;
+    emulator->layout_file = skin_file_create_from_aconfig(aconfig, basepath);
+    emulator->layout      = emulator->layout_file->layouts;
+    emulator->keyboard    = skin_keyboard_create_from_aconfig(aconfig, opts->raw_keys);
+    emulator->window      = NULL;
+    emulator->win_x       = x;
+    emulator->win_y       = y;
+    emulator->opts[0]     = opts[0];
+
+    /* register as a framebuffer clients for all displays defined in the skin file */
+    SKIN_FILE_LOOP_PARTS( emulator->layout_file, part )
+        SkinDisplay*  disp = part->display;
+        if (disp->valid) {
+            qframebuffer_add_client( disp->qfbuff,
+                                        emulator,
+                                        qemulator_fb_update,
+                                        qemulator_fb_rotate,
+                                        NULL );
+        }
+    SKIN_FILE_LOOP_END_PARTS
+    return 0;
+}
+
+
+static AndroidKeyCode
+qemulator_rotate_keycode( QEmulator*      emulator,
+                          AndroidKeyCode  sym )
+{
+    switch (skin_layout_get_dpad_rotation(emulator->layout)) {
+        case SKIN_ROTATION_90:
+            switch (sym) {
+                case kKeyCodeDpadLeft:  sym = kKeyCodeDpadDown; break;
+                case kKeyCodeDpadRight: sym = kKeyCodeDpadUp; break;
+                case kKeyCodeDpadUp:    sym = kKeyCodeDpadLeft; break;
+                case kKeyCodeDpadDown:  sym = kKeyCodeDpadRight; break;
+                default: ;
+            }
+            break;
+
+        case SKIN_ROTATION_180:
+            switch (sym) {
+                case kKeyCodeDpadLeft:  sym = kKeyCodeDpadRight; break;
+                case kKeyCodeDpadRight: sym = kKeyCodeDpadLeft; break;
+                case kKeyCodeDpadUp:    sym = kKeyCodeDpadDown; break;
+                case kKeyCodeDpadDown:  sym = kKeyCodeDpadUp; break;
+                default: ;
+            }
+            break;
+
+        case SKIN_ROTATION_270:
+            switch (sym) {
+                case kKeyCodeDpadLeft:  sym = kKeyCodeDpadUp; break;
+                case kKeyCodeDpadRight: sym = kKeyCodeDpadDown; break;
+                case kKeyCodeDpadUp:    sym = kKeyCodeDpadRight; break;
+                case kKeyCodeDpadDown:  sym = kKeyCodeDpadLeft; break;
+                default: ;
+            }
+            break;
+
+        default: ;
+    }
+    return sym;
+}
+
+static int
+get_device_dpi( AndroidOptions*  opts )
+{
+    int    dpi_device  = DEFAULT_DEVICE_DPI;
+
+    if (opts->dpi_device != NULL) {
+        char*  end;
+        dpi_device = strtol( opts->dpi_device, &end, 0 );
+        if (end == NULL || *end != 0 || dpi_device <= 0) {
+            fprintf(stderr, "argument for -dpi-device must be a positive integer. Aborting\n" );
+            exit(1);
+        }
+    }
+    return  dpi_device;
+}
+
+static double
+get_default_scale( AndroidOptions*  opts )
+{
+    int     dpi_device  = get_device_dpi( opts );
+    int     dpi_monitor = -1;
+    double  scale       = 0.0;
+
+    /* possible values for the 'scale' option are
+     *   'auto'        : try to determine the scale automatically
+     *   '<number>dpi' : indicates the host monitor dpi, compute scale accordingly
+     *   '<fraction>'  : use direct scale coefficient
+     */
+
+    if (opts->scale) {
+        if (!strcmp(opts->scale, "auto"))
+        {
+            /* we need to get the host dpi resolution ? */
+            int   xdpi, ydpi;
+
+            if ( get_monitor_resolution( &xdpi, &ydpi ) < 0 ) {
+                fprintf(stderr, "could not get monitor DPI resolution from system. please use -dpi-monitor to specify one\n" );
+                exit(1);
+            }
+            D( "system reported monitor resolutions: xdpi=%d ydpi=%d\n", xdpi, ydpi);
+            dpi_monitor = (xdpi + ydpi+1)/2;
+        }
+        else
+        {
+            char*   end;
+            scale = strtod( opts->scale, &end );
+
+            if (end && end[0] == 'd' && end[1] == 'p' && end[2] == 'i' && end[3] == 0) {
+                if ( scale < 20 || scale > 1000 ) {
+                    fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
+                            "host dpi number must be between 20 and 1000" );
+                    exit(1);
+                }
+                dpi_monitor = scale;
+                scale       = 0.0;
+            }
+            else if (end == NULL || *end != 0) {
+                fprintf(stderr, "emulator: ignoring bad -scale argument '%s': %s\n", opts->scale,
+                        "not a number or the 'auto' keyword" );
+                exit(1);
+            }
+            else if ( scale < 0.1 || scale > 3. ) {
+                fprintf(stderr, "emulator: ignoring bad -window-scale argument '%s': %s\n", opts->scale,
+                        "must be between 0.1 and 3.0" );
+                exit(1);
+            }
+        }
+    }
+
+    if (scale == 0.0 && dpi_monitor > 0)
+        scale = dpi_monitor*1.0/dpi_device;
+
+    if (scale == 0.0)
+        scale = 1.0;
+
+    return scale;
+}
+
+void
+android_emulator_set_window_scale( double  scale, int  is_dpi )
+{
+    QEmulator*  emulator = qemulator;
+
+    if (is_dpi)
+        scale /= get_device_dpi( emulator->opts );
+
+    if (emulator->window)
+        skin_window_set_scale( emulator->window, scale );
+}
+
+
+static void
+qemulator_set_title( QEmulator*  emulator )
+{
+    char  temp[64];
+
+    if (emulator->window == NULL)
+        return;
+
+    snprintf( temp, sizeof(temp), "Android Emulator (%s:%d)",
+              avdInfo_getName( android_avdInfo ),
+              android_base_port );
+
+    skin_window_set_title( emulator->window, temp );
+}
+
+/* called by the emulated framebuffer device each time the content of the
+ * framebuffer has changed. the rectangle is the bounding box of all changes
+ */
+static void
+sdl_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    /* this function is being called from the console code that is currently inactive
+    ** simple totally ignore it...
+    */
+    (void)ds;
+    (void)x;
+    (void)y;
+    (void)w;
+    (void)h;
+}
+
+
+
+static void
+qemulator_light_brightness( void* opaque, const char*  light, int  value )
+{
+    QEmulator*  emulator = opaque;
+
+    VERBOSE_PRINT(hw_control,"%s: light='%s' value=%d window=%p", __FUNCTION__, light, value, emulator->window);
+    if ( !strcmp(light, "lcd_backlight") ) {
+        emulator->lcd_brightness = value;
+        if (emulator->window)
+            skin_window_set_lcd_brightness( emulator->window, value );
+        return;
+    }
+}
+
+
+static void
+qemulator_setup( QEmulator*  emulator )
+{
+    AndroidOptions*  opts = emulator->opts;
+
+    if ( !emulator->window && !opts->no_window ) {
+        SkinLayout*  layout = emulator->layout;
+        double       scale  = get_default_scale(emulator->opts);
+
+        emulator->window = skin_window_create( layout, emulator->win_x, emulator->win_y, scale, 0);
+        if (emulator->window == NULL)
+            return;
+
+        {
+            SkinTrackBall*           ball;
+            SkinTrackBallParameters  params;
+
+            params.diameter   = 30;
+            params.ring       = 2;
+            params.ball_color = 0xffe0e0e0;
+            params.dot_color  = 0xff202020;
+            params.ring_color = 0xff000000;
+
+            ball = skin_trackball_create( &params );
+            emulator->trackball = ball;
+            skin_window_set_trackball( emulator->window, ball );
+
+            emulator->lcd_brightness = 128;  /* 50% */
+            skin_window_set_lcd_brightness( emulator->window, emulator->lcd_brightness );
+        }
+
+        if ( emulator->onion != NULL )
+            skin_window_set_onion( emulator->window,
+                                   emulator->onion,
+                                   emulator->onion_rotation,
+                                   emulator->onion_alpha );
+
+        qemulator_set_title( emulator );
+
+        skin_window_enable_touch ( emulator->window, android_hw->hw_touchScreen != 0 );
+        skin_window_enable_dpad  ( emulator->window, android_hw->hw_dPad != 0 );
+        skin_window_enable_qwerty( emulator->window, android_hw->hw_keyboard != 0 );
+        skin_window_enable_trackball( emulator->window, android_hw->hw_trackBall != 0 );
+    }
+
+    /* initialize hardware control support */
+    {
+        AndroidHwControlFuncs  funcs;
+
+        funcs.light_brightness = qemulator_light_brightness;
+        android_hw_control_init( emulator, &funcs );
+    }
+}
+
+
+/* called by the emulated framebuffer device each time the framebuffer
+ * is resized or rotated */
+static void
+sdl_resize(DisplayState *ds, int w, int h)
+{
+    fprintf(stderr, "weird, sdl_resize being called with framebuffer interface\n");
+    exit(1);
+}
+
+
+static void sdl_refresh(DisplayState *ds)
+{
+    QEmulator*     emulator = ds->opaque;
+    SDL_Event      ev;
+    SkinWindow*    window   = emulator->window;
+    SkinKeyboard*  keyboard = emulator->keyboard;
+
+   /* this will eventually call sdl_update if the content of the VGA framebuffer
+    * has changed */
+    qframebuffer_check_updates();
+
+    if (window == NULL)
+        return;
+
+    while(SDL_PollEvent(&ev)){
+        switch(ev.type){
+        case SDL_VIDEOEXPOSE:
+            skin_window_redraw( window, NULL );
+            break;
+
+        case SDL_KEYDOWN:
+#ifdef _WIN32
+            /* special code to deal with Alt-F4 properly */
+            if (ev.key.keysym.sym == SDLK_F4 &&
+                ev.key.keysym.mod & KMOD_ALT) {
+              goto CleanExit;
+            }
+#endif
+#ifdef __APPLE__
+            /* special code to deal with Command-Q properly */
+            if (ev.key.keysym.sym == SDLK_q &&
+                ev.key.keysym.mod & KMOD_META) {
+              goto CleanExit;
+            }
+#endif
+            skin_keyboard_process_event( keyboard, &ev, 1 );
+            break;
+
+        case SDL_KEYUP:
+            skin_keyboard_process_event( keyboard, &ev, 0 );
+            break;
+
+        case SDL_MOUSEMOTION:
+            skin_window_process_event( window, &ev );
+            break;
+
+        case SDL_MOUSEBUTTONDOWN:
+        case SDL_MOUSEBUTTONUP:
+            {
+                int  down = (ev.type == SDL_MOUSEBUTTONDOWN);
+                if (ev.button.button == 4)
+                {
+                    /* scroll-wheel simulates DPad up */
+                    AndroidKeyCode  kcode;
+
+                    kcode = qemulator_rotate_keycode(emulator, kKeyCodeDpadUp);
+                    send_key_event( kcode, down );
+                }
+                else if (ev.button.button == 5)
+                {
+                    /* scroll-wheel simulates DPad down */
+                    AndroidKeyCode  kcode;
+
+                    kcode = qemulator_rotate_keycode(emulator, kKeyCodeDpadDown);
+                    send_key_event( kcode, down );
+                }
+                else if (ev.button.button == SDL_BUTTON_LEFT) {
+                    skin_window_process_event( window, &ev );
+                }
+#if 0
+                else {
+                fprintf(stderr, "... mouse button %s: button=%d state=%04x x=%d y=%d\n",
+                                down ? "down" : "up  ",
+                                ev.button.button, ev.button.state, ev.button.x, ev.button.y);
+                }
+#endif
+            }
+            break;
+
+        case SDL_QUIT:
+#if defined _WIN32 || defined __APPLE__
+        CleanExit:
+#endif
+            /* only save emulator config through clean exit */
+            qemulator_done( emulator );
+            qemu_system_shutdown_request();
+            return;
+        }
+    }
+
+    skin_keyboard_flush( keyboard );
+}
+
+
+/* used to respond to a given keyboard command shortcut
+ */
+static void
+handle_key_command( void*  opaque, SkinKeyCommand  command, int  down )
+{
+    static const struct { SkinKeyCommand  cmd; AndroidKeyCode  kcode; }  keycodes[] =
+    {
+        { SKIN_KEY_COMMAND_BUTTON_CALL,   kKeyCodeCall },
+        { SKIN_KEY_COMMAND_BUTTON_HOME,   kKeyCodeHome },
+        { SKIN_KEY_COMMAND_BUTTON_BACK,   kKeyCodeBack },
+        { SKIN_KEY_COMMAND_BUTTON_HANGUP, kKeyCodeEndCall },
+        { SKIN_KEY_COMMAND_BUTTON_POWER,  kKeyCodePower },
+        { SKIN_KEY_COMMAND_BUTTON_SEARCH,      kKeyCodeSearch },
+        { SKIN_KEY_COMMAND_BUTTON_MENU,        kKeyCodeMenu },
+        { SKIN_KEY_COMMAND_BUTTON_DPAD_UP,     kKeyCodeDpadUp },
+        { SKIN_KEY_COMMAND_BUTTON_DPAD_LEFT,   kKeyCodeDpadLeft },
+        { SKIN_KEY_COMMAND_BUTTON_DPAD_RIGHT,  kKeyCodeDpadRight },
+        { SKIN_KEY_COMMAND_BUTTON_DPAD_DOWN,   kKeyCodeDpadDown },
+        { SKIN_KEY_COMMAND_BUTTON_DPAD_CENTER, kKeyCodeDpadCenter },
+        { SKIN_KEY_COMMAND_BUTTON_VOLUME_UP,   kKeyCodeVolumeUp },
+        { SKIN_KEY_COMMAND_BUTTON_VOLUME_DOWN, kKeyCodeVolumeDown },
+        { SKIN_KEY_COMMAND_NONE, 0 }
+    };
+    int          nn;
+#ifdef CONFIG_TRACE
+    static int   tracing = 0;
+#endif
+    QEmulator*   emulator = opaque;
+
+
+    for (nn = 0; keycodes[nn].kcode != 0; nn++) {
+        if (command == keycodes[nn].cmd) {
+            unsigned  code = keycodes[nn].kcode;
+            if (down)
+                code |= 0x200;
+            kbd_put_keycode( code );
+            return;
+        }
+    }
+
+    // for the show-trackball command, handle down events to enable, and
+    // up events to disable
+    if (command == SKIN_KEY_COMMAND_SHOW_TRACKBALL) {
+        emulator->show_trackball = (down != 0);
+        skin_window_show_trackball( emulator->window, emulator->show_trackball );
+        //qemulator_set_title( emulator );
+        return;
+    }
+
+    // only handle down events for the rest
+    if (down == 0)
+        return;
+
+    switch (command)
+    {
+    case SKIN_KEY_COMMAND_TOGGLE_NETWORK:
+        {
+            qemu_net_disable = !qemu_net_disable;
+            if (android_modem) {
+                amodem_set_data_registration(
+                        android_modem,
+                qemu_net_disable ? A_REGISTRATION_UNREGISTERED
+                    : A_REGISTRATION_HOME);
+            }
+            D( "network is now %s", qemu_net_disable ? "disconnected" : "connected" );
+        }
+        break;
+
+    case SKIN_KEY_COMMAND_TOGGLE_FULLSCREEN:
+        if (emulator->window) {
+            skin_window_toggle_fullscreen(emulator->window);
+        }
+        break;
+
+    case SKIN_KEY_COMMAND_TOGGLE_TRACING:
+        {
+#ifdef CONFIG_TRACE
+            tracing = !tracing;
+            if (tracing)
+                start_tracing();
+            else
+                stop_tracing();
+#endif
+        }
+        break;
+
+    case SKIN_KEY_COMMAND_TOGGLE_TRACKBALL:
+        emulator->show_trackball = !emulator->show_trackball;
+        skin_window_show_trackball( emulator->window, emulator->show_trackball );
+        break;
+
+    case SKIN_KEY_COMMAND_ONION_ALPHA_UP:
+    case SKIN_KEY_COMMAND_ONION_ALPHA_DOWN:
+        if (emulator->onion)
+        {
+            int  alpha = emulator->onion_alpha;
+
+            if (command == SKIN_KEY_COMMAND_ONION_ALPHA_UP)
+                alpha += 16;
+            else
+                alpha -= 16;
+
+            if (alpha > 256)
+                alpha = 256;
+            else if (alpha < 0)
+                alpha = 0;
+
+            emulator->onion_alpha = alpha;
+            skin_window_set_onion( emulator->window, emulator->onion, emulator->onion_rotation, alpha );
+            skin_window_redraw( emulator->window, NULL );
+            //dprint( "onion alpha set to %d (%.f %%)", alpha, alpha/2.56 );
+        }
+        break;
+
+    case SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV:
+    case SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT:
+        {
+            SkinLayout*  layout = NULL;
+
+            if (command == SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT) {
+                layout = emulator->layout->next;
+                if (layout == NULL)
+                    layout = emulator->layout_file->layouts;
+            }
+            else if (command == SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV) {
+                layout = emulator->layout_file->layouts;
+                while (layout->next && layout->next != emulator->layout)
+                    layout = layout->next;
+            }
+            if (layout != NULL) {
+                SkinRotation  rotation;
+
+                emulator->layout = layout;
+                skin_window_reset( emulator->window, layout );
+
+                rotation = skin_layout_get_dpad_rotation( layout );
+
+                if (emulator->keyboard)
+                    skin_keyboard_set_rotation( emulator->keyboard, rotation );
+
+                if (emulator->trackball) {
+                    skin_trackball_set_rotation( emulator->trackball, rotation );
+                    skin_window_set_trackball( emulator->window, emulator->trackball );
+                    skin_window_show_trackball( emulator->window, emulator->show_trackball );
+                }
+
+                skin_window_set_lcd_brightness( emulator->window, emulator->lcd_brightness );
+
+                qframebuffer_invalidate_all();
+                qframebuffer_check_updates();
+            }
+        }
+        break;
+
+    default:
+        /* XXX: TODO ? */
+        ;
+    }
+}
+
+
+static void sdl_at_exit(void)
+{
+    emulator_config_done();
+    qemulator_done( qemulator );
+    SDL_Quit();
+}
+
+
+void sdl_display_init(DisplayState *ds, int full_screen, int  no_frame)
+{
+    QEmulator*    emulator = qemulator;
+    SkinDisplay*  disp     = skin_layout_get_display(emulator->layout);
+
+//    fprintf(stderr,"*** sdl_display_init ***\n");
+    ds->opaque = emulator;
+
+    if (disp->rotation & 1) {
+        ds->width  = disp->rect.size.h;
+        ds->height = disp->rect.size.w;
+    } else {
+        ds->width  = disp->rect.size.w;
+        ds->height = disp->rect.size.h;
+    }
+
+    ds->dpy_update  = sdl_update;
+    ds->dpy_resize  = sdl_resize;
+    ds->dpy_refresh = sdl_refresh;
+
+    skin_keyboard_enable( emulator->keyboard, 1 );
+    skin_keyboard_on_command( emulator->keyboard, handle_key_command, emulator );
+}
+
+
+extern SkinKeyboard*  android_emulator_get_keyboard(void)
+{
+    return qemulator->keyboard;
+}
+
+static const char*  skin_network_speed = NULL;
+static const char*  skin_network_delay = NULL;
+
+/* list of skin aliases */
+static const struct {
+    const char*  name;
+    const char*  alias;
+} skin_aliases[] = {
+    { "QVGA-L", "320x240" },
+    { "QVGA-P", "240x320" },
+    { "HVGA-L", "480x320" },
+    { "HVGA-P", "320x480" },
+    { "QVGA", "320x240" },
+    { "HVGA", "320x480" },
+    { NULL, NULL }
+};
+
+/* this is used by hw/events_device.c to send the charmap name to the system */
+const char*    android_skin_keycharmap = NULL;
+
+void init_skinned_ui(const char *path, const char *name, AndroidOptions*  opts)
+{
+    char      tmp[1024];
+    AConfig*  root;
+    AConfig*  n;
+    int       win_x, win_y, flags;
+
+    signal(SIGINT, SIG_DFL);
+#ifndef _WIN32
+    signal(SIGQUIT, SIG_DFL);
+#endif
+
+    /* we're not a game, so allow the screensaver to run */
+    putenv("SDL_VIDEO_ALLOW_SCREENSAVER=1");
+
+    flags = SDL_INIT_NOPARACHUTE;
+    if (!opts->no_window)
+        flags |= SDL_INIT_VIDEO;
+
+    if(SDL_Init(flags)){
+        fprintf(stderr, "SDL init failure, reason is: %s\n", SDL_GetError() );
+        exit(1);
+    }
+
+    if (!opts->no_window) {
+        SDL_EnableUNICODE(!opts->raw_keys);
+        SDL_EnableKeyRepeat(0,0);
+
+        sdl_set_window_icon();
+    }
+    else
+    {
+#ifndef _WIN32
+       /* prevent SIGTTIN and SIGTTOUT from stopping us. this is necessary to be
+        * able to run the emulator in the background (e.g. "emulator &").
+        * despite the fact that the emulator should not grab input or try to
+        * write to the output in normal cases, we're stopped on some systems
+        * (e.g. OS X)
+        */
+        signal(SIGTTIN, SIG_IGN);
+        signal(SIGTTOU, SIG_IGN);
+#endif
+    }
+    atexit(sdl_at_exit);
+
+    root = aconfig_node("", "");
+
+    if(name) {
+        /* Support skin aliases like QVGA-H QVGA-P, etc...
+           But first we check if it's a directory that exist before applying
+           the alias */
+        int  checkAlias = 1;
+
+        if (path != NULL) {
+            bufprint(tmp, tmp+sizeof(tmp), "%s/%s", path, name);
+            if (path_exists(tmp)) {
+                checkAlias = 0;
+            } else {
+                D("there is no '%s' skin in '%s'", name, path);
+            }
+        }
+
+        if (checkAlias) {
+            int  nn;
+
+            for (nn = 0; ; nn++ ) {
+                const char*  skin_name  = skin_aliases[nn].name;
+                const char*  skin_alias = skin_aliases[nn].alias;
+
+                if ( !skin_name )
+                    break;
+
+                if ( !strcasecmp( skin_name, name ) ) {
+                    D("skin name '%s' aliased to '%s'", name, skin_alias);
+                    name = skin_alias;
+                    break;
+                }
+            }
+        }
+
+        /* Magically support skins like "320x240" */
+        if(isdigit(name[0])) {
+            char *x = strchr(name, 'x');
+            if(x && isdigit(x[1])) {
+                int width = atoi(name);
+                int height = atoi(x + 1);
+                sprintf(tmp,"display {\n  width %d\n  height %d\n}\n", 
+                        width, height);
+                aconfig_load(root, strdup(tmp));
+                path = ":";
+                goto found_a_skin;
+            }
+        }
+
+        if (path == NULL) {
+            derror("unknown skin name '%s'", name);
+            exit(1);
+        }
+
+        sprintf(tmp, "%s/%s/layout", path, name);
+        D("trying to load skin file '%s'", tmp);
+
+        if(aconfig_load_file(root, tmp) >= 0) {
+            sprintf(tmp, "%s/%s/", path, name);
+            path = tmp;
+            goto found_a_skin;
+        } else {
+            dwarning("could not load skin file '%s', using built-in one\n", 
+                     tmp);
+        }
+    }
+
+    {
+        const unsigned char*  layout_base;
+        size_t                layout_size;
+
+        name = "<builtin>";
+
+        layout_base = android_resource_find( "layout", &layout_size );
+        if (layout_base != NULL) {
+            char*  base = malloc( layout_size+1 );
+            memcpy( base, layout_base, layout_size );
+            base[layout_size] = 0;
+
+            D("parsing built-in skin layout file (size=%d)", (int)layout_size);
+            aconfig_load(root, base);
+            path = ":";
+        } else {
+            fprintf(stderr, "Couldn't load builtin skin\n");
+            exit(1);
+        }
+    }
+
+found_a_skin:
+    {
+        win_x = 10;
+        win_y = 10;
+
+        if (userConfig)
+            auserConfig_getWindowPos(userConfig, &win_x, &win_y);
+    }
+    if ( qemulator_init( qemulator, root, path, win_x, win_y, opts ) < 0 ) {
+        fprintf(stderr, "### Error: could not load emulator skin '%s'\n", name);
+        exit(1);
+    }
+
+    android_skin_keycharmap = skin_keyboard_charmap_name(qemulator->keyboard);
+
+    /* the default network speed and latency can now be specified by the device skin */
+    n = aconfig_find(root, "network");
+    if (n != NULL) {
+        skin_network_speed = aconfig_str(n, "speed", 0);
+        skin_network_delay = aconfig_str(n, "delay", 0);
+    }
+
+#if 0
+    /* create a trackball if needed */
+    n = aconfig_find(root, "trackball");
+    if (n != NULL) {
+        SkinTrackBallParameters  params;
+
+        params.x        = aconfig_unsigned(n, "x", 0);
+        params.y        = aconfig_unsigned(n, "y", 0);
+        params.diameter = aconfig_unsigned(n, "diameter", 20);
+        params.ring     = aconfig_unsigned(n, "ring", 1);
+
+        params.ball_color = aconfig_unsigned(n, "ball-color", 0xffe0e0e0);
+        params.dot_color  = aconfig_unsigned(n, "dot-color",  0xff202020 );
+        params.ring_color = aconfig_unsigned(n, "ring-color", 0xff000000 );
+
+        qemu_disp->trackball = skin_trackball_create( &params );
+        skin_trackball_refresh( qemu_disp->trackball );
+    }
+#endif
+
+    /* add an onion overlay image if needed */
+    if (opts->onion) {
+        SkinImage*  onion = skin_image_find_simple( opts->onion );
+        int         alpha, rotate;
+
+        if ( opts->onion_alpha && 1 == sscanf( opts->onion_alpha, "%d", &alpha ) ) {
+            alpha = (256*alpha)/100;
+        } else
+            alpha = 128;
+
+        if ( opts->onion_rotation && 1 == sscanf( opts->onion_rotation, "%d", &rotate ) ) {
+            rotate &= 3;
+        } else
+            rotate = SKIN_ROTATION_0;
+
+        qemulator->onion          = onion;
+        qemulator->onion_alpha    = alpha;
+        qemulator->onion_rotation = rotate;
+    }
+}
+
+int qemu_main(int argc, char **argv);
+
+/* this function dumps the QEMU help */
+extern void  help( void );
+extern void  emulator_help( void );
+
+#define  VERBOSE_OPT(str,var)   { str, &var }
+
+#define  _VERBOSE_TAG(x,y)   { #x, VERBOSE_##x, y },
+static const struct { const char*  name; int  flag; const char*  text; }
+verbose_options[] = {
+    VERBOSE_TAG_LIST
+    { 0, 0, 0 }
+};
+
+int
+android_parse_network_speed(const char*  speed)
+{
+    int          n;
+    char*  end;
+    double       sp;
+
+    if (speed == NULL || speed[0] == 0) {
+        speed = DEFAULT_NETSPEED;
+    }
+
+    for (n = 0; android_netspeeds[n].name != NULL; n++) {
+        if (!strcmp(android_netspeeds[n].name, speed)) {
+            qemu_net_download_speed = android_netspeeds[n].download;
+            qemu_net_upload_speed   = android_netspeeds[n].upload;
+            return 0;
+        }
+    }
+
+    /* is this a number ? */
+    sp = strtod(speed, &end);
+    if (end == speed) {
+        return -1;
+    }
+
+    qemu_net_download_speed = qemu_net_upload_speed = sp*1000.;
+    if (*end == ':') {
+        speed = end+1;
+        sp = strtod(speed, &end);
+        if (end > speed) {
+            qemu_net_download_speed = sp*1000.;
+        }
+    }
+
+    if (android_modem)
+        amodem_set_data_network_type( android_modem, 
+                                      android_parse_network_type(speed) );
+    return 0;
+}
+
+
+int
+android_parse_network_latency(const char*  delay)
+{
+    int  n;
+    char*  end;
+    double  sp;
+
+    if (delay == NULL || delay[0] == 0)
+        delay = DEFAULT_NETDELAY;
+
+    for (n = 0; android_netdelays[n].name != NULL; n++) {
+        if ( !strcmp( android_netdelays[n].name, delay ) ) {
+            qemu_net_min_latency = android_netdelays[n].min_ms;
+            qemu_net_max_latency = android_netdelays[n].max_ms;
+            return 0;
+        }
+    }
+
+    /* is this a number ? */
+    sp = strtod(delay, &end);
+    if (end == delay) {
+        return -1;
+    }
+
+    qemu_net_min_latency = qemu_net_max_latency = (int)sp;
+    if (*end == ':') {
+        delay = (const char*)end+1;
+        sp = strtod(delay, &end);
+        if (end > delay) {
+            qemu_net_max_latency = (int)sp;
+        }
+    }
+    return 0;
+}
+
+
+static int
+load_keyset(const char*  path)
+{
+    if (path_can_read(path)) {
+        AConfig*  root = aconfig_node("","");
+        if (!aconfig_load_file(root, path)) {
+            android_keyset = skin_keyset_new(root);
+            if (android_keyset != NULL) {
+                D( "keyset loaded from: %s", path);
+                return 0;
+            }
+        }
+    }
+    return -1;
+}
+
+static void
+parse_keyset(const char*  keyset, AndroidOptions*  opts)
+{
+    char   kname[MAX_PATH];
+    char   temp[MAX_PATH];
+    char*  p;
+    char*  end;
+
+    /* append .keyset suffix if needed */
+    if (strchr(keyset, '.') == NULL) {
+        p   =  kname;
+        end = p + sizeof(kname);
+        p   = bufprint(p, end, "%s.keyset", keyset);
+        if (p >= end) {
+            derror( "keyset name too long: '%s'\n", keyset);
+            exit(1);
+        }
+        keyset = kname;
+    }
+
+    /* look for a the keyset file */
+    p   = temp;
+    end = p + sizeof(temp);
+    p = bufprint_config_file(p, end, keyset);
+    if (p < end && load_keyset(temp) == 0)
+        return;
+
+    p = temp;
+    p = bufprint(p, end, "%s" PATH_SEP "keysets" PATH_SEP "%s", opts->sysdir, keyset);
+    if (p < end && load_keyset(temp) == 0)
+        return;
+
+    p = temp;
+    p = bufprint_app_dir(p, end);
+    p = bufprint(p, end, PATH_SEP "keysets" PATH_SEP "%s", keyset);
+    if (p < end && load_keyset(temp) == 0)
+        return;
+
+    return;
+}
+
+static void
+write_default_keyset( void )
+{
+    char   path[MAX_PATH];
+
+    bufprint_config_file( path, path+sizeof(path), KEYSET_FILE );
+
+    /* only write if there is no file here */
+    if ( !path_exists(path) ) {
+        int          fd = open( path, O_WRONLY | O_CREAT, 0666 );
+        int          ret;
+        const char*  ks = skin_keyset_get_default();
+
+
+        D( "writing default keyset file to %s", path );
+
+        if (fd < 0) {
+            D( "%s: could not create file: %s", __FUNCTION__, strerror(errno) );
+            return;
+        }
+        CHECKED(ret, write(fd, ks, strlen(ks)));
+        close(fd);
+    }
+}
+
+#ifdef CONFIG_NAND_LIMITS
+
+static uint64_t
+parse_nand_rw_limit( const char*  value )
+{
+    char*     end;
+    uint64_t  val = strtoul( value, &end, 0 );
+
+    if (end == value) {
+        derror( "bad parameter value '%s': expecting unsigned integer", value );
+        exit(1);
+    }
+
+    switch (end[0]) {
+        case 'K':  val <<= 10; break;
+        case 'M':  val <<= 20; break;
+        case 'G':  val <<= 30; break;
+        case 0: break;
+        default:
+            derror( "bad read/write limit suffix: use K, M or G" );
+            exit(1);
+    }
+    return val;
+}
+
+static void
+parse_nand_limits(char*  limits)
+{
+    int      pid = -1, signal = -1;
+    int64_t  reads = 0, writes = 0;
+    char*    item = limits;
+
+    /* parse over comma-separated items */
+    while (item && *item) {
+        char*  next = strchr(item, ',');
+        char*  end;
+
+        if (next == NULL) {
+            next = item + strlen(item);
+        } else {
+            *next++ = 0;
+        }
+
+        if ( !memcmp(item, "pid=", 4) ) {
+            pid = strtol(item+4, &end, 10);
+            if (end == NULL || *end) {
+                derror( "bad parameter, expecting pid=<number>, got '%s'",
+                        item );
+                exit(1);
+            }
+            if (pid <= 0) {
+                derror( "bad parameter: process identifier must be > 0" );
+                exit(1);
+            }
+        }
+        else if ( !memcmp(item, "signal=", 7) ) {
+            signal = strtol(item+7,&end, 10);
+            if (end == NULL || *end) {
+                derror( "bad parameter: expecting signal=<number>, got '%s'",
+                        item );
+                exit(1);
+            }
+            if (signal <= 0) {
+                derror( "bad parameter: signal number must be > 0" );
+                exit(1);
+            }
+        }
+        else if ( !memcmp(item, "reads=", 6) ) {
+            reads = parse_nand_rw_limit(item+6);
+        }
+        else if ( !memcmp(item, "writes=", 7) ) {
+            writes = parse_nand_rw_limit(item+7);
+        }
+        else {
+            derror( "bad parameter '%s' (see -help-nand-limits)", item );
+            exit(1);
+        }
+        item = next;
+    }
+    if (pid < 0) {
+        derror( "bad paramater: missing pid=<number>" );
+        exit(1);
+    }
+    else if (signal < 0) {
+        derror( "bad parameter: missing signal=<number>" );
+        exit(1);
+    }
+    else if (reads == 0 && writes == 0) {
+        dwarning( "no read or write limit specified. ignoring -nand-limits" );
+    } else {
+        nand_threshold*  t;
+
+        t  = &android_nand_read_threshold;
+        t->pid     = pid;
+        t->signal  = signal;
+        t->counter = 0;
+        t->limit   = reads;
+
+        t  = &android_nand_write_threshold;
+        t->pid     = pid;
+        t->signal  = signal;
+        t->counter = 0;
+        t->limit   = writes;
+    }
+}
+#endif /* CONFIG_NAND_LIMITS */
+
+void emulator_help( void )
+{
+    STRALLOC_DEFINE(out);
+    android_help_main(out);
+    printf( "%.*s", out->n, out->s );
+    stralloc_reset(out);
+    exit(1);
+}
+
+static int
+add_dns_server( const char*  server_name )
+{
+    SockAddress   addr;
+
+    if (sock_address_init_resolve( &addr, server_name, 55, 0 ) < 0) {
+        fprintf(stderr,
+                "### WARNING: can't resolve DNS server name '%s'\n",
+                server_name );
+        return -1;
+    }
+
+    D( "DNS server name '%s' resolved to %s", server_name, sock_address_to_string(&addr) );
+
+    if ( slirp_add_dns_server( &addr ) < 0 ) {
+        fprintf(stderr,
+                "### WARNING: could not add DNS server '%s' to the network stack\n", server_name);
+        return -1;
+    }
+    return 0;
+}
+
+
+enum {
+    REPORT_CONSOLE_SERVER = (1 << 0),
+    REPORT_CONSOLE_MAX    = (1 << 1)
+};
+
+static int
+get_report_console_options( char*  end, int  *maxtries )
+{
+    int    flags = 0;
+
+    if (end == NULL || *end == 0)
+        return 0;
+
+    if (end[0] != ',') {
+        derror( "socket port/path can be followed by [,<option>]+ only\n");
+        exit(3);
+    }
+    end += 1;
+    while (*end) {
+        char*  p = strchr(end, ',');
+        if (p == NULL)
+            p = end + strlen(end);
+
+        if (memcmp( end, "server", p-end ) == 0)
+            flags |= REPORT_CONSOLE_SERVER;
+        else if (memcmp( end, "max=", 4) == 0) {
+            end  += 4;
+            *maxtries = strtol( end, NULL, 10 );
+            flags |= REPORT_CONSOLE_MAX;
+        } else {
+            derror( "socket port/path can be followed by [,server][,max=<count>] only\n");
+            exit(3);
+        }
+
+        end = p;
+        if (*end)
+            end += 1;
+    }
+    return flags;
+}
+
+static void
+report_console( const char*  proto_port, int  console_port )
+{
+    int   s = -1, s2;
+    int   maxtries = 10;
+    int   flags = 0;
+    signal_state_t  sigstate;
+
+    disable_sigalrm( &sigstate );
+
+    if ( !strncmp( proto_port, "tcp:", 4) ) {
+        char*  end;
+        long   port = strtol(proto_port + 4, &end, 10);
+
+        flags = get_report_console_options( end, &maxtries );
+
+        if (flags & REPORT_CONSOLE_SERVER) {
+            s = socket_loopback_server( port, SOCKET_STREAM );
+            if (s < 0) {
+                fprintf(stderr, "could not create server socket on TCP:%ld: %s\n",
+                        port, errno_str);
+                exit(3);
+            }
+        } else {
+            for ( ; maxtries > 0; maxtries-- ) {
+                D("trying to find console-report client on tcp:%d", port);
+                s = socket_loopback_client( port, SOCKET_STREAM );
+                if (s >= 0)
+                    break;
+
+                sleep_ms(1000);
+            }
+            if (s < 0) {
+                fprintf(stderr, "could not connect to server on TCP:%ld: %s\n",
+                        port, errno_str);
+                exit(3);
+            }
+        }
+    } else if ( !strncmp( proto_port, "unix:", 5) ) {
+#ifdef _WIN32
+        fprintf(stderr, "sorry, the unix: protocol is not supported on Win32\n");
+        exit(3);
+#else
+        char*  path = strdup(proto_port+5);
+        char*  end  = strchr(path, ',');
+        if (end != NULL) {
+            flags = get_report_console_options( end, &maxtries );
+            *end  = 0;
+        }
+        if (flags & REPORT_CONSOLE_SERVER) {
+            s = socket_unix_server( path, SOCKET_STREAM );
+            if (s < 0) {
+                fprintf(stderr, "could not bind unix socket on '%s': %s\n",
+                        proto_port+5, errno_str);
+                exit(3);
+            }
+        } else {
+            for ( ; maxtries > 0; maxtries-- ) {
+                s = socket_unix_client( path, SOCKET_STREAM );
+                if (s >= 0)
+                    break;
+
+                sleep_ms(1000);
+            }
+            if (s < 0) {
+                fprintf(stderr, "could not connect to unix socket on '%s': %s\n",
+                        path, errno_str);
+                exit(3);
+            }
+        }
+        free(path);
+#endif
+    } else {
+        fprintf(stderr, "-report-console must be followed by a 'tcp:<port>' or 'unix:<path>'\n");
+        exit(3);
+    }
+
+    if (flags & REPORT_CONSOLE_SERVER) {
+        int  tries = 3;
+        D( "waiting for console-reporting client" );
+        do {
+            s2 = socket_accept(s, NULL);
+        } while (s2 < 0 && --tries > 0);
+
+        if (s2 < 0) {
+            fprintf(stderr, "could not accept console-reporting client connection: %s\n",
+                   errno_str);
+            exit(3);
+        }
+
+        socket_close(s);
+        s = s2;
+    }
+
+    /* simply send the console port in text */
+    {
+        char  temp[12];
+        snprintf( temp, sizeof(temp), "%d", console_port );
+
+        if (socket_send(s, temp, strlen(temp)) < 0) {
+            fprintf(stderr, "could not send console number report: %d: %s\n",
+                    errno, errno_str );
+            exit(3);
+        }
+        socket_close(s);
+    }
+    D( "console port number sent to remote. resuming boot" );
+
+    restore_sigalrm (&sigstate);
+}
+
+/* this function is used to perform auto-detection of the
+ * system directory in the case of a SDK installation.
+ *
+ * we want to deal with several historical usages, hence
+ * the slightly complicated logic.
+ *
+ * NOTE: the function returns the path to the directory
+ *       containing 'fileName'. this is *not* the full
+ *       path to 'fileName'.
+ */
+static char*
+_getSdkImagePath( const char*  fileName )
+{
+    char   temp[MAX_PATH];
+    char*  p   = temp;
+    char*  end = p + sizeof(temp);
+    char*  q;
+    char*  app;
+
+    static const char* const  searchPaths[] = {
+        "",                                  /* program's directory */
+        "/lib/images",                       /* this is for SDK 1.0 */
+        "/../platforms/android-1.1/images",  /* this is for SDK 1.1 */
+        NULL
+    };
+
+    app = bufprint_app_dir(temp, end);
+    if (app >= end)
+        return NULL;
+
+    do {
+        int  nn;
+
+        /* first search a few well-known paths */
+        for (nn = 0; searchPaths[nn] != NULL; nn++) {
+            p = bufprint(app, end, "%s", searchPaths[nn]);
+            q = bufprint(p, end, "/%s", fileName);
+            if (q < end && path_exists(temp)) {
+                *p = 0;
+                goto FOUND_IT;
+            }
+        }
+
+        /* hmmm. let's assume that we are in a post-1.1 SDK
+         * scan ../platforms if it exists
+         */
+        p = bufprint(app, end, "/../platforms");
+        if (p < end) {
+            DirScanner*  scanner = dirScanner_new(temp);
+            if (scanner != NULL) {
+                int          found = 0;
+                const char*  subdir;
+
+                for (;;) {
+                    subdir = dirScanner_next(scanner);
+                    if (!subdir) break;
+
+                    q = bufprint(p, end, "/%s/images/%s", subdir, fileName);
+                    if (q >= end || !path_exists(temp))
+                        continue;
+
+                    found = 1;
+                    p = bufprint(p, end, "/%s/images", subdir);
+                    break;
+                }
+                dirScanner_free(scanner);
+                if (found)
+                    break;
+            }
+        }
+
+        /* I'm out of ideas */
+        return NULL;
+
+    } while (0);
+
+FOUND_IT:
+    //D("image auto-detection: %s/%s", temp, fileName);
+    return qemu_strdup(temp);
+}
+
+static char*
+_getSdkImage( const char*  path, const char*  file )
+{
+    char  temp[MAX_PATH];
+    char  *p = temp, *end = p + sizeof(temp);
+
+    p = bufprint(temp, end, "%s/%s", path, file);
+    if (p >= end || !path_exists(temp))
+        return NULL;
+
+    return qemu_strdup(temp);
+}
+
+static char*
+_getSdkSystemImage( const char*  path, const char*  optionName, const char*  file )
+{
+    char*  image = _getSdkImage(path, file);
+
+    if (image == NULL) {
+        derror("Your system directory is missing the '%s' image file.\n"
+               "Please specify one with the '%s <filepath>' option",
+               file, optionName);
+        exit(2);
+    }
+    return image;
+}
+
+static void
+_forceAvdImagePath( AvdImageType  imageType, 
+                   const char*   path, 
+                   const char*   description,
+                   int           required )
+{
+    if (path == NULL)
+        return;
+
+    if (required && !path_exists(path)) {
+        derror("Cannot find %s image file: %s", description, path);
+        exit(1);
+    }
+    android_avdParams->forcePaths[imageType] = path;
+}
+
+#ifdef _WIN32
+#undef main  /* we don't want SDL to define main */
+#endif
+
+int main(int argc, char **argv)
+{
+    char   tmp[MAX_PATH];
+    char*  tmpend = tmp + sizeof(tmp);
+    char*  args[128];
+    int    n;
+    char*  opt;
+    int    use_sdcard_img = 0;
+    int    serial = 0;
+    int    gps_serial = 0;
+    int    radio_serial = 0;
+    int    qemud_serial = 0;
+    int    shell_serial = 0;
+    int    dns_count = 0;
+    unsigned  cachePartitionSize = 0;
+
+    AndroidHwConfig*  hw;
+
+    //const char *appdir = get_app_dir();
+    char*       android_build_root = NULL;
+    char*       android_build_out  = NULL;
+
+    AndroidOptions  opts[1];
+
+    args[0] = argv[0];
+
+    if ( android_parse_options( &argc, &argv, opts ) < 0 ) {
+        exit(1);
+    }
+
+    while (argc-- > 1) {
+        opt = (++argv)[0];
+
+        if(!strcmp(opt, "-qemu")) {
+            argc--;
+            argv++;
+            break;
+        }
+
+        if (!strcmp(opt, "-help")) {
+            emulator_help();
+        }
+
+        if (!strncmp(opt, "-help-",6)) {
+            STRALLOC_DEFINE(out);
+            opt += 6;
+
+            if (!strcmp(opt, "all")) {
+                android_help_all(out);
+            }
+            else if (android_help_for_option(opt, out) == 0) {
+                /* ok */
+            }
+            else if (android_help_for_topic(opt, out) == 0) {
+                /* ok */
+            }
+            if (out->n > 0) {
+                printf("\n%.*s", out->n, out->s);
+                exit(0);
+            }
+
+            fprintf(stderr, "unknown option: -help-%s\n", opt);
+            fprintf(stderr, "please use -help for a list of valid topics\n");
+            exit(1);
+        }
+
+        if (opt[0] == '-') {
+            fprintf(stderr, "unknown option: %s\n", opt);
+            fprintf(stderr, "please use -help for a list of valid options\n");
+            exit(1);
+        }
+
+        fprintf(stderr, "invalid command-line parameter: %s.\n", opt);
+        fprintf(stderr, "Hint: use '@foo' to launch a virtual device named 'foo'.\n");
+        fprintf(stderr, "please use -help for more information\n");
+        exit(1);
+    }
+
+    /* special case, if -qemu -h is used, directly invoke the QEMU-specific help */
+    if (argc > 0) {
+        int  nn;
+        for (nn = 0; nn < argc; nn++)
+            if (!strcmp(argv[nn], "-h")) {
+                qemu_help(0);
+                break;
+            }
+    }
+
+    android_charmap = android_charmaps[0];
+
+    if (opts->version) {
+        printf("Android emulator version %s\n"
+               "Copyright (C) 2006-2008 The Android Open Source Project and many others.\n"
+               "This program is a derivative of the QEMU CPU emulator (www.qemu.org).\n\n",
+#if defined ANDROID_BUILD_ID
+               VERSION_STRING " (build_id " STRINGIFY(ANDROID_BUILD_ID) ")" );
+#else
+               VERSION_STRING);
+#endif
+        printf("  This software is licensed under the terms of the GNU General Public\n"
+               "  License version 2, as published by the Free Software Foundation, and\n"
+               "  may be copied, distributed, and modified under those terms.\n\n"
+               "  This program is distributed in the hope that it will be useful,\n"
+               "  but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+               "  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+               "  GNU General Public License for more details.\n\n");
+
+        exit(0);
+    }
+
+    if (opts->timezone) {
+        if ( timezone_set(opts->timezone) < 0 ) {
+            fprintf(stderr, "emulator: it seems the timezone '%s' is not in zoneinfo format\n", opts->timezone);
+        }
+    }
+
+    /* legacy support: we used to use -system <dir> and -image <file>
+     * instead of -sysdir <dir> and -system <file>, so handle this by checking
+     * whether the options point to directories or files.
+     */
+    if (opts->image != NULL) {
+        if (opts->system != NULL) {
+            if (opts->sysdir != NULL) {
+                derror( "You can't use -sysdir, -system and -image at the same time.\n"
+                        "You should probably use '-sysdir <path> -system <file>'.\n" );
+                exit(2);
+            }
+        }
+        dwarning( "Please note that -image is obsolete and that -system is now used to point\n"
+                  "to the system image. Next time, try using '-sysdir <path> -system <file>' instead.\n" );
+        opts->sysdir = opts->system;
+        opts->system = opts->image;
+        opts->image  = NULL;
+    }
+    else if (opts->system != NULL && path_is_dir(opts->system)) {
+        if (opts->sysdir != NULL) {
+            derror( "Option -system should now be followed by a file path, not a directory one.\n"
+                    "Please use '-sysdir <path>' to point to the system directory.\n" );
+            exit(1);
+        }
+        dwarning( "Please note that the -system option should now be used to point to the initial\n"
+                  "system image (like the obsolete -image option). To point to the system directory\n"
+                  "please now use '-sysdir <path>' instead.\n" );
+
+        opts->sysdir = opts->system;
+        opts->system = NULL;
+    }
+
+    if (opts->nojni)
+        opts->no_jni = opts->nojni;
+
+    if (opts->nocache)
+        opts->no_cache = opts->nocache;
+
+    if (opts->noaudio)
+        opts->no_audio = opts->noaudio;
+
+    if (opts->noskin)
+        opts->no_skin = opts->noskin;
+
+    if (opts->initdata) {
+        opts->init_data = opts->initdata;
+        opts->initdata  = NULL;
+    }
+
+    /* If no AVD name was given, try to find the top of the
+     * Android build tree
+     */
+    if (opts->avd == NULL) {
+        do {
+            char*  out = getenv("ANDROID_PRODUCT_OUT");
+
+            if (out == NULL || out[0] == 0)
+                break;
+
+            if (!path_exists(out)) {
+                derror("Can't access ANDROID_PRODUCT_OUT as '%s\n"
+                    "You need to build the Android system before launching the emulator",
+                    out);
+                exit(2);
+            }
+
+            android_build_root = path_parent( out, 4 );
+            if (android_build_root == NULL || !path_exists(android_build_root)) {
+                derror("Can't find the Android build root from '%s'\n"
+                    "Please check the definition of the ANDROID_PRODUCT_OUT variable.\n"
+                    "It should point to your product-specific build output directory.\n",
+                    out );
+                exit(2);
+            }
+            android_build_out = out;
+            D( "found Android build root: %s", android_build_root );
+            D( "found Android build out:  %s", android_build_out );
+        } while (0);
+    }
+    /* if no virtual device name is given, and we're not in the
+     * Android build system, we'll need to perform some auto-detection
+     * magic :-)
+     */
+    if (opts->avd == NULL && !android_build_out) 
+    {
+        char   dataDirIsSystem = 0;
+
+        if (!opts->sysdir) {
+            opts->sysdir = _getSdkImagePath("system.img");
+            if (!opts->sysdir) {
+                derror(
+                "You did not specify a virtual device name, and the system\n"
+                "directory could not be found.\n\n"
+                "If you are an Android SDK user, please use '@<name>' or '-avd <name>'\n"
+                "to start a given virtual device (see -help-avd for details).\n\n"
+
+                "Otherwise, follow the instructions in -help-disk-images to start the emulator\n"
+                );
+                exit(2);
+            }
+            D("autoconfig: -sysdir %s", opts->sysdir);
+        }
+
+        if (!opts->system) {
+            opts->system = _getSdkSystemImage(opts->sysdir, "-image", "system.img");
+            D("autoconfig: -image %s", opts->image);
+        }
+
+        if (!opts->kernel) {
+            opts->kernel = _getSdkSystemImage(opts->sysdir, "-kernel", "kernel-qemu");
+            D("autoconfig: -kernel %s", opts->kernel);
+        }
+
+        if (!opts->ramdisk) {
+            opts->ramdisk = _getSdkSystemImage(opts->sysdir, "-ramdisk", "ramdisk.img");
+            D("autoconfig: -ramdisk %s", opts->ramdisk);
+        }
+
+        /* if no data directory is specified, use the system directory */
+        if (!opts->datadir) {
+            opts->datadir   = qemu_strdup(opts->sysdir);
+            dataDirIsSystem = 1;
+            D("autoconfig: -datadir %s", opts->sysdir);
+        }
+
+        if (!opts->data) {
+            /* check for userdata-qemu.img in the data directory */
+            bufprint(tmp, tmpend, "%s/userdata-qemu.img", opts->datadir);
+            if (!path_exists(tmp)) {
+                derror(
+                "You did not provide the name of an Android Virtual Device\n"
+                "with the '-avd <name>' option. Read -help-avd for more information.\n\n"
+
+                "If you *really* want to *NOT* run an AVD, consider using '-data <file>'\n"
+                "to specify a data partition image file (I hope you know what you're doing).\n"
+                );
+                exit(2);
+            }
+
+            opts->data = qemu_strdup(tmp);
+            D("autoconfig: -data %s", opts->data);
+        }
+
+        if (!opts->sdcard && opts->datadir) {
+            bufprint(tmp, tmpend, "%s/sdcard.img", opts->datadir);
+            if (path_exists(tmp)) {
+                opts->sdcard = qemu_strdup(tmp);
+                D("autoconfig: -sdcard %s", opts->sdcard);
+            }
+        }
+    }
+
+    /* setup the virtual device parameters from our options
+     */
+    if (opts->no_cache) {
+        android_avdParams->flags |= AVDINFO_NO_CACHE;
+    }
+    if (opts->wipe_data) {
+        android_avdParams->flags |= AVDINFO_WIPE_DATA | AVDINFO_WIPE_CACHE;
+    }
+
+    /* if certain options are set, we can force the path of
+        * certain kernel/disk image files
+        */
+    _forceAvdImagePath(AVD_IMAGE_KERNEL,     opts->kernel, "kernel", 1);
+    _forceAvdImagePath(AVD_IMAGE_INITSYSTEM, opts->system, "system", 1);
+    _forceAvdImagePath(AVD_IMAGE_RAMDISK,    opts->ramdisk,"ramdisk", 1);
+    _forceAvdImagePath(AVD_IMAGE_USERDATA,   opts->data,   "user data", 0);
+    _forceAvdImagePath(AVD_IMAGE_CACHE,      opts->cache,  "cache", 0);
+    _forceAvdImagePath(AVD_IMAGE_SDCARD,     opts->sdcard, "SD Card", 0);
+
+    /* we don't accept -skindir without -skin now
+     * to simplify the autoconfig stuff with virtual devices
+     */
+    if (opts->no_skin) {
+        opts->skin    = "320x480";
+        opts->skindir = NULL;
+    }
+
+    if (opts->skindir) {
+        if (!opts->skin) {
+            derror( "the -skindir <path> option requires a -skin <name> option");
+            exit(1);
+        }
+    }
+    else {
+        if (!opts->skin && android_build_out) {
+            /* select default skin based on product type */
+            const char*  p = strrchr(android_build_out,'/');
+            if (p) {
+                if (p[1] == 's') {
+                    opts->skin = "HVGA";  /* used to be QVGA-L */
+                } else if (p[1] == 'd') {
+                    opts->skin = "HVGA";
+                }
+            }
+            D("autoconfig: -skin %s", opts->skin);
+        }
+        android_avdParams->skinName = opts->skin;
+    }
+    /* setup the virtual device differently depending on whether
+     * we are in the Android build system or not
+     */
+    if (opts->avd != NULL)
+    {
+        android_avdInfo = avdInfo_new( opts->avd, android_avdParams );
+        if (android_avdInfo == NULL) {
+            /* an error message has already been printed */
+            dprint("could not find virtual device named '%s'", opts->avd);
+            exit(1);
+        }
+    }
+    else
+    {
+        if (!android_build_out) {
+            android_build_out = android_build_root = opts->sysdir;
+        }
+        android_avdInfo = avdInfo_newForAndroidBuild(
+                            android_build_root,
+                            android_build_out,
+                            android_avdParams );
+
+        if(android_avdInfo == NULL) {
+            D("could not start virtual device\n");
+            exit(1);
+        }
+    }
+
+    /* get the skin from the virtual device configuration */
+    opts->skin    = (char*) avdInfo_getSkinName( android_avdInfo );
+    opts->skindir = (char*) avdInfo_getSkinDir( android_avdInfo );
+
+    if (opts->skin) {
+        D("autoconfig: -skin %s", opts->skin);
+    }
+    if (opts->skindir) {
+        D("autoconfig: -skindir %s", opts->skindir);
+    }
+
+    /* Read hardware configuration */
+    hw = android_hw;
+    if (avdInfo_getHwConfig(android_avdInfo, hw) < 0) {
+        derror("could not read hardware configuration ?");
+        exit(1);
+    }
+
+#ifdef CONFIG_NAND_LIMITS
+    if (opts->nand_limits)
+        parse_nand_limits(opts->nand_limits);
+#endif
+
+    if (opts->keyset) {
+        parse_keyset(opts->keyset, opts);
+        if (!android_keyset) {
+            fprintf(stderr,
+                    "emulator: WARNING: could not find keyset file named '%s',"
+                    " using defaults instead\n",
+                    opts->keyset);
+        }
+    }
+    if (!android_keyset) {
+        parse_keyset("default", opts);
+        if (!android_keyset) {
+            android_keyset = skin_keyset_new_from_text( skin_keyset_get_default() );
+            if (!android_keyset) {
+                fprintf(stderr, "PANIC: default keyset file is corrupted !!\n" );
+                fprintf(stderr, "PANIC: please update the code in android/skin/keyset.c\n" );
+                exit(1);
+            }
+            if (!opts->keyset)
+                write_default_keyset();
+        }
+    }
+
+    if (opts->audio) {
+        if (opts->audio_in || opts->audio_out) {
+            derror( "you can't use -audio with -audio-in or -audio-out\n" );
+            exit(1);
+        }
+        if ( !audio_check_backend_name( 0, opts->audio ) ) {
+            derror( "'%s' is not a valid audio output backend. see -help-audio-out\n",
+                    opts->audio);
+            exit(1);
+        }
+        opts->audio_out = opts->audio;
+        opts->audio_in  = opts->audio;
+
+        if ( !audio_check_backend_name( 1, opts->audio ) ) {
+            fprintf(stderr,
+                    "emulator: warning: '%s' is not a valid audio input backend. audio record disabled\n",
+                    opts->audio);
+            opts->audio_in = "none";
+        }
+    }
+
+    if (opts->audio_in) {
+        static char  env[64]; /* note: putenv needs a static unique string buffer */
+        if ( !audio_check_backend_name( 1, opts->audio_in ) ) {
+            derror( "'%s' is not a valid audio input backend. see -help-audio-in\n",
+                    opts->audio_in);
+            exit(1);
+        }
+        bufprint( env, env+sizeof(env), "QEMU_AUDIO_IN_DRV=%s", opts->audio_in );
+        putenv( env );
+
+        if (!hw->hw_audioInput) {
+            dwarning( "Emulated hardware doesn't have audio input.");
+        }
+    }
+    if (opts->audio_out) {
+        static char  env[64]; /* note: putenv needs a static unique string buffer */
+        if ( !audio_check_backend_name( 0, opts->audio_out ) ) {
+            derror( "'%s' is not a valid audio output backend. see -help-audio-out\n",
+                    opts->audio_out);
+            exit(1);
+        }
+        bufprint( env, env+sizeof(env), "QEMU_AUDIO_OUT_DRV=%s", opts->audio_out );
+        putenv( env );
+        if (!hw->hw_audioOutput) {
+            dwarning( "Emulated hardware doesn't have audio output");
+        }
+    }
+
+    if (opts->cpu_delay) {
+        char*   end;
+        long    delay = strtol(opts->cpu_delay, &end, 0);
+        if (end == NULL || *end || delay < 0 || delay > 1000 ) {
+            fprintf(stderr, "option -cpu-delay must be an integer between 0 and 1000\n" );
+            exit(1);
+        }
+        if (delay > 0)
+            delay = (1000-delay);
+
+        qemu_cpu_delay = (int) delay;
+    }
+
+    emulator_config_init();
+    init_skinned_ui(opts->skindir, opts->skin, opts);
+
+    if (!opts->netspeed) {
+        if (skin_network_speed)
+            D("skin network speed: '%s'", skin_network_speed);
+        opts->netspeed = (char*)skin_network_speed;
+    }
+    if (!opts->netdelay) {
+        if (skin_network_delay)
+            D("skin network delay: '%s'", skin_network_delay);
+        opts->netdelay = (char*)skin_network_delay;
+    }
+
+    if ( android_parse_network_speed(opts->netspeed) < 0 ) {
+        fprintf(stderr, "invalid -netspeed parameter '%s', see emulator -usage\n", opts->netspeed);
+        emulator_help();
+    }
+
+    if ( android_parse_network_latency(opts->netdelay) < 0 ) {
+        fprintf(stderr, "invalid -netdelay parameter '%s', see emulator -usage\n", opts->netdelay);
+        emulator_help();
+    }
+
+    if (opts->netfast) {
+        qemu_net_download_speed = 0;
+        qemu_net_upload_speed = 0;
+        qemu_net_min_latency = 0;
+        qemu_net_max_latency = 0;
+    }
+
+    if (opts->trace) {
+        char*   tracePath = avdInfo_getTracePath(android_avdInfo, opts->trace);
+        int     ret;
+
+        if (tracePath == NULL) {
+            derror( "bad -trace parameter" );
+            exit(1);
+        }
+        ret = path_mkdir_if_needed( tracePath, 0755 );
+        if (ret < 0) {
+            fprintf(stderr, "could not create directory '%s'\n", tmp);
+            exit(2);
+        }
+        opts->trace = tracePath;
+    }
+
+    if (opts->tcpdump) {
+        if (qemu_tcpdump_start(opts->tcpdump) < 0) {
+            dwarning( "could not start packet capture: %s", strerror(errno));
+        }
+    }
+
+    if (opts->no_cache)
+        opts->cache = 0;
+
+    if (opts->dns_server) {
+        char*  x = strchr(opts->dns_server, ',');
+        dns_count = 0;
+        if (x == NULL)
+        {
+            if ( add_dns_server( opts->dns_server ) == 0 )
+                dns_count = 1;
+        }
+        else
+        {
+            x = strdup(opts->dns_server);
+            while (*x) {
+                char*  y = strchr(x, ',');
+
+                if (y != NULL)
+                    *y = 0;
+
+                if (y == NULL || y > x) {
+                    if ( add_dns_server( x ) == 0 )
+                        dns_count += 1;
+                }
+
+                if (y == NULL)
+                    break;
+
+                x = y+1;
+            }
+        }
+        if (dns_count == 0)
+            fprintf( stderr, "### WARNING: will use system default DNS server\n" );
+    }
+
+    if (dns_count == 0)
+        dns_count = slirp_get_system_dns_servers();
+
+    n = 1;
+    /* generate arguments for the underlying qemu main() */
+    args[n++] = "-kernel";
+    args[n++] = (char*) avdInfo_getImageFile(android_avdInfo, AVD_IMAGE_KERNEL);
+
+    args[n++] = "-initrd";
+    args[n++] = (char*) avdInfo_getImageFile(android_avdInfo, AVD_IMAGE_RAMDISK);
+
+    {
+        const char*  filetype = "file";
+
+        if (avdInfo_isImageReadOnly(android_avdInfo, AVD_IMAGE_INITSYSTEM))
+            filetype = "initfile";
+
+        bufprint(tmp, tmpend,
+             "system,size=0x4200000,%s=%s", filetype,
+             avdInfo_getImageFile(android_avdInfo, AVD_IMAGE_INITSYSTEM));
+
+        args[n++] = "-nand";
+        args[n++] = strdup(tmp);
+    }
+
+    bufprint(tmp, tmpend,
+             "userdata,size=0x4200000,file=%s",
+             avdInfo_getImageFile(android_avdInfo, AVD_IMAGE_USERDATA));
+
+    args[n++] = "-nand";
+    args[n++] = strdup(tmp);
+
+    if (hw->disk_cachePartition) {
+        opts->cache = (char*) avdInfo_getImageFile(android_avdInfo, AVD_IMAGE_CACHE);
+        cachePartitionSize = hw->disk_cachePartition_size;
+    }
+    else if (opts->cache) {
+        dwarning( "Emulated hardware doesn't support a cache partition" );
+        opts->cache    = NULL;
+        opts->no_cache = 1;
+    }
+
+    if (opts->cache) {
+        /* use a specific cache file */
+        sprintf(tmp, "cache,size=0x%0x,file=%s", cachePartitionSize, opts->cache);
+        args[n++] = "-nand";
+        args[n++] = strdup(tmp);
+    }
+    else if (!opts->no_cache) {
+        /* create a temporary cache partition file */
+        sprintf(tmp, "cache,size=0x%0x", cachePartitionSize);
+        args[n++] = "-nand";
+        args[n++] = strdup(tmp);
+    }
+
+    if (hw->hw_sdCard != 0)
+        opts->sdcard = (char*) avdInfo_getImageFile(android_avdInfo, AVD_IMAGE_SDCARD);
+    else if (opts->sdcard) {
+        dwarning( "Emulated hardware doesn't support SD Cards" );
+        opts->sdcard = NULL;
+    }
+
+    if(opts->sdcard) {
+        uint64_t  size;
+        if (path_get_size(opts->sdcard, &size) == 0) {
+            /* see if we have an sdcard image.  get its size if it exists */
+            if (size < 8*1024*1024ULL) {
+                fprintf(stderr, "### WARNING: SD Card files must be at least 8 MB, ignoring '%s'\n", opts->sdcard);
+            } else {
+                args[n++] = "-hda";
+                args[n++] = opts->sdcard;
+                use_sdcard_img = 1;
+            }
+        } else {
+            D("no SD Card image at '%s'", opts->sdcard);
+        }
+    }
+
+    if (!opts->logcat || opts->logcat[0] == 0) {
+        opts->logcat = getenv("ANDROID_LOG_TAGS");
+        if (opts->logcat && opts->logcat[0] == 0)
+            opts->logcat = NULL;
+    }
+
+#if 0
+    if (opts->console) {
+        derror( "option -console is obsolete. please use -shell instead" );
+        exit(1);
+    }
+#endif
+
+    /* we always send the kernel messages from ttyS0 to android_kmsg */
+    {
+        AndroidKmsgFlags  flags = 0;
+
+        if (opts->show_kernel)
+            flags |= ANDROID_KMSG_PRINT_MESSAGES;
+
+        android_kmsg_init( flags );
+        args[n++] = "-serial";
+        args[n++] = "android-kmsg";
+        serial++;
+    }
+
+    /* XXXX: TODO: implement -shell and -logcat through qemud instead */
+    if (!opts->shell_serial) {
+#ifdef _WIN32
+        opts->shell_serial = "con:";
+#else
+        opts->shell_serial = "stdio";
+#endif
+    }
+    else
+        opts->shell = 1;
+
+    if (opts->shell || opts->logcat) {
+        args[n++] = "-serial";
+        args[n++] = opts->shell_serial;
+        shell_serial = serial++;
+    }
+
+    if (opts->old_system)
+    {
+        if (opts->radio) {
+            args[n++] = "-serial";
+            args[n++] = opts->radio;
+            radio_serial = serial++;
+        }
+        else {
+            args[n++] = "-serial";
+            args[n++] = "android-modem";
+            radio_serial = serial++;
+        }
+        if (opts->gps) {
+            args[n++] = "-serial";
+            args[n++] = opts->gps;
+            gps_serial = serial++;
+        }
+    }
+    else /* !opts->old_system */
+    {
+        args[n++] = "-serial";
+        args[n++] = "android-qemud";
+        qemud_serial = serial++;
+
+        if (opts->radio) {
+            CharDriverState*  cs = qemu_chr_open(opts->radio);
+            if (cs == NULL) {
+                derror( "unsupported character device specification: %s\n"
+                        "used -help-char-devices for list of available formats\n", opts->radio );
+                exit(1);
+            }
+            android_qemud_set_channel( ANDROID_QEMUD_GSM, cs);
+        }
+        else if ( hw->hw_gsmModem != 0 ) {
+            if ( android_qemud_get_channel( ANDROID_QEMUD_GSM, &android_modem_cs ) < 0 ) {
+                derror( "could not initialize qemud 'gsm' channel" );
+                exit(1);
+            }
+        }
+
+        if (opts->gps) {
+            CharDriverState*  cs = qemu_chr_open(opts->gps);
+            if (cs == NULL) {
+                derror( "unsupported character device specification: %s\n"
+                        "used -help-char-devices for list of available formats\n", opts->gps );
+                exit(1);
+            }
+            android_qemud_set_channel( ANDROID_QEMUD_GPS, cs);
+        }
+        else if ( hw->hw_gps != 0 ) {
+            if ( android_qemud_get_channel( "gps", &android_gps_cs ) < 0 ) {
+                derror( "could not initialize qemud 'gps' channel" );
+                exit(1);
+            }
+        }
+    }
+
+    if (opts->memory) {
+        char*  end;
+        long   ramSize = strtol(opts->memory, &end, 0);
+        if (ramSize < 0 || *end != 0) {
+            derror( "-memory must be followed by a positive integer" );
+            exit(1);
+        }
+        if (ramSize < 32 || ramSize > 4096) {
+            derror( "physical memory size must be between 32 and 4096 MB" );
+            exit(1);
+        }
+    }
+    if (!opts->memory) {
+        bufprint(tmp, tmpend, "%d", hw->hw_ramSize);
+        opts->memory = qemu_strdup(tmp);
+    }
+
+    if (opts->no_audio) {
+        args[n++] = "-noaudio";
+    }
+
+    if (opts->trace) {
+        args[n++] = "-trace";
+        args[n++] = opts->trace;
+        args[n++] = "-tracing";
+        args[n++] = "off";
+    }
+
+    args[n++] = "-append";
+
+    if (opts->bootchart) {
+        char*  end;
+        int    timeout = strtol(opts->bootchart, &end, 10);
+        if (timeout == 0)
+            opts->bootchart = NULL;
+        else if (timeout < 0 || timeout > 15*60) {
+            derror( "timeout specified for -bootchart option is invalid.\n"
+                    "please use integers between 1 and 900\n");
+            exit(1);
+        }
+    }
+
+    {
+        static char  params[1024];
+        char        *p = params, *end = p + sizeof(params);
+
+        p = bufprint(p, end, "qemu=1 console=ttyS0" );
+
+        if (opts->shell || opts->logcat) {
+            p = bufprint(p, end, " androidboot.console=ttyS%d", shell_serial );
+        }
+
+        if (opts->trace) {
+            p = bufprint(p, end, " android.tracing=1");
+        }
+
+        if (!opts->no_jni) {
+            p = bufprint(p, end, " android.checkjni=1");
+        }
+
+        if (opts->no_boot_anim) {
+            p = bufprint( p, end, " android.bootanim=0" );
+        }
+
+        if (opts->logcat) {
+            char*  q = bufprint(p, end, " androidboot.logcat=%s", opts->logcat);
+
+            if (q < end) {
+                /* replace any space by a comma ! */
+                {
+                    int  nn;
+                    for (nn = 1; p[nn] != 0; nn++)
+                        if (p[nn] == ' ' || p[nn] == '\t')
+                            p[nn] = ',';
+                    p += nn;
+                }
+            }
+            p = q;
+        }
+
+        if (opts->old_system)
+        {
+            p = bufprint(p, end, " android.ril=ttyS%d", radio_serial);
+
+            if (opts->gps) {
+                p = bufprint(p, end, " android.gps=ttyS%d", gps_serial);
+            }
+        }
+        else
+        {
+            p = bufprint(p, end, " android.qemud=ttyS%d", qemud_serial);
+        }
+
+        if (dns_count > 0) {
+            p = bufprint(p, end, " android.ndns=%d", dns_count);
+        }
+
+        if (opts->bootchart) {
+            p = bufprint(p, end, " androidboot.bootchart=%s", opts->bootchart);
+        }
+
+        if (p >= end) {
+            fprintf(stderr, "### ERROR: kernel parameters too long\n");
+            exit(1);
+        }
+
+        args[n++] = strdup(params);
+    }
+
+    /* physical memory */
+    args[n++] = "-m";
+    args[n++] = opts->memory;
+
+    /* on Linux, the 'dynticks' clock sometimes doesn't work
+     * properly. this results in the UI freezing while emulation
+     * continues, for several seconds...
+     */
+#ifdef __linux__
+    args[n++] = "-clock";
+    args[n++] = "unix";
+#endif
+
+    while(argc-- > 0) {
+        args[n++] = *argv++;
+    }
+    args[n] = 0;
+
+    if(VERBOSE_CHECK(init)) {
+        int i;
+        for(i = 0; i < n; i++) {
+            fprintf(stdout, "emulator: argv[%02d] = \"%s\"\n", i, args[i]);
+        }
+    }
+    return qemu_main(n, args);
+}
+
+/* this function is called from qemu_main() once all arguments have been parsed
+ * it should be used to setup any Android-specific items in the emulation before the
+ * main loop runs
+ */
+void  android_emulation_setup( void )
+{
+    int   tries     = 16;
+    int   base_port = 5554;
+    int   success   = 0;
+    int   s;
+    uint32_t  guest_ip;
+
+    AndroidOptions*  opts = qemulator->opts;
+
+    inet_strtoip("10.0.2.15", &guest_ip);
+
+#if 0
+    if (opts->adb_port) {
+        fprintf( stderr, "option -adb-port is obsolete, use -port instead\n" );
+        exit(1);
+    }
+#endif
+
+    if (opts->port && opts->ports) {
+        fprintf( stderr, "options -port and -ports cannot be used together.\n");
+        exit(1);
+    }
+
+    if (opts->ports) {
+        char* comma_location;
+        char* end;
+        int console_port = strtol( opts->ports, &comma_location, 0 );
+
+        if ( comma_location == NULL || *comma_location != ',' ) {
+            derror( "option -ports must be followed by two comma separated positive integer numbers" );
+            exit(1);
+        }
+
+        int adb_port = strtol( comma_location+1, &end, 0 );
+
+        if ( end == NULL || *end ) {
+            derror( "option -ports must be followed by two comma separated positive integer numbers" );
+            exit(1);
+        }
+
+        if ( console_port == adb_port ) {
+            derror( "option -ports must be followed by two different integer numbers" );
+            exit(1);
+        }
+
+        slirp_redir( 0, adb_port, guest_ip, 5555 );
+        if ( control_console_start( console_port ) < 0 ) {
+            slirp_unredir( 0, adb_port );
+        }
+
+        base_port = console_port;
+    } else {
+        if (opts->port) {
+            char*  end;
+            int    port = strtol( opts->port, &end, 0 );
+            if ( end == NULL || *end ||
+                (unsigned)((port - base_port) >> 1) >= (unsigned)tries ) {
+                derror( "option -port must be followed by an even integer number between %d and %d\n",
+                        base_port, base_port + (tries-1)*2 );
+                exit(1);
+            }
+            if ( (port & 1) != 0 ) {
+                port &= ~1;
+                dwarning( "option -port must be followed by an even integer, using  port number %d\n",
+                          port );
+            }
+            base_port = port;
+            tries     = 1;
+        }
+
+        for ( ; tries > 0; tries--, base_port += 2 ) {
+
+            /* setup first redirection for ADB, the Android Debug Bridge */
+            if ( slirp_redir( 0, base_port+1, guest_ip, 5555 ) < 0 )
+                continue;
+
+            /* setup second redirection for the emulator console */
+            if ( control_console_start( base_port ) < 0 ) {
+                slirp_unredir( 0, base_port+1 );
+                continue;
+            }
+
+            D( "control console listening on port %d, ADB on port %d", base_port, base_port+1 );
+            success = 1;
+            break;
+        }
+
+        if (!success) {
+            fprintf(stderr, "it seems too many emulator instances are running on this machine. Aborting\n" );
+            exit(1);
+        }
+    }
+
+    if (opts->report_console) {
+        report_console(opts->report_console, base_port);
+    }
+
+    android_modem_init( base_port );
+
+    android_base_port = base_port;
+   /* send a simple message to the ADB host server to tell it we just started.
+    * it should be listening on port 5037. if we can't reach it, don't bother
+    */
+    do
+    {
+        SockAddress  addr;
+        char         tmp[32];
+
+        s = socket_create_inet( SOCKET_STREAM );
+        if (s < 0) {
+            D("can't create socket to talk to the ADB server");
+            break;
+        }
+
+        sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, 5037 );
+        if (socket_connect( s, &addr ) < 0) {
+            D("can't connect to ADB server: %s", errno_str );
+            break;
+        }
+
+        sprintf(tmp,"0012host:emulator:%d",base_port+1);
+        socket_send(s, tmp, 18+4);
+        D("sent '%s' to ADB server", tmp);
+    }
+    while (0);
+
+    if (s >= 0)
+        socket_close(s);
+
+    /* setup the http proxy, if any */
+    if (VERBOSE_CHECK(proxy))
+        proxy_set_verbose(1);
+
+    if (!opts->http_proxy) {
+        opts->http_proxy = getenv("http_proxy");
+    }
+
+    do
+    {
+        const char*  env = opts->http_proxy;
+        int          envlen;
+        ProxyOption  option_tab[4];
+        ProxyOption* option = option_tab;
+        char*        p;
+        char*        q;
+        const char*  proxy_name;
+        int          proxy_name_len;
+        int          proxy_port;
+
+        if (!env)
+            break;
+
+        envlen = strlen(env);
+
+        /* skip the 'http://' header, if present */
+        if (envlen >= 7 && !memcmp(env, "http://", 7)) {
+            env    += 7;
+            envlen -= 7;
+        }
+
+        /* do we have a username:password pair ? */
+        p = strchr(env, '@');
+        if (p != 0) {
+            q = strchr(env, ':');
+            if (q == NULL) {
+            BadHttpProxyFormat:
+                dprint("http_proxy format unsupported, try 'proxy:port' or 'username:password@proxy:port'");
+                break;
+            }
+
+            option->type       = PROXY_OPTION_AUTH_USERNAME;
+            option->string     = env;
+            option->string_len = q - env;
+            option++;
+
+            option->type       = PROXY_OPTION_AUTH_PASSWORD;
+            option->string     = q+1;
+            option->string_len = p - (q+1);
+            option++;
+
+            env = p+1;
+        }
+
+        p = strchr(env,':');
+        if (p == NULL)
+            goto BadHttpProxyFormat;
+
+        proxy_name     = env;
+        proxy_name_len = p - env;
+        proxy_port     = atoi(p+1);
+
+        D( "setting up http proxy:  server=%.*s port=%d",
+                proxy_name_len, proxy_name, proxy_port );
+
+        if ( proxy_http_setup( proxy_name, proxy_name_len, proxy_port,
+                               option - option_tab, option_tab ) < 0 )
+        {
+            dprint( "http proxy setup failed, check your $http_proxy variable");
+        }
+    }
+    while (0);
+
+   /* cool, now try to run the "ddms ping" command, which will take care of pinging usage
+    * if the user agreed for it. the emulator itself never sends anything to any outside
+    * machine
+    */
+    {
+#ifdef _WIN32
+#  define  _ANDROID_PING_PROGRAM   "ddms.bat"
+#else
+#  define  _ANDROID_PING_PROGRAM   "ddms"
+#endif
+
+        char         tmp[PATH_MAX];
+        const char*  appdir = get_app_dir();
+
+        if (snprintf( tmp, PATH_MAX, "%s%s%s", appdir, PATH_SEP,
+                      _ANDROID_PING_PROGRAM ) >= PATH_MAX) {
+            dprint( "Application directory too long: %s", appdir);
+            return;
+        }
+
+        /* if the program isn't there, don't bother */
+        D( "ping program: %s", tmp);
+        if (path_exists(tmp)) {
+#ifdef _WIN32
+            STARTUPINFO           startup;
+            PROCESS_INFORMATION   pinfo;
+
+            ZeroMemory( &startup, sizeof(startup) );
+            startup.cb = sizeof(startup);
+            startup.dwFlags = STARTF_USESHOWWINDOW;
+            startup.wShowWindow = SW_SHOWMINIMIZED;
+
+            ZeroMemory( &pinfo, sizeof(pinfo) );
+
+            char* comspec = getenv("COMSPEC");
+            if (!comspec) comspec = "cmd.exe";
+
+            // Run
+            char args[PATH_MAX + 30];
+            if (snprintf( args, PATH_MAX, "/C \"%s\" ping emulator " VERSION_STRING,
+                          tmp) >= PATH_MAX ) {
+                D( "DDMS path too long: %s", tmp);
+                return;
+            }
+
+            CreateProcess(
+                comspec,                                      /* program path */
+                args,                                    /* command line args */
+                NULL,                    /* process handle is not inheritable */
+                NULL,                     /* thread handle is not inheritable */
+                FALSE,                       /* no, don't inherit any handles */
+                DETACHED_PROCESS,   /* the new process doesn't have a console */
+                NULL,                       /* use parent's environment block */
+                NULL,                      /* use parent's starting directory */
+                &startup,                   /* startup info, i.e. std handles */
+                &pinfo );
+
+            D( "ping command: %s %s", comspec, args );
+#else
+            int  pid = fork();
+            if (pid == 0) {
+                int  fd = open("/dev/null", O_WRONLY);
+                dup2(fd, 1);
+                dup2(fd, 2);
+                execl( tmp, _ANDROID_PING_PROGRAM, "ping", "emulator", VERSION_STRING, NULL );
+            }
+            /* don't do anything in the parent or in case of error */
+            strncat( tmp, " ping emulator " VERSION_STRING, PATH_MAX - strlen(tmp) );
+            D( "ping command: %s", tmp );
+#endif
+        }
+    }
+}
+
+
+void  android_emulation_teardown( void )
+{
+}
diff --git a/android/qemud.c b/android/qemud.c
new file mode 100644
index 0000000..b127fc9
--- /dev/null
+++ b/android/qemud.c
@@ -0,0 +1,456 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/qemud.h"
+#include "android/utils/debug.h"
+#include "android/utils/misc.h"
+#include "qemu-char.h"
+#include "charpipe.h"
+#include "cbuffer.h"
+
+#define  D(...)    VERBOSE_PRINT(qemud,__VA_ARGS__)
+#define  D_ACTIVE  VERBOSE_CHECK(qemud)
+
+/* the T(...) macro is used to dump traffic */
+#define  T_ACTIVE   0
+
+#if T_ACTIVE
+#define  T(...)    VERBOSE_PRINT(qemud,__VA_ARGS__)
+#else
+#define  T(...)    ((void)0)
+#endif
+
+#define  MAX_PAYLOAD   4000
+#define  MAX_CHANNELS  8
+
+#define  CHANNEL_CONTROL_INDEX 0
+
+/** packets
+ **/
+#define  HEADER_SIZE  6
+
+typedef struct Packet {
+    struct Packet*  next;
+    int             len;
+    uint8_t         header[HEADER_SIZE];
+    uint8_t         data[MAX_PAYLOAD];
+} Packet;
+
+static Packet*  _free_packets;
+
+static void
+packet_free( Packet*  p )
+{
+    p->next       = _free_packets;
+    _free_packets = p;
+}
+
+static Packet*
+packet_alloc( void )
+{
+    Packet*  p = _free_packets;
+    if (p != NULL) {
+        _free_packets = p->next;
+    } else {
+        p = malloc(sizeof(*p));
+        if (p == NULL) {
+            derror("%s: not enough memory", __FUNCTION__);
+            exit(1);
+        }
+    }
+    p->next = NULL;
+    p->len  = 0;
+    return p;
+}
+
+/** channels
+ **/
+typedef void (*EnqueueFunc)( void*  user, Packet*  p );
+
+typedef struct {
+    const char*      name;
+    int              index;
+    CharDriverState* cs;
+    EnqueueFunc      enq_func;
+    void*            enq_user;
+} Channel;
+
+
+static int
+channel_can_read( void*  opaque )
+{
+    Channel*  c = opaque;
+
+    return c->index < 0 ? 0 : MAX_PAYLOAD;
+}
+
+
+/* here, the data comes from the emulated device (e.g. GSM modem) through
+ * a charpipe, we simply need to send it through the multiplexer */
+static void
+channel_read( void* opaque, const uint8_t*  from, int  len )
+{
+    Channel*  c = opaque;
+
+    if (c->enq_func != NULL) {
+        Packet*   p = packet_alloc();
+
+        if (len > MAX_PAYLOAD)
+            len = MAX_PAYLOAD;
+
+        memcpy( p->data, from, len );
+        p->len = len + HEADER_SIZE;
+        int2hex( p->header+0, 4, len );
+        int2hex( p->header+4, 2, c->index );
+
+        c->enq_func( c->enq_user, p );
+    }
+    else
+    {
+        D("%s: discarding %d bytes for channel '%s'",
+          __FUNCTION__, len, c->name);
+    }
+}
+
+static void
+channel_init( Channel*  c, const char*  name, CharDriverState* peer_cs )
+{
+    c->name     = name;
+    c->index    = -1;
+    c->enq_func = NULL;
+    c->enq_user = NULL;
+    c->cs       = peer_cs;
+}
+
+
+static void
+channel_set_peer( Channel*  c, int  index, EnqueueFunc  enq_func, void*  enq_user )
+{
+    c->index = index;
+    qemu_chr_add_handlers( c->cs,
+                           channel_can_read,
+                           channel_read,
+                           NULL,
+                           c );
+    c->enq_func = enq_func;
+    c->enq_user = enq_user;
+}
+
+
+static int
+channel_write( Channel*c , const uint8_t*  buf, int  len )
+{
+    return qemu_chr_write( c->cs, buf, len );
+}
+
+/** multiplexer
+ **/
+#define  IN_BUFF_SIZE  (2*MAX_PAYLOAD)
+
+typedef struct {
+    CharDriverState*  cs;
+
+    CBuffer  in_cbuffer[1];
+    int      in_datalen;
+    int      in_channel;
+
+    int      count;
+    Channel  channels[MAX_CHANNELS];
+    uint8_t  in_buff[ IN_BUFF_SIZE + HEADER_SIZE ];
+} Multiplexer;
+
+
+/* called by channel_read when data comes from an emulated
+ * device, and needs to be multiplexed through the serial
+ * port
+ */
+static void
+multiplexer_enqueue( Multiplexer*  m, Packet*  p )
+{
+    T("%s: sending %d bytes: '%s'", __FUNCTION__,
+       p->len - HEADER_SIZE, quote_bytes( p->data, p->len - HEADER_SIZE ) );
+
+    qemu_chr_write( m->cs, p->header, HEADER_SIZE );
+    qemu_chr_write( m->cs, p->data, p->len - HEADER_SIZE );
+    packet_free(p);
+}
+
+/* called when we received a channel registration from the
+ * qemud daemon
+ */
+static void
+multiplexer_register_channel( Multiplexer*  m,
+                              const char*   name,
+                              int           index )
+{
+    Channel*  c = m->channels;
+    Channel*  c_end = c + m->count;
+
+    for ( ; c < c_end; c++ ) {
+        if ( !strcmp(c->name, name) )
+            break;
+    }
+
+    if (c >= c_end) {
+        D( "%s: unknown channel name '%s'",
+            __FUNCTION__, name );
+        return;
+    }
+
+    if (c->index >= 0) {
+        D( "%s: channel '%s' re-assigned index %d",
+            __FUNCTION__, name, index );
+        c->index = index;
+        return;
+    }
+    channel_set_peer( c, index, (EnqueueFunc) multiplexer_enqueue, m );
+    D( "%s: channel '%s' registered as index %d",
+       __FUNCTION__, c->name, c->index );
+}
+
+
+/* handle answers from the control channel */
+static void
+multiplexer_handle_control( Multiplexer*  m, Packet*  p )
+{
+    int  len = p->len - HEADER_SIZE;
+
+    /* for now, the only supported answer is 'ok:connect:<name>:<XX>' where
+     * <XX> is a hexdecimal channel numner */
+    D( "%s: received '%s'", __FUNCTION__, quote_bytes( (const void*)p->data, (unsigned)len ) );
+    if ( !memcmp( p->data, "ok:connect:", 11 ) ) do {
+        char*  name = (char*)p->data + 11;
+        char*  q    = strchr( name, ':' );
+        int    index;
+
+        if (q == NULL)
+            break;
+
+        q[0] = 0;
+        if (q + 3 > (char*)p->data + len)
+            break;
+
+        index = hex2int( (uint8_t*)q+1, 2 );
+        if (index < 0)
+            break;
+
+        multiplexer_register_channel( m, name, index );
+        goto Exit;
+    }
+    while(0);
+
+    D( "%s: unsupported message !!", __FUNCTION__ );
+Exit:
+    packet_free(p);
+}
+
+
+static int
+multiplexer_can_read( void*  opaque )
+{
+    Multiplexer* m = opaque;
+
+    return cbuffer_write_avail( m->in_cbuffer );
+}
+
+/* the data comes from the serial port, we need to reconstruct packets then
+ * dispatch them to the appropriate channel */
+static void
+multiplexer_read( void*  opaque, const uint8_t* from, int  len )
+{
+    Multiplexer*  m  = opaque;
+    CBuffer*      cb = m->in_cbuffer;
+    int           ret = 0;
+
+    T("%s: received %d bytes from serial: '%s'",
+      __FUNCTION__, len, quote_bytes( from, len ));
+
+    ret = cbuffer_write( cb, from, len );
+    if (ret == 0)
+        return;
+
+    for (;;) {
+        int   len = cbuffer_read_avail( cb );
+
+        if (m->in_datalen == 0) {
+            uint8_t  header[HEADER_SIZE];
+
+            if (len < HEADER_SIZE)
+                break;
+
+            cbuffer_read( cb, header, HEADER_SIZE );
+            m->in_datalen = hex2int( header+0, 4 );
+            m->in_channel = hex2int( header+4, 2 );
+        }
+        else
+        {
+            Packet*  p;
+
+            if (len < m->in_datalen)
+                break;
+
+            /* a full packet was received */
+            p = packet_alloc();
+            cbuffer_read( cb, p->data, m->in_datalen );
+            p->len = HEADER_SIZE + m->in_datalen;
+
+            /* find the channel for this packet */
+            if (m->in_channel == CHANNEL_CONTROL_INDEX)
+                multiplexer_handle_control( m, p );
+            else {
+                Channel*  c = m->channels;
+                Channel*  c_end = c + m->count;
+
+                for ( ; c < c_end; c++ ) {
+                    if (c->index == m->in_channel) {
+                        channel_write( c, p->data, m->in_datalen );
+                        break;
+                    }
+                }
+                packet_free(p);
+            }
+            m->in_datalen = 0;
+        }
+
+    }
+    return;
+}
+
+static void
+multiplexer_query_channel( Multiplexer*  m, const char*  name )
+{
+    Packet*  p = packet_alloc();
+    int      len;
+
+    len = snprintf( (char*)p->data, MAX_PAYLOAD, "connect:%s", name );
+
+    int2hex( p->header+0, 4, len );
+    int2hex( p->header+4, 2, CHANNEL_CONTROL_INDEX );
+    p->len = HEADER_SIZE + len;
+
+    multiplexer_enqueue( m, p );
+}
+
+
+static Channel*
+multiplexer_find_channel( Multiplexer*  m, const char*  name )
+{
+    int  n;
+    for (n = 0; n < m->count; n++)
+        if ( !strcmp(m->channels[n].name, name) )
+            return m->channels + n;
+
+    return NULL;
+}
+
+
+static Multiplexer       _multiplexer[1];
+static CharDriverState*  android_qemud_cs;
+
+extern void
+android_qemud_init( void )
+{
+    Multiplexer*      m = _multiplexer;
+
+    if (android_qemud_cs != NULL)
+        return;
+
+    m->count = 0;
+
+    cbuffer_reset( m->in_cbuffer, m->in_buff, sizeof(m->in_buff) );
+    m->in_datalen = 0;
+    m->in_channel = 0;
+
+    if (qemu_chr_open_charpipe( &android_qemud_cs, &m->cs ) < 0) {
+        derror( "%s: can't create charpipe to serial port",
+                __FUNCTION__ );
+        exit(1);
+    }
+
+    qemu_chr_add_handlers( m->cs, multiplexer_can_read,
+                           multiplexer_read, NULL, m );
+}
+
+
+CharDriverState*  android_qemud_get_cs( void )
+{
+    if (android_qemud_cs == NULL)
+        android_qemud_init();
+
+    return android_qemud_cs;
+}
+
+
+extern int
+android_qemud_get_channel( const char*  name, CharDriverState**  pcs )
+{
+    Multiplexer*      m = _multiplexer;
+    Channel*          c;
+    CharDriverState*  peer_cs;
+    int               ret;
+
+    if (m->cs == NULL)
+        android_qemud_init();
+
+    c = multiplexer_find_channel( m, name );
+    if (c) {
+        derror( "%s: trying to get already-opened qemud channel '%s'",
+                __FUNCTION__, name );
+        return -1;
+    }
+
+    if (m->count >= MAX_CHANNELS) {
+        derror( "%s: too many registered channels (%d)",
+                __FUNCTION__, m->count );
+        return -1;
+    }
+
+    c = m->channels + m->count;
+
+    ret = qemu_chr_open_charpipe( &peer_cs, pcs );
+    if (ret == 0) {
+        channel_init(c, name, peer_cs);
+        m->count += 1;
+        multiplexer_query_channel( m, c->name );
+    }
+
+    return ret;
+}
+
+extern int
+android_qemud_set_channel( const char*  name, CharDriverState*  peer_cs )
+{
+    Multiplexer*  m = _multiplexer;
+    Channel*      c;
+
+    if (m->cs == NULL)
+        android_qemud_init();
+
+    c = multiplexer_find_channel(m, name);
+    if (c != NULL) {
+        derror( "%s: trying to set opened qemud channel '%s'",
+                __FUNCTION__, name );
+        return -1;
+    }
+
+    if (m->count >= MAX_CHANNELS) {
+        derror( "%s: too many registered channels (%d)",
+                __FUNCTION__, m->count );
+        return -1;
+    }
+
+    c = m->channels + m->count;
+    channel_init(c, name, peer_cs);
+    m->count += 1;
+    multiplexer_query_channel( m, c->name );
+
+    return 0;
+}
diff --git a/android/qemud.h b/android/qemud.h
new file mode 100644
index 0000000..4fa71d0
--- /dev/null
+++ b/android/qemud.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_qemud_h
+#define _android_qemud_h
+
+#include "qemu-common.h"
+
+/* recent versions of the emulated Android system contains a background
+ * daemon, named 'qemud', which runs as root and opens /dev/ttyS0
+ *
+ * its purpose is to multiplex several communication channels between
+ * the emulator and the system through a single serial port.
+ *
+ * each channel will be connected to a qemud-created unix socket on the
+ * system, and to either a emulated character device or other facility in
+ * the emulator.
+ *
+ *                                                       +--------> /dev/socket/qemud_gsm
+ *   emulated GSM    <-----+                       ______|_
+ *                         |        emulated      |        |
+ *                         +====> /dev/ttyS0 <===>| qemud  |------> /dev/socket/qemud_gps
+ *                         |                      |________|
+ *   emulated GPS    <-----+                            |
+ *                         |                            +---------> other
+ *                         |
+ *   other  <--------------+
+ *
+ *
+ *   this is done to overcome specific permission problems, as well as to add various
+ *   features that would require special kernel drivers otherwise even though they
+ *   only need a simple character channel.
+ */
+
+/* initialize the qemud support code in the emulator
+ */
+
+extern void  android_qemud_init( void );
+
+/* return the character driver state object that needs to be connected to the
+ * emulated serial port where all multiplexed channels go through.
+ */
+extern CharDriverState*  android_qemud_get_cs( void );
+
+/* return the character driver state corresponding to a named qemud communication
+ * channel. this can be used to send/data the channel.
+ * returns 0 on success, or -1 in case of error
+ */
+extern int  android_qemud_get_channel( const char*  name, CharDriverState* *pcs );
+
+/* set the character driver state for a given qemud communication channel. this
+ * is used to attach the channel to an external char driver device directly.
+ * returns 0 on success, -1 on error
+ */
+extern int  android_qemud_set_channel( const char*  name, CharDriverState*  peer_cs );
+
+/* list of known qemud channel names */
+#define  ANDROID_QEMUD_GSM      "gsm"
+#define  ANDROID_QEMUD_GPS      "gps"
+#define  ANDROID_QEMUD_CONTROL  "control"
+
+/* add new channel names here when you need them */
+
+#endif /* _android_qemud_h */
diff --git a/android/resource.c b/android/resource.c
new file mode 100644
index 0000000..1327ea0
--- /dev/null
+++ b/android/resource.c
@@ -0,0 +1,64 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/resource.h"
+#include "config-host.h"
+#include <string.h>
+
+typedef struct {
+    const char*           name;
+    const unsigned char*  base;
+    size_t                size;
+} FileEntry;
+
+const unsigned char*
+_resource_find( const char*       name,
+                const FileEntry*  entries,
+                size_t           *psize )
+{
+    const FileEntry*  e = entries;
+
+    for ( ; e->name != NULL; e++ ) {
+        //dprint("SCAN %s\n", e->name);
+        if ( strcmp(e->name, name) == 0 ) {
+            *psize = e->size;
+            return e->base;
+        }
+    }
+    return NULL;
+}
+
+#undef   _file_entries
+#define  _file_entries  _skin_entries
+const unsigned char*
+android_resource_find( const char*  name,
+                       size_t      *psize )
+{
+#    include "android/skin/default.h"
+    return _resource_find( name, _file_entries, psize );
+}
+
+#undef   _file_entries
+#define  _file_entries  _icon_entries
+
+const unsigned char*
+android_icon_find( const char*  name,
+                   size_t      *psize )
+{
+#ifdef _WIN32
+    return NULL;
+#else
+#   include "android/icons.h"
+    return _resource_find( name, _file_entries, psize );
+#endif
+}
+
+
diff --git a/android/resource.h b/android/resource.h
new file mode 100644
index 0000000..00453af
--- /dev/null
+++ b/android/resource.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_RESOURCE_H
+#define _ANDROID_RESOURCE_H
+
+#include <stddef.h>
+
+extern const unsigned char*
+android_resource_find( const char*    name,
+                       size_t        *psize );
+
+extern const unsigned char*
+android_icon_find( const char*   name,
+                   size_t       *psize );
+
+#endif /* END */
+
diff --git a/android/skin/argb.h b/android/skin/argb.h
new file mode 100644
index 0000000..b3f0a6d
--- /dev/null
+++ b/android/skin/argb.h
@@ -0,0 +1,856 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+/* this file contains template code and may be included multiple times */
+
+#ifndef ARGB_T_DEFINED
+#define ARGB_T_DEFINED
+
+#if USE_MMX
+#include <mmintrin.h>
+
+typedef __m64   mmx_t;
+typedef  mmx_t  argb_t;
+
+static inline mmx_t
+mmx_load8888( unsigned  value, mmx_t  zero )
+{
+    return _mm_unpacklo_pi8( _mm_cvtsi32_si64 (value), zero);
+}
+
+static inline unsigned
+mmx_save8888( mmx_t   argb, mmx_t  zero )
+{
+    return (unsigned) _mm_cvtsi64_si32( _mm_packs_pu16( argb, zero ) );
+}
+
+static inline mmx_t
+mmx_expand16( int  value )
+{
+    mmx_t  t1 = _mm_cvtsi32_si64( value );
+    return _mm_packs_pi32( t1, t1 );
+}
+
+static inline int
+mmx_makescale( double  s )
+{
+    return (int)(s*(1 << 16));
+}
+
+static inline mmx_t
+mmx_mulshift( mmx_t   argb, int  multiplier, int  rshift, mmx_t  zero )
+{
+    mmx_t   ar   = _mm_unpackhi_pi16(argb, zero );
+    mmx_t   gb   = _mm_unpacklo_pi16(argb, zero );
+    mmx_t   mult = mmx_expand16(multiplier);
+
+    ar = _mm_srli_pi32( _mm_madd_pi16( ar, mult ), rshift );
+    gb = _mm_srli_pi32( _mm_madd_pi16( gb, mult ), rshift );
+
+    return _mm_packs_pi32( gb, ar );
+}
+
+static inline mmx_t
+mmx_interp255( mmx_t  m1, mmx_t  m2, mmx_t  zero, int  alpha )
+{
+    mmx_t  mult, mult2, t1, t2, r1, r2;
+
+    // m1 = [ a1 | r1 | g1 | b1 ]
+    // m2 = [ a2 | r2 | g2 | b2 ]
+    alpha = (alpha << 16) | (alpha ^ 255);
+    mult  = _mm_cvtsi32_si64( alpha );                   // mult  = [  0  |  0  |  a  | 1-a ]
+    mult2 = _mm_slli_si64( mult, 32 );                   // mult2 = [  a  | 1-a |  0  |  0  ]
+    mult  = _mm_or_si64( mult, mult2 );                  // mults = [  a  | 1-a |  a  | 1-a ]
+
+    t1 = _mm_unpackhi_pi16( m1, m2 );    // t1 = [ a2 | a1 | r2 | r1 ]
+    r1 = _mm_madd_pi16( t1, mult );      // r1 = [   ra    |    rr   ]
+
+    t2 = _mm_unpacklo_pi16( m1, m2 );    // t1 = [ g2 | g1 | b2 | b1 ]
+    r2 = _mm_madd_pi16( t2, mult );      // r2 = [   rg    |    rb   ]
+
+    r1 = _mm_srli_pi32( r1, 8 );
+    r2 = _mm_srli_pi32( r2, 8 );
+
+    return  _mm_packs_pi32( r2, r1 );
+}
+
+#define   ARGB_DECL_ZERO()      mmx_t    _zero = _mm_setzero_si64()
+#define   ARGB_DECL(x)          mmx_t    x
+#define   ARGB_DECL2(x1,x2)     mmx_t    x1, x2
+#define   ARGB_ZERO(x)          x = _zero
+#define   ARGB_UNPACK(x,v)      x =  mmx_load8888((v), _zero)
+#define   ARGB_PACK(x)          mmx_save8888(x, _zero)
+#define   ARGB_COPY(x,y)        x = y
+#define   ARGB_SUM(x1,x2,x3)    x1 = _mm_add_pi32(x2, x3)
+#define   ARGB_REDUCE(x,red)   \
+    ({ \
+        int  _red = (red) >> 8;  \
+        if (_red < 256) \
+            x = mmx_mulshift( x, _red, 8, _zero ); \
+    })
+
+#define  ARGB_INTERP255(x1,x2,x3,alpha)  \
+    x1 = mmx_interp255( x2, x3, _zero, (alpha))
+
+#define    ARGB_ADDW_11(x1,x2,x3)  \
+    ARGB_SUM(x1,x2,x3)
+
+#define    ARGB_ADDW_31(x1,x2,x3)  \
+    ({ \
+        mmx_t   _t1 = _mm_add_pi16(x2, x3);  \
+        mmx_t   _t2 = _mm_slli_pi16(x2, 1);  \
+        x1 = _mm_add_pi16(_t1, _t2);  \
+    })
+
+#define    ARGB_ADDW_13(x1,x2,x3)  \
+    ({ \
+        mmx_t   _t1 = _mm_add_pi16(x2, x3);  \
+        mmx_t   _t2 = _mm_slli_pi16(x3, 1);  \
+        x1 = _mm_add_pi16(_t1, _t2);  \
+    })
+
+#define    ARGB_SHR(x1,x2,s)   \
+    x1 = _mm_srli_pi16(x2, s)
+
+
+#define    ARGB_MULSHIFT(x1,x2,v,s)   \
+    x1 = mmx_mulshift(x2, v, s, _zero)
+
+#define   ARGB_DONE  _mm_empty()
+
+#define   ARGB_RESCALE_SHIFT      10
+#define   ARGB_DECL_SCALE(s2,s)   int   s2 = (int)((s)*(s)*(1 << ARGB_RESCALE_SHIFT))
+#define   ARGB_RESCALE(x,s2)      x = mmx_mulshift( x, s2, ARGB_RESCALE_SHIFT, _zero )
+
+#else /* !USE_MMX */
+
+typedef uint32_t    argb_t;
+
+#define  ARGB_DECL_ZERO()   argb_t     _zero = 0
+#define  ARGB_DECL(x)       argb_t    x##_ag, x##_rb
+#define  ARGB_DECL2(x1,x2)  argb_t    x1##_ag, x1##_rb, x2##_ag, x2##_rb
+#define  ARGB_ZERO(x)       (x##_ag = x##_rb = 0)
+#define  ARGB_COPY(x,y)     (x##_ag = y##_ag, x##_rb = y##_rb)
+
+#define  ARGB_UNPACK(x,v)  \
+    ({ \
+        argb_t  _v = (argb_t)(v); \
+        x##_ag = (_v >> 8) & 0xff00ff; \
+        x##_rb = (_v)      & 0xff00ff; \
+    })
+
+#define  ARGB_PACK(x)      (uint32_t)(((x##_ag) << 8) | x##_rb)
+
+#define   ARGB_SUM(x1,x2,x3)  \
+    ({ \
+        x1##_ag = x2##_ag + x3##_ag; \
+        x1##_rb = x2##_rb + x3##_rb; \
+    })
+
+#define   ARGB_REDUCE(x,red)   \
+    ({ \
+        int  _red = (red) >> 8; \
+        if (_red < 256) { \
+            x##_ag = ((x##_ag*_red) >> 8) & 0xff00ff; \
+            x##_rb = ((x##_rb*_red) >> 8) & 0xff00ff; \
+        } \
+    })
+
+#define    ARGB_INTERP255(x1,x2,x3,alpha)  \
+    ({ \
+        int  _alpha = (alpha); \
+        int  _ialpha; \
+        _alpha += _alpha >> 8; \
+        _ialpha = 256 - _alpha; \
+        x1##_ag = ((x2##_ag*_ialpha + x3##_ag*_alpha) >> 8) & 0xff00ff;  \
+        x1##_rb = ((x2##_rb*_ialpha + x3##_rb*_alpha) >> 8) & 0xff00ff;  \
+    })
+
+#define    ARGB_ADDW_11(x1,x2,x3)  \
+    ({ \
+        x1##_ag = (x2##_ag + x3##_ag);  \
+        x1##_rb = (x2##_rb + x3##_rb);  \
+    })
+
+#define    ARGB_ADDW_31(x1,x2,x3)  \
+    ({ \
+        x1##_ag = (3*x2##_ag + x3##_ag);  \
+        x1##_rb = (3*x2##_rb + x3##_rb);  \
+    })
+
+#define    ARGB_ADDW_13(x1,x2,x3)  \
+    ({ \
+        x1##_ag = (x2##_ag + 3*x3##_ag);  \
+        x1##_rb = (x2##_rb + 3*x3##_rb);  \
+    })
+
+#define    ARGB_MULSHIFT(x1,x2,v,s)   \
+    ({ \
+        unsigned  _vv = (v);  \
+        x1##_ag = ((x2##_ag * _vv) >> (s)) & 0xff00ff;  \
+        x1##_rb = ((x2##_rb * _vv) >> (s)) & 0xff00ff;  \
+    })
+
+#define   ARGB_SHR(x1,x2,s)  \
+    ({  \
+        int  _s = (s);  \
+        x1##_ag = (x2##_ag >> _s) & 0xff00ff; \
+        x1##_rb = (x2##_rb >> _s) & 0xff00ff; \
+    })
+
+#define   ARGB_DONE  ((void)0)
+
+#define   ARGB_RESCALE_SHIFT      8
+#define   ARGB_DECL_SCALE(s2,s)   int   s2 = (int)((s)*(s)*(1 << ARGB_RESCALE_SHIFT))
+#define   ARGB_RESCALE(x,scale2)  ARGB_MULSHIFT(x,x,scale2,ARGB_RESCALE_SHIFT)
+
+#endif /* !USE_MMX */
+
+#define   ARGB_ADD(x1,x2)     ARGB_SUM(x1,x1,x2)
+#define   ARGB_READ(x,p)      ARGB_UNPACK(x,*(uint32_t*)(p))
+#define   ARGB_WRITE(x,p)     *(uint32_t*)(p) = ARGB_PACK(x)
+
+#endif /* !ARGB_T_DEFINED */
+
+
+
+#ifdef ARGB_SCALE_GENERIC
+static void
+ARGB_SCALE_GENERIC( ScaleOp*   op )
+{
+    int        dst_pitch = op->dst_pitch;
+    int        src_pitch = op->src_pitch;
+    uint8_t*   dst_line  = op->dst_line;
+    uint8_t*   src_line  = op->src_line;
+    ARGB_DECL_SCALE(scale2, op->scale);
+    int        h;
+    int        sx = op->sx;
+    int        sy = op->sy;
+    int        ix = op->ix;
+    int        iy = op->iy;
+
+    src_line += (sx >> 16)*4 + (sy >> 16)*src_pitch;
+    sx       &= 0xffff;
+    sy       &= 0xffff;
+
+    for ( h = op->rd.h; h > 0; h-- ) {
+        uint8_t*  dst = dst_line;
+        uint8_t*  src = src_line;
+        uint8_t*  dst_end = dst + 4*op->rd.w;
+        int       sx1 = sx;
+        int       sy1 = sy;
+
+        for ( ; dst < dst_end; ) {
+            int  sx2 = sx1 + ix;
+            int  sy2 = sy1 + iy;
+
+            ARGB_DECL_ZERO();
+            ARGB_DECL(spix);
+            ARGB_DECL(pix);
+            ARGB_ZERO(pix);
+
+            /* the current destination pixel maps to the (sx1,sy1)-(sx2,sy2)
+            * source square, we're going to compute the sum of its pixels'
+            * colors...  simple box filtering
+            */
+            {
+                int  gsy, gsx;
+                for ( gsy = 0; gsy < sy2; gsy += 65536 ) {
+                    for ( gsx = 0; gsx < sx2; gsx += 65536 ) {
+                        uint8_t*  s    = src + (gsx >> 16)*4 + (gsy >> 16)*src_pitch;
+                        int       xmin = gsx, xmax = gsx + 65536, ymin = gsy, ymax = gsy + 65536;
+                        unsigned  ww, hh;
+                        unsigned  red;
+
+                        if (xmin < sx1) xmin = sx1;
+                        if (xmax > sx2) xmax = sx2;
+                        if (ymin < sy1) ymin = sy1;
+                        if (ymax > sy2) ymax = sy2;
+
+                        ww = (unsigned)(xmax-xmin);
+                        red = ww;
+
+                        hh = (unsigned)(ymax-ymin);
+                        red = (hh < 65536) ? (red*hh >> 16U) : red;
+
+                        ARGB_READ(spix,s);
+                        ARGB_REDUCE(spix,red);
+                        ARGB_ADD(pix,spix);
+                    }
+                }
+            }
+
+            ARGB_RESCALE(pix,scale2);
+            ARGB_WRITE(pix,dst);
+
+            sx1  = sx2;
+            src += (sx1 >> 16)*4;
+            sx1 &= 0xffff;
+            dst += 4;
+        }
+
+        sy       += iy;
+        src_line += (sy >> 16)*src_pitch;
+        sy       &= 0xffff;
+
+        dst_line += dst_pitch;
+    }
+    ARGB_DONE;
+}
+#endif
+#undef  ARGB_SCALE_GENERIC
+
+
+#ifdef ARGB_SCALE_05_TO_10
+static inline int cross( int  x, int  y ) {
+    if (x == 65536 && y == 65536)
+        return 65536;
+
+    return (int)((unsigned)x * (unsigned)y >> 16U);
+}
+
+static void
+scale_05_to_10( ScaleOp*   op )
+{
+    int        dst_pitch = op->dst_pitch;
+    int        src_pitch = op->src_pitch;
+    uint8_t*   dst_line  = op->dst_line;
+    uint8_t*   src_line  = op->src_line;
+    ARGB_DECL_SCALE(scale2, op->scale);
+    int        h;
+    int        sx = op->sx;
+    int        sy = op->sy;
+    int        ix = op->ix;
+    int        iy = op->iy;
+
+    src_line += (sx >> 16)*4 + (sy >> 16)*src_pitch;
+    sx       &= 0xffff;
+    sy       &= 0xffff;
+
+    for ( h = op->rd.h; h > 0; h-- ) {
+        uint8_t*  dst = dst_line;
+        uint8_t*  src = src_line;
+        uint8_t*  dst_end = dst + 4*op->rd.w;
+        int       sx1 = sx;
+        int       sy1 = sy;
+
+        for ( ; dst < dst_end; ) {
+            int  sx2 = sx1 + ix;
+            int  sy2 = sy1 + iy;
+
+            ARGB_DECL_ZERO();
+            ARGB_DECL2(spix, pix);
+
+            int      off = src_pitch;
+            int      fx1 = sx1 & 0xffff;
+            int      fx2 = sx2 & 0xffff;
+            int      fy1 = sy1 & 0xffff;
+            int      fy2 = sy2 & 0xffff;
+
+            int      center_x = ((sx1 >> 16) + 1) < ((sx2-1) >> 16);
+            int      center_y = ((sy1 >> 16) + 1) < ((sy2-1) >> 16);
+
+            ARGB_ZERO(pix);
+
+            if (fx2 == 0) {
+                fx2  = 65536;
+            }
+            if (fy2 == 0) {
+                fy2  = 65536;
+            }
+            fx1 = 65536 - fx1;
+            fy1 = 65536 - fy1;
+
+            /** TOP BAND
+             **/
+
+            /* top-left pixel */
+            ARGB_READ(spix,src);
+            ARGB_REDUCE(spix,cross(fx1,fy1));
+            ARGB_ADD(pix,spix);
+
+            /* top-center pixel, if any */
+            ARGB_READ(spix,src + 4);
+            if (center_x) {
+                ARGB_REDUCE(spix,fy1);
+                ARGB_ADD(pix,spix);
+                ARGB_READ(spix,src + 8);
+            }
+
+            /* top-right pixel */
+            ARGB_REDUCE(spix,cross(fx2,fy1));
+            ARGB_ADD(pix,spix);
+
+            /** MIDDLE BAND, IF ANY
+             **/
+            if (center_y) {
+                /* left-middle pixel */
+                ARGB_READ(spix,src + off);
+                ARGB_REDUCE(spix,fx1);
+                ARGB_ADD(pix,spix);
+
+                /* center pixel, if any */
+                ARGB_READ(spix,src + off + 4);
+                if (center_x) {
+                    ARGB_ADD(pix,spix);
+                    ARGB_READ(spix,src + off + 8);
+                }
+
+                /* right-middle pixel */
+                ARGB_REDUCE(spix,fx2);
+                ARGB_ADD(pix,spix);
+
+                off += src_pitch;
+            }
+
+            /** BOTTOM BAND
+             **/
+            /* left-bottom pixel */
+            ARGB_READ(spix,src + off);
+            ARGB_REDUCE(spix,cross(fx1,fy2));
+            ARGB_ADD(pix,spix);
+
+            /* center-bottom, if any */
+            ARGB_READ(spix,src + off + 4);
+            if (center_x) {
+                ARGB_REDUCE(spix,fy2);
+                ARGB_ADD(pix,spix);
+                ARGB_READ(spix,src + off + 8);
+            }
+
+            /* right-bottom pixel */
+            ARGB_REDUCE(spix,cross(fx2,fy2));
+            ARGB_ADD(pix,spix);
+
+            /** WRITE IT
+             **/
+            ARGB_RESCALE(pix,scale2);
+            ARGB_WRITE(pix,dst);
+
+            sx1  = sx2;
+            src += (sx1 >> 16)*4;
+            sx1 &= 0xffff;
+            dst += 4;
+        }
+
+        sy       += iy;
+        src_line += (sy >> 16)*src_pitch;
+        sy       &= 0xffff;
+
+        dst_line += dst_pitch;
+    }
+    ARGB_DONE;
+}
+#endif
+#undef ARGB_SCALE_05_TO_10
+
+
+#ifdef ARGB_SCALE_UP_BILINEAR
+static void
+scale_up_bilinear( ScaleOp*  op )
+{
+    int        dst_pitch = op->dst_pitch;
+    int        src_pitch = op->src_pitch;
+    uint8_t*   dst_line  = op->dst_line;
+    uint8_t*   src_line  = op->src_line;
+    int        sx = op->sx;
+    int        sy = op->sy;
+    int        ix = op->ix;
+    int        iy = op->iy;
+    int        xlimit, ylimit;
+    int        h, sx0;
+
+    /* the center pixel is at (sx+ix/2, sy+iy/2), we then want to get */
+    /* the four nearest source pixels, which are at (0.5,0.5) offsets */
+
+    sx = sx + ix/2 - 32768;
+    sy = sy + iy/2 - 32768;
+
+    xlimit = (op->src_w-1);
+    ylimit = (op->src_h-1);
+
+    sx0 = sx;
+
+    for ( h = op->rd.h; h > 0; h-- ) {
+        uint8_t*  dst = dst_line;
+        uint8_t*  dst_end = dst + 4*op->rd.w;
+
+        sx = sx0;
+        for ( ; dst < dst_end; ) {
+            int        ex1, ex2, ey1, ey2, alpha;
+            uint8_t*   s;
+
+            ARGB_DECL_ZERO();
+            ARGB_DECL2(spix1,spix2);
+            ARGB_DECL2(pix3,pix4);
+            ARGB_DECL(pix);
+
+            /* find the four neighbours */
+            ex1 = (sx >> 16);
+            ey1 = (sy >> 16);
+            ex2 = (sx+65535) >> 16;
+            ey2 = (sy+65535) >> 16;
+
+            if (ex1 < 0) ex1 = 0; else if (ex1 > xlimit) ex1 = xlimit;
+            if (ey1 < 0) ey1 = 0; else if (ey1 > ylimit) ey1 = ylimit;
+            if (ex2 < 0) ex2 = 0; else if (ex2 > xlimit) ex2 = xlimit;
+            if (ey2 < 0) ey2 = 0; else if (ey2 > ylimit) ey2 = ylimit;
+
+            ex2 = (ex2-ex1)*4;
+            ey2 = (ey2-ey1)*src_pitch;
+
+            /* interpolate */
+            s   = src_line + ex1*4 + ey1*src_pitch;
+            ARGB_READ(spix1, s);
+            ARGB_READ(spix2, s+ex2);
+
+            alpha  = (sx >> 8) & 0xff;
+            ARGB_INTERP255(pix3,spix1,spix2,alpha);
+
+            s  += ey2;
+            ARGB_READ(spix1, s);
+            ARGB_READ(spix2, s+ex2);
+
+            ARGB_INTERP255(pix4,spix1,spix2,alpha);
+
+            alpha = (sy >> 8) & 0xff;
+            ARGB_INTERP255(pix,pix3,pix4,alpha);
+
+            ARGB_WRITE(pix,dst);
+
+            sx  += ix;
+            dst += 4;
+        }
+
+        sy       += iy;
+        dst_line += dst_pitch;
+    }
+    ARGB_DONE;
+}
+#endif
+#undef ARGB_SCALE_UP_BILINEAR
+
+#ifdef ARGB_SCALE_UP_QUICK_4x4
+static void
+ARGB_SCALE_UP_QUICK_4x4( ScaleOp*  op )
+{
+    int        dst_pitch = op->dst_pitch;
+    int        src_pitch = op->src_pitch;
+    uint8_t*   dst_line  = op->dst_line;
+    uint8_t*   src_line  = op->src_line;
+    int        sx = op->sx;
+    int        sy = op->sy;
+    int        ix = op->ix;
+    int        iy = op->iy;
+    int        xlimit, ylimit;
+    int        h, sx0;
+
+    /* the center pixel is at (sx+ix/2, sy+iy/2), we then want to get */
+    /* the four nearest source pixels, which are at (0.5,0.5) offsets */
+
+    sx = sx + ix/2 - 32768;
+    sy = sy + iy/2 - 32768;
+
+    xlimit = (op->src_w-1);
+    ylimit = (op->src_h-1);
+
+    sx0 = sx;
+
+    for ( h = op->rd.h; h > 0; h-- ) {
+        uint8_t*  dst = dst_line;
+        uint8_t*  dst_end = dst + 4*op->rd.w;
+
+        sx = sx0;
+        for ( ; dst < dst_end; ) {
+            int        ex1, ex2, ey1, ey2;
+            uint8_t*   p;
+            ARGB_DECL_ZERO();
+            ARGB_DECL(pix);
+            ARGB_DECL2(spix1, spix2);
+            ARGB_DECL2(pix3, pix4);
+
+            /* find the four neighbours */
+            ex1 = (sx >> 16);
+            ey1 = (sy >> 16);
+            ex2 = (sx+65535) >> 16;
+            ey2 = (sy+65535) >> 16;
+
+            if (ex1 < 0) ex1 = 0; else if (ex1 > xlimit) ex1 = xlimit;
+            if (ey1 < 0) ey1 = 0; else if (ey1 > ylimit) ey1 = ylimit;
+            if (ex2 < 0) ex2 = 0; else if (ex2 > xlimit) ex2 = xlimit;
+            if (ey2 < 0) ey2 = 0; else if (ey2 > ylimit) ey2 = ylimit;
+
+            /* interpolate */
+            p   = (src_line + ex1*4 + ey1*src_pitch);
+
+            ex2 = (ex2-ex1)*4;
+            ey2 = (ey2-ey1)*src_pitch;
+
+            switch (((sx >> 14) & 3) | ((sy >> 12) & 12)) {
+                case 0:
+                    *(uint32_t*)dst = *(uint32_t*)p;
+                    break;
+
+                /* top-line is easy */
+                case 1:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix,spix1,spix2);
+                    ARGB_SHR(pix,pix,2);
+                    ARGB_WRITE(pix, dst);
+                    break;
+
+                case 2:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix, spix1, spix2);
+                    ARGB_SHR(pix,pix,1);
+                    ARGB_WRITE(pix, dst);
+                    break;
+
+                case 3:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix,spix1,spix2);
+                    ARGB_SHR(pix,pix,2);
+                    ARGB_WRITE(pix, dst);
+                    break;
+
+                /* second line is harder */
+                case 4:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ey2);
+                    ARGB_ADDW_31(pix,spix1,spix2);
+                    ARGB_SHR(pix,pix,2);
+                    ARGB_WRITE(pix, dst);
+                    break;
+
+                case 5:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix4,spix1,spix2);
+
+                    ARGB_ADDW_31(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,4);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                case 6:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix4,spix1,spix2);
+
+                    ARGB_ADDW_31(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,3);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                case 7:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix4,spix1,spix2);
+
+                    ARGB_ADDW_31(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,4);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                 /* third line */
+                case 8:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ey2);
+                    ARGB_ADDW_11(pix,spix1,spix2);
+                    ARGB_SHR(pix,pix,1);
+                    ARGB_WRITE(pix, dst);
+                    break;
+
+                case 9:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix4,spix1,spix2);
+
+                    ARGB_ADDW_11(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,3);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                case 10:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix4,spix1,spix2);
+
+                    ARGB_ADDW_11(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,2);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                case 11:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix4,spix1,spix2);
+
+                    ARGB_ADDW_11(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,3);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                 /* last line */
+                case 12:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ey2);
+                    ARGB_ADDW_13(pix,spix1,spix2);
+                    ARGB_SHR(pix,pix,2);
+                    ARGB_WRITE(pix, dst);
+                    break;
+
+                case 13:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_31(pix4,spix1,spix2);
+
+                    ARGB_ADDW_13(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,4);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                case 14:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_11(pix4,spix1,spix2);
+
+                    ARGB_ADDW_13(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,3);
+                    ARGB_WRITE(pix,dst);
+                    break;
+
+                default:
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix3,spix1,spix2);
+                    p += ey2;
+                    ARGB_READ(spix1, p);
+                    ARGB_READ(spix2, p+ex2);
+                    ARGB_ADDW_13(pix4,spix1,spix2);
+
+                    ARGB_ADDW_13(pix,pix3,pix4);
+                    ARGB_SHR(pix,pix,4);
+                    ARGB_WRITE(pix,dst);
+            }
+            sx  += ix;
+            dst += 4;
+        }
+
+        sy       += iy;
+        dst_line += dst_pitch;
+    }
+    ARGB_DONE;
+}
+#endif
+#undef  ARGB_SCALE_UP_QUICK_4x4
+
+
+#ifdef ARGB_SCALE_NEAREST
+/* this version scales up with nearest neighbours - looks crap */
+static void
+ARGB_SCALE_NEAREST( ScaleOp*  op )
+{
+    int        dst_pitch = op->dst_pitch;
+    int        src_pitch = op->src_pitch;
+    uint8_t*   dst_line  = op->dst_line;
+    uint8_t*   src_line  = op->src_line;
+    int        sx = op->sx;
+    int        sy = op->sy;
+    int        ix = op->ix;
+    int        iy = op->iy;
+    int        xlimit, ylimit;
+    int        h, sx0;
+
+    /* the center pixel is at (sx+ix/2, sy+iy/2), we then want to get */
+    /* the four nearest source pixels, which are at (0.5,0.5) offsets */
+
+    sx = sx + ix/2 - 32768;
+    sy = sy + iy/2 - 32768;
+
+    xlimit = (op->src_w-1);
+    ylimit = (op->src_h-1);
+
+    sx0 = sx;
+
+    for ( h = op->rd.h; h > 0; h-- ) {
+        uint8_t*  dst = dst_line;
+        uint8_t*  dst_end = dst + 4*op->rd.w;
+
+        sx = sx0;
+        for ( ; dst < dst_end; ) {
+            int        ex1, ex2, ey1, ey2;
+            unsigned*  p;
+
+            /* find the top-left neighbour */
+            ex1 = (sx >> 16);
+            ey1 = (sy >> 16);
+            ex2 = ex1+1;
+            ey2 = ey1+1;
+
+            if (ex1 < 0) ex1 = 0; else if (ex1 > xlimit) ex1 = xlimit;
+            if (ey1 < 0) ey1 = 0; else if (ey1 > ylimit) ey1 = ylimit;
+            if (ex2 < 0) ex2 = 0; else if (ex2 > xlimit) ex2 = xlimit;
+            if (ey2 < 0) ey2 = 0; else if (ey2 > ylimit) ey2 = ylimit;
+
+            p   = (unsigned*)(src_line + ex1*4 + ey1*src_pitch);
+            if ((sx & 0xffff) >= 32768)
+                p += (ex2-ex1);
+            if ((sy & 0xffff) >= 32768)
+                p = (unsigned*)((char*)p + (ey2-ey1)*src_pitch);
+
+            *(unsigned*)dst = p[0];
+
+            sx  += ix;
+            dst += 4;
+        }
+
+        sy       += iy;
+        dst_line += dst_pitch;
+    }
+}
+#endif
+#undef  ARGB_SCALE_NEAREST
diff --git a/android/skin/composer.c b/android/skin/composer.c
new file mode 100644
index 0000000..6076449
--- /dev/null
+++ b/android/skin/composer.c
@@ -0,0 +1,401 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/composer.h"
+#include <stddef.h>
+#include "android/utils/system.h"
+
+/* forwards */
+static void  skin_plate_get_region       ( SkinPlate*  p, SkinRegion  *pregion );
+static void  skin_plate_get_opaque_region( SkinPlate*  p, SkinRegion  *pregion );
+
+/* recompute region if needed */
+static void
+skin_plate_ensure_region( SkinPlate*  p )
+{
+    if (p->any.type == SKIN_PLATE_SURFACE || p->group.hasRegion)
+        return;
+    else {
+        int  n, count = areflist_count( p->group.children );
+
+        skin_region_reset(p->any.region);
+
+        for (n = 0; n < count; n++) {
+            SkinRegion  r[1];
+            SkinPlate*  child = areflist_get( p->group.children, n );
+
+            skin_plate_get_region( child, r );
+            skin_region_translate( r, child->any.pos.x, child->any.pos.y );
+            skin_region_union( p->any.region, r );
+        }
+
+        p->group.hasRegion = 1;
+    }
+}
+
+/* return region in 'region' */
+static void
+skin_plate_get_region( SkinPlate*  p, SkinRegion*  region )
+{
+    if ( p->any.type != SKIN_PLATE_SURFACE && !p->group.hasRegion ) {
+        skin_plate_ensure_region(p);
+    }
+    skin_region_init_copy( region, p->any.region );
+}
+
+
+/* recompute opaque region is needed */
+static void
+skin_plate_ensure_opaque_region( SkinPlate*  p )
+{
+    if (p->any.type != SKIN_PLATE_SURFACE && !p->group.hasOpaqueRegion) {
+        int  n, count = areflist_count( p->group.children );
+
+        skin_region_reset(p->group.opaqueRegion);
+
+        for (n = 0; n < count; n++) {
+            SkinRegion  r[1];
+            SkinPlate*  child = areflist_get( p->group.children, n );
+
+            skin_plate_get_opaque_region(child, r);
+            skin_region_translate(r, child->any.pos.x, child->any.pos.y);
+            skin_region_union( p->group.opaqueRegion, r);
+        }
+
+        p->group.hasOpaqueRegion = 1;
+    }
+}
+
+
+/* return opaque pixels region */
+static void
+skin_plate_get_opaque_region( SkinPlate*  p, SkinRegion  *pregion )
+{
+    if ( p->any.type == SKIN_PLATE_SURFACE ) {
+        if (p->any.isOpaque)
+            skin_region_init_copy(pregion, p->any.region);
+        else
+            skin_region_reset(pregion);
+    } else {
+        skin_plate_ensure_opaque_region(p);
+        skin_region_init_copy(pregion, p->group.opaqueRegion);
+    }
+}
+
+
+/* invalidate region in parent groups */
+static void
+skin_plate_invalidate_parent( SkinPlate*  p )
+{
+    if (!p->any.isVisible)
+        return;
+
+    while (p) {
+        if (p->any.type != SKIN_PLATE_SURFACE) {
+            p->group.hasRegion       = 0;
+            p->group.hasOpaqueRegion = 0;
+        }
+        p = p->any.parent;
+    }
+}
+
+
+static void
+skin_plate_invalidate_( SkinPlate*  p, SkinRegion*  r, SkinPlate*  child )
+{
+    if (p->any.type != SKIN_PLATE_SURFACE) {
+        int  n = areflist_count( p->group.children );
+        if (child != NULL) {
+            n = areflist_indexOf( p->group.children, child );
+        }
+        while (n > 0) {
+            n -= 1;
+            child = areflist_get( p->group.children, n );
+            skin_region_translate( r, child->any.pos.x, child->any.pos.y );
+            skin_plate_invalidate_( p, r, NULL );
+            skin_region_translate( r, -child->any.pos.x, -child->any.pos.y );
+            if (skin_region_is_empty(r))
+                return;
+        }
+        if (p->any.type != SKIN_PLATE_SPACE) {
+            SkinPlate*  parent = p->any.parent;
+            skin_region_translate(r, parent->any.pos.x, parent->any.pos.y );
+            skin_plate_invalidate_(parent, r, p);
+        } else {
+            /* send to viewports */
+            int  n, count = areflist_count( p->space.viewports );
+            for (n = 0; n < count; n++) {
+                SkinViewport*  v = areflist_get( p->space.viewports, n );
+                skin_viewport_invalidate(v, r);
+            }
+        }
+    }
+}
+
+static void
+skin_plate_invalidate_region( SkinPlate*  p )
+{
+    SkinRegion  r[1];
+
+    skin_plate_get_region( p, r );
+    skin_plate_invalidate_(p->any.parent, r, p);
+    skin_region_reset(r);
+}
+
+/* change visibility */
+void
+skin_plate_set_visible( SkinPlate*  p, int  isVisible )
+{
+    isVisible = !!isVisible;
+    if (isVisible == p->any.isVisible)
+        return;
+
+    skin_plate_invalidate_parent(p);
+    skin_plate_invalidate_region(p);
+    p->any.isVisible = isVisible;
+}
+
+void
+skin_plate_set_opaque( SkinPlate*  p, int  isOpaque )
+{
+    isOpaque = !!isOpaque;
+    if (isOpaque == p->any.isOpaque)
+        return;
+
+    skin_plate_invalidate_parent(p);
+    skin_plate_invalidate_region(p);
+    p->any.isOpaque = isOpaque;
+}
+
+
+
+extern SkinPlate*
+skin_plate_surface( SkinPlate*         parent,
+                    SkinPos*           pos,
+                    SkinRegion*        region,
+                    void*              surface,
+                    SkinPlateDrawFunc  draw,
+                    SkinPlateDoneFunc  done )
+{
+    SkinPlate*  p;
+
+    ANEW0(p);
+    p->any.type      = SKIN_PLATE_SURFACE;
+    p->any.parent    = parent;
+    p->any.pos.x     = pos->x;
+    p->any.pos.y     = pos->y;
+    p->any.isVisible = 1;
+    p->any.isOpaque  = 1;
+
+    skin_region_init_copy( p->any.region, region );
+    return p;
+}
+
+
+SkinPlate*
+skin_plate_group( SkinPlate*  parent, SkinPos*  pos )
+{
+    SkinRegion  r[1];
+    SkinPlate*  p;
+
+    skin_region_reset(r);
+    p = skin_plate_surface( parent, pos, r, NULL, NULL, NULL );
+    p->any.type              = SKIN_PLATE_GROUP;
+    p->group.hasOpaqueRegion = 0;
+    skin_region_init_empty( p->group.opaqueRegion );
+
+    areflist_init( p->group.children );
+    return p;
+}
+
+
+SkinPlate*
+skin_plate_space( void )
+{
+    SkinPos     pos;
+    SkinPlate*  p;
+
+    pos.x       = pos.y = 0;
+    p           = skin_plate_group( NULL, &pos );
+    p->any.type = SKIN_PLATE_SPACE;
+    areflist_init( p->space.viewports );
+    return p;
+}
+
+
+extern void
+skin_plate_free( SkinPlate*  p )
+{
+    if (p->any.type >= SKIN_PLATE_SPACE) {
+        while ( areflist_count( p->space.viewports ) )
+            skin_viewport_free( areflist_get( p->space.viewports, 0 ) );
+    }
+    if (p->any.type >= SKIN_PLATE_GROUP) {
+        skin_region_reset( p->group.opaqueRegion );
+        p->group.hasOpaqueRegion = 0;
+        p->group.hasRegion       = 0;
+
+        while ( areflist_count( p->group.children ) )
+            skin_plate_free( areflist_get( p->group.children, 0 ) );
+    }
+    if (p->any.type == SKIN_PLATE_SURFACE) {
+        if (p->surface.done)
+            p->surface.done( p->surface.user );
+    }
+
+    skin_region_reset( p->any.region );
+
+    if (p->any.parent) {
+        areflist_del( p->any.parent->group.children, p );
+    }
+}
+
+void
+skin_plate_invalidate( SkinPlate*  plate, SkinRegion*  region )
+{
+    SkinRegion  r[1];
+    skin_region_init_copy( r, region );
+}
+
+
+/* we use two regions to manage the front-to-back composition here
+ *
+ *  'updated' initially contains the update region, in parent coordinates
+ *
+ *  'drawn'   is initially empty, and will be filled with the region of translucent
+ *            pixels that have been
+ *
+ *  for a given surface plate, we translate the regions to plate coordinates,
+ *  then we do an opaque blit of 'intersection(updated,region)', then removing it from 'updated'
+ *
+ *  after that, we make a DSTOVER blit of 'intersection(drawn,region)'
+ *  if the plate is not opaque, we add this intersection to 'drawn'
+ *
+ */
+static void
+skin_plate_redraw( SkinPlate*  plate, SkinRegion*  updated, SkinRegion*  drawn, SkinPos*  apos, SkinViewport*  viewport )
+{
+    SkinPos  pos = plate->any.pos;
+
+    if (!plate->any.isVisible)
+        return;
+
+    if (skin_region_is_empty(updated) && skin_region_is_empty(drawn))
+        return;
+
+    /* translate regions to plate coordinates */
+    skin_region_translate( updated, pos.x, pos.y );
+    skin_region_translate( drawn,   pos.y, pos.y );
+    apos->x += pos.x;
+    apos->y += pos.y;
+
+    if (plate->any.type == SKIN_PLATE_SURFACE) {
+        SkinRegion  r[1];
+
+        /* inter(updated,region) => opaque blit + remove 'region' from 'updated'*/
+        skin_plate_get_region(plate, r);
+        skin_region_intersect(r, updated);
+        if (!skin_region_is_empty(r)) {
+            plate->surface.draw( plate->surface.user, r, apos, viewport, 1 );
+            skin_region_substract(updated, r);
+            skin_region_reset(r);
+        }
+
+        /* inter(drawn,region) => DSTOVER blit + if non-opaque add it to 'drawn' */
+        skin_plate_get_region(plate, r);
+        skin_region_intersect(r, drawn);
+        if (!skin_region_is_empty(r)) {
+            plate->surface.draw( plate->surface.user, r, apos, viewport, 0);
+            if (!plate->any.isOpaque)
+                skin_region_union(drawn, r);
+            skin_region_reset(r);
+        }
+
+    } else {
+        int  n, count = areflist_count(plate->group.children);
+        for (n = 0; n < count; n++) {
+            SkinPos  pos;
+
+            pos.x = apos->x + plate->any.pos.x;
+            pos.y = apos->y + plate->any.pos.y;
+
+            skin_plate_redraw( areflist_get(plate->group.children, n ), updated, drawn, &pos, viewport );
+            if (skin_region_is_empty(updated) && skin_region_is_empty(drawn))
+                break;
+        }
+    }
+
+    /* convert back to parent coordinates */
+    apos->x -= pos.x;
+    apos->y -= pos.y;
+    skin_region_translate( updated, -pos.x, -pos.y );
+    skin_region_translate( drawn,   -pos.x, -pos.y );
+}
+
+
+extern SkinViewport*
+skin_viewport( SkinPlate*  space, SkinRect*  rect, void*  surface, int  sx, int  sy )
+{
+    SkinViewport*  v;
+
+    ANEW0(v);
+    v->space   = space;
+    v->rect    = rect[0];
+    v->spos.x  = sx;
+    v->spos.y  = sy;
+    v->surface = surface;
+
+    skin_region_init_empty( v->update );
+    return v;
+}
+
+extern void
+skin_viewport_free( SkinViewport*  v )
+{
+    SkinPlate*  space = v->space;
+    if (space != NULL) {
+        areflist_del( space->space.viewports, v );
+        v->space = NULL;
+    }
+    skin_region_reset( v->update );
+    AFREE(v);
+}
+
+extern void
+skin_viewport_invalidate( SkinViewport*  v, SkinRegion*  region )
+{
+    SkinRegion  r[1];
+    skin_region_init_copy(r,region);
+    skin_region_translate(r, -v->spos.x, -v->spos.y);
+    skin_region_intersect_rect(r,&v->rect);
+    skin_region_union( v->update, r );
+    skin_region_reset(r);
+}
+
+extern void
+skin_viewport_redraw( SkinViewport*  v )
+{
+    if (v->space && !skin_region_is_empty(v->update)) {
+        SkinRegion  update[1];
+        SkinRegion  drawn[1];
+        SkinPos     apos;
+
+        skin_region_copy(update, v->update);
+        skin_region_reset(drawn);
+        skin_region_reset( v->update );
+
+        apos.x = apos.y = 0;
+        skin_plate_redraw( v->space, update, drawn, &apos, v );
+
+        skin_region_reset(update);
+        skin_region_reset(drawn);
+    }
+}
diff --git a/android/skin/composer.h b/android/skin/composer.h
new file mode 100644
index 0000000..a52a972
--- /dev/null
+++ b/android/skin/composer.h
@@ -0,0 +1,102 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_COMPOSER_H
+#define _ANDROID_SKIN_COMPOSER_H
+
+#include "android/skin/rect.h"
+#include "android/skin/region.h"
+#include "android/utils/reflist.h"
+
+/* the composer displays stacked surfaces on a target window/SDL_Surface */
+
+typedef enum {
+    SKIN_PLATE_SURFACE = 0,
+    SKIN_PLATE_GROUP,
+    SKIN_PLATE_SPACE
+} SkinPlateType;
+
+typedef union SkinPlate      SkinPlate;
+typedef struct SkinViewport  SkinViewport;
+
+struct SkinPlateAny {
+    SkinPlateType    type;         /* class pointer */
+    SkinPlate*       parent;       /* parent container */
+    SkinPos          pos;          /* position relative to parent */
+    SkinRegion       region[1];    /* the plate's region */
+    char             isVisible;    /* flag: TRUE iff the region is visible */
+    char             isOpaque;     /* flag: TRUE iff the region is opaque */
+};
+
+
+typedef void (*SkinPlateDrawFunc)( void*  user, SkinRegion*  region, SkinPos*  apos, SkinViewport*  viewport, int  opaque );
+typedef void (*SkinPlateDoneFunc)( void*  user );
+
+struct SkinPlateSurface {
+    struct SkinPlateAny   any;
+    void*                 user;
+    SkinPlateDrawFunc     draw;
+    SkinPlateDoneFunc     done;
+};
+
+struct SkinPlateGroup {
+    struct SkinPlateAny   any;
+    char                  hasRegion;
+    char                  hasOpaqueRegion;
+    SkinRegion            opaqueRegion[1];
+    ARefList              children[1];
+};
+
+struct SkinPlateSpace {
+    struct SkinPlateGroup   group;
+    ARefList                viewports[1];
+};
+
+
+union SkinPlate {
+    struct SkinPlateAny        any;
+    struct SkinPlateSurface    surface;
+    struct SkinPlateGroup      group;
+    struct SkinPlateSpace      space;
+};
+
+
+extern SkinPlate*   skin_plate_surface( SkinPlate*         parent,
+                                        SkinPos*           pos,
+                                        SkinRegion*        region,
+                                        void*              user,
+                                        SkinPlateDrawFunc  draw,
+                                        SkinPlateDoneFunc  done );
+
+extern SkinPlate*   skin_plate_group( SkinPlate*  parent, SkinPos*  pos );
+
+extern SkinPlate*   skin_plate_space( void );
+
+extern void  skin_plate_free( SkinPlate*  plate );
+extern void  skin_plate_invalidate( SkinPlate*  plate, SkinRegion*  region );
+extern void  skin_plate_set_pos( SkinPlate*  plate, int  x, int  y );
+extern void  skin_plate_set_visible( SkinPlate*  plate, int  isVisible );
+extern void  skin_plate_set_opaque( SkinPlate* plate, int  isOpaque );
+
+struct SkinViewport {
+    SkinPlate*  space;
+    SkinRect    rect;
+    void*       surface;
+    SkinPos     spos;
+    SkinRegion  update[1];
+};
+
+extern SkinViewport*  skin_viewport( SkinPlate*  space, SkinRect*  rect, void*  surface, int  sx, int  sy );
+extern void           skin_viewport_free( SkinViewport*  v );
+extern void           skin_viewport_invalidate( SkinViewport*  v, SkinRegion*  r );
+extern void           skin_viewport_redraw( SkinViewport*  v );
+
+#endif /* _ANDROID_SKIN_COMPOSER_H */
diff --git a/android/skin/default.h b/android/skin/default.h
new file mode 100644
index 0000000..8ab0c01
--- /dev/null
+++ b/android/skin/default.h
@@ -0,0 +1,7414 @@
+/* automatically generated, do not touch */
+
+
+static const unsigned char _data_arrow_down_png[3438] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 98,  0,  0,  0, 31, 16,  6,  0,  0,  0, 76,137,196,
+     82,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  2,
+    153, 73, 68, 65, 84,120,218,236,156, 59, 75,195, 80, 24,134,163,
+    184,104,113, 18, 20, 68,135, 90,169,163, 93,189, 45, 90,156, 26,
+    208,165, 46, 29,252, 13,222,  6, 23, 69, 71, 47,163,179, 66, 39,
+     65,112,238,170, 80, 71, 69, 23,171, 78,186, 40, 56,137, 56,198,
+    225,203, 43, 36,161,164,177, 39,151,230,188,207,242,129, 96, 27,
+    206,121,159,156,227,151, 99,186, 44,203,178, 44,203, 48,102,231,
+    170,213,151,103, 35, 38,  6, 22,157,149,232,201,103,205, 89,163,
+    227,250,170, 82,201,141, 27, 70, 79, 50,  6, 98,189,223, 22,162,
+    159,161,208, 90,  8,123,254,183, 99,187,130,238,120,  7, 96, 97,
+    139, 43,  3,241,238, 20,144, 11,109,132,232,173, 74, 45,237, 48,
+      4,196, 11,114,129,156,164, 94,136,242,152,212,190, 42, 39,159,
+    120, 65, 46,144,147,212, 10, 49,242, 33,117,250,158,147, 78,252,
+     65, 78,144,155,212,  9,177, 50,206, 73, 38, 73,206, 77, 68, 66,
+     20, 10, 82,243,  7,156, 92, 18, 28,228,  6, 57,234,120, 33,202,
+     57, 78, 42,233,132, 28,133, 44,132, 57, 44,149,109, 85,162,  2,
+    228,  8,185,234, 24, 33,112,225,243, 79,156, 68,162, 30,228, 74,
+    253,141, 54,164, 39,213,165, 93,169,186,180, 85,207, 95,164,190,
+     14,198,123, 29,163, 31,122,108, 81,145, 43,228,236,172,150, 80,
+     33,242,135, 82,117,107,171,226, 65,210,209,183,212,183,136,197,
+     64, 91,178,148,177,127,160,201,141,  8, 57,171,219,185,107,108,
+     36,108,203,100,238,233,185,132,227,142,181,158,113,  6, 52, 42,
+     17,240,189,186, 62,232, 84,151, 59, 69, 66, 76, 61,216, 43,132,
+    230,109,213,168,196,160,  8,174,157,201,129, 51,135,177,  9,129,
+    179, 38, 38,207, 36, 69, 34,  6, 69,240, 89, 41,218, 62,  3,213,
+    166, 16, 69,158, 86,141, 68, 12,138,208, 26,200, 97,113, 43, 98,
+     33,240,197, 60,173, 26,174, 24, 20,161,189, 38, 71,240, 27,245,
+     63,133, 40,243, 76, 82,168, 98, 80,  4, 53,  4,207,105, 64, 33,
+    208, 86, 45, 76,114,176,195, 16,131, 34,168,  5, 57, 69,110,149,
+     11,193,149, 33, 92, 49, 40, 66,220, 43, 69,139, 66,160,157, 53,
+    250,206,193, 13, 83, 12,138, 16, 14,200,173,127, 91,214, 71,  8,
+    180,175,202, 89, 14, 42, 73,193, 74,145,117,230,218,139,207,209,
+     13,115,215,190,115,125,115, 48, 73,122, 86, 98,211,126,144,119,
+    222,234, 10,241,247,246,131, 77, 14, 34, 73, 31,200,181,183, 45,
+    219, 68,136,213, 27, 14, 26, 73, 63,222,156,187,182, 76,104, 79,
+    229,249,194, 48,162,  1, 56,  3,149,255,146,218,216,112,173, 16,
+    171,117, 14, 18,209,112,165,168, 55,217, 50,237, 47, 75, 61,157,
+    145,122,123,199,193, 34,233,  3,185, 70,206,145,123,207,150,233,
+    167, 34, 21,194,184, 23, 12,108,169, 38,142,165,142, 44, 73, 69,
+    159,151,135,252, 72,156,224, 37,201,175, 67, 82,223, 46,165, 62,
+    174, 97, 75,228,250,133, 19,159,191, 33,252,192,  7, 54,154,125,
+    224,133, 83, 28,  0,129,  8, 81,  1,  2,238,206,101, 83, 90,254,
+     79,186, 95,  0,  0,  0,255,255,  3,  0,243,175,157,107, 47,151,
+    202,243,  0,  0,  0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_arrow_left_png[4122] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 47,  0,  0,  0, 87, 16,  6,  0,  0,  0,196,198,192,
+     67,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  5,
+     69, 73, 68, 65, 84,120,218,236, 93, 61, 76, 20, 65, 20, 62, 47,
+     52,136,216,144,  0, 49, 88, 32,  8,141,198, 43,  5,172, 60, 67,
+     37,137,132,228,108,136,208, 25, 40,  1, 41,140,  9,168,  9,  5,
+     63,150, 98,  9, 66, 35,137,129,  4, 42,  5, 59, 14, 74,136, 54,
+     30,160,197, 17,140, 38, 52,138, 71,107, 49,243,173,238,122,199,
+    238,236,205,223,238,204,215, 12,176,100,127,190,121,251,189,247,
+    102,222,204,158,235,184,181,176,176,191,151, 48,  4, 53,157,164,
+     77,127, 33,109,126,133,180, 91,215,100,223, 73, 69,188,137,110,
+    153, 38,109,215, 51,250,123,143,251,120,161,145,180, 59,139,164,
+     61,237,181,196,135, 66, 42, 69,218, 76, 19,181,240,106,122, 96,
+    170,248,255,159,167,132,119,209,227, 75,210,238, 52, 25, 15,139,
+     30,190, 72,218,129,  1,183,164,  4, 69,250, 17,105, 27,126, 88,
+    226,139,162,146, 90,104, 95,150, 18, 78, 45,186,101,138,207,249,
+    239, 55, 91,226,139, 74,200, 68, 55,105,219, 63, 10,122,131,166,
+    220,215, 51,142,120, 88,118,166,217, 45, 33,208,100,209,128,143,
+     48,134,120,104,243,240,  5,183,246,170,186,143,174, 75, 49, 39,
+     30, 78,237,201, 50,105, 47,127,215,227,190,110,239,185,223,192,
+    216,132,147,109,159, 72,219,191,172,167,228, 65,218, 50,215, 73,
+     59, 31,117,139,119,  8,223,140,134,115,135, 51, 71,248, 26, 57,
+    226,163, 70,184, 23,200,128, 35, 67,124,212,  9,247,134,155,120,
+     30,109,137,135,211,140, 58,225,255, 89,254, 88,185, 78, 87, 16,
+    241, 78, 88, 88,149,136, 37,240,124,119, 70, 53, 35,126, 96, 80,
+    110,194,163, 58,220,100, 29, 27,226, 78, 60, 50, 77, 93,226,112,
+    105,225,102,179, 34,226, 49,182,161, 42,211, 84,141,212, 13,214,
+    112,179, 76,226,157,209,194,205,132,  5,147,229,151, 73,124,215,
+    184, 25, 90, 30, 20,144, 88,255,112, 51, 36,241,  8, 19, 77,149,
+     22, 95,203,111,244, 11, 55, 67, 18, 47,111,194, 32,218, 78,183,
+    116,184,201, 72, 60,156,  7,175, 25,159,184,227,238, 88,169,112,
+    147,145,120,126, 99, 21,102,161,127,219,251,151, 10, 70, 75,175,
+    182, 36,134,  1, 20,162,229, 23,105,115, 35,  1, 45, 30,  5, 64,
+     22,101, 90,254, 86, 64,139,135, 54, 33, 65,176, 40, 15, 78, 37,
+     91, 83, 69,176,140,212,130, 13,  5, 90,145,150,163,225,246,225,
+    138,251,120,126,200,135,248,244,  1,253,161,201,146,121, 22,178,
+    116,106,112,131,134,217,135,126,163,178, 35, 21,103, 39, 72, 53,
+    203,150,212,179,136, 94,163,153,251,241, 67,214, 51,148, 32,190,
+    117,218, 90,122, 49,233,152,157,165, 18,210, 94,238, 25, 75, 68,
+     53,169, 87,150,236, 68, 34,145,200,215,145,246,241, 50,194, 64,
+     94,103, 78,158, 29,119,154, 78,248,204,  9,105,249,151,111, 39,
+    139, 39, 74,166, 75,138, 56,194, 75, 16,223,250, 34, 26,196, 28,
+    191, 19,115,126,104,184,248,  5, 10, 30,226, 27,238,105,110,137,
+    191, 41,241,239,249,158,127,103,151,183,134, 51, 18,175,219,132,
+    134,151,240,195, 90, 49,215, 89,218,151,253,100, 73, 61,157,170,
+     44,194, 17,143,139,146, 46,230,168, 38,238,132,  3,187,131,170,
+    158, 52,169, 71, 52, 35,155,112, 71,219,119, 20, 19,111, 26,225,
+     57,229,115,197, 73,179,  8,119, 18,164, 90,195,136, 87, 77, 56,
+    112, 90, 48,140,120, 36, 40,135,202, 45,206, 48,226, 81,105, 85,
+    185,104,137,151, 10, 84, 90, 97, 85,159,185, 29,160,200,185,170,
+    238,128,150, 19, 67,137, 87,221,  1, 13,223, 52, 33, 62, 95,111,
+     86,  7, 96, 76, 74,222,166, 17, 37,136,151,183, 79,139, 94, 29,
+    144,222, 87, 76,188,147, 88,212,153,213,  1,168, 23,146,239,228,
+     61,196,203, 31,165, 83,219,  1,206, 70, 65,227,138,137,247, 22,
+    222,104,219,  1,175, 57, 75,142,234,141,130, 62, 15,233, 29,253,
+    162,  3, 68, 45,110,235,207,202,146, 30,175,198,215, 39,140,  6,
+     58, 20, 29, 32,141,120, 68, 55,186, 56, 89, 85,128,211,237, 19,
+    246,  6,148, 72,160,228, 77,250,234, 13,236,218,  1,223,194,190,
+    144,152,145,248,108,214,146, 94, 76,130,176,145, 81,122, 84, 16,
+    241, 24,182,213, 45,188, 84, 13,103, 37, 55,173, 41,157,232,113,
+    119, 68,112, 73,242, 41,211, 70,189, 73,186,211,146, 94, 12,144,
+    158, 12,253, 61, 67, 39,120,114,116, 31,204, 28,149, 40,204,120,
+     97, 34,168,240,192,135,248,141, 43,148,120,203, 49, 19,156, 53,
+     79, 37,142,175,165,124,136,135,212,228,182,221, 39,180,  8,  7,
+     88,252,122, 85,192, 97, 97,235,108,249, 96,233, 43,194,246,128,
+    196, 99,155,111,235,108,195,  1,121,209,223,237,210, 25, 39, 66,
+     86,159, 90, 18, 67, 89,250,126,192,112,210, 90, 62, 31,160, 54,
+    243,255,132, 52,228,212,223, 92,155, 37, 53,136, 19,197,226,180,
+    192,  9,148, 31,208,131,136,243, 45,220,248,112,213, 79, 25,202,
+    156,236,134,118, 21,122, 45,217,255, 18,189, 62, 25,114,200,128,
+    245, 66,107,214,233, 18, 67, 60, 64,184, 40,152,120, 39,195,157,
+     52, 91,122, 80,125, 28,188,236,155,115, 93,205, 92,187,153, 81,
+    207, 27,230,106,  5,206,196,227, 21,123, 57,107,134,246, 35, 92,
+    100, 47,194, 21, 84, 73,134, 27, 65,138, 28,215,112,113, 41,244,
+     62, 62,130, 75,248,144,112,193,233,196, 45, 92, 12, 95,  8, 38,
+    169,118, 18,206, 23,175,102,212,195,197,213,163,114,207, 36,185,
+    104,117,190, 61,218, 29,192, 47, 99, 87, 84, 45,140, 14,136,138,
+      4, 33, 92, 20,190,123,135, 44, 64,130,230, 58, 52,183,244,155,
+    188,207,168,201, 71, 22,225,132,243,180,132, 14, 27,254,171, 94,
+    226,191, 65,103,220,142,185, 87, 21,107,182,178, 27, 97,168,179,
+     49,143,162,245,168,  8, 23, 87,199, 69, 93, 65,211, 79,206, 33,
+     76,155,249, 73, 90,217, 99, 65,184,158,180,253,106,116,  5,194,
+    183,231,221, 98,223,  4, 76,209,109, 76,138,126,162,136,125, 86,
+     20, 82,132, 55,  1, 78,153,215,208,132,188,237, 83, 34,254, 33,
+     93, 56,101,248,  4, 72,  4,107, 71, 40,223, 40, 40,170,128, 22,
+     67,146,188, 29,225, 55, 90, 42,127,163,160,152,126, 44,221,233,
+      8,248,136,183,164, 77,209,132,173,141, 38,112, 88,  1,115,124,
+     36,251, 14,255, 12,  0,112,186,137,242,235,201, 20,157,  0,  0,
+      0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_arrow_right_png[4147] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 47,  0,  0,  0, 87, 16,  6,  0,  0,  0,196,198,192,
+     67,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  5,
+     94, 73, 68, 65, 84,120,218,236,157, 59, 76, 20, 65, 24,199,199,
+     11,141,248,104, 72,208, 24, 40,  4, 61, 26,141,103, 39,167,149,
+     24, 43, 72, 32, 38,216,144,120,157,129, 22,206,194,168,128, 38,
+     22, 60, 44,197, 18,149, 70, 18,131,  9, 84, 42,118,128,229, 33,
+     52,242,178,128, 96, 48,161, 81, 30,173,197,204,127,143,221,123,
+    176,183, 59,175,221,153,175,249,  2,  7,251,248,125,179,223, 99,
+    118,230,187, 19, 55,111,189,127,191,182, 74, 20, 73,243, 50,213,
+    245,237, 84,207, 54, 80,189,251,153,196, 92,170,212,156,246,228,
+      4,213,157, 23,169,174,206, 82,221,194, 62, 95,249, 78,245,244,
+     51,246,115,159,  5,207, 69,218,  6, 24,240,253,226,159, 39,135,
+    169,238,101, 63,239,222,163,122,114,157,234, 92, 46,234,224, 19,
+    114, 79, 87,115,151,141,236,108,176,255,235,238,102,  6, 57,203,
+     12, 52, 98,193,251,146,204,119, 62,199,113,158,136, 51, 84, 63,
+    152,119,187, 48, 11,158, 73, 42,229,  6,198, 91,210, 75, 84,191,
+    236,112,159,207,120,240,157,141,114,206, 83, 61,225,118, 73,157,
+    151,116,125, 18,  4,131,111,187,224,246,209,178,  5,177,164,247,
+    180,218,235,144,  6, 30, 35,236,246,170, 30,183, 89,191, 67,245,
+    147, 41,170,235,254,196, 20,124,103,131,251,209,215, 69,112, 61,
+     79,167,220,  5, 92,228,193, 35,189, 67,176,211, 93, 50,115,170,
+     12,192, 25,124,219,243,104,102,213,242, 13,192,169,114,197,  5,
+     39,231,162, 93, 79,194,  0,132,221,207,194, 21, 77, 71, 60,130,
+    104, 91, 63,137,149,192,  0,226,130,112, 72,240,119, 30,233,150,
+    166,241,149,222, 83,162,238, 47, 32,120, 92,136, 46,233,162,240,
+    130,172, 71, 19,240,168,  8,171, 39,136, 17,130, 58,  0,247, 45,
+     29, 60,210,197,212, 53, 98,164,160, 18, 14, 63, 23,148,  8, 54,
+    210, 77,151,  7,115,238,228, 66, 24,120,231, 21,221,142,133,126,
+    212,197,226,133, 14,119,240,222, 87,116, 86,138,187,158,202,211,
+    206,132,191,116,209,148, 32, 26, 84,238, 95,226,  4, 30,233, 98,
+    107,191,133,234, 43,233, 24,118, 39, 31,129,193,243,122, 69,103,
+    154,248,159,171,170, 42,158, 46, 38,207, 88,136,161, 70,254, 63,
+    170, 75, 47, 75,241,140,248,204,130,133,199, 37,232,110,248,116,
+     53, 45, 49,159,115,145, 45, 40, 48, 75,243,100,224, 55,107,169,
+    158, 25,116,235,220, 34,213,  7, 93, 22,102, 16, 73, 47, 31,227,
+    227,225,139, 86,188,159,191,118,107,228,171, 45,107,236,192, 75,
+     22,110,217,194,179,153,234,233,143, 33,167, 12,182,216,147,241,
+     54, 77,245, 99,118,192,249,171, 22,114,185,180,188,176,192, 10,
+     57, 31,143, 85,189, 48,196,232, 63,235,154,138, 73,211,  8,103,
+    240, 94,129,203,122,204,222,226,111,158,179,208,  9, 33, 36,245,
+     70, 48,120,200, 33, 27,241,163,123,214,  0, 71,243,123,225,224,
+     75, 25,192,116, 23,148,159, 82,144,180,118, 18,  6, 24, 27, 19,
+    115,124,196, 26,221, 13,219,244, 74, 50,120,111, 12,224,157,  5,
+    237,126, 97, 79,214,190,222,  6,168,107, 87,  4, 30, 50, 51, 32,
+    230,184, 72,119,117, 53, 64,126,122, 93, 17,120,184,  6, 81,249,
+    191,174,  6,200,  7,217,132,218, 11, 89,236, 17,123,124,125,159,
+      0,197,224,101,109, 34,211,205,  0,201,145,132, 30,246, 95,201,
+    154,102,  0, 77,192, 99,118, 84,150,168, 55,128, 38,224, 15, 15,
+    212,156, 87,157,  1, 52,  1,175, 90, 96,  0, 81,  5, 94,161, 84,
+     89,232,132, 28, 89, 63,196, 54,169,145, 29, 59,226,165,  0,199,
+    174, 64,121, 43,229, 52,  1,159,220, 51,  5,184,102,224,235,126,
+    155,  2, 92, 19,240,120, 37, 38,122,137,160, 62,192, 89,250,124,
+     94, 49,120,188, 52, 55,  5,184,147, 62,119, 41,  2, 15, 32,162,
+     54, 56,232, 10, 60,255, 38, 78, 81, 58,233,172, 66,222, 55,  3,
+     56, 36,223,242, 75, 50,120,248,244,214, 41,206,192,223, 49,224,
+     68, 79,224, 78,161,246, 73, 50,120,140,196,204,188,152,227, 71,
+    101,167,138, 51, 39,181, 45,201,199,  3,184,233, 91,121,126,246,
+      9, 30,241, 78,  9,206,186,120,164, 12, 95,234,135,160,122,216,
+     46,  8,188,211,180, 13,193,205,174,173, 36,132, 20, 91, 39,207,
+    201,213, 96,153, 55, 26,241,216,221,129,110,153, 47,136,109, 21,
+    142,120,184,144,244, 15,  6,156,245,129,172, 65,207, 49,187, 73,
+    173,104,250,184,245,176,  4,120,167,116,127,231, 46,225,235,217,
+    239, 49,137,149, 68,222,221,232,209, 86,138, 10,246, 23, 20, 10,
+      3,127, 61,197,242,107,236,242,235,182,208,120,  8,122, 37,147,
+    181, 18, 62,254,235, 16,213,118,121, 53,167, 96,154,245, 86,170,
+     37,192, 99,109,227,228, 47, 11, 77, 76, 48, 61, 38,171, 65, 43,
+     40, 89,203, 45,226, 26, 76,143,111,169, 85, 34,157, 68,251,112,
+     43,149,201,244,160,223,191, 76,148, 79,248,237,222, 38,222, 35,
+    221,103,  1,133, 85,189, 54,232,150,151,241,230, 74,255, 35,225,
+    207,146,223, 46, 91,184,229,242,244,202,191,209,193,231,148,  1,
+    210,205,248,127,119,135, 63,129,  7,152, 12,252,234,210, 39,120,
+     39,221, 92,183,208,  9,201,239,124, 15, 62, 16, 43,156, 36,195,
+    178,106, 83,211, 77,184,150,217,161,176, 71, 10, 56, 59,249, 97,
+    205, 44,224, 24,217,227,105, 94, 71, 12,  8, 30,139, 60, 77, 73,
+     55, 95,143,185, 93,174, 50,240,144,201,141,120,167,155,227, 55,
+    221,  3,141,159,132,  4,143, 17, 16,183,116, 19, 73,132,182,221,
+    180,157, 82,121, 59, 30,233, 38, 92,103,248,224, 41,  9,124,240,
+     10, 78, 47,224,111,211,178,206, 40,168,123, 71, 84,210, 77,228,
+    227,242,128, 11,  2,239,140,252, 27,209,  8,154,112,145,242, 69,
+     16,120,248,250,217, 97, 61, 64, 35,235, 66, 35, 35,113, 65, 83,
+     49,120, 39,232, 14,168, 77, 55,225,242,208,184, 72,159,175, 39,
+    149,212,175,102,102, 80,238,109,225,124,163,127,121, 23, 62, 17,
+      1, 15, 65,122, 38,170, 83, 19, 70,246,139, 14,213,190, 91, 51,
+    240, 78, 97,194,105,142,  7,174, 11, 65, 18, 35,123,171,150, 68,
+     68, 36,175,143,135,143,205,177,174, 29,126,119,132, 28,120, 42,
+    228,175,167,116,117, 33,154,130,247,142,252, 82,224,157,119,152,
+     11, 30,208,112, 33,145,159, 27, 82,  4, 30, 96,103, 88, 11, 88,
+    180,140, 90, 96,235, 81, 10,218,169,196,110, 18,238,255,  0,  9,
+    225,144,193,153,103,176,249,  0,  0,  0,  0, 73, 69, 78, 68,174,
+     66, 96,130
+};
+
+static const unsigned char _data_arrow_up_png[3493] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 98,  0,  0,  0, 31, 16,  6,  0,  0,  0, 76,137,196,
+     82,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  2,
+    208, 73, 68, 65, 84,120,218,236,156,189, 79,219, 80, 20,197, 93,
+     43, 75, 17,116,161,130,165,153,160,233,  2,123,  9, 48, 85,160,
+    142,176, 91, 85,214,238,237, 86,137, 34,161, 14, 41,255,  0, 27,
+     66, 89,200,  2, 18, 12,136,143, 46,109,194,154, 74, 93, 26,  1,
+     75,171, 74, 84,101, 65,  5, 70, 51,188,123,  2, 54,114, 18,219,
+    239,197, 78,114,126,203,145,162,248, 35,215,231,216,126,247,217,
+    121,228,186,174,235,186,150, 53, 51, 91, 42,157,158, 88,154,121,
+    246, 87,233,192,134,247,243,225,215, 74,159,206, 91,132,  4,242,
+    111, 95,233,197,158,247,243,235, 55, 74,127,143,232,218,210,183,
+    175,142, 51, 54,110, 89,153,112,139, 61, 46, 41,125,177,170, 52,
+     43,134,207,253, 23,163,207,137,194,232, 91,162, 67,190, 21, 85,
+     68,151,120,208, 73, 19, 42,  1,254,129,175,214, 36, 48,  8,206,
+    129,210,250,160,210, 95, 18,152,159,239,148,222, 56,173, 54,152,
+    105,126,102,207,231,197,240,  8,192,185,124,225, 45,143, 21, 73,
+     15, 56,  1, 67,115,254, 47, 92, 73, 64,182, 37, 48, 18,144,106,
+    213,127,165,241,  5,226,131, 44,144, 69,  2,223,139,158,179,232,
+    164,251,193,  9, 61, 43,190,126,181,229, 13,202,202,130,237, 93,
+    224,104,156, 69, 35,253,199,157,239,125,129, 56,158,144,196,140,
+    178, 72,164,247,129,207,225,251,  7,129,  0,229, 19, 22,139,244,
+     62, 15,125, 30, 16,  8, 12, 58,106,223, 89, 52,210,123,192,215,
+    240,121,203, 64,248, 19,116,237,176,136,164,251,129,143,131,239,
+    128, 90,  4,  2,253,221, 47,207, 89, 76,210,253,192,199,240,117,
+    232, 64,128,195, 98,171, 21, 17,146, 94,224, 91,248, 56,152, 54,
+      3,129, 25,190,157,101, 22,215,  4,235,211, 94, 37,122,129,111,
+     35,207, 84,  7,129,246, 84,254,137,210,220,103, 22, 91, 71, 16,
+    238,218,126,194, 15, 37,133, 10,107, 20,135,186, 76,192, 29, 95,
+    182,187,132, 29,109, 67,155,108,203,154,  9,130,239,196,195, 43,
+     70, 60,194,251, 52, 98, 32,240,236, 71,117,146, 69,215, 25,  4,
+      6, 67, 15,240,101,248,167, 97,237,120, 27, 46,159, 41,101, 91,
+     86,111, 16, 24,140,104, 52,218,170,103, 81,215, 16, 51, 16, 24,
+    164,176, 45,107, 38,  8, 12, 70, 56,224,195, 27, 39,161, 64, 52,
+     70,241,127,148,178, 45,107, 38,  8, 12, 70,115,224, 59,248, 48,
+     58,182,222, 29, 43,159, 50,  8, 38,131,192, 96,152,246,157,230,
+     64,212,106, 74,209,238, 98, 16, 24, 12,147,192,103,240, 93,124,
+     50,134, 12,242, 82,233,167, 62, 57, 48,152, 47, 40,172, 37,188,
+     35,125, 54,111,  1,159, 89,218,110,213,109, 51, 59,138,123,186,
+     35, 78,220, 17,  3,192, 87,250,199,172,182,217, 29,223,249,168,
+    148,109, 89,162,  3,248,  8,190,210,143,225, 64,160,253,181,203,
+    103,160,136,  6,118,219,126, 38, 41,165,129,104, 92,226,248,180,
+     44,209,113, 11, 94, 52,189, 37,187,179, 63,108,125,138,  7,151,
+    164,217, 55, 29, 14,  4, 95, 77, 37, 97,  8,126,213,179, 71,  2,
+      1,248, 39,  6, 36,157, 62, 73, 40, 16,184, 39,228, 96,155, 52,
+     27, 60,119,126,204,105, 39,251,195,241, 74, 31,219,178,228,190,
+     15, 14,139, 73,237, 65, 38,217,  2,160,125,182,226,251,111, 78,
+    210,159, 92, 44,138, 47, 18,235, 70,222,  2,  0,  0,255,255,  3,
+      0,166,247,214, 91, 44,122,150,205,  0,  0,  0,  0, 73, 69, 78,
+     68,174, 66, 96,130
+};
+
+static const unsigned char _data_back_png[3564] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 46,  0,  0,  0, 47, 16,  6,  0,  0,  0,204,117, 36,
+    209,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  3,
+     23, 73, 68, 65, 84,120,218,236,155, 59,108, 19, 65, 16,134, 23,
+     43, 13, 78, 66, 67, 20, 40,156, 34,194,144,  6,228,107, 19, 83,
+     97,100,165,177,196, 67, 10,141, 37,232, 34,187,  4, 76,129, 16,
+    226,209,133,164,141, 41, 19,146, 42,141,145,160, 66, 28, 93, 28,
+     74, 35,165,193,  9,105, 28,129,176,148,  6,129,211, 82,236,252,
+    129, 59,251,228,123,236,173,207,246,252,205, 40,241,227,124,223,
+    206,254, 59,123,119,115, 42,125,117, 99, 99,127, 79, 68, 92,151,
+    150,189,189,191,113, 94,198,227,124,212,206,100,164,183,135, 63,
+    189, 41,163, 97, 16,216, 95, 50,206,172,200,120, 54,235,243,139,
+    255,200,208,162, 88, 47, 81, 28,147,177, 86,147,241,232,195,128,
+      3, 71,166,102, 14,  8, 52,  1, 17,219,182, 55,102,213, 28, 47,
+    142,  1, 77, 81,164,255, 47, 92,160,  1,248, 44, 99,181, 42,227,
+    206,229, 62,  7, 14,192,185, 23,244,247, 56,189,144,138,136, 85,
+    189,162, 72,  3,158,187, 45,227,214, 55,235, 76, 80,167, 88, 56,
+     22,177,144,148,241,193,184,245,196,162, 46, 88, 88,161, 64,191,
+    255, 76, 64,107, 11, 11,120,162, 73, 63,144, 60, 50, 83, 18,  3,
+     33, 36,202,147,138,117,173,233, 25,240, 19,208,163, 50, 78,253,
+     20,  3, 41,172,  5,200,252,217, 93,205, 30,142,145, 46, 84,196,
+     80,234, 30, 22,249, 93,175,139,109,204, 95, 70,223,221, 22,172,
+    255,192,187,207,120,151,192,177, 24,194, 58, 48,197, 88, 86,240,
+     72,200,192,192,139, 69,  6,237,138, 83,193,154,160,158,129, 99,
+    170,244, 75, 89, 23,149,178, 50,247,204, 35,240,147,122,122,154,
+     33,250, 17,202,226,118,139,113,  0,126,253, 17, 91,136, 10,221,
+     73,186,  4,126,109,143, 97,169,220, 56,253,219,169,198, 58,123,
+     54,103,182, 98,139, 57,112,  0,110,172, 50,156, 48,100,164, 28,
+    128,115, 53, 18,110,245,146,104, 18,112, 92, 70,101, 43,  9, 87,
+     51,203,  4,124,170,201, 48,116, 40,113, 35,198,153,173, 83, 19,
+    243,176,148,223, 12, 67, 75,134,255,136, 49,  4,157,138,111, 50,
+    112,205, 98,224, 12,124, 40,128, 55, 38, 25,133, 14,213, 75,  4,
+    252,232, 22,195,208,161, 86,158, 51, 92,171, 14,223, 18,240,250,
+     67,134,161, 67, 95,239,219, 30,147,168,125,145,209, 72, 49, 28,
+    181, 86, 66,137, 61,106,171, 82,106, 69,134, 19,134,144,200,109,
+    101, 33, 30, 94,108,229, 25,146, 74,153, 73,  7,224,120,128,253,
+    211, 69,134,164,166, 12,164,197,114,178,203,198,231,227, 18,103,
+    186, 10,189,123,234,114,167,137, 76,127,255,156,161,  5,241,236,
+    246,234,175,203,214,222,164, 76,111,156, 99,136, 94,170,145,181,
+    185, 46, 91,251,110, 42,175,178,197,184,209,122,218,234, 16,190,
+    129,163,249,168, 92,102,168,157,  4,235,237,222,162,226,241,106,
+     33, 60,105, 45,205,144,133, 16,162,122,133, 22,199,239,110, 63,
+    225,243,242, 44, 30, 64, 31, 86,240, 38, 61, 78,178, 62,231,245,
+    147,  1,175,135,  3,252,203,155,195,225,241, 72,176,173,125,191,
+    223,160,232,  6,  4, 10,251,199, 21,251, 86,182,191,133,234, 12,
+      9, 21,188,143, 83,113,159, 38, 86,103,172,173,232,  5, 66, 35,
+    170,186,246,187,112,203, 58, 44,130,230,162,234, 35,132,220, 24,
+    139, 85, 27,113,150, 78, 36, 67, 83,178,215, 93,111,168,190, 76,
+     74,136, 42,205,208,227,165,176,142,168,185,245, 27, 83,114, 71,
+     88, 51, 30, 51,193,120, 45,163,234,103, 28, 97, 13,168,178,208,
+    234,125,184,168,123,136, 71, 34,146, 97,136,120,129, 64,160,131,
+     32,254,134,  6,104, 94,198,137,108,103, 43,176,223,185,114,188,
+    177,210,179, 59, 92,127,  7,  0,162,170,196, 84,182,147,226,216,
+      0,  0,  0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_end_png[3562] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 46,  0,  0,  0, 47, 16,  6,  0,  0,  0,204,117, 36,
+    209,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  3,
+     21, 73, 68, 65, 84,120,218,236,156, 61, 72, 91, 81, 20,199, 95,
+     31, 89,140,208, 69,209, 82,226,208,150,218,197,162, 99,243,236,
+    166,136,131,  1, 69,112, 10,146,173,152,177,126, 76,173, 70, 40,
+     29,172,174,149,110, 17, 51,  9,146,130, 29, 74, 81, 55,147,142,
+     79,236,162,141,237,160,180, 24,234, 98, 75,178, 58,220,115,132,
+    119,227,235,251,186,247,230, 37, 57,255,229,228,227,230, 37,249,
+    221,115,207, 61,231,230,222,220, 25,124,190,185, 89,250,174,133,
+     92, 29, 35, 86,107,167,202, 52,179,231, 93, 97,253, 38,145,112,
+    124,140,129,  1,102,123,255,129, 93,101,182,231,194,227,133,242,
+     96, 63, 48,115,214, 13, 22, 58,224,228, 46,179,166,201,108, 53,
+    217,228,192, 99,101,102,135, 74,  0,186,159,217,104,142,107,120,
+     33,230,253,176,195,208, 26,220,243,102,154,217,189,135,208, 33,
+    115, 13, 14, 28, 67, 64,234, 43,120,110,158,107,112, 84,231,145,
+      5, 29, 14,  3,236,102,  4,236, 44,202,234,  0, 73,192, 19,247,
+    153, 29,155,132,  7, 38,181,134, 80,239, 59,102,103,185, 17,144,
+     53, 68,133,160,136, 88, 79,158, 73,251,140,189, 33, 21,142,128,
+    183, 19,204,174,149,131, 78,202,186,152,152,252, 42,223, 92,160,
+    121,225, 28,243, 26,190,103,252,155, 98,224,  8,122,182,221,102,
+    210,107,114,165, 14,252,130,215,  9,180, 90,240, 46,129,183,  1,
+    216,244, 12,129,190, 77, 83, 15,172, 14, 25, 24,120,170,224,174,
+    210,107, 85, 69, 57,135,108,203,249,  4,142, 21, 32,206,214, 36,
+    119,217, 90, 34,227, 17, 56,246,208,212, 35,130,232, 71, 67,243,
+    118, 17,193,  6,248,240,  2,133, 16, 17, 26,203,184,  4, 30,143,
+     19, 44, 17, 50,142,120,199,213,111,143,217,228,217,130, 67,204,
+     15, 27,224,253,239,  9,142,204, 37,130, 90, 15,167,108, 68,106,
+    246, 18, 43,235,214,132,157, 10, 26,185,122,178,170,227, 13,130,
+    161, 66,177,113,157, 60, 91,165, 58, 71,  1, 56,254,150, 72,146,
+    236,225,191,117,130,160, 82,209, 28,  1, 87, 44,  2, 78,192, 91,
+      2,248, 89, 23,161, 80,161,147,121,  0, 94,173, 16, 12, 21,170,
+     36,  1,248,241, 75,130,161, 66,231, 31,  1,184,252, 45, 94, 36,
+    230,216,220, 70, 32,243,144, 89, 90,196, 18, 29, 74,192,177,219,
+    185, 44,  5,183,118,145,196, 10, 29,185, 38, 45,196,109,188,149,
+     36, 65, 18,169, 98,  1,111,113, 33,  5, 55, 43,154,208,192, 32,
+     86,  1,211, 64,176, 87, 14,133,207,167, 12,121,186,  8,225,182,
+    103,199, 74,243,242, 11,179,251,143,  9, 90, 32,207,158,243, 88,
+    218,239,252,130, 74,180,155, 32,122,201, 70,178,207, 28, 74,123,
+     39,225,134,116, 10, 49,255,215,214, 79,107,132,240, 13, 28, 55,
+    160,227,  5, 73, 86,237,193,201,137, 98,159, 83, 75,143,171,133,
+    120,193,236, 32, 65,214, 52, 77, 43, 60,  5, 71, 44,185,125,133,
+    207,229,217, 86,  7,143,160, 55, 60,231,205,  1,215,195, 17,252,
+    250,122,107,196,248,173, 83,191,160,  5,  1,231, 43,212, 55, 19,
+    214,180,168,209,133,147,223,218, 95,136,213, 43, 65,175, 24,145,
+    244,  1,225,126, 28, 66, 78, 98,137,217,176,239, 89,196, 17,138,
+    245,199,238,138,181,  2, 15, 46,201,  7, 99, 49,228, 20,177,  3,
+    150,195,213,  1, 53,128,225,236, 82, 21,234, 15, 77,120,136, 84,
+    124,244,155,239,128, 24,196,126,  3, 98,162,239, 51,246, 46, 71,
+    222, 49,156,120, 61,132, 85, 81,243,  5,215, 80,250, 28, 84,231,
+     63, 55,184,201,239,241,129,113,235,243,216,  1,184, 51,172,199,
+    225,208,210, 31,  0,123,249, 25, 42,228,123,224,177,219,208, 96,
+    187,222, 65,235,122,  0,217,217,201, 30, 62,205,111,211,  0,  0,
+      0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_home_png[3578] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 46,  0,  0,  0, 47, 16,  6,  0,  0,  0,204,117, 36,
+    209,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  3,
+     37, 73, 68, 65, 84,120,218,236,155, 61,108, 19, 49, 20,199,143,
+    136,  5, 10, 12,128,130,144,154,  1, 40, 97, 41, 52, 43, 41, 19,
+     65,136,  1,164, 10,164,176, 68,148, 13,114,107, 10, 27, 95, 18,
+     83, 18,214, 68,108,169,218, 41, 75,145,202,128, 42,194,150,118,
+    228, 16, 44,164,133,165, 21, 31, 17,217, 10, 93, 25,252, 94, 42,
+     91,181,236,107,206,190, 75,238,253,151,167,228, 46,185,228,231,
+    231,191,159,239,236,  3,211, 87, 22, 22, 54,214,157,136,234,208,
+     34,139,169, 95,254, 62,215,153,139,234, 63, 58, 24,238,229, 79,
+     92,103, 49,147, 97, 49,189, 13,177,194,226, 97,  0,238, 28,245,
+    249,197,175, 89,232,173,176,248,181,  4, 13,113,140, 69,207, 99,
+    113,167, 48,226,192, 47,127, 97, 49,155,  5,176,119, 36, 39, 46,
+      6,219,160,217,207, 16,133,227,158,203, 98,235,172,173,158, 97,
+     24, 56,102,110,254, 28,  0,104,195,129, 74, 52, 58,120,102, 10,
+    162,195,247,128,229,167,166, 26, 32, 97,198, 34, 74,240,195,139,
+     69,254,253,168, 11,173,172,  4, 22,150,159,224,199,146,200,100,
+     56,102,242,236,146,224,189, 67,174,220, 35,104,136, 35, 44, 54,
+    186, 44,110, 37, 67,202,112,244,100,204,228, 81,  1, 45, 42,245,
+     27, 50,127,140,197,241,174,101,224,  8,250,126,219,137,149, 48,
+    161,158, 44,241, 61,219, 24,240,184,130,150,105,182,237, 55,227,
+     53,129,227, 23, 18,232,189, 51, 30,173, 70, 61,184, 38,244,102,
+    122,110,145,224,234,128,119,221,  1,129,223,122, 62, 92,101, 93,
+     84,202, 74,180, 94,109,224,104, 33, 88, 22,145,252, 41,127, 70,
+    102, 49, 18,224,119, 39,  8, 90, 16, 22,115,237,177,  2, 56, 90,
+     71,186, 66,208,130,208,213,117,  5,240,220,119,130,100, 34,211,
+    119, 61, 93,  0,142, 55,115, 72,193, 42, 83, 19,128,227, 32, 73,
+    213,136, 33,224, 83,  2,240, 11, 85,130, 98,165,108,172, 98,134,
+    207, 16, 12, 27, 74,117,  1,248,201, 27,  4,195,206, 32,138, 25,
+    254,147, 96, 88,177,148,237,  4, 95,190,144, 76, 43, 65,  8,  8,
+    120, 28,128,255, 43, 16, 10,171,192,183, 78, 19, 10, 27,218, 76,
+     82,134, 91, 85,239, 54,102,248, 27,130, 97, 53,195, 63,122,  4,
+    195,134, 58,115,152,225, 73,178, 22,147,242, 62, 73,202,194,221,
+      3,164, 64,129,187, 18,224, 45,122,180, 22,168,208, 49,188,190,
+    101, 11,107, 11,209, 90,112, 21, 41, 61,106, 27, 76, 31,206,179,
+    184,243, 67, 49,211,196,229,186,164,193, 50,251,125, 89,115,106,
+    143,235,162,201,211,247,167,183, 47, 32,179, 11,154,192, 81,141,
+     44, 85, 47,190,234,236, 83, 48, 22,150, 21, 83,123,153,176,133,
+    230,167,  9,166,142,133,212,107,170, 51, 53,239, 22,226, 40,139,
+     93,133,196,171, 94,135,169,251,138,234, 76,159, 59, 32,150, 97,
+    180, 61,126,145, 69,220,172, 20, 87, 53,160,231,119, 38,117, 63,
+    177,207,251,225,243,224,237,173, 74,188, 65,175, 77,250,253,228,
+    128,123,124,154, 27, 48, 88,192, 15, 24,213,245,227,232,209,175,
+    254,242,243, 21,255, 10,104, 83, 21,182,244, 38, 46,220, 95,101,
+     17,247,198, 12,237,148, 28,202,226,198,152,172,204, 11,  9,184,
+     56, 83,125,  9,175,115,176,122,244,230, 51, 22,163,254,176, 26,
+      7,189,230, 55, 97, 74, 94, 11,234, 10,134, 55,198, 98, 61,186,
+    122,  9,  6, 89,248, 35, 57,136, 97, 47,173,235,215,205,112, 15,
+    105,237,129,233, 43, 90,218,250,141, 93,177,229,240,113, 28,202,
+    169,254, 86,240,170, 25, 43,234,192,198,  2,239, 33,159,185,234,
+     50,110, 72,129,171, 44,168,137,111,204,240,199,211,194,154,199,
+     84,119,111,107,250,  3,224,122,239, 96,144,187, 39, 25,220,202,
+     97,155,214,255,  1,  0,168,120,199,110, 67,179,158,126,  0,  0,
+      0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_key_png[2857] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 35,  0,  0,  0, 34, 16,  6,  0,  0,  0,133, 21,188,
+    191,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  0,
+     84, 73, 68, 65, 84,120,218,236,208, 49,  1,  0, 16, 16,  0, 64,
+    212,250,112,212, 48,107, 32,207, 23,177,153,101,112, 23,225,106,
+    236,117,114,142, 94,120, 52,  5, 98,196,136, 17, 35, 70,140, 24,
+     49, 98,196,136, 65,140, 24, 49, 98,196,136, 17, 35, 70,140, 24,
+     49,136, 17, 35, 70,140, 24, 49, 98,196,136, 17,243,159, 11,  0,
+      0,255,255,  3,  0, 56,171,  5, 65,131,211,234, 11,  0,  0,  0,
+      0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_layout[7714] = {
+    112, 97,114,116,115, 32,123, 10, 32, 32, 32, 32,100,101,118,105,
+     99,101, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97, 99,
+    107,103,114,111,117,110,100, 32,123, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32, 32,100,101,
+    118,105, 99,101, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32,100,105,115,112,108,
+     97,121, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,119,105,100,116,104, 32, 32, 32, 51, 50, 48, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,104,101,105,103,104,116, 32,
+     32, 52, 56, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,120, 32, 32, 32, 32, 32, 32, 32, 51, 49, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 32, 32, 32, 32, 32, 32,
+     55, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 98,117,116,116,111,110,115, 32,123, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,115,111,102,116,
+     45,108,101,102,116, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32,109,101,110,117, 46,112,110,103, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120,
+     32, 49, 52, 55, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 53, 53, 53, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,104,111,109,101, 32,123, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32,104,111,109,101, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,120, 32, 52, 56, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 53, 57, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     98, 97, 99,107, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101,
+     32, 98, 97, 99,107, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32,
+     50, 56, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 53, 57, 48, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,100,112, 97,100, 45,117,112, 32,
+    123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 97,114,114,111,
+    119, 95,117,112, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49,
+     52, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,121, 32, 53, 57, 53, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,100,112, 97,100, 45,100,111,119,110,
+     32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 97,114,114,
+    111,119, 95,100,111,119,110, 46,112,110,103, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    120, 32, 49, 52, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 54, 53, 54, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,100,112, 97,100, 45,108,
+    101,102,116, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32,
+     97,114,114,111,119, 95,108,101,102,116, 46,112,110,103, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 49, 49, 49, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 53,
+     57, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,100,112, 97,
+    100, 45,114,105,103,104,116, 32,123, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109,
+     97,103,101, 32, 97,114,114,111,119, 95,114,105,103,104,116, 46,
+    112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 50, 50, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,121, 32, 53, 57, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,100,112, 97,100, 45, 99,101,110,116,101,114, 32,123, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32,115,101,108,101, 99,116,
+     46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 52, 50, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,121, 32, 54, 50, 54, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,112,104,111,110,101, 45,100,105, 97,108, 32,123, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32,115,101,110,100, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,120, 32, 52, 56, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 54, 52, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    112,104,111,110,101, 45,104, 97,110,103,117,112, 32,123, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,105,109, 97,103,101, 32,101,110,100, 46,112,110,103,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,120, 32, 50, 56, 54, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121,
+     32, 54, 52, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    112,111,119,101,114, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32,112,111,119,101,114, 46,112,110,103, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    120, 32, 45, 51, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 53, 50, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,118,111,108,117,109,101,
+     45,117,112, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32,
+    118,111,108,117,109,101, 95,117,112, 46,112,110,103, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,120, 32, 51, 54, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 50, 54,
+     48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,118,111,108,
+    117,109,101, 45,100,111,119,110, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,
+    109, 97,103,101, 32,118,111,108,117,109,101, 95,100,111,119,110,
+     46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 51, 54, 50, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,121, 32, 51, 49, 48, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32,125,
+     10, 32, 32, 32, 32,125, 10, 10, 32, 32, 32, 32,107,101,121, 98,
+    111, 97,114,100, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 98,
+     97, 99,107,103,114,111,117,110,100, 32,123, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32, 32,
+    107,101,121, 98,111, 97,114,100, 46,112,110,103, 10, 32, 32, 32,
+     32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 98,
+    117,116,116,111,110,115, 32,123, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 49, 32,123, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,
+    107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,120, 32, 32, 48, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 32,
+     48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 50, 32,123, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120,
+     32, 51, 55, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,121, 32, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 51, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121,
+     46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,120, 32, 55, 52, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 48, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 52, 32,123, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 49, 49,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 53,
+     32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,
+    103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,120, 32, 49, 52, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 48, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 54, 32,123, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32,
+     32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 56, 53, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121,
+     32, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 55, 32,123,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    120, 32, 50, 50, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,121, 32, 48, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 56, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,
+    101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,120, 32, 50, 53, 57, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 48,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 57, 32,123, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,
+    109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32,
+     50, 57, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,121, 32, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 48, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121,
+     46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,120, 32, 51, 51, 51, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 48, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,113, 32,123, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109,
+     97,103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 32,
+     48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,121, 32, 32, 51, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,119, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121,
+     46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,120, 32, 51, 55, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 51, 54, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,101, 32,123, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,
+    103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 55, 52,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 51, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    114, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 49, 49, 49, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 51, 54, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,116, 32,123, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 52, 56,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 51, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 49, 56, 53, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 51, 54, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,117, 32,123, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 50, 50,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 51, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    105, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 50, 53, 57, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 51, 54, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,111, 32,123, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 57, 54,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 51, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    112, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 51, 51, 51, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 51, 54, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 97, 32,123, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,
+    103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 32, 48,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 32, 55, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,115, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,
+    112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,120, 32, 51, 55, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 55, 50, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,100, 32,123, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,
+    101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 55, 52, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 55, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,102,
+     32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,
+    103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,120, 32, 49, 49, 49, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 55, 50, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,103, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101,
+     32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 52, 56, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 55, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,104,
+     32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,
+    103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,120, 32, 49, 56, 53, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 55, 50, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,106, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101,
+     32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 50, 50, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 55, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,107,
+     32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,
+    103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,120, 32, 50, 53, 57, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 55, 50, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,108, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101,
+     32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 57, 54, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 55, 50, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 68,
+     69, 76, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,
+    112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,120, 32, 51, 51, 51, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 55, 50, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 67, 65, 80, 32,123, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120,
+     32, 32, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,121, 32, 32, 49, 48, 56, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,122, 32,123, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,
+    107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,120, 32, 51, 55, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 49,
+     48, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32,123,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    120, 32, 55, 52, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,121, 32, 49, 48, 56, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 99, 32,123, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,
+    107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 49, 49, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32,
+     49, 48, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,118, 32,
+    123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,103,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,120, 32, 49, 52, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,121, 32, 49, 48, 56, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 98, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101,
+     32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 56, 53, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 49, 48, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    110, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 50, 50, 50, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 49, 48, 56, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,109, 32,123, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,
+    103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 53,
+     57, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,121, 32, 49, 48, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 80, 69, 82, 73, 79, 68, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101,
+     32, 32,107,101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 50, 57, 54, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    121, 32, 49, 48, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     69, 78, 84, 69, 82, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,
+    101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,120, 32, 51, 51, 51, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 49,
+     48, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125,
+     10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 65, 76,
+     84, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,
+    110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,120, 32, 32, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 32, 49, 52, 52, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 83, 89, 77, 32,123, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,
+    109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32,
+     51, 55, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,121, 32, 49, 52, 52, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 65, 84, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,
+    101,121, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,120, 32, 55, 52, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 49, 52,
+     52, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 83, 80, 65, 67,
+     69, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,105,109, 97,103,101, 32, 32,115,112, 97, 99,101,
+     98, 97,114, 46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,120, 32, 49, 49, 49, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32,
+     49, 52, 52, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 83, 76,
+     65, 83, 72, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121,
+     46,112,110,103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,120, 32, 50, 53, 57, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32, 49, 52, 52,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 67, 79, 77, 77, 65,
+     32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32,105,109, 97,103,101, 32, 32,107,101,121, 46,112,110,
+    103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32,120, 32, 50, 57, 54, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,121, 32, 49, 52, 52, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 65, 76, 84, 50, 32,123, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,105,
+    109, 97,103,101, 32, 32,107,101,121, 46,112,110,103, 10, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32,
+     51, 51, 51, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,121, 32, 49, 52, 52, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,125, 10, 10, 32, 32, 32, 32, 32, 32, 32,
+     32,125, 10, 32, 32, 32, 32,125, 10,125, 10, 10,108, 97,121,111,
+    117,116,115, 32,123, 10, 32, 32, 32, 32,112,111,114,116,114, 97,
+    105,116, 32,123, 10, 32, 32, 32, 32, 32, 32, 32, 32,119,105,100,
+    116,104, 32, 32, 32, 32, 32, 57, 48, 48, 10, 32, 32, 32, 32, 32,
+     32, 32, 32,104,101,105,103,104,116, 32, 32, 32, 32, 55, 51, 48,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 99,111,108,111,114, 32, 32,
+     32, 32, 32, 48,120,101, 48,101, 48,101, 48, 10, 32, 32, 32, 32,
+     32, 32, 32, 32,101,118,101,110,116, 32, 32, 32, 32, 32, 69, 86,
+     95, 83, 87, 58, 48, 58, 49, 10, 10, 32, 32, 32, 32, 32, 32, 32,
+     32,112, 97,114,116, 49, 32,123, 10, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32,110, 97,109,101, 32, 32, 32, 32,100,101,118,
+    105, 99,101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+    120, 32, 32, 32, 32, 32, 32, 32, 52, 48, 10, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32,121, 32, 32, 32, 32, 32, 32, 32, 45,
+     49, 56, 10, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32,
+     32, 32, 32, 32, 32,112, 97,114,116, 50, 32,123, 10, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109,101, 32, 32, 32,
+     32,107,101,121, 98,111, 97,114,100, 10, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32,120, 32, 32, 32, 32, 32, 32, 32, 52, 56,
+     48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,121, 32,
+     32, 32, 32, 32, 32, 32, 50, 48, 48, 10, 32, 32, 32, 32, 32, 32,
+     32, 32,125, 10, 32, 32, 32, 32,125, 10, 10, 32, 32, 32, 32,108,
+     97,110,100,115, 99, 97,112,101, 32,123, 10, 32, 32, 32, 32, 32,
+     32, 32, 32,119,105,100,116,104, 32, 32, 32, 32, 32, 57, 48, 48,
+     10, 32, 32, 32, 32, 32, 32, 32, 32,104,101,105,103,104,116, 32,
+     32, 32, 32, 54, 55, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 99,
+    111,108,111,114, 32, 32, 32, 32, 32, 48,120,101, 48,101, 48,101,
+     48, 10, 32, 32, 32, 32, 32, 32, 32, 32,101,118,101,110,116, 32,
+     32, 32, 32, 32, 69, 86, 95, 83, 87, 58, 48, 58, 48, 10, 10, 32,
+     32, 32, 32, 32, 32, 32, 32,112, 97,114,116, 49, 32,123, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109,101, 32,
+     32, 32, 32, 32, 32,100,101,118,105, 99,101, 10, 32, 32, 32, 32,
+     32, 32, 32, 32, 32, 32, 32, 32,120, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 53, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+     32,121, 32, 32, 32, 32, 32, 32, 32, 32, 32, 52, 52, 48, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,114,111,116, 97,116,
+    105,111,110, 32, 32, 51, 10, 32, 32, 32, 32, 32, 32, 32, 32,125,
+     10, 32, 32, 32, 32, 32, 32, 32, 32,112, 97,114,116, 50, 32,123,
+     10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,110, 97,109,
+    101, 32, 32, 32, 32, 32,107,101,121, 98,111, 97,114,100, 10, 32,
+     32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,120, 32, 32, 32, 32,
+     32, 32, 32, 32, 50, 53, 48, 10, 32, 32, 32, 32, 32, 32, 32, 32,
+     32, 32, 32, 32,121, 32, 32, 32, 32, 32, 32, 32, 32, 52, 55, 48,
+     10, 32, 32, 32, 32, 32, 32, 32, 32,125, 10, 32, 32, 32, 32,125,
+     10,125, 10, 10,107,101,121, 98,111, 97,114,100, 32,123, 10, 32,
+     32, 32, 32, 99,104, 97,114,109, 97,112, 32,113,119,101,114,116,
+    121, 50, 10,125, 10, 10,110,101,116,119,111,114,107, 32,123, 10,
+     32, 32, 32, 32,115,112,101,101,100, 32, 32,102,117,108,108, 10,
+     32, 32, 32, 32,100,101,108, 97,121, 32, 32,110,111,110,101, 10,
+    125, 10
+};
+
+static const unsigned char _data_menu_png[3079] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 84,  0,  0,  0, 34, 16,  6,  0,  0,  0,145,128, 34,
+     94,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  1,
+     50, 73, 68, 65, 84,120,218,236,220,161, 78,195, 80, 20,128, 97,
+     70, 48, 44, 72, 50,  5, 10,130,  6, 75,192,162,120,132, 62,193,
+     44, 40, 12, 15, 64,176,224, 73, 61,104, 44,  4, 11,154, 48, 53,
+     12, 36, 56,194,228, 69,220, 94,193, 72, 89, 41, 91, 90,146,239,
+     51, 39, 89,167, 78,254,220,110,230,118, 66,  8, 33,132, 57,104,
+    165,121, 43,160,205, 22,198, 63,216,217,205,243,193, 83,217,215,
+     87, 94,227,220,218,180, 58,234,187,127,136,243,185, 55,254,228,
+    246, 38,203,214,214,127,  8,244,171,197, 60,206,131,165, 56, 87,
+     47,139,  7,199,150, 76,125,251, 69, 71,195,171, 56, 79,223,227,
+     28,101,191,124,197,247,251, 69,152, 47,150,202,244,165,174, 82,
+    103,149,127,131,166, 87,249,198,137, 37, 50,123,169,179,212,221,
+    196, 64,187, 23,150, 70,  3, 39,106,213, 64,161,  9,203,123,  2,
+    229, 95, 17, 40,  2,  5,129, 34, 80, 16, 40,  8, 20,129,130, 64,
+     17, 40,  8, 20,  4,138, 64, 65,160,  8, 20,  4, 10,  2, 69,160,
+     32, 80,  4, 10, 13,251,200,  4, 74,139, 13,123, 21,  3,125, 60,
+    140,243,237,218,210,152,189,212, 89,234,174,242,  9,122,118, 94,
+    118,244,194,244, 94,233,169,179,239, 38,220,205,148, 46,119, 58,
+     42,238,104,218, 30,196,217,205, 45,151,191,135,121, 87,220,209,
+     52, 42, 61,  0, 59,238,  7,197,191,120,168,233, 19,  0,  0,255,
+    255,  3,  0,  6, 52, 60,111, 44, 61, 74,153,  0,  0,  0,  0, 73,
+     69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_power_png[3782] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 58,  0,  0,  0, 67, 16,  6,  0,  0,  0,157,  6,202,
+     98,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  3,
+    241, 73, 68, 65, 84,120,218,236, 93, 63, 76, 83, 65, 28, 46,196,
+      5, 84, 22,140, 70,  3,139,104, 93, 76, 90,199, 86, 93,108, 36,
+     14,152, 72, 76,152, 72, 96, 51,178, 22, 54, 19, 37, 49, 14, 10,
+    107,113,172,166, 19,137,209,164, 14,  6,197, 77,234, 38, 68, 22,
+    234, 31,  6,136,  9, 70, 28, 20,203, 88,135,187,175,205, 93,251,
+    120,175,237, 81, 30,119,223,183,252,104, 31,247,238,253,238,235,
+    239,254,126,247,174,163, 92, 46,151,203,229,  8, 97,  9,142,224,
+    143, 43, 87,115,185,111, 95, 89, 32, 42,250,126, 10,123, 97, 70,
+    216,238, 92,107,247, 43,141, 10,187, 54, 41,236,230,201,125, 35,
+    212,109,244, 14, 10,155,250, 46,108, 98, 69, 35,112, 64,218,251,
+    134, 50,124, 41,  9,254, 39,108, 33, 38,236,226, 89, 97,183, 23,
+     72,104, 83,184,121, 70,216,161,219,218,133, 92,123,242,199, 15,
+     38, 53, 37,173,252,254,245,170,176,249, 31, 36,116, 79,116,201,
+      2, 76, 31, 19,182,127, 43,156,207, 57, 36,107,130,216, 41, 97,
+    103,119,132,221, 29,245, 75,217,233, 86, 91,248,104, 56,220, 68,
+    234,192,115,226,185,225,135,179,132, 34, 34,199,151,204,116,106,
+     14, 10,221,154, 31, 93, 57, 71,  9, 69,  1, 28,150,136, 12, 26,
+    177,240,203, 25, 66,227,113,105, 99,150,250, 23, 83,253,180,158,
+    208,145,  1, 55,250,  6,181,126, 90, 70,104,116, 70, 29, 87,186,
+     50,126,134,223,214, 17, 26,127,234,230,120,186,234,183,165, 17,
+    234, 26,172,141, 80, 91,122,179,205,251,221, 25, 33,172,  2,  9,
+    181,172,234, 37,161,140, 80,130,132, 18, 36,148,  8, 21,161,174,
+    142,  7,173, 35, 52,221, 35,237,113, 97,199,150, 88,196,135,154,
+    208,232, 19,245,243,137, 27, 44, 98,182,161,  4,  9, 37,154, 36,
+     20,210,135, 49, 95, 41,196,222, 72,172,182,150,158,240, 66, 64,
+    213, 31,196, 73,233,163,194, 66,227,242,123, 90,216,124,131,189,
+    223,241, 15,194, 66,127,154,145,247, 55, 47, 60,102,132,  6, 34,
+     18,184,246,165,177,236, 32,100,  6,176, 64,139,251,251,171,218,
+    136,150,  8,157,184, 91,159,200, 13,169, 23,125, 56,220, 88,118,
+    249,  7,106,122, 64, 87,181, 17,134,  9, 69, 27,167, 75, 57, 54,
+     52,225,111,163,146,125, 84,169, 94,233,177,174,135,252,  9, 67,
+    132,166, 60,182, 45,205,203,239,253, 21,220,123,  3,233,243,211,
+    245,175,199, 51,164,198, 40,161,250,202, 63, 34,179, 56,105, 54,
+    251,194,197,250,145,106,171,252,178,237,132,122,205,193,154, 38,
+     82,199,246, 91, 82,209,214,113,232,110, 41, 32, 49, 11, 44,210,
+     80,141, 67, 43,132,104,219,235,162, 59,193,110,151,153, 19,246,
+     82, 92,216, 79,104,139,125,198,151, 93,207,229, 31, 91,164,196,
+    104,132,130,208,146,214,233,193,164,187,159,128, 25,189, 88,236,
+    107,244,155, 40,192,253,188,218,108,194, 80,149, 91,240,232,148,
+    140,156, 51,155,253,248,199,250,223,175, 76,144, 26,163,132, 98,
+    107,184, 30,169,232,125,182,186,206,137,244,250,114, 27,242,123,
+    247,152,212, 24, 37, 20, 85,239,252,122,253,235,201,207,194, 98,
+     65,187,118, 23,148,246, 67,144,215,239,189, 82,211,215,140,115,
+    215,205,140,115,217, 41,242, 25, 39,246,203,170, 22,239,  2,208,
+    219,214,104,208,252, 60, 58, 61,217,203,106,126,196, 62, 15, 91,
+     48, 67, 52, 55,103,102,120,130,244,179,127, 73,100, 91, 35, 84,
+    199,242,178,106, 19,114,234, 46,250, 71, 88, 47,201,201,175, 55,
+    106,103,  7,233, 35, 47, 72,193,129, 18,234, 85, 37, 23,252,254,
+     49,201,162, 14, 85,149, 75,144, 80,130,132, 18, 36,148, 32,161,
+     36,148, 32,161,  4,  9, 37, 72, 40,161,163, 56, 73, 66, 25,161,
+     97,134,171,154,166,170,223,150, 17,186,150,118,147,208,170,223,
+    150, 17, 90,236,113,180,237,236,177,148, 80,172,254,148, 28, 81,
+     60,192,207,234,122,178,165,157,162,247,231,221, 32,180,214, 79,
+     75,  9,133,140,212, 86, 57, 40,252,170, 61,  6,196,242, 97, 75,
+     54,105, 87, 21, 12, 63,178, 73, 71, 39, 22, 32,244,246, 82, 47,
+     30, 54,192, 15,111,  1,187, 35,  7,241,160,211,176, 27, 23,118,
+     76,190, 18, 32,236,199,126, 32, 34,159, 73, 85,228,242, 29,199,
+     38, 22,252,  0,113, 26,118,158, 23,167, 66, 58, 12,153, 82,159,
+    179, 34,170,243,133,163,103,159, 85,100,164,242,115, 66, 70,  0,
+     54, 58,183,251,205,216,232,228, 44, 74,253,115, 65,170, 40, 35,
+     13,207,124,241,116, 66,165, 74,134,122, 17, 47,239,192, 46,186,
+    190, 91,106, 21,221,123, 93,218,193, 96, 85,230,230,105,245,115,
+     81,158,189,102,254,216,201, 14, 30,  8,107, 23, 72,168,101,248,
+     63,  0,179,160, 16,217,188,199, 69, 53,  0,  0,  0,  0, 73, 69,
+     78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_select_png[3374] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 98,  0,  0,  0, 33, 16,  6,  0,  0,  0,114,249,162,
+    143,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  2,
+     89, 73, 68, 65, 84,120,218,236,156, 59, 79,219, 96, 20,134, 67,
+    196,  2, 20,150, 86, 97, 33, 83, 43,179, 80, 53, 43,208,141,170,
+     98,233, 90,150, 72,229, 55,132,203,136, 84,196, 68,163,142,204,
+     69,176, 20,  9, 21, 41, 75, 85, 53, 35,  9, 99, 16, 83,211,192,
+     18,166,  8,150,150,203,202,112,206, 73, 20, 87, 86, 76,108, 82,
+     98, 63,207,242,202, 78,236, 47, 58, 62,175,191,139,227, 51, 48,
+    251,122,103,167,246, 59,241, 64, 60,125, 43,234,124, 86,253, 35,
+    250,108,190,253,123,206,167,  4, 64,147,234, 74,251,246,197,119,
+    221, 63,166,154, 19,189,252, 17,118,203,  3,225, 26,194,201,139,
+    190, 91, 39,209,161,119,198, 41,172,233,246,114,208, 51, 14,  6,
+     59,124,162, 33,186,240, 66, 13, 48,170, 31, 96,  4,232,  1,118,
+    195, 93, 74,180,247, 32, 95,107,162,231,169, 30,245, 16,115,171,
+    162,239,159,115, 81,224,241,178,119, 42, 90,220,244,123, 68,242,
+    126, 13,124, 40, 97,  4,232, 31, 44, 79, 45,111, 67, 51,132,157,
+    112,230,132, 32, 67,255, 97,121,219,217, 24, 73,127, 67, 35,140,
+      0, 81, 50,134,229,181,111, 67,216,114, 41, 67, 35,136,242, 80,
+    202, 22,133, 58, 26, 98,241,136,160, 65,244,177,213,209, 22,174,
+    101, 87,123,142,208, 92, 62,  5,136, 48,182,108,235,252, 21,173,
+     46,187,122,136,185, 51,130,  4,241,163,149,247,218, 67, 12,237,
+    138,102,174,  9, 14,196,143,204, 43,243,129,246, 16,147,121,130,
+      2, 48,153, 87, 67,164, 27,  4,  3, 32,221, 80, 67, 56, 87,  4,
+      3,192,185, 74, 18,  4,128, 22, 24,  2,  0, 67,  0, 96,  8,  0,
+    191,134,168,167,  8,  5, 64, 61,101,134, 56, 32, 24,  0,245,  3,
+     53,132,189,180, 13, 16,103,170, 57, 53,132, 85, 47,112, 87, 59,
+      0,136,133, 17, 86,204,  7,174, 73,117,169, 68,112, 32,126,180,
+    242,222,101,136,242,148,142,165,198,  9, 18, 68, 31, 27, 25, 89,
+    222,123, 46,187,238,213,  8, 22, 68,159, 47,211,238, 61, 30,134,
+    176,130, 79, 69,234, 43, 65,  4,177,188,254,183,176, 89,135,  7,
+    115,214, 83, 48,217,134, 40, 77,158,189, 71, 64, 62,159, 84,111,
+    109, 49,183,128,254,197,242,214,242,216, 27,159,165, 44,111,179,
+    162, 27,186, 77,157, 38,232,  7, 74, 47, 69,183,103,252, 30,209,
+    101,109, 87,107,224, 56,163,  6, 57, 20, 29,222,229, 34,192,255,
+    227, 70,111,220,219,179,162,149,202,125,207, 16,176,216,177, 53,
+    248,235,155,232,155,143,162,211, 58,123,183,250, 78,  0, 15, 65,
+    115,217,180, 44,250,115, 68, 71, 52,149,110,207, 56, 24,206, 15,
+    179, 33, 85, 65,183, 11,251,162, 25, 45, 54,107,111,228,217,171,
+    170,148,201,135,110, 38,195,246, 39,212,234, 19,143, 30, 32, 27,
+    180,165, 59,  0,  0,  0,255,255,  3,  0, 97,168,142, 82, 14,236,
+     17,113,  0,  0,  0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_send_png[3561] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 46,  0,  0,  0, 47, 16,  6,  0,  0,  0,204,117, 36,
+    209,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  3,
+     20, 73, 68, 65, 84,120,218,236,156, 61,108,211, 64, 20,199, 15,
+    171, 75,194,199, 66, 21, 22, 60, 32,192, 93, 64,201, 72, 93,182,
+     34,196,208, 74, 32,164, 78, 29,186, 33, 58, 66,202, 80, 33,  8,
+     31, 98, 32,201, 74,214, 64, 59, 69,170,130,212, 76, 72, 97,107,
+    186,  6,193, 66, 40, 48, 36,226,163, 18, 75, 33,237,202,112,239,
+    165,178, 19,247, 92,251,238,156,212,239,191,188,184, 57,217,201,
+    239,222, 61,191,119,125,206,177,169,171, 43, 43, 91, 95,216,144,
+    235,236, 54,183,201, 55,  7,143,251,243,206,105,135, 79, 99,209,
+     94, 62,177,202,109, 38,195,173,181,195,173,  9,128,205,223, 48,
+    176, 10,246,164,224,132,183,157,182,125,134,219, 86, 22,236,  9,
+    110,155,205,152,  0,183, 10,220, 78,127,  3,208, 93,120, 99, 67,
+    205,245,112,194,204, 37,184, 46,252,125,119, 30,192,127,224,182,
+    126,129,219, 78,106,196,129, 35,224,217,167,112,140, 30,154,142,
+    118,101, 37, 97,101,217,112,108,127,132, 21,112,138,219,242, 21,
+     85,161,105, 76, 77,136, 88,104,128,  7, 35,224, 60, 27,  9, 89,
+    240, 57, 95,192,113,237, 19,183,235, 63,134, 12, 56,222,212,238,
+     31,  7, 15,234,178, 35,161,153,199,176, 32,225, 94, 80,122, 21,
+    214,243, 67,  2,159,  4, 15, 88,168,178, 35, 45,188, 23, 60,132,
+    239, 89,236,  6,141,249, 70, 72,208, 27, 44, 86,194,216,143, 43,
+     25, 87,182, 50,224,113,  5, 45, 15,188, 79,224,120,194,185,115,
+    140, 52,  0,252,226, 93,103,210, 16, 24,120,194,117,194,228, 42,
+     65, 30,164,211,215,157,217, 89, 96,224,179, 57,231,  9, 73,  7,
+     43,147,118, 86,206,190,129, 35,224,233, 37,130, 24, 68,115,231,
+    189, 66,140,  7,240,153, 28, 65,147, 17, 98,174, 61, 16,  0,199,
+    129, 88,234,146, 66,214, 41,147,  2,224,184,169, 68,146,235,233,
+    251, 49,221, 24, 28,244, 73,146, 61,221,118,  1,199, 60,155,178,
+     17, 53,178,242, 46,224, 19,  5,130,162,163, 64,178, 10,232,225,
+     55,  9,138, 14,153,219,  0,124,252,  6,193,208,227,233,232,225,
+     63,  9,134,150, 88,254,207,112,198, 24,146,106, 25,132,128,128,
+     19,112,146,116,224, 45,218, 21,212,162,118, 10,128, 99, 99, 12,
+     73,173,246,118,  1,120,231, 45,193,208,161,207,247, 12,124, 65,
+     48,116,168,149,197, 24,158,165,208,162, 82,216,195,216,151,165,
+    236,191, 65,146, 10,124,209,  3,248,102,131,224,200, 84,175, 75,
+    183,233,  1,188,215, 71, 77,105,162,212, 80,178, 55, 47, 40,124,
+    214, 31, 17, 44, 25,158, 93,203,249,172, 52,201,211,195,233,253,
+     69,110,251,187,108,  5,165, 61, 54,166, 83,246,226,179,146,132,
+    182,102,239,126,114,  1,112,156,161,202,119,130,233, 39,132,148,
+    109,209, 72,159,155, 87,155,151,184,173,231,  9,238, 32,161, 67,
+    138,251,197, 15,185, 91, 88,217,226,182,113,153, 32, 51,198, 88,
+    121,202,233,144, 98,  5,220,158,125,109,199, 27,252,225, 65,135,
+      4,238,  6, 95,249, 26,143, 24, 93, 42,  5,  5, 45,  9, 56,170,
+    254,146,219,226, 95,175,116,104, 52,133,105,241,243, 91,238,138,
+     49, 98,224,238,252,253, 25,124,192,218,147,209, 74, 43,209, 81,
+     48,100, 20,119,100, 59,144,162,127,177, 97, 41,139,249,232,114,
+    117, 56, 39,192, 13,120,121, 45,108,200, 16, 73,211,163,223,189,
+      9,192,173,  3,120, 24,  9,187, 74,211,240,252,227, 68,145, 91,
+    217, 61,142,238,103,238, 27,176, 73,215,185,  3,  3,214,116, 77,
+    113,196, 63,110,128, 49,177,233, 42, 24, 18, 16,146,204, 95, 48,
+      1,208, 25, 54, 46,152,136,118,202,185,130, 16,112,159, 82, 81,
+    125,227,255,  3,  0,233,237,207,153, 62,209, 88,144,  0,  0,  0,
+      0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_spacebar_png[2916] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0,145,  0,  0,  0, 34, 16,  6,  0,  0,  0, 15, 22,231,
+    187,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  0,
+    143, 73, 68, 65, 84,120,218,236,212, 49,  1,  0, 32, 12,192, 48,
+    192,214,196,129, 13,110, 28,160,103, 70,248,144,177, 39,145,208,
+    163, 61,238,121,185,215,108,  0, 69,134,  4,128, 17,  1, 70, 36,
+      1, 96, 68,128, 17, 73,  0, 24, 17, 96, 68, 18,  0, 70,  4, 24,
+    145,  4,128, 17,  1, 70, 36,  1, 96, 68,128, 17, 73,  0, 24, 17,
+     96, 68, 18,  0, 70,  4, 24,145,  4,128, 17,  1, 70, 36,  1, 96,
+     68,128, 17, 73,  0, 24, 17, 96, 68, 18,  0, 70,  4, 24,145,  4,
+    128, 17,  1, 70, 36,  1, 96, 68,128, 17, 73,  0, 24, 17, 96, 68,
+     18,  0, 70,  4, 24,145,  4, 64,181, 15,  0,  0,255,255,  3,  0,
+     87,196,  5, 65, 55,106, 37, 28,  0,  0,  0,  0, 73, 69, 78, 68,
+    174, 66, 96,130
+};
+
+static const unsigned char _data_volume_down_png[3586] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 67,  0,  0,  0, 49, 16,  6,  0,  0,  0,209,176,200,
+    250,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  3,
+     45, 73, 68, 65, 84,120,218,236,157,191,111,211, 64, 20,199, 13,
+    234, 82, 68,197, 64, 21, 33,148, 12, 80,  4, 11,136,172,117, 97,
+    138,132, 24, 26,  9,150,178, 88, 34, 27,136, 17,  8,107, 11, 35,
+     16,254,134, 84,242, 66, 36, 36,134, 14,168, 82, 38,136, 25, 88,
+    140,202,210,136, 76,141, 16, 32,178, 80, 53,108,152,225,222,139,
+    100,171,174,206,241, 97,159,115,223,239,242,106,183,138,239,221,
+    125,242,238,221, 15, 95,143,  5, 65, 16,  4,129,101, 93,187,238,
+    186,131,175,150, 98,205,187,194, 86,190, 11,123,233, 85,186,207,
+    219,125, 40,236,222, 25, 97,255, 56, 22,100, 89,150,101,125,120,
+    239, 56, 75, 23,212,125,222,156, 90,  0,170, 85, 97,107,132, 88,
+    229,  7,253,193,  2,217,245,116,207, 89,229, 31, 14,  8,144,183,
+    194,118,169, 74,124, 31,192,104,  1,198,242, 23, 97,215,206,  9,
+    123,194,205,182,248, 12, 94,131,236,152,128,232, 80,185, 62, 94,
+     70, 19,103,  2,  6, 71,134,134, 71, 17,162, 71,191,232,233,225,
+     14,131,217,160,235,234,  3, 97,219, 54, 34, 73, 50, 29, 79,  6,
+    196,163,147, 84,225, 87,139,225, 30,151,147,203, 61,239,162,201,
+    149,128, 17,  5, 98,146, 51, 20, 76, 92,110,  0,162,  8,140,250,
+     70,177,129,136,  3,132,253,130, 18,230, 24, 23, 95,210,232, 98,
+     97, 54,221,174, 53,105, 20,179, 47,108,255, 49, 80,144,138, 24,
+    245,103,102,184,111,138,159,169,193, 40,255,164,136,241,194, 12,
+    247,217, 79,246, 27,138,  1,195,182,205,172,  6, 83,253,150,  6,
+    163, 98,232, 55,135,115, 42, 40,  6, 12, 83,186,144,184,209, 10,
+     36, 57, 92,133,  0,  6,132, 46,  5, 96,196,118, 41, 57,231, 88,
+    188, 40, 89, 63, 11, 48,180,210, 94, 41, 95, 32, 26, 61, 93,106,
+      2, 96,104, 17, 33,244,  1,130, 53,135,198,201, 67,119,105,219,
+    130,221,211,181,132,136, 24,249,  0,177,163,123, 73, 17, 49, 82,
+     73, 54, 73, 44,223, 18,182,186,163, 22,176,197,155,194,182,126,
+      3, 12,173,180,186,158,237,243,120, 79,237, 36,226,208,132,228,
+    242,138,176,234,182, 50,162, 43, 41,148,120,179,115,191, 25,190,
+    175,126,173,  7, 96, 20, 82,158, 23,190, 86,191,148,  1, 48, 10,
+    169,209, 59,140, 74,160, 67, 20,157,161, 29, 59,  0,195,104,241,
+     38,230,218, 32,124, 63,154,115,  0, 12,211, 34,  5,189,234,121,
+    250, 70,248,126,247, 60,134,171, 90,169,181,159, 44,244,175, 45,
+    165,123, 30,111, 90,110,211, 84,250, 34,  1,210,255,  6, 48,180,
+    146,236,238,242, 62,231,  2,138,214, 70,254,255,171,151,232, 74,
+     50, 21, 55,104,123, 69,247,146,  2,140, 92,  1,225,174,104,236,
+      0, 12,232,144,174,168,117,160, 27, 32,  0, 67, 11, 13, 75, 97,
+     64,126,109,231, 93, 34, 36,159, 33,157,122,162,  7, 32, 67, 68,
+     12,189,244,233, 47,234,  0, 96, 64,242, 96,168,159, 90, 45, 72,
+     18,216,  4, 10, 71,130,225,223, 55,179, 26, 76,245, 91, 30, 12,
+    223, 80, 48,124,160,112, 36, 24, 35, 26, 38,121, 87,204,112,159,
+    253, 28,109,  3,  5,169,228,115,107, 67,216,241,140,158,114,199,
+    126,177,159,144, 36, 24,252, 13,218,122, 58,155,110,179, 95,136,
+     20, 83, 14, 87,187,207,103,171,107, 97, 63,216, 47,104, 74, 48,
+     88,155,118,177,  1,225,114,111,226,228, 28,181, 96, 68,  1,233,
+     12,138,213,101,  0,136,164,154,114,173,132, 67,241, 46,237, 76,
+    186, 67,135,188,231,125, 34, 15, 79, 84,189,166, 67,238,135,247,
+    208,196,153,130,193,154,172, 10,210,117,249,182,176,252,  2, 12,
+     31,217, 28,221,163,152, 86,156, 52,250,159,169,171,240, 34, 32,
+    148,208,180,185,130, 17,  7, 74,135,174, 59,252, 31, 80,222, 80,
+     68, 73,121, 98, 77,236, 86, 58,128,160, 88,255,  6,  0,161,232,
+    198,240, 68,249,103, 76,  0,  0,  0,  0, 73, 69, 78, 68,174, 66,
+     96,130
+};
+
+static const unsigned char _data_volume_up_png[3856] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  0, 67,  0,  0,  0, 49, 16,  6,  0,  0,  0,209,176,200,
+    250,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0, 10, 79,105, 67, 67, 80, 80,104,
+    111,116,111,115,104,111,112, 32, 73, 67, 67, 32,112,114,111,102,
+    105,108,101,  0,  0,120,218,157, 83,103, 84, 83,233, 22, 61,247,
+    222,244, 66, 75,136,128,148, 75,111, 82, 21,  8, 32, 82, 66,139,
+    128, 20,145, 38, 42, 33,  9, 16, 74,136, 33,161,217, 21, 81,193,
+     17, 69, 69,  4, 27,200,160,136,  3,142,142,128,140, 21, 81, 44,
+     12,138, 10,216,  7,228, 33,162,142,131,163,136,138,202,251,225,
+    123,163,107,214,188,247,230,205,254,181,215, 62,231,172,243,157,
+    179,207,  7,192,  8, 12,150, 72, 51, 81, 53,128, 12,169, 66, 30,
+     17,224,131,199,196,198,225,228, 46, 64,129, 10, 36,112,  0, 16,
+      8,179,100, 33,115,253, 35,  1,  0,248,126, 60, 60, 43, 34,192,
+      7,190,  0,  1,120,211, 11,  8,  0,192, 77,155,192, 48, 28,135,
+    255, 15,234, 66,153, 92,  1,128,132,  1,192,116,145, 56, 75,  8,
+    128, 20,  0, 64,122,142, 66,166,  0, 64, 70,  1,128,157,152, 38,
+     83,  0,160,  4,  0, 96,203, 99, 98,227,  0, 80, 45,  0, 96, 39,
+    127,230,211,  0,128,157,248,153,123,  1,  0, 91,148, 33, 21,  1,
+    160,145,  0, 32, 19,101,136, 68,  0,104, 59,  0,172,207, 86,138,
+     69,  0, 88, 48,  0, 20,102, 75,196, 57,  0,216, 45,  0, 48, 73,
+     87,102, 72,  0,176,183,  0,192,206, 16, 11,178,  0,  8, 12,  0,
+     48, 81,136,133, 41,  0,  4,123,  0, 96,200, 35, 35,120,  0,132,
+    153,  0, 20, 70,242, 87, 60,241, 43,174, 16,231, 42,  0,  0,120,
+    153,178, 60,185, 36, 57, 69,129, 91,  8, 45,113,  7, 87, 87, 46,
+     30, 40,206, 73, 23, 43, 20, 54, 97,  2, 97,154, 64, 46,194,121,
+    153, 25, 50,129, 52, 15,224,243,204,  0,  0,160,145, 21, 17,224,
+    131,243,253,120,206, 14,174,206,206, 54,142,182, 14, 95, 45,234,
+    191,  6,255, 34, 98, 98,227,254,229,207,171,112, 64,  0,  0,225,
+    116,126,209,254, 44, 47,179, 26,128, 59,  6,128,109,254,162, 37,
+    238,  4,104, 94, 11,160,117,247,139,102,178, 15, 64,181,  0,160,
+    233,218, 87,243,112,248,126, 60, 60, 69,161,144,185,217,217,229,
+    228,228,216, 74,196, 66, 91, 97,202, 87,125,254,103,194, 95,192,
+     87,253,108,249,126, 60,252,247,245,224,190,226, 36,129, 50, 93,
+    129, 71,  4,248,224,194,204,244, 76,165, 28,207,146,  9,132, 98,
+    220,230,143, 71,252,183, 11,255,252, 29,211, 34,196, 73, 98,185,
+     88, 42, 20,227, 81, 18,113,142, 68,154,140,243, 50,165, 34,137,
+     66,146, 41,197, 37,210,255,100,226,223, 44,251,  3, 62,223, 53,
+      0,176,106, 62,  1,123,145, 45,168, 93, 99,  3,246, 75, 39, 16,
+     88,116,192,226,247,  0,  0,242,187,111,193,212, 40,  8,  3,128,
+    104,131,225,207,119,255,239, 63,253, 71,160, 37,  0,128,102, 73,
+    146,113,  0,  0, 94, 68, 36, 46, 84,202,179, 63,199,  8,  0,  0,
+     68,160,129, 42,176, 65, 27,244,193, 24, 44,192,  6, 28,193,  5,
+    220,193, 11,252, 96, 54,132, 66, 36,196,194, 66, 16, 66, 10,100,
+    128, 28,114, 96, 41,172,130, 66, 40,134,205,176, 29, 42, 96, 47,
+    212, 64, 29, 52,192, 81,104,134,147,112, 14, 46,194, 85,184, 14,
+     61,112, 15,250, 97,  8,158,193, 40,188,129,  9,  4, 65,200,  8,
+     19, 97, 33,218,136,  1, 98,138, 88, 35,142,  8, 23,153,133,248,
+     33,193, 72,  4, 18,139, 36, 32,201,136, 20, 81, 34, 75,145, 53,
+     72, 49, 82,138, 84, 32, 85, 72, 29,242, 61,114,  2, 57,135, 92,
+     70,186,145, 59,200,  0, 50,130,252,134,188, 71, 49,148,129,178,
+     81, 61,212, 12,181, 67,185,168, 55, 26,132, 70,162, 11,208,100,
+    116, 49,154,143, 22,160,155,208,114,180, 26, 61,140, 54,161,231,
+    208,171,104, 15,218,143, 62, 67,199, 48,192,232, 24,  7, 51,196,
+    108, 48, 46,198,195, 66,177, 56, 44,  9,147, 99,203,177, 34,172,
+     12,171,198, 26,176, 86,172,  3,187,137,245, 99,207,177,119,  4,
+     18,129, 69,192,  9, 54,  4,119, 66, 32, 97, 30, 65, 72, 88, 76,
+     88, 78,216, 72,168, 32, 28, 36, 52, 17,218,  9, 55,  9,  3,132,
+     81,194, 39, 34,147,168, 75,180, 38,186, 17,249,196, 24, 98, 50,
+     49,135, 88, 72, 44, 35,214, 18,143, 19, 47, 16,123,136, 67,196,
+     55, 36, 18,137, 67, 50, 39,185,144,  2, 73,177,164, 84,210, 18,
+    210, 70,210,110, 82, 35,233, 44,169,155, 52, 72, 26, 35,147,201,
+    218,100,107,178,  7, 57,148, 44, 32, 43,200,133,228,157,228,195,
+    228, 51,228, 27,228, 33,242, 91, 10,157, 98, 64,113,164,248, 83,
+    226, 40, 82,202,106, 74, 25,229, 16,229, 52,229,  6,101,152, 50,
+     65, 85,163,154, 82,221,168,161, 84, 17, 53,143, 90, 66,173,161,
+    182, 82,175, 81,135,168, 19, 52,117,154, 57,205,131, 22, 73, 75,
+    165,173,162,149,211, 26,104, 23,104,247,105,175,232,116,186, 17,
+    221,149, 30, 78,151,208, 87,210,203,233, 71,232,151,232,  3,244,
+    119, 12, 13,134, 21,131,199,136,103, 40, 25,155, 24,  7, 24,103,
+     25,119, 24,175,152, 76,166, 25,211,139, 25,199, 84, 48, 55, 49,
+    235,152,231,153, 15,153,111, 85, 88, 42,182, 42,124, 21,145,202,
+     10,149, 74,149, 38,149, 27, 42, 47, 84,169,170,166,170,222,170,
+     11, 85,243, 85,203, 84,143,169, 94, 83,125,174, 70, 85, 51, 83,
+    227,169,  9,212,150,171, 85,170,157, 80,235, 83, 27, 83,103,169,
+     59,168,135,170,103,168,111, 84, 63,164,126, 89,253,137,  6, 89,
+    195, 76,195, 79, 67,164, 81,160,177, 95,227,188,198, 32, 11, 99,
+     25,179,120, 44, 33,107, 13,171,134,117,129, 53,196, 38,177,205,
+    217,124,118, 42,187,152,253, 29,187,139, 61,170,169,161, 57, 67,
+     51, 74, 51, 87,179, 82,243,148,102, 63,  7,227,152,113,248,156,
+    116, 78,  9,231, 40,167,151,243,126,138,222, 20,239, 41,226, 41,
+     27,166, 52, 76,185, 49,101, 92,107,170,150,151,150, 88,171, 72,
+    171, 81,171, 71,235,189, 54,174,237,167,157,166,189, 69,187, 89,
+    251,129, 14, 65,199, 74, 39, 92, 39, 71,103,143,206,  5,157,231,
+     83,217, 83,221,167, 10,167, 22, 77, 61, 58,245,174, 46,170,107,
+    165, 27,161,187, 68,119,191,110,167,238,152,158,190, 94,128,158,
+     76,111,167,222,121,189,231,250, 28,125, 47,253, 84,253,109,250,
+    167,245, 71, 12, 88,  6,179, 12, 36,  6,219, 12,206, 24, 60,197,
+     53,113,111, 60, 29, 47,199,219,241, 81, 67, 93,195, 64, 67,165,
+     97,149, 97,151,225,132,145,185,209, 60,163,213, 70,141, 70, 15,
+    140,105,198, 92,227, 36,227,109,198,109,198,163, 38,  6, 38, 33,
+     38, 75, 77,234, 77,238,154, 82, 77,185,166, 41,166, 59, 76, 59,
+     76,199,205,204,205,162,205,214,153, 53,155, 61, 49,215, 50,231,
+    155,231,155,215,155,223,183, 96, 90,120, 90, 44,182,168,182,184,
+    101, 73,178,228, 90,166, 89,238,182,188,110,133, 90, 57, 89,165,
+     88, 85, 90, 93,179, 70,173,157,173, 37,214,187,173,187,167, 17,
+    167,185, 78,147, 78,171,158,214,103,195,176,241,182,201,182,169,
+    183, 25,176,229,216,  6,219,174,182,109,182,125, 97,103, 98, 23,
+    103,183,197,174,195,238,147,189,147,125,186,125,141,253, 61,  7,
+     13,135,217, 14,171, 29, 90, 29,126,115,180,114, 20, 58, 86, 58,
+    222,154,206,156,238, 63,125,197,244,150,233, 47,103, 88,207, 16,
+    207,216, 51,227,182, 19,203, 41,196,105,157, 83,155,211, 71,103,
+     23,103,185,115,131,243,136,139,137, 75,130,203, 46,151, 62, 46,
+    155, 27,198,221,200,189,228, 74,116,245,113, 93,225,122,210,245,
+    157,155,179,155,194,237,168,219,175,238, 54,238,105,238,135,220,
+    159,204, 52,159, 41,158, 89, 51,115,208,195,200, 67,224, 81,229,
+    209, 63, 11,159,149, 48,107,223,172,126, 79, 67, 79,129,103,181,
+    231, 35, 47, 99, 47,145, 87,173,215,176,183,165,119,170,247, 97,
+    239, 23, 62,246, 62,114,159,227, 62,227, 60, 55,222, 50,222, 89,
+     95,204, 55,192,183,200,183,203, 79,195,111,158, 95,133,223, 67,
+    127, 35,255,100,255,122,255,209,  0,167,128, 37,  1,103,  3,137,
+    129, 65,129, 91,  2,251,248,122,124, 33,191,142, 63, 58,219,101,
+    246,178,217,237, 65,140,160,185, 65, 21, 65,143,130,173,130,229,
+    193,173, 33,104,200,236,144,173, 33,247,231,152,206,145,206,105,
+     14,133, 80,126,232,214,208,  7, 97,230, 97,139,195,126, 12, 39,
+    133,135,133, 87,134, 63,142,112,136, 88, 26,209, 49,151, 53,119,
+    209,220, 67,115,223, 68,250, 68,150, 68,222,155,103, 49, 79, 57,
+    175, 45, 74, 53, 42, 62,170, 46,106, 60,218, 55,186, 52,186, 63,
+    198, 46,102, 89,204,213, 88,157, 88, 73,108, 75, 28, 57, 46, 42,
+    174, 54,110,108,190,223,252,237,243,135,226,157,226, 11,227,123,
+     23,152, 47,200, 93,112,121,161,206,194,244,133,167, 22,169, 46,
+     18, 44, 58,150, 64, 76,136, 78, 56,148,240, 65, 16, 42,168, 22,
+    140, 37,242, 19,119, 37,142, 10,121,194, 29,194,103, 34, 47,209,
+     54,209,136,216, 67, 92, 42, 30, 78,242, 72, 42, 77,122,146,236,
+    145,188, 53,121, 36,197, 51,165, 44,229,185,132, 39,169,144,188,
+     76, 13, 76,221,155, 58,158, 22,154,118, 32,109, 50, 61, 58,189,
+     49,131,146,145,144,113, 66,170, 33, 77,147,182,103,234,103,230,
+    102,118,203,172,101,133,178,254,197,110,139,183, 47, 30,149,  7,
+    201,107,179,144,172,  5, 89, 45, 10,182, 66,166,232, 84, 90, 40,
+    215, 42,  7,178,103,101, 87,102,191,205,137,202, 57,150,171,158,
+     43,205,237,204,179,202,219,144, 55,156,239,159,255,237, 18,194,
+     18,225,146,182,165,134, 75, 87, 45, 29, 88,230,189,172,106, 57,
+    178, 60,113,121,219, 10,227, 21,  5, 43,134, 86,  6,172, 60,184,
+    138,182, 42,109,213, 79,171,237, 87,151,174,126,189, 38,122, 77,
+    107,129, 94,193,202,130,193,181,  1,107,235, 11, 85, 10,229,133,
+    125,235,220,215,237, 93, 79, 88, 47, 89,223,181, 97,250,134,157,
+     27, 62, 21,137,138,174, 20,219, 23,151, 21,127,216, 40,220,120,
+    229, 27,135,111,202,191,153,220,148,180,169,171,196,185,100,207,
+    102,210,102,233,230,222, 45,158, 91, 14,150,170,151,230,151, 14,
+    110, 13,217,218,180, 13,223, 86,180,237,245,246, 69,219, 47,151,
+    205, 40,219,187,131,182, 67,185,163,191, 60,184,188,101,167,201,
+    206,205, 59, 63, 84,164, 84,244, 84,250, 84, 54,238,210,221,181,
+     97,215,248,110,209,238, 27,123,188,246, 52,236,213,219, 91,188,
+    247,253, 62,201,190,219, 85,  1, 85, 77,213,102,213,101,251, 73,
+    251,179,247, 63,174,137,170,233,248,150,251,109, 93,173, 78,109,
+    113,237,199,  3,210,  3,253,  7, 35, 14,182,215,185,212,213, 29,
+    210, 61, 84, 82,143,214, 43,235, 71, 14,199, 31,190,254,157,239,
+    119, 45, 13, 54, 13, 85,141,156,198,226, 35,112, 68,121,228,233,
+    247,  9,223,247, 30, 13, 58,218,118,140,123,172,225,  7,211, 31,
+    118, 29,103, 29, 47,106, 66,154,242,154, 70,155, 83,154,251, 91,
+     98, 91,186, 79,204, 62,209,214,234,222,122,252, 71,219, 31, 15,
+    156, 52, 60, 89,121, 74,243, 84,201,105,218,233,130,211,147,103,
+    242,207,140,157,149,157,125,126, 46,249,220, 96,219,162,182,123,
+    231, 99,206,223,106, 15,111,239,186, 16,116,225,210, 69,255,139,
+    231, 59,188, 59,206, 92,242,184,116,242,178,219,229, 19, 87,184,
+     87,154,175, 58, 95,109,234,116,234, 60,254,147,211, 79,199,187,
+    156,187,154,174,185, 92,107,185,238,122,189,181,123,102,247,233,
+     27,158, 55,206,221,244,189,121,241, 22,255,214,213,158, 57, 61,
+    221,189,243,122,111,247,197,247,245,223, 22,221,126,114, 39,253,
+    206,203,187,217,119, 39,238,173,188, 79,188, 95,244, 64,237, 65,
+    217, 67,221,135,213, 63, 91,254,220,216,239,220,127,106,192,119,
+    160,243,209,220, 71,247,  6,133,131,207,254,145,245,143, 15, 67,
+      5,143,153,143,203,134, 13,134,235,158, 56, 62, 57, 57,226, 63,
+    114,253,233,252,167, 67,207,100,207, 38,158, 23,254,162,254,203,
+    174, 23, 22, 47,126,248,213,235,215,206,209,152,209,161,151,242,
+    151,147,191,109,124,165,253,234,192,235, 25,175,219,198,194,198,
+     30,190,201,120, 51, 49, 94,244, 86,251,237,193,119,220,119, 29,
+    239,163,223, 15, 79,228,124, 32,127, 40,255,104,249,177,245, 83,
+    208,167,251,147, 25,147,147,255,  4,  3,152,243,252, 99, 51, 45,
+    219,  0,  0,  0, 32, 99, 72, 82, 77,  0,  0,122, 37,  0,  0,128,
+    131,  0,  0,249,255,  0,  0,128,233,  0,  0,117, 48,  0,  0,234,
+     96,  0,  0, 58,152,  0,  0, 23,111,146, 95,197, 70,  0,  0,  4,
+     59, 73, 68, 65, 84,120,218,236,157, 61, 76, 20, 65, 20,199, 87,
+     67,163,  6, 27,  8, 24,195, 53, 66,176, 17,185, 82, 78,109,196,
+     24, 11, 76, 52, 36, 84, 68,175,195,163,148,143, 82,161,177, 16,
+    177,  4,236, 78,115, 21,141,197, 81,153, 80, 41,123,150,103,176,
+      1, 65, 11,136,  2,209,  6,149, 43,207, 98,222,255,200, 12,187,
+    183,115,183,115, 31,187,251,254,205,228,150,185,189,121, 55,191,
+    155,247,222,124, 44,167,138,197, 98,177, 88,180,172, 27, 55, 51,
+    153,237, 45,171,198,234,125,233,239,253,155,147, 22,171,166,250,
+    248, 97,116,180,187,199,178, 90,204,222,182,237,142, 40,  7,191,
+    201, 32,196,246, 13,125,192,107, 81,236,116,202,160,172, 94, 18,
+    229,239,247,220,181,102,212, 98,  6,132,228, 39,  2, 97, 88,169,
+    176, 95,155,102,  3,180,216, 20,129,136, 17,133,218,145,190,198,
+    160, 52,  4,140,193,105, 81,142,116,211,133,225,230, 48,167,119,
+     78,148,207,233,245,114,156, 70,148, 23,220,213,149,233,116,101,
+    213, 31,217, 10, 16, 77, 46,180, 19,237,102, 25,  6,  3, 95,108,
+     98, 61,152,102,162,221, 12,136, 33, 48,224, 50,130, 10,132, 27,
+     32,176,139, 85, 33, 24,  8, 42,135,158,133,211,108,216,  5, 59,
+     89,154, 96, 12,205,136,242,108, 38,156,102,195, 46,216,201,242,
+      0,  3,191,160,176,184, 14, 93,215,194, 35,135,  7, 24,241,120,
+     52,191,134,168,218,173, 15,198, 82, 68,193, 88, 98, 20,202,130,
+    209,245, 51,154, 95,  3, 38,198, 88, 46, 96,132, 53,216,100, 25,
+    202, 74,162,170,174,131,112,219,165, 31, 75, 49, 24,242,136,249,
+    182, 57, 58,112,224,139,217,251, 77,156, 19,101, 42,165,251,  3,
+     96, 48,154,234, 23,141, 14,108, 55,148, 62, 23, 70,157,175,143,
+     19, 32,103, 50, 12,134,150, 98,  7,141,  5,194,111,140,167,142,
+     56,216,118,176,184, 40,215,195,188,205,237,105,  6, 67, 75, 59,
+     29,245,249, 28,116,156,105, 32,112,191,228, 26,101, 91,180, 81,
+     10, 27,154,236, 62,249,125,183,190,186,141, 28, 12, 70, 93,  5,
+     32,208,113,166,178, 64,140, 12,133,135,242,245,100, 78,126,189,
+     50,227,156,133,158, 12, 74, 91,184,179,252, 72,119, 15, 43, 92,
+    212,200,154, 89,192, 16,139,100, 41,150, 72, 83,123, 38, 20,151,
+     49, 48, 43,202,220, 21, 81,230,123,  8,136,126, 81, 38, 18,244,
+    119,  6,195,140, 38, 90, 53, 43,182,154,  5, 17, 35,142, 69,171,
+    196, 27,127,100,151,177,121,158,234,211,196, 93,124, 65,238,248,
+    252, 56, 93, 95,147,235, 89, 99,236, 74,130, 25,  3, 93,144, 93,
+      7,132,205,215, 80,254,177,  2,212,156, 94, 44,117,156,198, 50,
+     24,129, 18,210,207,213,238,202, 58, 30,177,  4,130,204, 93, 23,
+     48,142,231,113, 24,140, 80,100, 79,106, 16,139,145,229, 68,172,
+    179,167,251,  9, 12, 70, 32,213,118,215, 35,216,221, 43,159,189,
+     48, 24, 33, 21,178,  8,104,115,170,252,  8,162,130,225,189, 49,
+    137,193,  8,148, 48,223,160,198, 20,182,178,251,189,127, 65,113,
+     45,157,206,233,179,170,227, 35,160,156,174,250,210,202,172, 94,
+    189,174,251,242,188, 65,181,202,231,  9,  4,154,193,108, 39,151,
+    146, 59,148, 71,  2,117,107,102,206, 13,156,117,103,112, 24, 12,
+    159,202,254,208,172, 72, 29, 97,234,124,206,155,132,243,117, 28,
+     21,197,201,192, 35,202, 98,236,119,114, 86, 82,  2,116, 93, 29,
+     41, 24,140,134,168,212,161,134, 15,112,149, 92, 76,202,121, 68,
+     43,208, 17,205,123, 23, 41,  6,249, 39,215,195,161,112,107,139,
+     99, 12,231,104,254,160,190,128,232,186, 34, 93, 23,147,190, 46,
+    187, 26,156,217,133,139,193,162, 89, 41, 54,233,115,203, 86,120,
+    196, 40, 59, 63, 80, 47, 87,244,139, 58, 52,233,115, 45,  5,107,
+     33,202,218,153,149, 26,151,179, 21,184,152,149,  7,110,119, 98,
+     48,154, 66,232, 80, 11,139, 99,134,238,235,246,124,146,229,239,
+     94,243, 26, 12,134,164, 35, 44, 91, 79, 54, 24, 16, 67, 66, 80,
+    153, 38,224, 98,148, 29,229,198,188,222,201, 96, 72,218,237,  8,
+    167, 93,110, 46,198, 93, 28,124,178,116,192, 56, 57,209, 17,145,
+    160,179,147, 81, 40, 11, 70, 84,159,138,199, 79,  3,244,  0,195,
+    142,232, 19,103,108,126,210, 78,121, 48, 16,124,169,171,117,161,
+     29, 41,166,194, 29,116, 26,  3,  3,202, 62,141,134,249, 81,177,
+    211, 24, 24,165,  7,171,134,244, 20, 56,236,226,216,162,202,116,
+     53, 59, 19,174,168, 29,118,192, 46, 86,149, 96, 96,243,233,252,
+    223, 96,  3,130,118,195, 14,183, 51,157, 44, 77, 48,220,  0,201,
+    127, 14, 86,112,201, 64,212,  8, 12, 21,144, 69,218,120,178,188,
+     45,202,163, 38,249,194,209, 14,180,107,254,144,129,168, 78, 62,
+    215, 74,176,222,111, 95, 21,101,130, 58,100,128,246, 27,196,246,
+    107,219,124,184,  8,108, 93,195, 78,165,  2, 63, 59,188,177, 96,
+    168, 35,201,170, 37,151,216, 32,130,242,242, 43,127,159,179,241,
+     68,148, 88, 46,230,255, 46, 80, 43,253, 31,  0,165, 77, 46,225,
+     17, 27,221, 39,  0,  0,  0,  0, 73, 69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_device_png[45511] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  1,149,  0,  0,  3, 34,  8,  2,  0,  0,  0, 41,105,201,
+    136,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0,  0,  7,116, 73, 77, 69,  7,216,
+      1, 30, 13,  5, 29,169,182, 54, 49,  0,  0, 32,  0, 73, 68, 65,
+     84,120,218,236,189, 89,112,100, 87,122,231,247,157,115,111,174,
+     88, 10, 40, 84, 21,139,100, 85,145, 44,146, 93,100, 55, 91,205,
+    110,182,122,139,222,212,148,186, 37,185,229,112, 43, 36,217,150,
+     34, 70,114,132,165,152,241,131, 61,178,159,198, 49,143, 19,158,
+    183,113,204,219,196,232,101,244, 48, 19, 99, 43, 28,154,113,104,
+    108,105, 70,106,141,130, 82,111, 86,179, 91, 18,155,108, 54,151,
+     34,107, 97, 85,161, 10, 64,  1,185,220,229, 44,159, 31,238,118,
+    238,205,  4,144,  0, 18,185,  0,255, 31, 73, 48,145,184,153,121,
+     51,145,249,195,255,251,238,185,231,136,183,223,185, 78,  0,140,
+      1, 30,121, 75,129, 23, 11,140,  5, 31, 47,193,137,209,  7, 51,
+     49, 91,230,170, 83,152, 89, 41,165,148,178,150,247,208, 13, 51,
+     71, 81,212,237,118,149, 82,  7,213, 20,239,103,177,122,189,190,
+    184,184,216,104, 52,132,216, 75, 94, 66,136, 90,173, 94,171,213,
+    164, 20,131, 63,147, 66,208,222,183,135, 58,225, 47, 48, 21,251,
+     88,203,204,169,110,180,209, 42,142,141,177, 68,100,173,  9,130,
+    160,215,235, 25, 99,146, 77,181,214,253, 94,175, 31,  4,198,232,
+     92, 26,177, 82, 58,140, 60,102,203,182, 98, 18,107,237,114,171,
+     85,243,188, 76,109,187,  6,165,154,239,175, 46, 44,202, 97,110,
+     28,118,113,168, 21,134,171, 76, 17, 95,239,245,148,209,187, 27,
+     36,217, 90, 40,163, 59, 81, 40,165, 28,212,151,149,162,214,108,
+    214,234,245,252, 74,207,243,218,237,246, 66,123,193,243,189,244,
+     13,237,251,237,118,187,213,106,121,158, 71, 68, 82,202,122,189,
+    238,251, 62,  9, 65, 68, 82,  8, 33, 68,114,249,224, 38,131,251,
+    102, 14,129,250,241,216,210, 80,138, 49, 38,142, 99,109,140, 53,
+     38,  8,130,126,175,175,141,214, 90,247,251,253,126,191,111,140,
+     49,198,132,253,126,141,200, 38,194, 98,219,170,213,219,245,  6,
+    103,  2, 88,104, 52, 23,155,205,226, 35, 78,180,208,108, 46, 54,
+     91,156,185, 68, 12, 81, 13, 15, 10,136,121,224, 35, 40,178,228,
+     54,112,181,171,172, 34,224,141, 94, 65, 86,247,131,247, 80,128,
+     16,130,119, 83,227,174,185, 46,189, 69, 47, 10,123, 81,228, 94,
+    223,139,162,228,154,228,193,  2, 21,135, 70, 11, 33,137,200,147,
+     82,123,178,217,110,123,158,151,184,175,221,110,251,190,159, 91,
+     79,122,158,239,121,245,122,221,243, 60, 33, 68,146,249,136, 68,
+    190,227,  2,178,131,191, 78,134,155,140,177,113, 28, 41,173, 85,
+     28,119, 58,157, 40,138,162, 40,218,217,217,137,227, 56, 12,  3,
+    161, 12, 89,107,217,122, 66,158,105,183, 83, 19, 53,155, 11,141,
+    102,230,160,214, 98,171, 85,124,104,139,255,115,102, 16, 30, 30,
+    130,202, 91, 14, 90,131,137,197, 96, 86,202,237,197,251, 60,185,
+    210,157,187, 15,199, 44, 70,214, 23,237,225,163,106,  9,200,123,
+    239, 18, 31,224,237, 44, 10,167, 11,226, 93,238, 34,255,206,117,
+     95, 47,138,250,113,122,185, 19,133,134, 88, 10, 73, 82,138,102,
+    189,217,108,213,235,245,229,229,229, 70,163,209,104, 52,150,150,
+    150,106,181,122,173,230,215,235,117,233,121,105,166, 27, 48,156,
+    128,224,224,175, 41, 26,202, 88,107,180,142,227, 56, 12,195, 78,
+    167,211,235,245,146,196,212,235,245,132,214,100,173, 49,230,220,
+    210,114,242,121,121,100,117, 53,185,221,197,213,179,197, 71, 87,
+    164,159,149,146,137,184,144, 78, 38, 32, 87, 67,188,107,118,201,
+    220, 52,236, 74, 55, 55,177,123,189,112,219, 99,195,101, 48,164,
+     17, 54,240, 19,222, 87, 74,213, 29,216,199, 51, 98,  4, 43,241,
+    208,102, 23, 15,127, 92, 49,228,233,141,214, 43,227,234, 19, 24,
+    242, 35,102,190,223,221, 73,174,186,223,237, 36, 27,110,  6,125,
+    207,243,132,231,137, 70,125, 97, 97,161,213,110, 47, 44, 44, 44,
+     44, 44, 44, 45, 45, 53,154,205,122,173,238,251,158,148, 94,214,
+    203, 19, 21,119,  9,216, 13,254, 58,154,164,200, 90,163,181, 14,
+    195,176,219,237,118,187,221, 78,167,211,235,118,183,183,183,235,
+     68, 74,233, 86,173,214,110, 52,137,248,145,213,179,196,188,216,
+    106, 45, 54, 91,236,124, 74,133, 83, 92, 17,231, 63,201, 63, 74,
+     69,213,198,196,185,167, 74,185,137,185,242,109,238, 29,102, 46,
+     55,158,184,106,146,129,107,134,110, 86,242, 14,243,222, 42, 24,
+    182, 25,237, 91, 63,138,189, 67, 20, 15,126,126,197,174,162, 97,
+    222,213, 59, 98,151,155, 15,181,149,112, 30,125,183, 31, 21,226,
+    203, 54,221, 93,121,188,223,149,189, 56,173, 91,239,119, 59,130,
+     40, 80, 42,178,218,247,107,166, 94, 59,115,230,204,162, 67,179,
+    217,244,253, 90, 18,221,134,239, 19,212,  6,127,185,111, 51,107,
+    173, 82, 42,241,212,195, 12, 82,138,141,109,214,106,237, 70, 99,
+    177,217, 90,104, 54, 27,181,218,217,165,229,236,131,202, 68,130,
+     28,137,184, 98, 74,138,169,212, 79,133,151,184,164, 39,215, 77,
+    204,101,211,229,183,229,146, 99,184, 26,202,220,123,168,214,140,
+     21,247, 81,249, 81,134, 74,118,223, 78, 22,243, 64,206,226,145,
+    162,215, 40,149,158, 99,135,146, 40,134, 89, 99,200,167, 88, 12,
+    223, 94, 36, 79,177,116,231, 98,247,199, 21,187,238,149,168, 62,
+     82,169,  8, 29,  8, 80,206, 37,167,106,173,100,232,236,133,121,
+     24,244,149,214,189, 56, 14, 84, 28,104, 21, 91, 35,253,154,215,
+    106,174,172,174,158, 57,115,102,101,101, 37,241, 90,173, 86,147,
+     82,150,119, 82,140,150,218,  4,252,117,114, 84,213,239,  7,157,
+    206,206,195,135, 15, 55, 54, 54, 58,219,219, 62,115,221,247, 23,
+     26,205,133,102,115,177,213, 58,187,180, 92,247,125,215, 11, 84,
+    169,188,242,203, 37,239,148, 37,149, 27,138,157,219,231, 55, 41,
+     93, 40,  4,151, 89,136,221,138,176, 48,102,122, 57,251,105,245,
+    178,107, 52, 46,212,199, 60,216,185,114, 36, 85,190,135, 61,234,
+    193,193,155,140,104,174, 81,219, 83,187,186, 67,236, 37, 50, 87,
+     49, 98,136,190,242,123,174,  4, 52,231, 74,225,124,250,179,221,
+    112,182,174,228, 32,177,187,  4,157, 99,151,156,109,195,196, 69,
+    142, 27,144, 32, 19,103,  7, 40,200,253,146,108,175,140,121, 24,
+    244,123,113, 20, 40, 21,106,165,152,185, 81, 95, 94, 89, 89, 91,
+     91, 59,115,102,101,121,121,169,221,110, 39, 82, 27,216, 35, 49,
+     66, 94, 19,240,215, 76,219,202, 24, 27, 69, 97,183,219,221,216,
+    216,216,220,220,220,220,220,240, 13,215, 61,175,221,104,156, 93,
+     90,174,215,252,139,171,107,121, 84, 17,206,199,184,106,168,178,
+    110, 50,215,148, 37,149, 93,200,111, 91,186, 80,140,127, 56,200,
+    173,156, 58,145,203,138, 44, 41,111, 80,100,217, 13,135,199,186,
+    129,166, 91,165,167,182, 71,147,107,247, 10,148,119,251,108,140,
+    244, 41, 17,229,207,218,208, 44, 86,232,108,160, 29,238, 72,167,
+    106,171,172,125,158,109,146, 29, 50,204,190, 21,149,252,149, 59,
+     72,228,251, 36,202,114,204,  4, 38,202,138, 20,101,163,186, 30,
+     20, 69, 95,111,184,139, 69,222,161, 19,110, 93, 91,241, 90,254,
+     18,223,239,117,149,214,219, 97, 16, 26,173,136, 68,163,190,178,
+    182,118,254,252,249,179,103,215,150,150, 22, 27,141,134,231,121,
+    238,179, 28, 89,106,  2,254,154,102, 91, 61, 86,170,215,237,222,
+    191,127,255,222,189,123,219, 91, 91,210,218,165,102,107,161,217,
+     60,187,188,188,182,180, 92,243,125, 81,201, 26,185, 92, 92, 73,
+     37, 67, 57, 19, 95,176,165,194, 27, 54, 21,141,107, 25,203,142,
+    206,242, 13,153, 42, 55, 73,175, 76, 15, 79,146,107, 52,203,185,
+    215,152,152,172,107,180,226,  2,115,177, 99,121, 76,203,246,194,
+    186,165,107, 85,115,236, 20,168, 21, 59,239,110,180,161, 86,114,
+    111,184,119,231, 75, 12,205,109,187, 21,133, 21, 49,149,  5, 82,
+    114,217, 96,  2,114,135,107, 13,202,168, 16, 77,201, 50, 34,137,
+     85,206,205,156,156,149, 95, 46, 76,148,222,133,112, 30,216, 29,
+     46,155, 63,174, 40, 30, 37,185,151,194,128,162, 16,100,250, 20,
+    132, 72,235,218,194,116, 92,186,156,121,173,168,241,139,  2,148,
+    216,141,211, 68,177, 49,219, 65,176, 29,  5,129, 86,129, 49,178,
+    213, 92, 89, 91,187,112,225,194,249,243, 23, 22, 23, 23,106,181,
+    122,113,160, 64, 12,171,117, 79,162,209,102,220, 95,204,204,113,
+     28,239,236,116,214,215,239,221,186,117, 75,245,251,237, 90,189,
+    221,104, 92, 60,187,118,118,121,185,238,215,200,173,188, 28, 53,
+    148,180,101, 45, 19,101,198,201,174, 79, 68,145,248, 34,253,202,
+    185,152,216, 50, 37,222, 98,231, 38,217,215,193, 11,213,159, 22,
+    119, 69,108,237,128,212, 50,  7, 13, 83,103,177,217,174, 25,205,
+    113, 49,187,250,226, 82, 95,172, 92,150, 14,145,218, 80,175,237,
+    114,229,208,177,102,163, 84,140,149,  2, 80,148,107, 49, 81,201,
+     47,174, 32, 92, 69,185,201, 72,148,243, 85, 58,122, 65, 20,121,
+     73, 56,242,202, 77,147,125, 83,186, 38,151, 86,225, 65, 81,186,
+     57, 13, 92,147,252, 39, 75,119, 91,185,183,178,245,178,125, 22,
+    206,147,116, 29,231,170, 37,201, 99,217,229,244, 53,207, 74,212,
+    138,209,152, 40,214,122, 59, 10, 55,250,189,200,152, 88, 80,251,
+    236,202,165,199, 47,157,191,240,200,242,242, 82,189, 94,207,  6,
+    175,237, 98,180, 19,161,179, 25,244, 23,107,173, 59,157,238,221,
+    187,119,110,220,184,145,  8,107,101,113,241,236,242,153, 71,207,
+    174, 13, 85, 85,242,201, 31, 34, 20,235, 58, 37, 75, 73,201, 56,
+    247,252,250,204, 86,204,214,221,158,152,147, 17,241,197, 64,212,
+    244, 91,170,108, 89,217, 44, 55, 87,246,136,214,121,136,170,230,
+    202,215,228, 10, 43,  5, 58, 87,199, 60,172,218,229, 97,141,185,
+    189,155,104, 60,228,176,230,240,107,152,246, 75, 89,251,246,185,
+     68,165,232,162,161,  1,106, 96,195, 82,149,151,121,161,148,170,
+    210,248, 51,196, 77, 85, 79,165,114, 41,162,147, 16, 85, 85, 57,
+      1, 45, 27,207, 37,164,115, 71, 66, 86,239,202,185, 67, 33,179,
+      8, 39, 68,105, 63, 51,205, 13, 23,101,245, 66,201,226, 36,170,
+      7, 37, 72,136,226,119, 34, 68,209,131,205,255,130, 17, 61,232,
+    247,118,162,176,171, 98, 37, 69,123,117,245,242,149, 43, 23, 47,
+     94, 92, 90, 90,242,125,223,121,225, 69, 53,242,206,173,206,102,
+    194, 95,204, 28,199,209,131,  7, 15,110,221,186,117,239,206,157,
+    166,244,206,180,219, 23,215,206, 61,186,182, 86, 42,178,114,103,
+    229, 95, 43,  2,226,236,202,196, 26, 92,182, 76, 98,174,212, 35,
+    169,131,138, 45, 45, 91,107,137,243,109,138,123,200,110,232,126,
+    101,103,203,236,202, 92,142,249,206,100,143, 80, 85, 94, 98, 40,
+    215,113,101,153,230,230, 74,246,161,200,137,142,197,156, 38,157,
+     83, 84, 38, 42,119, 43,202,234, 49, 80,183,215, 50, 36,142,141,
+     82, 99,238,253,182, 22, 67,251, 86, 67,107, 64, 81,237,181,187,
+    193,101, 72,245, 87,117, 83, 57, 34,185, 22,203, 21, 67, 36,164,
+     44, 46,184,186,145,194,181, 76,113,101, 73,112, 14, 50, 81, 88,
+    182,181,112, 12, 85, 92, 87,186,  9, 13,123,172,106,124, 19,162,
+    178,231, 78,137,234,244, 10, 69, 54,212,163,120,125, 57, 55, 90,
+    126,188, 60, 63,166,147,252, 34, 55,250,189,141,176, 31, 88,195,
+    173,230,197,199, 31,191,244,248,165,181,115,107,217, 89,168,135,
+    208,153,128,191,138, 15,135, 82,234,193,131,  7,215,223,125,247,
+    193,250,250, 98,189,241,200,217,179,143,158, 93, 91,108,181,137,
+    170, 29,116,102, 75,182, 28,118, 42,194,178, 54,119,  4, 91,155,
+     41, 35,221,192, 90, 75, 37,133,101,150, 73,111,101, 51,121,177,
+    181,150,172,181,169, 89,216, 26, 91,186,243,252,230,165,123, 96,
+    102,203,198,114, 69,118,217,157,184,254, 26,154,221, 92,  5, 23,
+    249,142,  6,116,198,165,230,218, 94,205,126,167, 85,230,182,224,
+    121,216,224, 47, 30,126,210,207,190,103,100, 31, 96,164,196,110,
+    209, 76,  8,177,107,139,166,170,179,129,246, 86, 94, 48, 58,230,
+     26, 76, 73, 37,161, 56,238, 16, 50,191, 94,230, 95, 40,243,148,
+    227, 44, 33,132, 36,231,178,115,129,132,144,233, 13,147,155, 12,
+    220, 48,125,208,202,198,249,163, 16,145,115, 19, 26,178,219, 89,
+     53,234, 22,167,187, 27,141,179,226,147,139, 97,124,156, 29,254,
+    225,158,138, 55,195,224, 97, 28,233,122,237,194,165,199,159,122,
+    234,169,115,231,206,213,106,181,188, 39,184,107,177, 57,195, 46,
+    155,176,191,216, 90,238,245,186,215,175, 95,127,255,189,247,234,
+     36, 46,157, 63,255,216,185,243,201,105, 52,121,  7,138, 42,129,
+    101,184,116,152,172, 45,164,195,108, 19,173,152,138, 98,172, 53,
+    214,241,148,181, 54,251,169,205,175,201, 54, 54,229,111,243,203,
+    249, 77, 56,191, 55, 46,174,103,107,179,157, 25,180,103,113,185,
+    146,212,156,203,238,177,203,129, 60, 53, 80,250,237,209,204, 58,
+    160,105,120,215,111, 70,127,251, 28,250,237, 44, 14,252,131,  1,
+    235, 85, 71, 19,184,253, 44, 55,232,229, 81,173,154,215,100,166,
+    188, 84, 53,101,  1,201,226,130,116,180, 85,124, 21, 50,255, 81,
+    105,179, 98, 99, 41,203,130, 19, 66,122, 50,243,154, 99,183,236,
+    135,249,110, 16,145, 24, 80, 91,226, 95,114, 52, 77,197,209,135,
+    188, 77, 86,209, 89,250,215, 45, 63, 32,149,188,223,122, 42,222,
+    140,194,205, 56,146, 75, 11, 79, 94,125,250,169,171, 79, 45, 44,
+     44, 56,227,206,118,143,102, 51,230,178,201,248,139,173,181, 91,
+     91, 91,111,188,241,198,195,  7, 15, 86,218,237,103, 47, 63,177,
+    182,188, 92, 77, 19,238, 71,189, 82,178, 25, 91,146, 84, 69, 61,
+    201,101,227, 92,105, 74, 63,181,198, 36,247,105,173,101, 99,202,
+    215, 23,146, 42,127,107,115, 33,218,129,135,179, 21,169, 89,155,
+    164,188,129,  2,147,220, 10,177, 84,241,177,155,154, 70, 27, 89,
+    186,183,128, 14,245,123, 57,202,143,197, 17, 36,117,244,219,236,
+     54, 80,108,143, 59, 25,140,126,110,172, 27,108,135,185, 53,160,
+    168, 90, 70, 74, 47,243,139, 39, 11, 85,201,221, 47, 75, 33,165,
+     20, 66, 14, 94,206,197, 39,115,247,165, 63,173,154,148,156,244,
+     71,133,127,243, 98, 83,148,189,150,232, 76, 20,127,  6,201,105,
+    177, 80,250, 22,222, 81,209,221,160, 23,249,222,133, 39, 46, 63,
+    247,220,243,171,171,171,201,212, 29,123,230,178,221,106, 76,113,
+    162,252,101,173,125,248,112,235,245,215, 95,223, 92, 95,191,114,
+    225,145, 39, 31,125,116,177,213, 46, 26, 58,110,255,200,173,209,
+     74,202, 48,105,170,178,156, 94,206,124,148,234, 38,253, 90,136,
+     41,149, 84,118,125,113,193, 14,217,222, 26,147, 61,156,177, 38,
+    123,196,108,  7,108,217,131,105,254, 42,165,194,162, 75,197,165,
+    145, 16,187,142,255,228,145,181,194, 71,212, 18,211,172, 35, 14,
+    255,150,223, 55,249,137,145,196, 39,118,219,184,146,242,132,219,
+    128,207,186,105, 78, 85, 88,216, 45,245,154,148,169,143,178,175,
+     66, 10, 41,189,226, 74, 79, 58,194,114,190, 77,172,231,201,146,
+    212,156, 12, 40,165,164, 97,106,171, 30, 88, 16, 84,209, 89,114,
+    122, 91,222, 75,206, 44,150,254,229,181,108,251, 74, 61,136,195,
+    109,193,143, 60,245,228,135, 63,252,225,149,149,149, 60,145, 13,
+    186,108,191, 80, 38,230,216, 95, 76, 28,  6,193,155,111,190,121,
+    253,157,119,174, 62,114,241,201,199, 30, 91,108,181,203,206,114,
+    242, 75,102,156, 60, 58,185, 57, 40,245,139, 49,169, 86, 82, 49,
+    177, 53, 58,117, 80,162, 45, 99,172,179,153,209,154,141,181,214,
+     88, 99,178,205,210,111,243, 59,201,228,149, 92, 40,197,177,172,
+    243,101,171, 45,170,193,154,142,246,152,247,133, 71,116,  8, 31,
+    202, 53,135,171, 25,231,232,200,248,161,243,155, 24, 73,124, 98,
+    223, 15, 92,165,133, 39,134,  9,206, 61,168, 90,  4,185,212,108,
+    110, 33,233,196, 49,175,240,154,244, 50,163,121, 82, 74,207,181,
+    152,244, 60, 87,127,123,  8, 46, 79,115,148, 94, 40,106,210,172,
+    137, 38,243,  3,184,204,197, 16,179,124, 88, 99,214,196, 77,255,
+    208,247,181,218, 80,209,118,205,123,230, 35, 31,190,118,237, 90,
+    171,213,114,250,253, 21,149, 85, 67,153, 16, 19, 21,217,152,253,
+    197,108, 55, 55, 55,255,250,123,223,171,147,120,225,233,167,215,
+    206,172, 84, 14,225, 85,138,178,212, 32,198,186,122, 42, 89, 38,
+    189,172,109,241,213, 88,157,125,171,141,181,198,106, 51,240,173,
+    177, 38,187,144, 95, 83,122,184,129, 10,212, 61,  2,232,182,198,
+     75,126, 25, 41,217,240,168, 17,139,199,250, 55,227,164, 72,107,
+    172,247, 39,142,176,145,216,175, 44,221, 67,112,238,103,221,237,
+    194,201,244, 24,130,148, 78,225,153, 75, 45,215, 89,122,193,243,
+    164, 87,254,182,162,182,228,130, 87, 14,122,110, 73, 43,  4,101,
+    241,144,178, 35,176,238, 65,128,124, 16, 70,214,181,205,106, 21,
+    107,181, 49, 93,163,239,153,184,241,200,249,159,254,236,103,207,
+    158, 61, 91,137, 99,101,147,149, 69, 54,169, 68, 54, 54,127, 89,
+    107,110,221,186,245, 55,175,190,122,113,245,236, 79, 61,243,108,
+    221,247,147,126,188, 91,  9,218,193, 36,101,140,209,169,107, 56,
+     19,141,209,186,176, 79, 98, 37, 99,172,214, 70, 39,230,202,182,
+    209,218,106, 99, 76,118,165,123,219,226,223,114, 93,233, 30, 58,
+     28,108, 69,237,217,126,218, 55, 37, 29, 78, 73, 39, 53, 62,205,
+    161,  7,247,158,217,122, 88, 64,219, 83,112,165, 22,248, 64,106,
+     43,186,242,233,144,142, 82, 11, 44, 79, 91,137,161,164,231,101,
+    255,166, 49, 77,122,158,148,158,244,188, 92,124,153,203,188,193,
+    188,150, 29, 70,112,142, 84,  8, 73,162, 24, 69,156,157, 36,146,
+    156,  0, 98,211,201,163,108, 50,183,166,137,140,185,103, 84,124,
+    102,241,165,207,125,246,210,165,203,217,180, 25,  3,199, 75,134,
+     38,178,221, 78,111,154, 29,127, 49,219,251,247,239,127,231,175,
+    254,234,218,229, 43,215,158,120,210, 29, 28,144,215,101, 73,  8,
+     50,131,162, 73, 28, 84,185,172,181,213,218,100,194, 50,217,183,
+    182,122, 77,106, 46,199, 89, 78, 69, 89, 58, 26,152, 31,120, 33,
+    103,  6,  7, 30, 93, 79, 60,178,103,224,163,147,237, 59,177,235,
+     20,178,123, 53,247,134,122,109, 31,169,149, 15, 89, 74, 41,  7,
+    228, 37,165,231, 73,223,247,188,220, 95,158,244,189,220,101,137,
+    239,  6, 75, 81,183,176,117,206, 16,160,188, 45,146, 30,147,226,
+     36, 93, 24,109,180,214,198, 24,179, 73,166,187,178,244,249,159,
+    125,249,194,249, 11, 89,251,207, 21,217,224,209,223, 99,183,216,
+     17,253,197, 65, 16,188,242, 23,127,113,182,213,254,228, 71, 94,
+    200, 14, 35,166,135,231,172, 49,108,147, 23, 32,247,145, 54,170,
+    226, 35,109,148,182,233,245,202, 42, 99,180,114,174,209, 70,169,
+    210,246,201,205,147, 56,150, 23,131, 69,191,204, 29, 17,234,230,
+     41,222,187,178, 27, 49, 58,193, 77,112,220, 33,236, 54,108,242,
+    177,170,215,134, 74, 45,183,129, 59,170, 67, 10, 33, 60, 79, 10,
+     81,142,102, 82,122,158,231,121,210,247,165, 39, 61,207,151, 94,
+    234, 50, 47,151, 90, 97, 52,175,148,203,178,222,127,158, 20,211,
+    166, 88,122,  4, 43,249,  4, 27,173,149,210,218, 24,179,238,139,
+    198,213, 43, 95,252,202, 87, 90,173,118, 90, 14,151,162, 36, 13,
+    251, 58,180, 59, 38,166,233, 47,107,237, 27,111,188,254,193,245,
+    235,159,249,232,199,150,218,109,119,152,123,210,141, 50, 73,158,
+     82,202,104,173,149, 50, 42,249,154,252,171,139,175, 90,155, 56,
+     46,174,209,233,  6,201,198, 73, 34, 51, 74, 23,133,100,126,108,
+    177, 24,169,192, 92, 58,213,153, 15, 45, 41, 24, 10, 28,143,221,
+    196,158,199,  7,246,144, 90,213,104,162, 66,214, 64,171, 84,154,
+    158,231, 75, 79,122,190, 47,253,228,178,231,249,158,244,156,176,
+    230,121, 89, 91, 77,150,134,170,101,  3, 47,138,118,152, 49,198,
+     24,173,181,210, 90, 41,165,180,138,152, 31, 44, 53, 95,248,242,
+     23,175, 61,247,188,231, 57,227,212,168,116,226,131,107,177,210,
+    137,251,227,179,216,225,252,197,113, 28,255,233,127,252,143,207,
+     95,186,242,212,227,143,167, 51,249,185,230, 82,218,106,173,149,
+     54, 42,214, 74,233, 56,249, 55,185, 28,155,228,114,172,140,138,
+     77,172,180, 82, 38, 86, 90,197, 70,105, 19, 43,163, 84, 17,193,
+    116,214,146, 55,206,192,174, 52, 91,101,211,212, 12,158,125,188,
+    159,136, 32, 41, 48, 35,106, 27,172,171,196,176,164, 38,134,101,
+    180,161, 58,115,143,108,230,170,242, 60,223,243,147, 74,211,243,
+    124, 63, 19, 89, 22,208, 60, 95,122,178,200,101,249, 64,217,196,
+     98,201,216,162,180,142, 74, 21, 22, 43, 21, 43,213,175,251,244,
+    252,211, 63,247,139,191,152,157, 43, 94, 29,111,187, 71,105, 57,
+    174,138,242, 16,254,226,173,173,173, 87,254,252,207,191,252,241,
+    151,150, 22, 22,136, 40, 29,119,154, 55,182,148, 74,157, 21,197,
+     58,142, 84, 28,235, 40,214,113,172,162, 88, 69, 81,114, 65,199,
+    177,137,227, 68,106, 38,142,203,129, 43,105,105, 25,174,180,177,
+    156,105,101,170, 11, 88,192, 83,224,228, 74, 77,136,225, 58,171,
+      4,180,138,206,210,227,  0,206, 65,  0,207, 79, 83,152,231,251,
+    137,209, 60,223,175,232,204,243, 60,247, 56,102, 50,  7, 70,242,
+      1, 52,214,106,163,181,210, 74,235, 88,197,113,172, 98, 21,199,
+     68,219, 79, 60,250,213, 95,249,229,124,176,216, 32, 35, 91, 76,
+     28,187,191,152,248,198,251,239, 95,127,253,141,207,124,244,167,
+    234,181,154,200, 78, 69, 76, 14, 20, 38,  1, 74,199,177,142, 99,
+     21, 70, 42, 10, 85, 24,199, 81,168,194, 72, 69, 81,250, 53,138,
+     18,139,233, 40, 54, 74,101,129, 75, 27,109,138, 33, 93,110, 39,
+    139,120,248, 28, 47, 80, 21, 56, 53, 82,115, 63,230, 98, 96,162,
+    124, 55,157,  9, 18,123,187,204, 75, 19, 89, 26,196, 82,151,213,
+    252,204,101,190,231,229,135,  8,100,118,154,122, 58, 94,219, 24,
+    109,140,209, 74,199, 74,197, 42,142,149,138,162, 40,102,219,121,
+    100,237, 51,191,250,141,203,151, 47,167,227,220,134,121,108,224,
+    112,229,174,167,129, 29,151,191,152,237,107,175,189,182,125,231,
+    238,231,126,234, 99,148,159,172,103, 44, 91,155,116,181,116, 20,
+    167,158, 10,195, 56,  8,227, 48,140,131, 64,133, 81, 28,134,113,
+     24,230,254, 74,  4,151, 52,188, 74, 45,173, 98, 10,  7, 34,226,
+    253, 58, 89,  0,156, 82,163, 13,213,217, 96,177, 57,232,178,236,
+    196, 39, 41, 18,145, 73,233,101, 69,165, 95,243, 61,207,247, 19,
+    145, 37,245,166, 87,196,177,188,150, 76,122, 97, 74,171, 56, 86,
+    113, 28, 71,113, 28,169, 56,138,227,238, 99,231, 63,250, 75,191,
+    240,252,135, 63,236, 73, 47, 59,111, 93, 10, 57, 74, 28, 59, 82,
+     16, 27,213, 95,204,246,123,223,251,222, 50,211,135,174, 60,145,
+     62, 66, 50, 74, 36, 57, 38,168,148, 10, 67, 21, 69,113, 16,196,
+    253, 48,238,247,163,126, 16,  5, 65, 28,  4,137,200, 84, 24,170,
+     40, 86,113,164,227,172,127,111, 12, 59,227,221, 43, 67, 70,161,
+     42,  0, 14,173, 51,215,101,110,247, 92,150,102,199, 72, 90,101,
+    194,203,154,250,190,239,123,190,159,126,117, 69,150, 40, 44,233,
+    235,103, 41, 76,169,204, 95,113, 28,197,113, 24, 71,209,217,149,
+    167,190,241,139,159,120,233,165,100,233,223, 52,244,185,231,141,
+     86,210,216,240,  6,255,193, 20,230,143, 38, 47,126,237,181,215,
+    150, 12, 63,115,229, 74,209,173, 55, 54, 25,168,165,163, 56, 14,
+     66, 21,  4, 81, 16, 68,189,126,212,235, 71,253, 94,212, 15,162,
+     94, 63, 14,195, 56,  8,211,216,165,148,206,142, 39,186,141,173,
+    161,163,177,  0,  0,251, 86, 30,217, 50, 34,238,100, 71,217,105,
+    219,196,197,100,174,156,207,179,207,130, 51,145, 25,147, 72, 70,
+    107,157,100, 49,157,212,150,169,196, 10,151,165, 69,101, 62,248,
+    158,173,176,236, 49,249, 36, 44,  9,195,100,152,124, 38,117,103,
+    253,237, 63,248,247,126,173,246,209, 23, 94,144,158,199,150,133,
+     20, 44, 89,112,114,211,100, 61, 96,145, 75, 53, 23, 46, 39,203,
+    155, 48,113,145,200,120, 68,133,249, 35,188,100,124,227,198,251,
+     15,111,127,240,233, 15,127,132,242,179,215,147,110,189, 82, 42,
+    138, 84, 16,196,253, 32,234,245,194,110, 47,236,246,194, 94, 47,
+    234,245,163, 32,136,250,129, 10,195, 36,104, 22,227,182,  6,  3,
+     23,  0,224,200, 58,219,195,101,233, 44,135, 68,194, 21, 25,  9,
+    193,198,178, 16, 66, 88,107,165,144, 70,106, 41,165,167, 61,157,
+    228,174, 76,100,190,163,176,164,150, 20,150,133,101,201,228, 17,
+    249, 66, 24, 34,159,201,183,172,111,221,125,237,255,252,119, 11,
+     11, 11, 79, 62,249,164,148, 82,178,100,102, 41,153, 89, 72, 41,
+     89,144,116, 39, 99,115,151,124, 74,213,197,204,  7, 83,216,190,
+    254,226,173,205,205,183,255,246,239, 62,255,209,159,202,229, 69,
+    214, 90,173, 77,172, 84, 24,170,126, 16,245,131,176,211, 13,187,
+    221,176,219, 13, 58,221,168,223,143,250, 65, 28,132,113, 20,169,
+     56, 50, 74,235,108,124,124,113,226,206,  1, 39,138,  1,  0, 28,
+    218,101,204,105,113,198,196,105,129,153,137, 76, 16, 11, 22, 44,
+    132, 37, 43, 88, 74,107,211,  1, 95,210,120, 90, 26,207, 55,190,
+    103,124,223,243, 18,139,121, 73,123,139,136,200, 90,193, 86, 50,
+    121, 76, 73, 22,171,145,208, 66,154,235, 55,191,245,111,255, 96,
+    233,191,255,239, 86,207,158,245,164,199,146, 89,178,148,169,200,
+    178, 18, 86, 74,105,157,234,150,184,112,216,193, 20,230,253,143,
+    255,211, 63,220,227, 53,137,227,248, 79,255,159,255,247, 43, 47,
+    126, 60, 61, 93,221, 90, 50,198, 42,109,162, 88,  7, 97,220,235,
+     71,157,110,184,179, 19,108,239,  4,219,219,253,237, 78,208,233,
+    132,157,110,212,235,199, 65, 16,135,161,138, 99,163, 84,118,122,
+     35,115,101,185, 86, 48,153,119, 54,179, 37, 50,217,192, 57, 49,
+    116, 29,198,108, 54,  2,195,108,152,147,191,221, 98,151, 51,  2,
+     45,167,155,101,127,132,134,111,152, 28, 66,206,142,203,236,241,
+    184, 35,237, 30, 24, 83,203, 76, 56,225, 44, 17, 71,218,117,206,
+    150, 96,160,129,  9, 23,210,137,249, 40,155, 51,134,146,182,143,
+     97, 74,102,142, 49,150,181,177,198, 48,145,101,226,245,141,183,
+     59, 15,159,254,200,135,165, 20,233,212,137,229, 79,124,117, 73,
+     60, 42, 45,102, 55,228,108,240, 67,229, 47,182,214,254,167, 63,
+    249,147,207, 61,247, 97,107,173, 76, 15,162,178,213,218,102,201,
+     43,236,246,194, 78, 39,216,233,  4,157, 78,208,233,134,189, 94,
+    212, 79,181,165,149, 74, 99,151,205,231,231,152,155,105,169, 78,
+    144,185,200,176, 13,140,238, 25, 21, 26, 67, 68, 13,207, 91,244,
+    106, 45,207,247,202, 11,145, 25,230,200,152,174, 86,129,213,150,
+    185, 38,229,130, 87,107,123,126,178, 24,180,235,154,200,218,158,
+     86,125,163, 52,179, 39, 68,219,243, 23,252, 90, 93,122, 94,121,
+     51,197,182,175,117,207,168,216, 90, 73,212,244,252,  5,191,214,
+    148,222,224,227,  6, 70,119,181, 10,141, 38,162,134,244, 22,252,
+     90,203,243,124, 33, 97,177,241,230,178,106,129,201, 34, 47, 45,
+    147, 56, 38,  4, 49,147, 48,134,165,100, 33, 44,179, 21, 50,153,
+     25,207, 26,207,250,214, 26,227, 39,  3, 47,146,  9, 45, 44,147,
+    101, 73,228, 17,121, 68,190,148, 62,179, 47,217,176, 53,175,190,
+    246,103, 23,254,248,231,126,225, 23,253, 26,123, 82,178,244,216,
+    115, 22,173,145, 44, 73, 74, 65,182, 88,125, 32, 95, 49, 47,111,
+    135,  9, 22,249, 76, 63,226,160,254, 98, 38,122,227,141, 55,174,
+    174,174,181, 27,117, 50,217,250, 99,198,152, 88,153, 40, 82,253,
+    126,212,233,133,157, 78,176,189,147,248, 43,236,245,195, 36,115,
+     69,177,214,202, 24, 99,141, 53,214,112,121, 73,  8,200,107,146,
+     24,182,219, 42,190, 19,246, 54,226,112, 51,142, 60, 33, 26,158,
+    119,181,189,124,174,209, 92,244,235,126,246,  7,208, 50,247,180,
+    186, 23,246,215,163, 96, 83,133,150,185, 38,189, 71,155,237, 71,
+    155, 11, 43,181,122, 67,122,233, 42,211,204,161, 53,247,163,224,
+     78,208,219, 80, 81,108,141, 47,228, 90,189,113,185,181,180, 90,
+    111,180, 60, 63, 55,157, 98,187, 25,135, 31,  4,253,141, 56,232,
+    106,237,  9,177, 84,171, 61,209, 90, 90,107,180, 22, 60,223,203,
+     30,215, 48,239,168,248,110,216,187, 31,  5,155, 42,146, 36, 26,
+    158,247, 68,123,233, 92,189,185, 92,171,215,  8, 10, 59, 46,145,
+     57,139,184,164,245,100,201, 98,214,178, 16, 44,  4, 39,195, 87,
+     61,201,238,217,129,214,114, 58, 82,159, 18,133,  9,102,143,200,
+     19,210,151,214,103,161,133,148, 97,188,253,202,119,223,188,122,
+    245, 67,215,174, 89,207,243, 60,246, 88,178,231,177, 27,197,  4,
+    137,108, 77, 21, 34,153,239,154,171, 48,218,175,144,244,119,123,
+    166, 65,191,255,254, 27, 63,254,252,135, 95, 32, 99, 57,153,247,
+     44, 57,107, 58,140,226,126, 16,117,187,225, 78, 42,175,126,167,
+    147, 37,175, 40,142, 99,173,149,209,198,102,230,130,188,166, 88,
+     54,134,214,220,139,250,215,251, 59, 55,131, 94,100, 77,114,253,
+    205,160,251,213, 11,151,155, 73,  4, 35, 65, 68,202,218,205, 56,
+    188, 25,116,223,233,237,116,141, 74, 54,123,167,183,243,169,213,
+     11,139,203,103,107, 68,146, 73,  8, 50,204,219, 42,250, 32,232,
+    253,164,247,112, 35,142,146,205,222,234,209, 67, 21,127,246,236,
+    197,186, 76, 75,206,196,134,119,195,254,187,189,237,219, 81,223,
+    102,111,128, 59, 97,255,203,231, 30,111,121,158, 71,156,124, 98,
+     34,163,215,163,254,205,160,251,118,111,199,221,189,207,157,189,
+    184,224,215,124, 65,176,215,241,137,172, 20,199,  6, 44,150,188,
+    127, 88,202,236,  4,227,108,206, 97,203,108, 45,121, 54, 57,  5,
+    156,152,  5,179, 96,146, 68,158, 16,158,144,190, 96, 95, 90,205,
+     66,221,223,250,193, 31,255,167,199, 30,123,108, 97, 97,145, 45,
+    179, 39, 37,179,231,121,196,204,228,229, 41,140,136,164,144,148,
+    182,195,100, 69, 97,251,246,194,228,208,103,103, 45,127,243, 79,
+    255,244,167,159,126,150,141, 33,107,217,104,214,218,196,202,132,
+    145,234,  7,113,175, 23,117,186, 97,167, 27,118, 58, 97,167, 19,
+    117,123, 73,195, 94,197,145, 78,166,139,176, 38, 25, 32, 65,144,
+    215,244,176,196, 61,173, 58, 42,190, 17,116,115, 59, 16, 81, 71,
+    171,255,252,224,131,154, 40, 10,195,200,154,109, 21,223, 14,123,
+    185,188,136, 72,179,253,235,135,235,145, 53, 50,139, 75,138,237,
+    142, 82,247,162,126, 46,175,132, 31,119, 31,222,137,250,126, 86,
+     24,106,182, 93,173, 54,227,232,  3, 71, 94, 68,116, 47, 10, 94,
+    239,108,122, 89, 97,200,196,129, 49, 59, 42,126,199,145, 87,178,
+    123,127,181,121,183, 38,164, 32,129, 99, 60,199,106,177, 98,122,
+    115,202, 87,106, 78, 47, 91,102, 75,156,204,185,110,172,213,214,
+    170, 98, 46,157,164, 57,164,141,214, 73, 34, 19,196,201, 34, 75,
+     94,250,175, 76, 46,200, 55,222,121,229,155,223, 76, 50, 77,178,
+    189, 78,102,226, 73,166,237, 75,  7, 35,216,124,233,136,172,209,
+     84, 44,103, 89, 89,170,116, 20,127, 17, 17,221,189,123,231,188,
+     95,247,132, 32,203,172, 13, 43, 99, 99,101,227, 88,  7,161, 10,
+    130,184,219,139, 58,221,112,167, 19,238,116,195, 94, 63, 31, 39,
+    161,149, 74,167, 72,205, 23,251, 26,120,165,192,  4,139, 71, 86,
+    214,238,104, 21, 91, 91,249,209, 70, 28,238,  8,235,249,126,242,
+      6, 73,122,231, 15,226,176,178, 89,108,237,219, 65,167,214,168,
+     39,198,177,233,102,209,224, 99,253,168,187, 85,111, 54,211,129,
+    218, 76,134,121, 83, 69,102, 64, 63,127,183,179,217,168,215,165,
+    231, 37,159, 25,205,182,111,116,232,200, 43,161,171,213,186,137,
+    189,154,143,242,113,194, 22,203, 39,243, 76,215,122, 38, 78,142,
+    213, 20, 34, 75,228, 99,141,214,201, 60,162, 38, 25, 14, 37,  4,
+     73, 18,146, 74, 10, 35, 99,182,190,255,183,119,239,222, 81,201,
+    180, 21,249, 36, 90,153,192,114,139,241, 16,133, 13, 91, 90,121,
+     63,127, 49, 17, 25, 99,191,243,151,127,249,204, 35, 23,147,163,
+    141,108, 12,107,109,226, 88,135,145, 10,130,184,215,143,186,189,
+    100,192, 68,212,239,199,253, 64, 69,145,202,202, 70, 99,109,101,
+    158, 64,152,107, 90, 72, 18, 53, 41,  3,171,135,219,205,247,252,
+    122, 45,155, 29, 93,216, 93,126, 81, 94,173,230, 55, 82,227, 72,
+     33,106, 82,122,195,164, 34,165, 87,107, 54,164,239, 39, 45,216,
+    154,148, 53, 57,252, 79,227, 29,171,252, 90, 45, 89,147,218, 43,
+    141,  2, 42,241,129, 10,235,141,134,220,229, 78,192, 49, 89,172,
+     48, 87, 42,175,116, 22,157, 68, 97, 58, 61,207,185, 80, 88, 62,
+    171,113,242,123, 79, 78,152,244, 10,139,  9,186,125,239,187,255,
+    249, 47,162, 40, 82, 90,105,149,228, 54,157, 26, 76,167, 45,242,
+      1,133, 13,134,176, 93, 87,123,144, 21,121, 49,209,173, 91, 55,
+     47, 47, 46, 91, 99, 41, 57,155, 90,235, 52,124,133,161, 10,194,
+    184,219,139,186,189,168,215,143,123,253, 56,200,228,165,180, 53,
+    198, 50,228, 53, 67,120, 66, 46,250,181,203,173,197,225,141,207,
+     90, 93,250,105,205,215,144,242,145, 70,123,248,157,248,190, 95,
+    171, 37,135,182,125, 33,151,252,225,161, 72, 72,233,213,211,152,
+    230,  9,185,224,213, 26,210, 27,122,135,207,158,127, 68,250, 30,
+      9,146, 68, 45,207,111,123,195,143, 32, 61,115,238,130, 87,175,
+    145, 68,  0,155,116, 95,140,156, 37,253,152,210,225, 20, 54, 11,
+    233,198,178,225,108,250,119,107,147,252,197,249,192, 23, 74, 21,
+    150,254, 75,194, 19, 34,250,241,219,183,111,223, 86,177, 82, 42,
+    171, 60,181,214, 38,115, 88,118,164,207, 81,152, 45,  2,152,179,
+    234,233,208, 42,178,250,247,205, 26,251,215,223,254,206,149,149,
+    179,233,200, 14,109, 56,153,198, 43,138,117, 24,169,126, 63,238,
+      7,113,175, 23,247,251,113, 16,170, 48, 82,113,108,180, 54, 69,
+    240, 98,198,201, 64,179,129, 16,212,242,252,103, 23, 87,150,252,
+     90,229, 71,143, 46,157,121,250,220,133,252,247, 84,151,222,106,
+    189,241,161,197,149,202,102, 77,191,246,201, 75, 79,230, 17,222,
+     23,242, 76,173,241,226,202,185,193,199,250,252,147, 79, 39,237,
+     86, 34,242,132, 88,244,107, 31, 59,179, 86, 31, 80,216, 75,143,
+     93, 17,158, 76,198, 22,  9, 33,154,158,127,109,105,117,173,222,
+    172,108,182,218,106, 63,189,118, 33, 89, 52, 15,191,199,153, 80,
+     88,210, 17, 99, 54,233, 74, 22, 54, 93, 26,167, 88, 89, 62,121,
+    203, 37,107, 31,  9, 73, 34, 91,167,151,104,125,227,  7,223,250,
+    118, 28, 71,233,228,135, 74,107,173, 76,230,176,244,140,112, 71,
+     97, 92, 14, 97, 89, 25, 57,124,192,123, 62,126, 53,141, 75, 31,
+    124,112, 91, 60,216, 90,110,182,  4,179,176, 76,214,178,214, 54,
+    138,211,206,125,183, 31,118,187, 97,183, 23,246,250, 81, 16,196,
+    201, 56, 47,227, 62,178, 51,234, 23,111,135,233, 43, 76, 52,164,
+    255,196,210,202,123,253, 78,100,210, 66,242,177,229,149, 95,251,
+    169, 79, 54,153,162,126,223, 40,149,111,246,228,226,153,187,113,
+    184,157,181,183,154,181,218,127,253,177, 79, 62,214, 94,140,186,
+     61, 29,199,105, 97,232,121,143,180, 22,164,231,221,236,117,242,
+     71,249,194, 83,207,126,238,210,147, 81,175,175,194, 40, 85,152,
+    148, 75,181,250,249,246,226,123,189, 29,205,105,247,237,233,181,
+    243,191,242,209, 79,112, 24,199,253,192, 90,155, 20,164, 13,207,
+    191,180,184,124, 55,238,247, 84,122,232, 96,181,189,240,247, 94,
+    250,108,147, 41,236,245,140, 82,248, 37, 78,231,157,147,126,117,
+    230, 76, 21,165, 63,141,206,114, 29,233,117,201,231,221, 50,167,
+    181,103, 54, 22,218, 18,199, 65,112,230,233, 39,219,237,118,101,
+    122,198,236,159, 98,225,224,252,254,203,139, 13, 87,150,  8, 41,
+     30, 54,159,127, 34, 61,236,248,127,255,187, 63,252,233,179, 23,
+    132, 16, 30,  9,201, 44,172,101,165, 77, 20,171, 32,136, 58,221,
+     96,167,211,127,184,221,223,217,  9,186,189, 40,  8,162, 40, 82,
+     74,233, 34, 68, 22,254,130,188,102,165, 11,230,121,126,189, 86,
+    111,181,110, 71,193,142, 81,107, 11,139, 79,173,174,197, 65, 16,
+    236,116,226, 94,223,102,173,125, 33,165, 95,171,213,154,205, 77,
+    214,247,163, 96,109,113,233,209,165, 51,190,181,193, 78, 39,234,
+    246,140,214,185, 16,165,239,213, 26,205,192, 19, 15, 84, 36,125,
+    239,177,229,149,229, 90, 61,236,116,131,157, 78,162,185,228,157,
+     41, 60, 89,107, 52,108,163,182,161,227, 29, 21, 63,190,178,122,
+    113, 97, 41,234,245,251, 59, 59, 42,  8,243, 63,166,210,243,252,
+     90,173,222,106,221,142,251, 59, 70,175, 45, 44, 94, 93, 61, 23,
+      7, 65,176,179, 19, 57,187,  7,166,171, 48,145,157,186,232,101,
+    199, 25,125, 41, 61, 33,124,233,249, 66,120,153, 88, 44,179,102,
+    171,172,141,172,  9,141,  9,141,  9,173, 14,141,137,172,105,253,
+    204,231,190,244,179, 47,215,106, 53,223,247,125, 63,253,127, 54,
+    229, 69, 58, 67,172, 39,179,127,210, 89,100,221, 89, 43,  6,166,
+    220, 73,218, 32,110,248,234,247,123,173, 72, 25, 99,124,145, 78,
+     77,200,198,218, 88,153, 40, 54, 97,164,195, 72, 39, 51,225,132,
+     81, 50, 25,180,209, 38, 63,191,  0,242,154,209,138,192, 90, 29,
+     43, 34,122,180,209,184,220, 94, 36, 33,186, 27,155, 42,  8,117,
+     28,187,118, 72, 38,113, 35,162,213, 70,253,252,210,170,144,210,
+    236,116,194, 40,210, 81,156,203, 43,  9,241, 86, 27, 77, 81,171,
+    209,120,170,181, 40, 61,143,195,184,187,221, 85, 81,228,102, 37,
+     38,174, 62, 35,195,  0,  0, 32,  0, 73, 68, 65, 84, 38,107,117,
+     28,251, 68,143, 54, 26,151,154, 11,100,168,243, 96, 83,133,161,
+     86,202,173,  4,172,177,154,146,221,107, 94,110,215, 72,136,206,
+    198, 70,242,  6,131,188,166, 94, 75,138,236, 76,239,100,230, 10,
+     98,178,130,  4,145, 37, 50,108,133,240, 44, 91, 75, 50, 27,126,
+     90,204, 60,150, 86,145, 73,168, 34, 18, 68, 59,111,191,219,253,
+    244,167, 22,151,150,242, 37,117,138, 97,104,162,136,122,121, 36,
+    147, 76,204,146,178, 19, 54,147,193,105, 34,249, 82,236,157, 59,
+    126,149,249,205, 55,223,124,116, 97,137,141, 97, 33,153,217, 26,
+     75,218,216, 56, 54,113,164,163, 72, 71,145, 10, 28,121, 37,171,
+    162, 57, 29,123,136,107, 22,223,133,204,108, 76, 28, 88, 21,197,
+    201,225,188,228,136,209,224,150,214, 90, 27, 69, 90, 41, 33,250,
+     66,136,100,  9,173,193,223, 41, 51, 39,163,252,226, 32, 72,122,
+     94,156,117,112, 43,155, 37, 51,142,168, 48, 20,178, 24,192, 61,
+     68,176,198,196,198,170,120,159,221,  3, 83, 84, 88,113,214, 36,
+    177,205, 78,182,182,214, 90, 41, 13, 89, 33,146, 65,244,249,218,
+    222,148,205,146, 35,164, 72,207,162, 16,247, 55,223,123,239,189,
+    107,207, 61,231,188,161,156,117,116,139, 10,178,152,120,145,136,
+    165,180,204,210, 45, 90, 43,227, 88,253,108,166, 83,178,204,239,
+    189,245,214,139, 75,171,172, 13, 11,203, 76,100, 12, 39,107,106,
+     36,147,217,135, 81, 50,147,189,206,103,124,182, 54,159,  8,199,
+     61,  4, 11,102,209, 99,150,205,  8, 94, 72, 15,134,143,166,197,
+    145, 54, 75, 78,251, 31,211,238,129, 41,137, 44,201, 97,204, 44,
+     88,144, 77, 38,174, 32,178,204,146,132, 77,198,239,151,187, 87,
+    233,140,209, 89,175,140,251,193,141,183,222,126,234,234, 85, 87,
+     16,197,242,228,130,132, 32, 35,132, 48,133,201,152,137, 89, 50,
+    115,169,225, 85, 12,202,103, 34, 81,228,175, 48,  8, 90,177,177,
+    218, 72,201,150,  4, 49, 11, 99, 88,105,171,148, 73,150,219,200,
+    102,127, 54, 74, 39,  3,236, 43,167,  7, 65, 94,  0,156,200,  8,
+     86,104, 76, 16, 23, 99, 92,133, 37,182,130,  5,145, 76,227, 87,
+    113,114, 82,218, 50,163,108, 97, 73, 18,225,  7,119,251,189,158,
+     88,204, 58,246,238,146,145,105,209,153,152,204, 80,102,192,228,
+      4, 76, 34, 33, 68,126, 84,176, 52,126,199,207, 42, 81,254,224,
+    131, 15,150,125,159, 77, 22,169, 44,179, 49,156, 44,215,152, 68,
+     48, 85, 76,253, 92,156,222,152,141,209,192,152,  9,  0, 78, 65,
+     21, 89,138, 96,146, 68, 30,193,178,179,237, 83, 15,  8, 55, 97,
+     37,183,220,233,222,191,127,191,209,104,  8,167,192, 44, 45,206,
+     43,132,169, 76,150,111,147, 42, 50, 31, 97,150,252, 87, 68, 48,
+     63,139,239,244,238,187,239, 94,246,235,108, 12, 91,145,204, 81,
+     72,218,176,214, 54, 89,155, 67,101, 10, 43,102,197,169,154, 11,
+      2,  3,224, 68,167,176,116, 90,155, 44,130,165,218,226,100,134,
+    196,116,213,219, 66,  4, 69,253,152,124,187,221,185,115,231,206,
+     35,143, 60,146, 23,153, 84,158, 24, 63,185, 96,203, 10, 75,207,
+    202,100, 73,201,212,215,229,  8,230,115,218,244,176, 59,119,239,
+     25,191, 37,165,180, 66,  8, 38, 97, 44,165,245,163,182, 74, 89,
+    149,230,175,116,204,218, 96,233,  8,  0, 56,233, 22, 75,211, 78,
+    110,177,116,250, 73, 33, 69, 90,231, 57,155, 59,  3,184, 50,187,
+    109,221,254, 32,254,200, 71, 74, 11,238,138,116,180,126,178,244,
+    154, 16, 82, 72, 43,140, 21,233, 37, 43,178, 53, 40, 69, 62, 72,
+    150,178,163,145, 68,126,178, 83, 81, 20,214, 99,109,133, 73,211,
+     32,179,180,150,180,177, 90, 25,165,141, 54,201,130,216,233,114,
+    103,197,128,219,188,103, 15,149,  1,112, 58, 34,152, 51, 41, 23,
+     23,115, 87,  8,206, 38, 12, 41, 90, 96,197,188,132,169,217,212,
+    230,195, 48,  8,220,133,213,146,149,144,210,139, 82,202,124,197,
+     34, 43,172,176, 66,138,100, 46,178,124, 18,223,172, 17,150, 30,
+    136,244,147,129,249, 59, 59, 59,181,164,225,101,217, 10, 18, 54,
+    173, 31,109,178,172,172,214,214,232,114,229,232,156,228,200, 40,
+     30,  1, 56, 53, 17,140,137, 69,222, 59, 18,201, 92,227,110,  9,
+     57,128,115,  0, 32,138,186,221,110,189, 94,151,114,112,149,112,
+     45,117,234, 48, 35,165,176, 54, 49,152, 16,130, 57,139, 96,194,
+    105,132,  9,230,236,248, 35,111,111,111, 55,153,172,214, 86, 74,
+     73,194,218, 60,127, 25, 78, 44,150, 45, 52,203,214,157,163,  7,
+    226,  2,224,180, 69,176,172,207,149, 76,202, 75, 34, 85, 75, 50,
+     45,229,192,108,247,238,205, 57,136,186,221,238,242,242,114,186,
+     72,119, 58,208, 94,107,153, 99,146,175, 54, 41, 30,237, 64, 21,
+     41, 68,145,240,152,252, 36,  4,110,111,111, 11,107,153, 12,219,
+    108,135,146, 56,102,138,245,177,173,201,  7,124,237, 54,155,  5,
+      0,224,180,120,204,169, 31,211,238, 23, 21,163, 28,134,247,148,
+    216,152, 94,175,103,180,214, 89,240,210,217,127, 58, 79, 97,158,
+    148, 86, 74,107,172,149,210, 10, 43,109, 54,143,117, 81, 69,230,
+    231,101,250,137, 75,123,221,110, 75, 91, 43,201, 74,182, 36,  4,
+    179,181, 76, 73,224, 74,102,209,201, 87,156,117, 98, 23, 38, 38,
+      4,224, 52,218,139,243,229,139,  6,230, 40,101, 26,218, 79, 74,
+    167,169, 14,163, 32,  8,180, 49, 66,235,188,112,212, 73,234, 50,
+    198, 24, 35,165,244,140,177, 73, 12,179,198, 90, 41,172,149, 66,
+     90, 81,116,193,210,168, 71,130,  4,167,227,239,149, 82,117,163,
+    211, 69, 66,146, 54,156,205,230,255,178, 38, 89,109,155, 77,113,
+     98,  7, 15,238, 27,  0,224,116, 68, 47,166,124,184,106,178,188,
+    119,238, 52,225,142,165, 42, 47,166,155,126, 81, 74, 25,173,147,
+    243,179,141, 54, 58,215,151,244,140, 52, 82, 74, 99,140,148,158,
+    148,214, 74,107,173,149, 86,102, 17, 44, 31, 75,145, 87,145,194,
+     15,131,208, 24, 19,132,161,191,211, 37,207, 99, 41, 89, 72,203,
+     36,172, 37, 99,141,214, 42,138, 84, 24,170, 40, 82,177, 50,198,
+     36, 19, 99,179, 59,103, 54, 97, 81, 71,  0, 78,143,194,  4,  9,
+    182,156, 24,196, 18,  9,178,146,132, 37, 41,211,249,194,152,117,
+     54,223, 97, 50, 45,141,205,230,212, 55,204,183,127,242,214,135,
+    158,125, 86, 74, 45,141,212, 82, 75,147,  4,175,100, 86,124,233,
+     25,207, 24,227,121,198,218, 76, 96,108,165, 77, 86,242,206,102,
+     23, 44,202, 85,146, 16, 15,  0, 96,146,100,190,210,102, 24,214,
+     38,157,246,124,105, 15,107,243, 73, 18, 75, 75,154, 49, 49, 29,
+     98,126,113,232, 14,  0,112,120, 15,232, 76, 84,195,236,149,253,
+    196, 49, 24,103,255, 12, 42,204,159,254,179,  1,  0,204, 15, 71,
+     95,146,192, 24, 99, 60, 79, 26,227, 73, 89,137, 94, 38, 29,234,
+     96,173,177,214, 75, 87, 86,179,108, 37,203,228,107, 62,248, 33,
+    169, 32,177,190, 11,  0, 96,162,228,227,177,202, 17,204,154,252,
+    202, 84, 96,121,  4,115,167,197, 39,119,153,141,195,251, 11,129,
+     11,  0,112, 24,127,229, 20,101,162, 77,150,  4, 49,214, 21, 87,
+    190,184,109, 33, 48,114,231,189, 25, 99,253,184,135,227,138,200,
+    135, 95, 29,  0, 51, 94, 24,102, 83,204,139,177, 74,160,116, 13,
+    115,197, 90, 89,227, 62,187,170,176, 23, 39, 11,170, 89,206,142,
+     64, 82, 62,221,179,115,254,208,120, 35,151,187,126, 47, 51, 91,
+     34,194, 57,146,  0,204,188,188,146, 57,  3,179,213, 58, 74,179,
+    223,140, 57,127, 21, 10, 51,214,122,133,182, 76, 41,146,113, 58,
+    108,190,188,168, 26,165,249, 75,112, 58,127, 14,149,150, 92, 43,
+    205, 49,125, 96,231, 48, 81, 50,208, 67,179, 53,201, 72,144,116,
+      9, 57,  0,192, 76, 43, 44, 89,128,182, 70,194, 23,194, 23,210,
+    203,102,175, 31,115, 89, 86, 10, 96, 92,201, 93,249,144,137,108,
+    101,  5,206,214,180, 45, 41, 44,249,215,207,229,149, 72,199, 79,
+     43,203,253,246,121, 23, 27, 37,139,190,105,203, 49,155,136,109,
+    192, 28, 18, 43, 98,131,119,  7,  0, 51, 47, 48, 65,228,179,168,
+    147,104,145,108, 17, 55, 73,214, 73,202,100,125,142,113,231, 47,
+     78,  7,184, 22,  2,227,106,223, 43, 95,145,187,218,187,207, 91,
+     96, 62,101,211,244,252,248,238,237,165, 70,243,234,218,121, 95,
+    212,188, 67, 41,215, 18, 27,203,138,109, 96, 77,151,109,135,108,
+    143,108,223,218,190,142, 35,165,176, 64, 17,  0,115, 16,193,132,
+     88,240,235,171,245,198,178,231, 47,177,183, 36,185, 65,146,132,
+     28,163,194,210,252,149,228,171,226,  2, 59,253,250,108,212,106,
+    222,253, 42,250,247,228,158,115,233, 39,123,237,121,222,139, 79,
+     61, 29, 71,209,255,247,222, 59, 31,191,244,196, 90,171,125,192,
+    154,145,153,200, 48,199,108,  3,107,182,217,108,145,125,104,212,
+    253,238,206,189,157,109,188, 45,  0,152, 59, 46, 47,175, 60,187,
+    124,150, 69,125,153, 68,147, 88,236, 82, 72, 30, 34,146,228,139,
+    233,101,181, 97, 58,200, 43, 13, 92,153,171, 42,227, 38,184,114,
+      0,146,136,136,100,190, 20,136,244,188,165,197,197,207, 62,115,
+    237,221,205,  7, 94,189,126,208,178,215, 50,107,203,161, 53, 15,
+    217,108,144,189, 27,  7, 63,190,123, 27,242,  2, 96, 78,185,185,
+    243,240,149, 15,222,187, 21,246,250,108,227,221,103,245, 58,100,
+    254,114,200,131, 88,170,178,162,109,239,110, 66, 78,221,152,223,
+     73, 54,254,203, 90,171,162, 72,133,145, 71,226, 76,171,189, 19,
+     71, 94,163, 62,226,193,135,164,147,150,132,175, 46,217,109,178,
+    247,194,254,219,247,238,104, 44,231,  7,192, 60,163,172,253,214,
+    221,155,239,245,119, 20,179, 41,  4,198,123,202,224,128,  6,203,
+     60,229,246,186,  6,114, 87, 46,175,116, 41,202,252,252, 33,153,
+     11, 40,189, 35,226,229,122, 67, 25, 45,253,154,244, 61,177,255,
+     40, 16, 38, 78, 14, 56,218,144,237, 14,217, 45,163,222,185,127,
+     23,191,123,  0, 78,  6,223, 90,191,253, 80, 69, 78, 24, 57,234,
+      1, 73,102,215, 97, 84, 57,186,104,135, 92,202,167,186, 41,214,
+     59, 75,238,196, 47,221,105,186,168,  7,179,177, 66, 10, 33, 37,
+    141, 80,242, 38,119,170,136,251,100, 67, 98, 87, 94, 88,162,  8,
+    128,249, 37,111, 34,253,233,221, 27,127,239,137,107,201, 85, 71,
+    175, 35,243,  6, 86,222,207, 34, 30, 38, 48, 71,113,101,125,229,
+     67, 40,200, 31, 22,167, 92,239,136, 81,130, 34, 51, 25,230,136,
+    248, 65,208, 15,148,202,175,255,111,254,219,223,192,155,  0,128,
+     57,229,255,248,183,255, 38,185,208,209,241, 91,157,135, 47,172,
+    156, 99,107,199, 50, 48,127,160, 29,191, 23,228, 12,188,119, 87,
+    202,102, 30,223,249,219,134,200, 16,109,245,187,248,173,  3,112,
+    242,184, 30,246,252,122,221,243,253,253,234,177,145,228, 85,186,
+     92, 17, 25,185,230,202,100, 71,217, 90,103,217, 63,201,247, 99,
+    243, 87,114,135, 91, 65, 31,191,105,  0, 78, 30, 55,123, 59,245,
+     86, 83,214,252, 97, 53,217,193,207,210, 41,157, 13,148,213,124,
+     89,159,158,202, 35, 85,211,172,149,231,175,172,103, 69, 52,190,
+    252, 37,136, 36,145,194, 49, 71,  0, 78, 34,161,209, 94,173,230,
+    121,222, 56,206, 39,114,231,197,207,207,150,166,114, 49,153,127,
+    239,230,175,234,173,229, 88,246,161,112, 24,  0,224,132, 34,199,
+    116, 42,145,123,252, 49, 43, 10,211,132,149,127,235,172, 35,233,
+    230,175,124,201,141,244, 58,204, 95,  8,  0, 24,177,200, 18, 36,
+    199, 52, 39, 69, 49,141,170,155,191,138,111, 41,147, 85,166,181,
+    114,106,226,163,231, 47,  0,192, 41,134,143,176, 49,151, 47, 23,
+     51,122, 21,249,139,156,252, 85,174, 31,115,225,141,216,191,199,
+     32, 46,  0,192,152,237,150,159,198,232,204,104, 95,228,175,202,
+    189,184,103,110, 31,186,255,  5,145,  1,  0,142,170,  7,174,244,
+    226,121,200, 72,119,103,168,132,187,100,135,243, 32, 99, 61,254,
+      8,  0,  0,  7,151,222, 80,113, 21,134,202,199,122, 13,249, 57,
+    252,  5,  0,152,116,252,218,237,218, 98, 97, 90,230,161, 61,178,
+    129,219,202,169, 60,  1,  0,  0, 98, 87,217, 28,124,136, 25, 78,
+    145,191,  0,  0,199,144, 94, 14, 21,199,170,147,244, 48, 13,255,
+     22,254,  2,  0,204,180, 15,247,200, 99, 24,255,  5,  0, 24,107,
+    226, 26,255, 77,121, 88, 50, 67,254,  2,  0,204,150,  7,249, 80,
+    150,147,163, 63,  0,  0,  0,236,235,  9, 62,228,237,118,253,158,
+    119,223, 82, 30,249,177,  0,  0,167,202, 82,  7, 80,192,168,135,
+     20,249,144, 63, 66,253,  8,  0, 24,123,124, 25,126, 80,113,236,
+     15, 10,127,  1,  0,166, 46,195, 67, 26, 14,254,  2,  0, 76, 51,
+    170, 29,  5,248, 11,  0, 48,175, 28,216, 95,104,223,  3,  0,102,
+     36,162, 73, 40, 10,  0, 48,187, 37, 34,234, 71,  0,192, 73,210,
+     22,252,  5,  0,152,123, 70,247, 23,170, 74,  0,192,108,229, 52,
+     57,243,123,  8,  0,  0,168, 31,  1,  0,240, 23,  0,  0,192, 95,
+      0,128,211,194,113,244,154,228,108,238, 22,  0, 96,222,221, 52,
+      1, 51,200,202,227, 57, 11,128,192, 75,  0,128,185,169, 31, 75,
+     11, 74,138, 81,108,123,244,243,199,  1,  0, 39,168, 24,228,201,
+     22,105,178,188,128, 55, 51,115, 47,142,132,148,194,243,246,118,
+     24,  0,  0,204, 68,254, 98,182,198,178,101,102,162,  7,221,206,
+    165, 71, 31, 21,158, 71,101,129, 33, 97,  1,  0,102,206, 95,204,
+    204,150,147,142,215,223,220,124,255,201,199, 30,175, 53,155,100,
+     45, 91,139, 23,  8,  0, 48,179,248, 68, 68, 36,132, 16,111,222,
+    189, 45,132,120,254,234,213,167,159,120,146,163,216,104,205,214,
+     50,241, 40,211, 93, 35,154,  1,  0,166,227, 47, 33,200,243,188,
+    151,158,251,240,153,179,171,205,102,211, 42,173,131,208, 42,197,
+    204, 48, 19,  0, 96,118,253, 37,136,  4,  9, 33, 37,179, 85, 65,
+     72,177, 18, 74,115, 20,179,214,100, 45, 97, 20,  5,  0, 96,182,
+    235, 71,178,214,170, 32, 12,180, 49,210,247,137,132,177,194, 88,
+    184, 11,  0, 48,235,254,226,180,133,111,173, 54, 70,178, 32,225,
+    113,178,202, 27,  4,  6,  0,152,249,252, 69,217,224, 47,178,123,
+     15, 93,133,209,  0,  0,179,194,168,231, 63,242, 48,125, 65,102,
+      0,128,195, 27,100,236,254,130,146,  0,  0,135,246,211,140,230,
+     47,  0,  0,152,215,250, 17,  0,  0, 78,136,191, 80,102,  2,  0,
+    144,191,  0,  0,  0,254,  2,  0, 28, 59,179, 85,122,201,121,220,
+    105,  0,  0, 64,254,  2,  0,156, 28,127, 49,114, 23,  0,  0,249,
+     11,  0,112, 74,153, 88,238,129,191,  0,  0, 39, 44,127, 29,216,
+    159, 40, 52,  1, 64,210,162,  9,207, 24,136,252,  5,  0,152,215,
+    168, 34,231,253,  9,  0,  0, 80, 63,194, 76,  0,128,185,246, 23,
+    210, 22,  0, 96,126, 74, 76,244,191,  0,  0, 51, 43,168,113,249,
+     11, 49, 12,  0, 48, 99, 82,144, 39,227,105,  0,  0, 78, 33,168,
+     31,  1,  0,167,214, 95,200, 94,  0,128,185,243, 23,196,  5,  0,
+    152,174,  9, 80, 63,  2,  0, 78,109,253,136, 36,  6,  0,152,146,
+     40,144,191,  0,  0,227,150,210,164, 50, 13,252,  5,  0, 56,205,
+    245, 35,  0,  0,169,107, 98, 69, 35,252,  5,  0,152,188,204,224,
+     47,  0,192,188,168,234,216,205, 38,103,107,119,  0,  0, 39,211,
+    122,199, 34, 15,121,200,157,  1,  0,128,105,115, 32,127,193, 92,
+      0,128,153,247, 23,143,228, 45,232, 12,  0, 48, 55,249, 11,  0,
+      0,118, 79, 47, 19,143, 52,242, 24,158,  4,  0,224, 52,137,140,
+    247, 42,218,142,213, 17, 18,110,  2,  0,140, 51,149, 77,208, 28,
+    168, 31,  1,  0,135,169,182,120,  6, 34, 15,252,  5,  0, 56, 70,
+    205, 77,218, 95,140,130, 18,  0, 48, 15, 32,127,  1,  0, 78,142,
+    191, 70, 27,251,133, 84,  6,  0,152, 29,127,241, 81,189,  4,145,
+      1,  0, 80, 63,  2,  0,  0,252,  5,  0, 24, 31, 51, 87, 99,201,
+    185,127,  6,  0,128,169,123,109, 74, 94,144,135, 85, 21, 60,  6,
+      0,152,178, 19, 80, 63,  2,  0,230, 21,248, 11,  0,  0,127,  1,
+      0,  0,252,  5,  0,152, 11,120,191,150,215,113,183,196,224, 47,
+      0,192, 97,220,133,252,  5,  0,128,213,142,201, 95, 60,123,190,
+      5,  0, 32,140, 13,245, 23,214,233,  0,  0,204, 71,233,136,250,
+     17,  0, 48,215, 28,213, 95,200,102,  0,128,121,243, 23,188,  5,
+      0,152,182, 40,228, 12,238, 19,  0,224,  4,120,107,  2,158, 64,
+    255, 11,  0,112,210,234, 71, 68, 44,  0,192,188,250, 11,213, 36,
+      0, 96,214, 63,245,114,196, 61,101,104, 10,  0,112, 88,197, 29,
+    147, 62,208,255,  2,  0,204,107, 36,131,191,  0,  0,243, 10,252,
+      5,  0,128,191,  0,  0, 40, 20, 39, 91, 98, 74,154,157,125,  1,
+      0,128,177,248,107, 20, 59,193, 96,  0,128,249,171, 31, 43,230,
+     98,152, 12,  0, 48,  7,254,130,170,  0,  0,179, 33, 10,121,168,
+     93,129,195,  0,  0,211, 87, 26,142, 63,  2,  0,230, 49,123,193,
+     95,  0,128,121, 70,238,237, 76,158,154, 88,  1,  0,224, 72,249,
+     11,182,  2,  0,204,171,191, 96, 53,  0,192, 17, 57, 70, 97,160,
+    255,  5,  0,152,148,151,198,173, 50,185,207,131, 32,107,  1,  0,
+    102,181, 42, 67,254,  2,  0,204, 43,251,251, 11,  9, 12,  0, 48,
+    175,254,154,233,248,  8,  0,128,191,160, 33,  0,192,220, 37, 21,
+    244,191,  0,  0, 39,188,126, 68, 44,  3,  0,204,156, 12,228,  9,
+    125, 94,  0,128,147,175, 55, 57, 31,187,  9,  0, 64,242, 58,162,
+    191,224, 44,  0,192,236,248, 66,206,218, 14,  1,  0,224,168, 99,
+    175, 31,  1,  0,128,166, 58,203, 22,252,  5,  0,152, 87,142,230,
+     47,148,147,  0,160, 92,156, 45,127,193, 74,  0,128,113,155,237,
+     56,188, 34,199,178,103,  0,  0, 88,139, 39,174, 15,121, 60, 79,
+      4,  0,  0,166, 82, 63,  2,  0,192, 60, 36, 25,248, 11,  0,112,
+    220, 22, 59, 46,167,193, 95,  0,  0,212,143,  0,  0, 84,135,147,
+    237,131,195, 95,  0,128,227,113, 25,252,  5,  0,152,109, 99, 77,
+    211, 98,240, 23,  0, 96, 62,211,215,110,254,226,  3,239, 52, 70,
+    127,  1,  0,102, 58,127,241, 46,170,130,188,  0,  0,168, 31,  1,
+      0,  0,254,  2,  0,204, 10,199, 86,161,201, 81, 31,  9, 53, 34,
+      0, 96, 90,162, 66,254,  2,  0,156, 48,129,201,153,221, 51,  0,
+      0,140, 54,186,191, 32, 40,  0,192, 24,165,116,236, 74,145, 71,
+    120,112,248, 14,  0,176,191, 11,142,207, 20, 18, 90,  2,  0, 28,
+     78, 82, 83,119,  5,250,247,  0,128,227, 83,221,241, 42, 78, 34,
+    123,  1,  0,230,148, 35,230, 47,248, 14,  0,128,250, 17,  0,112,
+     82,138, 70,248, 11,  0,  0,165, 29,206, 95,168, 11,  1,  0,  7,
+    181,210,196,189, 33,247,219, 73,134,208,  0,  0,123,122,106,106,
+    122, 56,106,253,200, 60, 19, 79,  3,  0,112,218,138,199,163,248,
+    139,225, 44,  0,192,116, 65,255, 30,  0, 48,111,185,235, 48,254,
+     66,210,  2,  0,204,146, 12,144,191,  0,  0,243, 42,179, 49,248,
+    139,145,203,  0,  0,211,245, 23, 44,  4,  0,152,175, 74, 82,206,
+    250, 14,  2,  0,192,120,235, 71, 40, 13,  0, 48,117, 63,200,185,
+    216, 75,  0,  0, 12, 54,102,127,  1,  0, 96,168,249,171, 31,  1,
+      0,208,215,212,109, 38, 71,182, 43,138, 69,  0,192,108, 33, 15,
+    161, 40,152, 12,  0, 48,155,245, 35, 38,148,  0,  0,140,189,194,
+    156,108,253, 56,115,123, 10,  0,  0,199,225, 47,  0,  0, 34,213,
+    228, 51,142,220,247, 49,144,173,  0,  0,200, 95,  0,  0, 48, 25,
+    127, 33,119,  1,  0,102,161,246, 28, 87,254,130,211,  0,  0,179,
+     99,  4,121,168,253,133,199,  0,  0,211, 23,158, 68,210,  2,  0,
+    204, 41,232,223,  3,  0, 78,149,191,152,136,145,208,  0,  0, 83,
+    174,217,144,191,  0,  0,135,215, 20, 59, 77,241,201, 27, 76,142,
+     79,168,  8,100,  0,156,218,160, 53,157,143, 63,242, 23,  0, 96,
+     94,139, 75, 57,197,199,  6,  0, 64, 85,199,236, 47,216, 11,  0,
+     48,147,160,126,  4,  0,156, 76,127, 33,122,  1,  0,102,182,124,
+     60, 68,254,130,211,  0,  0,179,225,  9, 57, 75, 59,  3,  0,  0,
+     71,240, 23, 68,  5,  0,152,151, 24, 38, 15,186, 19, 16, 28,  0,
+     96, 70,202, 53, 57, 15, 59,  9,  0, 56, 45,166, 99, 62,128,102,
+     48,126,  2,  0, 48, 99, 33,140, 71, 93,138,  3,254,  2,  0, 76,
+    208, 90,214, 10,107,247,220, 62, 61, 19,156,247,200,104,217, 87,
+     57,206, 61,  3,  0, 64, 85,123,210,221,220,218,185,183,110,226,
+    184, 90, 40, 14,150,142, 67, 31,138, 75,  9, 77, 78,247,201,  0,
+      0, 78, 21, 70,233,173, 15,238, 60,184,123,143, 43,206,170, 76,
+     77,191, 71,  1,201,168, 31,  1,  0, 83, 66,133, 81,208,237,106,
+    165,  6,211, 23, 87,  4,198,187,  6, 48,248, 11,  0, 48,133, 66,
+    147,217, 90, 99,114,127,241,176, 57, 17,147,237,216, 17, 88, 69,
+     97,188,187,191, 80, 11,  2,  0,142,160,168,125,183, 31, 24, 37,
+    193,238,143,216,153,214,149,147, 18,147,243,105,235,157, 76,198,
+    200, 95,  0,128,169,186,142, 51, 41, 49, 15,241,217,158, 51, 84,
+    203,131, 62, 22,  0,  0,238, 57,186, 32,152,136,221, 24,198,204,
+    165, 92,150,100, 45,222,251, 97,228,193,172,  5,157,  1,  0,198,
+    105, 68,174,120,133, 19,136,220,194, 49,181, 27, 85, 15, 83, 30,
+    168,126,100,196, 52,  0,192,120, 62,245,236,  4,175, 36,119,113,
+    169, 13,150,133, 48,206, 47, 19, 23, 87,237,239, 47, 72,  9,  0,
+    112,108,138,168,180,240,139,241, 19,204,133,216,156, 77,185, 52,
+    198,226,176,227, 87,161, 53,  0,192,193,229, 48,112, 86, 99, 86,
+     41,150, 58,243, 89, 31, 44, 43, 30,147,139,149,250, 49,147,153,
+     28,199,126,  1,  0,224,172, 67,222, 44,209, 83,214,247, 26,158,
+    191,168,168, 46,243,240,133,241, 19,  0,128, 25, 83, 96,145,191,
+     56,207, 95,249,113,201, 92,111, 35,244,191,  0,  0,224,184,170,
+     50, 46, 37,174,226, 59, 38,231,248, 99,145,185,242,248,229, 22,
+    155,240, 23,  0, 96,170,146, 99, 87, 81, 89,205,152, 23,149,213,
+      1, 21,165, 83, 34,229,100,247, 20,  0,  0, 40, 23, 22,231,195,
+     86,179, 14,189,147,196,200, 29,209,202,121,242, 98,206,  7, 81,
+     32,127,  1,  0,142, 39,196,240,104,219, 22,199, 22,179, 33,171,
+     78, 81, 73,229,102, 88,105,152,197,208,254, 61, 82, 20,  0,224,
+    232,101, 22,143, 82, 54,114, 81, 45,150,  2, 87,222,183,119, 11,
+    202,242, 16,138, 49,212,143,144, 29,  0, 48,216,225,182,100, 71,
+     97, 92,118,150,107, 43,167,161, 79,149, 30, 62,234, 71,  0,192,
+    180, 11,205,226, 20, 34,119,198, 47,118,126,144,119,249,157, 67,
+    147, 71,245, 23, 35,132,  1,128,216,117,120, 15,184, 67,240,221,
+    163,141, 69,225,200,249, 68, 21,204, 85,235, 29,201, 95,124,196,
+     93,  7,  0,204,169,196,248,232, 38, 99,118,143, 45, 14, 76,105,
+    152, 70,176,242,144,251,242,232,175, 67,143,191,135,174,  0,  0,
+      7,242,  4,239,187,101,249, 68, 34,103,192,106,218, 23,203, 92,
+    135,254, 23,  0, 96,122,133, 39, 87,199, 70, 12,157, 98,149, 43,
+    167, 64,186,163, 87,247,152,255, 30, 89, 12,  0,112,140, 31,122,
+     75,236,249, 62, 13,214,143,206,192, 47,103, 10,252,234, 50, 68,
+    121, 65,233, 31,101,223, 97, 46,  0,192,129,229, 37, 37,147,240,
+    107, 53, 30,168, 31,157, 62, 24,231,229, 36,149,230,211, 57,220,
+    248, 47, 70,246,  2,  0, 28,197,  3, 76, 68, 36,132,105,212,151,
+    207,174,142,120, 23,  3, 43, 63,150, 78,129, 68,255, 11,  0, 48,
+     57,180, 39,253,133,246,218,185,115,153,131,156,163,144, 78, 14,
+     43, 29,105,220,101,137,110, 66,255, 30,  0, 48, 73, 22,151,151,
+    159,188,122, 85,122, 94, 85, 73,110,217, 72, 84,140,100,101, 26,
+    178,138,109,214,  0,147,251,  5,190, 17, 98, 29,  0,  0,149,226,
+     94, 55, 40,110, 86,171,213,242,228,229,108,197,213,107, 10,151,
+     85, 87,246,112, 55,144,  7,218, 79, 72, 11,  0,112,172,122,228,
+    189,162, 84,181, 27, 38, 71,185, 71,  0,  0,152, 65, 23,200,221,
+    247, 18,226,  2,  0,204,180,233,208,191,  7,  0,140, 93, 91, 99,
+     23,152, 32, 65, 68, 98,119,127, 29,117, 25, 36,  0,192,169,118,
+    215,193, 92, 32, 68,245,178, 16,162,170,168,244, 10, 33,  6,212,
+     37,136, 72,200,209,118, 12,  0,  0,142, 43,225,  8,114,178, 85,
+    234, 53,145, 74,203, 53, 87,126, 57,187,  1,234, 71,  0,192,100,
+     41,  5, 47, 33,210,240,149,  5,173, 68, 93,130,138, 44, 38, 92,
+    107,149,130,216,232,254,226, 61, 60,138,132,  6,  0,  2,214,161,
+     74,199, 65,167,149,  5, 55,100,195, 60,169, 33,127,  1,  0,198,
+    171,179,145,125, 54,208,210, 18,165,122, 81,  8,202, 26, 98, 34,
+     41, 39,  7,110,115,120,127, 33,115,  1,  0, 14,233,129,164,104,
+    204, 11, 67,145,235, 73,164,174, 18, 37,117,185,213,165,219, 17,
+    147,199,184,139,  0,  0,176, 91,236, 74, 13,150,246,191, 74, 71,
+     30, 69,185,210, 20, 67, 74,199, 68, 98,168, 31,  1,  0,179,145,
+    204,146,204,149,143,165, 72,123,248,162, 20,208,168,100, 59,121,
+    192, 71, 67, 20,  3,  0, 28, 77,  5, 66, 84,142, 36, 38,122, 18,
+    110, 40, 75, 55, 40,140,230, 30,164,204,111,135,252,  5,  0,152,
+    140,183, 74, 53,100, 81, 61,230,122,114,242,151, 40,231,175, 36,
+    113,137,178,190,170,227,191, 24,241, 10,  0, 48,  9,165,137,234,
+    248,251,114,254, 42,174,205, 75, 73,167,109,150, 15,108, 69,254,
+      2,  0, 76,212,105, 98,160,142, 20,229,252,149,199, 50, 81,202,
+     95,121,215, 94,228,135, 32,229, 65, 45,202,149, 45,121,220,193,
+     18,  0,112,130,107, 72, 81, 58,207, 49, 27, 40, 81,228, 47,225,
+      6,180,180, 49,150,253,143,156,250, 81, 84,253,  5,  0,  0,199,
+    107, 54, 81, 36,169,210,120,137,188,233, 69,194,233,125, 81,254,
+     95,233, 86, 89,252, 58, 72,255, 30,241, 10,  0, 48,220, 11,135,
+    178, 67,169, 80, 44,138, 73,119,252, 68,169,144,204, 15, 74,230,
+    213,166,132,165,  0,  0,147,211,157,210, 30, 17, 49,139, 74, 77,
+     89, 20,147, 84,252, 47,111,119, 21,195,243, 75,179, 85,200,177,
+    101, 45,136, 15,  0, 20,142,251,209,221,218,218,124,255,134,142,
+    162,162,126,116, 38,255, 34,183,137, 79,197, 76, 20,162,226, 49,
+     66,255, 11,  0, 48,113,145,177,181,157,  7,155, 27,119,239,209,
+    192,176,213,226,248, 98, 49, 84,162,116, 38,209, 81,230,207,  1,
+      0,128, 49,160,226, 40, 10,  2, 21, 69,228, 28, 75,172,158,  3,
+    233, 76,  0, 54, 48, 45,107,225,181,113,248, 11,149, 35,  0,  8,
+     93,  7,249, 49, 51, 91,107,135,110,155, 87,137,110, 59, 95,148,
+    206,249, 70,254,  2,  0, 76, 47,183, 48, 15,206, 18, 86,204,178,
+     58,100,122, 67,162,210,184,175,108, 83,248, 11,  0, 48,  5, 42,
+    115,228,208,144,217,  9,135,174,217, 81,  2,254,  2,  0, 76, 54,
+    125,141, 15, 57, 79, 59, 11,  0,128,219,246,244, 23, 79,120, 15,
+      0,  0,115, 37, 38, 30,139,181,156,181,110,139,147,170,121,239,
+    187,100,212,143,  0,128,153,148, 35,103,146,218,101,  5, 16,199,
+    158,217,166,114, 79,193, 29,227,114,224,  0,  0, 48,204, 82,156,
+    184, 44,213, 21,179,235, 54, 46,203,  8,249, 11,  0, 48,253,138,
+    148,139,255,101,166,202,190,101,230,114,130,226,124,166, 85,121,
+    248,199,  4,  0,160,228, 59,194,141,153,185,156,175, 82, 49, 57,
+    105,139,  7,165,229, 62,166,132,166,  0,  0,147,201, 89,123, 89,
+    144,217, 41, 24, 57, 29,224,154,248,141,179,186,145, 29,101,242,
+     81,234, 71,184, 13,  0, 48, 62,187, 49, 57,214, 42, 76, 86,241,
+     86,209,255,226, 61,242, 23,195, 80,  0,128,227,118, 86,  6,229,
+    133,164,155,191,146, 43,243,252,149,111, 64, 89,149,201,232,223,
+      3,  0,166, 82,119,241, 46,249, 43,119, 23,229,233,171,232,228,
+     59,149, 36, 13, 25, 63,177,231,195, 48, 42, 71,  0,192, 24,  5,
+     86, 84,140, 92, 50, 83, 58, 12,172,240, 89,186,101, 81, 26,238,
+    223,255,130,170,  0,  0,199,150,207,152,203,227, 35,156,244,149,
+    151,149,121, 51, 44,203, 95, 78,  5,137,241, 95,  0,128,169,248,
+    141,203, 38,115,  7, 82,176, 51,156,130,139,178, 50,223, 52,171,
+     40,209,255,  2,  0, 28,147,166,246, 15, 99,121,208, 42,242, 87,
+    238,177,188,  7, 86, 12,113,205,227, 87,254, 40,242,  0,187,131,
+    170, 18,  0, 48, 54,201, 49,187, 23,135,228,175,210,129,199,180,
+    251, 94, 26, 27,193, 71,205, 95,144, 25,  0,168,  2,199,104,180,
+    161,249,203, 41, 32,217, 61,211, 72, 30,249,  9, 64,100,  0,128,
+    131, 97,139,161,170, 69,253,200, 67,149, 86, 46, 24,185,184,158,
+      9,253,123,  0,192, 68,146,154,139,176, 68,181, 70,195,233,201,
+    115, 49,192,190, 24,  1,230, 28,108,116, 79,140,116,206,138,148,
+    227,219, 39,  0,  0, 74,200,221,174, 46,190,183,190, 71,158, 39,
+    165,228,161,245, 99, 41, 99,185, 18, 27, 50,254, 84,142,229, 25,
+      0,  0,192, 40,158, 96, 41, 85,221, 95, 89, 59,187,215,102,123,
+    217,209, 45, 34, 15,232, 47, 40, 11,  0,112, 20,140, 39, 87, 46,
+     92, 88, 93, 91,203, 35, 23,187, 85, 97,105,214,  9,114,103, 45,
+     36, 26,109,254, 28, 30, 89, 96, 12,185,  1,  0, 14,194,153,149,
+    149, 11, 23, 46, 12,243,138, 83, 54,230, 62, 43,213,147,206,229,
+    236,  6, 99,232,223,195, 91,  0,128, 17, 77, 32,164, 40,123,202,
+    185, 92,157,104,149,157, 51,187,135,246,211,134,143,255,130,145,
+      0,  0,  7, 22,214,120, 22, 38,218,237, 78,134,205,234, 37,247,
+    221, 65,200, 12,  0, 48,155,213, 25,198,127,  1,  0,102,223, 84,
+    163,248, 11, 89, 11,  0, 48, 61, 67,  9, 34,177,219, 79,  4,149,
+    126, 40,144,191,  0,  0,147, 38,115,144,112,101, 36,170,215, 20,
+     54, 19,201, 79,134,121, 77,200,125, 92,139, 68,  6,  0, 24,191,
+    189, 74,223, 10, 87, 94, 66, 20,129, 75, 36,255, 23,165, 16,150,
+    221, 76, 32,127,  1,  0,198, 91,100,238,159,121,202,201, 75,  8,
+     71, 74, 66,228,215, 21,181,164, 24,144, 93,134, 60,210,158,  2,
+      0,160,172, 35,167, 48,218,181,237, 53,208,241,114, 91,100, 98,
+     52,127, 65, 84,  0,128,221,189,192,135, 51,135, 24,200,101, 34,
+    253,146,212,138,217,191,201,149,194, 41, 42,199,145,191,224, 55,
+      0, 32,177, 67,109, 35,132,219,243, 18, 66,184,215, 84,234,199,
+    244, 39,213, 66, 82, 28,201, 95,144, 22,  0,224, 16,228,210,202,
+     84, 85, 26, 25, 81, 42, 24, 69,233,192,163, 24, 56,120,137,254,
+     61,  0, 96, 54,114, 90,154,187,146,255,220,146, 49,189,222,209,
+    222, 81,199,127,241,200, 59,  5,  0, 56, 45, 54, 26,237, 28,110,
+     33, 74,255,119,234,199,114, 66, 43,108,229, 14,  3, 43, 54, 20,
+    242,160, 59, 10, 99,  1,  0,142, 92, 67,102,101,163, 91, 63, 22,
+    201, 43,203, 95,169,173,132,200, 47,149,238,227, 32,249,139,247,
+    250, 17,180,  6,  0,234,192, 67,230, 28,167,127,239,182,187,132,
+     27,204,242,186, 81,100,199, 38,105,108,253, 47,232, 11,  0, 80,
+     81,194,110,171,119,148,235,200, 82,254,114, 19, 24,149,251, 95,
+    197,224,  9,113,136,254, 23, 20,  5,  0, 56,122,229, 72, 36,220,
+    241, 18,194,137, 90,228, 94,147,187,140,242, 77,168,122, 18,247,
+     40,254, 98,232, 11,  0, 48, 62,129,149, 37, 84, 68, 45,167, 41,
+     70,131, 61, 50,145,107, 46, 63, 50,137,241, 19,  0,128,  3, 22,
+    134, 71,140, 95,142,184,114, 69,229, 22, 19,229,250,209, 57, 70,
+     89,154,140, 34,249, 71, 78,243,169,  0,  0, 78,161,238, 68,121,
+    188,132,147,191,168, 84, 46,186, 99,193,178,252, 85, 30,131, 47,
+     15, 40, 39,134,195,  0,  0, 71,214,153, 24,146,191,178,110,151,
+     91, 46,186, 35,238,171,  7, 40, 49,254, 30,  0,112,244, 68,117,
+    196,233,  2, 69,245,160,164, 27,185, 10,203, 81,249,100,110,248,
+     11,  0, 48,105,245, 13, 52,182, 74,165, 34, 21, 51, 24,150,103,
+     93, 21,206, 20,210,217, 88, 48,248, 11,  0, 48, 89,156, 89,113,
+    132,123, 58,163, 40,231, 47, 42,242,151,211, 35,115, 34,155, 24,
+    115,254, 66, 47, 12,  0, 48,220,  6,251, 77,  6,230, 12,140, 24,
+    200, 95,162, 80, 86,214,  1,195,250, 29,  0,128,137,184,107,152,
+    175, 92,113, 21, 93,124, 39,163, 81, 54,249,189, 16,228,134, 52,
+    167,171, 15,127,  1,  0,166, 84, 69,102, 30, 43,159,149,237, 12,
+    254,162, 34,146,149,234,199,236,  6,163,250,171,124, 54, 19,234,
+     68,  0,144,177, 14, 43, 46,103, 98, 85, 49,112,220,177,216,170,
+    152,243,107,215,217,241,145,191,  0,  0, 99,212,215,126,147,209,
+      8, 55,124,229,151,  7,214,127, 28, 50,235,234,144,197,109,225,
+     47,  0,192,100,211,152, 16,131, 82, 27,108,135,229,162, 19,206,
+    156, 19,229,  1,248,229,241, 19, 40, 11,  1,  0, 19, 70,236,178,
+    168,109,105,252,151,168, 46,164,134,249,239,  1,  0, 83,241,213,
+    174,169, 76, 84,214,241, 16,131,183, 17,168, 31,  1,  0,147, 44,
+     26,121, 87,141,137, 93, 22,215, 22,187,155,111,232,252, 95, 40,
+     30,  1,  0, 19,204, 97, 98,255,107,118,141, 95,  2,227, 87,  1,
+      0, 51, 88, 76,138, 33,139,213, 14,187,149,216,219, 95, 72,100,
+      0,128, 99, 44, 43,119,183,154, 24,205,117,200, 95,  0,128,105,
+      9, 76,236, 91, 86,138, 61,111, 47,199,188, 63,  0,128,211,102,
+    166,233,233,  1,249, 11,  0, 48,175,192, 95,  0,128,201, 85,140,
+    240, 23,  0,  0,236,230, 47, 70,187, 11,  0, 48,135,254,226,209,
+     66, 34,  4,  7,  0,152,122, 77, 41,103,115,183,  0,  0, 51, 40,
+     36,158, 49, 29,200,131,237, 62, 92,  6,  0,152,217,250,241,192,
+    118,130,192,  0, 64, 97,184,123, 62, 59, 86, 69,224,248, 35,  0,
+    224,232,101,228, 76,228, 47,164, 41,  0,192,161,172, 54, 13,121,
+     72,148,133,  0,128,113,149,144,227,191,209,184,235, 71, 44, 67,
+      4,  0,152,146,177, 14,234, 47, 72, 10,  0, 48,155, 72,200, 11,
+      0, 48, 71,153,235,136,245, 35, 28,  7,  0,152,135,252,  5,  0,
+      0,115,225, 47, 30, 53, 83, 49,  2, 24,  0,224,  0,182,224,227,
+     17,  5,242, 23,  0,224,224,166,226,153,  8, 47,114, 20,153, 34,
+     96,  1,  0,142,150,195, 38,228,175,253,246,  2, 50,  3,  0,204,
+    134, 17,228, 33,119, 10, 22,  3,  0,240,148,155,225,242, 32,187,
+     10,113,  1,  0,102,  8,121,104,115, 66,100,  0,128,153,241, 23,
+     68,  5,  0,152,111,127, 29,214, 86,176, 28,  0, 39,155, 25,252,
+    140,203,121,221,113,  0,192,169,151, 29,198,175,  2,  0,230, 53,
+    144, 73,100, 46,  0,192,156, 34, 15,166, 87, 72, 13,  0, 48, 31,
+    254,  2,  0,128, 25,174, 36,225, 47,  0,192, 49,232,107, 34, 74,
+    147, 99,217,105,148,149,  0,  0,228, 47,  0,  0,152,128,191, 16,
+    186,  0, 64,173,136,252,  5,  0, 56,201,174, 59, 54,231,201, 17,
+     13,203,152, 52, 26,  0, 48,170,178,120,119, 87,140, 83, 29,114,
+    140,251, 13,165,  1,  0, 38, 89,114,202,163, 61, 52,148,  5,  0,
+    152, 26,  7,205, 95, 12,139,  1,  0,230,200, 95,208, 19,  0, 96,
+     22,189,128,227,143,  0,128,121,116, 23,252,  5,  0, 56,201,245,
+     35,106, 71,  0,192,188,250, 11,  0,  0,224, 47,  0,192,  9, 99,
+    234,229,153, 60, 41, 79,  4,  0, 48,123, 94, 59,102, 49,200,227,
+    216,103,  0,192,169,119,215, 36,132, 32,199,183,187,  0,128,211,
+     84, 50,242, 97,111, 56, 17,127, 49, 15, 60,250,177,159, 78, 14,
+      0,  0,211,168, 31,  1,  0,200,101, 19,173,207,224, 47,  0,192,
+     49, 75, 12,254,  2,  0, 64, 95, 99,240, 23,163,  1,  6,  0,152,
+      1,144,191,  0,  0,227, 13, 95,147,139, 55,240, 23,  0,224,120,
+    140,118,252, 30,131,191,  0,  0, 71,203, 91,243, 81, 63,162,233,
+      5,  0,152, 37,228, 80, 65,141,106, 42,172,217,  1,  0,152, 94,
+    214,145,179,181, 59,  0,128,121,119,215,  4,109,129,254, 23,  0,
+     96, 54,211,213, 56,252,133,232,  5,  0,152, 77, 14,148,191,120,
+    215,239, 25,170,  3,224, 20, 37,175, 25,249,172, 75,100, 45,  0,
+    192,152,170,199, 73, 27, 68,162,120,  4,  0,204,125,253,  8, 85,
+      1,  0,142, 24,200,120,228, 31, 29, 75,254,226,221,247, 15,130,
+      3,  0,204,104,254, 26, 67,241, 11,  0,  0,179,236, 47, 24, 13,
+      0, 48,191,254,130,175,  0,  0, 39, 37,127,  1,  0, 78, 45,211,
+    141, 51, 18, 81, 11,  0,112,234,242, 23,220,  6,  0, 72, 93, 48,
+     37, 29,160,126,  4,  0,204,107,177, 41, 17,175,  0,  0, 99,183,
+     19, 79, 68, 39,200, 95,  0,128, 99, 76, 81,199,154,132,224, 47,
+      0,192,204, 21,134,135,247, 23, 42, 71,  0,192, 92,128,252,  5,
+      0,152,215, 12, 38,103, 44, 15,  2,  0,160, 45,228, 47,  0,192,
+    201,182,215,232,254, 66, 30,  3,  0,204,154, 27,144,191,  0,  0,
+    243,106, 57,248, 11,  0, 48,175,192, 95,  0,  0,248, 11,  0,112,
+    226, 75,192, 25,107,132,203,113, 60, 39,  0,192,105,178,216,124,
+    251,139, 33, 49,  0,224,176,131,223, 98,220,166, 64,253,  8,  0,
+    152, 87, 14,238, 47,100, 45,  0,192, 76,250, 11,114,  2,  0,140,
+    152,100,120,234,222,144, 72, 88,  0,128, 83, 83, 63,162,172,  4,
+    224,212, 70,174, 25,179,129, 60,194,174, 67, 89,  0,128,105, 26,
+     76, 30,112,167, 24,238,  2,  0,204,136,  6,228,193,118,  9,218,
+      2,  0,186, 58,144, 10,248, 24, 13, 34, 71,122,120,222,115,167,
+     32, 53,  0, 96,180,105,152,  0,227, 87,  1,  0,243, 10,252,  5,
+      0,152, 72, 60,155,156,191,184,252,101,194, 59,  5,  0, 56, 65,
+    174, 58, 62, 99,200,163,237, 24,  0,  0, 76, 45,147,201,163,237,
+      3,212,  6,  0,152, 26, 18, 57, 11,  0, 48,163,249,234,152,242,
+     23,  0,  0,236,165,175,137,120, 12,254,  2,  0,204,107,  0,147,
+    227,216, 17, 20,158,  0,192, 81, 83,240,130,132,148,  0,  0,115,
+     10,234, 71,  0,192,188,214,145,114,223,  7,102,100, 51,  0,192,
+     12,216,106,172,249,139, 97, 50,  0,192,190, 26, 59,198, 67,146,
+    242,104,123,  6,  0,  0, 83, 83,  5,250, 95,  0,128,121,168, 21,
+     15,225, 47,222,115,119, 81, 65,  2,  0,133, 77, 81,238,183,167,
+     75,  0,  0, 32,  0, 73, 68, 65, 84,  1,242,160,126,133,174,  0,
+      0,179,159,191, 14,100, 42,104, 13,  0, 48, 99,245, 35,  0,  0,
+    192, 95,  0,  0,112,220,254,226,161,223, 50,138, 68,  0,192, 62,
+     76,124,182,102,121,132, 61,  3,  0,156,118, 65,237,189,  2,209,
+    113,107,  3,245, 35,  0,224, 72, 17,107,150,234,199, 35, 61, 33,
+    132, 52,  0, 32,178, 41,249, 11, 35, 38,  0,  0,199, 80, 87,162,
+    126,  4,  0,  0,248, 11,  0,112,250,252,133,138, 17,  0, 48, 75,
+     66,144,187,237, 13,100,  5,  0,152,179,252, 53,130,182, 96, 54,
+      0,192, 92,213,143,144, 22,  0, 96, 84, 89, 76,106, 21, 72,244,
+    239,  1,  0, 39, 61,127,  1,  0,  0,252,  5,  0, 56,169,117,227,
+     28,251, 11, 45, 50,  0,192, 48, 37, 28,155, 27, 36,244,  3,  0,
+    152,211, 84, 38,103,119,215,  0,  0,224,160,254,194,124, 18,  0,
+    128,121,245, 23,  0,  0,192, 95,  0,  0,144, 21,115,199, 80,206,
+    193, 95,  0,128,211,145,191,208, 15,  3,  0,204,142, 40,228,252,
+    237, 50,  0,224, 20,184,  9,245, 35,  0, 96,114, 86,155,188,218,
+    228,248,118, 30,  0,112,138, 18, 24,207,192,167, 95,142,255,105,
+      1,  0, 80, 77,162,126,  4,  0,204,129,178,248,144,183,131,191,
+      0,  0, 51, 33,174,169, 36, 49,121, 92,207, 12,  0,128, 82,114,
+      6,253,  5, 89,  1,  0,102,  1,  9, 71,  1,  0, 78,152,191,  0,
+      0, 96,214,235, 72, 57,251,187,  8,  0,128,178, 14,236,175, 97,
+     11,218, 66, 92,  0,  0,212,143,  0,  0, 36, 50,248, 11,  0,112,
+    250,106, 71,248, 11,  0, 48, 86,117, 77,216,102,240, 23,  0, 96,
+     94, 67,154, 60,194, 46,160,151, 15,  0,152,102,101,233, 31,118,
+     39, 74, 87, 11, 65,130,132,192,111, 12,  0, 48,119,245,163, 32,
+     18, 68,146, 96, 48,  0, 78,174, 44,228,225,117,113, 76, 89, 76,
+     30,102, 71,120, 80, 94,194, 39, 81, 39,225, 11, 52,212,  0, 56,
+    129, 52,253,154,231,251, 98,255, 42,107,162,109,165,145,116,179,
+    239, 44, 25, 66, 80, 77,136, 22,137, 71, 90,109,252,166,  1, 56,
+    121, 60,125,238,188,244, 61,158,186,177,142,167,126, 20, 53, 18,
+    109, 33, 47,183, 22,241,155,  6,224,228,241,161,181, 11,196,196,
+    198, 28,229, 78,198,110, 58,191,122,247, 98,191,114,113,184,191,
+    200,147,178, 97,237,179,237,229,215,182, 55,186, 90, 37,215,175,
+    175,223,195, 47, 30,128,121,231,236,194,226,103,158,185,214,121,
+    176,161,149,158,170,175,246,246,215,190,143,207,187,215,149,204,
+    190,148, 45,182, 95, 90,123,244, 63,220,187,145, 92,249,231,223,
+    252, 51,252,238,  1,152,119,190,241,209,143,235, 56,142,250,125,
+    163, 20, 51, 19, 51,137,153, 24,111, 48,230,118,187,239,249, 79,
+    180,151,126,246,194, 37,252,202,  1, 56, 25,252,202, 39, 62,117,
+    237,209,199,251,219, 59,113, 16,176,181,135,136, 86,199, 23,195,
+    252,163,221,188,180, 99,204, 44,152,165,231,125,100,245,188, 95,
+    175,255,249,157, 27,145,209,248,245,  3, 48,167,180, 27,141,255,
+    226, 35, 47,126,250,234, 51,157,  7, 27,253,237, 29, 29,103,225,
+    107,102,240,199,123,119,214, 90, 18, 66, 18,191,176,118,225,202,
+    202,217,239,109,220,253,225,157, 91,120, 31,  0, 48,119,124,234,
+    234, 51, 63,243,236,243,231, 22,150, 58, 15, 54, 58, 27,155,113,
+     24, 88,107,118,151,215,144,222,249,204,248,235, 32, 43, 38, 89,
+     99,116, 76, 66,136, 51,173,214,215,159,126,254,231,174, 94,123,
+    235,225,131,157, 56,222,137,194,135, 97,128,183,197,145,254, 60,
+     48,199,108,243,195, 35, 68,116,161,181,192,108,217, 50, 94,135,
+     83,248, 58,140, 23, 65,180,210, 94, 88,105,181, 86, 90,237, 23,
+     46, 93, 89,108, 52,195,110,111,235,206,221, 96,103, 39, 14, 35,
+    107, 44,113,106, 47, 49,145,218,240,248,242,215,222,109,125, 98,
+     99, 84, 24, 89,109,140, 82,181, 86,235,163,107,143,212, 27, 77,
+    175,230, 75,207, 19, 82, 10,156,104,116, 88,250, 70,223,143,195,
+     87,119, 54,242,107,126,253,153, 23, 84, 28,233, 40,198,235,112,
+     10, 95,135, 99,249,219, 96,140, 53, 70,245,194,141,251,155, 81,
+    175,167,194, 72, 43,197,108,201,142, 94, 55, 78,110,174, 83,127,
+    255, 44,120,240,221, 96, 34,182,150,149, 50,198,196, 65, 24,214,
+    106, 94,205,247,124, 63,145,151,144,240,215, 33,233,106,125, 47,
+    234,223,120,120, 63,191,102,107,241,156, 10, 35, 21,133,120, 29,
+     78,225,235, 48, 70, 82, 57, 49, 39,254,210, 74, 89,173,173, 54,
+    156,113,208, 10,109,150,243,215,136,175,  8,179, 49,108,173, 49,
+     70,132, 68, 34,  9, 94,144,215,225,233,104,181, 21,246,214,183,
+    214,139, 79,242,197, 45, 21,134,113, 16,226,117, 56,133,175,195,
+    113,125,110,211,175, 76, 76,204, 51, 93,146, 31,222, 95, 60,154,
+    120,147,  3, 22, 76, 36,  4, 49,228, 53,142,108,175,181, 46, 93,
+    163,141, 61,218,168,104,188, 14, 96,152,195,142,226,133,  9, 37,
+     50,127,176,242, 59,166,136,148, 88, 29,111,141,163, 70,218, 74,
+    152, 79,255, 62, 48, 94,135, 83,248, 58,204,192,111, 98,202,  5,
+     21,166,139,152,187,247,203,192,247,140,215,129,240,199,241,116,
+     34, 15,249,126,  1,  0,128, 81,254,174,204, 76,254,130,196,  0,
+      0,115,152,191,  0,  0, 96, 78,253,197,136,100,  0,128,  3,124,
+    244, 39,162,  6,228, 47,  0,192, 73,202, 95,140, 96,  5,  0, 56,
+     57,245, 35,152,155,216,142,215,  1,156, 30,124,188, 53,  0, 17,
+    173, 92,185,244,137,223,248, 85, 34,122,229,159,255, 11,213, 15,
+    136,232,133,111,124,253,194,115,207,110,189,127,235,  7,255,230,
+     15,146,109,190,242,143,126, 55,223,254,250, 95,126,231,250, 43,
+    223, 38,162,143,255,250,175,174, 62,113,233,214,171,127,243,147,
+     63,249,102,242, 45, 17,185, 55,201,239, 33,217,242,155,255,244,
+    127,199,171, 13,142,213, 95,176,216,169,163,222,110, 95,120,254,
+     26, 17, 93,122,233,197, 68, 76, 79,125,254, 51, 11,231,207,185,
+    219, 92,120,254, 90,220,235, 63,188,113,211,189,114,245,137, 75,
+     23,158,191,182,114,229,242,245, 87,190,173,250,193,234, 19,151,
+     42, 55,169,108,137,151, 26, 28,191,191, 38, 55,  1,  6,152, 33,
+    226, 94,255,252,115,207, 94,127,229,219, 43, 87, 46, 45,156, 63,
+    183,245,254,205,202, 91, 96,235,198,205, 63, 47,  7, 40, 38,234,
+    221,127,176,112,254,220,181,175,189,252,218, 31,254,209, 30, 51,
+    197,225,221,132, 82,127,146,249,107,191, 29,153,244, 80, 91,176,
+    199,171, 60,158, 23,255,214,171, 63,188,244,137, 23,191, 71,191,
+    127,233,165, 23,183,222,191, 25,247,251,149, 59, 95,189,114,249,
+    103,254,209,239, 18,209, 15,254,245, 31,228, 65,172,251, 96,227,
+    222,143,127,242,161,175,126,229,205, 63,249,179, 93,246,135,199,
+    190,171, 60,241,143, 10,200, 16, 99,249, 85,141,  5,121,228,119,
+     14,222, 49, 39,135,251, 63,126,171,190,208, 94,185,114,249,241,
+     79,124,236,250, 95,126,123,244, 27,254,232, 15,255, 67,125,161,
+    125,237,107, 47,227, 53, 60,213,127, 92,121,210,127, 81,124,188,
+    232, 32,167,119,127, 99,235,253,155, 31,250,218, 87, 86,159,184,
+    252,221,223,251,253,199, 63,241, 49, 26, 82, 63,254,179, 33, 55,
+    124,240,224,221, 87,190,245,161,175,126,165,247, 96, 35, 75,109,
+      0, 85,194,177,227,143, 92, 30,130, 83,241, 94, 89,127,227, 39,
+    215,126,254,229,238,253,  7,149, 62,125,194,194,185,181, 23,190,
+    241, 75,201,102,235, 63,126,179, 18,193,174,126,225,115,245,133,
+    246,189, 55,138,235,111,125,255,135,151, 94,122,241,227,191,254,
+    107, 68,244,200,243,215,110,125,255,135, 39,224, 51,  3,102,  7,
+    127,212, 55,  1,227,237,115, 42, 28,118,251,213,191,185,246,243,
+     47,223,254,254,223, 12,109,188, 47,158, 63,247,194, 55,190, 78,
+     68,175,209, 31,173, 59,158, 34,166,222,253,  7,239,190,242,173,
+    171, 95,248,156,123,147,239,254,222,239, 19,209,181,159,127, 57,
+    113,217,119,127,239,247,143,247,125,130, 55,225,  4, 16,123,188,
+    250,147,158, 12, 76,252,237,223,190,166,141,249,230, 55,255,172,
+    241,253,215,106, 82,214,164,172,145,144, 76,194, 90, 50,214,104,
+    165,194, 40, 10,130,160,215,239,119, 59, 97, 24, 25,182,134, 57,
+    249,215, 18, 91,102, 78,103, 95,194,123,231,216,233, 24,117, 43,
+    232,255,197,214,221,252,154,255,229, 99,159,137,131, 80,  5,  1,
+     94,135, 83,248, 58, 76,201, 95, 66, 16,  9, 34, 41,132, 36, 33,
+    133,240,132,240,132,240,133, 16, 36,136,200, 18,107,102,109,173,
+     98, 27, 89, 19, 25, 19, 88, 19, 26, 19, 90, 19, 90,211,191,112,
+    246, 67, 47,126,204,247,125,223,247, 61,207,175,213,124,223,247,
+    107,181, 90,173,158,210,104, 52,234,141, 70,179,209, 72, 46, 52,
+    234,141, 90,189, 94,175,215,106,181,122,173,230,167, 55,240,124,
+    207,247, 60,233, 73,252,241,  2,243,249, 33,194,107,112,146,251,
+     24, 35,130,243,135,230, 43,185,139, 90,121,245, 38, 41,229, 41,
+    252, 36, 75, 18, 53, 33,241, 58,128,253,252, 53,254,129, 59,224,
+    240,212,165, 60,227,215,243,111,155,190,239,215,235,167,112, 73,
+    167,154,148,203,126,205, 85,216,233,124, 29,166,155,180,248, 64,
+     91, 79,204, 95,208,212,236,126,110,133, 60, 91,111,188,124,225,
+     82,195,243,155,126,237, 23,174,189, 64, 66, 48,219,211,249, 58,
+    124,241,252, 99,201,235,240,141,143,188, 40, 78,229,235,  0, 48,
+    254,107,174,234, 71, 33,106,158,255,209,165, 71, 62,245,212, 51,
+    181,102,211, 26, 29,236,116,140,210,120, 29,130,157,174,209, 26,
+    239,144,233, 36,177,249,242, 23,207,226, 19, 57, 53,239, 24,107,
+     85, 24, 89, 99,165,215, 73,215, 73, 62,149,139, 30, 14,121, 29,
+     52, 22,127, 60,117, 82, 67,254,154,175,183,  6, 51,179,177,214,
+     40,133,215,  1,175,195,212,252, 36,102, 69, 96, 56,254,  8,  0,
+    152, 87, 14,153,191,  6,231,158, 64,  1,  9,192,105, 96,164,163,
+    188,147,210,  1,242, 23,  0, 96,156,197,229, 36,145,179,180, 51,
+      0,  0,112, 60,249, 11, 50,  3,  0,204,148, 17, 36,132,  5,  0,
+    152, 83,124,216, 10,127, 26,231,  2,156, 28, 52, 83,239,174, 25,
+    249,117, 96,252,215, 73,240, 20,151,103, 48, 74,191, 61,113,242,
+     18,217,167, 38, 57, 85, 59,249, 22, 94, 59,205, 54,131,191,230,
+    216, 89,204,196,196, 76,148, 92,216,209, 74, 89,203, 68,129,213,
+    193,  9, 26,148,223,146, 94,211,243,  4, 81, 77,202, 37,191, 46,
+    136,  4,147, 32, 33,  4, 11, 74, 46,192,101,167,190,126, 28,177,
+     18, 65,153, 57, 69,109,185,206,138,173,221,136,195, 29,173, 54,
+     85, 20, 90, 19, 26,115,102,237,172,223,168, 17, 81,123,233, 76,
+    107, 97,129,153,231,127, 78, 73,182,214,110,117,123, 97,191, 79,
+     68, 42,142,187,247,239, 55, 61,175, 41,189,149, 90,125,201,175,
+    173,213, 26, 53,233, 73, 38,153, 78,170,151,186, 12, 34, 59, 37,
+    165, 36,242,215,156,164, 45,102, 75,100,153,183,117,124, 59,236,
+    109,170, 56,242,196,133,199, 30, 91, 57,183,246,177,139, 23,155,
+    237,118,115,161,173,148, 82, 74, 89,107,173,181, 74, 41,173,181,
+    181,115, 63, 37,  3, 51, 75, 41,107,181,154,231,121,201, 53, 97,
+    191,223,239,118, 31,222,127,112,123,123,231,245,141,  7, 77,166,
+     85,191,241,104,179,117,198,175, 75, 33, 36, 11, 41, 72,166,179,
+    132, 34,145, 77,255,173, 59,125,127, 49,226,215, 84,  3,151, 37,
+    238,104,117, 35,232,174,199, 97,115,117,229,234, 39, 95,124,238,
+    226,197,133, 51,203, 81, 20,245,251,253, 94,175,119,239,214, 70,
+    175,215, 11,130, 32,142, 99, 99,140, 49, 70,107,109,172, 97, 59,
+    223,191, 47, 33,132, 16,194,243, 60,223,247,189,140, 70,163,209,
+    108, 54,151, 46,156, 63,127,229,178,239,251,253,157,206,246,198,
+    198,107,183, 63,176, 59, 91,231,234,141,203,173,133, 69,175,230,
+     37, 83, 27, 35,142, 77,215, 83,199,191,156, 26,242,215,172,155,
+    235,118,216,191, 25,116,227, 86,227,185,159,254,248,199,158,120,
+    194,107,212,187,157,206,131,173,173,159,188,251,206,246,246,118,
+    183,219,237,247,251,169,185,230, 95, 88,251, 34,165, 76, 20,214,
+    202,104,183,219, 11,103,150,159,185,248,136, 81,234,225,250,253,
+    239,223,188,213, 54,252,120,179,253,104,163,237,  9,225,145,144,
+     68,176,216, 73,197, 63,200,103, 10, 76,212, 92,111,247,118,110,
+    134,189, 51,143, 94,252,228,207,125,105,229,252,185,157,157,157,
+    219,119,239,220,187,119,111, 99, 99, 99,123,123,187, 31,  4, 42,
+    142, 43, 45, 46,113,210, 63,164,204,172,181,214, 90,247,122, 61,
+     18, 66, 10, 81,175,215,219,237,246,226,210,226,210,210,242,226,
+    242,210,213, 79,188, 24,116, 58,239,221,184,245,206,230,189,199,
+    155,237,167,218,139,158,144,176,216,108,100,179,241, 59,196, 31,
+    242,120,248, 13, 79, 81, 94,204,134,248,131,176,127, 35,232,158,
+    125,246,233,159,255,244, 79, 75,223,223,216,216,248,193, 15,126,
+    112,231,206,157,205,205,205,126,191,175,181,182,156, 29,132, 20,
+    167,239,143,139,243,254,180,204, 97, 20, 69, 81,244,240,225,195,
+     68,100,203,203,203,203,203,203,143, 94,123,214, 42,189,126,227,
+    230,131,237,  7,143, 55, 22, 30,107,166, 89,204, 59, 13,142,159,
+    206, 59, 87, 76, 37,238,248, 71,221,109, 48,190,216,101,153, 54,
+     85,244,126,208,181,231, 86, 63,249,213, 47, 45,173,174,222,189,
+    123,247,198,141, 27,119,239,222,125,248,240, 97, 24,134,121, 63,
+     94,208,240,255,159,222, 23,144, 57,138,162, 56,142,119, 58,157,
+    214,198,198,210,210,210,210,210,210,226,197, 11,102,229,204,187,
+    247,214, 55, 58,155,151,154, 11,103,107, 13, 22, 66,146,148,  8,
+     98, 99, 23,214,148,114,143, 15, 65,205, 78,236,250,113,119,251,
+    150,137, 62,245,229, 47, 61,126,245,169,245,245,245, 87, 95,125,
+    245,198,141, 27, 91, 91, 91, 74,169,164, 78,116,163,  3,187,255,
+     63,109,191, 39,225,252, 87,198, 26,147, 28,202,216,222,222, 94,
+     90, 90, 58,115,230,204,234, 19,151,131,135,219, 63,184,123,239,
+    241,122,235,217,246,114, 77, 18,130,216,113, 36,174,169,224,143,
+    229,153,204, 26,159,254,237,223, 92,255,241, 79,174,191,242,237,
+    121,120, 31,164,177,235,189,160,235, 95,186,248,141,159,125,185,
+    219,235,189,246,218,107,215,175, 95,127,240,224, 65, 24,134,150,
+    237,208,151,184, 20,189,240, 65, 44,191, 52,150, 57, 12, 67,165,
+     84,191,223, 95, 90, 94, 94, 92, 88, 56,123,245,201,245, 59,247,
+    162,238,214,229, 52,136, 73,143, 72,138, 98, 76,255, 76, 81,111,
+    183, 63,255, 15,255,254,234,149,203,255,215,223,255,221,185,177,
+    215, 52,148,230, 31,241, 89,204, 32,159,254,237,223,188,250,197,
+    207, 93,253,226,231,136,104,150, 21,150,213,140,252, 94,208,125,
+     43,238,127,234,203, 95,122,244,169, 39,111,221,190,253,214, 91,
+    111,221,190,125,187,215,235, 25, 99, 40, 57, 75, 70,236,246,226,
+    159,226,121, 35,119,143, 96, 57,198,152,126,191, 31,199,113,208,
+    239, 47, 45, 45,181, 46,156,235,118,186,175,110,108, 62,211, 90,
+    186,210, 90,168,145,244, 72,204, 96, 45, 89,111,183,191,242,191,
+    254,207,171, 79, 92,158,219,100, 54, 87,249,107,  6,229, 21,247,
+    250,245,133,246,103,126,231,183,102, 86, 97,156, 14, 73,229, 55,
+    186,219,235, 62,125,245, 87,126, 89,248,254,235, 63,250,209,219,
+    111,191,189,177,177, 17,171,152, 56,173,111,118,243,148,115, 10,
+    224,124,255,189, 25, 22, 42,143,120, 47, 37, 31,105,173,187,221,
+    110, 28,199, 11,139,139,173,102,179,113,126,237,237,  7,155,161,
+    209,207, 46,156,169, 73,233, 39,115,176,204, 82, 45,249,233,223,
+    249,205, 89,150,215,144,152,197,196, 83,122,249, 78,148,191,114,
+    121,125,243,159,254,179,149, 43,151, 62,243, 59,191, 53,155, 10,
+    203, 27, 94,127,215,217,234, 45,182,126,241,151,255,171,173,135,
+     15,223,252,209,143,222,123,239,189, 78,167, 83, 25, 52, 63,232,
+    169, 67, 39,175,147, 89,101,238,171, 30, 33,146,238,190, 49, 70,
+     43,213,106,181,234,231,215,110,111,108,169,238,195,231, 23,207,
+    176,240,124, 73, 30,207,138,194, 62,253,219,191,121,233,165, 23,
+    183,222,191, 73, 68,115, 17,193,166,251, 71, 81,142, 99,175,102,
+    226,239,186, 43,175,173,247,111, 94,127,229,219,223,249,151,255,
+    138,136, 62,243, 59,191,245,212, 23, 62, 59,155,242,106, 60,117,
+    229,107,191,246, 43,247,214,215,127,248,195, 31,190,253,246,219,
+     59,157,157, 68, 94, 34, 29,121, 46,132, 24, 82,220,136,242,  6,
+     46, 36, 78,226,129, 53,177,219,211, 77,158,243, 48,163, 13,219,
+     32, 41, 39,123,189,158, 97,246,215, 86,239,  9,243, 70,119, 59,
+    102,163,173, 53, 52, 19,103,139, 38,239,225,173,247,111,126,243,
+    127,251,103,113,191, 79,224,216,242,215,108,213, 34, 21,121, 37,
+     87, 38,177,107,166, 82,152, 43,175,250, 83,151, 63,241,197,207,
+     95,191,126,253,141, 55,222,184,123,247,110, 28,151,107,198,236,
+    211, 36,136,168,114,216,113,247, 15, 90,105,126,153,211,195, 30,
+    209,169,220, 62, 52,198,  4, 97, 96,173,109, 52,155,220,106,222,
+    233,246,185,251,240,185,133, 21, 34, 34, 41,167,155,194,146,247,
+    112,247,254,  3, 87, 94, 47,252,242, 47,173,191,241,166,234,  7,
+    249,187,122,198, 75,201, 57,170, 31,121, 70, 84, 54, 84, 94, 51,
+    168,176,170,188,190,240,249,247,222,123,239, 71,175,191,190,126,
+    239,158, 78,214,142, 22, 34,113,147,235,172,234,  8,251, 97,122,
+     58,  1, 19, 77,140,179, 96,220,101,155,244,111,131,229, 40,138,
+    152,185, 94,175,115,187,121,167, 31, 80,111,250, 10,203,223,195,
+    127,249,207,255,133,155,188, 62,250,141,175,211, 55,190,158, 92,
+    190,245,253, 31, 94,127,229,219,183,190,255,195,185,248, 45,241,
+     76,249,107,102, 63, 31,123,200,107,166, 20,150, 30,109, 76,228,
+    245,228,229, 79,124,225,243,239,223,184,241,250,235,175,223, 95,
+     95,215,217, 92, 93,185,155, 74,249,107,132,240,133,145, 76,123,
+    188,  8,213,235,133, 32, 34,165, 20, 17, 53, 26, 13,221,108,220,
+    238,  5,212,123,248,252,226, 10, 89, 18, 82,202,137, 39,216,161,
+    239,225, 31,252,235, 63, 88, 56,183,182,242,196,101, 34,186,240,
+    220,179,171, 87, 46, 95,122,233,197,164, 53,246,221,223,251,253,
+    153,140, 99,115,149,191,120,  6,236,182,175,188,102, 73, 97,108,
+    153,223,232,110,119, 23,154, 63,251,197,207,223,188,121,243,141,
+    215, 95,191,127,255,190,214,122, 80, 91, 67, 69, 54, 52,124,141,
+    148,188,230,103, 38, 25,206,158,213,209, 83,216,224,207, 75,127,
+      9,152,181,214, 66,  8,207,247,  2, 79,220,137, 67,191,183,243,
+    161,133,101,193,194,159,236,184,176,221,222,195, 91,239,223,220,
+    122,255,166,155,182,158,250,194,103,175,125,237,229,213, 39, 46,
+    255,252, 63,249,199,223,249,151,255,106, 46,134, 55, 30, 55,242,
+    128,166,154,167,228, 85, 81,216, 20,219,249,201, 32,213,247,130,
+    238,186, 71, 95,254, 47,191,126,247,238,221, 55,223,124,115,125,
+    125, 61,249,  8,229,159, 43,145,181,158, 93, 97,237,209,200, 39,
+    218,171,175,237, 54,176,211, 65, 78, 98,166,201,159,252,136, 79,
+    106,239, 23, 99,240,234,202,183,204,172,148, 54,218,212,235,245,
+    190,239,221,138,250, 55,130,158,178,214,112, 58, 73,228,100,222,
+     27,235, 63,254,  9, 17,213, 23,218, 43, 87, 46,237,251, 30,254,
+    227,127,252, 79,254,238, 15,255, 40,121, 27, 95,120,254, 67,240,
+    151,247, 15,254,193,255, 96,153,175, 95,191,238,221, 89,247,132,
+    244,132,144, 66, 72, 34,145,142, 80,178,214,104,163,181, 86, 74,
+    197,177, 49,218,230,115, 36, 20, 19, 25, 79,129, 79,252,198,175,
+     62,251,242,151, 18,121, 45,156, 91, 91,126,236,226,206,157,187,
+    123,108,127,237,107, 47,111,188,115,253,193,219,239, 38, 33,188,
+    247, 96,227,225,141, 91, 19,203, 20,150,233,255,103,239, 93, 99,
+     44,203,174,251,190,181,214, 62,231, 62,235,253,234,119,247, 76,
+     15,103,122,154, 28,146,195,225,155,163, 33,101, 82, 50, 73, 88,
+     68, 98,138,162, 13,136,177,101, 32,118,190,216,128, 19,125, 72,
+     32, 27, 72, 62,200,130,191, 40,118,144,  0, 70,164,  4,145,  0,
+     41,128,  5,135, 66, 64, 57,162,  2, 83,136, 69, 81, 36, 45,241,
+     77,113,166,103, 56,211,211,211,239,238,170,234,122,221,215, 57,
+    103,175,149, 15,251,236,115,246, 62,231,220,170,234, 71,245,220,
+    234, 62,187,171,171,110,221,186, 85,117,235,220,123,126,247,255,
+    255,239,181,215,222, 74,162, 11,189,173,143,125,238,179,163, 56,
+    126,229,149, 87,174, 93,187,150, 36, 73,134,164, 34,182,170, 78,
+    210,253,158,216,229, 65, 68,120,104,  6, 56,255,247,196,242, 46,
+     72, 43, 95,237,125,234, 92, 84, 74,  9, 72,130,176, 19,141, 22,
+    194,102,131,136,178, 25,222,131,127,122,108,188,117,165,183,186,
+     86,126, 90,190,240,203,191,116,254,115,159,233, 46, 47,173,156,
+     63,215, 93, 90, 68,196,225,230, 22,  0,220,122,249, 85,115,251,
+    149,103,159,121,243,235,223,212,113,242, 54, 88,117,115,252,236,
+     33, 37, 64, 66, 36,123,192,108, 84,  2, 44,162, 69, 18,251, 22,
+     11,107,150,120,170,179,120,244, 40,217,161,148,121,159,143, 32,
+      8, 84,144, 14,243,137,243, 69,251, 13,102, 32,  5,227, 78, 57,
+    156,108,241,181,114,254, 92,166,188, 62,243,235,255, 60,234,245,
+    119,207, 53, 95,248,210, 23,111,190,124,225, 79,127,227,127, 76,
+     95,187,158,125,230,225,200,239,172, 78,245, 98,127,231, 29, 31,
+    254, 64,208,106,189,242,242,203,215,174, 93, 51,249, 75,238, 19,
+    253,229,141,178,255,240,107,247,115,236, 16,  6, 99, 89, 93,189,
+    236,186,  9,137,136,236,106, 16,199, 95,237,191, 80,136,136,214,
+     58,  8,130, 88, 36, 82,112,121,208,235,170,128,  8, 17,  5, 31,
+     86,150, 95,153,111,204,157, 57,117,228,252,185, 35,231,207,101,
+     55, 51, 79,242, 87,255,223, 63,189,248,245,111, 26,222, 61,243,
+    153, 79,253,248,203,127, 52, 49,214,255,109,120,174, 29,214,250,
+    213,175,254,243, 95,119, 63,109,116, 59,230,194, 39,127,237,191,
+    113, 31,242, 31,253,225, 87, 10, 15,240,197,175,127,243,225,  6,
+      7,194, 32,175,236,108,242,177,165, 51,231,158,185,120,241,226,
+    149, 43, 87,134,195, 81,118, 30,185,228, 42, 99,203, 61,135,177,
+     58, 86,222,223, 73,123,  8,  7,142,167,216,126,206,149,221,243,
+     47,247, 19, 17, 17,145, 48, 12, 99,136,175,199,163,160,191,253,
+     76,119,134,  4, 17, 65,193, 67, 74, 14,199, 69,180, 95,251,141,
+    223,236, 46, 45,118,151, 23, 87,158,125,230,200,249,115,102, 97,
+    220,149,239,124,255,199,127,248, 71, 39,223,255,252,147, 63,243,
+    209,137,225, 87,101,226, 36, 53,191, 14,241, 48,206,113, 59,137,
+    175, 36,163, 79,191,244, 51,183,110,221, 50, 21,246,  0, 98,242,
+    151, 74,138,249, 32,219,243,252, 41,157,167,143,214,100,164,165,
+     88,177,136, 68, 42, 20, 25,220,131,254, 50,151,153, 57,  8,  2,
+     34, 26,  5,112,109,212, 63,210,104,205,135, 77, 34,194,116,129,
+     36, 60,124,132,165,233,216,203,175,230, 47,210,157,246,147, 47,
+    125,236,185,191,253, 11,198, 60, 70,189,254,212,242,210,252,153,
+     83,135,100, 46, 82, 38,135, 95,147, 94,109,244,181,223,248,205,
+    149,243,207,188,251,111,127,238,237,126,196, 82,231,248,236, 11,
+    207, 15, 71,163, 75,111,189,181,182,182,198,204,230,  4,172,210,
+     95,233,177,245,103,202,220,  3,190,  7,173, 42,216,117,  8,113,
+     38,206,255,236,143,144,189,247,180,148,221,255,232,113,250,203,
+     92,212, 90, 43,165, 18,145, 72,209,213, 97,127, 42,  8,149,228,
+     91,180,193, 67, 71, 88,212, 43,214,223, 71,253,193,133, 63,249,
+    218, 27, 95,255,139, 23,126,249,139,166, 61,  1,  0,156,120,255,
+    123,223,102,126,189,173, 69,172,193, 35,197,173,201, 58,  9, 69,
+      4,174, 13,251,219,221,230,243,231,158,121,235,173,183,178, 58,
+     85, 87, 83,248, 20,203,213,214, 56,  9,182, 15, 49,113, 15,216,
+    194,135, 15,168,253, 68, 96, 62,211, 65,  0,157,  4,208, 19, 97,
+     34,178, 79, 35, 93,125,196,204, 81, 70, 36,162, 36,128,155,209,
+    104,118, 52, 56,217,234,144, 32,  1, 10,202,195, 71, 88, 22,137,
+     20, 70,220, 31,124,251,183,127, 23,  0, 12,194,230, 79,159,154,
+    236,179,224, 96,185, 17,220,223,115, 79,106,162,141, 59, 72, 34,
+    192, 34,111, 13,118,158,123,241,111,108,109,109, 93,189,122,117,
+     48, 24,164, 82,  2,  0, 68,112,223,201,253,216, 19,178,234,226,
+    158,130,108, 18, 76,225,254, 36,152, 20, 89, 15, 32,217, 18,  5,
+    188,187, 39,223,126,184,111, 92,100,146, 36,163,128,110,140,250,
+     71, 26, 45,133,164, 64, 16, 30,106,159,176, 12, 97,187,220,230,
+    219,191,253,187,243,103, 78,205,159, 57, 21,118,218,135,194, 48,
+    202,164,240,107, 82,135, 41,135,105,116,188, 87,173,238,226,226,
+    219, 84, 38, 35,  2,112,117,212,167,197,249,233,133,249,139, 23,
+     47,174,175,175,107,173, 77,236,101, 94,235, 43,201, 85,118,142,
+    165, 83,199,171,100,242, 47,140,129, 24, 28,142, 18, 86,113,208,
+     85,168,224,205,161,159,246,252,151,241, 57, 88,245,193,220, 93,
+    164,186,143,  2, 17,105,165, 54,147,248,198,104,112,178,213, 85,
+     10,201,136,191,135,120,252, 12,194,230,119,173,  8,251,238,239,
+    255,193,167,126,237, 87,187, 75,139, 19,229, 57, 30,178,113, 11,
+    238,230,119, 76,202,130,199,202,241,169, 95,251,213,242,149, 89,
+     35,195,135, 79, 47, 35,190,206,127,244, 19,219,219,219,183,110,
+    221, 26, 69, 81, 10,175,130,254,202,133,  6, 58,207,  2,207, 78,
+    142,145, 84,233,  5,255,195, 46, 12,115,180, 15, 78, 34,183,208,
+    119,143,230,192, 32,  8, 32, 58, 23, 50,  5, 54, 86,130,149,130,
+    252,221,167, 34,139,  7,140,136, 68, 36, 14,212,205,104,112,164,
+    217, 14,  4, 85, 90,251,251, 80,143,202,197,175,127,243,226,174,
+     55,184,245,242,171,111,252,217, 95,196,143,119,155,138,224,158,
+    158,106,147, 53,126,244,135, 95, 41, 92,211,187,189,118, 11, 94,
+    253, 17,124,165,112,229,195, 75,190,  0,110, 69,  3,181, 56, 63,
+    179,184,240,230,155,111,110,110,109,101,138, 32,211, 95,105,106,
+      3,238, 82,109, 48,242,162,100, 86, 42,206, 55,239,195, 24,148,
+     77,104,119,228,106, 55, 89,110,138,103,170, 39, 48,191, 32,146,
+    191,  6,236,230, 30,171,181,210, 46,115, 31,133,227, 75, 68,137,
+    162,205, 36, 94,141,134,199,154, 29,165,132,  0,  5,100,210, 14,
+    167,  9,194,106,126, 29, 22, 82, 85,143,202, 18,152,222,234,154,
+     59,247,252,118,136,175,222,153,247,125,168,215,235,173,174,174,
+     70,163, 81,230, 25,119,137,189,118, 49,121,101, 85,229,128, 43,
+    189,  8,152,214, 50, 49,155,178,217, 67, 31, 78,102,127, 99, 86,
+    143, 15, 25,188,140,250, 42,105,176,177, 85, 20, 57, 45, 61, 10,
+    149, 25,102, 44,164, 82, 42, 86,250,102, 52, 88,106,180,  2, 73,
+     83,176,122,153,252,164,233,156,186,254,235,193, 63,114,  2,208,
+    211,201, 78, 67, 45, 29, 59,122,229,202,149,205,205, 45, 95, 56,
+     64, 86, 60, 81,157,212,  8,  8, 72,105,241, 80,122,214,185, 18,
+     43,255, 33,  0,172, 53,107,157,232, 68,107,205,204,194, 98, 55,
+    254, 56,172, 59,122, 34, 66,186,242,201, 89, 95,146, 47,133,178,
+    127,152,236, 42,190, 50,167,185,191, 32, 44, 63,186, 68, 20,  7,
+    234,206, 48,234,233,164, 65,196,136,100,148, 96,253, 20, 63,196,
+    254,177, 30,251,146, 95,114,101,216, 59,254,212, 19,195,225,112,
+    109,109, 61,138, 70, 89,193,151, 93,245,130, 48, 62,176,207, 88,
+     85, 25,123,121,126, 17, 80, 64, 88,235, 40,138,162, 40, 66,196,
+    169,110,119,170,219, 13,195, 16,  4, 26,237,150,  9,215, 36,171,
+     65,240, 47, 76,196,235,179,184,169,125,118,127, 69,  0,132, 89,
+     68,226,225,136,133,147, 36, 25, 70, 81, 20, 69, 74,169, 48, 12,
+    149, 82,184,143,229,137, 34, 96,194,198,177, 49,152,163,197,202,
+    194,214,184,200,155,163,193,116, 16,  6, 34, 10,165,210,231,214,
+     99, 55,211, 94,243,235,208,209,139, 69,174,141,250,239, 63,125,
+    122,123,123,123,123,123,155, 89,178, 34,  0,183, 78,181, 98,170,
+    177,232, 31, 43, 52, 66,246, 94,  4,152,117, 20, 69,195,225,176,
+    213,106,157, 62,121,170,213,237,184,132, 18,145,  2, 29, 38, 10,
+     97, 30,203,220,187,237, 30, 17, 17,213,  8,145, 25,149, 82, 97,
+    200,204, 58,142, 71,163,136, 20,101, 20, 27,107, 30,109,116,182,
+     87, 10, 86, 93,144, 98, 22, 23, 39,138,214,226,209, 19,204, 26,
+    137, 69,168,182,144,135, 83,127,213,101, 94,251, 60, 76, 34,  0,
+    155, 73, 28, 76, 79,169, 70,184,121,235,230,112, 56, 44,193, 75,
+    252, 58,213, 74,114,141, 21,  8,217, 15, 73,146,100, 56, 24, 32,
+    209,153, 83,167,219,221, 14,  0,204,119,167,206, 46,173,156, 93,
+     90,158,111,182, 69,235, 51,115, 11,200, 44,  6,168,249,212,246,
+      4, 78, 34, 75, 81,138, 73,202, 49, 22,254,233,218,109,  0, 92,
+    237,239, 92,184,117,227,213, 91, 55,110,111,109,134,141,144,147,
+     36, 26,141,194, 70, 35,  8,  2,159, 40, 57,176,118,143,219,199,
+     87,165,184, 22, 18,129,104,  4,122, 43,137,155,164,178,206, 43,
+     88, 75,176,137,  9,196,106,253,245,224, 17,182, 22, 13,231,143,
+    175,196,113,178,179,179,163,117,226,152,199, 66,242,181, 27,185,
+    198,158, 88,  0, 34, 18, 71,113,127,208,159,157,153, 57,114,244,
+     40,  0, 28,153,153,125,207,201,211,159,124,234, 89,142, 34, 29,
+     69, 58,142,133,113,120,103, 67,180, 78, 85,152,100,219,116,139,
+    147,178,165,191,216, 45,105,127,248,142, 35,223, 64,188,200, 47,
+     17,129,147, 24,  2,192,201,169,249,247, 77, 47,200,211,207,253,
+     63,175,191,252,157,171,111, 93,189,179,206,146,182,129, 14,195,
+    176,236, 37,247, 76,241, 43, 41,230, 30,106,147,226, 19, 81, 66,
+    184, 30,143, 22,194, 38,139, 72,157,128, 29, 74,253, 85,203,175,
+    187, 48,143,112, 43, 26, 62,121,252, 88, 20,141,134,195,161, 61,
+    145, 10,201, 87, 21,185, 82,141,180,139,191, 49,243,111, 18,199,
+    113,191,223, 59,114,228,200,204,236, 44,  0,188,244,204,249, 95,
+     56,255,110, 61, 24,142,238,108,232, 40,210, 73,194,137, 22,214,
+    194, 34,194,192,226, 58, 71,215,179, 77,226,225,203,  8,109, 61,
+    112,122, 89,196, 28,154,159, 95, 57,249,243,199,159,248,119,175,
+    254,248,107, 63,125, 69,107,205,137,198, 46,  5,129, 74,219, 62,
+    143,115,142, 99,143,237, 30, 41,152, 82, 42, 65,220, 78,226,  4,
+    152,133, 88,128, 30,191,221, 81, 38, 67,105,213,250,235,224, 31,
+     56,227, 47, 54,226,104,122, 97,254,214,173, 91,195,225,104, 76,
+    242, 85, 69,174,252, 52,218,109,201, 94,146, 36,131,193, 96,101,
+    101,101,122,102, 38, 84,234, 75, 31,121,233,217,185,197,209,198,
+    102, 60, 24,234, 56, 22, 59,249,232,229, 95,238,251,137,229,151,
+    248,255, 37,237,146, 41, 44,194, 44,194,233,  5,  0, 68,252,207,
+     79, 62,117,118,106,230,127,251,238,183,134,113,196,219, 60, 61,
+     59,227, 54, 43, 44, 88, 60, 83, 58,134,121,153, 89,181,255, 43,
+    167, 96, 70,127,177,162, 94, 28,107, 17,157,234,216, 90,131, 29,
+    190,252,171, 30,251, 37,216, 90, 52,154, 91, 94, 50,123, 62,199,
+    113,140, 56, 54,249,170, 34,215, 30,214, 38, 73,146,225,112, 56,
+     53, 53, 53, 53, 51, 19, 42,245, 95,127,234,179,243,168,  6, 27,
+    155,201,112,196, 73, 98, 38,236, 64,114,120, 21, 17, 54,129,228,
+    170, 92, 36,231,136, 47,251,102,103, 70,152, 89, 24,162,232,157,
+    173,233,255,246,131, 31,255,245,111,254,233, 40,142,130,126,191,
+     59, 53,101,187, 66,123,250,200, 78, 65,238,171,111,154,115,204,
+    243, 20, 12, 17, 53,225,102, 28,181, 72,113,138, 47,168,  9, 54,
+     33, 10, 44, 56,  4,247,241, 80, 61,114,235,241,104,230,216,177,
+     56,142, 77, 58,  3, 78,239,151, 93, 74, 37,246,145,206,160,  8,
+    199,113,204,154,151,150,150, 64,228,151, 63,252, 51,115, 64,195,
+    173,237,100, 56,148, 68, 75, 42, 88,192, 65, 24, 27,231, 85,133,
+    176,137,124, 32,197,227,186,135, 48,102, 96,  6, 97, 16,  6,102,
+    102, 22,145, 37,162, 95,121,215, 11,191,253,195,191,236,245,251,
+    205, 86,171,209,104, 64,177,135,199, 56,120, 57, 20, 43,177,168,
+     92,139,175,  9,183,146,120,185,209,146,250,137,239,153,244, 67,
+    161,191,164,102,215,126,211, 27, 17,216, 74,226,197,169, 41, 91,
+    144, 85, 76,190,236, 52,160,236,115, 61,181,139,176, 36,209,209,
+    104,180,178,188,196, 34, 31,126,242,169,103,103, 23, 70,155, 91,
+    201, 96,200, 73,146, 85, 27,216,240,200,120, 46, 25, 27,222, 79,
+    208,163, 88, 85,133, 42,249,  1, 21, 71, 75,166,115,169, 44,192,
+     44,204, 73,162,207,119,103,127,230,228, 19,127,126,229,205,157,
+    173,173,197,229,229,194, 26,210,253, 47, 90, 31, 23,228, 27, 11,
+    169,  1,250, 58, 97, 16, 78,183,246,168, 35,176, 73,  1, 88,112,
+     79,247,187, 30,227, 14,141, 36,194, 42,  8,146, 36,201,187, 77,
+     32,250, 86,177,234,164,218,205,148, 32, 34, 48,115, 18,199,164,
+     84,216,108, 30,153,153,253,252,115,239,139,182,123,241, 96,200,
+    113, 92,168,249,202,135, 99, 39, 11,252,154,156,149, 69,  8,  0,
+    192, 69,132,185, 19,145,204, 96, 18, 61, 22, 72,103, 36, 88, 88,
+     18,150, 68, 88,115,242,217, 19, 79,190,186,190,122,171,191, 19,
+     69, 81,179,217,130,116,186,183,170,202, 97,111,231, 87,177,206,
+    148,136, 52, 66, 98, 66, 69,111,218,182,126,182,191,253,116,168,
+    243,175,  7,154,126,  1,108,196,209,185,133,249,245,245,117,219,
+    170,208,150,218,143,179,138,105,175,  9, 25,103,103,172,121,148,
+     56, 73,102,166,166,153,249,189, 39, 78,225, 48,138,251,125,142,
+     99, 97,206,  3,163, 76,  4, 50,155,201,199, 84, 48, 88, 59, 86,
+    244,104,147,115, 14, 56, 34,172,184,189, 37,179,137,189,204,155,
+    185,108,166, 87, 53,115, 34,146, 48,159,159, 95,186,213,223,233,
+    109,111,119, 58, 29,131,238,226,161, 46, 31,228,241, 32,115, 31,
+    168, 84,127, 33,166,250,203,217,118,171, 30, 80, 33,155, 31, 54,
+    217,131,123,189,175,245,168,118, 60,  0,192,204,163, 81,100,246,
+    118, 44, 39, 95,  0,126,201, 83,133, 34,171, 56,193,152,153,153,
+    195,102,163, 25,  4, 47,157,121, 42,222,233,235, 81,196,  6,145,
+     78,160, 38, 41,181,204,217,110,150, 64,166,225,183, 56,246,117,
+     50,  7, 22, 67,176,244,104,102,134, 81,152, 57,101, 86,186, 49,
+     87,204, 58, 17,121, 97, 97,229, 91, 55,174, 12,163,168,209,104,
+     48,179,217,219,169, 64,174, 42,217,187, 27,200,242,  5,167,144,
+    238,188,201,217,202, 38,172, 53,216,164,140,224,  0,206,100, 17,
+      1, 51,223,204, 34,242,216,160, 79,139,172, 70,195,238,252, 92,
+    146,232, 36,142,157,149,195,232,157, 75,166,  1,223,190, 26,113,
+    165,223, 45, 44, 90,235, 64,  5,204,252,212,210,145,134,150,225,
+    112,164,227,216,246,243,179, 39,187,131, 47, 73, 67,110,150,108,
+    242, 46,151, 52, 19, 79, 46,183,139,182,145,147, 58,229, 87, 34,
+    108,156, 99,204,156, 88,253, 21,  0,156,156,154,121,125,115, 61,
+    138,162, 86,171,149,196,201,174, 47, 15, 99,239, 66,133,248,181,
+    160,138, 17, 86,163, 17, 54, 80,139,132, 66,234, 81, 15,192, 12,
+    179,  3, 36,101,122,127, 76, 42,174,131,123,133,212,216,193,  2,
+      3,157,108, 37, 81, 79, 39, 49,243,227,163,218, 24,100, 43,137,
+    135, 24,198,113,172, 89, 99,169,131,251,190,207,165,138,151,  4,
+    102, 14,149,210, 90,159,156,157, 75,134, 67, 29, 69,146,232,130,
+    211, 18, 48,188,178,240, 98,102,230,108,253, 54, 64,113, 47,144,
+    201,129,151,140, 33, 90,206, 47,102,102,209, 41,191, 56,227, 87,
+    156,126, 42, 75,173,246,235,155, 48,236,247,167,167,167,205,204,
+    239,125,244,105,244, 94,116,204,131, 54, 98,189, 26, 13, 35,214,
+      1, 81,128, 72,240,200,243, 11, 66,162,174, 10,103,130,176,165,
+    148,154, 84,132,  5, 15,220, 47,198,172, 87,163,225,213, 81,127,
+     53, 26,198, 34,143,143,204, 22,128,132, 57, 12,167,205, 22,244,
+    213,103,195,189,255,116, 33,165,180,214, 79,206, 47,154, 21, 66,
+    172, 53,218,179,223, 13,239,141,236, 98, 35, 88,220,249,187, 98,
+    224, 52,113, 10, 12, 61,243,102, 67, 61, 22, 96,214,194,  9,139,
+    150, 92,127, 37,204,177,131,179,149, 86, 27,  0,122,189,254,153,
+     78,199, 76,254, 62, 56, 33,  2,134, 95,215, 71,131,213,120, 68,
+    153,159,124,212,  7, 33,206,  4,225, 59,187,115, 10,145,198, 41,
+    206,183,219, 72,  7,247,123,143, 74, 39, 66, 34,210,211,201, 27,
+    131,157,190, 78,224, 49, 27,  2, 50,111,211,174, 61, 58, 21,200,
+    126,206,104, 55,139, 65,102, 22,173, 37, 73, 52, 19,199,137,104,
+    237,224,200, 52,156, 73,165,151,136, 88,126,177,228,107,113, 96,
+     66, 59,128, 99,229,197, 92,127, 89,114, 89,253, 37,146,100,228,
+    202,174,209,108, 94, 42,218,237,118,175,215, 43, 68, 96,251,191,
+      3,227, 70, 34, 50,210,  9,242,227,149,122,221,138,134, 91, 73,
+    252,159,173,156, 38,162,201,220,171, 39,184,111,209, 85,220,107,
+    151, 16, 59, 42,120, 12,225,229,158, 11,105,104,176,203,241,196,
+    187,251,161,136,200,137, 22, 17,157, 36, 76,196, 73, 34, 70,127,
+    101,224,116,235, 38,172,225, 18,225,172,151,131, 76, 98,241,215,
+    238, 79, 38, 83, 53, 33,137,112, 34,172,173,236,178,239, 83,138,
+    105,243, 37,102,115,156,130, 32, 80,164,118,133,226,221, 65, 21,
+      1, 31,231,134, 19,183,162,225,212,220,108, 52, 24, 38,209,232,
+    238, 14,168, 60, 56, 79,247,192,243, 10,121, 31,104,  0,  0, 32,
+      0, 73, 68, 65, 84,175, 93,238, 89,131,104,185,209, 90,  8,155,
+    235,241,232, 49, 69,152, 25,100,170, 87,203,143,156,220,213, 73,
+    132,  0,130, 68,136, 17,107,102,224, 68, 51, 37,150, 95, 54,248,
+    246,249, 37, 25,190,196,246, 96,157,232, 24,210,187,119,146,  9,
+     47,  0, 22, 51,207,232, 73, 45, 39,  5, 19,  3, 47,227, 43,205,
+      1, 11,130,  0, 41,107, 71, 33,119,139, 44, 44,  1,236,225,109,
+    192, 61,145,227,137,185,133,102,183, 27, 71,209, 61, 55, 41, 57,
+    208,167, 94, 80,254, 77,114,127,247, 38, 64,154,110,168, 47,156,
+    121,230,175,  7, 91,170,216,158,233, 81, 30, 44,178, 57, 28,220,
+      1, 38,164,188,163,139, 61,143,156, 70, 49,120,183,103, 20, 41,
+     82, 65,192,131,129, 22, 97,173, 89,136, 19,109,252, 35, 58,221,
+    178,178,185, 71,157,190, 29,142, 54,248,121,113,173, 83, 86, 98,
+     58,150,153, 63,196,211, 92,182,236,203,188,103,225,236, 54,230,
+    149,163,217,108, 18, 17,185,125, 87,239,150, 95, 88,241,106, 52,
+    213,104, 30,233, 76,205,180, 90, 13, 21,  4, 68,244, 56, 60,171,
+     17, 17,241,195, 39,206,112,127, 96,158,108,111, 51,171,238, 86,
+    127,201,125,252,229, 13,162,143, 44, 29, 11,155, 77,116, 27,155,
+     60,202,201, 23,104,214, 87,182,183,190,183,189,102,186,215, 35,
+     82,126,  8,241,222,142,102,122,150, 16, 82, 16,  4,  0,144, 36,
+    201,197,181,219,167, 22,143, 49,107, 73,146, 60,249,178,213, 73,
+     12,162, 37, 69, 24,231, 11, 32,139,143, 41,190,205,207,186,234,
+     95, 47, 96,215,107,218,106,144,212, 60, 90,231,232,164, 96,162,
+    133,179,191, 81, 64, 54,162, 17,  0, 44, 46, 46, 41, 82, 32,247,
+    214, 38,181,162, 28,204,236, 26,  2,  0,221, 48,124,106,118,254,
+    196,244,108, 39,108,180,148, 82,164, 30,125,128, 33,128, 72,188,
+    211, 27,245,251, 58,153,208, 56, 40, 40, 61,149,240, 62,159,207,
+      2,  2,154,147,209,136,227,100,168,122,143,137,254, 18,  0,205,
+     50, 24,246,178,149, 67, 68,232,148,103,221, 35,196,236,206,207,
+     24,  4,  1, 41,165,135,195,215, 87,111,189,180,120, 76, 51,107,
+    173,109,139,153,244,189, 89,160,103,245, 23,179,  5,129, 83,120,
+     54,225,177, 87, 90,172,102,144,148,133, 95, 58,115,139,105,224,
+    101,213,165,243, 71,109, 68, 67,  0, 56,122,236,104,156,196,137,
+     78,238,157, 95, 85,171, 32, 69, 68, 39,122,184,211,235,107, 16,
+     21,196,164,212, 99, 33,192,192, 76,  4,177,214,247, 17, 64,200,
+    195,228, 87,245,239,151,187, 71,152,137, 96,224,113, 10,241,181,
+     72,155,101,176,189,109,214, 60,102,219,125,229,235, 98,228,174,
+     33,150,109,229, 17,168,160,213,110,111,109,111,191,124,227, 90,
+    252,236,123, 18,128, 88,242,130,112,247,132, 55, 20,211, 86,152,
+    136,215,111,117,130,225, 37,158,254,202,254, 16,167,108, 34, 39,
+    151,159,153,137,  0,172,141,  6,  0,112,250,204,153,181,181, 53,
+    179,242,225,238,213,198,152,125,213,  0,152,153, 52, 55, 89,146,
+     81, 20, 43,141, 68,252,120, 47,224,158,156,229,  7,245,250,199,
+      7, 57, 66, 36, 78, 91,217,100,237,234,161,160,194,246,120,149,
+    194,234,147, 74,  5,170,211,233, 52,130,112,117,103,251, 27, 87,
+     47,189,119,106, 62, 78, 51,122,123,182,139, 48,  0, 91, 75,101,
+    206,127, 55,252, 42, 76, 36,200,  4,182, 47,116,244, 87,166, 37,
+    179,153, 71, 87,115, 21, 14,219, 91,189,237,126,146, 44,206, 47,
+    116, 58,221, 43, 87,174,228,107,230,247,119,144,203,226, 43,123,
+    217,128,180,158, 78, 76, 49, 58,214,173,239, 39, 34,245,170,249,
+    117, 48,113,  1,  2,116,131, 96,184,211, 35, 34, 69,202, 84,142,
+    230, 91,172, 34, 96,165, 10, 18,144,180,214,162,100, 97,242,109,
+     31, 41, 12, 27,211, 51,211,107,235,235,127,254,214,197,119,191,
+    103, 41, 65,212,206, 36,163, 43,190, 28,126, 57,251,118, 72, 85,
+    226, 52, 73, 39, 64, 33,249,202,140,112, 82, 69, 46,112,130,255,
+    183,122,219,  0,240,222,231,223,219,239,247,134,131,161, 51,249,
+    136,222, 77, 81,118, 89, 33,143, 37,120, 33,166,149,255,196,210,
+    164, 28, 94, 53,194, 38,135,111, 53,191, 30, 40,193, 16, 91,164,
+    162,225,176, 57, 61, 21,132, 65,146, 36, 40,233,190,243,226,189,
+    250, 75, 73,139, 75, 94, 58,230,157, 34, 89, 35, 80,  8,195, 96,
+     97,113,113,107,107,235,226,218,237, 63,185,244,211,151,150,142,
+    199,144,106,176,  2,182,236,101,167,239,132, 84,164,248,147, 20,
+    179,100, 73,133,184,138,210, 53,140,133,166, 91, 25,188, 94,222,
+    188,179, 54, 26,180, 91,173,119,190,235, 93, 23, 46,188,106,246,
+    193, 28,243, 26, 81,121,132, 11,230,209,219, 32, 24,128, 89, 51,
+      1,132, 68,  4,104,227,252,154, 96,247,196, 45,121, 27,249, 85,
+    119,159,216,159,254,154, 86,225, 86,175,215,153,155, 13,195,144,
+    211, 14, 92,  6, 95,152, 46, 39, 46, 73, 48, 41, 79,217,231, 39,
+     88,126, 58,153,237, 14,143, 30, 59,118,249,242,229,175,189,241,
+    234,217,153,185,  5, 84,166,158,192,203,191, 28,132,101, 68, 40,
+     71, 96, 19, 37,189,178,254,176,118, 46, 66, 24, 64, 11,251,162,
+     75,  4,176,112,247, 55,226,232,194,214, 58,  0,124,254, 23,127,
+    113,115, 99,115,107,107,147,133,203,221, 62, 10,237,192,198,111,
+    158,230,110, 82,135,136,144, 36,162,181, 38,145,182, 10,208, 86,
+    178, 98,205,165,137, 57,  6,193,110,119, 76,118, 69, 88, 77,180,
+     42,132,205,135,141,155, 91,219,116,138,  2, 21, 68, 24,217,189,
+    184, 32,119,145,101,  9, 86, 37,228,202,219, 62,138, 72, 16,  4,
+    179,179,115, 59, 91,219,119, 54, 55,254,240,181,159,252,210, 59,
+    222,217, 64,176,179,141, 69,  9,230,216, 49,207, 62, 74, 21, 60,
+    222,246,215,231, 66,254,229,116, 44,243,247, 11,112,198, 64,235,
+    239,172,221,  4,128,231,223,243,222,249,249,249, 87, 94,185, 48,
+     24, 12,210, 93,154,188,109, 32,139,237,192,138, 81,189,215, 42,
+    199,254, 67, 52,109,215, 18,157,  4,  2,109,165,  8,144,234,198,
+    171, 19, 54,106,255,248,128, 13,228, 76,208, 24,109,109, 34, 34,
+     41, 34, 36, 73, 59,176,166, 77,245,196,169,100,173,120,221,144,
+    114,132,236,157, 78,  0,208,108, 54,158, 56,251,228,232,149, 87,
+     86,123, 59,255,247, 27, 23,254,198,137, 51, 51,168, 18, 22,102,
+     91, 15,149,137, 47, 17,182,109, 95, 29,120,249,221,239, 39, 50,
+    197,223, 15,230,183,226,248,251,119,110,109,199,209,177,163, 71,
+     63,253,217,207,188,246,218,107, 27, 27,119,152,185, 96, 14,199,
+     72,133,138, 62,171,174,246,178, 91,129, 72,146, 36, 58, 73, 90,
+      2, 51, 65, 72,147,221, 73,166,230,215,132, 70,116,135,201, 63,
+     34, 52,136,218,130,253,237, 29, 10,  3, 36,194,116, 97, 15, 32,
+    216, 40, 12,100, 79,245,229, 57, 71,159, 95, 34,210,108, 54,223,
+    253,158,247,254,240,  7,223,191,182,189,249,111, 95,253,241,  7,
+    143,156, 56, 63,179,160, 33, 85, 97,110, 85,167, 28,170,135, 69,
+    246,109, 87,126,186,189,249,147,205, 53, 22, 89,152,159,255, 47,
+    254,222,223,187,122,245,234,205,155, 55,205,110, 79,222, 55,101,
+     27, 65,250, 66,206,147,183,249, 12, 73, 74, 47,107, 30,145, 57,
+    137,163,  8,180,110, 17, 53, 80, 17, 34,  1,214, 83,144, 19,117,
+    254,  7, 53,150, 30,116,  4,134,115, 97,163,183,177, 57,179,178,
+     28,  4,  1,107, 45,152, 53,177, 73,189,100,  6,179,170,  3, 46,
+      5,229,149, 42,  2,219,181, 69, 68,180,214,173, 86,251,227,159,
+    248,196, 15,127,240,131,235, 55,110,124,243,250,229, 43, 59, 91,
+    231,102, 23,150, 27, 45, 45, 98,226,176, 67,251,236,151, 93,110,
+    128,  0,183, 71,131,215,183, 55,111, 12,122,  0,240,238,231,158,
+    251,197, 47,124,225,205, 55,223,188,124,249, 74,191, 63,200,203,
+    238,109, 21,118,234, 28,211,233, 93,192, 10,126, 21, 95, 36,242,
+     15,136, 81,164, 71,163,136, 88,186, 65,160, 16,149,201,239,235,
+    103,249,225,209, 95, 82,  3,237,238,220,163, 32,161, 28,111,118,
+    126,176,182, 54,119,244, 72, 16,  4,177,112,170,185,208, 10,177,
+     52, 15,131, 44,209, 47, 11,178, 49,137, 76,122,182, 49,115, 20,
+    141,130, 64,189,255,  3, 31, 88,189,189,250,151,127,249,151,151,
+    183, 55, 47,111,111, 46,183,187,199, 58,221,217,176,185,216,104,
+    226,164,175,217,190,139,231,149,  0,172,141,134,155,241,232,122,
+    191,103,234, 84,195, 32,248,187,127,247,239,158, 62,115,230,181,
+    215, 94,187,122,245,106,175,183, 99,143, 44, 88, 94,229,251,166,
+    101, 74, 12,199,120,243,204, 51,250,230, 17,146, 36,137,162,168,
+      5,184, 16, 54, 21, 34,161, 89,202, 93, 11,176, 58,255,122,180,
+     35,176, 48,196,237, 81, 18,141, 40,  8,136, 40,219,253, 43,163,
+    152, 51, 31,185,219,207,  1,172,  8,101, 50,132, 13,  6, 67, 17,
+     88, 57,178,242,139,191,244,133, 11, 63,121,249,194,133, 87,110,
+     15,122,183,  7,189, 71,251,240,118, 90,237, 15,127,228,195, 31,
+    255,196, 39, 54,183,182, 94,126,249,149,219,183,111, 13,  6, 67,
+     27,123, 65,190,211,182,117,142,206, 22,144,133,156,203, 59,182,
+    133, 35,140,136, 73,146,140,134, 35, 78,226, 38,224, 84, 16,170,
+     12, 94,245, 24, 99,234, 15, 31,191,106, 49, 86, 25,129,145,224,
+     82,163,181,179,126,103,230,200, 10,145, 50,203,168,237,155, 41,
+    200,247,138, 42,198,129,172, 60, 29,102,127,131, 65,152, 30,141,
+    134,155,155,156, 36,201, 59,159,123,238,163, 47,190,120,253,250,
+    181,159,190,250,234,141, 27, 55,214,238,220,121,196, 14,236,242,
+    226,226,233,211,167,159,127,225,133,119,188,227,233,245, 59,235,
+    111,188,113,241,246,237, 91,219, 91,219,113, 18, 23, 55,  6,  6,
+     65,243,175,176,243,166,111, 21,161, 84, 42,225,194, 11,  0,147,
+     68, 15,  6,  3,197, 50, 29, 52, 20,162,170,195,175,177, 16, 40,
+    161, 76, 14,  9,191,106,138, 85, 66,135, 16, 79,182,186,223,185,
+    117,123,246,200, 17,165,148,237, 65,159, 55,115,206,130,176,180,
+    168,162, 12, 49,129,162,127,132, 52,125,113,202, 39, 81, 68,162,
+     40,214,122,123, 56, 26,237,236,108, 79, 77, 77,191,248,210, 75,
+     83,211,211,221, 78,167,209,108,154,174,229, 88,238,104, 85,245,
+    217,196,120, 71,167,191,181,216, 99, 35,146, 36,122, 48,232,111,
+    110,110,254,240,135, 63,216,220,220,220,217,233,197,113,204,172,
+    237, 95,146,215,  6, 99,230, 31,205, 42, 34,255, 72, 22, 95, 21,
+    178,216,203, 31,204, 60, 26, 13,135,195, 97, 27,112, 33,108,154,
+    109, 44,168,198,215,225,240,143, 82,177,104,187,134,212,254, 37,
+     24,  1, 78,  5, 65, 71,203,112,103,167, 53, 61, 69, 64,206,214,
+     26,217,192, 10, 47,233,128, 76,178, 62,212,254,252,163,123,162,
+    166, 15,141,112, 52, 26, 37,113,188,179,179,179,190,222,108,183,
+     91,205,102,171,209,  8,145,  8,139,154, 99,  2,233, 85,194,150,
+     67, 47,115,216,180,214,113, 28, 15,134,131,225, 96, 16, 69,145,
+    214, 90,156,149,157,152, 30,180,108,122,214, 44,155,151,116,243,
+     58, 27,185,151,253,184, 39,187, 32,109, 57,105, 70, 28,199,253,
+     94, 95,226,120,138,130,233, 32, 12, 76,248, 85,135,247,135, 47,
+    255,218,127,193,246,  4, 19,174,187,188, 56,181,180,100, 46,223,
+    121,235,114,212,235, 31,180,133, 84,130,199,154,157,107,171,107,
+    237,233, 41, 36, 18, 17, 17,146,226,216,219, 75, 22,245,151,173,
+      0,119, 75, 58,179, 51, 86,107, 30,142,134, 81, 52, 66,220,178,
+    137,116,206, 64,184,255,158,202, 15,135, 95,206, 94,188,146,247,
+    149,229,236,127,245, 65, 79,191, 49, 19,182,169,121,244,177, 85,
+    229, 25,193, 97, 23,165,101, 95,195,225,176,215,219,105,  8,204,
+      5,141, 16, 73, 33, 41,215, 60, 62,222, 47,230,178, 63, 21, 42,
+     19,193,175,195, 60,142,156, 63,247,228, 75, 31, 93,126,231, 51,
+    211, 22, 94,  0, 48,234,247,191,247,123,127,240,198,159,253,197,
+     65, 91,200, 19,173,206,155,235, 55,227,227,199,194,102,211,246,
+    210,225, 18,194, 42,189,164, 39,196, 92,132,165,  5, 26,165,167,
+    138,164, 69, 23, 94,169, 42, 22, 94,125, 38, 87, 60,248, 47,146,
+    213,252,202, 86, 18,216,203, 85,155,174,217,169, 93, 44,170,215,
+    113,158,209,113,143,100,197,215,104, 20,109,111,111, 71,131,225,
+     34,170,229, 70, 43, 32, 12,106,243,184,219, 99,135,111,163,156,
+    121,  4,249,213,232,118, 78,190,255,249,119,125,254, 23, 12,182,
+    182,227,254,215,175,255,240,167,155, 87,  1, 96, 42,108,127,230,
+    244,135, 62,242,143,126,165,119,123,237,230,203, 23, 14,212, 66,
+     42,196,227,173,206,234,181,235,203, 79, 62, 65,100,188,141,170,
+     66, 88,217, 75, 58,142, 50, 95,214, 13,110,253,101,254, 12,201,
+    151,201,236,178,172, 82, 38,173,229,234, 88,140,185,251, 41, 65,
+    190,247,155, 71,177,234,150,116, 25,255,179,248,190,202, 45, 22,
+    235,188, 10,240, 34, 17, 30,244,251, 91,155, 91, 45,192,249,176,
+     25, 18,165,225, 87,109, 30,239, 61,211,172,249,181,239,113,238,
+     51, 63,247,220,231,127,161,217,233,  0,192,215,175,255,240,171,
+    151,255,211,159, 95,255,161,123,131, 63,191,241,163,255,233,197,
+    127,242,161,255,234,239,127,229,159,254,218,129, 90, 72,  2,124,
+    162, 61,125,109,253,102,124,252,104,163,213,166,212,155,224, 88,
+    132, 21,188,100,169,111,  5,238,150, 24, 85, 61,119, 76,253, 64,
+     42, 68,196,180,143,129,251,216,214,245,225, 69, 95, 30,193,242,
+    234, 19,219,162,162,130, 96,185, 96, 53, 69,117,228,112,203,  9,
+     16,113, 87,120, 33,194, 96, 16,111,108,220,137, 71,195, 25, 84,
+    199,154,237, 16, 41,176, 57, 98,205,175,253,169,176,135,250,210,
+     24,220, 51, 49, 39,237, 21,252,228,  7,158,127,223,151,190,104,
+     52,215, 31,191,245,237,223,185,240,199, 55,250,235,229,155,125,
+    127,245,181,239,173,190,246,190,165,167,207,125,230,231, 46,124,
+    245, 63, 28,160,133,  4,  8, 16,143,183, 58,215, 47, 94, 58,254,
+    236, 51,  0,  1, 17,  2, 32, 51,102,129, 78, 17, 97, 34,206,122,
+    201, 92, 81,164, 89, 53,186,114, 43, 91, 20,147, 63, 30,197, 94,
+     13,232,  5,100, 89, 19,172, 73, 13,112, 10,125,202, 60,  9,  6,
+     69, 43, 89,106,236,111, 15,187,128, 16, 82,217, 45, 86,194, 43,
+    243,140,198, 32,106,157,236,108,111,111,108,108,118,  4,231, 27,
+    205,144, 40,180,187,109, 63, 52,124, 29, 57,127, 14,  0, 86,206,
+     63, 83,249,213,222,234, 90,239,246,218, 65,103,184,135,104, 60,
+     10,250,171,209,237,124,248, 31,254,253, 83, 31,120, 95, 70,174,
+    169,176,253,133,179, 63,251,142,217, 19,239, 91,122,250,235,215,
+    127,248, 47,191,247,251, 59,241, 32,187,253,191,252,222,239,255,
+    219,159,255, 31,158,251,252, 47, 28, 36,191,128,172,  4,219,220,
+     90,235,221,217,152, 94, 92,180,243, 91,102,143, 70,220, 21, 97,
+     48, 86,134, 21,156, 97,102,186, 42, 58,243,228,228, 50,251,160,
+    227,196,178, 11,160, 18, 94,249,230, 74,  5, 29,  6,144,107,177,
+      2,192,128,138,110,209, 89, 18, 84,158,106, 76,131,123, 66, 17,
+     25, 12,  6,107,107,171,144, 36, 51, 42, 56,214,108, 55,172,248,
+     58,232, 29,212,230,207,156,122,242,165,143, 30, 61,127,110,238,
+    204,169,125,126, 75,127,117,237,198, 79, 46, 92,249,238,247,175,
+    252,213,247,107,126,221,175,230,122, 27,207,137,147, 31,120,254,
+    195,255,232, 87,154,157,206,107,155, 87,254,151, 31,255, 33,  0,
+    252,119,239,251,229,247, 45, 61,157,221,224,165, 99,239,249,233,
+    230,213,223,185,240,199,217, 53,207, 47, 61,189, 29,247,167, 59,
+    157,249, 51,167,238, 92,186,124,112, 18, 76,  1,  4,136, 39, 90,
+    221,151, 47, 95,105,207, 76, 19,181, 84, 64,132, 36,194,136, 50,
+     70,136,101,  8,179,117,152,217,191, 98,255,174,188,184, 60, 61,
+    219,171, 58, 91,184,226,203, 80, 12, 74,121,218,219,171,186, 42,
+     10,117, 36,111,167, 51, 70,136, 85,244,254,201, 68, 86, 33,234,
+     42,151,167,122,222, 49, 45, 50,193,209,104,184,190,182,190,189,
+    185, 53, 45,176, 24, 54,155,164,140,248, 82,  7, 41,190,230,207,
+    156,122,225, 75, 95, 52,154, 11,  0,228,198, 53,184,113, 29, 54,
+     54,224,230,117, 24, 14, 42,190,225,204, 89,104,181,224,232,177,
+    206, 19,103,207,126,252, 99,103, 63,254,177,222,237,213,111,253,
+    214,239, 30, 92,152, 59,225, 89,216,225,214, 95, 47,124,233,139,
+    207,126,230,231,  0,224,255,120,229,143,255,221, 27,255,223,175,
+    156,251,236, 47, 61,245,179,  0,112,253,149, 11,151,254,236,155,
+     87,190,243,125,  0,248,220,191,250,141, 47, 60,245,137,175, 94,
+    254,246,141,254,250, 59,102, 79,252,227,231, 62,111,232,246,202,
+     87,255,195,193,193, 43, 75,193, 20,224, 74,163,117, 39, 30,173,
+     95,189,182,116,250, 20, 17, 82, 16, 16, 40,102, 65,100, 43,196,
+     74, 50, 44,239, 90, 81,  4, 89, 94,239,108,150,200,152,204,204,
+    118,217,119, 82,127, 79,124, 89, 90, 21,102,250,112,  2,159,221,
+    226,238,231, 81,156,140,116,246,186,204,154,203, 26,245, 73,119,
+     11, 47,114, 98, 47, 76,146,100,115, 99,227,246,237, 91, 45,128,
+    165,176,185,220,104, 53,108,114,127,112,244,154, 63,115,234,231,
+    254,217,175,134,157,142,188,249,  6,252,224,187,240,202, 79, 96,
+     56,  4,  0,104,181,224,217,119,194,153, 39,225,232,177,252,214,
+     27,119,224,230, 13,248,193,119, 97,227, 78,122,212,158,125, 39,
+    156, 59,223,125,254,253,159,250,103,191,250,173,223,250,157,  3,
+    157, 82,159,216, 76,233,176,242,171,209,237,188,240,203, 95, 60,
+    251,241,143,109,199,253,127,254,159,254,247,159,110, 94,249,215,
+     47,254,147,167,103, 79,110,175,174,126,239,247,254,192, 21,213,
+    175,126,245,107,239,254,252,231,254,241,115,159,223,137,  7,159,
+     61,253, 97, 67,183,191,254,191,254,232,214,193,191,100, 33, 32,
+     33, 40,196,179,157,233, 31,173,223,217,238, 78,205, 46, 47, 42,
+    165, 80, 41,133, 34,130, 25,197,236,182,217, 54,175,134,188,101,
+    133,181,147, 46,200, 76, 99, 66,143,106,126,127,196,130,211, 52,
+     63,160,172,185, 38,201, 75,138,255,206,177,198,190, 10,  3,167,
+    223,180, 65, 23, 82,198, 41, 40, 78, 49, 86,154,198,156, 92,132,
+    132,200, 44, 59, 59,219, 55,111,222,192, 56, 89,160,240,120,171,
+    211,176,226,235, 64,203, 38, 94,248,210, 23,195, 78, 71,254,228,
+    223,195,183,190,145, 95,251,145, 23,225, 19,159,196, 86,187,250,
+    123, 62,253,183,228, 91,223,128, 63,249,247,  0,  0,175,252,  4,
+     94,249,137, 92,120, 25,255,206,151,222,255,165, 47, 62, 60,126,
+     77, 82, 45, 78, 48,161,207,230,189,198,167,126,237, 87,231,207,
+    156,122,109,243,202, 63,253,198,255, 12,  0,  6, 94,151,255,234,
+    123,223,254,237,223, 45, 68,155, 63,250,242, 87,158,248,248, 71,
+     95, 58,246, 30,  0,216, 94, 93,253,235, 47,255,209, 67,123,164,
+    109, 33,  5, 53,  8, 78,180,186, 63,185,244, 86,179,219, 86, 74,
+     53, 72, 17, 41, 17,  3, 47, 76, 55,225,192, 42, 37,102,201,229,
+    131, 44,203,244, 37, 95,239,  7, 82,202,192,138,179, 66,226,132,
+    254, 56, 65,212,170,250, 76, 10,251,114,139,247,207,252, 49, 37,
+    100, 21,139, 36,118, 73,235,211, 77,210, 73, 64,250,253,222,141,
+    235,215,123, 59, 59, 51,130,139,141,102,139,148, 21, 95,116,160,
+    101, 19, 71,206,159,147, 27,215, 60,120,  1,224,167,255, 86,220,
+    239,191,254,213,255,112,229, 59, 63,112, 95, 98, 87,206,159,155,
+     63,115,242,217, 79,127,170,251,145, 23,229,194, 79,224,205,139,
+    233, 23, 94,249,137,124,255, 59,225,243,239, 95, 57,127,238,214,
+     36,187,200, 90,127,229, 47, 81,255,232, 87, 50,120,237,196,131,
+     95, 57,247, 89,  3,175,175,255,235,127, 83,121,251, 63,255, 87,
+    255,230,220,167, 63,117,231,173,203, 23,191,254,205,135, 63,113,
+     67,  8,  1,224, 74,163,181,157,196,215, 95,191,120,252,252, 57,
+      0,108, 54,155,164,  8, 82,  9,102, 82, 48, 11, 50,191,222, 73,
+    160,  2,100,222,123, 91,215, 89,216,105, 18,242, 46, 12, 21,128,
+    144, 73,129,152,236,173,197, 28,102,101,253,209, 10,236,  2,172,
+    238,225, 85,240,140,174,248, 34, 66, 17, 24,244,251, 55,174, 95,
+     91, 93, 93,155, 22, 92, 12,155,105,242,133, 70,124, 29,252, 95,
+    111, 12,163, 63,214, 47, 93,254,238,239,253, 65,225,202, 91, 47,
+     95,184,245,242,133, 70,167,243,238,207,127,174,248, 13, 27, 27,
+    143,107,124,127,  8,249,181,114,254,220,217,143,127,236,122,127,
+    205,192, 11,  0,222, 49,123,  2,  0,174,126,247,  7,227,190,229,
+    206,165,203,223,250,173,223,121, 91,238, 45,218,112, 38, 64, 58,
+    219,153, 26,238,108,220,190,120,105,233,137,211, 68,212, 82, 77,
+     36,229,194,203, 94,150,170, 50,125, 17,175, 38,170, 40, 70,188,
+    168,  8, 74, 36,155,148,188,226, 30,189,100, 58,119, 74,128, 64,
+     99,203,234, 11,105,215, 94,202, 11,  0,134,195,193,141, 27, 55,
+    174, 95,191,222, 17, 88, 80,225,169,118, 55, 19, 95,198, 57,214,
+     53, 95, 19,238, 33,233,208,  5,119,231, 62,253, 73,  0,248,151,
+    223,251, 63,179,146,136,127,247,198,127,  4,128,119,125,254, 23,
+     38,243,161,206,202,241,  3,164,103,187,115,221,222, 96,237,242,
+    213,225,112, 24,199, 49,  0, 40, 69,170,106, 16, 41,162,236,139,
+     68,138,136,148, 34, 51,148,249, 52,125,239, 12, 69,206, 53,120,
+    104,207, 65, 76, 83, 45,239,239, 82,222,  5, 42,254,217,233,  1,
+     82,148, 30, 58,242, 15,166,243,137, 82, 74, 33, 97, 20,141,110,
+    221,186,121,245,242,149, 22,195,146, 10, 79,183,167, 90, 68, 13,
+     82,117,193,253,253,159,245, 15,141,  7,193,189,210,245,109,123,
+    233, 94,121,231,185,235,253,181,239,175,190,150, 93,243,253,213,
+    215,254,248,173,111,127,246,244,135,223,253,249,207,253,232,203,
+     95,153,204,199,218,184, 72, 33,122,166, 51,251,202,230,198,198,
+    205,155,178,178,220,237, 66,171,213, 34, 82,146, 55,104, 69, 17,
+    177, 90,172, 32,192,178,182, 11,142, 16, 43,196, 67,126,114, 84,
+    245,113,  2,149, 23,238,126,169, 98, 55,185, 49,166,209, 79,235,
+    177, 58,179, 71,  4,144,225, 96,120,227,250,141, 75,111,190, 25,
+     48, 47,169,240, 84,171,219, 34,213,204, 99,251,135,245,151, 63,
+    113, 22,254,251,223,184,235,239,250,251,255,176,134,231,131,241,
+    143, 15,255, 60,136,250,253,169,217,206, 84,216,118, 75, 82,127,
+    231,194, 31,255,204,177,119, 63,243,153, 79, 77, 38,191,114, 23,
+      9,208, 84,116,178,213,189,124,115,117, 93,235,100,121, 25, 68,
+    218,157,142, 82,100,139, 38, 80, 68,144, 42,124,100, 69, 13,148,
+    255,222,179,142,224,150, 86, 29,198,134,  9, 89,147,212,  2,197,
+     42,182, 53,113,186, 63,151, 99, 47, 42,173,112,148, 94,175,127,
+    237,234,213, 75,111,190, 25,138,172, 80,176,220,104,117, 84,208,
+     82,222,156,227, 67, 32,216,183,126,235,119,158,124,233,163,229,
+    235,127,244,229, 63, 26,247, 45,111,124,253, 47, 42, 75,243,239,
+     92,186,252,182,135,247,111,139,165, 12, 14, 21,187,  0,  0,174,
+    254,213,247,159,253,204,207,125,225,236,207,102, 37,169, 71, 59,
+     11,255,248,185,207, 79,135,157, 81, 60,185,139, 42, 28,132,209,
+     98,216, 20,128, 43,171,119,214,163, 72,152, 89,164, 59,213, 13,
+     84,  0,  0, 41,191, 76,187, 29,174,230, 87,198,176, 42,  9,230,
+    130,172,234, 49,154,100,148, 97,197,231,101,241, 85,170,243, 42,
+    240,171,162,196,222,100, 94,128,200,204, 59, 59,219,151,223,186,
+    124,245,202,149,166,200, 18,  5,203,141,246, 66,216,104,146,202,
+    170,237, 31,154,229,126,227,207,254,226,110,167,194,123,183,215,
+    190,246, 47,126,243,145,176,152,147,161,191, 30,254,248,241, 31,
+    254,209,147, 31,255,216, 63,120,246,179, 95,189,252,237,157,120,
+    240,133,179, 63,251, 15,158,253, 44,  0,108,175,174,126,251,127,
+    253,221,  9, 79,117,204,194, 60, 32, 90, 12,155, 45,162, 55,182,
+    118, 86,227,183,244,137, 19, 73,146,204,204, 76, 55, 26, 77, 36,
+      5, 25,187,200,214,231,131,219,132, 97, 44,191,252,202,207,125,
+    185, 70,153, 28, 88,221, 27,191, 42,244, 87,101,177, 42, 34, 64,
+    156, 36, 27,119, 54,222,186,244,230,237, 91,183, 91, 44, 75, 42,
+     60,222,234, 76,169,176, 69,212, 32, 10,168,142,189,238, 67,114,
+     21,186,161, 76,146,254,146,251,190,193,  3,245,143,189,254,143,
+    191,252,149,247,127,233,239,252,250,135,254,203,163,157,133,233,
+    176, 51,234,247,127,252,229,175, 92,248,234,215, 14,129, 41, 50,
+      8,  3,  0,162, 41,  8,207,117,103, 95,237,109,174, 94,124,115,
+    116,236,104, 20,141,230,231,231,219,237, 14,145,  2,112,186, 29,
+     82,169,160,  2,202, 65,216, 56,253,  5, 19, 61,  3,137,123,127,
+    205,181,145, 88,236, 68,232,237,217,232,252, 47,218, 71, 16, 25,
+     12,134,183,111,223,186,244,230,155,189,237,157, 14,203, 98, 26,
+    216,171, 38,169,166,129, 87,189,189,246,225,179,143,135,179,254,
+    235,194, 87,191,246,196,199, 63,246,244,233, 83, 70,132,127,231,
+    247,255, 32, 62, 84,203,241,201,244,101, 39,  2,128,115, 83,179,
+    175,247,182,174,191,249, 86,124,100, 57,142,226,185,249,249,169,
+    233,169, 70, 24, 34, 81, 65,115, 21, 83, 48,247,  2,184, 77,103,
+    202,  8, 59, 92, 17, 24, 86,102, 96, 99, 54, 13, 42, 34,172, 64,
+     48,  0,208, 73,178,189,189,125,245,234,213,183, 46, 93, 34,150,
+     46,203, 82,208, 56,213,234, 54,149, 74,109,163,129, 87,205,174,
+     67, 56,130,201,124, 97,222,115,252,233,191,248,205,249,211,167,
+    122,171,171,189,219,107,135,235,136, 99,134, 48,  0, 36, 66,129,
+    167,187, 51,221,209,224,250,237,181,213,193, 96, 48,232,207,204,
+    204,206, 47, 44,116,187, 29,149, 38, 98, 57,196,242,126, 88,246,
+    114, 97, 73, 13,120,123,110, 23, 58,210, 28,166, 99, 84,104,117,
+     61,110,199,179, 18,194, 60, 33,198,204,131,193,224,246,237, 91,
+    151,223,186,188,185,185,209,100,153,  5,181,216,108,174, 52, 90,
+     70,118,133,182,212,171, 86, 94,135, 50,253, 58,188,235, 31,227,
+     94,255, 80,175,150, 48, 70,210, 20,101, 34,225,137, 86,103, 46,
+    104,188,217,223, 89,235, 95,237,207,110,247,251,189,249,133,133,
+    217,217,217, 86,171, 77, 68,214, 28,238, 14,175, 92,133,237, 41,
+    190,100,242,142,198,216,235,156,222,253,229,142,208, 30,194,156,
+      6, 95,194, 50, 26,141, 54, 54,238, 92,189,114,245,218,181,171,
+    129, 64,151,101, 65,133,199, 90,157,174, 10,154,164, 26, 68, 33,
+    146,217, 24,173,174, 83,125,176,196,122,152,207,174, 96, 47,114,
+    202, 94, 87,214,227,126, 78, 90, 84,102, 67, 14, 32, 10,194,103,
+    187,179,215, 71,253, 27,235,155,235,189,254,206,246,206,157,153,
+    233,133,133,133,153,153,217, 86,187,165, 72, 33,161,167,194,188,
+     10,124,103, 73,115,177,145,233, 97, 61, 58, 94,183,236,194,230,
+    103, 99,151,107,  3,179,140,134,195,205,205,205, 27,215,111, 92,
+    187,118, 53,137,147, 54,203, 52,170,197,102,218, 85,162,129,166,
+    194, 30, 21, 98, 93,100,255,200,250,199,  9,209,135,143,  3,194,
+     40, 61,  3,133, 16, 78,180,186, 43,205,214, 27,253,157, 27, 55,
+    110, 14, 55, 55, 55,239,108, 76,207,204,204,205,207,205,206,206,
+    118, 58,157, 48,108, 16,102,114,204,239,242,231,144,171,132, 45,
+     57, 44, 15, 23,238,170,191,220,173,152, 10,123,  9,129, 72,156,
+     36,131,254, 96, 99, 99,227,246,173, 91, 55,110, 92,215,113, 18,
+    178, 76,177, 44,134,141, 19,173,110,131,168, 73, 20,162, 10,  9,
+    157,253,132,106,120, 61, 32,213,133,135,145, 95,245,120, 64,  8,
+     51,109,  8, 17,  8, 73,148,224,211,221,153, 19,186,115,101,216,
+    187,121,123,117,117,107,107,237,246,237,238,244,244,236,220,236,
+    236,236,220,212, 84,183,213,110,135, 65,136, 10,  1,160,106, 17,
+    164,159,122,201, 33, 63, 52, 30,200,138,213,247, 34,146, 36,201,
+    112, 56,236,237,236,172,223, 89,191,125,235,214,250,250, 58,  9,
+      4, 44, 45,150,197,176,177,210,105,119, 72,153,126, 56,153, 97,
+     36,179,250,177,134,215,161,140,188,238,155, 95,181,234, 58, 16,
+     21,102,119,253, 32, 20, 18, 84,136, 79,119,102, 78,104,125,109,
+    212, 95,219,218,217,233,245,183, 86, 87,175,181, 91,221,110,119,
+    118,110,110,122,122,166,211,105, 55,155,173, 70,163, 17,  4,  1,
+    169,180,197,113,105,237,208,161,218,133, 24,171,240,229,171, 48,
+    179,163,109,146, 36,113, 28,141, 70,163,126,127,176,185,177,177,
+    182,182,182,190,190,198,204,  1, 75, 91,164, 37, 56, 27, 54,150,
+     27,173,142,197, 86, 96,155, 17,214,178,235,225,162,236,192,159,
+    112, 65,141,173, 73, 19, 98,102,239, 15,  5,164, 20,  6, 72, 29,
+     53,125, 70,166, 86,163,225,237,104,184, 57,220,220,218,218,185,
+    115,243, 54, 52,194,102,179,217,237,118,167,166,166, 58,221, 78,
+    179,217, 10,195, 48,  8,  2, 21, 40,183, 14,224,145,200,191,192,
+     52,147,213, 90, 39, 73, 18,199,241,112, 56,232,247,122,219,219,
+     59,219,219, 91,189, 94, 15,  0,  2,150,134, 64,192, 50,165,130,
+    249, 70, 99,193,238,123, 22, 34,  6, 72,138, 48,  0, 36,187,244,
+    177,150, 93, 15, 95,143, 29,220,115,176,246,143,147,103,152,  0,
+     76, 34, 70, 32,154, 36,  0, 12, 68,142,183, 58, 43,141,214,136,
+    121, 53, 30,110,196,209,230, 78, 47,233, 15,214, 55, 54,110,  3,
+     50,  2, 40, 10,131,176,209,108,132, 97,104,230, 43, 31,177,193,
+    204, 81, 20,141,134,163, 56,137,  1,128,  4, 72,128, 64,218, 44,
+     74,100, 90,133, 51, 97, 56, 23, 54,155, 68,  1, 82,128,104,138,
+    233,205,214, 65, 42,171,167,168,201,245,246, 57,202,  3, 42,226,
+    169,249, 53,177, 66, 44,111,168,170,  0, 24, 69, 35, 53, 72, 58,
+     74,157,104,118, 18,145,141, 36,218, 74,162,190,214, 67, 78,162,
+     36,214, 81,146,244,  7, 35,194, 71,248,176,  4, 44,  1,128, 18,
+      9,145, 90, 74,181, 72, 77, 55,194,153, 48, 84,144,210, 74, 33,
+    170, 44,228,202, 39, 70,108,234, 95, 63,183, 38,  0,100, 19,197,
+    175,218, 83, 30,104, 16,148, 26, 30, 66, 96, 65,133,194, 34, 12,
+    200,  2, 90,164, 69,106,185,209, 98, 49, 87,202,102, 18,139,200,
+    182,142, 69, 30,205,  7,  6,  1,166,154,  1,  2, 78,  7, 33,  1,
+     80, 74, 40, 36, 68, 83, 61,175, 16, 41, 13, 16, 17, 33,219,244,
+    172, 38,215,193,160,  8, 39,130, 14,181,254, 58,  4, 20,  3,  0,
+    101, 86, 28,153,  0, 27,129, 69, 24, 64, 68, 24,132,  5,  4,164,
+     69,129,128,172, 64, 91,108,  5,190, 60,114,135, 34, 93,162,109,
+     55, 70, 49,239,  9,210,110,170,100,191, 84, 11,174, 73,101,222,
+    131,127, 74, 22,249, 37, 50,182,114,187,126, 54, 76,134, 28, 67,
+      1, 81,136,166, 21,190,  0,176,221, 14,209, 33,151, 60,162,199,
+      1,125,138,121,204,  2, 39,244,175,159,171,147,233, 29, 31,248,
+     51, 51, 40,192,107, 63, 17,103,253,228,120, 91, 41,102, 66,177,
+    244,129, 18,  0,195, 50,128, 20,103,143,170,177,119,254,124,183,
+    138,181, 54,137, 19, 77, 44,187, 73,231, 65, 61, 33,  3, 31, 94,
+    102,171, 64,244,183, 63, 78,191, 92, 63, 36,147,168,200,204,131,
+    147,203,142, 67, 92, 50,113, 87,134,186,  6,214,225,  1,153, 28,
+     92, 21, 69,224,  3,211,228,114,226,236, 73, 47,187,194, 11,235,
+      8,127,162,180, 73,173, 68,234, 49,  9,186,171,156,192,102, 42,
+    232,193,242, 34,112, 49,201,108,186,229,121, 78,181, 62, 25,234,
+     81,143,122,220,181,115,180, 32,203,152,117, 16, 22, 46,175,117,
+     20,246,154, 20,131,179,119,106, 22, 59, 32,212, 76,171, 71, 61,
+    234,177, 47,136,  9,216,153, 37,176, 93, 56, 15,206, 63, 10, 51,
+      3,  8,160, 32,152, 85,102,192, 89,111, 22,169,246, 44,133,253,
+    234,235, 81,143,122,212,220, 74, 53, 87,222,174,206, 82,236, 32,
+    231, 31,153, 89, 16,205,123,  1,180,173,138, 57,235,204,143,158,
+    244, 50,248,170, 83,253,122,212,163, 30, 46,188, 50,255,230,188,
+     25,156,129,136, 45,121, 57, 16,126, 49,128,144, 48, 18,  1, 10,
+    164,149, 69, 34,130,144,206, 75,154,173, 16,176, 54,144,245,168,
+     71, 61, 60, 47,230,233, 47,112,153,101,183,157, 49, 31,130,217,
+    233,  7, 24, 68, 57,252,210, 90,139,104,  1, 66, 96, 83, 98,196,
+    146,237,204,181, 11,179,208, 78, 89,214,163, 30,245,120, 60,217,
+    133, 89, 57, 34,128,  0, 48, 56,204,  2, 96,  1,  6, 97, 16,  1,
+      8,130,  7,185,230, 39,255, 89, 90,107,101, 82, 47, 20, 54,101,
+    205, 44, 34, 12,204,238,125, 69,216, 27,157, 53,203,234, 81,143,
+     71, 87, 99,237,246, 37,199, 48,166,204, 50,235,115,249,  0,138,
+     39,114,126,  5, 65,192, 73,162, 17, 53, 49, 41, 69, 72,  8, 72,
+     34,162, 89, 68,128,  5,236,186, 13,211,172, 55, 11,239,235,  2,
+    176,122,212,227,113, 24,222,146,109,244,187, 75,230,251,214, 74,
+    182, 73, 60,219,205,151, 83,132,137, 48,136,136,184,253,157,112,
+     95,120,220,  7,191,186,221,238,109, 69, 20, 37,  9,  9,177,144,
+    217, 71, 93,  0,152,133,217, 20,120, 91,120,165,157,123,177,230,
+     86, 61,234,241, 56, 43, 50, 44, 80, 71,  0,128,157,169, 70,  6,
+     48,189, 82, 44,194,128, 27, 97, 24,132, 15, 80,251,165,252,154,
+    158,158,214,  0, 73, 18, 19, 42,173, 56,221, 74, 29, 32,181,177,
+    105, 33,  5, 80,186, 83,149,160,160, 93,181, 98, 35,253, 26,103,
+    245,168,199, 99,  9, 49,244,205, 99, 70, 43, 45,162, 93,241,  5,
+      2, 68,141, 70,227,222,221,105,233, 75, 41,191,230,102,231,226,
+     70,160, 54, 98, 34, 77, 28, 16,165, 18, 12, 69,140,254,  2, 16,
+    115, 79,209,218, 70,187,134,214,172,155,172, 35,252,122,212,227,
+    241, 32,214, 24,164,  8,100,211,141,194, 14,185,180,136,150,212,
+     78, 74, 24, 84,240,171, 84, 80,129, 99, 63,197,106,253, 53, 53,
+     61,165, 91,205, 36,142, 21, 41, 98, 33, 98, 84, 10,140,  6,147,
+    180,144, 21,237,102,121,229, 55, 41,253,190,154,101,245,168,199,
+     35,134,173,  2,114,202, 32, 51,158, 81, 11, 24,120,105,145, 68,
+     88,131, 65, 24,  8,  0,  4, 65, 24,134,251,150, 89,123,135, 98,
+    105,150,214,  8, 27,225,194, 92, 18,199,113, 20,197, 81,148,196,
+    145,142, 99,157, 36, 58,209,172, 89,132, 17,  4,115,132,165, 73,
+     24,250,197,172,117, 81, 88, 61,234,241,184, 81, 45,143,238,197,
+    153,109, 76,201,197,218,234, 47,227,207,168,219, 81, 74,193,190,
+    227,122,172,248, 77, 37,126, 33,  0, 18,174, 60,117, 54, 22,142,
+    227, 40,137,226, 56,138,147, 40,214,113,194, 58,225, 36, 17,205,
+    194,105,  9, 24, 98,190, 77,123,250,134, 21,248,170, 89, 86,143,
+    122, 60,210,204,242, 58,157,100,105, 61, 67,238, 25, 19, 54, 18,
+     44,173,252,  2,162,246,252, 44,238,183,254,126,215, 30,186,152,
+    241,203, 94, 58,117,250,180,158,234, 70,163, 40,138, 70,142, 10,
+     75, 88,107, 97, 78, 75, 40,208,238,137,144,153, 71,116,100,100,
+     45,193,234, 81,143, 71,218, 60, 98, 94,176, 90, 60,221,179,228,
+     62, 17, 78,172,121, 76,132,181,112,154,139, 53, 27,211, 83,211,
+    123,253,112,191, 52,191, 80,161, 81,226, 75, 94,191,186,178,178,
+    194,179,211,124,245, 58,105, 69,138, 73, 49,114,  0,138, 21,145,
+    217,232, 24, 68, 80,196, 52, 29,103, 49, 93,198, 37,205,242,209,
+    244,  0,245, 86,115,215, 41, 88, 61,234,241, 72,210, 12, 29,220,
+    152, 79, 24,192, 77,235, 13,185, 18, 35,190,196, 54,162,104, 53,
+     58,157,182,255, 51, 92, 58,225,238,198,173,242, 43,121, 45, 89,
+    171,213,234,156, 61, 99,126,101,172,117,156, 36,113, 20, 37, 81,
+    164,227,152,181,137,225,172,133,180,155,187,228, 22,178,150, 96,
+    245,168,199, 99, 32,190,188, 62, 14,102,201,144,164,123, 47,112,
+     78, 46, 73, 68, 98, 54,  8,227,172,255,  4,205, 76, 43, 21,148,
+    225,229,168, 47,251,179, 17, 11,147,  3,227,150,253, 80, 14, 62,
+    196,167,223,251, 30,152,158, 74,216,220,  3, 78, 68, 18,102,157,
+    104, 29, 39,156,104, 96,  6,145,172,120,130,192,108,250,146, 89,
+     72,196,146,103,173, 89, 86,143,122, 60,122, 44, 67,159, 47, 38,
+    185,215, 32,134, 92,137,112,194, 28,139,196,146, 47,219,134, 70,
+     56,187,184, 88,  1,174,114,241,  4, 66,105, 94, 16,171,191,238,
+    234, 47,  4,124,234,169,167,240,137,147, 73, 46,255,216, 58, 88,
+     97, 97,102,  6,177,155,190,228,209,125,118, 49, 79,196,246, 84,
+    125,245,168, 71, 61, 14,159,248,242,120,147,159,217,102, 19,172,
+    204, 54,198,204,177,112, 34,204, 22, 95, 12,160,167,186,115,179,
+    179,222, 26, 36,180, 90,  7, 51, 27,234,125, 25,178,127,232,252,
+    106,244,238, 26,185,182,175,213,108, 45,189,235,188, 14, 40, 17,
+    142,141,  2,100, 73, 68,180, 48,103,205, 19, 17, 16,128, 82,253,
+    229, 75, 48,103, 23, 43,172, 17, 86,143,122, 60, 98, 44,195,138,
+     24,157,157, 82,175,132, 83,120,197,204, 49,179,105,  4,198,  0,
+     64,212, 88,152, 83,165,206, 19, 25,194, 48,199,147,155,170,185,
+    248,116,172,157,131,177,192,197, 12, 34,190,247,131, 31,184,241,
+     31,191, 17, 95,189, 73,192, 68, 64,166,153, 33, 16,  0,187,141,
+    199,156,153, 71, 68,  1,  2,144, 52,228, 79,247,189, 23,216,101,
+     77, 81, 29,235,215,163, 30,147, 47,185, 42,196,151,167,155,108,
+    147,249,212, 57, 26,114,  9, 71,204,177,176,216, 80, 95, 68,184,
+    211, 58,178,178,226, 57,208,202, 95,107,  9,134, 69, 90, 90,187,
+    138,197,219,146,239,107, 97,121,105,169,243,238,243,137,152,187,
+     34,  6,165,214, 66, 10, 11,128,157,112,204, 37, 24, 98,254,169,
+    239, 34,235, 32,172, 30,245, 56,108,163,122,227, 30,183, 82, 42,
+    133,151,  0,167,240, 28,110,250, 69,  0,  0, 32,  0, 73, 68, 65,
+     84,226, 68, 56, 18,142, 56,133,151,206,186, 80,152, 31,183, 48,
+    215,110,183,209,227, 31,230, 18, 12,243,192, 62,219,143, 56,155,
+    221,116,234,228,157,249,131,188,254,171,144,135, 17,125,240,227,
+     47,209,241, 35,153,  8,244, 16,102,155,144,101,  8, 36,  4, 50,
+     63,  5, 60, 23,233,108,137, 92, 35,172, 30,245, 56,220,122, 12,
+    243,170, 79,187,229,104, 90,179,154,214,169, 26, 86, 24,132, 37,
+    204, 98, 59,127,137,  8,119,219, 43,199,143,123, 85,171, 41,193,
+    208,243,143, 22, 84, 46,187,114,114,184,113, 24,228,121, 24,101,
+     19,  9,104,239,228,177, 99,199,186,239,121,103,194, 98,212, 96,
+    108,167, 18, 44,194, 50,251,151,146,147,208,190,153,203, 37,132,
+     65,149, 10,173, 71, 61,234, 49,177,254, 17,199,157,179,232,194,
+     11, 76,181,106,156,139, 47, 29,177,182,133,248,144, 54, 62, 93,
+    152,235,118,187,  0,133, 94, 21,224, 83, 36,157, 14,116,  6,128,
+    183,218,199,185, 91,206,207, 32, 71,197,217,108,158,232, 99,159,
+    252, 27,116,246, 84, 33,141, 51,  8, 99,225,116, 59, 34,204,193,
+    156, 27,201,244, 55,102,156, 70,172,206,242,107,132,213,163, 30,
+    147, 30,128, 97,201, 57,230, 38, 83,192,100,246, 49,167,228, 26,
+    177,142,152, 51, 93,102, 42,194,146,153,169, 99,167, 78,186, 66,
+     38,229,131,131, 41,207,174,121,235, 18, 29,118,229, 30, 19,115,
+    177,229,215, 79,228, 42,110, 97,113,241,232,  7, 95,208,173,134,
+    129, 87, 36,218, 32,204,148,134,153, 44,204,248,200,108, 45,183,
+     73,190,  8,211,169, 73,196, 61, 17, 86,143,122,212, 99,226,216,
+    133, 21,159,123,147,129,105,181,  4,136,182,202, 38, 98, 61, 98,
+    142,152,117,218,121,213, 68, 99,  2,129,106,172, 44,181,154, 45,
+    112,122,213, 20,126,137,195,168,140,104, 14,220,208,  7,104,138,
+    149,252,115,207,237,101,132, 35,196, 15,191,248, 98,112,238,169,
+     76,127,165,  8, 75,203,106,197, 24,201, 84,194, 25,102,229, 22,
+     50,189,  6,205,123, 79,222,185,  8,171, 57, 86,143,122, 76, 46,
+    188,156,249, 64,171,185,108,157, 61, 91,120, 69,194, 35,214,230,
+     45,157,115,148, 84,127, 49, 64, 50, 55,115,244,248,113,116,  3,
+    123, 39,167, 47,193,202, 14, 42, 94,145,219,200, 66,253,127,154,
+    127,185,211,133,246,150,173, 86,235,249,159,251, 36, 44, 45, 68,
+    204,145, 69, 88,196, 58,118,139, 90,157,169,138,180, 40,204,153,
+    136,204, 16, 70, 99,227,252, 26, 97,245,168,199,196,217, 70, 24,
+     51,237, 38, 86,124,153,229, 65,145,240,136,205, 91,234, 28,221,
+    110,247,210,110, 46,158, 62, 25, 40,  5,158,214,  2,112,121,180,
+    207,145,137, 46,155,226,103,171,199,201,129,153,123, 35, 68,196,
+     39,159,124,114,233, 99, 31,212,205, 70, 54,179, 16,231,197,181,
+    162,109, 81, 69,150,134,165,130,203,254,202, 12, 97,  8, 99,103,
+     36,235,229,146,245,168,199,100,192,171, 88,246, 85,168,118, 55,
+    193,150, 89,158,157,195, 75,235, 33,235,145,137,189,236,182, 29,
+     90, 68,  2, 69,199,142,204,206,206,129, 95, 30,  1, 78, 56,239,
+    165, 96,169,240,162, 42,249,149,171, 47,116,219, 94, 32, 64, 90,
+     63,225,174,  4,114,176, 72,164, 94,252,196, 39,154,239,125,103,
+    134,173,136,109,137,135,173,242,208,226, 86, 84, 24,132,129,231,
+     37,193, 79,225,176, 76,250, 26, 97,245,168,199,164,195,203,246,
+    150, 72, 69,204,136,181,133,151, 22,200,154, 23,166,251,119, 36,
+    203, 11, 39, 78,157,242, 23,126, 99,217, 60,186,236, 34,  7, 95,
+     84, 69, 49, 23, 34,217, 79,162,188,  2,195, 73,253,179, 31, 25,
+    134,225,139,127,243,231,233,201,211, 57,188,204,  5,209,134, 98,
+    153,145, 20, 71, 82, 17, 88, 33,102, 17,230, 23, 85, 96, 21,194,
+    106,138,213,163, 30,147,  3,175,140, 92,249,  6, 28, 90, 36, 22,
+    137, 82,120,113, 21,188,132, 69,146,185,233, 19, 79, 60,161,136,
+     60,217, 85, 40, 85,117,194, 46,151, 88, 68,  6,101,132, 68,213,
+     34,204, 89, 12,105,212,146, 87,129,225, 40, 48, 66, 68, 34,154,
+    155,159,127,238,111,126, 74,150, 23,173,139,212, 17,167,240,138,
+     36,243,146, 98,215,106,218,138, 10, 27,234, 99,246,222, 44,252,
+    118,155,184,214, 94,178, 30,245,152, 48,120,185,121,151, 41,162,
+     79,201,197, 98,167, 26,245,144, 51,229, 37, 34,121,231, 85, 45,
+    194,157,246,194,147, 79, 52, 91, 45,112, 48,227, 34,204, 82, 10,
+    115, 66, 17, 17,146, 25,233,101,123, 53,209,152, 48,223, 82,140,
+     48,239,101,239,231,255,206, 15,127,242,201,179, 39, 62,241, 34,
+    119, 90,110,149,173, 41,250,200,234, 42,180,216,245,154,146,247,
+      8, 35, 64,133,102, 70,210, 80, 12, 76,129, 43,213, 94,178, 30,
+    245,152, 84,120,217,166, 55, 32,  0,102,247, 13, 83,164, 58, 98,
+     30, 90,120, 13, 89, 51,164,116,203, 98,123,  9,131,214,153,147,
+    115,115,115,249, 58, 33, 39,246,  2,207,219, 17,165,174,145,178,
+    208, 43,  3, 24,122,228, 34,215, 76,102,245,247,230,  7,  6,224,
+    236, 37, 82, 18,117,118, 40,122,239,123,222,187,125,103, 99,253,
+     27,223,150,193,144, 25,  4,133, 73,152, 73, 16,152, 64,  0, 25,
+     72,  1, 40,244,148, 20, 34,128,  0,129, 89,219,141,128, 32,  2,
+    156,173,158,242,247, 93,203, 66, 52,169, 23,120,215,163, 30, 15,
+     11, 94, 99, 55, 67,179,150, 80,219,181,217,182, 78,149, 71,162,
+     83,204, 57,182,145,195,128, 78, 29, 63,122,228,136, 83, 40, 95,
+    168, 85, 45, 70, 94,228, 48,134, 28,  5,150,125, 76,111, 84, 57,
+     27,  9,128,  0,193,152, 64,141, 28,145, 71, 68, 20,  4,234,195,
+     31,251,232, 55,146,120,227, 91,223,145,225, 72,  0,153, 65, 16,
+    152,132, 89,  4, 41, 32, 16, 64, 17,195, 62,191, 72, 67,132,  0,
+     25,133,  4,  5,  5,  4,  5, 68, 16, 57,157,185, 68,  0,179,137,
+     36, 56, 29,122,160,166, 88, 61,234,113, 48,228,130, 93,226,102,
+    177, 93, 37,108, 51,123,187, 66,136,217, 36, 95,177,176, 57, 57,
+     61,120, 41,  5, 39,142,158, 60,125, 58, 83, 93,110, 97,189, 23,
+    120, 89,174,144, 21, 92, 62,195,148,197, 87,102, 49,115,129,230,
+    152, 71, 39,191,183, 68,243, 44,164,  5, 33,102, 63, 63,  8,195,
+     15,126,244,163,205,103,223,145, 32,102,117,183,246,175, 74, 47,
+    251,113, 88,102,129, 49,171, 14,203,214, 75, 90, 47,233, 76, 41,
+     64, 33, 17,195,218, 76,214,163, 30,  7, 32,187, 42,108, 99,190,
+    117,118,222,207, 75, 76, 82, 52, 98, 30,106, 61,212,122,200, 58,
+    102,150, 18,188,132,144, 87, 22, 79,157, 57, 67, 78,145,106,121,
+     97, 99,166,166,200, 85, 94, 42,  3,151,242, 62,181, 34, 44,151,
+     82,133, 90, 10,  4, 68,171,191, 42,204, 35,250,226, 78, 41, 69,
+    212,  8,195,143,124,234,147,223, 84, 52,248,241, 43, 60,140, 24,
+    144, 25, 24,133, 81, 24,136, 81, 24, 41, 64, 96, 68,149,229, 92,
+     89,120,103,182, 80, 50, 91,122,  3, 18,130,  8,  8,138,  0,178,
+      8,154, 13,190,125, 33,102, 13,104, 45,196,234, 81,143,  7, 28,
+    120,129,179, 30,200,216, 29,179, 71,163,215,143, 80, 36, 74,157,
+    163, 54,203,131,202,183,228, 48,224,229,133,211, 79, 61, 69, 68,
+    246,148,205, 49, 89,172,145,168, 48,140,164,204, 40, 73, 48,  7,
+     96, 78,  2,150,247,168, 71,  0,  8,198,205,105, 82, 46,193,188,
+     95,212,106,182, 62,242,137, 79,252,101,163,177,253,157, 31,202,
+    112,100,152,101, 92, 36, 35,105,  4, 38,  9,128, 24, 80,  1, 42,
+     48,202, 43,131, 81,106, 24,  9, 68,  4,  5,205,174,146, 66,102,
+    247, 34,144,130,119,172,237,100, 61,234,113, 64,240,114,201,  5,
+    217,186, 31,103,  3, 33,179, 88, 48, 22, 30, 25,217,149,222, 94,
+     76, 45, 88, 90,109, 31,  6,120,242,216,153,211,167,137,168,184,
+    180,198,197,138, 21, 80,101,116, 21, 57, 70,202,151, 96, 89, 97,
+     24, 21,166, 31,141, 46, 10, 10, 75,145,  8,137,137,125,237,229,
+    252, 92,165,148,162, 70, 24,190,255, 67, 31,250, 43,129,237,239,
+    254,128,135, 35,  1, 96, 70, 70,208, 86,136,105,148,  0, 41, 64,
+    100, 68,  5,233,190,221,  8,130,222,159, 39,104, 18,125,200,133,
+    152,164, 91, 45,213, 20,171, 71, 61, 14, 60,240, 42,144,139,  1,
+    116,186,123, 16,199,156,246, 46, 53, 75,  6,115,143,153, 46,111,
+      4,177,129,253,201,211,167, 41,239,137,143,110,145,189, 27,164,
+     23,243,121,165, 12,188, 84, 62,204,149,164,200,190,243, 44,100,
+     54, 13,153,215,175, 34, 66, 96,141, 93,177,144, 63,207,213, 20,
+    145, 38, 82,100,127,133, 82, 74,  5, 65,248,190, 15,188,255, 39,
+    157,246,173,191,250, 30,223,217, 10, 16, 83, 33,134,162, 33,189,
+    192, 72, 10, 49, 64, 82, 89,143, 67,244,118,142, 19,  4, 18, 17,
+    192, 28, 94,233,  5, 91,134, 81, 53, 59, 89, 83,172, 30,245,184,
+     31,217,229,122, 64,119,146,209,172,  8,204, 91,254,177,142,132,
+    217, 58, 35,200,225, 37, 44,160, 59,173,246,153, 83, 71,143, 28,
+    193, 50,188,192, 53,143, 69,207,168,236,123,229,193, 43, 35,152,
+     50, 20, 51,183, 67,155,192,251,141,193,242,245,224,129, 69,151,
+    228,232,226, 84,130, 57,248, 74, 37,152,202, 62,  6, 42,228,240,
+    217,103,159,237,116, 58,111,124,251, 47,229,198,109,  6,100,128,
+     44, 17,211, 72, 26, 37, 32, 98, 16,  5,100,188,164,161,152,223,
+     65,209,  4, 92,230, 14,165,118, 50,149,134,134, 98,104, 55,153,
+    171, 41, 86,143,122,220,151,236, 18,  7, 67,185,230,242,247,109,
+    116, 90,254,  9,139,243,157, 22,121,194,  0,201,236,244,194,217,
+     39,230,230,102,157,173,206, 16, 43,245, 87, 94,199,224,103,233,
+     46,181,220, 79, 45,189, 84,209, 64,146,151,129, 89, 33,100,242,
+    123,241,188, 37, 33,113,209,165,234, 76,130, 25,253,165, 20, 43,
+     21,  4,193,201,147, 39, 59,157,206,143,191,245,109,190,116,149,
+    153, 25, 81, 91,120,105, 36, 13,162,145,  2,148,  0,145,145,200,
+     82, 12, 11,221,250, 77, 89,  5, 32,185, 46,178, 76, 49,  7, 89,
+    146,191,176, 72,141,177,122,212,163, 10, 94,233, 43,126, 25, 67,
+     46,185, 50,207,152,216,192, 43,102,214,246,155,140,252,202, 58,
+     79, 48, 97,178, 52,127,252,236, 89,211,207,222,135,151, 95,170,
+    154,151,111,149, 51, 46, 85, 64,152,131, 47,195,153, 98, 77, 24,
+    146, 87, 31,145,201,152,192, 82,194,199,166, 65, 88,174,192,216,
+    252,116,118,224,149,129,108,110,110,238,133,151,126,230,  7,237,
+    239,196, 63,189,200, 81,204,146,122, 73,141,162,193,224, 12, 53,
+    146,114, 40, 70, 32,100, 52,159, 13,197, 76,221,106, 58,225, 56,
+    142, 98,102, 26,211,206, 81,218,  7,198, 87,104,245,168, 71, 61,
+    252, 61,190, 36,205,145,109,110,229,147,203,120, 70, 55,243,114,
+    103,253, 51,217, 37,  0, 58, 12,224,232,242, 19, 79, 60,161,130,
+    192,219,168, 49,195, 73,161,133,151,165,151,202,216,229, 36, 94,
+    129,115, 33, 40,249,200,220, 64, 22,234,239,243, 89,205,188,126,
+     85, 10, 11,136,  8,133, 51, 13,166, 72, 49, 49, 27,126, 25,118,
+    217,183,128,153, 57, 96,110,181,219,239,251,208,  7, 95, 95, 90,
+    184,253,227,151,131,181, 13,  6, 52,182,145, 57,151, 99, 10, 73,
+     35, 42, 20,133,168, 16,149,100,229, 96,198, 59,166, 47, 26,146,
+    115,220,163, 24,218, 87, 15,176, 20,115,181,152,179,230,180,  6,
+     89, 61,234,145, 75,175, 98,185,131,237,129,227,206, 51,218, 29,
+    179,153,197,227, 29, 88,217, 37,  0,201,116,183,123,250,196,202,
+    242, 10, 22,186, 65,103, 20, 43,212,214,151,  4,151, 59,  2,165,
+     84, 16,168, 32,112,  8,230,114, 44, 47,162, 64,127,254,177,176,
+     29, 81,144, 22, 83, 72,158,130, 17,161,  8,146,144, 16, 19, 41,
+     69,204, 86,130,113,192, 14,187, 20,155,207,130, 32, 96,150, 48,
+    124,234,236, 83,243,115,243,175,126,239,251,250,202,117,197,172,
+     80, 52, 98,144,122, 73, 84,233,123, 10, 12,191, 44,194,  8, 16,
+     65, 40,165, 82, 30,240,167, 91, 73, 26,123, 41, 32, 32,230,243,
+    244, 50,160, 85,110,149, 32,171, 41, 86,143,199, 28, 91,  0,142,
+    224, 18, 17,237,151, 71,228,202, 43,237,168,204,  5,225,150,201,
+     46,227, 25,143, 61,249,100,167,211,129,242, 14,218, 80,220,124,
+     35,223,208,199, 15,188,200, 85, 91, 65,144, 94, 14,130,  2,190,
+     40,207,191,148, 93,208,157, 21, 96,248, 27,172,  1,  6,238,214,
+    106, 88,114,144, 68, 66,164,148, 98,102, 98, 86,204,172,148, 10,
+     84,192, 86,121, 49,179,176,112,192,166,201,198,252,252,252,123,
+     62,246,209, 87,254,250,175, 71,175, 95,226, 94,159, 17,217, 81,
+     94,206,123, 84, 72, 25,194, 20, 34,129,160,141,198,156, 61,222,
+     82, 24,101, 20,203,221,101,201, 84,186,138, 89,106, 57, 86,143,
+    199,144, 92,146,151,104, 57,130, 11, 28,217,149, 98, 43, 77,235,
+     57, 77,190,202,  4,204, 61, 99,171,169,142,173, 60,113,234,148,
+     82,170,  2, 94,165,192, 62, 95,149,173,138,105,125,174,185,  2,
+    243, 63,200, 63,122, 14, 50,173,172,200,218, 81, 84,245, 99, 77,
+    239, 73,224,134,125,174,250, 19, 17, 50,255,172,246, 50, 35,  8,
+      2,102,163,195,  2, 78, 57,198,129,  4,233,126, 35, 34,173,102,
+    243, 93,239,126,247,141,229,229, 43, 47,191,162,111,220, 54, 66,
+    204,  0,203, 85, 97,233,149,144,106, 49, 66,164, 92,142,229,  1,
+    191,183, 97, 82, 26,122,165,246,220, 53,149, 70,142,165,182,189,
+      6, 89, 61, 30, 75,181,149,113,135,157, 86,206,185, 91,  4,209,
+    194,  9,123,179,141,229,200,204,228,245,169,236,154,159, 93, 60,
+    125,106,110,118,214,219, 10, 49,171, 94, 24,  3,175, 66, 93,189,
+    107, 24, 29,108,217,207,172, 18, 11,130,192,203,191, 92,255,232,
+    172, 31,242,118,  2, 49,235,135,210,240,  9, 12, 18,220,121,207,
+    148, 97,164,136, 36, 35, 88,134, 48, 97, 97, 49, 18,204,192, 43,
+     72, 59, 95,139,200,209,163, 71,103,103,103, 95,187,112, 97,116,
+    233, 74,176,211,211,130, 10,197, 72,173,  4, 41,200,225, 69, 42,
+    179,147, 57,194,192,163, 88,161,211,142,185, 44,104,230,128, 83,
+    147, 89,131,172, 30,143,153,212,  2,168,192,150,228,216,242,170,
+    186,204,122, 70, 87,127, 73, 53, 10,211,159,166, 59, 45,117,100,
+    249,137,147, 39, 85, 16, 20,100, 23,248,219,104, 87,194,203,173,
+     21,117,224,165, 44,184,220,161, 84,250,193,175,160,200,151,113,
+    123,240,202, 86, 62,154,187, 18,216,213,134, 89,138,238,210, 11,
+     81,144,132, 20, 41, 33,118,  8,198, 65, 16,  8, 51, 75, 32, 22,
+     95,146,  9, 48, 73,149,103,171,213, 62,255,174,119,173, 29, 61,
+    250,214, 43, 23,240,214,154,138, 99, 37,152, 75, 48,112, 84, 88,
+    198, 47,176, 90, 12,170, 65,150,  7,245, 70, 67, 10, 74, 26,150,
+    217,116,204,250,202,253,128,172, 48, 77, 83,143,122, 76, 62,179,
+    202,216,114,235, 33,210, 38,130,190,230, 42,240, 11,118, 37,151,
+     40, 74, 22,231, 86, 78,159,158,158,154, 42,182,145,246,225,133,
+    133,158,129,222, 82,233,188,210,189, 96, 24,131,208,251, 16,248,
+    240,114,179,251,180,236,190, 80,187,234,136, 47,  0,  8,210,164,
+     60,149, 96, 14,192,172,139, 20, 17, 81, 66,162, 72, 68,177,100,
+    176, 98,230,192,124,200,201, 37, 89,105, 92,218,  0, 13,100,113,
+    113,113,230, 67, 31,188,124,229,202,198,235, 23,245,157, 77,197,
+    172,173, 97, 84,226,193, 75,153,  2,177,220, 78, 66, 14, 50,  1,
+     52, 25, 25,102, 51,149,182,120, 76, 28, 81,107,126, 53, 26,105,
+    182, 55,200, 32,223, 78,184,  6, 89, 61, 14,143,218,114, 23,241,
+     56,125, 32,116,105,122, 49,213, 95,246, 74, 25,243,163, 83,114,
+     33, 38, 51, 83,221,227, 71, 79,173,172,152,197,216, 21,158, 49,
+    211, 92,174,246,202,219, 74, 80, 94, 35,145, 37,244, 70, 96,133,
+     65, 16,132, 65, 24,250, 20,203,209,166,130, 64,169,128,108,210,
+    159,215,126,149, 26,175,186,187,186,  5,206,159,226,192, 53,237,
+    171, 35, 25,194, 20, 41, 33, 17, 37, 98, 61, 99, 16,  4, 30,184,
+     36, 39,152,109, 37,157, 99,251,204,233,211, 43,203,203,151, 46,
+     94, 28, 92,187, 17,108,237,104,102, 66, 84,  8, 36,168,144, 40,
+    229,151,184,113,152,178,157,118,148, 32, 33,160,177,150,198,226,
+    102, 91, 90,138,215,134, 45,175,189, 40,130,172,194, 90,186, 93,
+     46,100, 76,189, 95, 61,234, 49, 25,130,107, 55,108,217,221,100,
+     11,178, 43,189, 38,177,107,128,170,101,151, 33, 23,161,238,118,
+    194,229,197,211,199,143,135, 97,232,166,205, 99, 60,163,173,245,
+     42,244,195,113,224,101,125,161, 51, 50,201, 21, 26,140,133,110,
+    118, 31, 84,184, 71,242, 26,226, 64,161,195,150,229, 23,162, 73,
+    190,220,134, 93, 64,132,  0, 36,  2, 41,193,148, 40, 33, 17, 35,
+    194,198, 13,200, 62,184,  7,200,252,241,157, 78,231,233,115,231,
+    122, 39, 79, 94,122,253,245,232,198,109,181,211,215,166,175,180,
+     72,  6,172, 28, 94,246,211, 92,130, 33, 42,163,194, 28,156, 33,
+      0, 66, 74, 52, 41,246,195, 77, 11, 67,140, 57,206, 50, 50,176,
+    235,195, 51,227,233,227,170,102, 89, 61, 38, 72,112,185,229,163,
+     92,165,182, 50,102,185,202,203, 37, 26,148,150,110,139,127, 65,
+    119, 90,184, 56,127,226,228,201,102,179,  9,165,189,117,220, 10,
+    175,138,181, 65, 88,174,174,183,153, 87,102, 25,195, 48,116, 25,
+    150,193,203,185,214, 22,130,145, 87,249,133,101,  5,230, 78, 28,
+    128, 89, 63,  4,  5,  9,230,  6, 97,150, 98,  6, 97, 74, 21,146,
+    174, 10,205, 85, 65, 46,119, 76, 77, 77, 61,251,220,115,155,167,
+     54,175, 94,186, 20,221, 90, 85, 59,125, 66,212, 54,182,119,225,
+     69,227, 66, 49,107, 45,209,128, 12, 28, 81,230, 77, 89,122,123,
+    169,184, 25, 25,166, 12,243,220, 37,212, 44,171,199,164, 96,171,
+    156,109, 85,152,196,242,101,183,194,171, 18, 88, 80, 34, 23, 44,
+    204,173, 28, 63,222,237,116,188,170,208,113,178,203,201,188,104,
+    156,109,116,224,149,139,172, 48, 27,198, 71,250,240, 82,121,  0,
+    150, 59,199,194,172, 99,149,248,  2,183,254,190, 32,193, 32, 95,
+     62, 46, 78, 16,230, 32, 12,202,242, 11,100,252, 73,238,238,165,
+     52, 55, 55, 55, 61, 61,189,181,181,117,237,210, 91,209,234,186,
+    234,245,137,153, 16, 82,144, 57,252, 34, 79,136, 65, 33,221, 71,
+    123,125,198, 50, 91,220,230,177, 12,220,248,223,110,146,  4, 54,
+     58,203, 86,120,213, 44,171,199, 65, 15,172,164,137,236,109, 18,
+    199,161,138,237,101, 22,209,105, 10, 63,158,143,230, 61,161,238,
+    180,113,126,118,229,216,177, 49,228,242,211,174, 66,230,149,245,
+    115, 46,182, 55, 37,107, 28, 85,230, 19, 51,118,  5,238, 91,224,
+     41,175,140, 94, 94,236, 85,220,202,182, 66,124,249,249,151,157,
+    136,116,251, 97, 16,129,233, 25,145, 39,243, 74, 84,154,206,167,
+      4, 43, 62, 32, 82,129,174,108,227, 54, 68,208, 90,167, 20,155,
+    157,157,126,238, 93,189, 94,239,218,149, 43,163,155,183,105,167,
+     79, 90, 27,123,104,202,193, 84,129,101,217,110, 70,185, 16,203,
+    182,200,205, 68, 89,186,245,145,207, 50,243,203,165,114, 87,116,
+    183,238,127, 23,150,185,121, 89,197,236, 77,125,106,214,163,138,
+     86, 48,238,228, 16, 40,119,  4, 76, 75,183, 82, 30,121,241,214,
+     56,132,177,128,185,176,159, 95, 42,138,116,167, 29, 44, 45, 28,
+     63,122,180,101,221, 98,217, 48,150,101, 87,117,157, 68, 33,243,
+    202,164,151, 27,121, 25,120,101,232,242,117,152,101, 87,154,220,
+    231,225,151,227, 28,193,111,155, 83,248,243,178,250, 85,201, 38,
+     34, 69,160, 92,225, 65, 68,230, 60, 86,160,188,249,197,172,165,
+    108,182,227,146, 20,101, 87,174,252,156,163,160,181, 70, 68,208,
+    122,106,106,234, 29,207, 60, 51, 58,115,230,198,205, 27, 59, 87,
+    111,192,206,142, 26, 70, 70,100, 37,136,132,144,  7,249, 14,200,
+     82,198,101,101, 22,246,178,105,171,143,150,101,148,109, 59, 46,
+    128,254,110,154,224,239, 62, 57,142,101, 96,203,255, 37,127,241,
+    220, 27,103, 53,209, 30,123, 90,217, 72,214,127, 62,236, 71,106,
+    165,151, 93, 90,101, 32,203,175, 44, 98,107, 23,253,  0,  0,186,
+     25,114,167,211, 57,178,188,180,180, 20,  6,193,158,228,  2,187,
+     78, 58, 87, 94, 68,165,201,198,162,109,244, 74, 84,173,248,114,
+    254, 59,174, 49,180,177, 87,154,223,147, 82, 94,205,106,185,216,
+    222,157,118,204,238,120, 80,117,204,109,163,231,244,219,210,198,
+    214, 41,182, 76,150, 15, 42,223, 37,174,178, 36, 52,207,159,208,
+    149, 96,238, 65,201,132, 24, 34,182, 90,173,211,167, 78, 39,199,
+    142,175,175,175,175, 93,187,158,220,217,164,193,144, 88, 19, 98,
+     82,150, 93, 96,251,235, 23,138,197,108, 58,134, 62,203,208,122,
+     76,203, 50, 65,191,203,218, 56,150,185,182, 81,242, 70,110,123,
+    227, 76,188,103, 70, 77,180, 71,155, 86,187,236,229, 35,149,204,
+    202, 42,182,178,114, 83,183, 98, 62,223, 11,214, 65, 88,134, 45,
+    115, 97,191,129, 26,162,110, 55, 97,118,122,110,101,101,110,118,
+    150,148,170,190,211,238, 41, 80, 50,140,224,247,147, 40,  7,246,
+    148,205, 30,250,202,171,196,175, 84,129, 57,213, 19, 42, 40, 57,
+    199, 76,219,121, 11,182,177,186,137,108,224,162, 43, 91, 53, 45,
+    158,139,204,162,176, 84,131,121, 15, 78,126,102,138,247,128,250,
+    252, 42, 21,113,160,246,133, 88, 54, 86, 86, 86, 22, 23, 23,123,
+    189,222,173,155, 55,135,171,107,180,181, 67,113,162, 33,163, 82,
+     78, 49, 85, 84, 97,222,167, 70,130,185, 44, 67,251, 41, 34,144,
+    100, 96,207,113,  6, 14,206,178, 99,229,224,204,101, 25, 56,171,
+    196,115,156, 85,153,205,154,104,143,158,176,218, 51,130,207,236,
+     97,133,206,178, 43, 19,129,211,205,171,203, 83,138, 69,231,152,
+    239,244, 83, 84, 90, 99,239, 12,135,129,158,234, 52,230,231,150,
+    151,151,219,237,118,113,253,207,238,228,170, 90,146, 93,222,125,
+     99, 23,120, 21,249, 21,230, 73, 88, 81,124,121,173,191,138,229,
+    170, 57, 69,161, 66,124,149,244,151, 83, 75, 33,146,183,225,  7,
+     48, 26,140,211, 44,204, 60, 56,169,143,180,150, 81,198, 88,232,
+    221,118,176, 76,  7, 51,103, 20, 99,102, 68,156,158,158,158,154,
+    154,138, 79,159,222,216,216, 88,191,117, 43, 89,223,160,193,144,
+     18,109,154, 85,160, 64,133,236, 74,185,  6, 25,206,208, 94, 46,
+    216,201, 20, 94, 98,  5,154, 43,205,  0, 48, 93, 88,153,215,249,
+    150, 15, 91,182,172,220,193, 89,246,188,245,112,230, 18, 13,198,
+     90,206,  7, 10, 53,169, 73,115, 32,156, 66, 40, 60,172, 69,102,
+    185,203,167,211, 60,203,210,170,176,154, 90,198,184,197, 92,136,
+     57,159,234,187, 89,240, 38,138,116,187,133,115, 51,179,139,139,
+    179,179, 51, 74, 41,128,187, 35, 23,140,221,106, 22,203,105,253,
+    184,216, 43,197,151,143, 45, 91, 71, 17,102,101,171,233,226, 34,
+     82,222,198,143, 78,175,  9,215, 32, 85,110,167, 24, 84,204,141,
+    160,123,106,162, 83,214,154, 27, 73,115, 90, 42, 80,165, 71,220,
+     57, 14, 80,220,215,168,140,243, 84,130, 49,187, 20, 19,251,105,
+    163,209, 88, 94, 94, 94, 92, 92, 28, 13,135,171,107,107, 59, 55,
+    111, 37,189,190, 26, 12,137,133, 77,229,151, 83, 17, 86, 86, 97,
+    104,233,230,238, 53,153,251,202,140,101,230, 30, 10,148,112,102,
+    136, 38,224,108, 70, 89, 38,154, 20,158,  9,197,236, 44,111,151,
+    237,105, 52,128,253, 65,173,144,249,222,223, 57, 44, 53,164,202,
+    223,141,119,245,179,197,153,177, 25, 23,102,153,141, 20,157,  5,
+    137, 41,158,138, 61, 33,138, 87,122,202, 75,246,245,136,217,215,
+     60, 36,221,110, 74,167,221, 93, 94, 90,152,159,111, 52, 26, 48,
+     78,112,237, 77, 46,175, 54, 53, 43, 79,117,235, 36,188, 22,208,
+    222,140, 99,161, 64, 53, 12,131, 48, 47,249,114,107,190,108,197,
+    170,221,174, 67,149,108, 99,190,207, 99,121,114,116,156,254,202,
+     93,100,222,153,198,126, 15, 17, 48,151,109,100,193, 53, 66,190,
+     67, 27, 22, 68,104,105, 10,195,182,218, 64,173,217, 26, 73,179,
+    190, 18,153, 13,200, 88,  4, 17,219,237,246,201, 19, 39,244,177,
+     99,253,126,127,253,206,122,255,214, 42,244,  7,  6,100,  8,192,
+      6,100, 86,112,161,151,232, 67,214, 37,145,192,185, 96,111,105,
+     54,214,205,246, 36,183, 56, 75,213, 89,122,  4,197,217, 70,216,
+     30,162,  2,209, 92,117,235,  9, 80,231, 53,218,211,104,  0,224,
+     30, 92,  7,122,165, 87,248,187,  0,146, 60,168, 51, 95, 14, 25,
+    149,118,253, 49,120, 63,191, 74, 74, 10,203,166, 90,134, 83, 46,
+    176,210,166,242,165,120,203,107, 54,239,139,175,236,154,244,242,
+    190, 31,  4,113,177,213, 94, 92, 88,158,155,107,183, 90, 72,180,
+     43,155,139,  9,125, 41,223,  1,111,175,198,170,253, 26,221,126,
+    132, 65,  5,191, 50, 88,165,162, 43,229,150,191, 96,200,237,151,
+    227,146,171, 80,173, 10,232, 85,113, 86,214,127, 85, 61,224,198,
+     69,250, 65, 24,  0, 24, 23,201,224, 31,164, 49,  7,203, 69,121,
+    118, 76,204,125, 77,136, 48, 73, 50,138,145,137,195,136, 88,107,
+    205,140,214, 75, 26, 35,203,204, 98, 12, 45,226,244,212,212, 84,
+    183,171,143,159, 24,244,251,235,119,238,244,111,167, 32, 67, 22,
+     11, 26, 73,213,147,128, 59, 35, 73,182,168, 34, 47, 28, 75,223,
+    251, 42,204,197,153, 11, 50, 68, 18,103, 58,213, 33,154, 57, 90,
+     89,209,172,255,106, 81,140, 17, 11,  7,202,217,219, 37,219,182,
+     36,127,226, 98, 33,105,188, 59,180,237,113, 22,200, 67,161,201,
+    193,195, 13,239,246,190,163, 39,132,171, 15,203, 46,185,123,145,
+     86, 78,149,105,118, 61, 59, 20,147, 42,241,229,198, 94, 50,166,
+    100, 75,118,137,216,144,116,187,181, 95,108, 85, 10, 46, 39,161,
+    135, 42,114, 21,100,151,103, 26,169, 74,121, 41,127, 69,118,  9,
+     91,110,181,106,  9, 94, 10, 75, 21,171, 94,236, 53,254, 17, 13,
+    198, 60,190, 94, 16,230, 43, 11, 34, 98,  0,103, 95,218,194, 97,
+    171, 84, 91,136,229,100,142,144,144,146,188, 79, 99, 38,192,172,
+     10,203,133,152, 67, 49, 17, 65,162,169,233,233,238,212, 20, 31,
+     63,222, 31, 12,238, 24,144, 13,134, 52, 24,145,214,  8, 98, 56,
+    194,144,110, 49,105,147,175,146,157,116,244, 90, 94, 53,230, 70,
+     99,  2,142,187, 68, 74,165,169,103, 57,115,105,102, 55, 17,207,
+    178,198,130,247,244,184,230,100,139,222,151,164, 98, 62, 68,156,
+    202, 23, 41, 61, 65,221,109, 50,177,226,  4,192, 93, 78,227,201,
+    144, 97,248, 64, 88,122,247,170,170,172,173,138,151,197, 22, 62,
+    102,192,170,242,137,238,123,225,116,107,159, 92,115, 73,149, 22,
+    219,253,224, 86,126,137, 21,113,187, 37,173,230, 56,108, 85, 38,
+     92, 37,209,181, 71,212, 85, 53,205,152,243, 75,149,150,  8, 21,
+     27,226,132, 89,127,137,226, 10,161,172,237,106, 65,121,217,212,
+    190,  4,175, 98,236,133,251,225, 87, 33,  8,179, 89,190,157, 79,
+     36,  2,102, 27,132,249,235, 13,253,250,216, 10,138, 21,142, 74,
+     66,  9, 38, 72, 68,148, 36,121,205,154,214,154,136,181,102,127,
+    152,251, 33,110,183, 30, 17, 68,156, 74, 21,217,241,209,104,184,
+    185,185,181,189,182,198,189, 62, 13, 70,106, 20,165,  0, 23, 64,
+      0,134,180,127, 43, 58,243,152,232,106, 49,119,154, 18, 60,195,
+    152, 87,195,186,251,  7, 72, 73,163,217,202, 19,175,103,153,228,
+    152,242,184,102, 27, 96,227,152,249,120,116,152,133,206, 51, 90,
+     74, 79,111, 41,213,245, 73,213,163, 93,104,179, 13,187, 87, 87,
+    190,125, 50, 12,239,255, 70,197, 63, 52,191,162, 16, 90,249,168,
+      2, 17,143, 89, 25,173,220, 60,158, 11,215, 88, 96,121, 20,203,
+     57,149,111,176, 40,251,240,134, 99, 75,183, 90, 45,234,182,187,
+    243,243, 51, 51, 51,205, 70, 99, 47,108,249,217,  5, 86, 87,210,
+    195,248, 84,218,221,107, 86,249,240,242, 58,169, 58,174, 81, 21,
+    123,226,184,  5,170,249, 59,149,214,122,145,242, 11, 38,208,209,
+     94,224,192, 11,112,143,135, 60,216, 69,101,103,255, 49,109,224,
+    128, 37,132, 49,216,  8, 63, 37, 92,161,232, 13,252,109,120,137,
+    144, 40,241,187, 99, 39, 73, 98, 62, 32,145, 46, 33, 44,181,147,
+     68,100, 26, 37,162,213, 96, 14,200,140, 80, 84,157,110,187,221,
+     57,114,228, 72, 28,199, 59, 59, 59,155,119,238, 68, 91,219, 48,
+     24,170, 76,148,229,189,119,128,197, 54, 15,131,220,102, 22,112,
+    150,170,179, 28, 91, 46,200, 42, 84, 88,  6, 71, 23,106,144,221,
+     88, 50, 87,157,114, 13,138,117, 45,153,247,148, 92,161,137,191,
+     84,160,218,235,151, 31,188,202,178,198,113, 25,194,216,197,170,
+     15, 96, 50,225,190,106, 17, 96,247,186,170, 49,156,146,114,165,
+    104, 73, 94,101,107,221, 28, 84,185,215,123,179,135,187,130, 44,
+      5,150, 87, 27,225,210,109,159,144,146,170,105, 68,105, 53,195,
+    153,169,249,217,185,169,110, 55,112,138, 78,247,233, 19, 97, 92,
+     25,125, 85,235, 46, 55,237,170, 10,235,199,194,203, 47,250, 42,
+    119, 38,204,192,149,175, 18,202,160,152,109, 80,155,195, 43, 63,
+     41,156,109, 98,199, 63, 79,130,221,131,  2,183, 48,223,236, 91,
+    102,142,161,131, 48,193,146, 88,200,238,  5, 22, 85, 97,134, 46,
+     52,  9,152,135, 48,173,117,146, 20, 16, 70,204,154,136, 53, 51,
+     49, 89, 71, 41, 34,156,129,172, 52,154,141, 70, 99, 97, 97,126,
+    126,158,153, 71,163,209,214,246,214,206,157, 13,189,211,195,225,
+    136, 70,113,198, 50, 43,148, 32,157,202,132,220, 27, 86,  9, 49,
+    231,122,135,104,249, 52,101,  9, 94,224, 41,181,156, 86,249,151,
+     32, 45, 42,203, 35, 51,119, 97,128,100, 83,159,158,118,243, 30,
+     79,172,148,111, 99,108,253,125, 72, 30,169,186,237,253,104, 51,
+    217,189,126, 74,170,111, 44,254,109,220, 13,190,192,223,169, 16,
+     28, 54,249,151,253, 48,171, 82,115,249, 85,166,174, 16,203,108,
+     99, 81,118,229,215, 20,235,192,246,143, 45, 81,164,155, 13,105,
+     54,212, 84,183, 59, 59, 59, 61, 61, 93, 41,181,238, 17, 91,176,
+     91, 21,147,107, 24, 83,162,100,  2,169,104, 27,109,187,155,192,
+    237,  0,237, 55,179,175,144, 93,206,182,143,182, 57,180, 19,176,
+     21,218,171,162,255,220,222,237,137, 22,236,231,  5, 51, 91,222,
+     13,144,199, 97, 22, 97, 44, 64,174,101,202,151, 90, 22,242, 47,
+    180,201,125,102, 21, 41,195,149, 97, 87, 74,177,236,141,137, 52,
+     51,105,157,162,203, 25, 70,144, 73,149, 22,203,158, 97,132,168,
+     58,157,118,187,125,100,121, 69,107, 61, 28, 13,183,119,118,122,
+     27,155, 62,203,114,143,153, 97, 69, 91,156,129, 31,231,147,135,
+    173,146, 16,  3, 40, 66,205, 89,244,144,177, 12,160,160,215, 28,
+     33, 38,121,255, 50,112,103,114,115,199,151,191, 40, 33, 56,171,
+      1, 60,223,224, 61,240,184,235, 60, 28, 30,140, 67,148, 10,155,
+     91,165, 50,220,169,180,162,203,205,247,239,130,106, 60,229,242,
+    198,153, 19, 44,244, 38,  5, 40,114, 10, 96, 12,170, 74,162,204,
+    208,170, 58,182,207,110, 80, 42,100,216, 27, 91,227,153, 53,211,
+    237, 78,181,154, 77, 34,218,135,212, 42,154,196,202,140,107, 79,
+    205, 53, 54,237,170,174,242,242, 82,123, 85,  6, 88,  1, 91,165,
+    180, 43, 23, 95,233,174,142,227,224,133,251,124, 94,  6,251,152,
+    171,129,172,162,162,140, 48, 17, 98, 16,242,154, 21,102, 39,121,
+     33,193, 79,239, 43, 89,144, 17, 17, 37, 58, 73, 63,106,143, 98,
+     68,202,216, 73,102, 67,177,114, 34,150, 35,108,140, 22,179, 75,
+    204, 17, 68,144,168, 27,  4,221, 78, 87, 86,142,104,173,135,195,
+    225,206,206, 78,111,115, 83,239,244, 32,138,213, 40,162, 56, 49,
+    249,184, 43,205,210,214,135,153, 35,147, 10,108,145,215,161,191,
+    240, 85, 39, 26,203, 29,118,222, 57, 54,163, 21,166,175, 18, 78,
+    243, 31,219, 90,214,179,150, 37,198, 57,  2, 13, 43, 83,252, 76,
+     26, 99,249,249, 47, 37,154, 61, 24,120,237,102, 51,165,240,191,
+    202,  0, 58,218,170, 60, 27,232, 56, 68,103, 81,151,107, 12,221,
+     73,195,242,  4, 98,149,127,172,  6, 25, 87,125,117, 76, 33,131,
+    236,143,212,  0,  0, 28,  6,220,108, 72, 24,170,169,206,238,204,
+     26, 39,181,118, 81, 91, 62,182,160, 80,  0, 80, 25,117,141,139,
+    234,171, 23, 55,170, 34,187,242, 13,132,  2, 15, 93,206, 94,142,
+    123,  4, 94, 69,120,249,149, 72,247,195,175,226,116,100,142, 48,
+    231,119, 56, 94, 18,179,141,217,192, 93, 34,100,  9,102,143, 85,
+     26,219,155,220,139, 52,105,107, 24, 51,138,153,145,169, 48, 67,
+     49, 19,135,101, 35,219, 58,132,153,169,152,236,143,  5,153,  0,
+     16, 98, 48, 53, 53,213,237,202,202,138,102,142,162,104, 48,232,
+    247,122,253,225,198, 38, 12,  6, 24, 37, 20, 39, 70,154,185, 56,
+    131,172,248, 43,133,136, 91,211,144, 71, 99, 96,101, 26, 20, 68,
+    153,175,197,192, 93,214,158,151, 98, 56, 97,191, 56, 55,206,226,
+     87,241,204, 96, 70, 45,148,210,228,102,225,  9, 47,176,159, 56,
+    180,170,148, 19,171,214,169,148, 42, 16,100,207,243,214, 59,197,
+    165, 52, 35, 33,254,151,188, 50,209,236, 39, 72,113,222,176, 50,
+    228,  2,135, 77, 80,149,115,193,190,253,227, 94,101,163,178, 31,
+    102,137, 34, 14, 67, 14,  3,108,183,154,115,179,211,157, 78,187,
+    221,110,132,225,190,153, 85,144, 90, 80,209,220,  6,138,205, 81,
+    247,227, 22,211,247, 46,184,118, 85, 94,121,148, 21,120,197, 19,
+    170,224, 21, 11,171,130, 20, 17,209,252,252,194, 65, 40,253, 96,
+     31,183,113, 16,  6,214, 72,250, 79,105, 35,196,192, 32,206, 81,
+     95,160,243,  3,105,145,159, 30, 49,163,179, 18, 75, 46,247,189,
+     46, 13,165,181, 86,202,196, 97, 69, 33, 38, 66,101, 71,105,125,
+    101,230, 37, 29,144,129,136,160, 85,109, 72, 20, 40,213,110,183,
+     23,230, 23,228,196,137, 56, 73,134,195, 97,191,223,239,111,109,
+    233,254,  0,162,136,140, 52, 99,169,194, 89,246,108, 18,118,202,
+     83,193,115,133, 78, 51,178, 60,249, 66,175,204,194, 41,203,  0,
+    159,110,224, 86,252,123, 24,205,251,100, 87, 71, 99,251,155,  1,
+    192, 93,242,116,217,175, 30,147, 10, 67, 56,246,196,118, 97, 52,
+     54,125,175,116,145,110,237,104, 81,109,185,193,150,231, 25,193,
+    223,205,112,156,127,220,207,164, 68, 73, 53,142,  7, 22, 34, 55,
+      2,110, 52,160,217, 80,237,118,103,102,186,211,105, 55,155,173,
+     32,  8,198, 25, 35, 28, 63, 49, 83,237, 16,199,  4,243, 80,170,
+     65, 40,212, 45,121,216, 42, 40, 47,139,173, 74,118, 21,172,163,
+    242, 46,  6,110, 52, 86, 41,187, 14,104,182, 58,216,223,205, 28,
+     35,153,182,152,113,235,194,204, 87,217, 82,204,129, 63,160, 70,
+    141,140,136,230, 93, 22,222,231,177,189, 54, 22, 50,231,151,210,
+     58, 41, 35,140,180,102,165,204,251,178,151, 76,101,152,253,152,
+    226,202,180, 93, 76, 41,230,117, 92,180, 79,109,139, 51,251,190,
+     73,212,104, 52,102,166,167,229,200, 17, 97,142,227,120, 48, 28,
+     14,134,131, 81,127, 16,245,122, 48, 24, 98,148,144,214, 20, 39,
+    110, 74, 85,144, 69, 94,123, 68, 17, 44, 86, 45,160,183, 74,161,
+    112,217,121,121, 69, 55,219,170,138,240,177,212,  5,  5, 75,175,
+    215, 56,158, 86, 21, 98, 76, 42,151,215,200,158,181, 22, 50,206,
+     61,150,112, 38, 69, 61, 38,126, 53, 86,110, 32,139, 22,210,111,
+    152,  5,165,110,165,224,  1, 75, 74, 45,152,  1,224,238, 42, 69,
+    203, 34,107, 28,176, 56, 12, 68, 41, 14,  3,104,183,194,110,167,
+    213,105,183, 91,173, 86,171, 29, 26, 96,221, 63,179, 74, 14,209,
+    243,137,197,108,190,184,  0,104,111,114, 57,216, 26, 11, 47,101,
+    203,230, 51,127, 24,184,166,210,219,126, 54,133, 23,229,187,104,
+    191,189,252, 42, 32, 44,175, 11,195,172,183,  2, 18,176,  0,  8,
+     17,  8, 10,176, 19, 17, 99,198, 47, 55,188,207,  2,252,132, 52,
+    233,140, 95, 74,235,164, 66,130,105,173, 89,107,173,148, 30,135,
+     48,159, 98, 57,200,178,230,177,213,190,210,129,152,248, 21,187,
+    136,164, 84,179,217,156,155,157, 21,  0, 97,142,147, 36, 26,141,
+     70, 81,212, 31,244,163,141, 45,142, 99,140, 19,138, 34, 76,152,
+     76, 47, 32,223,178, 21,160,102,143, 94,102, 60,203,  5, 89, 88,
+     76,238, 43,167, 38, 43,  2,178,146,  4,195,170,116,191, 26, 94,
+     78, 75,218,251,159, 82,172, 58,219,165,138,101, 34,197,200,172,
+    100, 33,139,104,115, 90, 24,185, 78,208,201,245,247,242,125,123,
+      3, 75,246, 84,142, 32,138, 56, 80,220,104, 64,160,176,209,104,
+    206,206, 76,181,219,141, 70,163,217,108,238,162,176, 96,236, 90,
+    129,253, 49,171, 50,150,135, 61,123, 35,228, 57,211, 88,114,237,
+     11, 94,185, 43,116, 86,255,228, 23,201, 65,151,211, 12, 71,101,
+    167,251,219,206,175,221, 17,  6,  8, 32,100, 26,130, 17,179, 80,
+    186,188,144,253, 37,144,140,108, 86, 11,229, 32,211,218, 82, 44,
+    139,190,198, 72, 48,173,181, 98,214, 90,179,102,173, 52, 87,133,
+    250,149, 66,108,175,104, 44,155,180,  4, 79,154,217,191, 15,204,
+     86,108, 74, 53, 21, 53, 27,141, 41,128, 69, 88,148, 19,194,172,
+    147, 56, 25,142, 70, 81, 52, 26, 14, 71,209,214, 22, 39,  9,142,
+     98,140, 19, 20, 86,163,216, 43, 95,200, 48, 83, 44, 31,205,159,
+    175,130,174,  3,173, 40, 34, 64,255,249,238, 87,141,129, 23,143,
+      8,140, 41,142,245, 85, 88,190,  1,221, 61, 87,137, 86, 82,108,
+    183, 32,223, 45, 50, 40, 92, 41, 62, 70,164,202, 66,  2,236,127,
+    145,224, 93,208,106,172,182,106,134,130,196, 97, 96,104,213,152,
+    153,110,181, 90,141, 70,163,213,108,170, 32, 48,116,184,187, 74,
+    219,221, 58,205,131,219,167,175,210, 36,230,200,218, 71, 73,196,
+    126,101,151,227,245,118, 17, 95, 99, 63,117, 42,196,168,208,200,
+    203,206, 51, 78,  2,191, 60,132, 65,182,192,200,188,130, 59,203,
+    140,213,100, 61, 27,  0,  0, 17, 42, 73, 68, 65, 84,140,145, 76,
+     27,185, 22,123,229, 32, 35, 34, 49,105, 36,180,  5,171,105,213,
+    132, 19,119, 37, 74,107,237, 83,140, 53,107,118, 46,229, 20,243,
+     64,150,239, 10,206,214, 60,122, 91,236,150, 88,230, 33, 44, 83,
+    100,142,193,204, 89,150,170, 30, 35, 87,144, 16,  3, 21, 52, 91,
+    173, 44, 84, 97, 22,157, 36, 81, 28, 69, 81, 60, 28, 14, 71,189,
+    158, 30,142, 32,138, 68, 51,197,  9, 10,211, 40,198,138,148,170,
+    136,182,  2,221,192,109, 92,129, 21,168,192, 93, 79,217,177,177,
+    125, 53,188, 16,238,105,133,115,169,136,107, 87,241, 53,222,108,
+    142, 99,206,  1,169,170,106, 84, 17, 65,179,161,154,205,102,183,
+     99,104, 21,134,161, 57,189,239,154, 86,187,  3,107, 63,193, 86,
+    101,188, 85,  5, 47,119, 74,207, 78,246, 59,145,179, 27,214,103,
+    213, 93,238, 63,  7, 68,187,194,203,243,137,150, 92, 84,213,131,
+    208,105,134, 51, 25,252,  2,119,226, 41, 91,230, 93,152,242,202,
+     50, 83, 34,176, 39,123,169,227,151,193,151, 70,237,191, 28, 16,
+    105,149,209,140, 85,209, 63,154, 11, 70,133,217,247,198, 87,114,
+    105,106, 50, 19, 99, 25,182, 76,216, 47,187,142,  2,194, 10,239,
+    114, 80,167,210, 76,108,133,  5,162,  8, 41, 80, 74, 53,154,205,
+    124, 91, 80, 17,214, 58,209, 58,137, 99,195,181, 40,138,226,237,
+    109,102,198,225, 72,  4, 84, 20,  1,128,153, 34, 40, 63,203, 93,
+     21,134,227,107, 73,101,215, 22, 11,178, 71,167,235,187,  5,197,
+     62, 26, 35,220,211,247, 11,220,251,111,222,127, 17,131, 16, 74,
+     24,  8,  0, 55, 26,  8, 32,173, 38, 41, 10,166,167, 26,141, 70,
+     35,108, 52, 26,141, 48, 72,251, 25, 23,155,138,220,179, 37, 28,
+     11,172,241,106,171, 44,183,246, 97, 21, 51, 90, 56,236, 40,112,
+    203,153,103,244, 61,163,219,143,208, 71, 84,225,162,231, 20,199,
+    147, 43,215,127,147,198,175,202, 73, 73, 47,209, 55,137,138,241,
+    146,217,169,239,250,200,180,225,  4, 35,103,201,125, 58,255,145,
+     86, 77, 80, 26,116,149,224,101,104,229, 92, 74,241,149,161, 75,
+    103, 85, 22,169, 12,147,130,169,204, 18,253, 42, 57,  6, 99,230,
+     43, 11,121, 63,120,210, 12, 16,196,238,206, 38,229,122, 19, 34,
+     10,194,  0,154, 77, 55, 36, 98, 17,102,157, 36, 58,142,227, 56,
+    142,147, 36,137,227, 56, 30, 12,244,104, 36,137,198, 40, 22,  3,
+     53, 97, 96,177,181,105,149, 26, 10,177,116,170,226, 94,132, 56,
+    232,101,141,242,160,111,191, 31, 60, 21,  2,117, 32, 52, 98, 10,
+      1,164, 17, 98,160,168,209,108,116,218, 78, 43,189, 48,  8,  2,
+    115,182,237, 39,253, 27, 31,194, 99, 69,205, 73,101,113, 41,184,
+    221,202,113,108,221,214,248,249, 68, 24, 47,184, 60,120,149, 10,
+    234, 51,177, 53,166,157,132,175,165, 84,197, 52,162,173, 65,173,
+     16, 92,202,229, 99,169, 48,117, 50,249, 53, 38, 14,203,218, 79,
+     59,183, 33, 18, 17, 19,234,163,109,140, 67, 72, 22, 55,166,111,
+    142, 49,145,236,112, 76,147,210, 74,171,162,123,100,135, 98, 69,
+     33,102,249,165,115,  9, 54,206, 81, 90,148,249, 56,219,167,175,
+    172,164, 25,218,171, 28,117,150,154,205,212,170, 73, 78,125,  5,
+    162,136,194, 32,108,183, 90,133, 60, 72,152, 53,179, 41,229,205,
+    135, 78,146,193, 80,143, 34, 17,129, 56,198, 68, 11,  0,106,109,
+    230, 13,  0, 32,115,166,251,203,176, 14,172,244,254, 46, 51,254,
+    253,131,204,117,121,233,  5,165, 68, 41,  4,144, 64, 65, 24, 32,
+     18, 53, 26, 97,187, 85,108,216, 66, 68, 74, 33,226,126,244,212,
+     30,124,175,108,134, 80, 85,159, 85,182,132,123, 82,107,255, 62,
+    177, 68, 47, 79,119,149,221,162, 93,134,157,243,171, 32,187, 10,
+      2,140, 28,177,229,250, 74,175,192,194,153, 91, 76,169, 85, 37,
+    187,252, 38,132,147,197,175,170, 56, 44,109,160,239,204,147, 57,
+     66, 12, 81,152,  1, 17, 25, 77,151, 46, 27,241,155,142, 57,100,
+    150, 11,165,213,170,100, 10,190,116, 10,179, 20, 82, 90,235, 44,
+    194,231,156,101,  5,132,105,167,208, 85,118,161, 88,233,194,158,
+     32, 43,196,252,197,212, 63, 95,210, 82, 52,155,105,127, 29,143,
+    104, 21,103, 50,  2,128, 82, 74, 41,177,219,184,187, 22, 80,108,
+    204,102,254,160, 36,209, 90, 39,204,194,172,163, 56,102,205, 34,
+    146, 68,145,238,255,255,237,157,235,118, 27, 57, 14,132,  1,182,
+    146,153,221,247,127,215, 61, 51,137,200,253,209, 55, 92, 10,  0,
+     91,182, 99,249, 34, 29,123,168, 30,197,246, 81,172, 47,133, 98,
+      1,252,223,249,253,255,249, 71,126,147,246,239,191,230, 87,169,
+    253,250, 77,190,116,125,107, 69,214,184,255,184,153, 39,244,159,
+     63,213,166,232, 95,127, 29,239,223,229,191,255,185,253,252,201,
+    204,109,105, 63,110, 63,150,101, 97,230, 53, 58,121, 28,181, 69,
+     87,  8, 85,113,138,242,177,203, 70, 91, 89, 72, 81,148,140,151,
+    212, 74,247, 18,243,106,209,152,243,  1,185,116, 48,213,194, 75,
+    210,107,  3,214,142, 48,169,203, 20,185,188, 61,175,125, 46, 48,
+    192,139,101,150,241,233,248,229,236,176,163,150,180,191, 71,188,
+     59, 98, 60, 70,223,103, 68,111,145,138,179,189,241,104, 23,218,
+     40,182,194,107, 89,150,131, 95,242,174,  4,152,208, 94, 93, 94,
+     16, 20,115, 21,165,248,124, 30, 43,158,128, 44, 46, 45,141,221,
+     79,190,204, 20,186,108, 39, 26, 29, 26,237,124,  5,161, 60, 25,
+     71,152,139,  7, 19,181,182, 14,253,216, 74,210,  1, 74,173, 67,
+    233,172, 82,116, 19, 44,189,255,250,253,235,126, 63,251,246,122,
+    239,191,127,255,186,223,187,159,148, 96, 80,120,249,215, 66, 64,
+      7,252,175,165,221,110, 63,228,182,186,185,114,188, 49,200, 15,
+    183,189,254,219, 57,  3,169,136, 83, 24, 85,211, 30,124,104,199,
+      7, 69, 98,  6, 46,239,207, 55,215,  4, 20,198, 82,245,210,  9,
+     48,229,226,139,103,156, 66, 11,109, 44,  6,154,235,196, 22,243,
+    133,102,198,119,225,215,140, 16,219,118,239,134,213, 98,107, 45,
+    185,105,177, 19,100, 39,197, 86,134,245, 53, 56,177, 44,203,253,
+    222,151,142,252,251, 29, 90,119,101,129, 73, 83,204,148,148,189,
+    175,236,218,239,178,176,220, 64,134, 88, 38, 17,150, 81, 44,228,
+     89,184,128, 80, 19, 92, 27,226,112,  2,169,203, 88, 29, 11,174,
+    195, 98, 68,212,150, 38,255,192,223,244,247,181,194,237, 49,122,
+    189, 12, 58, 47,162, 82,181,143,202,181,215,238,  5, 88,129,170,
+    201, 13,196,156, 92, 24, 94,102,178,223,217, 67,204,  7, 65,108,
+      7, 35,203, 86, 32,167,192, 54, 44,  1,255,222,225,173, 53,117,
+    178,134,194, 86,228,115,  5,211,  7,249,201,249,149, 11,177, 67,
+    142,169, 78,107,230, 49, 58,119,238, 60,152,185,141,182,166,195,
+     36,197,118,152, 45,189,111,  8,235,189, 47,247,123, 95,182,252,
+    215, 94, 42,138,133,208, 96, 14,100,128, 95, 39,200,156, 55, 86,
+    236, 84,122,168, 17,138,146, 69,114,140,236, 85,146,173,203,199,
+     53, 83,126, 42,189,118,152, 88, 99,  4,182,189,  2, 92,130, 42,
+    118,123,149,122,127,224, 57,110, 92,213,124,243, 10,107, 78, 94,
+     57, 48,249, 93,195, 76,112, 37,187,136, 51,219,137, 65,181,232,
+    176,133,199, 71, 52,233,217, 47,106,109, 23, 74,163,  5, 69,162,
+     23, 92, 59,194, 28,182, 20,187,222,214,105,189,189,193,175, 88,
+    234,136,137,232, 17,143, 49,120,112,231, 62,  6,175,159,152, 71,
+     27,146, 98,167,123,213, 78,164,173, 20, 91, 65,181,116,100,123,
+      9,114,221,141, 10,219,120,101, 19,175,160,168,148, 44, 11,228,
+    152,151,100,228, 45,255, 74,145, 57,136,129,133, 50,191,197, 73,
+    148, 82,178,  9,216,145,  5,156, 11, 81,132, 27,148,252,154, 27,
+    139,175, 47,172,174, 52, 60, 63,198,172, 89,157,117, 85,109,249,
+    131, 80, 33,194, 76, 49, 38, 39, 11,234, 82,209,170, 46, 54, 77,
+    216,106,179, 81, 92,148,120, 90,150,  5, 33, 79, 24,255,154, 92,
+     98,167, 96,255, 17, 15, 32, 67,217,245,214,255,250,221,222,236,
+    151,208, 10, 49,115, 20,236, 56,182,227,218,104, 99,109,169,238,
+    123,226,189,141,214,219, 62,177,176,157,198,123,211,228, 57, 65,
+      6,157,123,141,173,172,223, 72,218,251, 39,190,124,224, 66,211,
+    236,  2,203,140, 46, 43,253,178,124, 33,101, 26,153,254,227,177,
+    143,122, 85,128,147,242, 77,138, 56, 11, 38, 14,135, 78, 95,  8,
+    144,189, 34,176,102,106, 64,247, 99,207,  2, 43,252,152,220, 61,
+    156,138,154, 78, 49, 75,159,  9,113,140,107, 57, 74, 52,  5,174,
+     67,127, 37,253, 64,190,238,  3,240,130, 26,107,166, 78,180, 62,
+     23, 46, 24,153,248,202, 63, 76,207,195, 47, 36,196,116, 57, 41,
+    236,232,213, 17, 91,171,202,253,120, 14,238, 99,108,190,216,230,
+    227,247,214,155,243,224,205,  6, 99,239,125, 57, 96,134,203, 70,
+    253,108, 68,177, 49,122, 71, 20, 83,139,138, 98,175, 10,178,144,
+    107, 33,209,156,105,  5,103,154,234,190,114,198,204, 98, 60,139,
+    186,172, 58, 95, 86,  9,206,168, 42,232, 88, 65, 84, 25, 51, 14,
+    210,170, 84, 88,111,140, 45, 77, 46,161,185,172, 59,207, 25,185,
+     56, 67,211,146,240,202,126, 81, 41,184,206, 34,209, 24, 93,100,
+     68, 87, 92, 48,242,199,210, 95, 72,136,157,229, 36, 29,199, 83,
+     31, 97, 40,222,167,216,239, 51, 34,184,143,157, 96, 99,172,181,
+    100, 27,237,208, 98, 67, 81,204, 19,109, 21,101,227,188, 56, 74,
+    150,137,116,133, 79,237, 43,156,217,252,107, 78, 49, 61,245,194,
+     98,108,  6,103, 22,100, 88,144,129, 21,  4,153,223,101,196, 50,
+    106, 76,207,185, 31,227,241,209,135,243,189, 56,209, 60, 82,175,
+    188,  2,108, 17,202,189,195, 82,103, 30, 91,114,224, 48,235,133,
+     81, 38,137,183, 37,231,227,113, 11,183, 22,163,116,  4,167, 82,
+    138, 17,217,  0,  2,245,134,128,199, 86, 83,135,185,198,228,138,
+    165,240, 71,228, 23, 40, 39,137, 78, 83, 44,162, 24,211,174,196,
+      6,143, 49,250,  1,178, 85,139,141,118, 18, 76,237, 33,122, 18,
+    105,113,229,130, 20,138, 98, 67,242, 78, 55,129,203,187,140,236,
+    251, 94, 74, 37,205,232, 64, 23,225,  8,198,  5,138,205,179, 12,
+    235,179,151,176,204,122,106,127,196,255, 74,124,174,107,204,  2,
+    235,151, 48, 43, 34, 23,134,151,120,179, 75,145,197,201, 28,103,
+    143, 18, 37,140, 68,123,142,101,146,213, 99,108, 48,167, 63,212,
+     38,128, 70,167,207,246, 43, 62,  3,114,121,171,235, 15,237,250,
+    220,232, 15,221,144, 41,230, 40,182,111, 79,186,109,202, 29,104,
+    141, 91, 31,125, 28,254,152,215, 72,214,200, 50,  8, 83, 56,139,
+    117,156,253, 34, 88,139, 25,130,117, 80, 88,210, 69, 69,118,158,
+    236, 76,149, 46,139,161,166,230, 58, 76,226, 44,138, 74, 84, 39,
+     21,226,192,218,107,137, 47, 40,187,114,126, 65, 96, 57,193,101,
+     80, 85, 49, 75,127,154, 83, 91, 51,  7,161,130, 41, 17, 78,115,
+    177,211, 74, 86, 55, 65, 96,105,238,153, 71,199,103, 41,181,252,
+    126, 34,156,239,236,201,245,103,172,174,247,229,151,162,152, 50,
+    197,172,189,191,185,251, 58,108, 65,135, 32, 91,181,216,104, 99,
+     19, 98, 27,197,128, 72, 58, 52, 26,196,153,128,151, 95,184,103,
+    129, 78,240, 97,146,252,197,164,158,  1,180,152, 57,112,132,  0,
+    193,106, 81,118,244,  6, 62, 34,202, 60,181,  2,138,213,136,154,
+    167,216, 76, 83,244, 12,185,226,135,165,212, 18,179,214, 43,193,
+    101,220,105,127, 28,134,215, 92,161,197, 37,225, 37, 39, 68,  0,
+    106,  1,226, 56, 91,221,199, 26, 52,176, 84, 51,164,185, 35, 96,
+    133,198,188,202,161, 62,  7,185,222,133, 95,128, 98,251,193,178,
+    167, 47, 38, 40,118, 32, 76,190,215,133, 54,107, 99,195, 87,219,
+    234,202,131, 39, 43,198, 70,164,157,188,182, 50,251,143, 49,185,
+    108,108,223,231,197,142,239, 62,  5,178,173,204,164, 25,171,127,
+     14, 97,136,104, 50,128,241, 50,150, 77,  3,237,138, 20,227, 73,
+    144,205, 48, 11, 84, 53,230,173,151,195,203,232, 46,184,144,181,
+    212, 76,132, 75,146,194,229,185, 18,237,197, 46,190,224,210, 12,
+     64, 85, 65,133,165, 44, 54,177,205,233,180, 22, 19,206, 67,176,
+     60, 39,241, 93,170,197, 39,225, 87,224,139,109, 50,108,168,225,
+     87,103, 85,185, 78, 25,211, 39,114,172, 97,254,181,160, 28, 99,
+    180,190,141, 91, 85,161,  7,211,235,232,244,147,188, 26,222,193,
+    179, 20,191, 92, 63,101, 92, 83,134,  8,219,175,208,121, 33,160,
+    152,199, 88,205, 50,105, 99,169,225,205, 49,206,  2, 85,245,186,
+    240,202, 16,230,174,170, 11, 25,177, 10,102,225, 18, 49, 35,151,
+     47, 14, 47,204, 62,197, 93,139, 62, 76,207, 14, 91, 64, 55,  1,
+     74,233,144,131, 14, 94, 88,223, 77,  7,204, 72,185,242, 81,  6,
+     53, 72, 69,188, 39,185,222,157, 95,193, 30,165, 43, 40,247,243,
+    115,181, 28,147,198, 18,239, 71,114,172, 73,178,209, 71, 19,115,
+    163, 97,127, 35, 88, 36,132, 83,208, 82, 20,  3,124,  4, 31,  3,
+    216,252,208, 30,155,152,123,  1, 53,152,133,150,188,160, 63, 41,
+    138,145, 56,149, 98, 68,216,242,231, 86,228,215,175,253,237,227,
+    173, 75,119, 61,212, 93,136, 97,165,202,  2,218,162, 12, 65,  4,
+    180, 34, 84,123, 97,225,229,234, 54, 35,142, 32,138,124,117,233,
+     57,  5,228, 21,216, 43,128,  1,136,160, 72, 44, 75,197,247, 39,
+    215,147,240, 43,165, 24,185,243, 87,135,247,248,201,181, 36,182,
+    131, 12,221,176, 44,200,163,186,105, 58,129,164, 58,153,  5, 22,
+     82,241,193, 14,202, 50, 62, 38,116, 87,156,230,143, 71,146,129,
+     22,165, 97,201, 21, 38,251, 35, 69, 70,193,240,210,241,200, 12,
+    194,124, 46, 13,231,226, 43,240,183,  8,246, 30,  6,197, 33, 37,
+    135, 95, 32,108, 17, 56, 25,131, 38,203, 68,219,189,232,212, 80,
+    139, 23,225,136,  9,196,196, 24, 88,219, 37,154, 96,150, 73, 66,
+    168,198,132,167, 36,215, 83,241,203,188, 46,135, 53,230,229,152,
+     44, 41, 15,167, 31,131,140, 86,151,159, 52, 63, 26, 10,110, 25,
+    177, 20,104,168, 30,172,149,225,213, 61,180, 76, 73,105, 52, 88,
+     21,126,213, 92, 19, 68,219,141,179,253, 68,176, 65, 80,157,157,
+    103, 92, 20,  8,203,214,122, 25, 32,108, 92,249, 59,246,118,190,
+     30,203,111,119, 33, 47, 79,125, 56, 36,131,243,183,228, 89,241,
+     18, 73, 39,173, 28,167,168,112,186,148,157,212, 92, 90,162,217,
+    135,141, 91, 54,246, 57,229,148, 69,149,181,178,212,232, 45,241,
+     41,147, 90,198,225,122, 94,193,245,204,252, 10,229,152,240,248,
+    135,252,237, 21,231,  5, 69, 32, 19,198,191, 58,135, 40, 56,149,
+    168,  7,140,193,125,144, 50, 60,225, 60,251,158, 38, 92, 67,138,
+    145,254,148, 38,199, 52,188, 30,138,245,203, 78,164, 17,145,107,
+    148, 89,176,107,133,100,176,107,101,219,125,  2,138, 57, 51,230,
+    114,122,203, 32, 12,108, 39,186, 24,  4, 21,161,  8,164,125,  0,
+     91,162, 93, 63,115,212, 25,250,170,177,227,206,133, 13, 95, 97,
+     43,138,154, 60, 53,185,158,153, 95, 86,142, 73,143,159,252,104,
+    177, 26,100, 64,154, 69,246, 83,102, 87,141, 62,137,187,137, 47,
+    149,103, 43,204, 35, 83, 89,198,  6, 63,196,214,113, 22,153,  2,
+    153,214,101,132,167, 96,212,245,100,164,206, 10,241,  5,255,113,
+    103,125,108,  9, 16,  3,168, 45, 81, 83, 76,138,175, 60, 15,225,
+    236,121,184,175, 72,145,225, 21,235,176,153, 28, 69,140,163,118,
+    229, 75, 77,137,172, 73,108,125,  8,193,245,129,248,149,200,177,
+     75, 32,  3, 20,  3, 60,168,112, 54,201,186,  7,110,161,226,194,
+     63,157,170, 38, 61,200,  8,  4, 96, 47, 25, 98,117,204,194,116,
+     91,214, 27,150,241, 91, 36,172, 85, 46,133,185,210, 77, 69, 16,
+    224,114,216,170, 49,  6,115, 81,111,115,163, 41, 96, 17,116,229,
+    242,242,112, 14, 91, 31,131, 92, 31,136, 95, 72,142, 77,130,108,
+    237, 25, 39,142,219,117,108, 40,158, 48, 74, 40,194, 24,165,185,
+    136, 20,124, 38,140, 95,106, 45,109,222,203,159,212, 56,250,208,
+     11,195, 27,147, 38,114, 97, 19, 23,117, 53, 73,143,231,239, 83,
+    195, 62, 37, 24,206, 70, 88,112,133, 33, 84,181,144,  9, 84,149,
+    144,136, 24, 17,213,104,136,114, 25,151,226, 63,230,190, 23,217,
+    168,131,137,104,165, 17,249, 79,136,173,143,200,175,  7, 64,118,
+     28,240, 38, 96,134,222,211,129, 52,203,184,230, 69, 19, 21,138,
+    138, 52,  8,131,194, 54, 36, 23,153,108, 24,218,145,212,216, 58,
+     46, 88,150,229,177, 49,151,172,176,187,147,100, 79,165,125, 36,
+     69,129,236, 97, 38,148, 70, 21,181,163, 89,104,108, 57,183, 94,
+     20,145,206,188,135, 32, 35,182,142,190,193,150,181,198, 40,234,
+    116, 12,255, 67,185,148,139, 57, 21,136,172,184, 54,  4, 61,213,
+    159,  7, 91, 31,154, 95,240,117, 31,114, 95,106,159,247,170,118,
+     45, 19, 81,166, 93,240,204, 62, 11,138, 55, 39,136,230,235,190,
+    202,155,199,237,145,104,243,209,237, 69,  6,119,155,180,192,228,
+     42,118, 36, 35,116,129,  7, 28, 24, 96,214,  8, 75,119, 27, 51,
+    138, 89,219, 43,186, 71,254,189,230,156,179,243, 51,143, 31, 20,
+    155,232,127,133,193,215,128,149,161, 83, 23,248,122,105,208,244,
+    131, 89,242, 95,135, 95,133,217,191,237, 86, 14, 63,138,229, 28,
+    129,113,204,251,195,177,208, 64,166,121, 53, 20, 74, 36,183,206,
+     47, 82, 49,105, 39,115,185, 10,120, 61,232,223,151, 97,176, 23,
+     12,200,  7, 27,145,248,109,247,160,127, 31, 35, 44, 48,200,180,
+    174,201,131,248,112,244, 68, 60,143,130, 98, 25,133,  0,138, 75,
+     66, 36,177, 96, 91,207, 39,148, 90,159,155, 95, 69,117, 41, 56,
+     54,246,243, 48,118,152,145,103, 89,  8,180, 68,239,224,  5, 10,
+    157, 22,166, 21, 46,  3,211,198,238,172, 45, 50,182,237,135, 56,
+    151,141, 46, 12,121,173, 44,252, 57,197, 60, 87, 73,170, 53,192,
+    153,130, 23, 52,242,243,161, 18, 19, 62,127,186,155,151, 44, 60,
+    179,220,  2,234,193,  9,235, 29,190, 42,148,245, 51,124,210,219,
+    141, 62,237,141,205,187,234,140,146,173,110,153,110,178, 36,127,
+    236,200, 86, 99, 38, 56,192,133, 39,190,168,188, 40,163,141,  2,
+    175, 42, 52,176,242,125,197, 25,143, 62,238,136, 76,234, 69, 56,
+    198,181,134, 86,108,224,211, 99,161,251,164,219, 17,166,239, 97,
+    173, 85,226, 34,182,207, 92, 61,167,168,  4,159, 97, 36, 85, 18,
+    239,160,216,201,138,152,245, 53,164,214, 87,227,215,156, 83,134,
+     89,198, 18, 99,242,196,159, 41,156,165,152, 35, 71,165,217,  2,
+     48, 42, 10, 19,213, 85,128,235,138,217,229,227,247, 52,153,250,
+    154,175, 32,169, 26, 57,113,173, 99,219,230, 91,125, 28,191,114,
+    204,242,242,211, 64,141, 42, 48,205,  2,171,142,149,126,105,102,
+    125, 65,126, 77,177,140,  8, 28,147, 68, 99,247,254, 69, 39, 83,
+    133,179,163,190,138, 88, 70,186,187,231,130,215, 30,195,203,244,
+      9,  5,252, 10,125,250,224,  3,130, 42,113,237,231,234, 71, 70,
+     69, 36, 37, 45, 68,174,166,140,125,125,192, 47,229,244,187,237,
+     75,119,191,176, 39, 80, 73, 59,188, 87,154,  5,178,130,210, 57,
+      0,214, 87,100,214, 23,231,215, 76,141,  9,188,255,  3,101, 37,
+    206, 16,194,242,135,225, 69,131,171,  8, 85,214,198, 74,189,249,
+      2, 94, 51,252, 74, 46, 92,124,209,201, 82, 42,227, 87,240, 80,
+    113,170,240,248,245,  2, 66,205, 48, 43, 39, 17,100, 97,244,240,
+     10,176,190,153,245,205,175,199, 89, 22, 72,179, 24,103,123,104,
+     99,156,224,203,130,  9, 97,141,231,205,245,218,116,159, 89, 80,
+     48,244,235,129,209, 57,145,  6, 43, 73,198,149, 32,171,211, 21,
+    248, 33,132, 23,101,153,139,116,145, 86,161,213,152,158,178,250,
+     75,236,246,111, 96,125,243,235,157,112, 38,144,  6, 38, 99,231,
+     68,171, 48,231,158, 95,112, 42,189, 72,190,189,209, 62,156, 13,
+     73,140, 64,137, 93,120,101, 57, 40, 38,  9, 71, 43,114, 61,150,
+    174,243,139,190,248,156, 91,212,180, 74,107,192,111, 96,125,243,
+    235,121,112,230,254,192,224,108,214,127, 57,183,166, 30,108,163,
+    114, 15, 84, 78,193,177,161,  7,247,144,  8,228, 36,  8,158,239,
+    104,190,204, 35, 47,104, 54, 57,157, 17,218,176, 73, 22, 62,228,
+    234, 88, 15,136,173, 18, 79,217,122,134, 86,133, 18,253,190, 93,
+    185,253, 31,230,204,154,229,115, 62,144, 15,  0,  0,  0,  0, 73,
+     69, 78, 68,174, 66, 96,130
+};
+
+static const unsigned char _data_keyboard_png[11032] = {
+    137, 80, 78, 71, 13, 10, 26, 10,  0,  0,  0, 13, 73, 72, 68, 82,
+      0,  0,  1,112,  0,  0,  0,178,  8,  2,  0,  0,  0,212, 44,  3,
+     99,  0,  0,  0,  9,112, 72, 89,115,  0,  0, 11, 19,  0,  0, 11,
+     19,  1,  0,154,156, 24,  0,  0,  0,  7,116, 73, 77, 69,  7,216,
+      1, 30, 13,  6, 32,218,243, 41,227,  0,  0, 32,  0, 73, 68, 65,
+     84,120,218,237,125,207,111, 35,199,181,110,217, 20,203,160, 89,
+      6,221,101,112,186,  7, 84, 51,144,154,198,168, 21,152,116, 48,
+    212, 98, 56,139,153,  0, 17,  3,220,120, 17,103,241,222, 91, 36,
+    249,227,238,189,139, 36, 11,123, 51, 94,152, 50,144, 81,128,105,
+     45,166,  7, 30,210,184, 67, 14,204,166, 16,182,136,116, 13,145,
+    238, 33, 82, 52,145,226, 37,240, 22, 53,106,211, 36,245,139,234,
+    106, 73,156,250,160,133,212, 18,120, 84,213,117,190,250,206,169,
+    211,125,222,217,223,255, 27,136,  5,185,117, 29,  0,208, 59,114,
+    165, 57,105, 78,154, 91, 85,115,239,  2,  9,  9,  9,  9, 73, 40,
+     18, 18, 18,146, 80, 36, 36, 36, 36,161, 72,196,136, 68, 34,145,
+     74,165,228, 60,220, 68, 32,132, 52, 77,147,132,114, 89,164, 82,
+    169, 66,161, 16,155, 27,108,108,108, 64,  8, 33,132, 27, 27, 27,
+     49,152,203,231,243,124,104, 24,227, 24,150, 75, 42,149,202,229,
+    114, 66, 77, 96,140, 63, 41,126,242,233, 47, 62,205,102,179,225,
+     69,  8, 97,161, 80, 72, 36, 18,162,233,178, 80, 40, 64,  8, 69,
+     79, 99, 38,147,217,222,222,254,244, 23,159,110,111,111, 99,140,
+    227, 89,153,169, 84, 42,123, 43, 27,167, 15,167, 82,169,141,141,
+     13,209,119,141,223,184,141,141,141,211,125,252,221,  8,141,161,
+     15, 80, 12,163,226,206,144, 74,165, 24, 99,154,166, 77, 38,147,
+     56, 44,126,132,185, 33,132,144,232,123,150,207,231, 55, 54, 55,
+    208,  7,232,211, 95,124, 90, 40, 20,  4, 89,204,173,231, 14, 59,
+    135, 47,254,231,133,118, 91, 11,151,136,166,105,140, 49,209, 83,
+    154,205,102, 83,239,167, 68,243, 50,132,112, 99,115,131, 82,218,
+    254,190, 77, 41,205,255, 44, 47,148,194, 66, 81, 57, 26,141, 70,
+     63,140,226,148,153, 27, 27, 27,163,209, 40,  6, 71,152, 76, 38,
+    163,209,232,244, 45,252, 70,134, 60,217,108,150, 82,154, 72, 36,
+    240, 71,152, 82, 26,131,136,157, 76, 38,140, 49,190, 27,  8,181,
+    200,119,128,254,171,254,232,135, 81,251,251,246,104, 52,226,118,
+     35, 31, 81, 34,145,160,148, 50,198, 70, 63,140, 56,103, 33,132,
+     50, 31,102,122,189,158,104,198,204,222,202,182,191,111,227,143,
+    176, 80, 15,231, 31,238,251, 62,165,212,247,253,240,138, 56,153,
+    112,103,235,206,246,246,182,166,105, 92,232,125, 82,252, 36,159,
+    207,199,176,185,194,247, 96,191,223,143,199,245,250,253, 62,124,
+     15,158, 34,247,214,162,178, 20,131, 99,135, 11, 37,245,126,170,
+    223,239,103, 50, 25,209,118,249, 46,154, 74,165, 38,255, 59,121,
+    243,253,251,169, 76, 38,131, 16,242, 60, 79,  8,121,125,128,186,
+    127,239, 50,198, 16, 66,148,210,120,102,149,235, 74, 77,211,250,
+    175,250, 49,200,147,193,235,193,104, 52,242,255,233,107,154,214,
+    237,118,  5, 25, 26,141, 70,225,  6,192,183,  4,126, 69, 16,184,
+     20,  2,  0,228,114, 57,238,225,158,231,137,216, 12,230,195,186,
+    193,235, 65, 60, 58,157,139,148,193,235, 65, 38,147,225, 28,189,
+     10, 10,133, 49,246,252,219,231,190,239,251,190,255,252,219,231,
+     49,220, 51, 30, 94,113, 46, 99,255, 22, 27, 17,208,127,209,108,
+     54, 43, 58,191, 48, 63,132, 76, 38,  3, 33,244, 60, 15, 33, 36,
+     78,168, 67,  8,179,183,178,156,139, 61,207, 19, 42, 82, 38,147,
+     73,255, 85, 95,187,173,109,108,108,100,111,101,187,127,239,138,
+    246, 58, 74,105, 42,149, 74, 36, 18,189,163, 94, 54,155, 21,164,
+     46,103,111,220,135, 25,161, 68,185,144,169, 51, 31,102, 98, 10,
+    121, 98,211, 41,241,192,243, 60,207,243, 38,147,  9,223,112, 70,
+    163,209,104, 52,226, 23,  5, 89,236,245,122,147,201,132,  7,252,
+     24, 99, 65, 57, 41, 30,114, 35,132,184,220, 27, 12,  6,185, 92,
+    174,215,235,221,185,115, 39,151,203, 21, 62, 46, 76,103,106,163,
+     85,124,131,215,  3,238,102,140, 49, 46, 82,132,238, 61,137, 68,
+     34,243, 97,166,251,247,238, 96, 48,136, 65, 59,231,214,115,124,
+    171, 75,172, 37, 68,167,213,249, 86, 23,191,211,113,115, 39,237,
+     58,242,216,248, 28,183,237,253, 84,168,159, 69,239,  6,163,209,
+    168,221,110,119,255,222, 77,172, 37, 52, 77,187,115,231,142, 32,
+    189,112,216, 57,220,216,220,216,254,249,182,255, 79, 63,147,201,
+    112, 39, 79, 36, 18, 47, 95,190,124,217,122,153, 91,207,137,240,
+     55,252, 17,158,230, 98,161, 34, 37,151,203,105,183,181,222, 81,
+    111,244,195, 40,255,179, 55,135,116,252, 44, 82,144, 69,198, 88,
+    251,251,118,191,223,159, 76, 38,135,157, 67,209,217,168, 48, 86,
+    189, 18,156,100,122, 77,242,197, 73,192, 24,135, 26,129,103,215,
+     82,239,167, 38,255, 59, 65,  8,241, 93, 72,232,214, 58,250, 97,
+    116,120,120, 88, 40, 20, 52, 77, 59, 60, 60, 20,177,207,124,215,
+    248,142,175,140,237,159,111, 31,118, 14, 17, 66,161,118,  0,  0,
+    240, 36, 78,180, 70, 95,182, 94, 78, 71,  1,140,177,151,173,151,
+    226, 98,171,222, 81,175,223,239,251,190, 95, 40, 20,242,249,124,
+    187,221,230,225,164,184, 72, 36,156,177, 21,147,234,231, 71,148,
+    132,194,147, 82, 43,  3,126,  8,194, 19,218,124, 95, 45,124, 92,
+    224,161,120, 12,177, 49,207,  2,112,209, 46,212, 74, 46,151, 27,
+    188, 30,240,204,165, 80, 67, 11, 39, 77,144,226,227, 26,132,127,
+    248,100, 50,105,183,219,133, 66,225,206,157, 59,240, 61,184, 98,
+    171,244,186, 33,210, 58, 20,132,174, 80,131,137,112,  0,190,207,
+     12,  6,  3,254,205,100, 50,225,223,139, 35,148,237,237,237,124,
+     62,143, 49,230, 71,143,153, 15, 51, 66, 79,  4, 83,169, 84,230,
+    195, 12,167,203,153, 65,197,118,112, 32,142,188,194,104,113, 50,
+    153,248,190, 15,223,131,236,223, 44,230, 20,230, 21,166, 51,174,
+     36,113,179,118,190,219, 51,110, 54,155,105,132, 10,198,230, 73,
+    127,147,205,102,181,219, 90,184,153, 47, 59, 65,195,102,171,229,
+    251,  1,  0,  0, 99,197,220,218, 66, 40, 45,108,205,141,187,174,
+     75,  8,161,116,  8,  0,208,212, 91,134, 97,204,155, 75,165, 82,
+     60,159,151, 74,165,120,193, 82, 52,106,206,233, 12, 41,157,183,
+    216,239,247,121,174, 52,177,150, 24, 13, 70,221,110,119, 57,242,
+    162,116,232, 56,206,204,197,249, 59,152,203,229,250,175,250,220,
+      4,165, 52,183,158,227,135,226, 75, 59, 94,219,233, 76, 79,105,
+    169, 84,140,252,198,121,132, 16,143, 44,252, 85, 56, 64,198,216,
+    224,245, 32,183,158,131, 16,242,244, 51,250,  0,245,142,122, 24,
+    227, 66,161,208,110,183, 47, 74,151,140,141,219,142,227,186, 71,
+      0,  0,132,210,134,177,169,169,170, 72, 66,252,209,220,233,142,
+     48,250, 97, 20,  9,161,180,157,142,227,116,206,227,119,167, 59,
+    194,217,132,226,  7,193,227,199,251,148, 82, 85,213, 78, 33,148,
+    193, 96,128, 16,186, 76, 46,189, 94,111,212, 27, 13,  0,128,170,
+    106,227, 49,107, 54,155,142,227, 84,171,187, 88, 81, 34,191, 97,
+    126, 16,212,106,123,188,220, 35,157, 70,227, 49,171, 55, 26,205,
+     86,107,222,220, 96, 48,224,174,197,143,120, 34,177,222,117, 93,
+    203,178,  0,  0,170,166,206, 19, 10,231, 20, 77,211, 46,147,213,
+    163, 67,202, 39,115, 26, 51,119,144,215,182,133, 10,136, 49,214,
+    253,123, 55,155,205, 78, 38,147, 37,178, 54,140,141,107,123,123,
+    190,239, 67,  8, 21,  5,243, 73, 22,225,108,196, 35,243, 67,155,
+     31,224,225,225, 97, 54,155,229,197, 74,163,209, 27,106,246,125,
+     63,155,205, 94,116,161, 50, 54,126,244,213, 87,148, 82,140,113,
+     50,  9, 93,215,117, 28,167, 92, 46,111,155, 91, 98,116,199,240,
+    209, 87, 95,241,112, 59,153,132,167, 59,130,239,251,218,109, 13,
+     92,174,154,231,235,218, 55,132,120,220, 23,206,244,187,204,135,
+     25,239, 31,222,146,132,242,162,217,178,109,251, 60,207, 65,240,
+    227,137, 75, 78,165,174,235,247, 43, 21,  8,147,156, 50, 45,203,
+    170,215, 27,191,124,248, 32,242,123,134,210, 72,215,245,233,125,
+    134,155,123,250,244,217,175,171,191,250,201,126,120, 44,184,162,
+     74,179, 49, 54,182,172,131,211, 83,131,124,233, 95,114, 81,  2,
+      0,170,213,221, 83, 54,210,201,100, 50, 35, 39,  7,131,193,210,
+     91,  2,103, 19,113,110, 22,162, 84, 42,206, 11, 31,143,144, 90,
+    109, 79, 83,111,205,179,243,233, 67, 62, 15,158,218, 54,165,180,
+     82,169, 28,203,159,113,109,111,207,182,237,188,174,139, 80,208,
+     79,172,  3,198, 88,120,239,248,230,247,248,241,254,239, 62,255,
+    237, 73,132,130, 49, 94,122,193,180,157, 14, 33,158, 97, 24,247,
+     43,247, 66,115,150,117,240,217,111,254, 99,254,143, 57, 21,156,
+     98,235,221,211,181,165,109,219,134, 97, 84,119,119, 99,136,205,
+     74,165,226, 47, 31, 62,224,108,  2,  0, 40, 24,155, 24, 99,215,
+     21,242,166, 41,  8,147,247, 43,247,166,157,141,155, 35,196, 19,
+     61,204, 39,150,  5,  0, 48, 12,227,244, 20,192, 37,  9,101, 24,
+    239, 41, 67,219,233,248,190, 95, 42, 22, 69,179,201, 73,224,114,
+    221, 52, 77, 65,146,129,175,144,112,241,232,235,235, 92,  6,138,
+      8,118,184,123,135,139, 19, 43, 74,185, 92,166,148,182,157,206,
+    194, 93,161,119,212,211, 52,109,233,244,101,171,213,  2,  0,236,
+    148,203,161, 57,195, 48,124,223,159, 23,152,137, 68, 66,211,180,
+    222, 81,239,148,128,241, 52, 66,209, 84,181, 90,221,189, 95,185,
+     23, 58,121,204, 72, 38, 97,156,230, 24, 99,162, 75, 84,219, 78,
+    199,117,221, 74,229, 30, 76,198, 49,165, 66,227,252,216,252,249,
+    156,  9, 35,195, 48,  4, 45, 84, 46, 67, 56,173, 28, 71,148, 67,
+    174,115,  5,  4,227, 62,  0,  0,165,127, 34,124,242,186, 14,  0,
+      8, 78,  8, 33,125,223,127,241,226,197,210, 73,116,223,247, 85,
+     85,155,158, 58, 93, 95,  7,  0,184, 93,119,158,188, 94,188,120,
+    113,250, 86,247,238, 53, 89,145, 39, 81,181,170,198,244,106,137,
+    182,211,161,148,154, 91, 91, 66,215, 61, 87,124,124,125,136,245,
+    177,225, 48,206,155,197,239, 20,132, 73,198,198, 30, 33,130,178,
+     39, 39,161,217,106,  1,  0, 76, 97,226,136,127,242,227,253,125,
+    206, 41, 47,154, 45,199,113, 76,211, 20,119, 98, 48, 47,168,  1,
+      0,252,176, 34,106,254, 10, 66,198,156, 78,  8, 44,157,  2,187,
+    190,133,109,205,102, 19,  0, 96,156,156,  6,142,106,103,  3,  0,
+    120,228, 85, 16,248,165, 98, 81,196,169,196,116,108, 12, 33, 12,
+    181,165,232, 77, 27,  0,240,167, 63,255,133,103,106, 48,198,250,
+    250,186,105,154, 34,246,240,112, 81, 62,181,159,241,187,  6,  0,
+     64,  8,149,203,119, 99,160, 78,198,198,142,227,168,170, 38, 34,
+    121, 31, 70,  1, 15, 31, 62,176,172,131, 47,190,252,146,151,255,
+    137, 91, 42, 88,193,124, 65,150,126,186,219,137, 83,229,243,130,
+    136,243, 11, 99,227,213, 33, 20,143,144,122,163,113,250,185, 82,
+     20,219,248,143, 71, 33,170,170, 37, 69,198, 59,245,122,131, 16,
+    175, 90,221,141, 39,126, 52,205, 59,  0,  0,158,164,100,227,177,
+    235,186,245, 70,195, 61, 58,170,238, 70,255, 15,240, 69, 73,  8,
+    129, 16, 62,124,248,128,231,155,109,251,153,101, 29,224,223, 96,
+    209,219,120,219,113, 24, 99, 66, 55,158, 99,153,  0,249, 96, 25,
+     99,108, 60,102,108, 44,226, 86, 66,152, 52, 77,179,217,108, 62,
+    177, 14,248,160,136, 71,220,163, 35,112, 67,112, 29,  9,133, 31,
+     84, 99,140, 69,156,239,204,  4,116,127,252,195,239, 67,169, 98,
+    219,118,167,211, 89,152,220,190,252,136,234,141, 70,169, 88,140,
+     45,132,204,235,250,180, 58,216, 41,223,125, 98, 29, 56,142,211,
+    118, 28,113,121,211,105,182,130, 16,214,106,123,205, 86,107,167,
+    124, 87,232, 72, 91,173, 22, 58,181, 66, 42,146,112,216,178, 44,
+    195, 48,184,186,108, 54,155,245, 70,131, 16, 34, 98,169,  0,  0,
+     74,197, 34, 99,204,113, 28, 46,159, 17, 66,149,202,189, 90,109,
+     47,182,  8,107,165,  8,133,210, 97,173,182,  7,  0,120,248,224,
+     65,108,201, 96,132,210, 92,193,214, 27,141,182,211,137,124,117,
+    242,115, 98, 85, 83, 61, 66,166,115, 28,111, 74,248, 20, 28,195,
+     72, 75,197,162,227, 56,174,123, 36,136, 80,140,205,205,233, 81,
+    188, 57,242,244,197, 38, 83,120,230,171, 84, 44, 10,181, 98,219,
+    182,170,106,252, 84, 21,  0, 80, 42, 21,147, 16,218,182,253,162,
+    217, 18, 49,153,252,  8,114,167, 92,246,  3, 31, 66,136, 21,133,
+     47,155,153,192, 36,154,149,159, 70, 96, 46,227,198,227,229,229,
+    214,228,245, 34, 20,198,198,143,247,247,  1,  0,213,234,110,252,
+    124,172,106, 42,104,  8, 57,112,229,137,113, 78,148, 51, 43, 21,
+    156, 85, 45, 18, 33,105, 10, 75, 49, 96, 16,123, 26,152,195,113,
+     58, 16, 66,161,167, 75, 30, 33,140,177,153, 10,151,109,115,203,
+    182,109,113,236,204,253, 57, 92, 21,188,100, 86,207,235,130, 86,
+    197,244,  1, 22, 56, 62, 14, 95, 46, 39,181,118,173,216,164,182,
+    183, 71, 41, 21, 84, 29,123, 14,183, 15,  0,  0, 34, 50, 41,213,
+    234,238,188, 39,240,106, 75,140, 21,238,144, 49,164,165,  0,  0,
+     24, 43, 34,150, 62,198,152, 16, 50,191,203,137, 48, 55, 61, 34,
+     94,178, 17,127, 89,  3, 79, 88,198, 99, 87,116,214, 89,215,117,
+    215,117,167, 83, 66,156,191,148,165,238,221,187,215,138, 77,120,
+    129, 96, 12,108, 50,127,180,233, 17,210,104, 52, 32,132,133, 83,
+     75,206,150, 78,214,204,124,113,249,138,177,162,169,170,136,117,
+    217,118, 58,211, 89,122, 74,135,182,253, 12, 28,151, 24, 68,142,
+    173,173, 45, 74,233, 19,235, 32,188,155, 79,109, 91,156,185,144,
+    148,121, 40, 39,116,169,104,170, 10, 33,108,182, 90,211, 11,230,
+    120,116,122, 12, 11,181,182,183,  7,  0,216,217, 17,149,138,226,
+    169, 95, 62, 34,  0,128, 31,  4,142,227, 32,132,150, 59,161,187,
+     46, 10,197, 15,222,188, 97,228,209,163,175,230,131,255,200,143,
+    232,248,243, 32,225, 83, 39,195, 33,165,148, 66,  8,203,229,242,
+     85, 85,241, 69,157,181,177, 44,  0,248,195, 32,227,241,155,186,
+    219, 74,165, 34, 40,182, 42, 24,155,132, 16,199,113,  8, 33,233,
+     52, 10,  2,159, 49,102,154,166,184, 80,142,231,209, 85, 85,139,
+     33, 52,174, 84,238, 61,126,188,255,232,209, 87,188, 42,138,143,
+    206, 48, 12, 65,153, 96,254, 24,193, 84, 84,130,132,238,178,121,
+     93, 55, 12, 35,188,119,132,120,252,180,110,185, 79, 59, 47,161,
+    148,138,197,180,200,247,101,160, 52, 58,105,171, 81,181,232, 23,
+    165,105,154,105,132,194,231, 98, 21, 69,217,218,218, 18,244,104,
+    198, 73,131, 42,129,162,136, 82, 75,142,207, 62,251,141,219,117,
+     61,242, 10,  0,144, 76,194, 82,177,184,240, 89,234,  8,113,191,
+    114, 79, 85, 85, 46,158,103,158,147, 18, 66, 40, 67, 90, 42, 22,
+     69,164, 21, 22,186,220,239, 62,255,220,113, 28, 62,159,162, 71,
+    135, 21, 28,250,130,170,169, 49,228,215,166,239,157,105,154,151,
+    121,202,255,157,253,253,191,197,227, 66,178,199,189, 52, 39,205,
+    173,188, 57,249, 78, 89,  9,  9,  9, 73, 40, 18, 18, 18,146, 80,
+     36, 36, 36, 86, 24,239,180,157, 67, 57, 11, 18, 18, 18, 82,161,
+     72, 72, 72, 92, 47,172,201,228,182, 52, 39,205, 73,115, 82,161,
+     72, 72, 72,200,144, 71, 66, 66, 66, 18,138,132,132,132, 68,124,
+    132,  2, 33,212, 52, 77, 78,232,229,193,223, 45, 46,186, 49,168,
+    132,196,181, 39,148,219, 43, 75, 40, 27, 27, 27, 16, 66,  8,225,
+    198,198,134,104, 91,147,201,  4, 66,152,207,231, 99,238,235,154,
+    205,102, 11,133,130,104, 43,185, 92, 46,151,203,173, 82,203, 90,
+      9, 25,242, 92, 12, 24,227, 84, 42,197, 24,211, 52, 45,158,142,
+    191,221,110,151, 82, 26,167,226,211, 52, 77,187,173, 81,241,221,
+    124, 70,163, 17, 66, 40,  6, 94,150,144,132,114, 77,145,205,102,
+     41,165,137, 68,  2,127,132,105, 92, 13,180,186,221,238,101, 90,
+    145, 94, 20,  8,161,209, 15,163,203,244,165, 62, 39,124,223, 31,
+     12,  6,232,  3, 25,208, 69,131, 84, 42, 21,131,174, 60, 63,214,
+    228, 45, 57, 51,148, 75,189,159,234,247,251,188, 75, 46,141,183,
+     35,159,132,196,233, 72, 36, 18, 49,176,115, 34,145, 88,216,146,
+    125,222, 29, 36,161,156,  1,198,216,243,111,159,135,187,171,156,
+     16,137,183, 84,  7,125,188, 64,  7,133,174,113, 83,  9, 37,145,
+     72,108,108,108,160, 15,208,100, 50, 25,188, 30,112, 15,207,102,
+    179,135,135,242,137,164,203, 78,236,104, 52,138,141,163,185,197,
+    120, 18, 82,171,234,225,185, 92,142, 79, 35,  0, 32,140,122,218,
+    237,182,  8,115,148,210,121,238, 88,133,144, 39,149, 74, 81, 74,
+     61,207, 75, 36, 18, 24, 99,206,154,253, 87,125,185,194,150,  6,
+    198, 24, 99,156, 72, 36,250,253,152,166,113, 48, 24,208,127,209,
+     59,119,238,248,190, 31, 67,214, 38,102, 94, 14, 67,  3,222, 17,
+     76,144,161,201,100,194,195, 13, 30,146, 95,159, 72,252,134, 17,
+     10,165, 52,156,187,193, 96, 32,233, 32, 18,142,230,242, 68,220,
+    234,159,119,134,209,104,148, 72, 36, 86,175,214,102, 58, 52,240,
+    254,225,137,163, 75,198, 24,255,112,132, 16,254,  8,139,230,101,
+    132,208,106,134, 60, 18,145,131,159, 37,109,111,111,107,154, 22,
+    207,185, 82, 38,147,201,222,202,190,248,159, 23,177, 81, 88,156,
+     27,222, 57, 67,131,155,133,209,104,212,254,254, 92,193,148, 36,
+     20,137, 55, 59,222,194, 52,190,160,109, 28, 28,103, 82, 36, 98,
+    115,245, 72, 34,172,203, 18,138, 71,136,235, 30,241, 14, 88,  8,
+    165, 85, 85,141,176,117,  0,239,132,160, 96,101,166,  3, 72,219,
+    233, 12, 41,157,121, 75, 59,255,227,116,164, 93,108,235,245,198,
+    252, 69, 65,111,135,231,131, 10,127, 84,176,162,169,154,160,150,
+     29, 51,182,102, 16,121, 79, 18, 62,141,243, 31,187,240, 62, 70,
+     53,186,147,204, 69, 62,186,208, 17,136,247, 99, 39,179,180,224,
+    110,202,231,247,187,243,187,250,249,135,166,169,234,210,183,108,
+    237,228, 45,107,252,196,178, 92,215,229, 17, 20,132,208,113, 60,
+    199,113, 90,173,214,195,  7, 15, 34, 89, 34, 16,194,122,163,129,
+     49,158, 33, 20,219,182,249,246, 53,189, 56,252,192,175, 55, 26,
+    209, 54,157,172, 55, 22, 16,138,170,169, 34,  8,197,113, 58,132,
+    204,  6,186, 34, 90, 14,157,100, 75, 28,161,208,225,  2,174,167,
+    116,104, 89, 22, 66, 40,114,115,124,116,243, 31,123,210,245,168,
+    224,  7,  1, 33,132, 49,166,170,154, 33, 44,251, 19,131,223,205,
+    128,247,168,138,106,101,158, 72, 40,124, 84,186,174,239,148,203,
+    124, 24,140,141,121,223,249,199,251,251,213,221,221,203,239,174,
+    188,133,165,239,251,211,109, 16,121, 43, 89,  0,128, 71, 94,149,
+    166,135, 77, 94,  1,  0,212,159,182,152,189, 60, 84, 85,251,117,
+    245, 87,177,  9,212, 63,254,225,247,124, 38, 61,226, 89,214, 65,
+    189,209,152, 23,104,151,199,244,136,190,174,125, 67,136,199,237,
+    158, 30,242, 32,132, 16, 66, 75,108,119,188, 13,123,171,213,154,
+     38, 20,199,113,  0,  0,197,185, 94, 75,169, 84, 42,149, 74,177,
+    127,223,176,120,135,247,123,228,147, 41,116,193,196,224,119, 11,
+    193, 27,108, 95,126,101,190,123,178,226,114, 49,198,191,124,248,
+     35, 41, 66,152, 44,149,138,165, 98,209,247,253,102,179, 25,201,
+     48,244,245,117,  0,128, 55,181,157,114,245, 85, 42, 22,  9,241,
+    166,155,105,114,249,167,169,171,240,252, 33,132,201,188,174, 87,
+     42,247,192,113, 63,205, 43, 71,175,215, 27,141, 70, 11, 51,249,
+    103,  2,161,180, 97, 24,190,239,123,199,237,141, 25, 27, 55, 91,
+     45,180, 40, 46,200,229,114, 16,194,120,234,134, 10,133,194,167,
+    191,248,116,250,235, 90, 85,169, 95,161,223,157,190, 50,203,229,
+    242,210, 43,115,237, 36, 85,  9,  0,216,218, 90,208, 89,222, 52,
+    205,122,163,225,116, 58,145,104, 75, 85, 83, 65,  3, 16,242, 42,
+    228, 66,143,188,194, 24,243, 70,205, 30,241,194,235,132,120, 24,
+    227,213,232, 19, 26, 70,124,224,184,237,246,149, 99, 50,153, 92,
+    198,201,185, 72,169,215,191,211,170, 42,  0,160,237, 56,140, 49,
+    190, 46,103, 51, 29,237,118,108,131,138,211, 86, 84,209, 92, 60,
+    126,119,230, 14,177,244,202, 92,172, 80,130, 32,  0,  0, 44, 20,
+     60, 60, 78,161,148,206,216, 91, 46,219,204,219, 44,242,136,145,
+    143,129, 16, 79, 85, 85,174, 68,120,152,195,153, 27,  0,160,138,
+    239,201, 24, 39,120,100, 23, 91,243, 83,209, 75,208, 48, 12, 66,
+     60,222, 81,188,117,130, 60,145, 56, 29, 75,248,157, 80,181, 18,
+     25,161,240,146,246,147, 62, 49,153,132,  0,  0, 63,240,103,182,
+    184,229,178,205,170,170, 81, 74,121,143, 97, 30,251,168,234, 45,
+      8,147,170,170,133, 68,195,227,160,200, 19, 40,  0,128,225,144,
+    214,235,141,233,175,120,238, 22,165,195, 70,227, 59,  0,128,177,
+     42, 94,199,219,241, 54,155,173,182,211,161,148, 22,139, 69, 73,
+     16, 23,197, 18,126, 39,  2,205,102, 11,  0,160, 47,149,218,187,
+    250, 58, 20, 93, 95, 39,196,243,  8, 41,160, 77, 46, 73,184, 60,
+    209,245,117,219,182, 41, 29, 34,148,230,251,158,136,  4, 10,165,
+    116, 38,197, 45, 84, 82,126, 93,251,  6,  0, 48, 30, 51,223,247,
+     33,132,149, 74, 69, 91, 21,217,197, 69,138,227, 56,132, 16, 41,
+     79,110, 28, 28,167, 67, 60,194,198, 99,215,117, 41,165, 24,227,
+    229,238,224,213, 19,138,166,169,  0,  0, 66, 72,193,216, 36,132,
+    132,137, 18,126,253,152,104,136,160,  4, 74,204,167, 60, 33,139,
+      1,  0,170,213, 93,172, 40,171,180, 40,121, 38,133, 82, 90,169,
+     84,164,139,222, 52, 66,113,248, 55, 24,227,114,185,188,109,110,
+     45,247, 57,139,  9,101,254, 52,119, 26,227, 49,  3,  0, 96,  5,
+     71, 50, 18,172, 40, 16,194, 32,  8, 24, 27,251,190, 95, 58,150,
+    202,252,186,235,186,154,170, 50,198, 86, 35,129,194,201,203, 35,
+    164, 86,219,123,250,244, 89,252, 92, 38, 90,164,168,170, 70,136,
+     39,229,201,146,190, 16,163,223,205,128, 31, 27, 95,254,115, 22,
+    231, 80, 20, 69,  1,  0,116,221,  5,205,129,184,219, 35,132, 34,
+    212, 11,170,170,250,190,207,131, 67, 85, 83,167,162, 33,157, 16,
+    242,230,186,128,  4,202,149,137, 50, 85,213,117,157, 16,111,225,
+     12, 75,156,  7,243,185,201,225,240,198,191,251, 42,102,191, 19,
+    129,197,132,194, 51,133,173, 86,107,254, 87, 60,227, 96,108, 70,
+    185,  5,241,244, 79,189,254, 29, 56, 62,247,  9,137,134, 49,198,
+    207,210, 86,163,  2, 37,196, 78,185, 12, 33,180,237,103,215,228,
+    216,248,  6, 65,215,215,231,189,142,210, 33,165, 84,143,186, 68,
+     48,102,196,236,119,241, 17, 10,223, 66,125,223,255,235,227,125,
+    126,254,194, 57,178, 94,111, 52,155, 77,140,113,180, 37,240,156,
+     68,134, 67,170,254,148, 53,248,249, 89, 16,  4, 43, 86,129,194,
+    163,  3,115,107,107, 62, 37, 44,113, 38,242,186, 14, 33,180,109,
+     59,172,163,163,116,248,120,127, 31,220,252, 35,179,152,253, 78,
+      4, 78, 76,202,222,175, 84,120, 21,176,235,186,252,153,  2,126,
+    166,133, 49,142,188,254, 23,161, 52, 47,250,158, 41,233,  9,107,
+    243,197,205, 35, 33,222,127,254,215,127,139,  8, 38,207, 68,169,
+     84,116, 58,157,102,179,105, 24,155, 43,150,157, 21,205,197,229,
+    114,217,178,172, 90,109,111,122,101,154,166,153, 23,166, 80, 60,
+     66,154,205, 86, 16,248,  0,128,175,107,223, 24,198,166,160, 60,
+     81,156,126, 23, 43,161, 64,152,252,229,195,  7,211, 79, 61,242,
+     81,125,246,155,255, 16,241,127, 20,139,197, 33,165,243, 11,162,
+     88,252, 36,240,  3, 61, 47,100,161,148, 22,213, 74,160,180,144,
+    231,190, 12, 99, 83,155, 75,  3, 85, 42,247,136, 71, 40,165,226,
+      8,101,161, 93,161,162, 61,  6,115,  5, 99, 83, 83,213,102,171,
+    197, 87,166, 97, 24,134,177, 41,122, 27,192,138, 18,  3,239,207,
+    251,157, 97, 24,209, 62,229, 63,  3, 85, 83, 75,160, 24,213,178,
+     95, 59, 83,131,133,247,137, 63, 25,213,118, 58, 34,198,118,210,
+    103,230,117, 93,220,182, 19, 67, 21,243,233,  3,156,158,222, 56,
+    237,174,128, 57,132,210, 59,229,187,113,  6, 35,113, 86, 12,197,
+    105, 46, 90, 91, 23,232,203,179,179,115, 23, 66,104, 89, 86,251,
+    122, 60,207, 38, 33, 33,113,221,112,  1, 66,193,138, 82,173,238,
+     66,  8, 91,173,150, 60,155,144,144,144,184,112,200,179,152, 83,
+    146,112,197,206, 92, 36, 36, 36, 34,193, 59,109, 71,118,180,145,
+    144,144,136, 61,228,145,144,144,144, 56, 35,228,233, 29,197, 84,
+    253,157, 91,215,  1,  0,210,156, 52, 39,205,173,176, 57,169, 80,
+     36, 36, 36, 36,161, 72, 72, 72, 72, 66,145,144,144,144,132, 34,
+     33, 33, 33,113, 54,110, 94, 43, 82,  8, 97, 62,159,239,118,187,
+     49,247,178,212, 52,109, 52, 26,197,211,161, 61,151,203,241,126,
+    157,188,187,133, 80, 91, 24, 99,140, 49,  0,192,247,125,254, 28,
+    154,132,196, 91,164, 80, 24, 99, 16,194,108, 54, 27,167,209, 68,
+     34,161,221,214, 38,147, 73, 60,230, 82,169, 20,132,144, 82, 26,
+    131, 69,198, 24,165, 20,125,128,120, 91, 15,  9,137,183, 75,161,
+      0,  0,250,253,190,118, 91,235,245,122,177, 89,204,100, 50,224,
+    248, 93,176,177,241,166,231,121, 49, 24,162,148, 82, 74,181,219,
+     87,240,254, 42,132,144,166,105,130,186,231, 32,132, 48,198,158,
+    231,197,223,149,125,185,  6,140, 87,104, 46,108,134, 61,221, 45,
+    112,186, 67,118,169, 84,228,111,159, 57,243, 49,194, 27,153, 67,
+    241,125, 63,145, 72,112, 39,143,141, 80, 70, 63,140,128, 68,116,
+     62, 80, 40, 20, 54, 54, 55,196, 57, 30, 15, 21,183,127,190,157,
+    207,231,227, 20, 95,217,108,182,240,113, 33, 54,  5, 29,137, 57,
+    199,233, 36, 33, 84,176,226, 56,157, 39,214,193,244, 69, 85, 83,
+    249, 91, 89,137,247,147,158,234, 43,165, 80, 38,147,137,255, 79,
+     31, 99, 28, 79, 70,  3,  0, 16, 79,235,204,183,132, 74, 52, 77,
+    131, 16,122,158, 55, 56, 28,136,139,233, 38,147, 73,183,219,245,
+     60, 15, 99,124,103,235,206,224,245, 32, 30,181,194, 77,196, 38,
+    139,162, 50,135,177,162,169,106, 94,215,191,174,125,227,  7,  1,
+    127,243, 11,191,184,250, 33, 15,  0, 96, 48, 24,108,108,110, 64,
+      8,227, 23,180, 18,203,129,103,211, 57,149,196,150,253,229,145,
+     99,191,223,207,102,179,156, 86,186,221,174,232,149,249,252,219,
+    231,113, 58, 66,180,230, 52,245,150,219,117, 57,161,240,102, 61,
+    233,139,116, 89,122,247,230, 18, 10,251, 55,227,199, 19, 18, 55,
+     11,137, 68, 34,145, 72,172,182,197,213,128,162, 40,170,166, 98,
+    124,129,247,212,173,221,220,209,250,190,207,179,110,242,198,223,
+      8, 48,198,218,237, 54, 66, 40,155,205,106,183,181,120, 98, 16,
+    110, 14,125,128,  6,175,  7, 47, 91, 47,165,158, 61,219,173,130,
+     32,124,215,247, 91, 20,242,112, 66,209,110,107, 49,103,212, 37,
+     46,  9,126,168,  4, 33,212, 52,109,251,231,219,131,215,  3, 65,
+    249, 41,  8,225,198,198,  6,124, 15,246, 95,245,187,221,110,108,
+     71,254, 55,153,241,199,109,199, 25, 14,135,151,121,233,234,218,
+     77, 30, 63, 27,188, 30,240,150,244,114, 53,220,184,123,215,237,
+    118,123,189,158,184,211,144, 68, 34,209,239,247,227, 47,213,131,
+     16, 66,  8, 71,163, 81, 60, 20,150, 72, 36,178,217,108,191,223,
+    191,140, 57,140,149,227,174, 88,183,170,187,187,211, 23,235,224,
+     59,  0,192,175,171,191, 74, 35,228, 56, 29,175,246, 13,  0, 96,
+    103,231,238, 73,239,235, 62, 23,161,116, 93, 55,240,  3,211, 52,
+     69,191,168,141,115,164,235, 30,  1,  0, 32, 76,170,170,154,215,
+    117,132,210,167,100, 82,242, 63,203,247,122,189,139,206,166, 71,
+    126,114,  6,150,132, 80,211, 84,209,239, 52,159, 62,216, 15,161,
+    106,  2, 95, 71,236,  7,129,219,117, 61,242, 42, 92, 34,170,122,
+     43,242,151,126,243,113,205,191,241,251,164,235, 33, 38,147,201,
+     37, 35,214, 83, 76,140, 70,163,168,138,140,249,106, 49, 12, 99,
+    122, 41, 50, 54,110, 54,155, 73,  8,103,218,  0, 99,140,181,219,
+    218,119,141,239,  4,141,107,  6, 60,126,  4,  0, 92,102, 38, 23,
+    190,238,123,230, 98,225,124,157, 67,206, 69, 40,150,117,192, 24,
+    155,159,187,200,217,164,182,183,231,251, 62,132, 80, 81, 48,111,
+     77, 50, 28, 14, 79,121,185, 57,255,227, 68, 34,113, 81, 66, 33,
+     30,153,239,176,133, 16,122,248,240,129, 56, 90,113,156, 14, 33,
+    179,119,189,  4,138, 34,  8,133,177,241, 95, 31,239,115,115,  8,
+    161,116, 26, 17,226, 17,226, 53,155,205, 74,165, 18,237,187,233,
+    249,184,230, 87,255, 73,215, 99, 48, 29, 45,248,106, 81, 53,117,
+    154, 80,248, 90,253,236,179,223,204,175,162,193,235,203, 30,135,
+    159,127, 92,131,193,  0, 33, 20, 73,253,  4, 99, 99,222,246,247,
+    108, 69,163,156,216,120,239,108, 66,233,186, 46, 99, 12, 33,212,
+    233,116,132, 18, 74,179,217,244,125,223, 48,140,251,149,123,225,
+    206,112,102,187,144,203, 16,115,216,211,139,239,228,205, 86,235,
+    209,163,175, 68, 55,250,250,227, 31,126,127, 78, 41,139, 16, 90,
+     78, 57,135,212,172,235,250, 78,185, 28,186,  1,165,195,174,235,
+    206,176,  9,151,232, 50, 10,187, 16,158, 88,  7,190,239, 87, 42,
+    149,249,237,  7,125,128,218,223,183, 99,251, 79, 70,163, 81, 36,
+    165,198,225,154,185,144,227, 44, 67, 40,142,211,129, 16,110,109,
+    109,217,182, 29, 86,188,136,  0, 87,230, 59,229,114,120, 37,182,
+    214, 36,188,135,147,170,169,181,218,158,101, 29,252,238,243,223,
+     94,249,146, 77,189,159, 42,124, 92,104,127,223, 94, 34, 67,244,
+    212,182,103,168,249,120,243, 76,207,111,  9, 92,162, 75,142, 56,
+     63, 94, 52, 91,142,227,148,138,197,121,161,  7, 33,236, 29,245,
+    110, 92, 82,143,179,  9,165,244,179,207,126, 19, 58, 56,165, 67,
+    190, 21,133,223,240, 61,190, 86,219, 59,229,163,222, 61,211,146,
+    235,186,250,113,183, 45, 71,124, 71,158, 43, 60,216,211, 84,213,
+     48, 12, 74,233,149, 55, 30,106,183,219,207,191,125,254,252,219,
+    231, 75, 44, 77,198,198,142,227, 64,  8,167,169,249,116,137,199,
+    109,201,  3,248,115,102, 55,108,219, 54, 12, 99, 97, 60,194, 24,
+    235,247,251, 55,148, 77,170,213,221,144, 77,234,245,198,163,175,
+    190,242,131,160,237,116,190,248,242,203,176,141,244,153,120,247,
+    172,233,115,  0,  0,134,177,137, 80, 90,215,117,199,113,196, 13,
+     76,215,215,193,113,151,249,171,  2,255, 31,130, 32,184,185, 43,
+    222, 35, 30,  0, 64,215,117,217,234, 36,114,248, 65, 96,219, 54,
+    198,120, 70,250,221, 92, 44,100, 19,  0,128,105,154,  8,161, 90,
+    109,207,178, 44,195, 48,206, 31, 43,156, 17,242,116, 58, 29,132,
+     16,255, 56, 93,215, 93,215,237,186,174,160,222,160,  5,195,232,
+    116, 58,142,227, 16, 66,138,139,244,100, 12,224,217,132,176,151,
+    243, 77, 68,224,  7,  0,  0,148, 78,199,108,183, 94,159,221,  9,
+    134,195,149, 58,206,103,140, 89,214,  1,  0, 32, 60, 88, 93,  1,
+    120,196,227,185,182,153, 84,  6,132,201,205,205, 77,219,182,  1,
+      0,230, 69, 50,167,107,167,243,177,239,251,166,105,242, 31,243,
+    186,110,  1,224, 56, 29, 65,132,  2, 97,178,186,187,251,212,182,
+     29,199,177, 44,171,209,104,148,203,119,197, 53, 54,190, 42,252,
+    231,127,253,247,244,143,162,115,192,243, 70, 85, 85,251,117,245,
+     87,209, 19,202,149, 74,203, 24, 96,219,207, 24, 99,140,177,182,
+    227,  8, 61,157,136, 19,121, 93,175, 84, 42,150,101, 61,177, 14,
+    166,101, 87, 24,217,  5, 65, 80,171,237,205,232,151, 37,  9,133,
+    103, 76, 24, 99,225,230,131, 16,114, 93,151,177,177, 32, 57, 13,
+     97,242,126,229, 94,169, 88,172, 55, 26,142,227, 60,126,188,255,
+    240,225,131, 21,227,148, 82,241, 39,177,119, 84, 93,239,207,105,
+     84,156,219,207,159, 94,125, 93,251,102,254,152,252,230, 34,157,
+     70,213,221,123,181,189,189, 70,163,113,122,121,212,205,  2, 15,
+      5, 44,203,  2,  0,132,156,130,177, 98,154,230, 78,249, 46, 99,
+    227, 39,150, 21,141, 66,225, 25,147,249,188,137,104,134, 70, 40,
+    125,191,114, 79,215,215, 31, 63,222,111, 52,190,139,147, 80, 40,
+     29,  2,  0, 52,245,150, 64,223, 22, 92, 52,161, 96,  5,  0, 64,
+    135,195,133, 70, 87, 94, 71,136,188,113,159, 32,148,174, 84,238,
+    213,106,123, 79,172,  3, 17, 42,239,202, 57,133, 49, 22, 42, 17,
+    152, 76,114, 37,129, 21,197,237,186,110,215,157, 95, 87, 23, 32,
+     20, 94,126, 82, 46,151,103,184,227, 79,127,254,139,232,130,148,
+     80,140, 97,140, 99, 46,157,230,162, 76,193,202,205, 93, 28,154,
+    170,  1,  0,200,185,211,242, 18, 23,156, 94,213, 52,205,102,179,
+    249,162,217, 90,153,192, 39,228, 20,199,233,132,117,213, 39, 65,
+     85,181, 83, 10,151,214, 78,119,173,130, 97,204, 92, 55, 12,163,
+    217,108, 10, 45, 72,185, 42,180,157, 14, 33, 30, 66,232, 70,  7,
+     89, 16, 38, 13,195,112, 28,167, 94,111,136, 86, 67,111,169, 84,
+     41, 22, 93,215, 93,177,192,  7,156,187,184,254,116, 44, 62, 54,
+     14,203, 79,230,115, 37,198, 49,147, 69, 62,158, 23,205, 22, 99,
+    227,105,247,230,213, 89, 49, 76,165, 71,200, 19,235,192,178, 44,
+      8,225,195,135, 15, 86, 96,197, 67,  8,235,141,198, 83,251, 25,
+     15,226, 36,162,165,236, 74,229, 30, 99, 44,124, 91,162,196, 25,
+     10,133,151,159,232,139, 54,106,172, 40,  8, 33,199,113, 78,121,
+    196,102, 57,216,182,109,219, 54,127,240,100, 56,164,148, 82,140,
+    241, 57,171,179,150,195, 76,205,159,170,106,167, 60, 70,121,131,
+    128, 80,186, 90,221,125,252,120,191,217,108, 54,155,205,233, 41,
+    149, 43, 62,170,192,135,203, 64,161,129,207,204,129, 96,169, 88,
+    188,254,146,115,237,  4, 14,134,165, 98,241, 36,229, 95, 46,223,
+     13,252, 96,186, 32, 55, 18, 84,171,187,174,123,196,107, 64, 20,
+     69,217,218,218, 42, 24,134,160,227, 36, 85, 83, 75,224,199,123,
+    147, 70, 72, 83, 85,209,242,213, 48, 54,133,166,123,103,120,255,
+    119,159,255,182,237,116,  8, 33, 92,164,164,211,200,216,220, 76,
+     31, 87, 21,197, 48,174, 56,199, 43, 20,124,181,204,156,199,237,
+    148,203,226,138,125, 22, 78, 29,127, 89,244,141, 36,148,211, 67,
+    169,252,113, 37,126,228,172, 31,219,195, 59,113,218, 58,231,172,
+     94,219,168,120,233,113,197, 96,122, 56,164, 49, 60,217,184,112,
+    181, 64,152, 20,167, 23,174,164,170, 83, 32,161, 72, 72, 92, 91,
+     80, 58,244,  8,129, 48,233, 56, 29, 74,105, 88,120, 41, 33,  9,
+     69, 66,226,226,132, 50,164,214,113,169,149,174,235, 51,133,130,
+     18,146, 80, 36, 36, 46, 22,128, 84,171,187,  0,  0,148, 70,171,
+    116,106,187, 26,120,167,237,200, 22, 86, 18, 18, 18,209,224, 93,
+     57,  5, 18, 18, 18,145,133, 60,189, 35, 55, 30, 75,185,117, 29,
+      0, 32,205, 73,115,210,220, 10,155,147, 10, 69, 66, 66, 66, 18,
+    138,132,132,132, 36, 20,  9,  9,  9, 73, 40, 18, 18, 18, 18,146,
+     80, 36, 36, 36, 36,161, 72, 72, 72, 72, 66,145,144,144,144,132,
+     34, 33, 33, 33,113, 37,132,194,216,120,190,  3,139,132,132,132,
+      4,184,232,195,129, 97, 71,101, 58, 28,174, 76,243, 52,  9,  9,
+    137, 43, 80, 40, 97,215,194, 82,177,232, 56,142,124,161,166,132,
+    132,196,146, 10,101,166,  7,106, 26,161,153,206, 64, 18, 18, 18,
+     18,231, 34,148,249,142,202, 11,187,141, 73, 72, 72, 72, 66,185,
+     48,155,112, 72, 78,145,144,120,171,208,118, 58,173, 86, 43,157,
+     78,223,175, 84, 78,122,123,252,218,114,108, 18, 57,167, 80, 58,
+    156,239,121,202, 97, 24, 70,228, 47,230,122,209,108,141, 25,155,
+    127,201,112,215,117,  3, 63, 48, 77, 83,208,219,246,193,155,118,
+     98,111,222, 68,143, 80, 90, 81,148,104, 95,238,239, 17, 66, 60,
+    162,106, 11,222,171,204, 39, 57,141, 80,228,239, 64,158, 57,248,
+     19,218, 69,128,219,210,243,250,204,130,228,163, 91, 56,240,203,
+    204,228, 66, 91,211,255,137,136,249,188,158,112,156,142,239,251,
+    190,239,159,210,140,248, 12, 66,225,103, 58,  8,161,167, 79,159,
+    241, 43,188,115,141, 31,  4,225, 21,  8,161,227, 56, 16,194,203,
+    116,234,161, 67,122, 82,219, 93, 85, 19,178, 52,185,185,105, 78,
+    161,116,248,248,241,254,194,246,102,145,192, 15,  2,203, 58,224,
+    205, 85, 49,198,  0,  0,199,241,  0,  0, 11,169,109,105,240, 22,
+     95,198,208,152,247,171,174,235,214, 27,141,178,128, 86, 71, 11,
+    239, 29,239,182, 45,200,150, 71, 94,205,116, 23,230, 75,168,  4,
+    138, 81, 17, 10,241,  8,183,181,240, 76,147, 79, 38,  0, 64, 85,
+    181,149, 33,148,115,118,  4, 29, 51,182,100,200,163, 40, 74, 50,
+    249,166, 77,193,120,204,124,223,103,140,  1,  0, 24, 99,132,120,
+    170,170,  1,  0, 20,  5,  3,  0, 96,242, 82, 78,168,169,234, 31,
+    255,240,251, 25,113,244,197,151, 95, 66,  8, 69,244,187,216, 54,
+    183, 58,157, 78,179,213,154,150, 63, 79,172,  3,  8,161,160,214,
+     98,140,141,107,181, 61,198,152,105,154,165, 98, 49,228,172,182,
+    211,137,182, 39,  9,239,196,230,186, 46,  0,179, 62,224,186, 71,
+      0,  0, 65,141, 86, 85, 85, 11, 61,188,235,186,150,117,208,108,
+     54, 13, 99, 83, 80,227, 52, 66,188,174,235,198,208, 52,246,148,
+    201,132, 16,178,147, 93,235, 38,178, 73,173,182,247,255,254,239,
+    255, 17,152, 67,153, 38,102,143,144, 90,109,207,113, 58,196, 35,
+    188,  9,187,208,  6,244, 79,109,155, 49, 38,174, 49,104,165,114,
+    239,209,163,175,158, 88,  7,124, 20,188,177,113,185, 92, 22, 36,
+    212,249,112,230, 55,109, 17,155,155,174,235,205,102,115,198,223,
+     24, 27, 19,226, 97,140, 99,120,177,115, 94,215, 89,121,108, 89,
+    150,219,117, 69, 16,138,170,106, 65,224,219,246,179, 24,  8, 69,
+     85, 87,231,206,243,  0,  0, 10,171, 73, 68, 65, 84, 85,199,113,
+    230,201,139,247,234, 61, 41, 78,191,161,108,114,121,126,188,112,
+    165,172,227, 56,245, 70, 67,244, 60,122,132, 56,142,163,235,186,
+    184,118, 92, 88, 81, 76,211,228, 27, 29, 99, 99,219,182, 49,198,
+    130,218, 74, 50, 54,230, 81, 97, 60, 61, 31,120,255,105,174, 71,
+    166, 37, 58,  0, 96,115, 51, 38,113, 46,154,182,138,197, 34,165,
+     52,134,162,109, 93, 95, 95, 56,153,140, 49, 83, 88, 19,210, 27,
+    202, 38,224, 66,149,178, 40,141, 98,235,129, 98,219,207, 32,132,
+    247, 43, 21,161, 86, 74,197,162,235,186,150,117,160,235, 58, 99,
+    172, 34,236,172,202, 35, 30, 23, 14,226,114,189,103, 70, 61,174,
+    235,138,139,119, 22,109, 60, 29,  0,128,158, 23,101,110,219,220,
+    106,181, 90,205, 86, 75,104,  6,157,231,164,116, 93,159,155,204,
+     35,132,208, 10,180,193, 62, 63,155,248, 65, 64,136, 23, 37,161,
+    204,231, 77,  5,181, 98,172,215, 27,190,239,151,203,101,209,238,
+      7, 97,178, 82,185, 87,171,237, 57,142, 99,154,166,184,245, 17,
+    248,  1,  0, 64, 92, 43,220, 51,163, 30,198,198,174,235, 10,141,
+    119,134,195, 31,245,130, 71, 94,  5,129, 95,169, 84,132,186, 28,
+    191,119, 79,109, 91,116,213,  2, 39,148,233,168,199,117, 93,195,
+     48, 86,146, 77,102, 58,180, 47,158,144,147,247,137, 11, 55,250,
+    170, 86,119, 53, 85,229,249, 20, 17, 35,164,116,216,108,181,196,
+     69, 31,243,178,139,127,163,172,196,110, 51, 29,245, 52,155, 77,
+    215, 61,226, 62, 16, 67,188, 67,233, 79,246, 27,211, 52, 69, 71,
+     61,154,170,170,170,230, 56,142, 97,108, 10,237, 84,157,215,117,
+     11,128,112, 50,219, 78,135, 49,102,220,252,147, 29, 74,135, 75,
+     68, 58,170,170,157,178, 79, 92,152, 80,108,251, 89, 50,  9,199,
+     99, 81,201,109,158,188, 44, 11, 56,110, 60,201, 28,132, 16, 33,
+    100,219,118, 62,174,144, 36,158,168,  7, 99, 28, 10,117, 30,239,
+     20, 68,110,170,211,167, 60,126, 16,212,235,141, 90,109,175, 82,
+    169,  8, 61, 82,189, 95,185,247,197,151, 95,214,235,223,105, 85,
+    129,132,  2, 97,114, 58,234,113, 93,119, 53,226, 29,132,210, 23,
+    205, 43,235,186,126,122, 34, 98,237, 34,107, 20, 79,231, 80, 68,
+    108,119, 30, 33,174,235,154,166, 41,116,195,153, 49, 87, 42, 22,
+     85, 77,173,213,246,234,141,198,142, 24, 34, 83,176,  2,  0, 96,
+    227,113,156,203,101,115,115,211,182,237,174,235,106,170,198,143,
+     36, 98,163, 75,172, 40,247, 43,149, 63,253,249,207,141, 70, 67,
+     40,161, 32,148, 46, 21,139,245, 70,163,237,116,132, 10,162, 48,
+    234,225,147, 89, 22, 83, 91, 16, 63,120,180, 56,205, 41,188,205,
+    235, 73,114,254,204, 73,190,  0,161, 64,152, 20,148, 52,225, 96,
+    108,108, 89,  7,177, 29,133,  0,  0, 44,235,  0, 33,196,  7,165,
+    170,154,184,186,  9,172, 96,190,179,237,196,165,188,184, 80,183,
+    109,219,117,143, 24, 27,115,151,136,115,165,114,242,162,148,138,
+     54,100,154,102,179,213,106, 52, 26, 66, 85,109, 24,245,240,201,
+    204,199, 59,153,113,114,202, 37,247,242,107,244,198,182,102,179,
+     73, 41,141, 33, 23,203, 81,175, 55, 40,165,225,201, 14,159,214,
+    176,252, 55,242,141, 84, 85, 53, 74,233,139,102, 43, 78, 65,139,
+     49, 38,132, 16, 66,226,247,  1,238,120,  8,161, 24,152,171, 92,
+     46, 83, 74,155,205,151, 49, 68, 61,162,115,219, 87,197, 41, 81,
+    229,152,175, 11,161, 80, 58,172, 55, 26,177, 85, 49,243,212,239,
+    116,157, 11, 66,105,195, 48,  8,241,218, 78, 71,132,197,157,157,
+    187,  0,  0,219,182,235,245,  6,119,182, 48,221, 48,253, 99,228,
+     81, 15,165,148, 16,162,199,155, 30,242,131,160,182,183,  7,  0,
+     48, 98, 41,123, 41, 24,155, 24,227,243, 28,106, 94, 50,234, 97,
+    140,185,174, 27, 91, 45,207, 95, 31,239,255,233,207,127,241,  8,
+    185, 65,156,178,118, 77,  8,133, 43, 46, 66,188,249, 83,171, 82,
+    177, 24,121,168,197,223, 14, 53, 83,101,191, 83, 46,187,174, 43,
+     40, 59,139, 21,229,225,195,  7,150,117, 80,111, 52,234,141,  6,
+    198, 56,153,132,220,  7,248,193,153,184,168,135, 82, 90, 20, 31,
+     69,206,223, 59, 93,215,133,198,200,211, 40,151,239, 10, 58,118,
+    156,137,122,226,212,122, 60,149, 78, 60, 18, 79, 74,241,126,229,
+     30,132,112, 69,  8, 69,213,212, 18, 40,158,244,171,200,213,184,
+    166,222, 50,205, 59, 51,194,149,151,165,  4,126, 64,135, 20, 67,
+     69,196,138,212, 62,215,218,142, 67,  8,225,170,196, 48, 12, 69,
+     81,120,134, 69, 80,212, 83, 46,151,199,140,137,246,129,153,180,
+     87, 18, 66, 77, 83,  5,157,131,148,138,197,244, 92, 36,165,169,
+     42, 31,105,132,171,133,175,201,176,176,  0,194,100,165, 82, 97,
+    140, 77, 47,155,133,255, 76,116, 44, 89, 38,132,196, 89,240,114,
+    249, 28,223, 59,251,251,127,139,231,127,149, 61,238,165, 57,105,
+    110,229,205,201, 54, 26, 18, 18, 18,146, 80, 36, 36, 36, 36,161,
+     72, 72, 72,172, 48,222,105, 59,135,114, 22, 36, 36, 36,164, 66,
+    145,144,144,184, 94, 88,147,201,237,155,104, 78, 66, 46,149,235,
+    105, 78, 42, 20,  9,  9,  9, 73, 40, 18, 18, 18,146, 80, 36, 36,
+     36, 36,161, 72, 72, 72, 72,156,141, 53, 57,  5, 18, 18,145, 32,
+    147,201, 96,140, 51, 31,102,  0,  0,244, 95,180,215,235,141, 70,
+     35,169, 80, 36, 36, 36,150,  4, 99,172,253,125,187,253,125,123,
+     50,153, 20, 62, 46,200,144, 71, 66, 98, 53,161,105, 90,161, 32,
+    214,195,  7,131, 65,175,215,163,148, 82, 74,123,189, 94, 34,145,
+    136,225,253, 82,146, 80, 36, 36, 36, 36,161, 72, 72,220, 64,228,
+    114,185,171, 18, 68,236,223, 44,134, 87,234, 74, 66,145,144,136,
+     15,217, 91,217,124, 62, 31,179,209,124, 62,159,249, 48,115,120,
+    248, 54, 62, 37, 39, 79,121, 36,110, 62,107,100,179,137, 68,226,
+    164,223,226,143, 48,  0, 32,146,198,189,231,212, 38,248, 35,252,
+    178,245,242, 45, 60,226,145,132, 34,177, 18,113,205,250, 25,113,
+     77,230,195,204,224,245, 32,158,127,  6, 33,228,255,211,127, 59,
+    217, 68, 18,138,196, 42,224,101,235,229, 73, 10,165,240,113, 97,
+     50,153,180,191,111,103, 50,153,203,191,129,249, 60,232,245,122,
+    147,201,228,173,189, 23,146, 80, 36,110, 60, 78,145,  3,156, 77,
+     70,163, 81, 38,147,137,231,159,225,180, 21, 91,132, 37,  9,229,
+    122,193, 15,  2,207, 35, 99,198,  0,  0, 10, 86, 52, 85, 91,153,
+    246,198, 18,  0,  0,206, 38,209,126, 38,165, 67,199,113, 84, 77,
+     93,216,221, 98, 99,115,  3,  0,240,252,219,231,146, 80,222, 46,
+    116, 93,215,182,159, 81, 74, 33,132,138,130,199, 99, 86,111, 52,
+      0,  0,166,105,150,138, 69, 73, 43, 43, 47, 94, 46,179,114,234,
+    141, 70, 85, 91,220,  3,184,255,170,255, 54, 79,248, 91, 74, 40,
+     79,172,  3,199,113, 12,195,168,238,238, 78,183, 89,105, 59, 29,
+    219,182,  9, 33,213,221, 93,201, 41,171,  4,198, 88, 84,228,210,
+    106,181, 16, 66, 39, 53,223,234,245,122,111,243, 60,191,141,117,
+     40,245,122,195,117,221,106,117,247,126,229,222, 76,175,175,130,
+    177, 89,173,238, 82, 74,159, 88,150,116,194,149,138,109,125, 63,
+     18, 87,247,131,128, 82,106,196,213,141,116,  5, 21, 10,132, 16,
+     99, 12,  0,240,188, 55,189, 99, 49,198, 16, 66,254, 99,248,219,
+    169,  8,147, 46, 81, 32,232, 17, 66, 60,146,132,176, 96, 24,116,
+     72,221,174,171,231,117,222,122,142, 55, 24,223, 54,183,234,245,
+      6,  0,192, 48, 12,206,  2,126, 16,184, 93, 23,  0,112,161,126,
+    151,188,137,114,185, 92,230, 59,140, 31,  4,205,102,139,210, 33,
+      0,  0, 99, 69, 85,111,229,117,189, 92, 46, 91,150,229,  7,129,
+    160,222,119, 18, 55, 23,205,102,139, 47,194, 21, 27, 87, 84,110,
+    126, 54,161,100, 50,153,236,173,108, 34,145, 24, 12,  6, 92, 52,
+     98,140,209,  7, 40,180,164,221,214,126, 66, 13,255,240, 46, 74,
+     40, 30, 33, 97, 99,218,225,112, 88, 42, 22,155,173,150, 71, 94,
+    253,186,250, 43, 63,  8,108,219,230,157, 46,121,142,131,141,199,
+    188, 97,162,227,116,154,205,230, 69,  9,165,222,104, 32,132,182,
+    205, 45, 30,224, 88,150,165,170,154,166,222,  2,  0, 52, 91, 45,
+    223, 15,242,186, 94, 48, 54,109,219,118,187,174, 36, 20,137, 25,
+    184,174,171,170,218,140,176, 93,  1, 68,229,230,103, 19, 74, 54,
+    155, 29,188, 30, 32,132, 48,198,243,162,145, 82,250,252,219,231,
+      8,161,194,199,  5,239, 31, 94, 72,111, 23,  2,241,  8,  0,160,
+     90,221,229, 93,126, 33, 76,154, 91, 91,245, 70,195, 35,196,113,
+     58, 16, 66,211, 52,127,252,227,227,102,244,174,235, 34,132, 46,
+     74, 94,174,235,154, 91, 91,  0,  0,198,198,182,109,235,186,254,
+    203,135, 15,222,252,234,232, 40,204,155, 40, 10,246,200,171,146,
+    116, 32,137, 41,180,157, 14, 99,204, 48, 86, 48,222,137,202,205,
+    207,200,161,164, 82, 41,248, 30,164,148, 14,  6,  3, 94,194, 44,
+      2,188,221, 52, 23,147,220,165, 77,211,132, 16,218,246, 51,199,
+    113,204,173,173,208,207,117, 93,247,125,159,210, 33, 15,101,213,
+     11,118,165,247,131,128, 29,247,211,238,186, 46, 99,108, 90,221,
+    248,190, 47, 78,146, 60,181,159,125, 93,251,230,204, 63,251,186,
+    246,205,121,254, 76,226,170,228,  9,132, 80,116,231,249,248, 17,
+    161,155,159, 65, 40, 60,112, 26, 12,  6,148,210, 68, 34, 33,168,
+     58,168, 96,108,154,166,233,186,238, 23, 95,126,233,  7,  1,167,
+    149,114,185,236,251,254,140, 60,193,138,  2, 33,244,  8,241, 60,
+    130, 49, 70,233,139, 41,207,233,114,163, 33,165,252,  3,227,185,
+    103,190, 31, 16,114,182,124, 35,196, 59,207,159, 73,196, 15, 74,
+    135,174,235,234,186,190,122,199,127, 17,186,249,218,153,145, 21,
+      0,224,147,226, 39,225,143,131,129,144,103, 34,118,202,119, 13,
+     99,243,209,163,175,234,245,  6,143, 65, 10,198,166,101, 89,138,
+    130,103,238,159,174,235,174,235, 14,135, 67,125,125,253,242,118,
+     25, 27,243,207,231,121,217, 31,233,102, 40, 36,147, 47,213,199,
+    205, 69,215,117,  1,  0,186,190,190,122, 67,139,208,205,215,206,
+     20, 66,254, 63,125,190,177, 35,132, 50, 31,102, 64,247,205,111,
+    249,219,168, 38,147,201,229,143,247,249,173, 58,231,163, 22,170,
+    170,218,182,205, 24,171, 84,238,241, 83,158,243, 99,186, 44, 90,
+    207,235,245, 70,163,237, 56, 60, 65, 91,111, 52, 32,132,116, 56,
+      4,  0,120,132, 80, 74, 69,100,242,165,250,184,185,232,116, 58,
+      8,161, 85,141,119,162,114,243,181, 51,133,144,231,121,220, 18,
+    198, 56,255, 65, 62,148, 67,252,149,153,244, 95,180,221,110, 95,
+    114, 72,129, 31,240, 19, 28,  8,161,105,110,157,254,199,121, 93,
+    183, 44, 11, 33,132, 21,229,162,132,194, 35, 38, 66, 94,229,117,
+     29, 43,138, 97, 24,182,109,119, 58, 29,254, 91,126, 90, 76,233,
+     48,  8,124,211, 52,163,205,228,239,236,220,125,107,159,239, 88,
+      1,248, 65,224,251,254,116,244,189, 98,241, 78, 84,110,190,118,
+    106,216,239, 15,  6,131,208, 13,  6,131, 65,251,251, 54, 99,140,
+    191, 47,147, 95,228, 15, 86,142, 70, 35,254,171,229,134, 84, 42,
+     21,245,188,206, 24,195, 63, 13,112,170,213,221,105,217, 82,173,
+    238,162, 52,130, 48, 25, 94, 55, 12,131,103, 88,207, 15, 93,215,
+     29,199,225,197,245,247, 43,247,116,125, 61,240,131, 52, 66,  5,
+     99, 19,  0,128, 80,154,120,164, 84,250, 68,187, 96,186,247, 60,
+     92, 38,221,242,230,194,113, 58,  0,  0,126, 62, 24, 27,158, 88,
+      7,148, 14,119,118,238, 10, 93, 60,209,186,249,105,132, 50, 35,
+    114, 38,147,201, 73,103,180,167,252,234, 50,254, 54,227,213,225,
+    143,225, 55,  8,165, 47,170, 35,118,202,101,215,117,107,123,123,
+    188,184, 62,175,235,211, 34, 86, 83,213,200,169, 68, 98, 37,  8,
+    197,193, 24,199, 92,126,226, 56, 14,132, 80,244, 86, 20,173,155,
+    191,117,165,247, 92,224, 80, 74,191,248,242,203,122,189,225, 29,
+     87,181,120,132,180,157,206, 19,235,128, 31, 51, 73, 72,132,160,
+    116,104,110,109,149,203,119,227, 52,202, 87,166,126,211, 82, 54,
+    111,227,195,129, 88, 81,126,247,249,231,245, 70,163,217,106,241,
+    220,205,116, 64,  4,147, 80,186,144,196, 52, 16, 74, 95,168, 26,
+     59,162, 72, 36,  0,  0,156,153, 82,148,132,114, 93,116,202, 78,
+    249,238, 78,249, 46,165, 67, 58,164,128, 63,173, 32,211, 28, 18,
+    215,  6, 65, 16, 96,140,111,220,154,124,219, 95,176,180, 68, 22,
+     70, 66, 34,  6,220,175,220,187,137,255,182,108,163, 33, 33, 33,
+     17, 25,254, 63,181, 34,148,206,219,178, 22,153,  0,  0,  0,  0,
+     73, 69, 78, 68,174, 66, 96,130
+};
+
+
+static const FileEntry  _file_entries[] =
+{
+
+    { "arrow_down.png", _data_arrow_down_png, 3438 },
+    { "arrow_left.png", _data_arrow_left_png, 4122 },
+    { "arrow_right.png", _data_arrow_right_png, 4147 },
+    { "arrow_up.png", _data_arrow_up_png, 3493 },
+    { "back.png", _data_back_png, 3564 },
+    { "end.png", _data_end_png, 3562 },
+    { "home.png", _data_home_png, 3578 },
+    { "key.png", _data_key_png, 2857 },
+    { "layout", _data_layout, 7714 },
+    { "menu.png", _data_menu_png, 3079 },
+    { "power.png", _data_power_png, 3782 },
+    { "select.png", _data_select_png, 3374 },
+    { "send.png", _data_send_png, 3561 },
+    { "spacebar.png", _data_spacebar_png, 2916 },
+    { "volume_down.png", _data_volume_down_png, 3586 },
+    { "volume_up.png", _data_volume_up_png, 3856 },
+    { "device.png", _data_device_png, 45511 },
+    { "keyboard.png", _data_keyboard_png, 11032 },
+    { NULL, NULL, 0 }
+};
+
diff --git a/android/skin/file.c b/android/skin/file.c
new file mode 100644
index 0000000..b0cb9cf
--- /dev/null
+++ b/android/skin/file.c
@@ -0,0 +1,693 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/file.h"
+#include "android/utils/path.h"
+#include "android/charmap.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/system.h"
+#include "android/utils/debug.h"
+
+//#include "qemu-common.h"
+
+/** UTILITY ROUTINES
+ **/
+static SkinImage*
+skin_image_find_in( const char*  dirname, const char*  filename )
+{
+    char   buffer[1024];
+    char*  p   = buffer;
+    char*  end = p + sizeof(buffer);
+
+    p = bufprint( p, end, "%s" PATH_SEP "%s", dirname, filename );
+    if (p >= end)
+        return SKIN_IMAGE_NONE;
+
+    return skin_image_find_simple(buffer);
+}
+
+/** SKIN BACKGROUND
+ **/
+
+static void
+skin_background_done( SkinBackground*  background )
+{
+    if (background->image)
+        skin_image_unref(&background->image);
+}
+
+static int
+skin_background_init_from( SkinBackground*  background,
+                           AConfig*         node,
+                           const char*      basepath )
+{
+    const char* img = aconfig_str(node, "image", NULL);
+    int         x   = aconfig_int(node, "x", 0);
+    int         y   = aconfig_int(node, "y", 0);
+
+    background->valid = 0;
+
+    if (img == NULL)   /* no background */
+        return -1;
+
+    background->image = skin_image_find_in( basepath, img );
+    if (background->image == SKIN_IMAGE_NONE) {
+        background->image = NULL;
+        return -1;
+    }
+
+    background->rect.pos.x  = x;
+    background->rect.pos.y  = y;
+    background->rect.size.w = skin_image_w( background->image );
+    background->rect.size.h = skin_image_h( background->image );
+
+    background->valid = 1;
+
+    return 0;
+}
+
+/** SKIN DISPLAY
+ **/
+
+static void
+skin_display_done( SkinDisplay*  display )
+{
+    qframebuffer_done( display->qfbuff );
+}
+
+static int
+skin_display_init_from( SkinDisplay*  display, AConfig*  node )
+{
+    display->rect.pos.x  = aconfig_int(node, "x", 0);
+    display->rect.pos.y  = aconfig_int(node, "y", 0);
+    display->rect.size.w = aconfig_int(node, "width", 0);
+    display->rect.size.h = aconfig_int(node, "height", 0);
+    display->rotation    = aconfig_unsigned(node, "rotation", SKIN_ROTATION_0);
+
+    display->valid = ( display->rect.size.w > 0 && display->rect.size.h > 0 );
+
+    if (display->valid) {
+        SkinRect  r;
+        skin_rect_rotate( &r, &display->rect, -display->rotation );
+        qframebuffer_init( display->qfbuff,
+                           r.size.w,
+                           r.size.h,
+                           0,
+                           QFRAME_BUFFER_RGB565 );
+
+        qframebuffer_fifo_add( display->qfbuff );
+    }
+    return display->valid ? 0 : -1;
+}
+
+/** SKIN BUTTON
+ **/
+
+typedef struct
+{
+    const char*     name;
+    AndroidKeyCode  code;
+} KeyInfo;
+
+static KeyInfo  _keyinfo_table[] = {
+    { "dpad-up",      kKeyCodeDpadUp },
+    { "dpad-down",    kKeyCodeDpadDown },
+    { "dpad-left",    kKeyCodeDpadLeft },
+    { "dpad-right",   kKeyCodeDpadRight },
+    { "dpad-center",  kKeyCodeDpadCenter },
+    { "soft-left",    kKeyCodeSoftLeft },
+    { "soft-right",   kKeyCodeSoftRight },
+    { "volume-up",    kKeyCodeVolumeUp },
+    { "volume-down",  kKeyCodeVolumeDown },
+    { "power",        kKeyCodePower },
+    { "home",         kKeyCodeHome },
+    { "back",         kKeyCodeBack },
+    { "del",          kKeyCodeDel },
+    { "0",            kKeyCode0 },
+    { "1",            kKeyCode1 },
+    { "2",            kKeyCode2 },
+    { "3",            kKeyCode3 },
+    { "4",            kKeyCode4 },
+    { "5",            kKeyCode5 },
+    { "6",            kKeyCode6 },
+    { "7",            kKeyCode7 },
+    { "8",            kKeyCode8 },
+    { "9",            kKeyCode9 },
+    { "star",         kKeyCodeStar },
+    { "pound",        kKeyCodePound },
+    { "phone-dial",   kKeyCodeCall },
+    { "phone-hangup", kKeyCodeEndCall },
+    { "q",            kKeyCodeQ },
+    { "w",            kKeyCodeW },
+    { "e",            kKeyCodeE },
+    { "r",            kKeyCodeR },
+    { "t",            kKeyCodeT },
+    { "y",            kKeyCodeY },
+    { "u",            kKeyCodeU },
+    { "i",            kKeyCodeI },
+    { "o",            kKeyCodeO },
+    { "p",            kKeyCodeP },
+    { "a",            kKeyCodeA },
+    { "s",            kKeyCodeS },
+    { "d",            kKeyCodeD },
+    { "f",            kKeyCodeF },
+    { "g",            kKeyCodeG },
+    { "h",            kKeyCodeH },
+    { "j",            kKeyCodeJ },
+    { "k",            kKeyCodeK },
+    { "l",            kKeyCodeL },
+    { "DEL",          kKeyCodeDel },
+    { "z",            kKeyCodeZ },
+    { "x",            kKeyCodeX },
+    { "c",            kKeyCodeC },
+    { "v",            kKeyCodeV },
+    { "b",            kKeyCodeB },
+    { "n",            kKeyCodeN },
+    { "m",            kKeyCodeM },
+    { "COMMA",        kKeyCodeComma },
+    { "PERIOD",       kKeyCodePeriod },
+    { "ENTER",        kKeyCodeNewline },
+    { "AT",           kKeyCodeAt },
+    { "SPACE",        kKeyCodeSpace },
+    { "SLASH",        kKeyCodeSlash },
+    { "CAP",          kKeyCodeCapLeft },
+    { "SYM",          kKeyCodeSym },
+    { "ALT",          kKeyCodeAltLeft },
+    { "ALT2",         kKeyCodeAltRight },
+    { "CAP2",         kKeyCodeCapRight },
+    { 0, 0 },
+};
+
+static unsigned
+keyinfo_lookup_code(const char *name)
+{
+    KeyInfo *ki = _keyinfo_table;
+    while(ki->name) {
+        if(!strcmp(name, ki->name))
+            return ki->code;
+        ki++;
+    }
+    return 0;
+}
+
+
+static void
+skin_button_free( SkinButton*  button )
+{
+    if (button) {
+        skin_image_unref( &button->image );
+        AFREE(button);
+    }
+}
+
+static SkinButton*
+skin_button_create_from( AConfig*   node, const char*  basepath )
+{
+    SkinButton*  button;
+    ANEW0(button);
+    if (button) {
+        const char*  img = aconfig_str(node, "image", NULL);
+        int          x   = aconfig_int(node, "x", 0);
+        int          y   = aconfig_int(node, "y", 0);
+
+        button->name       = node->name;
+        button->rect.pos.x = x;
+        button->rect.pos.y = y;
+
+        if (img != NULL)
+            button->image = skin_image_find_in( basepath, img );
+
+        if (button->image == SKIN_IMAGE_NONE) {
+            skin_button_free(button);
+            return NULL;
+        }
+
+        button->rect.size.w = skin_image_w( button->image );
+        button->rect.size.h = skin_image_h( button->image );
+
+        button->keycode = keyinfo_lookup_code( button->name );
+        if (button->keycode == 0) {
+            dprint( "Warning: skin file button uses unknown key name '%s'", button->name );
+        }
+    }
+    return button;
+}
+
+/** SKIN PART
+ **/
+
+static void
+skin_part_free( SkinPart*  part )
+{
+    if (part) {
+        skin_background_done( part->background );
+        skin_display_done( part->display );
+
+        SKIN_PART_LOOP_BUTTONS(part,button)
+            skin_button_free(button);
+        SKIN_PART_LOOP_END
+        part->buttons = NULL;
+        AFREE(part);
+    }
+}
+
+static SkinLocation*
+skin_location_create_from_v2( AConfig*  node, SkinPart*  parts )
+{
+    const char*    partname = aconfig_str(node, "name", NULL);
+    int            x        = aconfig_int(node, "x", 0);
+    int            y        = aconfig_int(node, "y", 0);
+    SkinRotation   rot      = aconfig_int(node, "rotation", SKIN_ROTATION_0);
+    SkinPart*      part;
+    SkinLocation*  location;
+
+    if (partname == NULL) {
+        dprint( "### WARNING: ignoring part location without 'name' element" );
+        return NULL;
+    }
+
+    for (part = parts; part; part = part->next)
+        if (!strcmp(part->name, partname))
+            break;
+
+    if (part == NULL) {
+        dprint( "### WARNING: ignoring part location with unknown name '%s'", partname );
+        return NULL;
+    }
+
+    ANEW0(location);
+    location->part     = part;
+    location->anchor.x = x;
+    location->anchor.y = y;
+    location->rotation = rot;
+
+    return location;
+}
+
+static SkinPart*
+skin_part_create_from_v1( AConfig*  root, const char*  basepath )
+{
+    SkinPart*  part;
+    AConfig*  node;
+    SkinBox   box;
+
+    ANEW0(part);
+    part->name = root->name;
+
+    node = aconfig_find(root, "background");
+    if (node)
+        skin_background_init_from(part->background, node, basepath);
+
+    node = aconfig_find(root, "display");
+    if (node)
+        skin_display_init_from(part->display, node);
+
+    node = aconfig_find(root, "button");
+    if (node) {
+        for (node = node->first_child; node != NULL; node = node->next)
+        {
+            SkinButton*  button = skin_button_create_from(node, basepath);
+
+            if (button != NULL) {
+                button->next  = part->buttons;
+                part->buttons = button;
+            }
+        }
+    }
+
+    skin_box_minmax_init( &box );
+
+    if (part->background->valid)
+        skin_box_minmax_update( &box, &part->background->rect );
+
+    if (part->display->valid)
+        skin_box_minmax_update( &box, &part->display->rect );
+
+    SKIN_PART_LOOP_BUTTONS(part, button)
+        skin_box_minmax_update( &box, &button->rect );
+    SKIN_PART_LOOP_END
+
+    if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) {
+        skin_part_free(part);
+        part = NULL;
+    }
+
+    return part;
+}
+
+static SkinPart*
+skin_part_create_from_v2( AConfig*  root, const char*  basepath )
+{
+    SkinPart*  part;
+    AConfig*  node;
+    SkinBox   box;
+
+    ANEW0(part);
+    part->name = root->name;
+
+    node = aconfig_find(root, "background");
+    if (node)
+        skin_background_init_from(part->background, node, basepath);
+
+    node = aconfig_find(root, "display");
+    if (node)
+        skin_display_init_from(part->display, node);
+
+    node = aconfig_find(root, "buttons");
+    if (node) {
+        for (node = node->first_child; node != NULL; node = node->next)
+        {
+            SkinButton*  button = skin_button_create_from(node, basepath);
+
+            if (button != NULL) {
+                button->next  = part->buttons;
+                part->buttons = button;
+            }
+        }
+    }
+
+    skin_box_minmax_init( &box );
+
+    if (part->background->valid)
+        skin_box_minmax_update( &box, &part->background->rect );
+
+    if (part->display->valid)
+        skin_box_minmax_update( &box, &part->display->rect );
+
+    SKIN_PART_LOOP_BUTTONS(part, button)
+        skin_box_minmax_update( &box, &button->rect );
+    SKIN_PART_LOOP_END
+
+    if ( !skin_box_minmax_to_rect( &box, &part->rect ) ) {
+        skin_part_free(part);
+        part = NULL;
+    }
+    return part;
+}
+
+/** SKIN LAYOUT
+ **/
+
+static void
+skin_layout_free( SkinLayout*  layout )
+{
+    if (layout) {
+        SKIN_LAYOUT_LOOP_LOCS(layout,loc)
+            AFREE(loc);
+        SKIN_LAYOUT_LOOP_END
+        layout->locations = NULL;
+        AFREE(layout);
+    }
+}
+
+SkinDisplay*
+skin_layout_get_display( SkinLayout*  layout )
+{
+    SKIN_LAYOUT_LOOP_LOCS(layout,loc)
+        SkinPart*  part = loc->part;
+        if (part->display->valid) {
+            return part->display;
+        }
+    SKIN_LAYOUT_LOOP_END
+    return NULL;
+}
+
+SkinRotation
+skin_layout_get_dpad_rotation( SkinLayout*  layout )
+{
+    SKIN_LAYOUT_LOOP_LOCS(layout, loc)
+        SkinPart*  part = loc->part;
+        SKIN_PART_LOOP_BUTTONS(part,button)
+            if (button->keycode == kKeyCodeDpadUp)
+                return loc->rotation;
+        SKIN_PART_LOOP_END
+    SKIN_LAYOUT_LOOP_END
+
+    return SKIN_ROTATION_0;
+}
+
+
+static int
+skin_layout_event_decode( const char*  event, int  *ptype, int  *pcode, int *pvalue )
+{
+    typedef struct {
+        const char*  name;
+        int          value;
+    } EventName;
+
+    static const EventName  _event_names[] = {
+        { "EV_SW", 0x05 },
+        { NULL, 0 },
+    };
+
+    const char*       x = strchr(event, ':');
+    const char*       y = NULL;
+    const EventName*  ev = _event_names;
+
+    if (x != NULL)
+        y = strchr(x+1, ':');
+
+    if (x == NULL || y == NULL) {
+        dprint( "### WARNING: invalid skin layout event format: '%s', should be '<TYPE>:<CODE>:<VALUE>'", event );
+        return -1;
+    }
+
+    for ( ; ev->name != NULL; ev++ )
+        if (!memcmp( event, ev->name, x - event ) && ev->name[x-event] == 0)
+            break;
+
+    if (!ev->name) {
+        dprint( "### WARNING: unrecognized skin layout event name: %.*s", x-event, event );
+        return -1;
+    }
+
+    *ptype  = ev->value;
+    *pcode  = strtol(x+1, NULL, 0);
+    *pvalue = strtol(y+1, NULL, 0);
+    return 0;
+}
+
+static SkinLayout*
+skin_layout_create_from_v2( AConfig*  root, SkinPart*  parts )
+{
+    SkinLayout*    layout;
+    int            width, height;
+    SkinLocation** ptail;
+    AConfig*       node;
+
+    ANEW0(layout);
+
+    width  = aconfig_int( root, "width", 400 );
+    height = aconfig_int( root, "height", 400 );
+
+    node = aconfig_find( root, "event" );
+    if (node != NULL) {
+        skin_layout_event_decode( node->value,
+                                  &layout->event_type,
+                                  &layout->event_code,
+                                  &layout->event_value );
+    } else {
+        layout->event_type  = 0x05;  /* close keyboard by default */
+        layout->event_code  = 0;
+        layout->event_value = 1;
+    }
+
+    layout->name  = root->name;
+    layout->color = aconfig_unsigned( root, "color", 0x808080 ) | 0xff000000;
+    ptail         = &layout->locations;
+
+    for (node = root->first_child; node; node = node->next)
+    {
+        if (!memcmp(node->name, "part", 4)) {
+            SkinLocation*  location = skin_location_create_from_v2( node, parts );
+            if (location == NULL) {
+                continue;
+            }
+            *ptail = location;
+            ptail  = &location->next;
+        }
+    }
+
+    if (layout->locations == NULL)
+        goto Fail;
+
+    layout->size.w = width;
+    layout->size.h = height;
+
+    return layout;
+
+Fail:
+    skin_layout_free(layout);
+    return NULL;
+}
+
+/** SKIN FILE
+ **/
+
+static int
+skin_file_load_from_v1( SkinFile*  file, AConfig*  aconfig, const char*  basepath )
+{
+    SkinPart*      part;
+    SkinLayout*    layout;
+    SkinLayout**   ptail = &file->layouts;
+    SkinLocation*  location;
+    int            nn;
+
+    file->parts = part = skin_part_create_from_v1( aconfig, basepath );
+    if (part == NULL)
+        return -1;
+
+    for (nn = 0; nn < 2; nn++)
+    {
+        ANEW0(layout);
+
+        layout->color = 0xff808080;
+
+        ANEW0(location);
+
+        layout->event_type  = 0x05;  /* close keyboard by default */
+        layout->event_code  = 0;
+        layout->event_value = 1;
+
+        location->part     = part;
+        switch (nn) {
+            case 0:
+                location->anchor.x = 0;
+                location->anchor.y = 0;
+                location->rotation = SKIN_ROTATION_0;
+                layout->size       = part->rect.size;
+                break;
+
+#if 0
+            case 1:
+                location->anchor.x = part->rect.size.h;
+                location->anchor.y = 0;
+                location->rotation = SKIN_ROTATION_90;
+                layout->size.w     = part->rect.size.h;
+                layout->size.h     = part->rect.size.w;
+                layout->event_value = 0;
+                break;
+
+            case 2:
+                location->anchor.x = part->rect.size.w;
+                location->anchor.y = part->rect.size.h;
+                location->rotation = SKIN_ROTATION_180;
+                layout->size       = part->rect.size;
+                break;
+#endif
+            default:
+                location->anchor.x = 0;
+                location->anchor.y = part->rect.size.w;
+                location->rotation = SKIN_ROTATION_270;
+                layout->size.w     = part->rect.size.h;
+                layout->size.h     = part->rect.size.w;
+                layout->event_value = 0;
+                break;
+        }
+        layout->locations = location;
+
+        *ptail = layout;
+        ptail  = &layout->next;
+    }
+    return 0;
+}
+
+static int
+skin_file_load_from_v2( SkinFile*  file, AConfig*  aconfig, const char*  basepath )
+{
+    AConfig*  node;
+
+    /* first, load all parts */
+    node = aconfig_find(aconfig, "parts");
+    if (node == NULL)
+        return -1;
+    else
+    {
+        SkinPart**  ptail = &file->parts;
+        for (node = node->first_child; node != NULL; node = node->next)
+        {
+            SkinPart*  part = skin_part_create_from_v2( node, basepath );
+            if (part == NULL) {
+                dprint( "## WARNING: can't load part '%s' from skin\n", node->name ? "<NULL>" : node->name );
+                continue;
+            }
+            part->next = NULL;
+            *ptail     = part;
+            ptail      = &part->next;
+        }
+    }
+
+    if (file->parts == NULL)
+        return -1;
+
+    /* then load all layouts */
+    node = aconfig_find(aconfig, "layouts");
+    if (node == NULL)
+        return -1;
+    else
+    {
+        SkinLayout**  ptail = &file->layouts;
+        for (node = node->first_child; node != NULL; node = node->next)
+        {
+            SkinLayout*  layout = skin_layout_create_from_v2( node, file->parts );
+            if (layout == NULL) {
+                dprint( "## WARNING: ignoring layout in skin file" );
+                continue;
+            }
+            *ptail = layout;
+            layout->next = NULL;
+            ptail        = &layout->next;
+        }
+    }
+    if (file->layouts == NULL)
+        return -1;
+
+    return 0;
+}
+
+SkinFile*
+skin_file_create_from_aconfig( AConfig*   aconfig, const char*  basepath )
+{
+    SkinFile*  file;
+
+    ANEW0(file);
+    if ( aconfig_find(aconfig, "parts") != NULL) {
+        if (skin_file_load_from_v2( file, aconfig, basepath ) < 0) {
+            skin_file_free( file );
+            file = NULL;
+        }
+    }
+    else {
+        if (skin_file_load_from_v1( file, aconfig, basepath ) < 0) {
+            skin_file_free( file );
+            file = NULL;
+        }
+    }
+    return file;
+}
+
+void
+skin_file_free( SkinFile*  file )
+{
+    if (file) {
+        SKIN_FILE_LOOP_LAYOUTS(file,layout)
+            skin_layout_free(layout);
+        SKIN_FILE_LOOP_END_LAYOUTS
+        file->layouts = NULL;
+
+        SKIN_FILE_LOOP_PARTS(file,part)
+            skin_part_free(part);
+        SKIN_FILE_LOOP_END_PARTS
+        file->parts = NULL;
+
+        AFREE(file);
+    }
+}
diff --git a/android/skin/file.h b/android/skin/file.h
new file mode 100644
index 0000000..8f95368
--- /dev/null
+++ b/android/skin/file.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_FILE_H
+#define _ANDROID_SKIN_FILE_H
+
+#include "android/skin/image.h"
+#include "android/config.h"
+#include "framebuffer.h"
+
+/**  Layout
+ **/
+
+typedef struct SkinBackground {
+    SkinImage*  image;
+    SkinRect    rect;
+    char        valid;
+} SkinBackground;
+
+typedef struct SkinDisplay {
+    SkinRect      rect;      /* display rectangle    */
+    SkinRotation  rotation;  /* framebuffer rotation */
+    char          valid;
+    QFrameBuffer  qfbuff[1];
+} SkinDisplay;
+
+typedef struct SkinButton {
+    struct SkinButton*  next;
+    const char*         name;
+    SkinImage*          image;
+    SkinRect            rect;
+    unsigned            keycode;
+} SkinButton;
+
+typedef struct SkinPart {
+    struct SkinPart*   next;
+    const char*        name;
+    SkinBackground     background[1];
+    SkinDisplay        display[1];
+    SkinButton*        buttons;
+    SkinRect           rect;    /* bounding box of all parts */
+} SkinPart;
+
+#define  SKIN_PART_LOOP_BUTTONS(part,button)              \
+    do {                                                  \
+        SkinButton*  __button = (part)->buttons;          \
+        while (__button != NULL) {                        \
+            SkinButton*  __button_next = __button->next;  \
+            SkinButton*  button        = __button;
+
+#define   SKIN_PART_LOOP_END             \
+            __button = __button_next;    \
+        }                                \
+    } while (0);
+
+typedef struct SkinLocation {
+    SkinPart*             part;
+    SkinPos               anchor;
+    SkinRotation          rotation;
+    struct SkinLocation*  next;
+} SkinLocation;
+
+typedef struct SkinLayout {
+    struct SkinLayout*  next;
+    const char*         name;
+    unsigned            color;
+    int                 event_type;
+    int                 event_code;
+    int                 event_value;
+    SkinSize            size;
+    SkinLocation*       locations;
+} SkinLayout;
+
+#define  SKIN_LAYOUT_LOOP_LOCS(layout,loc)               \
+    do {                                                 \
+        SkinLocation*  __loc = (layout)->locations;      \
+        while (__loc != NULL) {                          \
+            SkinLocation*  __loc_next = (__loc)->next;   \
+            SkinLocation*  loc        = __loc;
+
+#define  SKIN_LAYOUT_LOOP_END   \
+            __loc = __loc_next; \
+        }                       \
+    } while (0);
+
+extern SkinDisplay*   skin_layout_get_display( SkinLayout*  layout );
+
+extern SkinRotation   skin_layout_get_dpad_rotation( SkinLayout*  layout );
+
+typedef struct SkinFile {
+    SkinPart*       parts;
+    SkinLayout*     layouts;
+    int             num_parts;
+    int             num_layouts;
+} SkinFile;
+
+#define  SKIN_FILE_LOOP_LAYOUTS(file,layout)             \
+    do {                                                 \
+        SkinLayout*  __layout = (file)->layouts;         \
+        while (__layout != NULL) {                       \
+            SkinLayout*  __layout_next = __layout->next; \
+            SkinLayout*  layout        = __layout;
+
+#define  SKIN_FILE_LOOP_END_LAYOUTS       \
+            __layout = __layout_next;     \
+        }                                 \
+    } while (0);
+
+#define  SKIN_FILE_LOOP_PARTS(file,part)                 \
+    do {                                                 \
+        SkinPart*   __part = (file)->parts;              \
+        while (__part != NULL) {                         \
+            SkinPart*  __part_next = __part->next;       \
+            SkinPart*  part        = __part;
+
+#define  SKIN_FILE_LOOP_END_PARTS  \
+            __part = __part_next;  \
+        }                          \
+    } while (0);
+
+extern SkinFile*  skin_file_create_from_aconfig( AConfig*   aconfig, const char*  basepath );
+extern void       skin_file_free( SkinFile*  file );
+
+#endif /* _ANDROID_SKIN_FILE_H */
diff --git a/android/skin/image.c b/android/skin/image.c
new file mode 100644
index 0000000..051fc6d
--- /dev/null
+++ b/android/skin/image.c
@@ -0,0 +1,742 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/image.h"
+#include "android/resource.h"
+#include <assert.h>
+#include <limits.h>
+
+#define  DEBUG  0
+
+#if DEBUG
+static void D(const char*  fmt, ...)
+{
+    va_list  args;
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args);
+}
+#else
+#define  D(...)  do{}while(0)
+#endif
+
+/********************************************************************************/
+/********************************************************************************/
+/*****                                                                      *****/
+/*****            U T I L I T Y   F U N C T I O N S                         *****/
+/*****                                                                      *****/
+/********************************************************************************/
+/********************************************************************************/
+
+SDL_Surface*
+sdl_surface_from_argb32( void*  base, int  w, int  h )
+{
+    return SDL_CreateRGBSurfaceFrom(
+                        base, w, h, 32, w*4,
+#if WORDS_BIGENDIAN
+                        0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
+#else
+                        0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000
+#endif
+                        );
+}
+
+static void*
+rotate_image( void*  data, unsigned  width, unsigned  height,  SkinRotation  rotation )
+{
+    void*  result;
+
+    result = malloc( width*height*4 );
+    if (result == NULL)
+        return NULL;
+
+    switch (rotation & 3)
+    {
+    case SKIN_ROTATION_0:
+        memcpy( (char*)result, (const char*)data, width*height*4 );
+        break;
+
+    case SKIN_ROTATION_270:
+        {
+            unsigned*  start    = (unsigned*)data;
+            unsigned*  src_line = start + (width-1);
+            unsigned*  dst_line = (unsigned*)result;
+            unsigned   hh;
+
+            for (hh = width; hh > 0; hh--)
+            {
+                unsigned*  src   = src_line;
+                unsigned*  dst   = dst_line;
+                unsigned   count = height;
+
+                for ( ; count > 0; count-- ) {
+                    dst[0] = src[0];
+                    dst   += 1;
+                    src   += width;
+                }
+
+                src_line -= 1;
+                dst_line += height;
+            }
+        }
+        break;
+
+    case SKIN_ROTATION_180:
+        {
+            unsigned*  start    = (unsigned*)data;
+            unsigned*  src_line = start + width*(height-1);
+            unsigned*  dst_line = (unsigned*)result;
+            unsigned   hh;
+
+            for (hh = height; hh > 0; hh--)
+            {
+                unsigned*  src = src_line + (width-1);
+                unsigned*  dst = dst_line;
+
+                while (src >= src_line)
+                    *dst++ = *src--;
+
+                dst_line += width;
+                src_line -= width;
+            }
+        }
+        break;
+
+    case SKIN_ROTATION_90:
+        {
+            unsigned*  start    = (unsigned*)data;
+            unsigned*  src_line = start + width*(height-1);
+            unsigned*  dst_line = (unsigned*)result ;
+            unsigned   hh;
+
+            for (hh = width; hh > 0; hh--)
+            {
+                unsigned*  src = src_line;
+                unsigned*  dst = dst_line;
+                unsigned   count;
+
+                for (count = height; count > 0; count--) {
+                    dst[0] = src[0];
+                    dst   += 1;
+                    src   -= width;
+                }
+
+                dst_line += height;
+                src_line += 1;
+            }
+        }
+        break;
+
+    default:
+        ;
+    }
+
+    return result;
+}
+
+
+static void
+blend_image( unsigned*  dst_pixels,
+             unsigned*  src_pixels,
+             unsigned   w,
+             unsigned   h,
+             int        alpha )
+{
+    unsigned*  dst     = dst_pixels;
+    unsigned*  dst_end = dst + w*h;
+    unsigned*  src     = src_pixels;
+
+    for ( ; dst < dst_end; dst++, src++ )
+    {
+        {
+            unsigned  ag = (src[0] >> 8) & 0xff00ff;
+            unsigned  rb =  src[0]       & 0xff00ff;
+
+            ag = (ag*alpha) & 0xff00ff00;
+            rb = ((rb*alpha) >> 8) & 0x00ff00ff;
+
+            dst[0] = ag | rb;
+        }
+    }
+}
+
+
+static unsigned
+skin_image_desc_hash( SkinImageDesc*  desc )
+{
+    unsigned  h = 0;
+    int       n;
+
+    for (n = 0; desc->path[n] != 0; n++) {
+        int  c = desc->path[n];
+        h = h*33 + c;
+    }
+    h += desc->rotation*1573;
+    h += desc->blend * 7;
+
+    return  h;
+}
+
+
+static int
+skin_image_desc_equal( SkinImageDesc*  a,
+                       SkinImageDesc*  b )
+{
+    return (a->rotation == b->rotation &&
+            a->blend    == b->blend    &&
+            !strcmp(a->path, b->path));
+}
+
+/********************************************************************************/
+/********************************************************************************/
+/*****                                                                      *****/
+/*****            S K I N   I M A G E S                                     *****/
+/*****                                                                      *****/
+/********************************************************************************/
+/********************************************************************************/
+
+enum {
+    SKIN_IMAGE_CLONE = (1 << 0)   /* this image is a clone */
+};
+
+struct SkinImage {
+    unsigned         hash;
+    SkinImage*       link;
+    int              ref_count;
+    SkinImage*       next;
+    SkinImage*       prev;
+    SDL_Surface*     surface;
+    unsigned         flags;
+    unsigned         w, h;
+    void*            pixels;  /* 32-bit ARGB */
+    SkinImageDesc    desc;
+};
+
+
+
+
+static const SkinImage  _no_image[1] = {
+    { 0, NULL, 0, NULL, NULL, NULL, 0, 0, 0, NULL, { "<none>", SKIN_ROTATION_0, 0 } }
+};
+
+SkinImage*  SKIN_IMAGE_NONE = (SkinImage*)&_no_image;
+
+static void
+skin_image_free( SkinImage*  image )
+{
+    if (image && image != _no_image)
+    {
+        if (image->surface) {
+            SDL_FreeSurface(image->surface);
+            image->surface = NULL;
+        }
+
+        if (image->pixels) {
+            free( image->pixels );
+            image->pixels = NULL;
+        }
+
+        free(image);
+    }
+}
+
+
+static SkinImage*
+skin_image_alloc( SkinImageDesc*  desc, unsigned  hash )
+{
+    int         len   = strlen(desc->path);
+    SkinImage*  image = calloc(1, sizeof(*image) + len + 1);
+
+    if (image) {
+        image->desc = desc[0];
+        image->desc.path = (const char*)(image + 1);
+        memcpy( (char*)image->desc.path, desc->path, len );
+        ((char*)image->desc.path)[len] = 0;
+
+        image->hash      = hash;
+        image->next      = image->prev = image;
+        image->ref_count = 1;
+    }
+    return image;
+}
+
+
+extern void *loadpng(const char *fn, unsigned *_width, unsigned *_height);
+extern void *readpng(const unsigned char*  base, size_t  size, unsigned *_width, unsigned *_height);
+
+static int
+skin_image_load( SkinImage*  image )
+{
+    void*     data;
+    unsigned  w, h;
+    const char*  path = image->desc.path;
+
+    if (path[0] == ':') {
+        size_t                size;
+        const unsigned char*  base;
+
+        if (path[1] == '/' || path[1] == '\\')
+            path += 1;
+
+        base = android_resource_find( path+1, &size );
+        if (base == NULL) {
+            fprintf(stderr, "failed to locate built-in image file '%s'\n", path );
+            return -1;
+        }
+
+        data = readpng(base, size, &w, &h);
+        if (data == NULL) {
+            fprintf(stderr, "failed to load built-in image file '%s'\n", path );
+            return -1;
+        }
+    } else {
+        data = loadpng(path, &w, &h);
+        if (data == NULL) {
+            fprintf(stderr, "failed to load image file '%s'\n", path );
+            return -1;
+        }
+    }
+
+   /* the data is loaded into memory as RGBA bytes by libpng. we want to manage
+    * the values as 32-bit ARGB pixels, so swap the bytes accordingly depending
+    * on our CPU endianess
+    */
+    {
+        unsigned*  d     = data;
+        unsigned*  d_end = d + w*h;
+
+        for ( ; d < d_end; d++ ) {
+            unsigned  pix = d[0];
+#if WORDS_BIGENDIAN
+            /* R,G,B,A read as RGBA => ARGB */
+            pix = ((pix >> 8) & 0xffffff) | (pix << 24);
+#else
+            /* R,G,B,A read as ABGR => ARGB */
+            pix = (pix & 0xff00ff00) | ((pix >> 16) & 0xff) | ((pix & 0xff) << 16);
+#endif
+            d[0] = pix;
+        }
+    }
+
+    image->pixels = data;
+    image->w      = w;
+    image->h      = h;
+
+    image->surface = sdl_surface_from_argb32( image->pixels, w, h );
+    if (image->surface == NULL) {
+        fprintf(stderr, "failed to create SDL surface for '%s' image\n", path);
+        return -1;
+    }
+    return 0;
+}
+
+
+/* simple hash table for images */
+
+#define  NUM_BUCKETS  64
+
+typedef struct {
+    SkinImage*     buckets[ NUM_BUCKETS ];
+    SkinImage      mru_head;
+    int            num_images;
+    unsigned long  total_pixels;
+    unsigned long  max_pixels;
+    unsigned long  total_images;
+} SkinImageCache;
+
+
+static void
+skin_image_cache_init( SkinImageCache*  cache )
+{
+    memset(cache, 0, sizeof(*cache));
+#if DEBUG
+    cache->max_pixels = 1;
+#else
+    cache->max_pixels = 4*1024*1024;  /* limit image cache to 4 MB */
+#endif
+    cache->mru_head.next = cache->mru_head.prev = &cache->mru_head;
+}
+
+
+static void
+skin_image_cache_remove( SkinImageCache*  cache,
+                         SkinImage*       image )
+{
+    /* remove from hash table */
+    SkinImage**  pnode = cache->buckets + (image->hash & (NUM_BUCKETS-1));
+    SkinImage*   node;
+
+    for (;;) {
+        node = *pnode;
+        assert(node != NULL);
+        if (node == NULL)  /* should not happen */
+            break;
+        if (node == image) {
+            *pnode = node->link;
+            break;
+        }
+        pnode = &node->link;
+    }
+
+    D( "skin_image_cache: remove '%s' (rot=%d), %d pixels\n",
+       node->desc.path, node->desc.rotation, node->w*node->h );
+
+    /* remove from mru list */
+    image->prev->next = image->next;
+    image->next->prev = image->prev;
+
+    cache->total_pixels -= image->w*image->h;
+    cache->total_images -= 1;
+}
+
+
+static SkinImage*
+skin_image_cache_raise( SkinImageCache*  cache,
+                        SkinImage*       image )
+{
+    if (image != cache->mru_head.next) {
+        SkinImage*  prev = image->prev;
+        SkinImage*  next = image->next;
+
+        /* remove from mru list */
+        prev->next = next;
+        next->prev = prev;
+
+        /* add to top */
+        image->prev = &cache->mru_head;
+        image->next = image->prev->next;
+        image->prev->next = image;
+        image->next->prev = image;
+    }
+    return image;
+}
+
+
+static void
+skin_image_cache_flush( SkinImageCache*  cache )
+{
+    SkinImage*     image = cache->mru_head.prev;
+    int            count = 0;
+
+    D("skin_image_cache_flush: starting\n");
+    while (cache->total_pixels > cache->max_pixels &&
+           image != &cache->mru_head)
+    {
+        SkinImage*  prev = image->prev;
+
+        if (image->ref_count == 0) {
+            skin_image_cache_remove(cache, image);
+            count += 1;
+        }
+        image = prev;
+    }
+    D("skin_image_cache_flush: finished, %d images flushed\n", count);
+}
+
+
+static SkinImage**
+skin_image_lookup_p( SkinImageCache*   cache,
+                     SkinImageDesc*    desc,
+                     unsigned         *phash )
+{
+    unsigned     h     = skin_image_desc_hash(desc);
+    unsigned     index = h & (NUM_BUCKETS-1);
+    SkinImage**  pnode = &cache->buckets[index];
+    for (;;) {
+        SkinImage*  node = *pnode;
+        if (node == NULL)
+            break;
+        if (node->hash == h && skin_image_desc_equal(desc, &node->desc))
+            break;
+        pnode = &node->link;
+    }
+    *phash = h;
+    return  pnode;
+}
+
+
+static SkinImage*
+skin_image_create( SkinImageDesc*  desc, unsigned  hash )
+{
+    SkinImage*  node;
+
+    node = skin_image_alloc( desc, hash );
+    if (node == NULL)
+        return SKIN_IMAGE_NONE;
+
+    if (desc->rotation == SKIN_ROTATION_0 &&
+        desc->blend    == SKIN_BLEND_FULL)
+    {
+        if (skin_image_load(node) < 0) {
+            skin_image_free(node);
+            return SKIN_IMAGE_NONE;
+        }
+    }
+    else
+    {
+        SkinImageDesc  desc0 = desc[0];
+        SkinImage*     parent;
+
+        desc0.rotation = SKIN_ROTATION_0;
+        desc0.blend    = SKIN_BLEND_FULL;
+
+        parent = skin_image_find( &desc0 );
+        if (parent == SKIN_IMAGE_NONE)
+            return SKIN_IMAGE_NONE;
+
+        SDL_LockSurface(parent->surface);
+
+        if (desc->rotation == SKIN_ROTATION_90 ||
+            desc->rotation == SKIN_ROTATION_270)
+        {
+            node->w = parent->h;
+            node->h = parent->w;
+        } else {
+            node->w = parent->w;
+            node->h = parent->h;
+        }
+
+        node->pixels = rotate_image( parent->pixels, parent->w, parent->h,
+                                    desc->rotation );
+
+        SDL_UnlockSurface(parent->surface);
+        skin_image_unref(&parent);
+
+        if (node->pixels  == NULL) {
+            skin_image_free(node);
+            return SKIN_IMAGE_NONE;
+        }
+
+        if (desc->blend != SKIN_BLEND_FULL)
+            blend_image( node->pixels, node->pixels, node->w, node->h, desc->blend );
+
+        node->surface = sdl_surface_from_argb32( node->pixels, node->w, node->h );
+        if (node->surface == NULL) {
+            skin_image_free(node);
+            return SKIN_IMAGE_NONE;
+        }
+    }
+    return node;
+}
+
+
+static SkinImageCache   _image_cache[1];
+static int              _image_cache_init;
+
+SkinImage*
+skin_image_find( SkinImageDesc*  desc )
+{
+    SkinImageCache*  cache = _image_cache;
+    unsigned         hash;
+    SkinImage**      pnode = skin_image_lookup_p( cache, desc, &hash );
+    SkinImage*       node  = *pnode;
+
+    if (!_image_cache_init) {
+        _image_cache_init = 1;
+        skin_image_cache_init(cache);
+    }
+
+    if (node) {
+        node->ref_count += 1;
+        return skin_image_cache_raise( cache, node );
+    }
+    node = skin_image_create( desc, hash );
+    if (node == SKIN_IMAGE_NONE)
+        return node;
+
+    /* add to hash table */
+    node->link = *pnode;
+    *pnode     = node;
+
+    /* add to mru list */
+    skin_image_cache_raise( cache, node );
+
+    D( "skin_image_cache: add '%s' (rot=%d), %d pixels\n",
+       node->desc.path, node->desc.rotation, node->w*node->h );
+
+    cache->total_pixels += node->w*node->h;
+    if (cache->total_pixels > cache->max_pixels)
+        skin_image_cache_flush( cache );
+
+    return node;
+}
+
+
+SkinImage*
+skin_image_find_simple( const char*  path )
+{
+    SkinImageDesc  desc;
+
+    desc.path     = path;
+    desc.rotation = SKIN_ROTATION_0;
+    desc.blend    = SKIN_BLEND_FULL;
+
+    return skin_image_find( &desc );
+}
+
+
+SkinImage*
+skin_image_ref( SkinImage*  image )
+{
+    if (image && image != _no_image)
+        image->ref_count += 1;
+
+    return image;
+}
+
+
+void
+skin_image_unref( SkinImage**  pimage )
+{
+    SkinImage*  image = *pimage;
+
+    if (image) {
+        if (image != _no_image && --image->ref_count == 0) {
+            if ((image->flags & SKIN_IMAGE_CLONE) != 0) {
+                skin_image_free(image);
+            }
+        }
+        *pimage = NULL;
+    }
+}
+
+
+SkinImage*
+skin_image_rotate( SkinImage*  source, SkinRotation  rotation )
+{
+    SkinImageDesc  desc;
+    SkinImage*     image;
+
+    if (source == _no_image || source->desc.rotation == rotation)
+        return source;
+
+    desc          = source->desc;
+    desc.rotation = rotation;
+    image         = skin_image_find( &desc );
+    skin_image_unref( &source );
+    return image;
+}
+
+
+SkinImage*
+skin_image_clone( SkinImage*  source )
+{
+    SkinImage*   image;
+
+    if (source == NULL || source == _no_image)
+        return SKIN_IMAGE_NONE;
+
+    image = calloc(1,sizeof(*image));
+    if (image == NULL)
+        goto Fail;
+
+    image->desc  = source->desc;
+    image->hash  = source->hash;
+    image->flags = SKIN_IMAGE_CLONE;
+    image->w     = source->w;
+    image->h     = source->h;
+    image->pixels = rotate_image( source->pixels, source->w, source->h,
+                                  SKIN_ROTATION_0 );
+    if (image->pixels == NULL)
+        goto Fail;
+
+    image->surface = sdl_surface_from_argb32( image->pixels, image->w, image->h );
+    if (image->surface == NULL)
+        goto Fail;
+
+    return image;
+Fail:
+    if (image != NULL)
+        skin_image_free(image);
+    return SKIN_IMAGE_NONE;
+}
+
+SkinImage*
+skin_image_clone_full( SkinImage*    source,
+                       SkinRotation  rotation,
+                       int           blend )
+{
+    SkinImageDesc   desc;
+    SkinImage*      clone;
+
+    if (source == NULL || source == SKIN_IMAGE_NONE)
+        return SKIN_IMAGE_NONE;
+
+    if (rotation == SKIN_ROTATION_0 &&
+        blend    == SKIN_BLEND_FULL)
+    {
+        return skin_image_clone(source);
+    }
+
+    desc.path     = source->desc.path;
+    desc.rotation = rotation;
+    desc.blend    = blend;
+
+    clone = skin_image_create( &desc, 0 );
+    if (clone != SKIN_IMAGE_NONE)
+        clone->flags |= SKIN_IMAGE_CLONE;
+
+    return clone;
+}
+
+/* apply blending to a source skin image and copy the result to a target clone image */
+extern void
+skin_image_blend_clone( SkinImage*  clone, SkinImage*  source, int  blend )
+{
+    SDL_LockSurface( clone->surface );
+    blend_image( clone->pixels, source->pixels, source->w, source->h, blend );
+    SDL_UnlockSurface( clone->surface );
+    SDL_SetAlpha( clone->surface, SDL_SRCALPHA, 255 );
+}
+
+int
+skin_image_w( SkinImage*  image )
+{
+    return  image ? image->w : 0;
+}
+
+int
+skin_image_h( SkinImage*  image )
+{
+    return  image ? image->h : 0;
+}
+
+int
+skin_image_org_w( SkinImage*  image )
+{
+    if (image) {
+        if (image->desc.rotation == SKIN_ROTATION_90 ||
+            image->desc.rotation == SKIN_ROTATION_270)
+            return image->h;
+        else
+            return image->w;
+    }
+    return 0;
+}
+
+int
+skin_image_org_h( SkinImage*  image )
+{
+    if (image) {
+        if (image->desc.rotation == SKIN_ROTATION_90 ||
+            image->desc.rotation == SKIN_ROTATION_270)
+            return image->w;
+        else
+            return image->h;
+    }
+    return 0;
+}
+
+SDL_Surface*
+skin_image_surface( SkinImage*  image )
+{
+    return image ? image->surface : NULL;
+}
diff --git a/android/skin/image.h b/android/skin/image.h
new file mode 100644
index 0000000..a94a372
--- /dev/null
+++ b/android/skin/image.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_IMAGE_H
+#define _ANDROID_SKIN_IMAGE_H
+
+#include "android/android.h"
+#include <SDL.h>
+#include "android/skin/rect.h"
+
+/* helper functions */
+
+extern SDL_Surface*    sdl_surface_from_argb32( void*  base, int  w, int  h );
+
+/* skin image file objects */
+
+/* opaque skin image type. all skin images are placed in a simple MRU cache
+ * to limit the emulator's memory usage, with the exception of 'clones' created
+ * with skin_image_clone() or skin_image_clone_blend()
+ */
+typedef struct SkinImage   SkinImage;
+
+/* a descriptor for a given skin image */
+typedef struct SkinImageDesc {
+    const char*      path;      /* image file path (must be .png) */
+    AndroidRotation  rotation;  /* rotation */
+    int              blend;     /* blending, 0..256 value */
+} SkinImageDesc;
+
+#define  SKIN_BLEND_NONE   0
+#define  SKIN_BLEND_HALF   128
+#define  SKIN_BLEND_FULL   256
+
+/* a special value returned when an image cannot be properly loaded */
+extern SkinImage*    SKIN_IMAGE_NONE;
+
+/* return the SDL_Surface* pointer of a given skin image */
+extern SDL_Surface*  skin_image_surface( SkinImage*  image );
+extern int           skin_image_w      ( SkinImage*  image );
+extern int           skin_image_h      ( SkinImage*  image );
+extern int           skin_image_org_w  ( SkinImage*  image );
+extern int           skin_image_org_h  ( SkinImage*  image );
+
+/* get an image from the cache (load it from the file if necessary).
+ * returns SKIN_IMAGE_NONE in case of error. cannot return NULL
+ * this function also increments the reference count of the skin image,
+ * use "skin_image_unref()" when you don't need it anymore
+ */
+extern SkinImage*    skin_image_find( SkinImageDesc*  desc );
+
+extern SkinImage*    skin_image_find_simple( const char*  path );
+
+/* increment the reference count of a given skin image,
+ * don't do anything if 'image' is NULL */
+extern SkinImage*    skin_image_ref( SkinImage*  image );
+
+/* decrement the reference count of a given skin image. if
+ * the count reaches 0, the image becomes eligible for cache flushing.
+ * unless it was created through a skin_image_clone... function, where
+ * it is immediately discarded...
+ */
+extern void          skin_image_unref( SkinImage**  pimage );
+
+/* get the rotation of a given image. this decrements the reference count
+ * of the source after returning the target, whose reference count is incremented
+ */
+extern SkinImage*    skin_image_rotate( SkinImage*  source, SkinRotation  rotation );
+
+/* create a skin image clone. the clone is not part of the cache and will
+ * be destroyed immediately when its reference count reaches 0. this is useful
+ * if you need to modify the content of the clone (e.g. blending)
+ */
+extern SkinImage*    skin_image_clone( SkinImage*  source );
+
+/* create a skin image clone, the clone is a rotated version of a source image
+ */
+extern SkinImage*    skin_image_clone_full( SkinImage*       source,
+                                            SkinRotation     rotation,
+                                            int              blend );
+
+/* apply blending to a source skin image and copy the result to a target clone image */
+extern void          skin_image_blend_clone( SkinImage*  clone, SkinImage*  source, int  blend );
+
+#endif /* _ANDROID_SKIN_IMAGE_H */
diff --git a/android/skin/keyboard.c b/android/skin/keyboard.c
new file mode 100644
index 0000000..6a9c79b
--- /dev/null
+++ b/android/skin/keyboard.c
@@ -0,0 +1,783 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/keyboard.h"
+#include "android/utils/debug.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/system.h"
+#include "android/android.h"
+
+#define  DEBUG  1
+
+#if DEBUG
+#  define  D(...)  VERBOSE_PRINT(keys,__VA_ARGS__)
+#else
+#  define  D(...)  ((void)0)
+#endif
+
+
+#define  USE_KEYSET  1
+
+/** LAST PRESSED KEYS
+ ** a small buffer of last pressed keys, this is used to properly
+ ** implement the Unicode keyboard mode (SDL key up event always have
+ ** their .unicode field set to 0
+ **/
+typedef struct {
+    int  unicode;  /* Unicode of last pressed key        */
+    int  sym;      /* SDL key symbol value (e.g. SDLK_a) */
+    int  mod;      /* SDL key modifier value             */
+} LastKey;
+
+#define  MAX_LAST_KEYS  16
+#define  MAX_KEYCODES   256*2
+
+struct SkinKeyboard {
+    const AKeyCharmap*  charmap;
+    SkinKeyset*         kset;
+    char                enabled;
+    char                raw_keys;
+    char                last_count;
+    int                 keycode_count;
+
+    SkinRotation        rotation;
+
+    SkinKeyCommandFunc  command_func;
+    void*               command_opaque;
+    SkinKeyEventFunc    press_func;
+    void*               press_opaque;
+
+    LastKey             last_keys[ MAX_LAST_KEYS ];
+    int                 keycodes[ MAX_KEYCODES ];
+};
+
+
+void
+skin_keyboard_set_keyset( SkinKeyboard*  keyboard, SkinKeyset*  kset )
+{
+    if (kset == NULL)
+        return;
+    if (keyboard->kset && keyboard->kset != android_keyset) {
+        skin_keyset_free(keyboard->kset);
+    }
+    keyboard->kset = kset;
+}
+
+
+const char*
+skin_keyboard_charmap_name( SkinKeyboard*  keyboard )
+{
+    if (keyboard && keyboard->charmap)
+        return keyboard->charmap->name;
+
+    return "qwerty";
+}
+
+void
+skin_keyboard_set_rotation( SkinKeyboard*     keyboard,
+                            SkinRotation      rotation )
+{
+    keyboard->rotation = (rotation & 3);
+}
+
+void
+skin_keyboard_on_command( SkinKeyboard*  keyboard, SkinKeyCommandFunc  cmd_func, void*  cmd_opaque )
+{
+    keyboard->command_func   = cmd_func;
+    keyboard->command_opaque = cmd_opaque;
+}
+
+void
+skin_keyboard_on_key_press( SkinKeyboard*  keyboard, SkinKeyEventFunc  press_func, void*  press_opaque )
+{
+    keyboard->press_func   = press_func;
+    keyboard->press_opaque = press_opaque;
+}
+
+void
+skin_keyboard_add_key_event( SkinKeyboard*  kb,
+                             unsigned       code,
+                             unsigned       down )
+{
+    if (code != 0 && kb->keycode_count < MAX_KEYCODES) {
+        //dprint("add keycode %d, down %d\n", code % 0x1ff, down );
+        kb->keycodes[(int)kb->keycode_count++] = ( (code & 0x1ff) | (down ? 0x200 : 0) );
+    }
+}
+
+
+void
+skin_keyboard_flush( SkinKeyboard*  kb )
+{
+    if (kb->keycode_count > 0) {
+        if (VERBOSE_CHECK(keys)) {
+            int  nn;
+            printf(">> KEY" );
+            for (nn = 0; nn < kb->keycode_count; nn++) {
+                int  code = kb->keycodes[nn];
+                printf(" [0x%03x,%s]", (code & 0x1ff), (code & 0x200) ? "down" : " up " );
+            }
+            printf( "\n" );
+        }
+        kbd_put_keycodes(kb->keycodes, kb->keycode_count);
+        kb->keycode_count = 0;
+    }
+}
+
+
+static void
+skin_keyboard_cmd( SkinKeyboard*   keyboard,
+                   SkinKeyCommand  command,
+                   int             param )
+{
+    if (keyboard->command_func) {
+        keyboard->command_func( keyboard->command_opaque, command, param );
+    }
+}
+
+
+static LastKey*
+skin_keyboard_find_last( SkinKeyboard*  keyboard,
+                         int            sym )
+{
+    LastKey*  k   = keyboard->last_keys;
+    LastKey*  end = k + keyboard->last_count;
+
+    for ( ; k < end; k++ ) {
+        if (k->sym == sym)
+            return k;
+    }
+    return NULL;
+}
+
+static void
+skin_keyboard_add_last( SkinKeyboard*  keyboard,
+                        int            sym,
+                        int            mod,
+                        int            unicode )
+{
+    LastKey*  k = keyboard->last_keys + keyboard->last_count;
+
+    if (keyboard->last_count < MAX_LAST_KEYS) {
+        k->sym     = sym;
+        k->mod     = mod;
+        k->unicode = unicode;
+
+        keyboard->last_count += 1;
+    }
+}
+
+static void
+skin_keyboard_remove_last( SkinKeyboard*  keyboard,
+                           int            sym )
+{
+    LastKey*  k   = keyboard->last_keys;
+    LastKey*  end = k + keyboard->last_count;
+
+    for ( ; k < end; k++ ) {
+        if (k->sym == sym) {
+           /* we don't need a sorted array, so place the last
+            * element in place at the position of the removed
+            * one... */
+            k[0] = end[-1];
+            keyboard->last_count -= 1;
+            break;
+        }
+    }
+}
+
+static void
+skin_keyboard_clear_last( SkinKeyboard*  keyboard )
+{
+    keyboard->last_count = 0;
+}
+
+static int
+skin_keyboard_rotate_sym( SkinKeyboard*  keyboard,
+                          int            sym )
+{
+    switch (keyboard->rotation) {
+        case SKIN_ROTATION_90:
+            switch (sym) {
+                case SDLK_LEFT:  sym = SDLK_DOWN; break;
+                case SDLK_RIGHT: sym = SDLK_UP; break;
+                case SDLK_UP:    sym = SDLK_LEFT; break;
+                case SDLK_DOWN:  sym = SDLK_RIGHT; break;
+            }
+            break;
+
+        case SKIN_ROTATION_180:
+            switch (sym) {
+                case SDLK_LEFT:  sym = SDLK_RIGHT; break;
+                case SDLK_RIGHT: sym = SDLK_LEFT; break;
+                case SDLK_UP:    sym = SDLK_DOWN; break;
+                case SDLK_DOWN:  sym = SDLK_UP; break;
+            }
+            break;
+
+        case SKIN_ROTATION_270:
+            switch (sym) {
+                case SDLK_LEFT:  sym = SDLK_UP; break;
+                case SDLK_RIGHT: sym = SDLK_DOWN; break;
+                case SDLK_UP:    sym = SDLK_RIGHT; break;
+                case SDLK_DOWN:  sym = SDLK_LEFT; break;
+            }
+            break;
+
+        default: ;
+    }
+    return  sym;
+}
+
+#if USE_KEYSET
+static AndroidKeyCode
+skin_keyboard_key_to_code( SkinKeyboard*  keyboard,
+                           unsigned       sym,
+                           int            mod,
+                           int            down )
+{
+    AndroidKeyCode  code   = 0;
+    int             mod0   = mod;
+    SkinKeyCommand  command;
+
+    /* first, handle the arrow keys directly */
+    /* rotate them if necessary */
+    sym  = skin_keyboard_rotate_sym(keyboard, sym);
+    mod &= (KMOD_CTRL | KMOD_ALT | KMOD_SHIFT);
+
+    switch (sym) {
+        case SDLK_LEFT:       code = kKeyCodeDpadLeft; break;
+        case SDLK_RIGHT:      code = kKeyCodeDpadRight; break;
+        case SDLK_UP:         code = kKeyCodeDpadUp; break;
+        case SDLK_DOWN:       code = kKeyCodeDpadDown; break;
+        default: ;
+    }
+
+    if (code != 0) {
+        D("handling arrow (sym=%d mod=%d)", sym, mod);
+        if (!keyboard->raw_keys) {
+            int  doCapL, doCapR, doAltL, doAltR;
+
+            if (!down) {
+                LastKey*  k = skin_keyboard_find_last(keyboard, sym);
+                if (k != NULL) {
+                    mod = k->mod;
+                    skin_keyboard_remove_last( keyboard, sym );
+                }
+            } else {
+                skin_keyboard_add_last( keyboard, sym, mod, 0);
+            }
+
+            doCapL = (mod & 0x7ff) & KMOD_LSHIFT;
+            doCapR = (mod & 0x7ff) & KMOD_RSHIFT;
+            doAltL = (mod & 0x7ff) & KMOD_LALT;
+            doAltR = (mod & 0x7ff) & KMOD_RALT;
+
+            if (down) {
+                if (doAltL) skin_keyboard_add_key_event( keyboard, kKeyCodeAltLeft, 1 );
+                if (doAltR) skin_keyboard_add_key_event( keyboard, kKeyCodeAltRight, 1 );
+                if (doCapL) skin_keyboard_add_key_event( keyboard, kKeyCodeCapLeft, 1 );
+                if (doCapR) skin_keyboard_add_key_event( keyboard, kKeyCodeCapRight, 1 );
+            }
+            skin_keyboard_add_key_event(keyboard, code, down);
+
+            if (!down) {
+                if (doCapR) skin_keyboard_add_key_event( keyboard, kKeyCodeCapRight, 0 );
+                if (doCapL) skin_keyboard_add_key_event( keyboard, kKeyCodeCapLeft, 0 );
+                if (doAltR) skin_keyboard_add_key_event( keyboard, kKeyCodeAltRight, 0 );
+                if (doAltL) skin_keyboard_add_key_event( keyboard, kKeyCodeAltLeft, 0 );
+            }
+            code = 0;
+        }
+        return code;
+    }
+
+    /* special case for keypad keys, ignore them here if numlock is on */
+    if ((mod0 & KMOD_NUM) != 0) {
+        switch (sym) {
+            case SDLK_KP0:
+            case SDLK_KP1:
+            case SDLK_KP2:
+            case SDLK_KP3:
+            case SDLK_KP4:
+            case SDLK_KP5:
+            case SDLK_KP6:
+            case SDLK_KP7:
+            case SDLK_KP8:
+            case SDLK_KP9:
+            case SDLK_KP_PLUS:
+            case SDLK_KP_MINUS:
+            case SDLK_KP_MULTIPLY:
+            case SDLK_KP_DIVIDE:
+            case SDLK_KP_EQUALS:
+            case SDLK_KP_PERIOD:
+            case SDLK_KP_ENTER:
+                return 0;
+        }
+    }
+
+    /* now try all keyset combos */
+    command = skin_keyset_get_command( keyboard->kset, sym, mod );
+    if (command != SKIN_KEY_COMMAND_NONE) {
+        D("handling command %s from (sym=%d, mod=%d, str=%s)",
+          skin_key_command_to_str(command), sym, mod, skin_key_symmod_to_str(sym,mod));
+        skin_keyboard_cmd( keyboard, command, down );
+        return 0;
+    }
+    D("could not handle (sym=%d, mod=%d, str=%s)", sym, mod,
+      skin_key_symmod_to_str(sym,mod));
+    return -1;
+}
+#else /* !USE_KEYSET */
+/* this will look for non-Unicode key strokes, e.g. arrows, F1, F2, etc...
+ * note that we have some special handling for shift-<arrow>, these will
+ * be emulated as two key strokes on the device
+ */
+static AndroidKeyCode
+skin_keyboard_key_to_code( SkinKeyboard*  keyboard,
+                           unsigned       sym,
+                           int            mod,
+                           int            down )
+{
+    AndroidKeyCode  code       = 0;
+    int             doAltShift = 0;
+
+    sym = skin_keyboard_rotate_sym(keyboard, sym);
+
+    switch (sym) {
+        case SDLK_LEFT:       code = kKeyCodeDpadLeft;  doAltShift = 1; break;
+        case SDLK_RIGHT:      code = kKeyCodeDpadRight; doAltShift = 1; break;
+        case SDLK_UP:         code = kKeyCodeDpadUp;    doAltShift = 1; break;
+        case SDLK_DOWN:       code = kKeyCodeDpadDown;  doAltShift = 1; break;
+        case SDLK_HOME:       code = kKeyCodeHome; break;
+        case SDLK_BACKSPACE:  code = kKeyCodeDel; doAltShift = 1; break;
+        case SDLK_ESCAPE:     code = kKeyCodeBack; break;
+        case SDLK_RETURN:     code = kKeyCodeNewline; doAltShift = 1; break;
+        case SDLK_F1:         code = kKeyCodeSoftLeft; break;
+        case SDLK_F2:         code = kKeyCodeSoftRight; break;
+        case SDLK_F3:         code = kKeyCodeCall; break;
+        case SDLK_F4:         code = kKeyCodeEndCall; break;
+        case SDLK_F5:         code = kKeyCodeSearch; break;
+        case SDLK_F7:         code = kKeyCodePower; break;
+
+        case SDLK_F8: /* network connect/disconnect */
+            if (down) {
+                skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_TOGGLE_NETWORK, 1 );
+            }
+            return 0;
+
+#ifdef CONFIG_TRACE
+        case SDLK_F9:  /* start tracing */
+            if (down) {
+                skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_TOGGLE_TRACING, 1 );
+            }
+            return 0;
+
+            case SDLK_F10: /* stop tracing */
+            if (down) {
+                skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_TOGGLE_TRACING, 1 );
+            }
+            return 0;
+#endif
+
+        case SDLK_F12: /* change orientation */
+            if (down && (mod & (KMOD_LCTRL | KMOD_RCTRL)) != 0) {
+                skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_CHANGE_LAYOUT, +1 );
+            }
+            return 0;
+
+        case SDLK_PAGEUP:     return kKeyCodeSoftLeft;
+        case SDLK_PAGEDOWN:   return kKeyCodeSoftRight;
+
+        case SDLK_t:
+            if (down && (mod & (KMOD_LCTRL| KMOD_RCTRL)) != 0) {
+                skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_TOGGLE_TRACKBALL, 1 );
+                return 0;
+            }
+            break;
+
+        case SDLK_KP5:
+            if ((mod & (KMOD_LCTRL | KMOD_RCTRL)) != 0)
+                code = kKeyCodeCamera;
+            break;
+    }
+
+    if (code != 0) {
+        if (doAltShift && !keyboard->raw_keys) {
+            int  doCapL, doCapR, doAltL, doAltR;
+
+            if (!down) {
+                LastKey*  k = skin_keyboard_find_last(keyboard, sym);
+                if (k != NULL) {
+                    mod = k->mod;
+                    skin_keyboard_remove_last( keyboard, sym );
+                }
+            } else {
+                skin_keyboard_add_last( keyboard, sym, mod, 0);
+            }
+
+            doCapL = (mod & 0x7ff) & KMOD_LSHIFT;
+            doCapR = (mod & 0x7ff) & KMOD_RSHIFT;
+            doAltL = (mod & 0x7ff) & KMOD_LALT;
+            doAltR = (mod & 0x7ff) & KMOD_RALT;
+
+            if (down) {
+                if (doAltL) skin_keyboard_add_key_event( keyboard, kKeyCodeAltLeft, 1 );
+                if (doAltR) skin_keyboard_add_key_event( keyboard, kKeyCodeAltRight, 1 );
+                if (doCapL) skin_keyboard_add_key_event( keyboard, kKeyCodeCapLeft, 1 );
+                if (doCapR) skin_keyboard_add_key_event( keyboard, kKeyCodeCapRight, 1 );
+            }
+            skin_keyboard_add_key_event(keyboard, code, down);
+
+            if (!down) {
+                if (doCapR) skin_keyboard_add_key_event( keyboard, kKeyCodeCapRight, 0 );
+                if (doCapL) skin_keyboard_add_key_event( keyboard, kKeyCodeCapLeft, 0 );
+                if (doAltR) skin_keyboard_add_key_event( keyboard, kKeyCodeAltRight, 0 );
+                if (doAltL) skin_keyboard_add_key_event( keyboard, kKeyCodeAltLeft, 0 );
+            }
+            code = 0;
+        }
+        return code;
+    }
+
+    if ((mod & KMOD_NUM) == 0) {
+        switch (sym) {
+            case SDLK_KP8:       return kKeyCodeDpadUp;
+            case SDLK_KP2:       return kKeyCodeDpadDown;
+            case SDLK_KP4:       return kKeyCodeDpadLeft;
+            case SDLK_KP6:       return kKeyCodeDpadRight;
+            case SDLK_KP5:       return kKeyCodeDpadCenter;
+
+            case SDLK_KP_PLUS:    return kKeyCodeVolumeUp;
+            case SDLK_KP_MINUS:   return kKeyCodeVolumeDown;
+
+            case SDLK_KP_MULTIPLY:
+                if (down) {
+                    skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_ONION_ALPHA_UP, 1 );
+                }
+                return 0;
+
+            case SDLK_KP_DIVIDE:
+                if (down) {
+                    skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_ONION_ALPHA_DOWN, 1 );
+                }
+                return 0;
+
+            case SDLK_KP7:
+                if (down) {
+                    skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_CHANGE_LAYOUT_PREV, 1 );
+                }
+                return 0;
+
+            case SDLK_KP9:
+                if (down) {
+                    skin_keyboard_cmd( keyboard, SKIN_KEY_COMMAND_CHANGE_LAYOUT_NEXT, 1 );
+                }
+                return 0;
+        }
+    }
+    return -1;
+}
+#endif /* !USE_KEYSET */
+
+/* this gets called only if the reverse unicode mapping didn't work
+ * or wasn't used (when in raw keys mode)
+ */
+static AndroidKeyCode
+skin_keyboard_raw_key_to_code(SkinKeyboard*  kb, unsigned sym, int  down)
+{
+    switch(sym){
+    case SDLK_1:          return kKeyCode1;
+    case SDLK_2:          return kKeyCode2;
+    case SDLK_3:          return kKeyCode3;
+    case SDLK_4:          return kKeyCode4;
+    case SDLK_5:          return kKeyCode5;
+    case SDLK_6:          return kKeyCode6;
+    case SDLK_7:          return kKeyCode7;
+    case SDLK_8:          return kKeyCode8;
+    case SDLK_9:          return kKeyCode9;
+    case SDLK_0:          return kKeyCode0;
+
+    case SDLK_q:          return kKeyCodeQ;
+    case SDLK_w:          return kKeyCodeW;
+    case SDLK_e:          return kKeyCodeE;
+    case SDLK_r:          return kKeyCodeR;
+    case SDLK_t:          return kKeyCodeT;
+    case SDLK_y:          return kKeyCodeY;
+    case SDLK_u:          return kKeyCodeU;
+    case SDLK_i:          return kKeyCodeI;
+    case SDLK_o:          return kKeyCodeO;
+    case SDLK_p:          return kKeyCodeP;
+    case SDLK_a:          return kKeyCodeA;
+    case SDLK_s:          return kKeyCodeS;
+    case SDLK_d:          return kKeyCodeD;
+    case SDLK_f:          return kKeyCodeF;
+    case SDLK_g:          return kKeyCodeG;
+    case SDLK_h:          return kKeyCodeH;
+    case SDLK_j:          return kKeyCodeJ;
+    case SDLK_k:          return kKeyCodeK;
+    case SDLK_l:          return kKeyCodeL;
+    case SDLK_z:          return kKeyCodeZ;
+    case SDLK_x:          return kKeyCodeX;
+    case SDLK_c:          return kKeyCodeC;
+    case SDLK_v:          return kKeyCodeV;
+    case SDLK_b:          return kKeyCodeB;
+    case SDLK_n:          return kKeyCodeN;
+    case SDLK_m:          return kKeyCodeM;
+    case SDLK_COMMA:      return kKeyCodeComma;
+    case SDLK_PERIOD:     return kKeyCodePeriod;
+    case SDLK_SPACE:      return kKeyCodeSpace;
+    case SDLK_SLASH:      return kKeyCodeSlash;
+    case SDLK_RETURN:     return kKeyCodeNewline;
+    case SDLK_BACKSPACE:  return kKeyCodeDel;
+
+/* these are qwerty keys not on a device keyboard */
+    case SDLK_TAB:        return kKeyCodeTab;
+    case SDLK_BACKQUOTE:  return kKeyCodeGrave;
+    case SDLK_MINUS:      return kKeyCodeMinus;
+    case SDLK_EQUALS:     return kKeyCodeEquals;
+    case SDLK_LEFTBRACKET: return kKeyCodeLeftBracket;
+    case SDLK_RIGHTBRACKET: return kKeyCodeRightBracket;
+    case SDLK_BACKSLASH:  return kKeyCodeBackslash;
+    case SDLK_SEMICOLON:  return kKeyCodeSemicolon;
+    case SDLK_QUOTE:      return kKeyCodeApostrophe;
+
+    case SDLK_RSHIFT:     return kKeyCodeCapRight;
+    case SDLK_LSHIFT:     return kKeyCodeCapLeft;
+    case SDLK_RMETA:      return kKeyCodeSym;
+    case SDLK_LMETA:      return kKeyCodeSym;
+    case SDLK_RALT:       return kKeyCodeAltRight;
+    case SDLK_LALT:       return kKeyCodeAltLeft;
+    case SDLK_RCTRL:      return kKeyCodeSym;
+    case SDLK_LCTRL:      return kKeyCodeSym;
+
+    default:
+        /* fprintf(stderr,"* unknown sdl keysym %d *\n", sym); */
+        return -1;
+    }
+}
+
+
+static void
+skin_keyboard_do_key_event( SkinKeyboard*   kb,
+                            AndroidKeyCode  code,
+                            int             down )
+{
+    if (kb->press_func) {
+        kb->press_func( kb->press_opaque, code, down );
+    }
+    skin_keyboard_add_key_event(kb, code, down);
+}
+
+
+int
+skin_keyboard_process_unicode_event( SkinKeyboard*  kb,  unsigned int  unicode, int  down )
+{
+    const AKeyCharmap*  cmap = kb->charmap;
+    int                 n;
+
+    if (unicode == 0)
+        return 0;
+
+    /* check base keys */
+    for (n = 0; n < cmap->num_entries; n++) {
+        if (cmap->entries[n].base == unicode) {
+            skin_keyboard_add_key_event(kb, cmap->entries[n].code, down);
+            return 1;
+        }
+    }
+
+    /* check caps + keys */
+    for (n = 0; n < cmap->num_entries; n++) {
+        if (cmap->entries[n].caps == unicode) {
+            if (down)
+                skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down);
+            skin_keyboard_add_key_event(kb, cmap->entries[n].code, down);
+            if (!down)
+                skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down);
+            return 2;
+        }
+    }
+
+    /* check fn + keys */
+    for (n = 0; n < cmap->num_entries; n++) {
+        if (cmap->entries[n].fn == unicode) {
+            if (down)
+                skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down);
+            skin_keyboard_add_key_event(kb, cmap->entries[n].code, down);
+            if (!down)
+                skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down);
+            return 2;
+        }
+    }
+
+    /* check caps + fn + keys */
+    for (n = 0; n < cmap->num_entries; n++) {
+        if (cmap->entries[n].caps_fn == unicode) {
+            if (down) {
+                skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down);
+                skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down);
+            }
+            skin_keyboard_add_key_event(kb, cmap->entries[n].code, down);
+            if (!down) {
+                skin_keyboard_add_key_event(kb, kKeyCodeCapLeft, down);
+                skin_keyboard_add_key_event(kb, kKeyCodeAltLeft, down);
+            }
+            return 3;
+        }
+    }
+
+    /* no match */
+    return 0;
+}
+
+
+void
+skin_keyboard_enable( SkinKeyboard*  keyboard,
+                      int            enabled )
+{
+    keyboard->enabled = enabled;
+    if (enabled) {
+        SDL_EnableUNICODE(!keyboard->raw_keys);
+        SDL_EnableKeyRepeat(0,0);
+    }
+}
+
+void
+skin_keyboard_process_event( SkinKeyboard*  kb, SDL_Event*  ev, int  down )
+{
+    unsigned         code;
+    int              unicode = ev->key.keysym.unicode;
+    int              sym     = ev->key.keysym.sym;
+    int              mod     = ev->key.keysym.mod;
+
+    /* ignore key events if we're not enabled */
+    if (!kb->enabled) {
+        printf( "ignoring key event sym=%d mod=0x%x unicode=%d\n",
+                sym, mod, unicode );
+        return;
+    }
+
+    /* first, try the keyboard-mode-independent keys */
+    code = skin_keyboard_key_to_code( kb, sym, mod, down );
+    if (code == 0)
+        return;
+
+    if ((int)code > 0) {
+        skin_keyboard_do_key_event(kb, code, down);
+        skin_keyboard_flush(kb);
+        return;
+    }
+
+    /* Ctrl-K is used to switch between 'unicode' and 'raw' modes */
+    if (sym == SDLK_k)
+    {
+        int  mod2 = mod & 0x7ff;
+
+        if ( mod2 == KMOD_LCTRL || mod2 == KMOD_RCTRL ) {
+            if (down) {
+                skin_keyboard_clear_last(kb);
+                kb->raw_keys = !kb->raw_keys;
+                SDL_EnableUNICODE(!kb->raw_keys);
+                D( "switching keyboard to %s mode", kb->raw_keys ? "raw" : "unicode" );
+            }
+            return;
+        }
+    }
+
+    if (!kb->raw_keys) {
+       /* ev->key.keysym.unicode is only valid on keydown events, and will be 0
+        * on the corresponding keyup ones, so remember the set of last pressed key
+        * syms to "undo" the job
+        */
+        if ( !down && unicode == 0 ) {
+            LastKey*  k = skin_keyboard_find_last(kb, sym);
+            if (k != NULL) {
+                unicode = k->unicode;
+                skin_keyboard_remove_last(kb, sym);
+            }
+        }
+    }
+    if (!kb->raw_keys &&
+        skin_keyboard_process_unicode_event( kb, unicode, down ) > 0)
+    {
+        if (down)
+            skin_keyboard_add_last( kb, sym, mod, unicode );
+
+        skin_keyboard_flush( kb );
+        return;
+    }
+
+    code = skin_keyboard_raw_key_to_code( kb, sym, down );
+
+    if ( !kb->raw_keys &&
+         (code == kKeyCodeAltLeft  || code == kKeyCodeAltRight ||
+          code == kKeyCodeCapLeft  || code == kKeyCodeCapRight ||
+          code == kKeyCodeSym) )
+        return;
+
+    if (code == -1) {
+        D("ignoring keysym %d", sym );
+    } else if (code > 0) {
+        skin_keyboard_do_key_event(kb, code, down);
+        skin_keyboard_flush(kb);
+    }
+}
+
+
+SkinKeyboard*
+skin_keyboard_create_from_aconfig( AConfig*  aconfig, int  use_raw_keys )
+{
+    SkinKeyboard*  kb;
+    const char*    charmap_name = "qwerty";
+    AConfig*       node;
+
+    ANEW0(kb);
+
+    node = aconfig_find( aconfig, "keyboard" );
+    if (node != NULL)
+        charmap_name = aconfig_str(node, "charmap", charmap_name);
+
+    {
+        int  nn;
+
+        for (nn = 0; nn < android_charmap_count; nn++) {
+            if ( !strcmp(android_charmaps[nn]->name, charmap_name) ) {
+                kb->charmap = android_charmaps[nn];
+                break;
+            }
+        }
+
+        if (!kb->charmap) {
+            fprintf(stderr, "### warning, skin requires unknown '%s' charmap, reverting to '%s'\n",
+                    charmap_name, android_charmaps[0]->name );
+            kb->charmap = android_charmaps[0];
+        }
+    }
+    kb->raw_keys = use_raw_keys;
+    kb->enabled  = 0;
+
+    /* add default keyset */
+    if (android_keyset)
+        kb->kset = android_keyset;
+    else
+        kb->kset = skin_keyset_new_from_text( skin_keyset_get_default() );
+
+    return kb;
+}
+
+void
+skin_keyboard_free( SkinKeyboard*  keyboard )
+{
+    if (keyboard) {
+        AFREE(keyboard);
+    }
+}
diff --git a/android/skin/keyboard.h b/android/skin/keyboard.h
new file mode 100644
index 0000000..45efd18
--- /dev/null
+++ b/android/skin/keyboard.h
@@ -0,0 +1,59 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_KEYBOARD_H
+#define _ANDROID_SKIN_KEYBOARD_H
+
+#include "android/charmap.h"
+#include "android/config.h"
+#include "android/skin/image.h"  /* for SkinRotation */
+#include "android/skin/keyset.h"
+#include <SDL.h>
+
+typedef struct SkinKeyboard   SkinKeyboard;
+
+typedef void (*SkinKeyCommandFunc)( void*  opaque, SkinKeyCommand  command, int  param );
+
+typedef void (*SkinKeyEventFunc)( void*  opaque, AndroidKeyCode  code, int  down );
+
+extern SkinKeyboard*  skin_keyboard_create_from_aconfig( AConfig*  aconfig, int  use_raw_keys );
+
+extern void           skin_keyboard_set_keyset( SkinKeyboard*  keyboard, SkinKeyset*  kset );
+
+extern const char*    skin_keyboard_charmap_name( SkinKeyboard*  keyboard );
+
+extern void           skin_keyboard_free( SkinKeyboard*  keyboard );
+
+extern void           skin_keyboard_enable( SkinKeyboard*  keyboard,
+                                            int            enabled );
+
+extern void           skin_keyboard_on_command( SkinKeyboard*       keyboard,
+                                                SkinKeyCommandFunc  cmd_func,
+                                                void*               cmd_opaque );
+
+extern void           skin_keyboard_set_rotation( SkinKeyboard*     keyboard,
+                                                  SkinRotation      rotation );
+
+extern void           skin_keyboard_on_key_press( SkinKeyboard*     keyboard,
+                                                  SkinKeyEventFunc  press_func,
+                                                  void*             press_opaque );
+
+extern void           skin_keyboard_process_event( SkinKeyboard*  keyboard, SDL_Event*  ev, int  down );
+extern int            skin_keyboard_process_unicode_event( SkinKeyboard*  kb,  unsigned int  unicode, int  down );
+
+extern void           skin_keyboard_add_key_event( SkinKeyboard*  k, unsigned code, unsigned  down );
+extern void           skin_keyboard_flush( SkinKeyboard*  kb );
+
+/* defined in android_main.c */
+extern SkinKeyboard*  android_emulator_get_keyboard( void );
+
+#endif /* _ANDROID_SKIN_KEYBOARD_H */
+
diff --git a/android/skin/keyset.c b/android/skin/keyset.c
new file mode 100644
index 0000000..7a92971
--- /dev/null
+++ b/android/skin/keyset.c
@@ -0,0 +1,542 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/keyset.h"
+#include "android/utils/debug.h"
+#include "android/utils/bufprint.h"
+#include "android/android.h"
+#include <SDL.h>
+
+#define  DEBUG  1
+
+#if 1
+#  define  D_ACTIVE  VERBOSE_CHECK(keys)
+#else
+#  define  D_ACTIVE  DEBUG
+#endif
+
+#if DEBUG
+#  define  D(...)   VERBOSE_PRINT(keys,__VA_ARGS__)
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+#define _SKIN_KEY_COMMAND(x,y)    #x ,
+static const char* const command_strings[ SKIN_KEY_COMMAND_MAX ] = {
+    SKIN_KEY_COMMAND_LIST
+};
+#undef _SKIN_KEY_COMMAND
+
+const char*
+skin_key_command_to_str( SkinKeyCommand  cmd )
+{
+    if (cmd > SKIN_KEY_COMMAND_NONE && cmd < SKIN_KEY_COMMAND_MAX)
+        return  command_strings[cmd];
+
+    return NULL;
+}
+
+SkinKeyCommand
+skin_key_command_from_str( const char*  str, int  len )
+{
+    int  nn;
+    if (len < 0)
+        len = strlen(str);
+    for (nn = 0; nn < SKIN_KEY_COMMAND_MAX; nn++) {
+        const char*  cmd = command_strings[nn];
+
+        if ( !memcmp( cmd, str, len ) && cmd[len] == 0 )
+            return (SkinKeyCommand) nn;
+    }
+    return SKIN_KEY_COMMAND_NONE;
+}
+
+
+#define _SKIN_KEY_COMMAND(x,y)    y ,
+static const char* const command_descriptions[ SKIN_KEY_COMMAND_MAX ] = {
+    SKIN_KEY_COMMAND_LIST
+};
+#undef _SKIN_KEY_COMMAND
+
+const char*
+skin_key_command_description( SkinKeyCommand  cmd )
+{
+    if (cmd > SKIN_KEY_COMMAND_NONE && cmd < SKIN_KEY_COMMAND_MAX)
+        return command_descriptions[cmd];
+
+    return NULL;
+}
+
+#define  _KEYSYM1_(x)  _KEYSYM_(x,x)
+
+#define _KEYSYM_LIST  \
+    _KEYSYM1_(BACKSPACE)   \
+    _KEYSYM1_(TAB)         \
+    _KEYSYM1_(CLEAR)       \
+    _KEYSYM_(RETURN,ENTER) \
+    _KEYSYM1_(PAUSE)       \
+    _KEYSYM1_(ESCAPE)      \
+    _KEYSYM1_(SPACE)       \
+    _KEYSYM_(EXCLAIM,EXCLAM)    \
+    _KEYSYM_(QUOTEDBL,DOUBLEQUOTE)   \
+    _KEYSYM_(HASH,HASH)    \
+    _KEYSYM1_(DOLLAR)      \
+    _KEYSYM1_(AMPERSAND)   \
+    _KEYSYM1_(QUOTE)       \
+    _KEYSYM_(LEFTPAREN,LPAREN)  \
+    _KEYSYM_(RIGHTPAREN,RPAREN) \
+    _KEYSYM1_(ASTERISK) \
+    _KEYSYM1_(PLUS) \
+    _KEYSYM1_(COMMA) \
+    _KEYSYM1_(MINUS) \
+    _KEYSYM1_(PERIOD) \
+    _KEYSYM1_(SLASH) \
+    _KEYSYM1_(0) \
+    _KEYSYM1_(1) \
+    _KEYSYM1_(2) \
+    _KEYSYM1_(3) \
+    _KEYSYM1_(4) \
+    _KEYSYM1_(5) \
+    _KEYSYM1_(6) \
+    _KEYSYM1_(7) \
+    _KEYSYM1_(8) \
+    _KEYSYM1_(9) \
+    _KEYSYM1_(COLON) \
+    _KEYSYM1_(SEMICOLON) \
+    _KEYSYM1_(LESS) \
+    _KEYSYM_(EQUALS,EQUAL) \
+    _KEYSYM1_(GREATER) \
+    _KEYSYM1_(QUESTION) \
+    _KEYSYM1_(AT) \
+    _KEYSYM1_(LEFTBRACKET) \
+    _KEYSYM1_(BACKSLASH) \
+    _KEYSYM1_(RIGHTBRACKET) \
+    _KEYSYM1_(CARET) \
+    _KEYSYM1_(UNDERSCORE) \
+    _KEYSYM1_(BACKQUOTE) \
+    _KEYSYM_(a,A) \
+    _KEYSYM_(b,B) \
+    _KEYSYM_(c,C) \
+    _KEYSYM_(d,D) \
+    _KEYSYM_(e,E) \
+    _KEYSYM_(f,F) \
+    _KEYSYM_(g,G) \
+    _KEYSYM_(h,H) \
+    _KEYSYM_(i,I) \
+    _KEYSYM_(j,J) \
+    _KEYSYM_(k,K) \
+    _KEYSYM_(l,L) \
+    _KEYSYM_(m,M) \
+    _KEYSYM_(n,N) \
+    _KEYSYM_(o,O) \
+    _KEYSYM_(p,P) \
+    _KEYSYM_(q,Q) \
+    _KEYSYM_(r,R) \
+    _KEYSYM_(s,S) \
+    _KEYSYM_(t,T) \
+    _KEYSYM_(u,U) \
+    _KEYSYM_(v,V) \
+    _KEYSYM_(w,W) \
+    _KEYSYM_(x,X) \
+    _KEYSYM_(y,Y) \
+    _KEYSYM_(z,Z) \
+    _KEYSYM1_(DELETE) \
+    _KEYSYM_(KP_PLUS,KEYPAD_PLUS)     \
+    _KEYSYM_(KP_MINUS,KEYPAD_MINUS)    \
+    _KEYSYM_(KP_MULTIPLY,KEYPAD_MULTIPLY) \
+    _KEYSYM_(KP_DIVIDE,KEYPAD_DIVIDE)   \
+    _KEYSYM_(KP_ENTER,KEYPAD_ENTER)    \
+    _KEYSYM_(KP_PERIOD,KEYPAD_PERIOD)   \
+    _KEYSYM_(KP_EQUALS,KEYPAD_EQUALS)   \
+    _KEYSYM_(KP1,KEYPAD_1)         \
+    _KEYSYM_(KP2,KEYPAD_2)         \
+    _KEYSYM_(KP3,KEYPAD_3)         \
+    _KEYSYM_(KP4,KEYPAD_4)         \
+    _KEYSYM_(KP5,KEYPAD_5)         \
+    _KEYSYM_(KP6,KEYPAD_6)         \
+    _KEYSYM_(KP7,KEYPAD_7)         \
+    _KEYSYM_(KP8,KEYPAD_8)         \
+    _KEYSYM_(KP9,KEYPAD_9)         \
+    _KEYSYM_(KP0,KEYPAD_0)         \
+    _KEYSYM1_(UP)  \
+    _KEYSYM1_(DOWN) \
+    _KEYSYM1_(RIGHT) \
+    _KEYSYM1_(LEFT) \
+    _KEYSYM1_(INSERT) \
+    _KEYSYM1_(HOME) \
+    _KEYSYM1_(END) \
+    _KEYSYM1_(PAGEUP) \
+    _KEYSYM1_(PAGEDOWN) \
+    _KEYSYM1_(F1) \
+    _KEYSYM1_(F2) \
+    _KEYSYM1_(F3) \
+    _KEYSYM1_(F4) \
+    _KEYSYM1_(F5) \
+    _KEYSYM1_(F6) \
+    _KEYSYM1_(F7) \
+    _KEYSYM1_(F8) \
+    _KEYSYM1_(F9) \
+    _KEYSYM1_(F10) \
+    _KEYSYM1_(F11) \
+    _KEYSYM1_(F12) \
+    _KEYSYM1_(F13) \
+    _KEYSYM1_(F14) \
+    _KEYSYM1_(F15) \
+    _KEYSYM1_(SCROLLOCK) \
+    _KEYSYM1_(SYSREQ) \
+    _KEYSYM1_(PRINT) \
+    _KEYSYM1_(BREAK) \
+
+#define _KEYSYM_(x,y)   { SDLK_##x, #y },
+static const struct { int  _sym; const char*  _str; }  keysym_names[] =
+{
+    _KEYSYM_LIST
+    { 0, NULL }
+};
+#undef _KEYSYM_
+
+int
+skin_keysym_str_count( void )
+{
+    return sizeof(keysym_names)/sizeof(keysym_names[0])-1;
+}
+
+const char*
+skin_keysym_str( int  index )
+{
+    if (index >= 0 && index < skin_keysym_str_count())
+        return keysym_names[index]._str;
+
+    return NULL;
+}
+
+const char*
+skin_key_symmod_to_str( int  sym, int  mod )
+{
+    static char  temp[32];
+    char*        p = temp;
+    char*        end = p + sizeof(temp);
+    int          nn;
+
+    if ((mod & KMOD_LCTRL) != 0) {
+        p = bufprint(p, end, "Ctrl-");
+    }
+    if ((mod & KMOD_RCTRL) != 0) {
+        p = bufprint(p, end, "RCtrl-");
+    }
+    if ((mod & KMOD_LSHIFT) != 0) {
+        p = bufprint(p, end, "Shift-");
+    }
+    if ((mod & KMOD_RSHIFT) != 0) {
+        p = bufprint(p, end, "RShift-");
+    }
+    if ((mod & KMOD_LALT) != 0) {
+        p = bufprint(p, end, "Alt-");
+    }
+    if ((mod & KMOD_RALT) != 0) {
+        p = bufprint(p, end, "RAlt-");
+    }
+    for (nn = 0; keysym_names[nn]._sym != 0; nn++) {
+        if (keysym_names[nn]._sym == sym) {
+            p = bufprint(p, end, "%s", keysym_names[nn]._str);
+            return temp;;
+        }
+    }
+
+    if (sym >= 32 && sym <= 127) {
+        p = bufprint(p, end, "%c", sym);
+        return temp;
+    }
+
+    return NULL;
+}
+
+
+int
+skin_key_symmod_from_str( const char*  str, int  *psym, int  *pmod )
+{
+    int          mod = 0;
+    int          match = 1;
+    int          nn;
+    const char*  s0 = str;
+    static const struct { const char*  prefix; int  mod; }  mods[] =
+    {
+        { "^",      KMOD_LCTRL },
+        { "Ctrl",   KMOD_LCTRL },
+        { "ctrl",   KMOD_LCTRL },
+        { "RCtrl",  KMOD_RCTRL },
+        { "rctrl",  KMOD_RCTRL },
+        { "Alt",    KMOD_LALT },
+        { "alt",    KMOD_LALT },
+        { "RAlt",   KMOD_RALT },
+        { "ralt",   KMOD_RALT },
+        { "Shift",  KMOD_LSHIFT },
+        { "shift",  KMOD_LSHIFT },
+        { "RShift", KMOD_RSHIFT },
+        { "rshift", KMOD_RSHIFT },
+        { NULL, 0 }
+    };
+
+    while (match) {
+        match = 0;
+        for (nn = 0; mods[nn].prefix != NULL; nn++) {
+            const char*  prefix = mods[nn].prefix;
+            int          len    = strlen(prefix);
+
+            if ( !memcmp(str, prefix, len) ) {
+                str  += len;
+                match = 1;
+                mod  |= mods[nn].mod;
+                if (str[0] == '-' && str[1] != 0)
+                    str++;
+                break;
+            }
+        }
+    }
+
+    for (nn = 0; keysym_names[nn]._sym; nn++) {
+#ifdef _WIN32
+        if ( !stricmp(str, keysym_names[nn]._str) )
+#else
+        if ( !strcasecmp(str, keysym_names[nn]._str) )
+#endif
+        {
+            *psym = keysym_names[nn]._sym;
+            *pmod = mod;
+            return 0;
+        }
+    }
+
+    D("%s: can't find sym value for '%s' (mod=%d, str=%s)", __FUNCTION__, s0, mod, str);
+    return -1;
+}
+
+
+typedef struct {
+    int             sym;
+    int             mod;
+    SkinKeyCommand  command;
+} SkinKeyItem;
+
+
+struct SkinKeyset {
+    int           num_items;
+    int           max_items;
+    SkinKeyItem*  items;
+};
+
+
+static int
+skin_keyset_add( SkinKeyset*  kset, int  sym, int  mod, SkinKeyCommand  command )
+{
+    SkinKeyItem*  item = kset->items;
+    SkinKeyItem*  end  = item + kset->num_items;
+    SkinKeyItem*  first = NULL;
+    int           count = 0;
+
+    D( "adding binding %s to %s", skin_key_command_to_str(command), skin_key_symmod_to_str(sym,mod));
+    for ( ; item < end; item++) {
+        if (item->command == command) {
+            if (!first)
+                first = item;
+            if (++count == SKIN_KEY_COMMAND_MAX_BINDINGS) {
+                /* replace the first (oldest) one in the list */
+                first->sym = sym;
+                first->mod = mod;
+                return 0;
+            }
+            continue;
+        }
+        if (item->sym == sym && item->mod == mod) {
+            /* replace a (sym,mod) binding */
+            item->command = command;
+            return 0;
+        }
+    }
+    if (kset->num_items >= kset->max_items) {
+        int           old_size  = kset->max_items;
+        int           new_size  = old_size + (old_size >> 1) + 4;
+        SkinKeyItem*  new_items = realloc( kset->items, new_size*sizeof(SkinKeyItem) );
+        if (new_items == NULL) {
+            return -1;
+        }
+        kset->items     = new_items;
+        kset->max_items = new_size;
+    }
+    item = kset->items + kset->num_items++;
+    item->command = command;
+    item->sym     = sym;
+    item->mod     = mod;
+    return 1;
+}
+
+
+SkinKeyset*
+skin_keyset_new ( AConfig*  root )
+{
+    SkinKeyset*  kset = calloc(1, sizeof(*kset));
+    AConfig*     node = root->first_child;;
+
+    if (kset == NULL)
+        return NULL;
+
+    for ( ; node != NULL; node = node->next )
+    {
+        SkinKeyCommand  command;
+        int             sym, mod;
+        char*           p;
+
+        command = skin_key_command_from_str( node->name, -1 );
+        if (command == SKIN_KEY_COMMAND_NONE) {
+            D( "ignoring unknown keyset command '%s'", node->name );
+            continue;
+        }
+        p = (char*)node->value;
+        while (*p) {
+            char*  q = strpbrk( p, " \t,:" );
+            if (q == NULL)
+                q = p + strlen(p);
+
+            if (q > p) {
+                int   len = q - p;
+                char  keys[24];
+                if (len+1 >= (int)sizeof(keys)) {
+                    D("key binding too long: '%s'", p);
+                }
+                else {
+                    memcpy( keys, p, len );
+                    keys[len] = 0;
+                    if ( skin_key_symmod_from_str( keys, &sym, &mod ) < 0 ) {
+                        D( "ignoring unknown keys '%s' for command '%s'",
+                                keys, node->name );
+                    } else {
+                        skin_keyset_add( kset, sym, mod, command );
+                    }
+                }
+            } else if (*q)
+                q += 1;
+
+            p = q;
+        }
+    }
+    return  kset;
+}
+
+
+SkinKeyset*
+skin_keyset_new_from_text( const char*  text )
+{
+    AConfig*     root = aconfig_node("","");
+    char*        str = strdup(text);
+    SkinKeyset*  result;
+
+    D("kset new from:\n%s", text);
+    aconfig_load( root, str );
+    result = skin_keyset_new( root );
+    free(str);
+    D("kset done result=%p", result);
+    return result;
+}
+
+
+void
+skin_keyset_free( SkinKeyset*  kset )
+{
+    if (kset) {
+        free(kset->items);
+        kset->items     = NULL;
+        kset->num_items = 0;
+        kset->max_items = 0;
+        free(kset);
+    }
+}
+
+
+extern int
+skin_keyset_get_bindings( SkinKeyset*      kset,
+                          SkinKeyCommand   command,
+                          SkinKeyBinding*  bindings )
+{
+    if (kset) {
+        int     count = 0;
+        SkinKeyItem*  item = kset->items;
+        SkinKeyItem*  end  = item + kset->num_items;
+
+        for ( ; item < end; item++ ) {
+            if (item->command == command) {
+                bindings->sym = item->sym;
+                bindings->mod = item->mod;
+                bindings ++;
+                if ( ++count >= SKIN_KEY_COMMAND_MAX_BINDINGS ) {
+                    /* shouldn't happen, but be safe */
+                    break;
+                }
+            }
+        }
+        return count;
+    }
+    return -1;
+}
+
+
+/* retrieve the command corresponding to a given (sym,mod) pair. returns SKIN_KEY_COMMAND_NONE if not found */
+SkinKeyCommand
+skin_keyset_get_command( SkinKeyset*  kset, int  sym, int mod )
+{
+    if (kset) {
+        SkinKeyItem*  item = kset->items;
+        SkinKeyItem*  end  = item + kset->num_items;
+
+        for ( ; item < end; item++ ) {
+            if (item->sym == sym && item->mod == mod) {
+                return item->command;
+            }
+        }
+    }
+    return SKIN_KEY_COMMAND_NONE;
+}
+
+
+const char*
+skin_keyset_get_default( void )
+{
+    return
+    "BUTTON_CALL         F3\n"
+    "BUTTON_HANGUP       F4\n"
+    "BUTTON_HOME         Home\n"
+    "BUTTON_BACK         Escape\n"
+    "BUTTON_MENU         F2, PageUp\n"
+    "BUTTON_STAR         Shift-F2, PageDown\n"
+    "BUTTON_POWER        F7\n"
+    "BUTTON_SEARCH       F5\n"
+    "BUTTON_CAMERA       Ctrl-Keypad_5, Ctrl-F3\n"
+    "BUTTON_VOLUME_UP    Keypad_Plus, Ctrl-F5\n"
+    "BUTTON_VOLUME_DOWN  Keypad_Minus, Ctrl-F6\n"
+
+    "TOGGLE_NETWORK      F8\n"
+    "TOGGLE_TRACING      F9\n"
+    "TOGGLE_FULLSCREEN   Alt-Enter\n"
+
+    "BUTTON_DPAD_CENTER  Keypad_5\n"
+    "BUTTON_DPAD_UP      Keypad_8\n"
+    "BUTTON_DPAD_LEFT    Keypad_4\n"
+    "BUTTON_DPAD_RIGHT   Keypad_6\n"
+    "BUTTON_DPAD_DOWN    Keypad_2\n"
+
+    "TOGGLE_TRACKBALL    F6\n"
+    "SHOW_TRACKBALL      Delete\n"
+
+    "CHANGE_LAYOUT_PREV  Keypad_7, Ctrl-F11\n"
+    "CHANGE_LAYOUT_NEXT  Keypad_9, Ctrl-F12\n"
+    "ONION_ALPHA_UP      Keypad_Multiply\n"
+    "ONION_ALPHA_DOWN    Keypad_Divide\n"
+    ;
+}
diff --git a/android/skin/keyset.h b/android/skin/keyset.h
new file mode 100644
index 0000000..d68d6a7
--- /dev/null
+++ b/android/skin/keyset.h
@@ -0,0 +1,122 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_KEYSET_H_
+#define _ANDROID_SKIN_KEYSET_H_
+
+#include "android/config.h"
+
+/* A SkinKeySet maps keystrokes to specific commands. we have a few hard-coded
+ * keysets in the emulator binary, and the user can define its own if he wants
+ * to...
+ */
+typedef struct SkinKeyset  SkinKeyset;
+
+#define  SKIN_KEY_COMMAND_LIST                  \
+    _SKIN_KEY_COMMAND(NONE,"no key")            \
+    _SKIN_KEY_COMMAND(BUTTON_HOME,"Home button")              \
+    _SKIN_KEY_COMMAND(BUTTON_MENU,"Menu (Soft-Left) button")   \
+    _SKIN_KEY_COMMAND(BUTTON_STAR,"Star (Soft-Right) button")  \
+    _SKIN_KEY_COMMAND(BUTTON_BACK,"Back button")              \
+    _SKIN_KEY_COMMAND(BUTTON_CALL,"Call/Dial button")              \
+    _SKIN_KEY_COMMAND(BUTTON_HANGUP,"Hangup/EndCall button")            \
+    _SKIN_KEY_COMMAND(BUTTON_POWER,"Power button")             \
+    _SKIN_KEY_COMMAND(BUTTON_SEARCH,"Search button")            \
+    _SKIN_KEY_COMMAND(BUTTON_VOLUME_UP,"Volume up button")         \
+    _SKIN_KEY_COMMAND(BUTTON_VOLUME_DOWN,"Volume down button")       \
+    _SKIN_KEY_COMMAND(BUTTON_CAMERA,"Camera button")            \
+    _SKIN_KEY_COMMAND(CHANGE_LAYOUT_PREV,"switch to previous layout")       \
+    _SKIN_KEY_COMMAND(CHANGE_LAYOUT_NEXT,"switch to next layout")       \
+    _SKIN_KEY_COMMAND(TOGGLE_NETWORK,"toggle cell network on/off")           \
+    _SKIN_KEY_COMMAND(TOGGLE_TRACING,"toggle code profiling")           \
+    _SKIN_KEY_COMMAND(TOGGLE_FULLSCREEN,"toggle fullscreen mode")        \
+    _SKIN_KEY_COMMAND(TOGGLE_TRACKBALL,"toggle trackball mode")         \
+    _SKIN_KEY_COMMAND(SHOW_TRACKBALL,"show trackball") \
+    _SKIN_KEY_COMMAND(BUTTON_DPAD_CENTER,"DPad center")       \
+    _SKIN_KEY_COMMAND(BUTTON_DPAD_LEFT,"DPad left") \
+    _SKIN_KEY_COMMAND(BUTTON_DPAD_RIGHT,"DPad right")        \
+    _SKIN_KEY_COMMAND(BUTTON_DPAD_UP,"DPad up")           \
+    _SKIN_KEY_COMMAND(BUTTON_DPAD_DOWN,"DPad down")         \
+    _SKIN_KEY_COMMAND(ONION_ALPHA_UP,"increase onion alpha")           \
+    _SKIN_KEY_COMMAND(ONION_ALPHA_DOWN,"decrease onion alpha")         \
+
+
+/* the list of commands in the emulator */
+#define _SKIN_KEY_COMMAND(x,y)  SKIN_KEY_COMMAND_##x,
+typedef enum {
+    SKIN_KEY_COMMAND_LIST
+    SKIN_KEY_COMMAND_MAX  // do not remove
+} SkinKeyCommand;
+#undef _SKIN_KEY_COMMAND
+
+/* retrieve the textual name of a given command, this is the command name without
+ * the "SKIN_KEY_COMMAND_" prefix. returns NULL if command is NONE or invalid
+ * the result is a static constant string that must not be freed
+ */
+extern const char*      skin_key_command_to_str  ( SkinKeyCommand  command );
+
+/* convert a string into a SkinKeyCommand. returns SKIN_COMMAND_NONE if the string
+ * is unknown
+ */
+extern SkinKeyCommand   skin_key_command_from_str( const char*  str, int  len );
+
+/* returns a short human-friendly description of the command */
+extern const char*      skin_key_command_description( SkinKeyCommand  cmd );
+
+/* returns the number of keysym string descriptors */
+extern int              skin_keysym_str_count( void );
+
+/* return the n-th keysym string descriptor */
+extern const char*      skin_keysym_str( int  index );
+
+/* convert a (sym,mod) pair into a descriptive string. e.g. "Ctrl-K" or "Alt-A", etc..
+ * result is a static string that is overwritten on each call
+ */
+extern const char*      skin_key_symmod_to_str   ( int  sym, int  mod );
+
+/* convert a key binding description into a (sym,mod) pair. returns 0 on success, -1
+ * if the string cannot be parsed.
+ */
+extern int              skin_key_symmod_from_str ( const char*  str, int  *psym, int  *pmod );
+
+/* create a new keyset from a configuration tree node */
+extern SkinKeyset*      skin_keyset_new ( AConfig*  root );
+extern SkinKeyset*      skin_keyset_new_from_text( const char*  text );
+
+/* destroy a given keyset */
+extern void             skin_keyset_free( SkinKeyset*  kset );
+
+/* maximum number of key bindings per command. one command can be bound to several
+ * key bindings for convenience
+ */
+#define  SKIN_KEY_COMMAND_MAX_BINDINGS  3
+
+/* a structure that describe a key binding */
+typedef struct {
+    int  sym;   // really a SDL key symbol
+    int  mod;   // really a SDL key modifier
+} SkinKeyBinding;
+
+/* return the number of keyboard bindings for a given command. results are placed in the 'bindings' array
+ * which must have at least SKIN_KEY_MAX_BINDINGS items */
+extern int              skin_keyset_get_bindings( SkinKeyset*      kset,
+                                                  SkinKeyCommand   command,
+                                                  SkinKeyBinding*  bindings );
+
+/* return the command for a given keypress - SKIN_KEY_COMMAND_NONE is returned if unknown */
+extern SkinKeyCommand   skin_keyset_get_command( SkinKeyset*  kset, int  sym, int  mod );
+
+extern const char*      skin_keyset_get_default( void );
+
+/* in android_main.c */
+extern SkinKeyset*      android_keyset;
+
+#endif /* _ANDROID_SKIN_KEYSET_H_ */
diff --git a/android/skin/rect.c b/android/skin/rect.c
new file mode 100644
index 0000000..93e8bc1
--- /dev/null
+++ b/android/skin/rect.c
@@ -0,0 +1,241 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/rect.h"
+#include <limits.h>
+
+#define  SKIN_POS_INITIALIZER   { 0, 0 }
+
+void
+skin_pos_rotate( SkinPos*  dst, SkinPos*  src, SkinRotation  rotation )
+{
+    int  x = src->x;
+    int  y = src->y;
+
+    switch ( rotation & 3 ) {
+    case SKIN_ROTATION_0:
+        dst->x = x;
+        dst->y = y;
+        break;
+
+    case SKIN_ROTATION_90:
+        dst->x = -y;
+        dst->y =  x;
+        break;
+
+    case SKIN_ROTATION_180:
+        dst->x = -x;
+        dst->y = -y;
+        break;
+
+    default:
+        dst->x =  y;
+        dst->y = -x;
+    }
+}
+
+
+#define  SKIN_SIZE_INITIALIZER  { 0, 0 }
+
+int
+skin_size_contains( SkinSize*  size, int  x, int  y )
+{
+    return ( (unsigned) x < (unsigned) size->w &&
+             (unsigned) y < (unsigned) size->h );
+}
+
+void
+skin_size_rotate( SkinSize*  dst, SkinSize*  src, SkinRotation  rot )
+{
+    int  w = src->w;
+    int  h = src->h;
+
+    if ((rot & 1) != 0) {
+        dst->w = h;
+        dst->h = w;
+    } else {
+        dst->w = w;
+        dst->h = h;
+    }
+}
+
+/** SKIN RECTANGLES
+ **/
+#define  SKIN_RECT_INITIALIZER  { SKIN_POS_INITIALIZER, SKIN_SIZE_INITIALIZER }
+
+void
+skin_rect_init( SkinRect*  r, int x, int  y, int  w, int  h )
+{
+    if (w < 0 || h < 0)
+        x = y = w = h = 0;
+
+    r->pos.x  = x;
+    r->pos.y  = y;
+    r->size.w = w;
+    r->size.h = h;
+}
+
+
+void
+skin_rect_translate( SkinRect*  r, int  dx, int  dy )
+{
+    r->pos.x += dx;
+    r->pos.y += dy;
+}
+
+
+void
+skin_rect_rotate( SkinRect*  dst, SkinRect*  src, SkinRotation  rot )
+{
+    int  x, y, w, h;
+
+    switch (rot & 3) {
+        case SKIN_ROTATION_90:
+            x = src->pos.x;
+            y = src->pos.y;
+            w = src->size.w;
+            h = src->size.h;
+            dst->pos.x  = -(y + h);
+            dst->pos.y  = x;
+            dst->size.w = h;
+            dst->size.h = w;
+            break;
+
+        case SKIN_ROTATION_180:
+            dst->pos.x = -(src->pos.x + src->size.w);
+            dst->pos.y = -(src->pos.y + src->size.h);
+            dst->size  = src->size;
+            break;
+
+        case SKIN_ROTATION_270:
+            x = src->pos.x;
+            y = src->pos.y;
+            w = src->size.w;
+            h = src->size.h;
+            dst->pos.x  = y;
+            dst->pos.y  = -(x + w);
+            dst->size.w = h;
+            dst->size.h = w;
+            break;
+
+        default:
+            dst[0] = src[0];
+    }
+}
+
+
+int
+skin_rect_contains( SkinRect*  r, int  x, int  y )
+{
+    return ( (unsigned)(x - r->pos.x) < (unsigned)r->size.w &&
+             (unsigned)(y - r->pos.y) < (unsigned)r->size.h );
+}
+
+SkinOverlap
+skin_rect_contains_rect( SkinRect  *r1, SkinRect  *r2 )
+{
+    SkinBox  a, b;
+
+    skin_box_from_rect( &a, r1 );
+    skin_box_from_rect( &b, r2 );
+
+    if (a.x2 <= b.x1 || b.x2 <= a.x1 || a.y2 <= b.y1 || b.y2 <= a.y1) {
+        return SKIN_OUTSIDE;
+    }
+
+    if (b.x1 >= a.x1 && b.x2 <= a.x2 && b.y1 >= a.y1 && b.y2 <= a.y2) {
+        return SKIN_INSIDE;
+    }
+
+    return SKIN_OVERLAP;
+}
+
+
+int
+skin_rect_intersect( SkinRect*  result, SkinRect*  r1, SkinRect*  r2 )
+{
+    SkinBox  a, b, r;
+
+    skin_box_from_rect( &a, r1 );
+    skin_box_from_rect( &b, r2 );
+
+    if (a.x2 <= b.x1 || b.x2 <= a.x1 || a.y2 <= b.y1 || b.y2 <= a.y1) {
+        result->pos.x = result->pos.y = result->size.w = result->size.h = 0;
+        return 0;
+    }
+
+    r.x1 = (a.x1 > b.x1) ? a.x1 : b.x1;
+    r.x2 = (a.x2 < b.x2) ? a.x2 : b.x2;
+    r.y1 = (a.y1 > b.y1) ? a.y1 : b.y1;
+    r.y2 = (a.y2 < b.y2) ? a.y2 : b.y2;
+
+    skin_box_to_rect( &r, result );
+    return 1;
+}
+
+int
+skin_rect_equals( SkinRect*  r1, SkinRect*  r2 )
+{
+    return (r1->pos.x  == r2->pos.x  && r1->pos.y  == r2->pos.y &&
+            r1->size.w == r2->size.w && r2->size.h == r2->size.h);
+}
+
+/** SKIN BOXES
+ **/
+void
+skin_box_minmax_init( SkinBox*  box )
+{
+    box->x1 = box->y1 = INT_MAX;
+    box->x2 = box->y2 = INT_MIN;
+}
+
+void
+skin_box_minmax_update( SkinBox*  a, SkinRect*  r )
+{
+    SkinBox  b[1];
+
+    skin_box_from_rect(b, r);
+
+    if (b->x1 < a->x1) a->x1 = b->x1;
+    if (b->y1 < a->y1) a->y1 = b->y1;
+    if (b->x2 > a->x2) a->x2 = b->x2;
+    if (b->y2 > a->y2) a->y2 = b->y2;
+}
+
+int
+skin_box_minmax_to_rect( SkinBox*  box, SkinRect*  r )
+{
+    if (box->x1 > box->x2) {
+        r->pos.x = r->pos.y = r->size.w = r->size.h = 0;
+        return 0;
+    }
+    skin_box_to_rect( box, r );
+    return 1;
+}
+
+void
+skin_box_from_rect( SkinBox*  box, SkinRect*  r )
+{
+    box->x1 = r->pos.x;
+    box->y1 = r->pos.y;
+    box->x2 = r->size.w + box->x1;
+    box->y2 = r->size.h + box->y1;
+}
+
+void
+skin_box_to_rect( SkinBox*  box, SkinRect*  r )
+{
+    r->pos.x  = box->x1;
+    r->pos.y  = box->y1;
+    r->size.w = box->x2 - box->x1;
+    r->size.h = box->y2 - box->y1;
+}
+
diff --git a/android/skin/rect.h b/android/skin/rect.h
new file mode 100644
index 0000000..757f888
--- /dev/null
+++ b/android/skin/rect.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_RECT_H
+#define _ANDROID_SKIN_RECT_H
+
+/**  Rectangles
+ **/
+
+typedef enum {
+    SKIN_ROTATION_0,
+    SKIN_ROTATION_90,
+    SKIN_ROTATION_180,
+    SKIN_ROTATION_270
+} SkinRotation;
+
+typedef struct {
+    int  x, y;
+} SkinPos;
+
+extern void  skin_pos_rotate( SkinPos*  dst, SkinPos*  src, SkinRotation  rot );
+
+typedef struct {
+    int  w, h;
+} SkinSize;
+
+extern void skin_size_rotate( SkinSize*  dst, SkinSize*  src, SkinRotation  rot );
+extern int  skin_size_contains( SkinSize*  size, int  x, int  y );
+
+
+typedef struct {
+    SkinPos   pos;
+    SkinSize  size;
+} SkinRect;
+
+extern void  skin_rect_init     ( SkinRect*  r, int x, int y, int  w, int  h );
+extern void  skin_rect_translate( SkinRect*  r, int  dx, int dy );
+extern void  skin_rect_rotate   ( SkinRect*  dst, SkinRect*  src, SkinRotation  rotation );
+extern int   skin_rect_contains ( SkinRect*  r, int  x, int  y );
+extern int   skin_rect_intersect( SkinRect*  result, SkinRect*  r1, SkinRect*  r2 );
+extern int   skin_rect_equals   ( SkinRect*  r1, SkinRect*  r2 );
+
+typedef enum {
+    SKIN_OUTSIDE = 0,
+    SKIN_INSIDE  = 1,
+    SKIN_OVERLAP = 2
+} SkinOverlap;
+
+extern SkinOverlap  skin_rect_contains_rect( SkinRect  *r1, SkinRect  *r2 );
+
+typedef struct {
+    int  x1, y1;
+    int  x2, y2;
+} SkinBox;
+
+extern void  skin_box_init( SkinBox*  box, int x1, int  y1, int  x2, int  y2 );
+extern void  skin_box_minmax_init( SkinBox*  box );
+extern void  skin_box_minmax_update( SkinBox*  box, SkinRect*  rect );
+extern int   skin_box_minmax_to_rect( SkinBox*  box, SkinRect*  rect );
+extern void  skin_box_from_rect( SkinBox*  box, SkinRect*  rect );
+extern void  skin_box_to_rect( SkinBox*  box, SkinRect*  rect );
+
+#endif /* _ANDROID_SKIN_RECT_H */
diff --git a/android/skin/region.c b/android/skin/region.c
new file mode 100644
index 0000000..a5f26a5
--- /dev/null
+++ b/android/skin/region.c
@@ -0,0 +1,1448 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/region.h"
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>  /* malloc/free */
+
+/*************************************************************************
+ *************************************************************************
+ ****
+ ****  ASSERTION SUPPORT
+ ****
+ ****
+ ****/
+
+#ifdef UNIT_TEST
+#include <stdlib.h>
+#include <stdio.h>
+static void
+_rpanic(void)
+{
+    *((char*)(void*)0) = 1;  /* should SEGFAULT */
+    /* put a breakpoint here */
+    exit(1);
+}
+
+#define  RASSERT(cond)   \
+  ({ if (!(cond)) { fprintf(stderr, "%s:%d:%s: assertion failed: %s", \
+      __FILE__, __LINE__,  __FUNCTION__, #cond ); _rpanic(); } })
+
+#else
+#define  RASSERT(cond)  ((void)0)
+#endif
+
+
+/*************************************************************************
+ *************************************************************************
+ ****
+ ****  IMPLEMENTATION DETAILS
+ ****
+ ****
+ ****/
+
+/* this implementation of regions encodes the the region's spans with the
+   following format:
+
+   region   ::= yband+ YSENTINEL
+   yband    ::= YTOP YBOTTOM scanline
+   scanline ::= span+  XSENTINEL
+   span     ::= XLEFT XRIGHT
+
+   XSENTINEL ::= 0x7fff
+   YSENTINEL :=  0x7fff
+
+   all values are sorted in increasing order, which means that:
+
+    - YTOP1 < YBOTTOM1 <= YTOP2 < YBOTTOM2 <= .... < YSENTINEL
+    - XLEFT1 < XRIGHT1 < XLEFT2 < XRIGHT2 < .... < XSENTINEL
+      (in a given scanline)
+*/
+
+/* convenience shortbuts */
+typedef SkinRegionRun   Run;
+typedef SkinRegion      Region;
+
+#define  RUNS_RECT_COUNT  6   /* YTOP YBOT XLEFT XRIGHT XSENTINEL YSENTINEL */
+
+#define  XSENTINEL  SKIN_REGION_SENTINEL
+#define  YSENTINEL  SKIN_REGION_SENTINEL
+
+#define  RUNS_EMPTY   ((Run*)(void*)(-1))
+#define  RUNS_RECT    ((Run*)(void*)(0))
+
+static __inline__ int
+region_isEmpty( Region*  r )
+{
+    return r->runs == RUNS_EMPTY;
+}
+
+static __inline__ int
+region_isRect( Region*  r )
+{
+    return r->runs == RUNS_RECT;
+}
+
+static __inline__ int
+region_isComplex( Region*  r )
+{
+    return r->runs != RUNS_EMPTY && r->runs != RUNS_RECT;
+}
+
+/**  RunStore: ref-counted storage for runs
+ **/
+
+typedef struct {
+    int  refcount;
+    int  count;
+} RunStore;
+
+static void
+runstore_free( RunStore*  s )
+{
+    free(s);
+}
+
+static RunStore*
+runstore_alloc( int   count )
+{
+    RunStore*  s = malloc( sizeof(*s) + sizeof(Run)*count );
+    RASSERT(s != NULL);
+    s->count    = count;
+    s->refcount = 1;
+    return s;
+}
+
+static RunStore*
+runstore_edit( RunStore*  s )
+{
+    RunStore*  s2;
+
+    if (s->refcount == 1)
+        return s;
+
+    s2 = runstore_alloc( s->count );
+    if (s2) {
+        memcpy( s2, s, sizeof(*s) + s->count*sizeof(Run) );
+        s->refcount -= 1;
+        s2->refcount = 1;
+    }
+    return s2;
+}
+
+static void
+runstore_unrefp( RunStore*  *ps )
+{
+    RunStore*  s = *ps;
+    if (s != NULL) {
+        if (s->refcount <= 0)
+            runstore_free(s);
+        *ps = NULL;
+    }
+}
+
+static RunStore*
+runstore_ref( RunStore*  s )
+{
+    if (s) s->refcount += 1;
+    return s;
+}
+
+static __inline__ RunStore*
+runstore_from_runs( Run*  runs )
+{
+    RASSERT(runs != RUNS_EMPTY);
+    RASSERT(runs != RUNS_RECT);
+    return (RunStore*)runs - 1;
+}
+
+static __inline__ Run*
+runstore_to_runs( RunStore*  s )
+{
+    RASSERT(s != NULL);
+    return (Run*)(s + 1);
+}
+
+static Run*
+region_edit( Region*  r )
+{
+    RunStore*  s;
+
+    RASSERT(region_isComplex(r));
+
+    s = runstore_from_runs(r->runs);
+    s = runstore_edit(s);
+    r->runs = runstore_to_runs(s);
+    return r->runs;
+}
+
+/** Run parsing
+ **/
+
+static Run*
+runs_next_scanline( Run*  runs )
+{
+    RASSERT(runs[0] != YSENTINEL && runs[1] != YSENTINEL );
+    runs += 2;
+    do { runs += 1; } while (runs[-1] != XSENTINEL);
+    return runs;
+}
+
+static Run*
+runs_find_y( Run*  runs, int  y )
+{
+    do {
+        int  ybot, ytop = runs[0];
+
+        if (y < ytop)
+            return NULL;
+
+        ybot = runs[1];
+        if (y < ybot)
+            return runs;
+
+        runs = runs_next_scanline( runs );
+    } while (runs[0] != YSENTINEL);
+
+    return NULL;
+}
+
+static void
+runs_set_rect( Run*  runs, SkinRect*  rect )
+{
+    runs[0] = rect->pos.y;
+    runs[1] = rect->pos.y + rect->size.h;
+    runs[2] = rect->pos.x;
+    runs[3] = rect->pos.x + rect->size.w;
+    runs[4] = XSENTINEL;
+    runs[5] = YSENTINEL;
+}
+
+static Run*
+runs_copy_scanline( Run*  dst, Run*  src )
+{
+    RASSERT(src[0] != YSENTINEL);
+    RASSERT(src[1] != YSENTINEL);
+    dst[0] = src[0];
+    dst[1] = src[1];
+    src += 2;
+    dst += 2;
+    do { *dst++ = *src++; } while (src[-1] != XSENTINEL);
+    return dst;
+}
+
+static Run*
+runs_copy_scanline_adj( Run*  dst, Run*  src, int  ytop, int  ybot )
+{
+    Run*  runs2 = runs_copy_scanline( dst, src );
+    dst[0] = (Run) ytop;
+    dst[1] = (Run) ybot;
+    return runs2;
+}
+
+static __inline__ Run*
+runs_add_span( Run*  dst, int  left, int  right )
+{
+    dst[0] = (Run) left;
+    dst[1] = (Run) right;
+    return dst + 2;
+}
+
+static __inline__ int
+runs_get_count( Run*  runs )
+{
+    RunStore*  s = runstore_from_runs(runs);
+    return s->count;
+}
+
+
+static void
+runs_coalesce_band( Run*  *psrc_spans, Run*  *pdst_spans, SkinBox*  minmax )
+{
+    Run*  sspan  = *psrc_spans;
+    Run*  dspan  = *pdst_spans;
+    int   pleft  = sspan[0];
+    int   pright = sspan[1];
+    int   xleft, xright;
+
+    RASSERT(pleft != XSENTINEL);
+    RASSERT(pright != XSENTINEL);
+    RASSERT(pleft < pright);
+
+    if (pleft < minmax->x1) minmax->x1 = pleft;
+
+    sspan += 2;
+    xleft  = sspan[0];
+
+    while (xleft != XSENTINEL)
+    {
+        xright = sspan[1];
+        RASSERT(xright != XSENTINEL);
+        RASSERT(xleft < xright);
+
+        if (xleft == pright) {
+            pright = xright;
+        } else {
+            dspan[0] = (Run) pleft;
+            dspan[1] = (Run) pright;
+            dspan   += 2;
+        }
+        sspan += 2;
+        xleft = sspan[0];
+    }
+    dspan[0] = (Run) pleft;
+    dspan[1] = (Run) pright;
+    dspan[2] = XSENTINEL;
+    dspan   += 3;
+    sspan   += 1;  /* skip XSENTINEL */
+
+    if (pright > minmax->x2) minmax->x2 = pright;
+
+    *psrc_spans = sspan;
+    *pdst_spans = dspan;
+}
+
+
+static int
+runs_coalesce( Run*  dst, Run*  src, SkinBox*  minmax )
+{
+    Run*  prev = NULL;
+    Run*  dst0 = dst;
+    int   ytop = src[0];
+    int   ybot;
+
+    while (ytop != YSENTINEL)
+    {
+        Run*  sspan = src + 2;
+        Run*  dspan = dst + 2;
+
+        ybot = src[1];
+        RASSERT( ytop < ybot );
+        RASSERT( ybot != YSENTINEL );
+        RASSERT( src[2] != XSENTINEL );
+
+        if (ytop < minmax->y1) minmax->y1 = ytop;
+        if (ybot > minmax->y2) minmax->y2 = ybot;
+
+        dst[0] = (Run) ytop;
+        dst[1] = (Run) ybot;
+
+        runs_coalesce_band( &sspan, &dspan, minmax );
+
+        if (prev && prev[1] == dst[0] && (dst-prev) == (dspan-dst) &&
+            !memcmp(prev+2, dst+2, (dspan-dst-2)*sizeof(Run)))
+        {
+            /* coalesce two identical bands */
+            prev[1] = dst[1];
+        }
+        else
+        {
+            prev = dst;
+            dst  = dspan;
+        }
+        src  = sspan;
+        ytop = src[0];
+    }
+    dst[0] = YSENTINEL;
+    return (dst + 1 - dst0);
+}
+
+/*************************************************************************
+ *************************************************************************
+ ****
+ ****  PUBLIC API
+ ****
+ ****/
+
+void
+skin_region_init_empty( SkinRegion*  r )
+{
+    /* empty region */
+    r->bounds.pos.x  = r->bounds.pos.y = 0;
+    r->bounds.size.w = r->bounds.size.h = 0;
+    r->runs = RUNS_EMPTY;
+}
+
+void
+skin_region_init( SkinRegion*  r, int  x1, int  y1, int  x2, int  y2 )
+{
+    if (x1 >= x2 || y1 >= y2) {
+        skin_region_init_empty(r);
+        return;
+    }
+    r->bounds.pos.x  = x1;
+    r->bounds.pos.y  = y1;
+    r->bounds.size.w = x2 - x1;
+    r->bounds.size.h = y2 - y1;
+    r->runs = RUNS_RECT;
+}
+
+void
+skin_region_init_rect( SkinRegion*  r, SkinRect*  rect )
+{
+    if (rect == NULL || rect->size.w <= 0 || rect->size.h <= 0) {
+        skin_region_init_empty(r);
+        return;
+    }
+    r->bounds = rect[0];
+    r->runs   = RUNS_RECT;
+}
+
+void
+skin_region_init_box( SkinRegion*  r, SkinBox*  box )
+{
+    if (box == NULL || box->x1 >= box->x2 || box->y1 >= box->y2) {
+        skin_region_init_empty(r);
+        return;
+    }
+    r->bounds.pos.x  = box->x1;
+    r->bounds.pos.y  = box->y1;
+    r->bounds.size.w = box->x2 - box->x1;
+    r->bounds.size.h = box->y2 - box->y1;
+    r->runs = RUNS_RECT;
+}
+
+void
+skin_region_init_copy( SkinRegion*  r, SkinRegion*  src )
+{
+    if (src == NULL || region_isEmpty(src))
+        skin_region_init_empty(r);
+    else {
+        r[0] = src[0];
+        if (region_isComplex(src)) {
+            RunStore*  s = runstore_from_runs(r->runs);
+            runstore_ref(s);
+        }
+    }
+}
+
+
+void
+skin_region_reset( SkinRegion*  r )
+{
+    if (r != NULL) {
+        if (region_isComplex(r)) {
+            RunStore*  s = runstore_from_runs(r->runs);
+            runstore_unrefp( &s );
+        }
+        skin_region_init_empty(r);
+    }
+}
+
+
+
+void
+skin_region_copy( SkinRegion*  r, SkinRegion*  src )
+{
+    skin_region_reset(r);
+    skin_region_init_copy(r, src);
+}
+
+
+int
+skin_region_equals( SkinRegion*  r1, SkinRegion*  r2 )
+{
+    Run       *runs1, *runs2;
+    RunStore  *store1, *store2;
+
+    if (r1 == r2)
+        return 1;
+
+    if (!skin_rect_equals( &r1->bounds, &r2->bounds ))
+        return 0;
+
+    runs1 = r1->runs;
+    runs2 = r2->runs;
+
+    if (runs1 == runs2)  /* empties and rects */
+        return 1;
+
+    if ( !region_isComplex(r1) || !region_isComplex(r2) )
+        return 0;
+
+    store1 = runstore_from_runs(runs1);
+    store2 = runstore_from_runs(runs2);
+
+    if (store1->count == store2->count &&
+        !memcmp( (char*)runs1, (char*)runs2, store1->count*sizeof(Run) ) )
+        return 1;
+
+    return 0;
+}
+
+void
+skin_region_translate( SkinRegion*  r, int  dx, int  dy )
+{
+    Run*  runs;
+
+    if (region_isEmpty(r))
+        return;
+
+    skin_rect_translate( &r->bounds, dx, dy );
+    if (region_isRect(r))
+        return;
+
+    runs = region_edit(r);
+    while (runs[0] != YSENTINEL) {
+        int  ytop = runs[0];
+        int  ybot = runs[1];
+
+        RASSERT(ybot != YSENTINEL);
+        runs[0] = (Run)(ytop + dy);
+        runs[1] = (Run)(ybot + dy);
+        runs += 2;
+        while (runs[0] != XSENTINEL) {
+            int  xleft  = runs[0];
+            int  xright = runs[1];
+            RASSERT(xright != YSENTINEL);
+            runs[0] = (Run)(xleft + dx);
+            runs[1] = (Run)(xright + dx);
+            runs += 2;
+        }
+        runs += 1;
+    }
+}
+
+void
+skin_region_get_bounds( SkinRegion*  r, SkinRect*  bounds )
+{
+    if (r != NULL) {
+        bounds[0] = r->bounds;
+    } else {
+        bounds->pos.x  = bounds->pos.y  = 0;
+        bounds->size.w = bounds->size.h = 0;
+    }
+}
+
+int
+skin_region_is_empty( SkinRegion*  r )
+{
+    return region_isEmpty(r);
+}
+
+int
+skin_region_is_rect( SkinRegion*  r )
+{
+    return region_isRect(r);
+}
+
+int
+skin_region_is_complex( SkinRegion*  r )
+{
+    return region_isComplex(r);
+}
+
+void
+skin_region_swap( SkinRegion*  r, SkinRegion*  r2 )
+{
+    SkinRegion  tmp;
+    tmp   = r[0];
+    r[0]   = r2[0];
+    r2[0]  = tmp;
+}
+
+
+SkinOverlap
+skin_region_contains( SkinRegion*  r, int  x, int  y )
+{
+    if (region_isEmpty(r))
+        return SKIN_OUTSIDE;
+    if (region_isRect(r)) {
+        return skin_rect_contains(&r->bounds,x,y);
+    } else {
+        Run*  runs = runs_find_y( r->runs, y );
+        if (runs != NULL) {
+            runs += 2;
+            do {
+                int  xright, xleft = runs[0];
+
+                if (x < xleft)  // also x < xleft == XSENTINEL
+                    break;
+                xright = runs[1];
+                if (xright == XSENTINEL)
+                    break;
+                if (x < xright)
+                    return SKIN_INSIDE;
+                runs += 2;
+            } while (runs[0] != XSENTINEL);
+        }
+        return SKIN_OUTSIDE;
+    }
+}
+
+
+SkinOverlap
+skin_region_contains_rect( SkinRegion*  r, SkinRect*  rect )
+{
+    SkinRegion  r2[1];
+    skin_region_init_rect( r2, rect );
+    return skin_region_test_intersect( r, r2 );
+}
+
+
+SkinOverlap
+skin_region_contains_box( SkinRegion*  r, SkinBox*  b )
+{
+    SkinRegion  r2[1];
+
+    skin_region_init_box( r2, b );
+    return skin_region_test_intersect( r, r2 );
+}
+
+
+
+#define FLAG_REGION_1     (1 << 0)
+#define FLAG_REGION_2     (1 << 1)
+#define FLAG_REGION_BOTH  (1 << 2)
+
+SkinOverlap
+skin_region_test_intersect( SkinRegion*  r1,
+                            SkinRegion*  r2 )
+{
+    Run  *runs1, *runs2;
+    Run   run2_tmp[ RUNS_RECT_COUNT ];
+    SkinRect  r;
+
+    if (region_isEmpty(r1) || region_isEmpty(r2))
+        return SKIN_OUTSIDE;
+
+    if ( !skin_rect_intersect( &r, &r1->bounds, &r2->bounds) )
+        return SKIN_OUTSIDE;
+
+    if (region_isRect(r1)) {
+        if (region_isRect(r2)) {
+            return skin_rect_contains_rect(&r1->bounds, &r2->bounds);
+        } else {
+            SkinRegion*  tmp = r1;
+            r1 = r2;
+            r2 = tmp;
+        }
+    }
+    /* here r1 is guaranteed to be complex, r2 is either rect of complex */
+    runs1 = r1->runs;
+    if (region_isRect(r2)) {
+        runs2 = run2_tmp;
+        runs_set_rect(runs2, &r2->bounds);
+    }
+    else {
+        runs2 = r2->runs;
+    }
+
+    {
+        int   flags = 0;
+
+        while (runs1[0] != YSENTINEL && runs2[0] != YSENTINEL)
+        {
+            int  ytop1 = runs1[0];
+            int  ybot1 = runs1[1];
+            int  ytop2 = runs2[0];
+            int  ybot2 = runs2[1];
+
+            if (ybot1 <= ytop2)
+            {
+                /* band1 over band2 */
+                flags |= FLAG_REGION_1;
+                runs1 = runs_next_scanline( runs1 );
+            }
+            else if (ybot2 <= ytop1)
+            {
+                /* band2 over band1 */
+                flags |= FLAG_REGION_2;
+                runs2  = runs_next_scanline( runs2 );
+            }
+            else  /* band1 and band2 overlap */
+            {
+                Run*  span1;
+                Run*  span2;
+                int   ybot;
+
+                if (ytop1 < ytop2) {
+                    flags |= FLAG_REGION_1;
+                    ytop1 = ytop2;
+                } else if (ytop2 < ytop1) {
+                    flags |= FLAG_REGION_2;
+                    ytop2 = ytop1;
+                }
+
+                ybot = (ybot1 < ybot2) ? ybot1 : ybot2;
+
+                span1 = runs1 + 2;
+                span2 = runs2 + 2;
+
+                while (span1[0] != XSENTINEL && span2[0] != XSENTINEL)
+                {
+                    int  xleft1  = span1[0];
+                    int  xright1 = span1[1];
+                    int  xleft2  = span2[0];
+                    int  xright2 = span2[1];
+
+                    RASSERT(xright1 != XSENTINEL);
+                    RASSERT(xright2 != XSENTINEL);
+
+                    if (xright1 <= xleft2) {
+                        flags |= FLAG_REGION_1;
+                        span1 += 2;
+                    }
+                    else if (xright2 <= xleft1) {
+                        flags |= FLAG_REGION_2;
+                        span2 += 2;
+                    }
+                    else {
+                        int  xright;
+
+                        if (xleft1 < xleft2) {
+                            flags |= FLAG_REGION_1;
+                            xleft1 = xleft2;
+                        } else if (xleft2 < xleft1) {
+                            flags |= FLAG_REGION_2;
+                            xleft2 = xleft1;
+                        }
+
+                        xright = (xright1 < xright2) ? xright1 : xright2;
+
+                        flags |= FLAG_REGION_BOTH;
+
+                        if (xright == xright1)
+                            span1 += 2;
+                        if (xright == xright2)
+                            span2 += 2;
+                    }
+                }
+
+                if (span1[0] != XSENTINEL) {
+                    flags |= FLAG_REGION_1;
+                }
+
+                if (span2[0] != XSENTINEL) {
+                    flags |= FLAG_REGION_2;
+                }
+
+                if (ybot == ybot1)
+                    runs1 = runs_next_scanline( runs1 );
+
+                if (ybot == ybot2)
+                    runs2 = runs_next_scanline( runs2 );
+            }
+        }
+
+        if (runs1[0] != YSENTINEL) {
+            flags |= FLAG_REGION_1;
+        }
+
+        if (runs2[0] != YSENTINEL) {
+            flags |= FLAG_REGION_2;
+        }
+
+        if ( !(flags & FLAG_REGION_BOTH) ) {
+            /* no intersection at all */
+            return SKIN_OUTSIDE;
+        }
+
+        if ( (flags & FLAG_REGION_2) != 0 ) {
+            /* intersection + overlap */
+            return SKIN_OVERLAP;
+        }
+
+        return SKIN_INSIDE;
+    }
+}
+
+typedef struct {
+    Run*       runs1;
+    Run*       runs2;
+    Run*       runs_base;
+    Run*       runs;
+    RunStore*  store;
+    Region     result[1];
+    Run        runs1_rect[ RUNS_RECT_COUNT ];
+    Run        runs2_rect[ RUNS_RECT_COUNT ];
+} RegionOperator;
+
+
+static void
+region_operator_init( RegionOperator*  o,
+                      Region*          r1,
+                      Region*          r2 )
+{
+    int  run1_count, run2_count;
+    int  maxruns;
+
+    RASSERT( !region_isEmpty(r1) );
+    RASSERT( !region_isEmpty(r2) );
+
+    if (region_isRect(r1)) {
+        run1_count = RUNS_RECT_COUNT;
+        o->runs1   = o->runs1_rect;
+        runs_set_rect( o->runs1, &r1->bounds );
+    } else {
+        o->runs1   = r1->runs;
+        run1_count = runs_get_count(r1->runs);
+    }
+
+    if (region_isRect(r2)) {
+        run2_count = RUNS_RECT_COUNT;
+        o->runs2   = o->runs2_rect;
+        runs_set_rect( o->runs2, &r2->bounds );
+    } else {
+        o->runs2   = r2->runs;
+        run2_count = runs_get_count(r2->runs);
+    }
+
+    maxruns  = run1_count < run2_count ? run2_count : run1_count;
+    o->store = runstore_alloc( 3*maxruns );
+    o->runs_base = runstore_to_runs(o->store);
+}
+
+
+static void
+region_operator_do( RegionOperator*  o, int  wanted )
+{
+    Run*  runs1 = o->runs1;
+    Run*  runs2 = o->runs2;
+    Run*  runs  = o->runs_base;
+    int   ytop1 = runs1[0];
+    int   ytop2 = runs2[0];
+
+    if (ytop1 != YSENTINEL && ytop2 != YSENTINEL)
+    {
+        int  ybot1, ybot2;
+
+        while (ytop1 != YSENTINEL && ytop2 != YSENTINEL)
+        {
+            int  ybot;
+
+            ybot1 = runs1[1];
+            ybot2 = runs2[1];
+
+            RASSERT(ybot1 != YSENTINEL);
+            RASSERT(ybot2 != YSENTINEL);
+
+            if (ybot1 <= ytop2) {
+                if (wanted & FLAG_REGION_1)
+                    runs = runs_copy_scanline_adj( runs, runs1, ytop1, ybot1 );
+                runs1   = runs_next_scanline( runs1 );
+                ytop1   = runs1[0];
+                continue;
+            }
+
+            if (ybot2 <= ytop1) {
+                if (wanted & FLAG_REGION_2)
+                    runs = runs_copy_scanline_adj( runs, runs2, ytop2, ybot2 );
+                runs2 = runs_next_scanline(runs2);
+                ytop2 = runs2[0];
+                continue;
+            }
+
+            if (ytop1 < ytop2) {
+                if (wanted & FLAG_REGION_1)
+                    runs = runs_copy_scanline_adj( runs, runs1, ytop1, ytop2 );
+                ytop1 = ytop2;
+            }
+            else if (ytop2 < ytop1) {
+                if (wanted & FLAG_REGION_2)
+                    runs = runs_copy_scanline_adj( runs, runs2, ytop2, ytop1 );
+                ytop2 = ytop1;
+            }
+
+            ybot  = (ybot1 <= ybot2) ? ybot1 : ybot2;
+
+            runs[0] = (Run) ytop1;
+            runs[1] = (Run) ybot;
+
+            /* do the common band */
+            {
+                Run*  span1 = runs1 + 2;
+                Run*  span2 = runs2 + 2;
+                Run*  span  = runs + 2;
+                int   xleft1 = span1[0];
+                int   xleft2 = span2[0];
+                int   xright1, xright2;
+
+                while (xleft1 != XSENTINEL && xleft2 != XSENTINEL)
+                {
+                    int  xright;
+
+                    xright1 = span1[1];
+                    xright2 = span2[1];
+
+                    RASSERT(xright1 != XSENTINEL);
+                    RASSERT(xright2 != XSENTINEL);
+
+                    if (xright1 <= xleft2) {
+                        if (wanted & FLAG_REGION_1)
+                            span = runs_add_span( span, xleft1, xright1 );
+                        span1 += 2;
+                        xleft1 = span1[0];
+                        continue;
+                    }
+
+                    if (xright2 <= xleft1) {
+                        if (wanted & FLAG_REGION_2)
+                            span = runs_add_span( span, xleft2, xright2 );
+                        span2 += 2;
+                        xleft2 = span2[0];
+                        continue;
+                    }
+
+                    if (xleft1 < xleft2) {
+                        if (wanted & FLAG_REGION_1)
+                            span = runs_add_span( span, xleft1, xleft2 );
+                        xleft1 = xleft2;
+                    }
+
+                    else if (xleft2 < xleft1) {
+                        if (wanted & FLAG_REGION_2)
+                            span = runs_add_span( span, xleft2, xleft1 );
+                        xleft2 = xleft1;
+                    }
+
+                    xright = (xright1 <= xright2) ? xright1 : xright2;
+
+                    if (wanted & FLAG_REGION_BOTH)
+                        span = runs_add_span( span, xleft1, xright );
+
+                    xleft1 = xleft2 = xright;
+
+                    if (xright == xright1) {
+                        span1 += 2;
+                        xleft1 = span1[0];
+                    }
+                    if (xright == xright2) {
+                        span2 += 2;
+                        xleft2 = span2[0];
+                    }
+                }
+
+                if (wanted & FLAG_REGION_1) {
+                    while (xleft1 != XSENTINEL) {
+                        RASSERT(span1[1] != XSENTINEL);
+                        span[0] = (Run) xleft1;
+                        span[1] = span1[1];
+                        span   += 2;
+                        span1  += 2;
+                        xleft1  = span1[0];
+                    }
+                }
+
+                if (wanted & FLAG_REGION_2) {
+                    while (xleft2 != XSENTINEL) {
+                        RASSERT(span2[1] != XSENTINEL);
+                        span[0] = (Run) xleft2;
+                        span[1] = span2[1];
+                        span   += 2;
+                        span2  += 2;
+                        xleft2  = span2[0];
+                    }
+                }
+
+                if (span > runs + 2) {
+                    span[0] = XSENTINEL;
+                    runs    = span + 1;
+                }
+            }
+
+            ytop1 = ytop2 = ybot;
+
+            if (ybot == ybot1) {
+                runs1 = runs_next_scanline( runs1 );
+                ytop1 = runs1[0];
+            }
+            if (ybot == ybot2) {
+                runs2 = runs_next_scanline( runs2 );
+                ytop2 = runs2[0];
+            }
+        }
+    }
+
+    if ((wanted & FLAG_REGION_1) != 0) {
+        while (ytop1 != YSENTINEL) {
+            runs = runs_copy_scanline_adj( runs, runs1, ytop1, runs1[1] );
+            runs1 = runs_next_scanline(runs1);
+            ytop1 = runs1[0];
+        }
+    }
+
+    if ((wanted & FLAG_REGION_2) != 0) {
+        while (ytop2 != YSENTINEL) {
+            runs = runs_copy_scanline_adj( runs, runs2, ytop2, runs2[1] );
+            runs2 = runs_next_scanline(runs2);
+            ytop2 = runs2[0];
+        }
+    }
+
+    runs[0] = YSENTINEL;
+    o->runs = runs + 1;
+}
+
+/* returns 1 if the result is not empty */
+static int
+region_operator_done( RegionOperator*  o )
+{
+    Run*       src = o->runs;
+    int        count;
+    SkinBox    minmax;
+    RunStore*  store;
+
+    if (src <= o->runs_base + 1) {
+        /* result is empty */
+        skin_region_init_empty( o->result );
+        return 0;
+    }
+
+    /* coalesce the temp runs in-place and compute the corresponding bounds */
+    minmax.x1 = minmax.y1 = INT_MAX;
+    minmax.x2 = minmax.y2 = INT_MIN;
+
+    count = runs_coalesce( o->runs_base, o->runs_base, &minmax );
+    if (count == 1) {
+        /* result is empty */
+        skin_region_init_empty( o->result );
+    }
+    else
+    {
+        skin_box_to_rect( &minmax, &o->result->bounds );
+        if (count == RUNS_RECT_COUNT) {
+            o->result->runs = RUNS_RECT;
+        }
+        else
+        {
+            /* result is complex */
+            store = runstore_alloc( count );
+            o->result->runs = runstore_to_runs(store);
+            memcpy( o->result->runs, o->runs_base, count*sizeof(Run) );
+        }
+    }
+
+    /* release temporary runstore */
+    runstore_unrefp( &o->store );
+
+    return region_isEmpty(o->result);
+}
+
+
+
+int
+skin_region_intersect( SkinRegion*  r, SkinRegion*  r2 )
+{
+    RegionOperator  oper[1];
+
+    if (region_isEmpty(r))
+        return 0;
+
+    if (region_isEmpty(r2))
+        return 1;
+
+    if ( skin_rect_contains_rect( &r->bounds, &r2->bounds ) == SKIN_OUTSIDE ) {
+        skin_region_init_empty(r);
+        return 0;
+    }
+
+    region_operator_init( oper, r, r2 );
+    region_operator_do( oper, FLAG_REGION_BOTH );
+    region_operator_done( oper );
+
+    skin_region_swap( r, oper->result );
+    skin_region_reset( oper->result );
+
+    return region_isEmpty( r );
+}
+
+
+/* performs r = (intersect r (region+_from_rect rect)), returns true iff
+   the resulting region is not empty */
+int
+skin_region_intersect_rect( SkinRegion*  r, SkinRect*  rect )
+{
+    Region  r2[1];
+
+    skin_region_init_rect( r2, rect );
+    return skin_region_intersect( r, r2 );
+}
+
+/* performs r = (union r r2) */
+void
+skin_region_union( SkinRegion*  r, SkinRegion*  r2 )
+{
+    RegionOperator  oper[1];
+
+    if (region_isEmpty(r)) {
+        skin_region_copy(r, r2);
+        return;
+    }
+
+    if (region_isEmpty(r2))
+        return;
+
+    region_operator_init( oper, r, r2 );
+    region_operator_do( oper, FLAG_REGION_1|FLAG_REGION_2|FLAG_REGION_BOTH );
+    region_operator_done( oper );
+
+    skin_region_swap( r, oper->result );
+    skin_region_reset( oper->result );
+}
+
+void
+skin_region_union_rect( SkinRegion*  r, SkinRect*  rect )
+{
+    Region  r2[1];
+
+    skin_region_init_rect(r2, rect);
+    return skin_region_union( r, r2 );
+}
+
+/* performs r = (difference r r2) */
+void
+skin_region_substract( SkinRegion*  r, SkinRegion*  r2 )
+{
+    RegionOperator  oper[1];
+
+    if (region_isEmpty(r) || region_isEmpty(r2))
+        return;
+
+    if ( skin_rect_contains_rect( &r->bounds, &r2->bounds ) == SKIN_OUTSIDE ) {
+        skin_region_init_empty(r);
+        return;
+    }
+
+    region_operator_init( oper, r, r2 );
+    region_operator_do( oper, FLAG_REGION_1 );
+    region_operator_done( oper );
+
+    skin_region_swap( r, oper->result );
+    skin_region_reset( oper->result );
+}
+
+void
+skin_region_substract_rect( SkinRegion*  r, SkinRect*  rect )
+{
+    Region  r2[1];
+
+    skin_region_init_rect(r2, rect);
+    return skin_region_substract( r, r2 );
+}
+
+/* performs r = (xor r r2) */
+void
+skin_region_xor( SkinRegion*  r, SkinRegion*  r2 )
+{
+    RegionOperator  oper[1];
+
+    if (region_isEmpty(r)) {
+        skin_region_copy(r, r2);
+        return;
+    }
+
+    if (region_isEmpty(r2))
+        return;
+
+    if ( skin_rect_contains_rect( &r->bounds, &r2->bounds ) == SKIN_OUTSIDE ) {
+        skin_region_init_empty(r);
+        return;
+    }
+
+    region_operator_init( oper, r, r2 );
+    region_operator_do( oper, FLAG_REGION_1 );
+    region_operator_done( oper );
+
+    skin_region_swap( r, oper->result );
+    skin_region_reset( oper->result );
+}
+
+
+void
+skin_region_iterator_init( SkinRegionIterator*  iter,
+                           SkinRegion*          region )
+{
+    iter->region = region;
+    iter->band   = NULL;
+    iter->span   = NULL;
+}
+
+int
+skin_region_iterator_next( SkinRegionIterator*  iter, SkinRect  *rect )
+{
+    static const Run dummy[ 2 ] = { XSENTINEL, YSENTINEL };
+
+    Run*  span = iter->span;
+    Run*  band = iter->band;
+
+    if (span == NULL) {
+        Region*  r = iter->region;
+        if (region_isEmpty(r))
+            return 0;
+        if (region_isRect(r)) {
+            rect[0] = r->bounds;
+            iter->span = (Run*) dummy;
+            return 1;
+        }
+        iter->band = band = r->runs;
+        iter->span = span = r->runs + 2;
+    }
+    else if (band == NULL)
+        return 0;
+
+    while (span[0] == XSENTINEL) {
+        band = span + 1;
+        if (band[0] == YSENTINEL || band[1] == YSENTINEL)
+            return 0;
+
+        iter->band = band;
+        iter->span = span = band + 2;
+    }
+
+    if (span[1] == XSENTINEL)
+        return 0;
+
+    rect->pos.y  = band[0];
+    rect->pos.x  = span[0];
+    rect->size.h = band[1] - band[0];
+    rect->size.w = span[1] - span[0];
+
+    iter->span = span + 2;
+    return 1;
+}
+
+int
+skin_region_iterator_next_box( SkinRegionIterator*  iter, SkinBox  *box )
+{
+    SkinRect  rect;
+    int       result = skin_region_iterator_next( iter, &rect );
+
+    if (result)
+        skin_box_from_rect( box, &rect );
+
+    return result;
+}
+
+#ifdef UNIT_TEST
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "skin_rect.c"
+
+static void
+panic(void)
+{
+    *((char*)0) = 1;
+    exit(0);
+}
+
+static void
+_expectCompare( Region*  r, const SkinBox*  boxes, int  count )
+{
+    if (count == 0) {
+        if ( !skin_region_is_empty(r) ) {
+            printf( " result is not empty\n" );
+            panic();
+        }
+    }
+    else if (count == 1) {
+        SkinRect  rect1, rect2;
+        if ( !skin_region_is_rect(r) ) {
+            printf( " result is not a rectangle\n" );
+            panic();
+        }
+        skin_region_get_bounds( r, &rect1 );
+        skin_box_to_rect( (SkinBox*)boxes, &rect2 );
+        if ( !skin_rect_equals( &rect1, &rect2 ) ) {
+            printf( " result is (%d,%d,%d,%d), expected (%d,%d,%d,%d)\n",
+                    rect1.pos.x, rect1.pos.y,
+                    rect1.pos.x + rect1.size.w, rect1.pos.y + rect1.size.h,
+                    rect2.pos.x, rect2.pos.y,
+                    rect2.pos.x + rect2.size.w, rect2.pos.y + rect2.size.h );
+            panic();
+        }
+    }
+    else {
+        SkinRegionIterator  iter;
+        SkinBox             b;
+        int                 n;
+
+        skin_region_iterator_init( &iter, r );
+        n = 0;
+        while (n < count) {
+            if ( !skin_region_iterator_next_box( &iter, &b ) ) {
+                printf( "missing region box (%d, %d, %d, %d)\n",
+                        boxes->x1, boxes->y1, boxes->x2, boxes->y2 );
+                panic();
+            }
+
+            if (b.x1 != boxes->x1 || b.x2 != boxes->x2||
+                b.y1 != boxes->y1 || b.y2 != boxes->y2)
+            {
+                printf( "invalid region box (%d,%d,%d,%d) expecting (%d,%d,%d,%d)\n",
+                        b.x1, b.y1, b.x2, b.y2,
+                        boxes->x1, boxes->y1, boxes->x2, boxes->y2 );
+                panic();
+            }
+            boxes += 1;
+            n += 1;
+        }
+
+        if ( skin_region_iterator_next_box( &iter, &b ) ) {
+            printf( "excess region box (%d,%d,%d,%d)\n",
+                    b.x1, b.y1, b.x2, b.y2 );
+            panic();
+        }
+    }
+}
+
+
+static void
+expectEmptyRegion( Region*  r )
+{
+    printf( "expectEmptyRegion: " );
+    if (!skin_region_is_empty(r)) {
+        printf( "region not empty !!\n" );
+        panic();
+    }
+    printf( "ok\n" );
+}
+
+static void
+expectTestIntersect( Region*  r1, Region*  r2, SkinOverlap  overlap )
+{
+    SkinOverlap  result;
+    printf( "expectTestIntersect(%d): ", overlap );
+    result = skin_region_test_intersect(r1, r2);
+    if (result != overlap) {
+        printf( "bad result %d, expected %d\n", result, overlap );
+        panic();
+    }
+    printf( "ok\n" );
+}
+
+static void
+expectRectRegion( Region*  r, int  x1, int  y1, int  x2, int  y2 )
+{
+    SkinRect  rect;
+    SkinBox   b;
+
+    printf( "expectRectRegion(%d,%d,%d,%d): ",x1,y1,x2,y2 );
+    if (!skin_region_is_rect(r)) {
+        printf( "region not rect !!\n" );
+        panic();
+    }
+
+    skin_region_get_bounds( r, &rect );
+    skin_box_from_rect( &b, &rect );
+
+    if (b.x1 != x1 || b.x2 != x2 || b.y1 != y1 || b.y2 != y2) {
+        printf( "rect region bounds are (%d,%d,%d,%d), expecting (%d,%d,%d,%d)\n",
+               b.x1, b.y1, b.x2, b.y2, x1, y1, x2, y2 );
+        panic();
+    }
+    printf( "ok\n" );
+}
+
+static void
+expectComplexRegion( Region* r, const SkinBox*  boxes, int  count )
+{
+    SkinRegionIterator  iter;
+    SkinBox             b;
+    int                 n;
+
+    printf( "expectComplexRegion(): " );
+    if (!skin_region_is_complex(r)) {
+        printf( "region is not complex !!\n" );
+        panic();
+    }
+    _expectCompare( r, boxes, count );
+    printf( "ok\n" );
+}
+
+static void
+expectIntersect( Region*  r1, Region*  r2, const SkinBox*  boxes, int  count )
+{
+    SkinRegion  r[1];
+
+    printf( "expectIntersect(%d): ", count );
+    skin_region_init_copy( r, r1 );
+    skin_region_intersect( r, r2 );
+    _expectCompare( r, boxes, count );
+    printf( "ok\n" );
+}
+
+static void
+expectUnion( Region*  r1, Region*  r2, const SkinBox*  boxes, int  count )
+{
+    SkinRegion  r[1];
+
+    printf( "expectUnion(%d): ", count );
+    skin_region_init_copy( r, r1 );
+    skin_region_union( r, r2 );
+    _expectCompare( r, boxes, count );
+    printf( "ok\n" );
+}
+
+
+static void
+expectSubstract( Region*  r1, Region*  r2, const SkinBox*  boxes, int  count )
+{
+    SkinRegion  r[1];
+
+    printf( "expectSubstract(%d): ", count );
+    skin_region_init_copy( r, r1 );
+    skin_region_substract( r, r2 );
+    _expectCompare( r, boxes, count );
+    printf( "ok\n" );
+}
+
+
+int main( void )
+{
+    SkinRegion  r[1], r2[1];
+
+    skin_region_init_empty( r );
+    expectEmptyRegion( r );
+
+    skin_region_init( r, 10, 20, 110, 120 );
+    expectRectRegion( r, 10, 20, 110, 120 );
+
+    skin_region_translate( r, 50, 80 );
+    expectRectRegion( r, 60, 100, 160, 200 );
+
+    skin_region_init( r, 10, 10, 40, 40 );
+    skin_region_init( r2, 20, 20, 50, 50 );
+    expectTestIntersect( r, r2, SKIN_OVERLAP );
+
+    skin_region_translate(r2, +20, + 20 );
+    expectTestIntersect( r, r2, SKIN_OUTSIDE );
+
+    skin_region_translate(r2, -30, -30 );
+    expectTestIntersect( r, r2, SKIN_INSIDE );
+
+    {
+        static const SkinBox  result1[1] = {
+            { 20, 20, 40, 40 }
+        };
+        static const SkinBox  result2[3] = {
+            { 10, 10, 40, 20 },
+            { 10, 20, 50, 40 },
+            { 20, 40, 50, 50 },
+        };
+        static const SkinBox  result3[2] = {
+            { 10, 10, 40, 20 },
+            { 10, 20, 20, 40 },
+        };
+
+        skin_region_init( r, 10, 10, 40, 40 );
+        skin_region_init( r2, 20, 20, 50, 50 );
+        expectIntersect( r, r2, result1, 1 );
+        expectUnion( r, r2, result2, 3 );
+        expectSubstract( r, r2, result3, 2 );
+    }
+
+    return 0;
+}
+
+#endif /* UNIT_TEST */
diff --git a/android/skin/region.h b/android/skin/region.h
new file mode 100644
index 0000000..9ac4103
--- /dev/null
+++ b/android/skin/region.h
@@ -0,0 +1,103 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_REGION_H
+#define _ANDROID_SKIN_REGION_H
+
+#include "android/skin/rect.h"
+
+typedef struct SkinRegion  SkinRegion;
+
+extern void  skin_region_init_empty( SkinRegion*  r );
+extern void  skin_region_init( SkinRegion*  r, int  x1, int  y1, int  x2, int  y2 );
+extern void  skin_region_init_rect( SkinRegion*  r, SkinRect*  rect );
+extern void  skin_region_init_box( SkinRegion*  r, SkinBox*  box );
+extern void  skin_region_init_copy( SkinRegion*  r, SkinRegion*  r2 );
+extern void  skin_region_reset( SkinRegion*  r );
+
+/* finalize region, then copy src into it */
+extern void  skin_region_copy( SkinRegion*  r, SkinRegion*  src );
+
+/* compare two regions for equality */
+extern int   skin_region_equals( SkinRegion*  r1, SkinRegion*  r2 );
+
+/* swap two regions */
+extern void  skin_region_swap( SkinRegion*  r, SkinRegion*  r2 );
+
+extern int   skin_region_is_empty( SkinRegion*  r );
+extern int   skin_region_is_rect( SkinRegion*  r );
+extern int   skin_region_is_complex( SkinRegion*  r );
+extern void  skin_region_get_bounds( SkinRegion*  r, SkinRect*  bounds );
+
+extern void  skin_region_translate( SkinRegion*  r, int  dx, int  dy );
+
+extern SkinOverlap  skin_region_contains( SkinRegion*  r, int  x, int  y );
+
+extern SkinOverlap  skin_region_contains_rect( SkinRegion*  r,
+                                               SkinRect*    rect );
+
+extern SkinOverlap  skin_region_contains_box( SkinRegion*  r, SkinBox*  b );
+
+/* returns overlap mode for "is r2 inside r1" */
+extern  SkinOverlap  skin_region_test_intersect( SkinRegion*  r1,
+                                                 SkinRegion*  r2 );
+
+/* performs r = (intersect r r2), returns true if the resulting region
+   is not empty */
+extern int  skin_region_intersect     ( SkinRegion*  r, SkinRegion*  r2 );
+extern int  skin_region_intersect_rect( SkinRegion*  r, SkinRect*    rect );
+
+/* performs r = (intersect r (region+_from_rect rect)), returns true iff
+   the resulting region is not empty */
+
+/* performs r = (union r r2) */
+extern void skin_region_union     ( SkinRegion*  r, SkinRegion*  r2 );
+extern void skin_region_union_rect( SkinRegion*  r, SkinRect*  rect );
+
+/* performs r = (difference r r2) */
+extern void skin_region_substract     ( SkinRegion*  r, SkinRegion*  r2 );
+extern void skin_region_substract_rect( SkinRegion*  r, SkinRect*  rect );
+
+/* performs r = (xor r r2) */
+extern void skin_region_xor( SkinRegion*  r, SkinRegion*  r2 );
+
+typedef struct SkinRegionIterator  SkinRegionIterator;
+
+/* iterator */
+extern void  skin_region_iterator_init( SkinRegionIterator*  iter,
+                                        SkinRegion*          r );
+
+extern int   skin_region_iterator_next( SkinRegionIterator*  iter,
+                                        SkinRect            *rect );
+
+extern int   skin_rection_iterator_next_box( SkinRegionIterator*  iter,
+                                             SkinBox             *box );
+
+/* the following should be considered private definitions. they're only here
+   to allow clients to allocate SkinRegion objects themselves... */
+
+typedef signed short   SkinRegionRun;
+#define SKIN_REGION_SENTINEL  0x7fff
+
+struct SkinRegion
+{
+    SkinRect        bounds;
+    SkinRegionRun*  runs;
+};
+
+struct SkinRegionIterator
+{
+    SkinRegion*     region;
+    SkinRegionRun*  band;
+    SkinRegionRun*  span;
+};
+
+#endif /* _ANDROID_SKIN_REGION_H */
diff --git a/android/skin/scaler.c b/android/skin/scaler.c
new file mode 100644
index 0000000..59e212f
--- /dev/null
+++ b/android/skin/scaler.c
@@ -0,0 +1,135 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/scaler.h"
+#include <stdint.h>
+#include <math.h>
+
+struct SkinScaler {
+    double  scale;
+    double  xdisp, ydisp;
+    double  invscale;
+    int     valid;
+};
+
+static SkinScaler  _scaler0;
+
+SkinScaler*
+skin_scaler_create( void )
+{
+    _scaler0.scale    = 1.0;
+    _scaler0.xdisp    = 0.0;
+    _scaler0.ydisp    = 0.0;
+    _scaler0.invscale = 1.0;
+    return &_scaler0;
+}
+
+/* change the scale of a given scaler. returns 0 on success, or -1 in case of
+ * problem (unsupported scale) */
+int
+skin_scaler_set( SkinScaler*  scaler, double  scale, double xdisp, double ydisp )
+{
+    /* right now, we only support scales in the 0.5 .. 1.0 range */
+    if (scale < 0.1)
+        scale = 0.1;
+    else if (scale > 6.0)
+        scale = 6.0;
+
+    scaler->scale    = scale;
+    scaler->xdisp    = xdisp;
+    scaler->ydisp    = ydisp;
+    scaler->invscale = 1/scale;
+    scaler->valid    = 1;
+
+    return 0;
+}
+
+void
+skin_scaler_free( SkinScaler*  scaler )
+{
+    scaler=scaler;
+}
+
+typedef struct {
+    SDL_Rect    rd;         /* destination rectangle */
+    int         sx, sy;     /* source start position in 16.16 format */
+    int         ix, iy;     /* source increments in 16.16 format */
+    int         src_pitch;
+    int         src_w;
+    int         src_h;
+    int         dst_pitch;
+    uint8_t*    dst_line;
+    uint8_t*    src_line;
+    double      scale;
+} ScaleOp;
+
+
+#define  ARGB_SCALE_GENERIC       scale_generic
+#define  ARGB_SCALE_05_TO_10      scale_05_to_10
+#define  ARGB_SCALE_UP_BILINEAR   scale_up_bilinear
+#define  ARGB_SCALE_UP_QUICK_4x4  scale_up_quick_4x4
+
+#include "android/skin/argb.h"
+
+
+void
+skin_scaler_scale( SkinScaler*   scaler,
+                   SDL_Surface*  dst_surface,
+                   SDL_Surface*  src_surface,
+                   int           sx,
+                   int           sy,
+                   int           sw,
+                   int           sh )
+{
+    ScaleOp   op;
+
+    if ( !scaler->valid )
+        return;
+
+    SDL_LockSurface( src_surface );
+    SDL_LockSurface( dst_surface );
+    {
+        op.scale     = scaler->scale;
+        op.src_pitch = src_surface->pitch;
+        op.src_line  = src_surface->pixels;
+        op.src_w     = src_surface->w;
+        op.src_h     = src_surface->h;
+        op.dst_pitch = dst_surface->pitch;
+        op.dst_line  = dst_surface->pixels;
+
+        /* compute the destination rectangle */
+        op.rd.x = (int)(sx * scaler->scale + scaler->xdisp);
+        op.rd.y = (int)(sy * scaler->scale + scaler->ydisp);
+        op.rd.w = (int)(ceil((sx + sw) * scaler->scale + scaler->xdisp)) - op.rd.x;
+        op.rd.h = (int)(ceil((sy + sh) * scaler->scale + scaler->ydisp)) - op.rd.y;
+
+        /* compute the starting source position in 16.16 format
+         * and the corresponding increments */
+        op.sx = (int)((op.rd.x - scaler->xdisp) * scaler->invscale * 65536);
+        op.sy = (int)((op.rd.y - scaler->ydisp) * scaler->invscale * 65536);
+
+        op.ix = (int)( scaler->invscale * 65536 );
+        op.iy = op.ix;
+
+        op.dst_line += op.rd.x*4 + op.rd.y*op.dst_pitch;
+
+        if (op.scale >= 0.5 && op.scale <= 1.0)
+            scale_05_to_10( &op );
+        else if (op.scale > 1.0)
+            scale_up_bilinear( &op );
+        else
+            scale_generic( &op );
+    }
+    SDL_UnlockSurface( dst_surface );
+    SDL_UnlockSurface( src_surface );
+
+    SDL_UpdateRects( dst_surface, 1, &op.rd );
+}
diff --git a/android/skin/scaler.h b/android/skin/scaler.h
new file mode 100644
index 0000000..4e0ec5a
--- /dev/null
+++ b/android/skin/scaler.h
@@ -0,0 +1,39 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_SCALER_H
+#define _ANDROID_SKIN_SCALER_H
+
+#include "android/skin/image.h"
+
+typedef struct SkinScaler   SkinScaler;
+
+/* create a new image scaler. by default, it uses a scale of 1.0 */
+extern SkinScaler*  skin_scaler_create( void );
+
+/* change the scale of a given scaler. returns 0 on success, or -1 in case of
+ * problem (unsupported scale) */
+extern int          skin_scaler_set( SkinScaler*  scaler,
+                                     double       scale,
+                                     double       xDisp,
+                                     double       yDisp );
+
+extern void         skin_scaler_free( SkinScaler*  scaler );
+
+extern void         skin_scaler_scale( SkinScaler*   scaler,
+                                       SDL_Surface*  dst,
+                                       SDL_Surface*  src,
+                                       int           sx,
+                                       int           sy,
+                                       int           sw,
+                                       int           sh );
+
+#endif /* _ANDROID_SKIN_SCALER_H */
diff --git a/android/skin/surface.c b/android/skin/surface.c
new file mode 100644
index 0000000..4424bd8
--- /dev/null
+++ b/android/skin/surface.c
@@ -0,0 +1,613 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/surface.h"
+#include "android/skin/argb.h"
+#include <SDL.h>
+
+#define  DEBUG  1
+
+#if DEBUG
+#include "android/utils/debug.h"
+#define  D(...)   VERBOSE_PRINT(surface,__VA_ARGS__)
+#else
+#define  D(...)   ((void)0)
+#endif
+
+struct SkinSurface {
+    int                  refcount;
+    uint32_t*            pixels;
+    SDL_Surface*         surface;
+    SkinSurfaceDoneFunc  done_func;
+    void*                done_user;
+};
+
+static void
+skin_surface_free( SkinSurface*  s )
+{
+    if (s->done_func) {
+        s->done_func( s->done_user );
+        s->done_func = NULL;
+    }
+    if (s->surface) {
+        SDL_FreeSurface(s->surface);
+        s->surface = NULL;
+    }
+    free(s);
+}
+
+extern SkinSurface*
+skin_surface_ref( SkinSurface*  surface )
+{
+    if (surface)
+        surface->refcount += 1;
+    return surface;
+}
+
+extern void
+skin_surface_unrefp( SkinSurface*  *psurface )
+{
+    SkinSurface*  surf = *psurface;
+    if (surf) {
+        if (--surf->refcount <= 0)
+            skin_surface_free(surf);
+        *psurface = NULL;
+    }
+}
+
+
+void
+skin_surface_set_done( SkinSurface*  s, SkinSurfaceDoneFunc  done_func, void*  done_user )
+{
+    s->done_func = done_func;
+    s->done_user = done_user;
+}
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#  define  ARGB32_R_MASK  0xff000000
+#  define  ARGB32_G_MASK  0x00ff0000
+#  define  ARGB32_B_MASK  0x0000ff00
+#  define  ARGB32_A_MASK  0x000000ff
+#else
+#  define  ARGB32_R_MASK  0x000000ff
+#  define  ARGB32_G_MASK  0x0000ff00
+#  define  ARGB32_B_MASK  0x00ff0000
+#  define  ARGB32_A_MASK  0xff000000
+#endif
+
+static SDL_Surface*
+_sdl_surface_create_rgb( int  width,
+                         int  height,
+                         int  depth,
+                         int  flags )
+{
+   Uint32   rmask, gmask, bmask, amask;
+
+    if (depth == 8) {
+        rmask = gmask = bmask = 0;
+        amask = 0xff;
+    } else if (depth == 32) {
+        rmask = ARGB32_R_MASK;
+        gmask = ARGB32_G_MASK;
+        bmask = ARGB32_B_MASK;
+        amask = ARGB32_A_MASK;
+    } else
+        return NULL;
+
+    return SDL_CreateRGBSurface( flags, width, height, depth,
+                                 rmask, gmask, bmask, amask );
+}
+
+
+static SDL_Surface*
+_sdl_surface_create_rgb_from( int   width,
+                              int   height,
+                              int   pitch,
+                              void* pixels,
+                              int   depth )
+{
+   Uint32   rmask, gmask, bmask, amask;
+
+    if (depth == 8) {
+        rmask = gmask = bmask = 0;
+        amask = 0xff;
+    } else if (depth == 32) {
+        rmask = ARGB32_R_MASK;
+        gmask = ARGB32_G_MASK;
+        bmask = ARGB32_B_MASK;
+        amask = ARGB32_A_MASK;
+    } else
+        return NULL;
+
+    return SDL_CreateRGBSurfaceFrom( pixels, width, height, pitch, depth,
+                                     rmask, gmask, bmask, amask );
+}
+
+
+static SkinSurface*
+_skin_surface_create( SDL_Surface*  surface,
+                      void*         pixels )
+{
+    SkinSurface*  s = malloc(sizeof(*s));
+    if (s != NULL) {
+        s->refcount = 1;
+        s->pixels   = pixels;
+        s->surface  = surface;
+        s->done_func = NULL;
+        s->done_user = NULL;
+    }
+    else {
+        SDL_FreeSurface(surface);
+        free(pixels);
+        D( "not enough memory to allocate new skin surface !" );
+    }
+    return  s;
+}
+
+
+SkinSurface*
+skin_surface_create_fast( int  w, int  h )
+{
+    SDL_Surface*  surface;
+
+    surface = _sdl_surface_create_rgb( w, h, 32, SDL_HWSURFACE );
+    if (surface == NULL) {
+        surface = _sdl_surface_create_rgb( w, h, 32, SDL_SWSURFACE );
+        if (surface == NULL) {
+            D( "could not create fast %dx%d ARGB32 surface: %s",
+               w, h, SDL_GetError() );
+            return NULL;
+        }
+    }
+    return _skin_surface_create( surface, NULL );
+}
+
+
+SkinSurface*
+skin_surface_create_slow( int  w, int  h )
+{
+    SDL_Surface*  surface;
+
+    surface = _sdl_surface_create_rgb( w, h, 32, SDL_SWSURFACE );
+    if (surface == NULL) {
+        D( "could not create slow %dx%d ARGB32 surface: %s",
+            w, h, SDL_GetError() );
+        return NULL;
+    }
+    return _skin_surface_create( surface, NULL );
+}
+
+
+SkinSurface*
+skin_surface_create_argb32_from(
+                        int                  w,
+                        int                  h,
+                        int                  pitch,
+                        uint32_t*            pixels,
+                        int                  do_copy )
+{
+    SDL_Surface*  surface;
+    uint32_t*     pixcopy = NULL;
+
+    if (do_copy) {
+        size_t  size = h*pitch;
+        pixcopy = malloc( size );
+        if (pixcopy == NULL && size > 0) {
+            D( "not enough memory to create %dx%d ARGB32 surface",
+               w, h );
+            return NULL;
+        }
+        memcpy( pixcopy, pixels, size );
+    }
+
+    surface = _sdl_surface_create_rgb_from( w, h, pitch,
+                                            pixcopy ? pixcopy : pixels,
+                                            32 );
+    if (surface == NULL) {
+        D( "could not create %dx%d slow ARGB32 surface: %s",
+            w, h, SDL_GetError() );
+        return NULL;
+    }
+    return _skin_surface_create( surface, pixcopy );
+}
+
+
+
+
+extern int
+skin_surface_lock( SkinSurface*  s, SkinSurfacePixels  *pix )
+{
+    if (!s || !s->surface) {
+        D( "error: trying to lock stale surface %p", s );
+        return -1;
+    }
+    if ( SDL_LockSurface( s->surface ) != 0 ) {
+        D( "could not lock surface %p: %s", s, SDL_GetError() );
+        return -1;
+    }
+    pix->w      = s->surface->w;
+    pix->h      = s->surface->h;
+    pix->pitch  = s->surface->pitch;
+    pix->pixels = s->surface->pixels;
+    return 0;
+}
+
+/* unlock a slow surface that was previously locked */
+extern void
+skin_surface_unlock( SkinSurface*  s )
+{
+    if (s && s->surface)
+        SDL_UnlockSurface( s->surface );
+}
+
+
+#if 0
+static uint32_t
+skin_surface_map_argb( SkinSurface*  s, uint32_t  c )
+{
+    if (s && s->surface) {
+        return SDL_MapRGBA( s->surface->format,
+                            ((c) >> 16) & 255,
+                            ((c) >> 8) & 255,
+                            ((c) & 255),
+                            ((c) >> 24) & 255 );
+    }
+    return 0x00000000;
+}
+#endif
+
+typedef struct {
+    int   x;
+    int   y;
+    int   w;
+    int   h;
+    int   sx;
+    int   sy;
+
+    uint8_t*      dst_line;
+    int           dst_pitch;
+    SDL_Surface*  dst_lock;
+
+    uint8_t*      src_line;
+    int           src_pitch;
+    SDL_Surface*  src_lock;
+    uint32_t      src_color;
+
+} SkinBlit;
+
+
+static int
+skin_blit_init_fill( SkinBlit*     blit,
+                     SkinSurface*  dst,
+                     SkinRect*     dst_rect,
+                     uint32_t      color )
+{
+    int  x = dst_rect->pos.x;
+    int  y = dst_rect->pos.y;
+    int  w = dst_rect->size.w;
+    int  h = dst_rect->size.h;
+    int  delta;
+
+    if (x < 0) {
+        w += x;
+        x  = 0;
+    }
+    delta = (x + w) - dst->surface->w;
+    if (delta > 0)
+        w -= delta;
+
+    if (y < 0) {
+        h += y;
+        y  = 0;
+    }
+    delta = (y + h) - dst->surface->h;
+    if (delta > 0)
+        h -= delta;
+
+    if (w <= 0 || h <= 0)
+        return 0;
+
+    blit->x = x;
+    blit->y = y;
+    blit->w = w;
+    blit->h = h;
+
+    if ( !SDL_LockSurface(dst->surface) )
+        return 0;
+
+    blit->dst_lock  = dst->surface;
+    blit->dst_pitch = dst->surface->pitch;
+    blit->dst_line  = dst->surface->pixels + y*blit->dst_pitch;
+
+    blit->src_lock  = NULL;
+    blit->src_color = color;
+
+    return 1;
+}
+
+static int
+skin_blit_init_blit( SkinBlit*     blit,
+                     SkinSurface*  dst,
+                     SkinPos*      dst_pos,
+                     SkinSurface*  src,
+                     SkinRect*     src_rect )
+{
+    int  x  = dst_pos->x;
+    int  y  = dst_pos->y;
+    int  sx = src_rect->pos.x;
+    int  sy = src_rect->pos.y;
+    int  w  = src_rect->size.w;
+    int  h  = src_rect->size.h;
+    int  delta;
+
+    if (x < 0) {
+        w  += x;
+        sx -= x;
+        x   = 0;
+    }
+    if (sx < 0) {
+        w  += sx;
+        x  -= sx;
+        sx  = 0;
+    }
+
+    delta = (x + w) - dst->surface->w;
+    if (delta > 0)
+        w -= delta;
+
+    delta = (sx + w) - src->surface->w;
+    if (delta > 0)
+        w -= delta;
+
+    if (y < 0) {
+        h  += y;
+        sy += y;
+        y   = 0;
+    }
+    if (sy < 0) {
+        h  += sy;
+        y  -= sy;
+        sy  = 0;
+    }
+    delta = (y + h) - dst->surface->h;
+    if (delta > 0)
+        h -= delta;
+
+    delta = (sy + h) - src->surface->h;
+
+    if (w <= 0 || h <= 0)
+        return 0;
+
+    blit->x = x;
+    blit->y = y;
+    blit->w = w;
+    blit->h = h;
+
+    blit->sx = sx;
+    blit->sy = sy;
+
+    if ( !SDL_LockSurface(dst->surface) )
+        return 0;
+
+    blit->dst_lock  = dst->surface;
+    blit->dst_pitch = dst->surface->pitch;
+    blit->dst_line  = (uint8_t*) dst->surface->pixels + y*blit->dst_pitch;
+
+    if ( !SDL_LockSurface(src->surface) ) {
+        SDL_UnlockSurface(dst->surface);
+        return 0;
+    }
+
+    blit->src_lock  = src->surface;
+    blit->src_pitch = src->surface->pitch;
+    blit->src_line  = (uint8_t*) src->surface->pixels + sy*blit->src_pitch;
+
+    return 1;
+}
+
+static void
+skin_blit_done( SkinBlit*  blit )
+{
+    if (blit->src_lock)
+        SDL_UnlockSurface( blit->src_lock );
+    if (blit->dst_lock)
+        SDL_UnlockSurface( blit->dst_lock );
+    ARGB_DONE;
+}
+
+typedef void (*SkinLineFillFunc)( uint32_t*  dst, uint32_t  color, int  len );
+typedef void (*SkinLineBlitFunc)( uint32_t*  dst, const uint32_t*  src,  int  len );
+
+static void
+skin_line_fill_copy( uint32_t*  dst, uint32_t  color, int  len )
+{
+    uint32_t*  end = dst + len;
+
+    while (dst + 4 <= end) {
+        dst[0] = dst[1] = dst[2] = dst[3] = color;
+        dst   += 4;
+    }
+    while (dst < end) {
+        dst[0] = color;
+        dst   += 1;
+    }
+}
+
+static void
+skin_line_fill_srcover( uint32_t*  dst, uint32_t  color, int  len )
+{
+    uint32_t*  end = dst + len;
+    uint32_t   alpha = (color >> 24);
+
+    if (alpha == 255)
+    {
+        skin_line_fill_copy(dst, color, len);
+    }
+    else
+    {
+        ARGB_DECL(src_c);
+        ARGB_DECL_ZERO();
+
+        alpha  = 255 - alpha;
+        alpha += (alpha >> 7);
+
+        ARGB_UNPACK(src_c,color);
+
+        for ( ; dst < end; dst++ )
+        {
+            ARGB_DECL(dst_c);
+
+            ARGB_READ(dst_c,dst);
+            ARGB_MULSHIFT(dst_c,dst_c,alpha,8);
+            ARGB_ADD(dst_c,src_c);
+            ARGB_WRITE(dst_c,dst);
+        }
+    }
+}
+
+static void
+skin_line_fill_dstover( uint32_t*  dst, uint32_t  color, int  len )
+{
+    uint32_t*  end = dst + len;
+    ARGB_DECL(src_c);
+    ARGB_DECL_ZERO();
+
+    ARGB_UNPACK(src_c,color);
+
+    for ( ; dst < end; dst++ )
+    {
+        ARGB_DECL(dst_c);
+        ARGB_DECL(val);
+
+        uint32_t   alpha;
+
+        ARGB_READ(dst_c,dst);
+        alpha = 256 - (dst[0] >> 24);
+        ARGB_MULSHIFT(val,src_c,alpha,8);
+        ARGB_ADD(val,dst_c);
+        ARGB_WRITE(val,dst);
+    }
+}
+
+extern void
+skin_surface_fill( SkinSurface*  dst,
+                   SkinRect*     rect,
+                   uint32_t      argb_premul,
+                   SkinBlitOp    blitop )
+{
+    SkinLineFillFunc  fill;
+    SkinBlit          blit[1];
+
+    switch (blitop) {
+        case SKIN_BLIT_COPY:    fill = skin_line_fill_copy; break;
+        case SKIN_BLIT_SRCOVER: fill = skin_line_fill_srcover; break;
+        case SKIN_BLIT_DSTOVER: fill = skin_line_fill_dstover; break;
+        default: return;
+    }
+
+    if ( skin_blit_init_fill( blit, dst, rect, argb_premul ) ) {
+        uint8_t*   line  = blit->dst_line;
+        int        pitch = blit->dst_pitch;
+        uint8_t*   end   = line + pitch*blit->h;
+
+        for ( ; line != end; line += pitch )
+            fill( (uint32_t*)line + blit->x, argb_premul, blit->w );
+    }
+}
+
+
+static void
+skin_line_blit_copy( uint32_t*  dst, const uint32_t*  src, int  len )
+{
+    memcpy( (char*)dst, (const char*)src, len*4 );
+}
+
+
+
+static void
+skin_line_blit_srcover( uint32_t*  dst, const uint32_t*  src, int  len )
+{
+    uint32_t*  end = dst + len;
+    ARGB_DECL_ZERO();
+
+    for ( ; dst < end; dst++ ) {
+        ARGB_DECL(s);
+        ARGB_DECL(d);
+        ARGB_DECL(v);
+        uint32_t  alpha;
+
+        ARGB_READ(s,src);
+        alpha = (src[0] >> 24);
+        if (alpha > 0) {
+            ARGB_READ(d,dst);
+            alpha = 256 - alpha;
+            ARGB_MULSHIFT(v,d,alpha,8);
+            ARGB_ADD(v,d);
+            ARGB_WRITE(v,dst);
+        }
+    }
+}
+
+static void
+skin_line_blit_dstover( uint32_t*  dst, const uint32_t*  src, int  len )
+{
+    uint32_t*  end = dst + len;
+    ARGB_DECL_ZERO();
+
+    for ( ; dst < end; dst++ ) {
+        ARGB_DECL(s);
+        ARGB_DECL(d);
+        ARGB_DECL(v);
+        uint32_t  alpha;
+
+        ARGB_READ(d,dst);
+        alpha = (dst[0] >> 24);
+        if (alpha < 255) {
+            ARGB_READ(s,src);
+            alpha = 256 - alpha;
+            ARGB_MULSHIFT(v,s,alpha,8);
+            ARGB_ADD(v,s);
+            ARGB_WRITE(v,dst);
+        }
+    }
+}
+
+
+extern void
+skin_surface_blit( SkinSurface*  dst,
+                   SkinPos*      dst_pos,
+                   SkinSurface*  src,
+                   SkinRect*     src_rect,
+                   SkinBlitOp    blitop )
+{
+    SkinLineBlitFunc  func;
+    SkinBlit          blit[1];
+
+    switch (blitop) {
+        case SKIN_BLIT_COPY:    func = skin_line_blit_copy; break;
+        case SKIN_BLIT_SRCOVER: func = skin_line_blit_srcover; break;
+        case SKIN_BLIT_DSTOVER: func = skin_line_blit_dstover; break;
+        default: return;
+    }
+
+    if ( skin_blit_init_blit( blit, dst, dst_pos, src, src_rect ) ) {
+        uint8_t*   line   = blit->dst_line;
+        uint8_t*   sline  = blit->src_line;
+        int        pitch  = blit->dst_pitch;
+        int        spitch = blit->src_pitch;
+        uint8_t*   end    = line + pitch*blit->h;
+
+        for ( ; line != end; line += pitch, sline += spitch )
+            func( (uint32_t*)line + blit->x, (uint32_t*)sline + blit->sx, blit->w );
+
+        skin_blit_done(blit);
+    }
+}
diff --git a/android/skin/surface.h b/android/skin/surface.h
new file mode 100644
index 0000000..39ab439
--- /dev/null
+++ b/android/skin/surface.h
@@ -0,0 +1,101 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_SURFACE_H
+#define _ANDROID_SKIN_SURFACE_H
+
+#include "android/skin/region.h"
+#include <stdint.h>
+
+/* a SkinSurface models a 32-bit ARGB pixel image that can be blitted to or from
+ */
+typedef struct SkinSurface  SkinSurface;
+
+/* increment surface's reference count */
+extern SkinSurface*  skin_surface_ref( SkinSurface*  surface );
+
+/* decrement a surface's reference count. takes the surface's address as parameter.
+   it will be set to NULL on exit */
+extern void          skin_surface_unrefp( SkinSurface*  *psurface );
+
+/* sets a callback that will be called when the surface is destroyed.
+ * used NULL for done_func to disable it
+ */
+typedef void (*SkinSurfaceDoneFunc)( void*  user );
+
+extern void  skin_surface_set_done( SkinSurface*  s, SkinSurfaceDoneFunc  done_func, void*  done_user );
+
+
+/* there are two kinds of surfaces:
+
+   - fast surfaces, whose content can be placed in video memory or
+     RLE-compressed for faster blitting and blending. the pixel format
+     used internally might be something very different from ARGB32.
+
+   - slow surfaces, whose content (pixel buffer) can be accessed and modified
+     with _lock()/_unlock() but may be blitted far slower since they reside in
+     system memory.
+*/
+
+/* create a 'fast' surface that contains a copy of an input ARGB32 pixmap */
+extern SkinSurface*  skin_surface_create_fast( int  w, int  h );
+
+/* create an empty 'slow' surface containing an ARGB32 pixmap */
+extern SkinSurface*  skin_surface_create_slow( int  w, int  h );
+
+/* create a 'slow' surface from a given pixel buffer. if 'do_copy' is TRUE, then
+ * the content of 'pixels' is copied into a heap-allocated buffer. otherwise
+ * the data will be used directly.
+ */
+extern SkinSurface*  skin_surface_create_argb32_from(
+                            int                  w,
+                            int                  h,
+                            int                  pitch,
+                            uint32_t*            pixels,
+                            int                  do_copy );
+
+/* surface pixels information for slow surfaces */
+typedef struct {
+    int         w;
+    int         h;
+    int         pitch;
+    uint32_t*   pixels;
+} SkinSurfacePixels;
+
+/* lock a slow surface, and returns its pixel information.
+   returns 0 in case of success, -1 otherwise */
+extern int     skin_surface_lock  ( SkinSurface*  s, SkinSurfacePixels  *pix );
+
+/* unlock a slow surface that was previously locked */
+extern void    skin_surface_unlock( SkinSurface*  s );
+
+/* list of composition operators for the blit routine */
+typedef enum {
+    SKIN_BLIT_COPY = 0,
+    SKIN_BLIT_SRCOVER,
+    SKIN_BLIT_DSTOVER,
+} SkinBlitOp;
+
+
+/* blit a surface into another one */
+extern void    skin_surface_blit( SkinSurface*  dst,
+                                  SkinPos*      dst_pos,
+                                  SkinSurface*  src,
+                                  SkinRect*     src_rect,
+                                  SkinBlitOp    blitop );
+
+/* blit a colored rectangle into a destination surface */
+extern void    skin_surface_fill( SkinSurface*  dst,
+                                  SkinRect*     rect,
+                                  uint32_t      argb_premul,
+                                  SkinBlitOp    blitop );
+
+#endif /* _ANDROID_SKIN_SURFACE_H */
diff --git a/android/skin/trackball.c b/android/skin/trackball.c
new file mode 100644
index 0000000..b18923a
--- /dev/null
+++ b/android/skin/trackball.c
@@ -0,0 +1,625 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/trackball.h"
+#include "android/skin/image.h"
+#include "android/utils/system.h"
+#include <math.h>
+
+/***********************************************************************/
+/***********************************************************************/
+/*****                                                             *****/
+/*****       T R A C K   B A L L                                   *****/
+/*****                                                             *****/
+/***********************************************************************/
+/***********************************************************************/
+
+// a 3-d vector
+typedef  double   VectorRec[3];
+typedef  double*  Vector;
+
+/* define FIX16_IS_FLOAT to use floats for computations */
+#define  FIX16_IS_FLOAT
+
+#ifdef FIX16_IS_FLOAT
+typedef float   Fix16;
+#define  FIX16_ONE           1.0
+#define  FIX16_FROM_FLOAT(x)  (x)
+#define  FIX16_TO_FLOAT(x)    (x)
+
+#else
+typedef int     Fix16;
+
+#define  FIX16_SHIFT  16
+#define  FIX16_ONE            (1 << FIX16_SHIFT)
+#define  FIX16_FROM_FLOAT(x)  (Fix16)((x) * FIX16_ONE)
+#define  FIX16_TO_FLOAT(x)    ((x)/(1.0*FIX16_ONE))
+
+#endif
+
+typedef Fix16   Fix16VectorRec[3];
+typedef Fix16*  Fix16Vector;
+
+static Fix16
+fixedvector_len( Fix16Vector  v )
+{
+    double  x = FIX16_TO_FLOAT(v[0]);
+    double  y = FIX16_TO_FLOAT(v[1]);
+    double  z = FIX16_TO_FLOAT(v[2]);
+    double  len = sqrt( x*x + y*y + z*z );
+
+    return FIX16_FROM_FLOAT(len);
+}
+
+static void
+fixedvector_from_vector( Fix16Vector  f, Vector  v )
+{
+    f[0] = FIX16_FROM_FLOAT(v[0]);
+    f[1] = FIX16_FROM_FLOAT(v[1]);
+    f[2] = FIX16_FROM_FLOAT(v[2]);
+}
+
+
+#ifdef FIX16_IS_FLOAT
+static double
+fixedvector_dot( Fix16Vector  u, Fix16Vector  v )
+{
+    return u[0]*v[0] + u[1]*v[1] + u[2]*v[2];
+}
+#else
+static Fix16
+fixedvector_dot( Fix16Vector  u, Fix16Vector  v )
+{
+    long long  t;
+
+    t = (long long)u[0] * v[0] + (long long)u[1] * v[1] + (long long)u[2] * v[2];
+    return (Fix16)(t >> FIX16_SHIFT);
+}
+#endif
+
+static int
+norm( int  dx, int  dy )
+{
+    return (int) sqrt( dx*1.0*dx + dy*1.0*dy );
+}
+
+/*** ROTATOR: used to rotate the reference axis when mouse motion happens
+ ***/
+
+typedef struct
+{
+    VectorRec   d;
+    VectorRec   n;
+    double      angle;
+
+} RotatorRec, *Rotator;
+
+
+#define  ANGLE_FACTOR  (M_PI/200)
+
+static void
+rotator_reset( Rotator  rot, int  dx, int  dy )
+{
+    double  len = sqrt( dx*dx + dy*dy );
+    double  zx, zy;
+
+    if (len < 1e-3 ) {
+        zx = 1.;
+        zy = 0;
+    } else {
+        zx = dx / len;
+        zy = dy / len;
+    }
+    rot->d[0] = zx;
+    rot->d[1] = zy;
+    rot->d[2] = 0.;
+
+    rot->n[0] = -rot->d[1];
+    rot->n[1] =  rot->d[0];
+    rot->n[2] = 0;
+
+    rot->angle = len * ANGLE_FACTOR;
+}
+
+static void
+rotator_apply( Rotator  rot, double*  vec )
+{
+    double   d, n, z, d2, z2, cs, sn;
+
+    /* project on D, N, Z */
+    d = vec[0]*rot->d[0] + vec[1]*rot->d[1];
+    n = vec[0]*rot->n[0] + vec[1]*rot->n[1];
+    z = vec[2];
+
+    /* rotate on D, Z */
+    cs = cos( rot->angle );
+    sn = sin( rot->angle );
+
+    d2 =  cs*d + sn*z;
+    z2 = -sn*d + cs*z;
+
+    /* project on X, Y, Z */
+    vec[0] = d2*rot->d[0] + n*rot->n[0];
+    vec[1] = d2*rot->d[1] + n*rot->n[1];
+    vec[2] = z2;
+}
+
+/*** TRACKBALL OBJECT
+ ***/
+typedef struct { int  x, y, offset, alpha; Fix16VectorRec  f; } SphereCoordRec, *SphereCoord;
+
+typedef struct SkinTrackBall
+{
+    int             diameter;
+    unsigned*       pixels;
+    SDL_Surface*    surface;
+    VectorRec       axes[3];  /* current ball axes */
+
+#define  DOT_GRID        3                        /* number of horizontal and vertical cells per side grid */
+#define  DOT_CELLS       2                        /* number of random dots per cell */
+#define  DOT_MAX         (6*DOT_GRID*DOT_GRID*DOT_CELLS)  /* total number of dots */
+#define  DOT_RANDOM_X    1007                     /* horizontal random range in each cell */
+#define  DOT_RANDOM_Y    1007                     /* vertical random range in each cell */
+
+#define  DOT_THRESHOLD  FIX16_FROM_FLOAT(0.17)
+
+    Fix16VectorRec   dots[ DOT_MAX ];
+
+    SphereCoordRec*  sphere_map;
+    int              sphere_count;
+
+    unsigned         ball_color;
+    unsigned         dot_color;
+    unsigned         ring_color;
+
+    Uint32           ticks_last;  /* ticks since last move */
+    int              acc_x;
+    int              acc_y;
+    int              acc_threshold;
+    double           acc_scale;
+
+    /* rotation applied to events send to the system */
+    SkinRotation     rotation;
+
+} TrackBallRec, *TrackBall;
+
+
+/* The following constants are used to better mimic a real trackball.
+ *
+ * ACC_THRESHOLD is used to filter small ball movements out.
+ * If the length of the relative mouse motion is smaller than this
+ * constant, then no corresponding ball event will be sent to the
+ * system.
+ *
+ * ACC_SCALE is used to scale the relative mouse motion vector into
+ * the corresponding ball motion vector.
+ */
+#define  ACC_THRESHOLD  20
+#define  ACC_SCALE      0.2
+
+static void
+trackball_init( TrackBall  ball, int  diameter, int  ring,
+                unsigned   ball_color, unsigned  dot_color,
+                unsigned   ring_color )
+{
+    int  diameter2 = diameter + ring*2;
+
+    memset( ball, 0, sizeof(*ball) );
+
+    ball->acc_threshold = ACC_THRESHOLD;
+    ball->acc_scale     = ACC_SCALE;
+
+    /* init SDL surface */
+    ball->diameter   = diameter2;
+    ball->ball_color = ball_color;
+    ball->dot_color  = dot_color;
+    ball->ring_color = ring_color;
+
+    ball->rotation   = SKIN_ROTATION_0;
+
+    ball->pixels   = (unsigned*)calloc( diameter2*diameter2, sizeof(unsigned) );
+    ball->surface  = sdl_surface_from_argb32( ball->pixels, diameter2, diameter2 );
+
+    /* init axes */
+    ball->axes[0][0] = 1.; ball->axes[0][1] = 0.; ball->axes[0][2] = 0.;
+    ball->axes[1][0] = 0.; ball->axes[1][1] = 1.; ball->axes[1][2] = 0.;
+    ball->axes[2][0] = 0.; ball->axes[2][1] = 0.; ball->axes[2][2] = 1.;
+
+    /* init dots */
+    {
+        int  side, nn = 0;
+
+        for (side = 0; side < 6; side++) {
+            VectorRec  origin, axis1, axis2;
+            int        xx, yy;
+
+            switch (side) {
+            case 0:
+                origin[0] = -1; origin[1] = -1; origin[2] = +1;
+                axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
+                axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
+                break;
+            case 1:
+                origin[0] = -1; origin[1] = -1; origin[2] = -1;
+                axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
+                axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
+                break;
+            case 2:
+                origin[0] = +1; origin[1] = -1; origin[2] = -1;
+                axis1 [0] =  0; axis1 [1] =  0; axis1 [2] =  1;
+                axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
+                break;
+            case 3:
+                origin[0] = -1; origin[1] = -1; origin[2] = -1;
+                axis1 [0] =  0; axis1 [1] =  0; axis1 [2] =  1;
+                axis2 [0] =  0; axis2 [1] =  1; axis2 [2] =  0;
+                break;
+            case 4:
+                origin[0] = -1; origin[1] = -1; origin[2] = -1;
+                axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
+                axis2 [0] =  0; axis2 [1] =  0; axis2 [2] =  1;
+                break;
+            default:
+                origin[0] = -1; origin[1] = +1; origin[2] = -1;
+                axis1 [0] =  1; axis1 [1] =  0; axis1 [2] =  0;
+                axis2 [0] =  0; axis2 [1] =  0; axis2 [2] =  1;
+            }
+
+            for (xx = 0; xx < DOT_GRID; xx++) {
+                double  tx = xx*(2./DOT_GRID);
+                for (yy = 0; yy < DOT_GRID; yy++) {
+                    double  ty = yy*(2./DOT_GRID);
+                    double  x0  = origin[0] + axis1[0]*tx + axis2[0]*ty;
+                    double  y0  = origin[1] + axis1[1]*tx + axis2[1]*ty;
+                    double  z0  = origin[2] + axis1[2]*tx + axis2[2]*ty;
+                    int     cc;
+                    for (cc = 0; cc < DOT_CELLS; cc++) {
+                        double  h = (rand() % DOT_RANDOM_X)/((double)DOT_RANDOM_X*DOT_GRID/2);
+                        double  v = (rand() % DOT_RANDOM_Y)/((double)DOT_RANDOM_Y*DOT_GRID/2);
+                        double  x = x0 + axis1[0]*h + axis2[0]*v;
+                        double  y = y0 + axis1[1]*h + axis2[1]*v;
+                        double  z = z0 + axis1[2]*h + axis2[2]*v;
+                        double  invlen = 1/sqrt( x*x + y*y + z*z );
+
+                        ball->dots[nn][0] = FIX16_FROM_FLOAT(x*invlen);
+                        ball->dots[nn][1] = FIX16_FROM_FLOAT(y*invlen);
+                        ball->dots[nn][2] = FIX16_FROM_FLOAT(z*invlen);
+                        nn++;
+                    }
+                }
+            }
+        }
+    }
+
+    /* init sphere */
+    {
+        int     diameter2 = diameter + 2*ring;
+        double  radius    = diameter*0.5;
+        double  radius2   = diameter2*0.5;
+        int     xx, yy;
+        int     empty = 0, total = 0;
+
+        ball->sphere_map = calloc( diameter2*diameter2, sizeof(SphereCoordRec) );
+
+        for (yy = 0; yy < diameter2; yy++) {
+            for (xx = 0; xx < diameter2; xx++) {
+                double       x0    = xx - radius2;
+                double       y0    = yy - radius2;
+                double       r0    = sqrt( x0*x0 + y0*y0 );
+                SphereCoord  coord = &ball->sphere_map[total];
+
+                if (r0 <= radius) {  /* ball pixel */
+                    double  rx = x0/radius;
+                    double  ry = y0/radius;
+                    double  rz = sqrt( 1.0 - rx*rx - ry*ry );
+
+                    coord->x      = xx;
+                    coord->y      = yy;
+                    coord->offset = xx + yy*diameter2;
+                    coord->alpha  = 256;
+                    coord->f[0]   = FIX16_FROM_FLOAT(rx);
+                    coord->f[1]   = FIX16_FROM_FLOAT(ry);
+                    coord->f[2]   = FIX16_FROM_FLOAT(rz);
+                    if (r0 >= radius-1.) {
+                        coord->alpha = 256*(radius - r0);
+                    }
+                    /* illumination model */
+                    {
+#define  LIGHT_X         -2.0
+#define  LIGHT_Y         -2.5
+#define  LIGHT_Z          5.0
+
+                        double  lx = LIGHT_X - rx;
+                        double  ly = LIGHT_Y - ry;
+                        double  lz = LIGHT_Z - rz;
+                        double  lir = 1/sqrt(lx*lx + ly*ly + lz*lz);
+                        double  cosphi = lir*(lx*rx + ly*ry + lz*rz);
+                        double  scale  = 1.1*cosphi + 0.3;
+
+                        if (scale < 0)
+                            scale = 0;
+
+                        coord->alpha = coord->alpha * scale;
+                    }
+                    total++;
+                } else if (r0 <= radius2) { /* ring pixel */
+                    coord->x      = xx;
+                    coord->y      = yy;
+                    coord->offset = xx + yy*diameter2;
+                    coord->alpha  = 0;
+                    if (r0 >= radius2-1.) {
+                        coord->alpha = -256*(r0 - (radius2-1.));
+                    }
+                    total++;
+
+                } else   /* outside pixel */
+                    empty++;
+            }
+        }
+        ball->sphere_count = total;
+    }
+}
+
+static int
+trackball_contains( TrackBall  ball, int  x, int  y )
+{
+    return ( (unsigned)(x) < (unsigned)ball->diameter &&
+             (unsigned)(y) < (unsigned)ball->diameter );
+}
+
+static void
+trackball_done( TrackBall  ball )
+{
+    free( ball->sphere_map );
+    ball->sphere_map   = NULL;
+    ball->sphere_count = 0;
+
+    if (ball->surface) {
+        SDL_FreeSurface( ball->surface );
+        ball->surface = NULL;
+    }
+
+    if (ball->pixels) {
+        free( ball->pixels );
+        ball->pixels = NULL;
+    }
+}
+
+/*** TRACKBALL SPHERE PIXELS
+ ***/
+static unsigned
+color_blend( unsigned  from, unsigned  to,  int  alpha )
+{
+    unsigned  from_ag    = (from >> 8) & 0x00ff00ff;
+    unsigned  to_ag      = (to >> 8) & 0x00ff00ff;
+    unsigned  from_rb    = from & 0x00ff00ff;
+    unsigned  to_rb      = to & 0x00ff00ff;
+    unsigned  result_ag = (from_ag + (alpha*(to_ag - from_ag) >> 8)) & 0xff00ff;
+    unsigned  result_rb = (from_rb + (alpha*(to_rb - from_rb) >> 8)) & 0xff00ff;
+
+    return (result_ag << 8) | result_rb;
+}
+
+static int
+trackball_move( TrackBall  ball,  int  dx, int  dy )
+{
+    RotatorRec  rot[1];
+    Uint32      now = SDL_GetTicks();
+
+    ball->acc_x += dx;
+    ball->acc_y += dy;
+
+    if ( norm( ball->acc_x, ball->acc_y ) > ball->acc_threshold )
+    {
+        int  ddx = ball->acc_x * ball->acc_scale;
+        int  ddy = ball->acc_y * ball->acc_scale;
+        int  ddt;
+
+        ball->acc_x = 0;
+        ball->acc_y = 0;
+
+        switch (ball->rotation) {
+        case SKIN_ROTATION_0:
+            break;
+
+        case SKIN_ROTATION_90:
+            ddt = ddx;
+            ddx = ddy;
+            ddy = -ddt;
+            break;
+
+        case SKIN_ROTATION_180:
+            ddx = -ddx;
+            ddy = -ddy;
+            break;
+
+        case SKIN_ROTATION_270:
+            ddt = ddx;
+            ddx = -ddy;
+            ddy = ddt;
+            break;
+        }
+
+        kbd_mouse_event(ddx, ddy, 1, 0);
+    }
+
+    rotator_reset( rot, dx, dy );
+    rotator_apply( rot, ball->axes[0] );
+    rotator_apply( rot, ball->axes[1] );
+    rotator_apply( rot, ball->axes[2] );
+
+    if ( ball->ticks_last == 0 )
+        ball->ticks_last = now;
+    else if ( now > ball->ticks_last + (1000/60) ) {
+        ball->ticks_last = now;
+        return 1;
+    }
+    return 0;
+}
+
+#define  BACK_COLOR   0x00000000
+#define  LIGHT_COLOR  0xffffffff
+
+static void
+trackball_refresh( TrackBall  ball )
+{
+    int             diameter = ball->diameter;
+    unsigned*       pixels   = ball->pixels;
+    Fix16VectorRec  faxes[3];
+    Fix16           dot_threshold = DOT_THRESHOLD * diameter;
+    int             nn;
+
+    SDL_LockSurface( ball->surface );
+
+    fixedvector_from_vector( (Fix16Vector)&faxes[0], (Vector)&ball->axes[0] );
+    fixedvector_from_vector( (Fix16Vector)&faxes[1], (Vector)&ball->axes[1] );
+    fixedvector_from_vector( (Fix16Vector)&faxes[2], (Vector)&ball->axes[2] );
+
+    for (nn = 0; nn < ball->sphere_count; nn++) {
+        SphereCoord  coord = &ball->sphere_map[nn];
+        unsigned     color = BACK_COLOR;
+
+        if (coord->alpha > 0) {
+            /* are we near one of the points ? */
+            Fix16  ax = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[0] );
+            Fix16  ay = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[1] );
+            Fix16  az = fixedvector_dot( (Fix16Vector)&coord->f, (Fix16Vector)&faxes[2] );
+
+            Fix16  best_dist = FIX16_ONE;
+            int    pp;
+
+            color = ball->ball_color;
+
+            for (pp = 0; pp < DOT_MAX; pp++) {
+                Fix16VectorRec  d;
+                Fix16           dist;
+
+                d[0] = ball->dots[pp][0] - ax;
+                d[1] = ball->dots[pp][1] - ay;
+                d[2] = ball->dots[pp][2] - az;
+
+                if (d[0] > dot_threshold || d[0] < -dot_threshold ||
+                    d[1] > dot_threshold || d[1] < -dot_threshold ||
+                    d[2] > dot_threshold || d[2] < -dot_threshold )
+                    continue;
+
+                dist = fixedvector_len( (Fix16Vector)&d );
+
+                if (dist < best_dist)
+                    best_dist = dist;
+            }
+            if (best_dist < DOT_THRESHOLD) {
+                int  a = 256*(DOT_THRESHOLD - best_dist) / DOT_THRESHOLD;
+                color = color_blend( color, ball->dot_color, a );
+            }
+
+            if (coord->alpha < 256) {
+                int  a = coord->alpha;
+                color = color_blend( ball->ring_color, color, a );
+            }
+            else if (coord->alpha > 256) {
+                int  a = (coord->alpha - 256);
+                color = color_blend( color, LIGHT_COLOR, a );
+            }
+        }
+        else /* coord->alpha <= 0 */
+        {
+            color = ball->ring_color;
+
+            if (coord->alpha < 0) {
+                int  a = -coord->alpha;
+                color = color_blend( color, BACK_COLOR, a );
+            }
+        }
+
+        pixels[coord->x + diameter*coord->y] = color;
+    }
+    SDL_UnlockSurface( ball->surface );
+}
+
+void
+trackball_draw( TrackBall  ball, int  x, int  y, SDL_Surface*  dst )
+{
+    SDL_Rect  d;
+
+    d.x = x;
+    d.y = y;
+    d.w = ball->diameter;
+    d.h = ball->diameter;
+
+    SDL_BlitSurface( ball->surface, NULL, dst, &d );
+    SDL_UpdateRects( dst, 1, &d );
+}
+
+
+SkinTrackBall*
+skin_trackball_create  ( SkinTrackBallParameters*  params )
+{
+    TrackBall  ball;
+
+    ANEW0(ball);
+    trackball_init( ball,
+                    params->diameter,
+                    params->ring,
+                    params->ball_color,
+                    params->dot_color,
+                    params->ring_color );
+    return  ball;
+}
+
+int
+skin_trackball_contains( SkinTrackBall*  ball, int  x, int  y )
+{
+    return  trackball_contains(ball, x, y);
+}
+
+int
+skin_trackball_move( SkinTrackBall*  ball, int  dx, int  dy )
+{
+    return  trackball_move(ball, dx, dy);
+}
+
+void
+skin_trackball_refresh ( SkinTrackBall*  ball )
+{
+    trackball_refresh(ball);
+}
+
+void
+skin_trackball_draw( SkinTrackBall*  ball, int  x, int  y, SDL_Surface*  dst )
+{
+    trackball_draw(ball, x, y, dst);
+}
+
+void
+skin_trackball_destroy ( SkinTrackBall*  ball )
+{
+    if (ball) {
+        trackball_done(ball);
+        AFREE(ball);
+    }
+}
+
+void
+skin_trackball_rect( SkinTrackBall*  ball, SDL_Rect*  rect )
+{
+    rect->x = 0;
+    rect->y = 0;
+    rect->w = ball->diameter;
+    rect->h = ball->diameter;
+}
+
+
+void
+skin_trackball_set_rotation( SkinTrackBall*  ball, SkinRotation  rotation )
+{
+    ball->rotation = rotation & 3;
+}
diff --git a/android/skin/trackball.h b/android/skin/trackball.h
new file mode 100644
index 0000000..06aa606
--- /dev/null
+++ b/android/skin/trackball.h
@@ -0,0 +1,43 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_SKIN_TRACKBALL_H
+#define _ANDROID_SKIN_TRACKBALL_H
+
+#include <SDL.h>
+#include "android/skin/rect.h"
+
+typedef struct SkinTrackBall  SkinTrackBall;
+
+typedef struct SkinTrackBallParameters
+{
+    int       diameter;
+    int       ring;
+    unsigned  ball_color;
+    unsigned  dot_color;
+    unsigned  ring_color;
+}
+SkinTrackBallParameters;
+
+
+extern SkinTrackBall*  skin_trackball_create  ( SkinTrackBallParameters*  params );
+extern void            skin_trackball_rect    ( SkinTrackBall*  ball, SDL_Rect*  rect );
+extern int             skin_trackball_contains( SkinTrackBall*  ball, int  x, int  y );
+extern int             skin_trackball_move    ( SkinTrackBall*  ball, int  dx, int  dy );
+extern void            skin_trackball_refresh ( SkinTrackBall*  ball );
+extern void            skin_trackball_draw    ( SkinTrackBall*  ball, int  x, int  y, SDL_Surface*  dst );
+extern void            skin_trackball_destroy ( SkinTrackBall*  ball );
+
+/* this sets the rotation that will be applied to mouse events sent to the system */
+extern void            skin_trackball_set_rotation( SkinTrackBall*  ball, SkinRotation  rotation);
+
+#endif /* END */
+
diff --git a/android/skin/window.c b/android/skin/window.c
new file mode 100644
index 0000000..6612ffe
--- /dev/null
+++ b/android/skin/window.c
@@ -0,0 +1,1516 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/skin/window.h"
+#include "android/skin/image.h"
+#include "android/skin/scaler.h"
+#include "android/charmap.h"
+#include "android/utils/debug.h"
+#include "android/utils/display.h"
+#include <SDL_syswm.h>
+#include "qemu-common.h"
+#include <math.h>
+
+#include "framebuffer.h"
+
+/* when shrinking, we reduce the pixel ratio by this fixed amount */
+#define  SHRINK_SCALE  0.6
+
+/* maximum value of LCD brighness */
+#define  LCD_BRIGHTNESS_MIN      0
+#define  LCD_BRIGHTNESS_DEFAULT  128
+#define  LCD_BRIGHTNESS_MAX      255
+
+typedef struct Background {
+    SkinImage*   image;
+    SkinRect     rect;
+    SkinPos      origin;
+} Background;
+
+static void
+background_done( Background*  back )
+{
+    skin_image_unref( &back->image );
+}
+
+static void
+background_init( Background*  back, SkinBackground*  sback, SkinLocation*  loc, SkinRect*  frame )
+{
+    SkinRect  r;
+
+    back->image = skin_image_rotate( sback->image, loc->rotation );
+    skin_rect_rotate( &r, &sback->rect, loc->rotation );
+    r.pos.x += loc->anchor.x;
+    r.pos.y += loc->anchor.y;
+
+    back->origin = r.pos;
+    skin_rect_intersect( &back->rect, &r, frame );
+}
+
+static void
+background_redraw( Background*  back, SkinRect*  rect, SDL_Surface*  surface )
+{
+    SkinRect  r;
+
+    if (skin_rect_intersect( &r, rect, &back->rect ) )
+    {
+        SDL_Rect  rd, rs;
+
+        rd.x = r.pos.x;
+        rd.y = r.pos.y;
+        rd.w = r.size.w;
+        rd.h = r.size.h;
+
+        rs.x = r.pos.x - back->origin.x;
+        rs.y = r.pos.y - back->origin.y;
+        rs.w = r.size.w;
+        rs.h = r.size.h;
+
+        SDL_BlitSurface( skin_image_surface(back->image), &rs, surface, &rd );
+        //SDL_UpdateRects( surface, 1, &rd );
+    }
+}
+
+
+typedef struct ADisplay {
+    SkinRect       rect;
+    SkinPos        origin;
+    SkinRotation   rotation;
+    SkinSize       datasize;  /* framebuffer size */
+    void*          data;      /* framebuffer pixels */
+    QFrameBuffer*  qfbuff;
+    SkinImage*     onion;       /* onion image */
+    SkinRect       onion_rect;  /* onion rect, if any */
+    int            brightness;
+} ADisplay;
+
+static void
+display_done( ADisplay*  disp )
+{
+    disp->data   = NULL;
+    disp->qfbuff = NULL;
+    skin_image_unref( &disp->onion );
+}
+
+static int
+display_init( ADisplay*  disp, SkinDisplay*  sdisp, SkinLocation*  loc, SkinRect*  frame )
+{
+    skin_rect_rotate( &disp->rect, &sdisp->rect, loc->rotation );
+    disp->rect.pos.x += loc->anchor.x;
+    disp->rect.pos.y += loc->anchor.y;
+
+    disp->rotation = (loc->rotation + sdisp->rotation) & 3;
+    switch (disp->rotation) {
+        case SKIN_ROTATION_0:
+            disp->origin = disp->rect.pos;
+            break;
+
+        case SKIN_ROTATION_90:
+            disp->origin.x = disp->rect.pos.x + disp->rect.size.w;
+            disp->origin.y = disp->rect.pos.y;
+            break;
+
+        case SKIN_ROTATION_180:
+            disp->origin.x = disp->rect.pos.x + disp->rect.size.w;
+            disp->origin.y = disp->rect.pos.y + disp->rect.size.h;
+            break;
+
+        default:
+            disp->origin.x = disp->rect.pos.x;
+            disp->origin.y = disp->rect.pos.y + disp->rect.size.h;
+            break;
+    }
+    skin_size_rotate( &disp->datasize, &sdisp->rect.size, sdisp->rotation );
+    skin_rect_intersect( &disp->rect, &disp->rect, frame );
+#if 0
+    fprintf(stderr, "... display_init  rect.pos(%d,%d) rect.size(%d,%d) datasize(%d,%d)\n",
+                    disp->rect.pos.x, disp->rect.pos.y,
+                    disp->rect.size.w, disp->rect.size.h,
+                    disp->datasize.w, disp->datasize.h);
+#endif
+    disp->qfbuff = sdisp->qfbuff;
+    disp->data   = sdisp->qfbuff->pixels;
+    disp->onion  = NULL;
+
+    disp->brightness = LCD_BRIGHTNESS_DEFAULT;
+
+    return (disp->data == NULL) ? -1 : 0;
+}
+
+static __inline__ uint32_t  rgb565_to_argb32( uint32_t  pix )
+{
+    uint32_t  r = ((pix & 0xf800) << 8) | ((pix & 0xe000) << 3);
+    uint32_t  g = ((pix & 0x07e0) << 5) | ((pix & 0x0600) >> 1);
+    uint32_t  b = ((pix & 0x001f) << 3) | ((pix & 0x001c) >> 2);
+
+    return 0xff000000 | r | g | b;
+}
+
+
+static void
+display_set_onion( ADisplay*  disp, SkinImage*  onion, SkinRotation  rotation, int  blend )
+{
+    int        onion_w, onion_h;
+    SkinRect*  rect  = &disp->rect;
+    SkinRect*  orect = &disp->onion_rect;
+
+    rotation = (rotation + disp->rotation) & 3;
+
+    skin_image_unref( &disp->onion );
+    disp->onion = skin_image_clone_full( onion, rotation, blend );
+
+    onion_w = skin_image_w(disp->onion);
+    onion_h = skin_image_h(disp->onion);
+
+    switch (rotation) {
+        case SKIN_ROTATION_0:
+            orect->pos = rect->pos;
+            break;
+
+        case SKIN_ROTATION_90:
+            orect->pos.x = rect->pos.x + rect->size.w - onion_w;
+            orect->pos.y = rect->pos.y;
+            break;
+
+        case SKIN_ROTATION_180:
+            orect->pos.x = rect->pos.x + rect->size.w - onion_w;
+            orect->pos.y = rect->pos.y + rect->size.h - onion_h;
+            break;
+
+        default:
+            orect->pos.x = rect->pos.x;
+            orect->pos.y = rect->pos.y + rect->size.h - onion_h;
+    }
+    orect->size.w = onion_w;
+    orect->size.h = onion_h;
+}
+
+#define  DOT_MATRIX  0
+
+#if DOT_MATRIX
+
+static void
+dotmatrix_dither_argb32( unsigned char*  pixels, int  x, int  y, int  w, int  h, int  pitch )
+{
+    static const unsigned dotmatrix_argb32[16] = {
+        0x003f00, 0x00003f, 0x3f0000, 0x000000,
+        0x3f3f3f, 0x000000, 0x3f3f3f, 0x000000,
+        0x3f0000, 0x000000, 0x003f00, 0x00003f,
+        0x3f3f3f, 0x000000, 0x3f3f3f, 0x000000
+    };
+
+    int   yy = y & 3;
+
+    pixels += 4*x + y*pitch;
+
+    for ( ; h > 0; h-- ) {
+        unsigned*  line = (unsigned*) pixels;
+        int        nn, xx = x & 3;
+
+        for (nn = 0; nn < w; nn++) {
+            unsigned  c = line[nn];
+
+            c = c - ((c >> 2) & dotmatrix_argb32[(yy << 2)|xx]);
+
+            xx = (xx + 1) & 3;
+            line[nn] = c;
+        }
+
+        yy      = (yy + 1) & 3;
+        pixels += pitch;
+    }
+}
+
+#endif /* DOT_MATRIX */
+
+/* technical note about the lightness emulation
+ *
+ * we try to emulate something that looks like the Dream's
+ * non-linear LCD lightness, without going too dark or bright.
+ *
+ * the default lightness is around 105 (about 40%) and we prefer
+ * to keep full RGB colors at that setting, to not alleviate
+ * developers who will not understand why the emulator's colors
+ * look slightly too dark.
+ *
+ * we also want to implement a 'bright' mode by de-saturating
+ * colors towards bright white.
+ *
+ * All of this leads to the implementation below that looks like
+ * the following:
+ *
+ * if (level == MIN)
+ *     screen is off
+ *
+ * if (level > MIN && level < LOW)
+ *     interpolate towards black, with
+ *     MINALPHA = 0.2
+ *     alpha = MINALPHA + (1-MINALPHA)*(level-MIN)/(LOW-MIN)
+ *
+ * if (level >= LOW && level <= HIGH)
+ *     keep full RGB colors
+ *
+ * if (level > HIGH)
+ *     interpolate towards bright white, with
+ *     MAXALPHA = 0.6
+ *     alpha = MAXALPHA*(level-HIGH)/(MAX-HIGH)
+ *
+ * we probably want some sort of power law instead of interpolating
+ * linearly, but frankly, this is sufficient for most uses.
+ */
+
+#define  LCD_BRIGHTNESS_LOW   80
+#define  LCD_BRIGHTNESS_HIGH  180
+
+#define  LCD_ALPHA_LOW_MIN      0.2
+#define  LCD_ALPHA_HIGH_MAX     0.6
+
+/* treat as special value to turn screen off */
+#define  LCD_BRIGHTNESS_OFF   LCD_BRIGHTNESS_MIN
+
+static void
+lcd_brightness_argb32( unsigned char*  pixels, SkinRect*  r, int  pitch, int  brightness )
+{
+    const unsigned  b_min  = LCD_BRIGHTNESS_MIN;
+    const unsigned  b_max  = LCD_BRIGHTNESS_MAX;
+    const unsigned  b_low  = LCD_BRIGHTNESS_LOW;
+    const unsigned  b_high = LCD_BRIGHTNESS_HIGH;
+
+    unsigned        alpha = brightness;
+    int             w     = r->size.w;
+    int             h     = r->size.h;
+
+    if (alpha <= b_min)
+        alpha = b_min;
+    else if (alpha > b_max)
+        alpha = b_max;
+
+    pixels += 4*r->pos.x + r->pos.y*pitch;
+
+    if (alpha < b_low)
+    {
+        const unsigned  alpha_min   = (255*LCD_ALPHA_LOW_MIN);
+        const unsigned  alpha_range = (255 - alpha_min);
+
+        alpha = alpha_min + ((alpha - b_min)*alpha_range) / (b_low - b_min);
+
+        for ( ; h > 0; h-- ) {
+            unsigned*  line = (unsigned*) pixels;
+            int        nn;
+
+            for (nn = 0; nn < w; nn++) {
+                unsigned  c  = line[nn];
+                unsigned  ag = (c >> 8) & 0x00ff00ff;
+                unsigned  rb = (c)      & 0x00ff00ff;
+
+                ag = (ag*alpha)        & 0xff00ff00;
+                rb = ((rb*alpha) >> 8) & 0x00ff00ff;
+
+                line[nn] = (unsigned)(ag | rb);
+            }
+            pixels += pitch;
+        }
+    }
+    else if (alpha > LCD_BRIGHTNESS_HIGH) /* 'superluminous' mode */
+    {
+        const unsigned  alpha_max   = (255*LCD_ALPHA_HIGH_MAX);
+        const unsigned  alpha_range = (255-alpha_max);
+        unsigned        ialpha;
+
+        alpha  = ((alpha - b_high)*alpha_range) / (b_max - b_high);
+        ialpha = 255-alpha;
+
+        for ( ; h > 0; h-- ) {
+            unsigned*  line = (unsigned*) pixels;
+            int        nn;
+
+            for (nn = 0; nn < w; nn++) {
+                unsigned  c  = line[nn];
+                unsigned  ag = (c >> 8) & 0x00ff00ff;
+                unsigned  rb = (c)      & 0x00ff00ff;
+
+                /* interpolate towards bright white, i.e. 0x00ffffff */
+                ag = ((ag*ialpha + 0x00ff00ff*alpha)) & 0xff00ff00;
+                rb = ((rb*ialpha + 0x00ff00ff*alpha) >> 8) & 0x00ff00ff;
+
+                line[nn] = (unsigned)(ag | rb);
+            }
+            pixels += pitch;
+        }
+    }
+}
+
+
+/* this is called when the LCD framebuffer is off */
+static void
+lcd_off_argb32( unsigned char*  pixels, SkinRect*  r, int  pitch )
+{
+    int  x = r->pos.x;
+    int  y = r->pos.y;
+    int  w = r->size.w;
+    int  h = r->size.h;
+
+    pixels += 4*x + y*pitch;
+    for ( ; h > 0; h-- ) {
+        memset( pixels, 0, w*4 );
+        pixels += pitch;
+    }
+}
+
+
+static void
+display_redraw( ADisplay*  disp, SkinRect*  rect, SDL_Surface*  surface )
+{
+    SkinRect  r;
+
+    if (skin_rect_intersect( &r, rect, &disp->rect ))
+    {
+        int           x  = r.pos.x - disp->rect.pos.x;
+        int           y  = r.pos.y - disp->rect.pos.y;
+        int           w  = r.size.w;
+        int           h  = r.size.h;
+        int           disp_w    = disp->rect.size.w;
+        int           disp_h    = disp->rect.size.h;
+        int           dst_pitch = surface->pitch;
+        uint8_t*      dst_line  = (uint8_t*)surface->pixels + r.pos.x*4 + r.pos.y*dst_pitch;
+        int           src_pitch = disp->datasize.w*2;
+        uint8_t*      src_line  = (uint8_t*)disp->data;
+        int           yy, xx;
+#if 0
+        fprintf(stderr, "--- display redraw r.pos(%d,%d) r.size(%d,%d) "
+                        "disp.pos(%d,%d) disp.size(%d,%d) datasize(%d,%d) rect.pos(%d,%d) rect.size(%d,%d)\n",
+                        r.pos.x - disp->rect.pos.x, r.pos.y - disp->rect.pos.y, w, h, disp->rect.pos.x, disp->rect.pos.y,
+                        disp->rect.size.w, disp->rect.size.h, disp->datasize.w, disp->datasize.h,
+                        rect->pos.x, rect->pos.y, rect->size.w, rect->size.h );
+#endif
+        SDL_LockSurface( surface );
+
+        if (disp->brightness == LCD_BRIGHTNESS_OFF)
+        {
+            lcd_off_argb32( surface->pixels, &r, dst_pitch );
+        }
+        else
+        {
+            switch ( disp->rotation & 3 )
+            {
+            case ANDROID_ROTATION_0:
+                src_line += x*2 + y*src_pitch;
+
+                for (yy = h; yy > 0; yy--)
+                {
+                    uint32_t*  dst = (uint32_t*)dst_line;
+                    uint16_t*  src = (uint16_t*)src_line;
+
+                    for (xx = 0; xx < w; xx++) {
+                        dst[xx] = rgb565_to_argb32(src[xx]);
+                    }
+                    src_line += src_pitch;
+                    dst_line += dst_pitch;
+                }
+                break;
+
+            case ANDROID_ROTATION_90:
+                src_line += y*2 + (disp_w - x - 1)*src_pitch;
+
+                for (yy = h; yy > 0; yy--)
+                {
+                    uint32_t*  dst = (uint32_t*)dst_line;
+                    uint8_t*   src = src_line;
+
+                    for (xx = w; xx > 0; xx--)
+                    {
+                        dst[0] = rgb565_to_argb32(((uint16_t*)src)[0]);
+                        src -= src_pitch;
+                        dst += 1;
+                    }
+                    src_line += 2;
+                    dst_line += dst_pitch;
+                }
+                break;
+
+            case ANDROID_ROTATION_180:
+                src_line += (disp_w -1 - x)*2 + (disp_h-1-y)*src_pitch;
+
+                for (yy = h; yy > 0; yy--)
+                {
+                    uint16_t*  src = (uint16_t*)src_line;
+                    uint32_t*  dst = (uint32_t*)dst_line;
+
+                    for (xx = w; xx > 0; xx--) {
+                        dst[0] = rgb565_to_argb32(src[0]);
+                        src -= 1;
+                        dst += 1;
+                    }
+
+                    src_line -= src_pitch;
+                    dst_line += dst_pitch;
+            }
+            break;
+
+            default:  /* ANDROID_ROTATION_270 */
+                src_line += (disp_h-1-y)*2 + x*src_pitch;
+
+                for (yy = h; yy > 0; yy--)
+                {
+                    uint32_t*  dst = (uint32_t*)dst_line;
+                    uint8_t*   src = src_line;
+
+                    for (xx = w; xx > 0; xx--) {
+                        dst[0] = rgb565_to_argb32(((uint16_t*)src)[0]);
+                        dst   += 1;
+                        src   += src_pitch;
+                    }
+                    src_line -= 2;
+                    dst_line += dst_pitch;
+                }
+            }
+#if DOT_MATRIX
+            dotmatrix_dither_argb32( surface->pixels, r.pos.x, r.pos.y, r.size.w, r.size.h, surface->pitch );
+#endif
+            /* apply lightness */
+            lcd_brightness_argb32( surface->pixels, &r, surface->pitch, disp->brightness );
+        }
+        SDL_UnlockSurface( surface );
+
+        /* Apply onion skin */
+        if (disp->onion != NULL) {
+            SkinRect  r2;
+
+            if ( skin_rect_intersect( &r2, &r, &disp->onion_rect ) ) {
+                SDL_Rect  rs, rd;
+
+                rd.x = r2.pos.x;
+                rd.y = r2.pos.y;
+                rd.w = r2.size.w;
+                rd.h = r2.size.h;
+
+                rs.x = rd.x - disp->onion_rect.pos.x;
+                rs.y = rd.y - disp->onion_rect.pos.y;
+                rs.w = rd.w;
+                rs.h = rd.h;
+
+                SDL_BlitSurface( skin_image_surface(disp->onion), &rs, surface, &rd );
+            }
+        }
+
+        SDL_UpdateRect( surface, r.pos.x, r.pos.y, w, h );
+    }
+}
+
+
+typedef struct Button {
+    SkinImage*       image;
+    SkinRect         rect;
+    SkinPos          origin;
+    Background*      background;
+    unsigned         keycode;
+    int              down;
+} Button;
+
+static void
+button_done( Button*  button )
+{
+    skin_image_unref( &button->image );
+    button->background = NULL;
+}
+
+static void
+button_init( Button*  button, SkinButton*  sbutton, SkinLocation*  loc, Background*  back, SkinRect*  frame )
+{
+    SkinRect  r;
+
+    button->image      = skin_image_rotate( sbutton->image, loc->rotation );
+    button->background = back;
+    button->keycode    = sbutton->keycode;
+    button->down       = 0;
+
+    skin_rect_rotate( &r, &sbutton->rect, loc->rotation );
+    r.pos.x += loc->anchor.x;
+    r.pos.y += loc->anchor.y;
+    button->origin = r.pos;
+    skin_rect_intersect( &button->rect, &r, frame );
+}
+
+static void
+button_redraw( Button*  button, SkinRect*  rect, SDL_Surface*  surface )
+{
+    SkinRect  r;
+
+    if (skin_rect_intersect( &r, rect, &button->rect ))
+    {
+        if ( button->down && button->image != SKIN_IMAGE_NONE )
+        {
+            SDL_Rect  rs, rd;
+
+            rs.x = r.pos.x - button->origin.x;
+            rs.y = r.pos.y - button->origin.y;
+            rs.w = r.size.w;
+            rs.h = r.size.h;
+
+            rd.x = r.pos.x;
+            rd.y = r.pos.y;
+            rd.w = r.size.w;
+            rd.h = r.size.h;
+
+            if (button->image != SKIN_IMAGE_NONE) {
+                SDL_BlitSurface( skin_image_surface(button->image), &rs, surface, &rd );
+                if (button->down > 1)
+                    SDL_BlitSurface( skin_image_surface(button->image), &rs, surface, &rd );
+            }
+        }
+    }
+}
+
+
+typedef struct {
+    char      tracking;
+    char      inside;
+    SkinPos   pos;
+    ADisplay*  display;
+} FingerState;
+
+static void
+finger_state_reset( FingerState*  finger )
+{
+    finger->tracking = 0;
+    finger->inside   = 0;
+}
+
+typedef struct {
+    Button*   pressed;
+    Button*   hover;
+} ButtonState;
+
+static void
+button_state_reset( ButtonState*  button )
+{
+    button->pressed = NULL;
+    button->hover   = NULL;
+}
+
+typedef struct {
+    char            tracking;
+    SkinTrackBall*  ball;
+    SkinRect        rect;
+    SkinWindow*     window;
+} BallState;
+
+static void
+ball_state_reset( BallState*  state, SkinWindow*  window )
+{
+    state->tracking = 0;
+    state->ball     = NULL;
+
+    state->rect.pos.x  = 0;
+    state->rect.pos.y  = 0;
+    state->rect.size.w = 0;
+    state->rect.size.h = 0;
+    state->window      = window;
+}
+
+static void
+ball_state_redraw( BallState*  state, SkinRect*  rect, SDL_Surface*  surface )
+{
+    SkinRect  r;
+
+    if (skin_rect_intersect( &r, rect, &state->rect ))
+        skin_trackball_draw( state->ball, 0, 0, surface );
+}
+
+static void
+ball_state_show( BallState*  state, int  enable )
+{
+    if (enable) {
+        if ( !state->tracking ) {
+            state->tracking = 1;
+            SDL_ShowCursor(0);
+            SDL_WM_GrabInput( SDL_GRAB_ON );
+            skin_trackball_refresh( state->ball );
+            skin_window_redraw( state->window, &state->rect );
+        }
+    } else {
+        if ( state->tracking ) {
+            state->tracking = 0;
+            SDL_WM_GrabInput( SDL_GRAB_OFF );
+            SDL_ShowCursor(1);
+            skin_window_redraw( state->window, &state->rect );
+        }
+    }
+}
+
+
+static void
+ball_state_set( BallState*  state, SkinTrackBall*  ball )
+{
+    ball_state_show( state, 0 );
+
+    state->ball = ball;
+    if (ball != NULL) {
+        SDL_Rect  sr;
+
+        skin_trackball_rect( ball, &sr );
+        state->rect.pos.x  = sr.x;
+        state->rect.pos.y  = sr.y;
+        state->rect.size.w = sr.w;
+        state->rect.size.h = sr.h;
+    }
+}
+
+typedef struct Layout {
+    int          num_buttons;
+    int          num_backgrounds;
+    int          num_displays;
+    unsigned     color;
+    Button*      buttons;
+    Background*  backgrounds;
+    ADisplay*    displays;
+    SkinRect     rect;
+    SkinLayout*  slayout;
+} Layout;
+
+#define  LAYOUT_LOOP_BUTTONS(layout,button)                          \
+    do {                                                             \
+        Button*  __button = (layout)->buttons;                       \
+        Button*  __button_end = __button + (layout)->num_buttons;    \
+        for ( ; __button < __button_end; __button ++ ) {             \
+            Button*  button = __button;
+
+#define  LAYOUT_LOOP_END_BUTTONS \
+        }                        \
+    } while (0);
+
+#define  LAYOUT_LOOP_DISPLAYS(layout,display)                          \
+    do {                                                               \
+        ADisplay*  __display = (layout)->displays;                     \
+        ADisplay*  __display_end = __display + (layout)->num_displays; \
+        for ( ; __display < __display_end; __display ++ ) {            \
+            ADisplay*  display = __display;
+
+#define  LAYOUT_LOOP_END_DISPLAYS \
+        }                         \
+    } while (0);
+
+
+static void
+layout_done( Layout*  layout )
+{
+    int  nn;
+
+    for (nn = 0; nn < layout->num_buttons; nn++)
+        button_done( &layout->buttons[nn] );
+
+    for (nn = 0; nn < layout->num_backgrounds; nn++)
+        background_done( &layout->backgrounds[nn] );
+
+    for (nn = 0; nn < layout->num_displays; nn++)
+        display_done( &layout->displays[nn] );
+
+    qemu_free( layout->buttons );
+    layout->buttons = NULL;
+
+    qemu_free( layout->backgrounds );
+    layout->backgrounds = NULL;
+
+    qemu_free( layout->displays );
+    layout->displays = NULL;
+
+    layout->num_buttons     = 0;
+    layout->num_backgrounds = 0;
+    layout->num_displays    = 0;
+}
+
+static int
+layout_init( Layout*  layout, SkinLayout*  slayout )
+{
+    int       n_buttons, n_backgrounds, n_displays;
+
+    /* first, count the number of elements of each kind */
+    n_buttons     = 0;
+    n_backgrounds = 0;
+    n_displays    = 0;
+
+    layout->color   = slayout->color;
+    layout->slayout = slayout;
+
+    SKIN_LAYOUT_LOOP_LOCS(slayout,loc)
+        SkinPart*    part = loc->part;
+
+        if ( part->background->valid )
+            n_backgrounds += 1;
+        if ( part->display->valid )
+            n_displays += 1;
+
+        SKIN_PART_LOOP_BUTTONS(part, sbutton)
+            n_buttons += 1;
+            sbutton=sbutton;
+        SKIN_PART_LOOP_END
+    SKIN_LAYOUT_LOOP_END
+
+    layout->num_buttons     = n_buttons;
+    layout->num_backgrounds = n_backgrounds;
+    layout->num_displays    = n_displays;
+
+    /* now allocate arrays, then populate them */
+    layout->buttons     = qemu_mallocz( sizeof(Button) *     n_buttons );
+    layout->backgrounds = qemu_mallocz( sizeof(Background) * n_backgrounds );
+    layout->displays    = qemu_mallocz( sizeof(ADisplay) *    n_displays );
+
+    if (layout->buttons == NULL && n_buttons > 0) goto Fail;
+    if (layout->backgrounds == NULL && n_backgrounds > 0) goto Fail;
+    if (layout->displays == NULL && n_displays > 0) goto Fail;
+
+    n_buttons     = 0;
+    n_backgrounds = 0;
+    n_displays    = 0;
+
+    layout->rect.pos.x = 0;
+    layout->rect.pos.y = 0;
+    layout->rect.size  = slayout->size;
+
+    SKIN_LAYOUT_LOOP_LOCS(slayout,loc)
+        SkinPart*    part = loc->part;
+        Background*  back = NULL;
+
+        if ( part->background->valid ) {
+            back = layout->backgrounds + n_backgrounds;
+            background_init( back, part->background, loc, &layout->rect );
+            n_backgrounds += 1;
+        }
+        if ( part->display->valid ) {
+            ADisplay*  disp = layout->displays + n_displays;
+            display_init( disp, part->display, loc, &layout->rect );
+            n_displays += 1;
+        }
+
+        SKIN_PART_LOOP_BUTTONS(part, sbutton)
+            Button*  button = layout->buttons + n_buttons;
+            button_init( button, sbutton, loc, back, &layout->rect );
+            n_buttons += 1;
+        SKIN_PART_LOOP_END
+    SKIN_LAYOUT_LOOP_END
+
+    return 0;
+
+Fail:
+    layout_done(layout);
+    return -1;
+}
+
+struct SkinWindow {
+    SDL_Surface*  surface;
+    Layout        layout;
+    SkinPos       pos;
+    FingerState   finger;
+    ButtonState   button;
+    BallState     ball;
+    char          enabled;
+    char          fullscreen;
+    char          no_display;
+
+    char          enable_touch;
+    char          enable_trackball;
+    char          enable_dpad;
+    char          enable_qwerty;
+
+    SkinImage*    onion;
+    SkinRotation  onion_rotation;
+    int           onion_alpha;
+
+    int           x_pos;
+    int           y_pos;
+
+    SkinScaler*   scaler;
+    int           shrink;
+    double        shrink_scale;
+    unsigned*     shrink_pixels;
+    SDL_Surface*  shrink_surface;
+
+    double        effective_scale;
+    double        effective_x;
+    double        effective_y;
+};
+
+static void
+add_finger_event(unsigned x, unsigned y, unsigned state)
+{
+    //fprintf(stderr, "::: finger %d,%d %d\n", x, y, state);
+    kbd_mouse_event(x, y, 0, state);
+}
+
+static void
+skin_window_find_finger( SkinWindow*  window,
+                         int          x,
+                         int          y )
+{
+    FingerState*  finger = &window->finger;
+
+    /* find the display that contains this movement */
+    finger->display = NULL;
+    finger->inside  = 0;
+
+    if (!window->enable_touch)
+        return;
+
+    LAYOUT_LOOP_DISPLAYS(&window->layout,disp)
+        if ( skin_rect_contains( &disp->rect, x, y ) ) {
+            finger->inside   = 1;
+            finger->display  = disp;
+            finger->pos.x    = x - disp->origin.x;
+            finger->pos.y    = y - disp->origin.y;
+
+            skin_pos_rotate( &finger->pos, &finger->pos, -disp->rotation );
+            break;
+        }
+    LAYOUT_LOOP_END_DISPLAYS
+}
+
+static void
+skin_window_move_mouse( SkinWindow*  window,
+                        int          x,
+                        int          y )
+{
+    FingerState*  finger = &window->finger;
+    ButtonState*  button = &window->button;
+
+    if (finger->tracking) {
+        ADisplay*  disp   = finger->display;
+        char       inside = 1;
+        int        dx     = x - disp->rect.pos.x;
+        int        dy     = y - disp->rect.pos.y;
+
+        if (dx < 0) {
+            dx = 0;
+            inside = 0;
+        }
+        else if (dx >= disp->rect.size.w) {
+            dx = disp->rect.size.w - 1;
+            inside = 0;
+        }
+        if (dy < 0) {
+            dy = 0;
+            inside = 0;
+        } else if (dy >= disp->rect.size.h) {
+            dy = disp->rect.size.h-1;
+            inside = 0;
+        }
+        finger->inside = inside;
+        finger->pos.x  = dx + (disp->rect.pos.x - disp->origin.x);
+        finger->pos.y  = dy + (disp->rect.pos.y - disp->origin.y);
+
+        skin_pos_rotate( &finger->pos, &finger->pos, -disp->rotation );
+    }
+
+    {
+        Button*  hover = button->hover;
+
+        if (hover) {
+            if ( skin_rect_contains( &hover->rect, x, y ) )
+                return;
+
+            hover->down = 0;
+            skin_window_redraw( window, &hover->rect );
+            button->hover = NULL;
+        }
+
+        hover = NULL;
+        LAYOUT_LOOP_BUTTONS( &window->layout, butt )
+            if ( skin_rect_contains( &butt->rect, x, y ) ) {
+                hover = butt;
+                break;
+            }
+        LAYOUT_LOOP_END_BUTTONS
+
+        /* filter DPAD and QWERTY buttons right here */
+        if (hover != NULL) {
+            switch (hover->keycode) {
+                /* these correspond to the DPad */
+                case kKeyCodeDpadUp:
+                case kKeyCodeDpadDown:
+                case kKeyCodeDpadLeft:
+                case kKeyCodeDpadRight:
+                case kKeyCodeDpadCenter:
+                    if (!window->enable_dpad)
+                        hover = NULL;
+                    break;
+
+                /* these correspond to non-qwerty buttons */
+                case kKeyCodeSoftLeft:
+                case kKeyCodeSoftRight:
+                case kKeyCodeVolumeUp:
+                case kKeyCodeVolumeDown:
+                case kKeyCodePower:
+                case kKeyCodeHome:
+                case kKeyCodeBack:
+                case kKeyCodeCall:
+                case kKeyCodeEndCall:
+                    break;
+
+                /* all the rest is assumed to be qwerty */
+                default:
+                    if (!window->enable_qwerty)
+                        hover = NULL;
+            }
+        }
+
+        if (hover != NULL) {
+            hover->down = 1;
+            skin_window_redraw( window, &hover->rect );
+            button->hover = hover;
+        }
+    }
+}
+
+static void
+skin_window_trackball_press( SkinWindow*  window, int  down )
+{
+    send_key_event( kKeyCodeBtnMouse, down );
+}
+
+static void
+skin_window_trackball_move( SkinWindow*  window, int  xrel, int  yrel )
+{
+    BallState*  state = &window->ball;
+
+    if ( skin_trackball_move( state->ball, xrel, yrel ) ) {
+        skin_trackball_refresh( state->ball );
+        skin_window_redraw( window, &state->rect );
+    }
+}
+
+void
+skin_window_set_trackball( SkinWindow*  window, SkinTrackBall*  ball )
+{
+    BallState*  state = &window->ball;
+
+    ball_state_set( state, ball );
+}
+
+void
+skin_window_show_trackball( SkinWindow*  window, int  enable )
+{
+    BallState*  state = &window->ball;
+
+    if (state->ball != NULL && window->enable_trackball) {
+        ball_state_show(state, enable);
+    }
+}
+
+
+static int  skin_window_reset_internal (SkinWindow*, SkinLayout*);
+
+SkinWindow*
+skin_window_create( SkinLayout*  slayout, int  x, int  y, double  scale, int  no_display )
+{
+    SkinWindow*  window = qemu_mallocz(sizeof(*window));
+
+    window->shrink_scale = scale;
+    window->shrink       = (scale != 1.0);
+    window->scaler       = skin_scaler_create();
+    window->no_display   = no_display;
+
+    /* enable everything by default */
+    window->enable_touch     = 1;
+    window->enable_trackball = 1;
+    window->enable_dpad      = 1;
+    window->enable_qwerty    = 1;
+
+    window->x_pos = x;
+    window->y_pos = y;
+
+    if (skin_window_reset_internal(window, slayout) < 0) {
+        skin_window_free( window );
+        return NULL;
+    }
+    //SDL_WM_SetCaption( "Android Emulator", "Android Emulator" );
+
+    SDL_WM_SetPos( x, y );
+    if ( !SDL_WM_IsFullyVisible( 1 ) ) {
+        dprint( "emulator window was out of view and was recentred\n" );
+    }
+
+    return window;
+}
+
+void
+skin_window_enable_touch( SkinWindow*  window, int  enabled )
+{
+    window->enable_touch = !!enabled;
+}
+
+void
+skin_window_enable_trackball( SkinWindow*  window, int  enabled )
+{
+    window->enable_trackball = !!enabled;
+}
+
+void
+skin_window_enable_dpad( SkinWindow*  window, int  enabled )
+{
+    window->enable_dpad = !!enabled;
+}
+
+void
+skin_window_enable_qwerty( SkinWindow*  window, int  enabled )
+{
+    window->enable_qwerty = !!enabled;
+}
+
+void
+skin_window_set_title( SkinWindow*  window, const char*  title )
+{
+    if (window && title)
+        SDL_WM_SetCaption( title, title );
+}
+
+static void
+skin_window_resize( SkinWindow*  window )
+{
+    /* now resize window */
+    if (window->surface) {
+        SDL_FreeSurface(window->surface);
+        window->surface = NULL;
+    }
+
+    if (window->shrink_surface) {
+        SDL_FreeSurface(window->shrink_surface);
+        window->shrink_surface = NULL;
+    }
+
+    if (window->shrink_pixels) {
+        qemu_free(window->shrink_pixels);
+        window->shrink_pixels = NULL;
+    }
+
+    if ( !window->no_display ) {
+        int           layout_w = window->layout.rect.size.w;
+        int           layout_h = window->layout.rect.size.h;
+        int           window_w = layout_w;
+        int           window_h = layout_h;
+        int           window_x = window->x_pos;
+        int           window_y = window->y_pos;
+        int           flags;
+        SDL_Surface*  surface;
+        double        scale = 1.0;
+        int           fullscreen = window->fullscreen;
+
+        if (fullscreen) {
+            if (get_nearest_monitor_rect(&window_x, &window_y,
+                                         &window_w, &window_h) < 0) {
+                fullscreen = 0;
+            } else {
+                double  x_scale = window_w * 1.0 / layout_w;
+                double  y_scale = window_h * 1.0 / layout_h;
+
+                scale = (x_scale <= y_scale) ? x_scale : y_scale;
+            }
+        }
+        else if (window->shrink) {
+            scale = window->shrink_scale;
+            window_w = (int) ceil(layout_w*scale);
+            window_h = (int) ceil(layout_h*scale);
+        }
+
+        {
+            char  temp[32];
+            sprintf(temp,"SDL_VIDEO_WINDOW_POS=%d,%d",window_x,window_y);
+            putenv(temp);
+            putenv("SDL_VIDEO_WINDOW_FORCE_VISIBLE=1");
+        }
+
+        flags = SDL_SWSURFACE;
+        if (fullscreen) {
+            flags |= SDL_FULLSCREEN;
+        }
+        surface = SDL_SetVideoMode( window_w, window_h, 32, flags );
+        if (surface == NULL) {
+            fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() );
+            exit(1);
+        }
+
+        SDL_WM_SetPos( window_x, window_y );
+
+        window->effective_scale = scale;
+        window->effective_x     = 0;
+        window->effective_y     = 0;
+
+        if (fullscreen) {
+            window->effective_x = (window_w - layout_w*scale)*0.5;
+            window->effective_y = (window_h - layout_h*scale)*0.5;
+        }
+
+        if (scale == 1.0)
+            window->surface = surface;
+        else
+        {
+            window_w = (int) ceil(window_w / scale );
+            window_h = (int) ceil(window_h / scale );
+
+            window->shrink_surface = surface;
+            window->shrink_pixels  = qemu_mallocz( window_w * window_h * 4 );
+            if (window->shrink_pixels == NULL) {
+                fprintf(stderr, "### Error: could not allocate memory for rescaling surface\n");
+                exit(1);
+            }
+            window->surface = sdl_surface_from_argb32( window->shrink_pixels, window_w, window_h );
+            if (window->surface == NULL) {
+                fprintf(stderr, "### Error: could not create or resize SDL window: %s\n", SDL_GetError() );
+                exit(1);
+            }
+            skin_scaler_set( window->scaler, scale, window->effective_x, window->effective_y );
+        }
+    }
+}
+
+static int
+skin_window_reset_internal ( SkinWindow*  window, SkinLayout*  slayout )
+{
+    Layout         layout;
+    ADisplay*      disp;
+
+    if ( layout_init( &layout, slayout ) < 0 )
+        return -1;
+
+    disp = window->layout.displays;
+
+    layout_done( &window->layout );
+    window->layout = layout;
+
+    disp = window->layout.displays;
+    if (disp != NULL && window->onion)
+        display_set_onion( disp,
+                           window->onion,
+                           window->onion_rotation,
+                           window->onion_alpha );
+
+    skin_window_resize(window);
+
+    finger_state_reset( &window->finger );
+    button_state_reset( &window->button );
+    ball_state_reset( &window->ball, window );
+
+    skin_window_redraw( window, NULL );
+
+    if (slayout->event_type != 0) {
+        kbd_generic_event( slayout->event_type, slayout->event_code, slayout->event_value );
+    }
+
+    return 0;
+}
+
+int
+skin_window_reset ( SkinWindow*  window, SkinLayout*  slayout )
+{
+    if (!window->fullscreen) {
+        SDL_WM_GetPos(&window->x_pos, &window->y_pos);
+    }
+    return skin_window_reset_internal( window, slayout );
+}
+
+void
+skin_window_set_lcd_brightness( SkinWindow*  window, int  brightness )
+{
+    ADisplay*  disp = window->layout.displays;
+
+    if (disp != NULL) {
+        disp->brightness = brightness;
+        skin_window_redraw( window, NULL );
+    }
+}
+
+void
+skin_window_free  ( SkinWindow*  window )
+{
+    if (window) {
+        if (window->surface) {
+            SDL_FreeSurface(window->surface);
+            window->surface = NULL;
+        }
+        if (window->shrink_surface) {
+            SDL_FreeSurface(window->shrink_surface);
+            window->shrink_surface = NULL;
+        }
+        if (window->shrink_pixels) {
+            qemu_free(window->shrink_pixels);
+            window->shrink_pixels = NULL;
+        }
+        if (window->onion) {
+            skin_image_unref( &window->onion );
+            window->onion_rotation = SKIN_ROTATION_0;
+        }
+        if (window->scaler) {
+            skin_scaler_free(window->scaler);
+            window->scaler = NULL;
+        }
+        layout_done( &window->layout );
+        qemu_free(window);
+    }
+}
+
+void
+skin_window_set_onion( SkinWindow*   window,
+                       SkinImage*    onion,
+                       SkinRotation  onion_rotation,
+                       int           onion_alpha )
+{
+    ADisplay*  disp;
+    SkinImage*  old = window->onion;
+
+    window->onion          = skin_image_ref(onion);
+    window->onion_rotation = onion_rotation;
+    window->onion_alpha    = onion_alpha;
+
+    skin_image_unref( &old );
+
+    disp = window->layout.displays;
+
+    if (disp != NULL)
+        display_set_onion( disp, window->onion, onion_rotation, onion_alpha );
+}
+
+static void
+skin_window_update_shrink( SkinWindow*  window, SkinRect*  rect )
+{
+    skin_scaler_scale( window->scaler, window->shrink_surface, window->surface,
+                       rect->pos.x, rect->pos.y, rect->size.w, rect->size.h );
+}
+
+void
+skin_window_set_scale( SkinWindow*  window, double  scale )
+{
+    window->shrink       = (scale != 1.0);
+    window->shrink_scale = scale;
+
+    skin_window_resize( window );
+    skin_window_redraw( window, NULL );
+}
+
+void
+skin_window_redraw( SkinWindow*  window, SkinRect*  rect )
+{
+    if (window != NULL && window->surface != NULL) {
+        Layout*  layout = &window->layout;
+
+        if (rect == NULL)
+            rect = &layout->rect;
+
+        {
+            SkinRect  r;
+
+            if ( skin_rect_intersect( &r, rect, &layout->rect ) ) {
+                SDL_Rect  rd;
+                rd.x = r.pos.x;
+                rd.y = r.pos.y;
+                rd.w = r.size.w;
+                rd.h = r.size.h;
+
+                SDL_FillRect( window->surface, &rd, layout->color );
+            }
+        }
+
+        {
+            Background*  back = layout->backgrounds;
+            Background*  end  = back + layout->num_backgrounds;
+            for ( ; back < end; back++ )
+                background_redraw( back, rect, window->surface );
+        }
+
+        {
+            ADisplay*  disp = layout->displays;
+            ADisplay*  end  = disp + layout->num_displays;
+            for ( ; disp < end; disp++ )
+                display_redraw( disp, rect, window->surface );
+        }
+
+        {
+            Button*  button = layout->buttons;
+            Button*  end    = button + layout->num_buttons;
+            for ( ; button < end; button++ )
+                button_redraw( button, rect, window->surface );
+        }
+
+        if ( window->ball.tracking )
+            ball_state_redraw( &window->ball, rect, window->surface );
+
+        if (window->effective_scale != 1.0)
+            skin_window_update_shrink( window, rect );
+        else
+        {
+            SDL_Rect  rd;
+            rd.x = rect->pos.x;
+            rd.y = rect->pos.y;
+            rd.w = rect->size.w;
+            rd.h = rect->size.h;
+
+            SDL_UpdateRects( window->surface, 1, &rd );
+        }
+    }
+}
+
+void
+skin_window_toggle_fullscreen( SkinWindow*  window )
+{
+    if (window && window->surface) {
+        if (!window->fullscreen)
+            SDL_WM_GetPos( &window->x_pos, &window->y_pos );
+
+        window->fullscreen = !window->fullscreen;
+        skin_window_resize( window );
+        skin_window_redraw( window, NULL );
+    }
+}
+
+void
+skin_window_get_display( SkinWindow*  window, ADisplayInfo  *info )
+{
+    ADisplay*  disp = window->layout.displays;
+
+    if (disp != NULL) {
+        info->width    = disp->datasize.w;
+        info->height   = disp->datasize.h;
+        info->rotation = disp->rotation;
+        info->data     = disp->data;
+    } else {
+        info->width    = 0;
+        info->height   = 0;
+        info->rotation = SKIN_ROTATION_0;
+        info->data     = NULL;
+    }
+}
+
+
+static void
+skin_window_map_to_scale( SkinWindow*  window, int  *x, int  *y )
+{
+    *x = (*x - window->effective_x) / window->effective_scale;
+    *y = (*y - window->effective_y) / window->effective_scale;
+}
+
+void
+skin_window_process_event( SkinWindow*  window, SDL_Event*  ev )
+{
+    Button*  button;
+    int      mx, my;
+
+    if (!window->surface)
+        return;
+
+    switch (ev->type) {
+    case SDL_MOUSEBUTTONDOWN:
+        if ( window->ball.tracking ) {
+            skin_window_trackball_press( window, 1 );
+            break;
+        }
+
+        mx = ev->button.x;
+        my = ev->button.y;
+        skin_window_map_to_scale( window, &mx, &my );
+        skin_window_move_mouse( window, mx, my );
+        skin_window_find_finger( window, mx, my );
+#if 0
+        printf("down: x=%d y=%d fx=%d fy=%d fis=%d\n",
+               ev->button.x, ev->button.y, window->finger.pos.x,
+               window->finger.pos.y, window->finger.inside);
+#endif
+        if (window->finger.inside) {
+            window->finger.tracking = 1;
+            add_finger_event(window->finger.pos.x, window->finger.pos.y, 1);
+        } else {
+            window->button.pressed = NULL;
+            button = window->button.hover;
+            if(button) {
+                button->down += 1;
+                skin_window_redraw( window, &button->rect );
+                window->button.pressed = button;
+                if(button->keycode) {
+                    send_key_event(button->keycode, 1);
+                }
+            }
+        }
+        break;
+
+    case SDL_MOUSEBUTTONUP:
+        if ( window->ball.tracking ) {
+            skin_window_trackball_press( window, 0 );
+            break;
+        }
+        button = window->button.pressed;
+        mx = ev->button.x;
+        my = ev->button.y;
+        skin_window_map_to_scale( window, &mx, &my );
+        if (button)
+        {
+            button->down = 0;
+            skin_window_redraw( window, &button->rect );
+            if(button->keycode) {
+                send_key_event(button->keycode, 0);
+            }
+            window->button.pressed = NULL;
+            window->button.hover   = NULL;
+            skin_window_move_mouse( window, mx, my );
+        }
+        else if (window->finger.tracking)
+        {
+            skin_window_move_mouse( window, mx, my );
+            window->finger.tracking = 0;
+            add_finger_event( window->finger.pos.x, window->finger.pos.y, 0);
+        }
+        break;
+
+    case SDL_MOUSEMOTION:
+        if ( window->ball.tracking ) {
+            skin_window_trackball_move( window, ev->motion.xrel, ev->motion.yrel );
+            break;
+        }
+        mx = ev->button.x;
+        my = ev->button.y;
+        skin_window_map_to_scale( window, &mx, &my );
+        if ( !window->button.pressed )
+        {
+            skin_window_move_mouse( window, mx, my );
+            if ( window->finger.tracking ) {
+                add_finger_event( window->finger.pos.x, window->finger.pos.y, 1 );
+            }
+        }
+        break;
+    }
+}
+
+static ADisplay*
+skin_window_display( SkinWindow*  window )
+{
+    return window->layout.displays;
+}
+
+void
+skin_window_update_display( SkinWindow*  window, int  x, int  y, int  w, int  h )
+{
+    ADisplay*  disp = skin_window_display(window);
+
+    if ( !window->surface )
+        return;
+
+    if (disp != NULL) {
+        SkinRect  r;
+        r.pos.x  = x;
+        r.pos.y  = y;
+        r.size.w = w;
+        r.size.h = h;
+
+        skin_rect_rotate( &r, &r, disp->rotation );
+        r.pos.x += disp->origin.x;
+        r.pos.y += disp->origin.y;
+
+        if (window->effective_scale != 1.0)
+            skin_window_redraw( window, &r );
+        else
+            display_redraw( disp, &r, window->surface );
+    }
+}
diff --git a/android/skin/window.h b/android/skin/window.h
new file mode 100644
index 0000000..3e92e40
--- /dev/null
+++ b/android/skin/window.h
@@ -0,0 +1,65 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _SKIN_WINDOW_H
+#define _SKIN_WINDOW_H
+
+#include "android/skin/file.h"
+#include "android/skin/trackball.h"
+#include <SDL.h>
+
+typedef struct SkinWindow  SkinWindow;
+
+extern SkinWindow*      skin_window_create( SkinLayout*  layout,
+                                            int          x,
+                                            int          y,
+                                            double       scale,
+                                            int          no_display );
+
+extern void             skin_window_enable_touch( SkinWindow*  window, int  enabled );
+extern void             skin_window_enable_trackball( SkinWindow*  window, int  enabled );
+extern void             skin_window_enable_dpad( SkinWindow*  window, int  enabled );
+extern void             skin_window_enable_qwerty( SkinWindow*  window, int  enabled );
+
+extern int              skin_window_reset ( SkinWindow*  window, SkinLayout*  layout );
+extern void             skin_window_free  ( SkinWindow*  window );
+extern void             skin_window_redraw( SkinWindow*  window, SkinRect*  rect );
+extern void             skin_window_process_event( SkinWindow*  window, SDL_Event*  ev );
+
+extern void             skin_window_set_onion( SkinWindow*   window,
+                                               SkinImage*    onion,
+                                               SkinRotation  rotation,
+                                               int           alpha );
+
+extern void             skin_window_set_scale( SkinWindow*  window,
+                                               double       scale );
+
+extern void             skin_window_set_title( SkinWindow*  window,
+                                               const char*  title );
+
+extern void             skin_window_set_trackball( SkinWindow*  window, SkinTrackBall*  ball );
+extern void             skin_window_show_trackball( SkinWindow*  window, int  enable );
+extern void             skin_window_toggle_fullscreen( SkinWindow*  window );
+
+/* change the brightness of the emulator LCD screen. 'brightness' will be clamped to 0..255 */
+extern void             skin_window_set_lcd_brightness( SkinWindow*  window, int  brightness );
+
+typedef struct {
+    int           width;
+    int           height;
+    SkinRotation  rotation;
+    void*         data;
+} ADisplayInfo;
+
+extern void             skin_window_get_display( SkinWindow*  window, ADisplayInfo  *info );
+extern void             skin_window_update_display( SkinWindow*  window, int  x, int  y, int  w, int  h );
+
+#endif /* _SKIN_WINDOW_H */
diff --git a/android/tools/gen-hw-config.py b/android/tools/gen-hw-config.py
new file mode 100755
index 0000000..928ccc5
--- /dev/null
+++ b/android/tools/gen-hw-config.py
@@ -0,0 +1,141 @@
+#!/usr/bin/env python
+#
+# This software is licensed under the terms of the GNU General Public
+# License version 2, as published by the Free Software Foundation, and
+# may be copied, distributed, and modified under those terms.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# this script is used to generate 'android/avd/hw-config.h' by
+# parsing 'android/avd/hardware-properties.ini'
+#
+#
+import  sys, os, string, re
+
+# location of source file, relative to current program directory
+relativeSourcePath = "../avd/hardware-properties.ini"
+
+# location of target file, relative to current program directory
+relativeTargetPath = "../avd/hw-config-defs.h"
+
+def quoteStringForC(str):
+    """quote a string so it can be used in C"""
+    return '\\"'.join('"'+p+'"' for p in str.split('"'))
+
+# a dictionary that maps item types as they appear in the .ini
+# file into macro names in the generated C header
+#
+typesToMacros = { 
+    'integer': 'HWCFG_INT',
+    'string': 'HWCFG_STRING',
+    'boolean': 'HWCFG_BOOL',
+    'diskSize': 'HWCFG_DISKSIZE',
+    'double': 'HWCFG_DOUBLE'
+    }
+
+# the list of macro names
+macroNames = typesToMacros.values()
+
+# target program header
+targetHeader = """\
+/* this file is automatically generated from 'hardware-properties.ini'
+ * DO NOT EDIT IT. To re-generate it, use android/tools/gen-hw-config.py'
+ */"""
+
+# locate source and target
+programDir = os.path.dirname(sys.argv[0])
+sourceFile = os.path.normpath(os.path.join(programDir,relativeSourcePath))
+targetFile = os.path.normpath(os.path.join(programDir,relativeTargetPath))
+
+# parse the source file and record items
+# I would love to use Python's ConfigParser, but it doesn't
+# support files without sections, or multiply defined items
+#
+items    = []
+lastItem = None
+
+class Item:
+    def __init__(self,name):
+        self.name     = name
+        self.type     = type
+        self.default  = None
+        self.abstract = ""
+        self.description = ""
+
+    def add(self,key,val):
+        if key == 'type':
+            self.type = val
+        elif key == 'default':
+            self.default = val
+        elif key == 'abstract':
+            self.abstract = val
+        elif key == 'description':
+            self.description = val
+
+for line in open(sourceFile):
+    line = line.strip()
+    # ignore empty lines and comments
+    if len(line) == 0 or line[0] in ";#":
+        continue
+    key, value = line.split('=')
+
+    key   = key.strip()
+    value = value.strip()
+
+    if key == 'name':
+        if lastItem: items.append(lastItem)
+        lastItem = Item(value)
+    else:
+        lastItem.add(key, value)
+
+if lastItem:
+    items.append(lastItem)
+
+
+print  targetHeader
+
+# write guards to prevent bad compiles
+for m in macroNames:
+    print """\
+#ifndef %(macro)s
+#error  %(macro)s not defined
+#endif""" % { 'macro':m }
+print ""
+
+for item in items:
+    if item.type == None:
+        sys.stderr.write("ignoring config item with no type '%s'\n" % item.name)
+        continue
+
+    if not typesToMacros.has_key(item.type):
+        sys.stderr.write("ignoring config item with unknown type '%s': '%s'\n" % \
+                (item.type, item.name))
+        continue
+
+    if item.default == None:
+        sys.stderr.write("ignoring config item with no default '%s' */" % item.name)
+        continue
+
+    # convert dots into underscores
+    varMacro   = typesToMacros[item.type]
+    varNameStr = quoteStringForC(item.name)
+    varName    = item.name.replace(".","_")
+    varDefault = item.default
+    varAbstract = quoteStringForC(item.abstract)
+    varDesc     = quoteStringForC(item.description)
+
+    if item.type in [ 'string', 'boolean', 'diskSize' ]:
+        # quote default value for strings
+        varDefault = quoteStringForC(varDefault)
+
+    print "%s(\n  %s,\n  %s,\n  %s,\n  %s,\n  %s)\n" % \
+            (varMacro,varName,varNameStr,varDefault,varAbstract,varDesc)
+
+
+for m in macroNames:
+    print "#undef %s" % m
+
+print "/* end of auto-generated file */"
diff --git a/android/user-config.c b/android/user-config.c
new file mode 100644
index 0000000..acd1a27
--- /dev/null
+++ b/android/user-config.c
@@ -0,0 +1,212 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/user-config.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/debug.h"
+#include "android/utils/system.h"
+#include "android/utils/path.h"
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
+
+#if 0 /* set to 1 for more debugging */
+#  define  DD(...)  D(__VA_ARGS__)
+#else
+#  define  DD(...)  ((void)0)
+#endif
+
+struct AUserConfig {
+    ABool      changed;
+    int        windowX;
+    int        windowY;
+    uint64_t   uuid;
+    char*      iniPath;
+};
+
+/* Name of the user-config file */
+#define  USER_CONFIG_FILE  "emulator-user.ini"
+
+#define  KEY_WINDOW_X  "window.x"
+#define  KEY_WINDOW_Y  "window.y"
+#define  KEY_UUID      "uuid"
+
+#define  DEFAULT_X  100
+#define  DEFAULT_Y  100
+
+/* Create a new AUserConfig object from a given AvdInfo */
+AUserConfig*
+auserConfig_new( AvdInfo*  info )
+{
+    AUserConfig*  uc;
+    char          inAndroidBuild = avdInfo_inAndroidBuild(info);
+    char          needUUID = 1;
+    char          temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+    char*         parentPath;
+    IniFile*      ini = NULL;
+
+    ANEW0(uc);
+
+    /* If we are in the Android build system, store the configuration
+     * in ~/.android/emulator-user.ini. otherwise, store it in the file
+     * emulator-user.ini in the AVD's content directory.
+     */
+    if (inAndroidBuild) {
+        p = bufprint_config_file(temp, end, USER_CONFIG_FILE);
+    } else {
+        p = bufprint(temp, end, "%s/%s", avdInfo_getContentPath(info), 
+                     USER_CONFIG_FILE);
+    }
+
+    /* handle the unexpected */
+    if (p >= end) {
+        /* Hmmm, something is weird, let's use a temporary file instead */
+        p = bufprint_temp_file(temp, end, USER_CONFIG_FILE);
+        if (p >= end) {
+            derror("Weird: Cannot create temporary user-config file?");
+            exit(2);
+        }
+        dwarning("Weird: Content path too long, using temporary user-config.");
+    }
+
+    uc->iniPath = ASTRDUP(temp);
+    DD("looking user-config in: %s", uc->iniPath);
+
+
+    /* ensure that the parent directory exists */
+    parentPath = path_parent(uc->iniPath, 1);
+    if (parentPath == NULL) {
+        derror("Weird: Can't find parent of user-config file: %s",
+               uc->iniPath);
+        exit(2);
+    }
+
+    if (!path_exists(parentPath)) {
+        if (!inAndroidBuild) {
+            derror("Weird: No content path for this AVD: %s", parentPath);
+            exit(2);
+        }
+        DD("creating missing directory: %s", parentPath);
+        if (path_mkdir_if_needed(parentPath, 0755) < 0) {
+            derror("Using empty user-config, can't create %s: %s",
+                   parentPath, strerror(errno));
+            exit(2);
+        }
+    }
+
+    if (path_exists(uc->iniPath)) {
+        DD("reading user-config file");
+        ini = iniFile_newFromFile(uc->iniPath);
+        if (ini == NULL) {
+            dwarning("Can't read user-config file: %s\nUsing default values",
+                     uc->iniPath);
+        }
+    }
+
+    if (ini != NULL) {
+        uc->windowX = iniFile_getInteger(ini, KEY_WINDOW_X, DEFAULT_X);
+        DD("    found %s = %d", KEY_WINDOW_X, uc->windowX);
+
+        uc->windowY = iniFile_getInteger(ini, KEY_WINDOW_Y, DEFAULT_Y);
+        DD("    found %s = %d", KEY_WINDOW_Y, uc->windowY);
+
+        if (iniFile_getValue(ini, KEY_UUID) != NULL) {
+            uc->uuid = (uint64_t) iniFile_getInt64(ini, KEY_UUID, 0LL);
+            needUUID = 0;
+            DD("    found %s = %lld", KEY_UUID, uc->uuid);
+        }
+
+        iniFile_free(ini);
+    }
+    else {
+        uc->windowX = DEFAULT_X;
+        uc->windowY = DEFAULT_Y;
+        uc->changed = 1;
+    }
+
+    /* Generate a 64-bit UUID if necessary. We simply take the
+     * current time, which avoids any privacy-related value.
+     */
+    if (needUUID) {
+        struct timeval  tm;
+
+        gettimeofday( &tm, NULL );
+        uc->uuid    = (uint64_t)tm.tv_sec*1000 + tm.tv_usec/1000;
+        uc->changed = 1;
+        DD("    Generated UUID = %lld", uc->uuid);
+    }
+
+    return uc;
+}
+
+
+uint64_t
+auserConfig_getUUID( AUserConfig*  uconfig )
+{
+    return uconfig->uuid;
+}
+
+void
+auserConfig_getWindowPos( AUserConfig*  uconfig, int  *pX, int  *pY )
+{
+    *pX = uconfig->windowX;
+    *pY = uconfig->windowY;
+}
+
+
+void
+auserConfig_setWindowPos( AUserConfig*  uconfig, int  x, int  y )
+{
+    if (x != uconfig->windowX || y != uconfig->windowY) {
+        uconfig->windowX = x;
+        uconfig->windowY = y;
+        uconfig->changed = 1;
+    }
+}
+
+/* Save the user configuration back to the content directory.
+ * Should be used in an atexit() handler */
+void
+auserConfig_save( AUserConfig*  uconfig )
+{
+    IniFile*   ini;
+    char       temp[256];
+
+    if (uconfig->changed == 0) {
+        D("User-config was not changed.");
+        return;
+    }
+
+    bufprint(temp, temp+sizeof(temp),
+             "%s = %d\n"
+             "%s = %d\n"
+             "%s = %lld\n",
+             KEY_WINDOW_X, uconfig->windowX,
+             KEY_WINDOW_Y, uconfig->windowY,
+             KEY_UUID,     uconfig->uuid );
+
+    DD("Generated user-config file:\n%s", temp);
+
+    ini = iniFile_newFromMemory(temp, uconfig->iniPath);
+    if (ini == NULL) {
+        D("Weird: can't create user-config iniFile?");
+        return;
+    }
+    if (iniFile_saveToFile(ini, uconfig->iniPath) < 0) {
+        dwarning("could not save user configuration: %s: %s",
+                 uconfig->iniPath, strerror(errno));
+    } else {
+        D("User configuration saved to %s", uconfig->iniPath);
+    }
+    iniFile_free(ini);
+}
diff --git a/android/user-config.h b/android/user-config.h
new file mode 100644
index 0000000..5fc6325
--- /dev/null
+++ b/android/user-config.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_USER_CONFIG_H
+#define _ANDROID_USER_CONFIG_H
+
+#include "android/avd/info.h"
+#include <stdint.h>
+
+/* a structure used to model the user-configuration settings
+ *
+ * At the moment, this is only used to store the last position
+ * of the emulator window and a unique 64-bit UUID. We might
+ * add more AVD-specific preferences here in the future.
+ *
+ * By definition, these settings should be optional and we
+ * should be able to work without them, unlike the AVD
+ * configuration information found in config.ini
+ */
+typedef struct AUserConfig   AUserConfig;
+
+/* Create a new AUserConfig object from a given AvdInfo */
+AUserConfig*   auserConfig_new( AvdInfo*  info );
+
+/* Retrieve the unique UID for this AVD */
+uint64_t       auserConfig_getUUID( AUserConfig*  uconfig );
+
+/* Retrieve the stored window position for this AVD */
+void           auserConfig_getWindowPos( AUserConfig*  uconfig, int  *pX, int  *pY );
+
+/* Change the stored window position for this AVD */
+void           auserConfig_setWindowPos( AUserConfig*  uconfig, int  x, int  y );
+
+/* Save the user configuration back to the content directory.
+ * Should be used in an atexit() handler. This will effectively
+ * only save the user configuration to disk if its content
+ * has changed.
+ */
+void           auserConfig_save( AUserConfig*  uconfig );
+
+/* */
+
+#endif /* _ANDROID_USER_CONFIG_H */
diff --git a/android/utils/bufprint.c b/android/utils/bufprint.c
new file mode 100644
index 0000000..4309a4b
--- /dev/null
+++ b/android/utils/bufprint.c
@@ -0,0 +1,230 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/utils/bufprint.h"
+#include "android/utils/path.h"
+#include "android/utils/debug.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include "windows.h"
+#  include "shlobj.h"
+#else
+#  include <unistd.h>
+#  include <sys/stat.h>
+#endif
+
+#define  D(...)  VERBOSE_PRINT(init,__VA_ARGS__)
+
+
+/** USEFUL STRING BUFFER FUNCTIONS
+ **/
+
+char*
+vbufprint( char*        buffer,
+           char*        buffer_end,
+           const char*  fmt,
+           va_list      args )
+{
+    int  len = vsnprintf( buffer, buffer_end - buffer, fmt, args );
+    if (len < 0 || buffer+len >= buffer_end) {
+        if (buffer < buffer_end)
+            buffer_end[-1] = 0;
+        return buffer_end;
+    }
+    return buffer + len;
+}
+
+char*
+bufprint(char*  buffer, char*  end, const char*  fmt, ... )
+{
+    va_list  args;
+    char*    result;
+
+    va_start(args, fmt);
+    result = vbufprint(buffer, end, fmt, args);
+    va_end(args);
+    return  result;
+}
+
+/** USEFUL DIRECTORY SUPPORT
+ **
+ **  bufprint_app_dir() returns the directory where the emulator binary is located
+ **
+ **  get_android_home() returns a user-specific directory where the emulator will
+ **  store its writable data (e.g. config files, profiles, etc...).
+ **  on Unix, this is $HOME/.android, on Windows, this is something like
+ **  "%USERPROFILE%/Local Settings/AppData/Android" on XP, and something different
+ **  on Vista.
+ **
+ **  both functions return a string that must be freed by the caller
+ **/
+
+#ifdef __linux__
+char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    char   path[1024];
+    int    len;
+    char*  x;
+
+    len = readlink("/proc/self/exe", path, sizeof(path));
+    if (len <= 0 || len >= (int)sizeof(path)) goto Fail;
+    path[len] = 0;
+
+    x = strrchr(path, '/');
+    if (x == 0) goto Fail;
+    *x = 0;
+
+    return bufprint(buff, end, "%s", path);
+Fail:
+    fprintf(stderr,"cannot locate application directory\n");
+    exit(1);
+    return end;
+}
+
+#elif defined(__APPLE__)
+/* the following hack is needed in order to build with XCode 3.1
+ * don't ask me why, but it seems that there were changes in the
+ * GCC compiler that we don't have in our pre-compiled version
+ */
+#ifndef __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
+#define __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ MAC_OS_X_VERSION_10_4
+#endif
+#import <Carbon/Carbon.h>
+#include <unistd.h>
+
+char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    ProcessSerialNumber psn;
+    CFDictionaryRef     dict;
+    CFStringRef         value;
+    char                s[PATH_MAX];
+    char*               x;
+
+    GetCurrentProcess(&psn);
+    dict  = ProcessInformationCopyDictionary(&psn, 0xffffffff);
+    value = (CFStringRef)CFDictionaryGetValue(dict,
+                                             CFSTR("CFBundleExecutable"));
+    CFStringGetCString(value, s, PATH_MAX - 1, kCFStringEncodingUTF8);
+    x = strrchr(s, '/');
+    if (x == 0) goto fail;
+    *x = 0;
+
+    return bufprint(buff, end, "%s", s);
+fail:
+    fprintf(stderr,"cannot locate application directory\n");
+    exit(1);
+    return end;
+}
+#elif defined _WIN32
+char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    char   appDir[MAX_PATH];
+	int    len;
+	char*  sep;
+
+    len = GetModuleFileName( 0, appDir, sizeof(appDir)-1 );
+	if (len == 0) {
+		fprintf(stderr, "PANIC CITY!!\n");
+		exit(1);
+	}
+	if (len >= (int)sizeof(appDir)) {
+		len = sizeof(appDir)-1;
+	    appDir[len] = 0;
+    }
+	
+	sep = strrchr(appDir, '\\');
+	if (sep)
+	  *sep = 0;
+
+    return bufprint(buff, end, "%s", appDir);
+}
+#else
+char*
+bufprint_app_dir(char*  buff, char*  end)
+{
+    return bufprint(buff, end, ".");
+}
+#endif
+
+#define  _ANDROID_PATH   ".android"
+
+char*
+bufprint_config_path(char*  buff, char*  end)
+{
+#ifdef _WIN32
+    const char*  home = getenv("ANDROID_SDK_HOME");
+    if (home != NULL) {
+        return bufprint(buff, end, "%s\\%s", home, _ANDROID_PATH );
+    } else {
+        char  path[MAX_PATH];
+
+        SHGetFolderPath( NULL, CSIDL_PROFILE,
+                         NULL, 0, path);
+
+        return bufprint(buff, end, "%s\\%s", path, _ANDROID_PATH );
+    }
+#else
+    const char*  home = getenv("HOME");
+    if (home == NULL)
+        home = "/tmp";
+    return bufprint(buff, end, "%s/%s", home, _ANDROID_PATH );
+#endif
+}
+
+char*
+bufprint_config_file(char*  buff, char*  end, const char*  suffix)
+{
+    char*   p;
+    p = bufprint_config_path(buff, end);
+    p = bufprint(p, end, PATH_SEP "%s", suffix);
+    return p;
+}
+
+char*
+bufprint_temp_dir(char*  buff, char*  end)
+{
+#ifdef _WIN32
+    char   path[MAX_PATH];
+    DWORD  retval;
+
+    retval = GetTempPath( sizeof(path), path );
+    if (retval > sizeof(path) || retval == 0) {
+        D( "can't locate TEMP directory" );
+        strncpy(path, "C:\\Temp", sizeof(path) );
+    }
+    strncat( path, "\\AndroidEmulator", sizeof(path)-1 );
+    path_mkdir(path, 0744);
+
+    return  bufprint(buff, end, "%s", path);
+#else
+    const char*  tmppath = "/tmp/android";
+    mkdir(tmppath, 0744);
+    return  bufprint(buff, end, "%s", tmppath );
+#endif
+}
+
+char*
+bufprint_temp_file(char*  buff, char*  end, const char*  suffix)
+{
+    char*  p;
+    p = bufprint_temp_dir(buff, end);
+    p = bufprint(p, end, PATH_SEP "%s", suffix);
+    return p;
+}
+
diff --git a/android/utils/bufprint.h b/android/utils/bufprint.h
new file mode 100644
index 0000000..32d64dc
--- /dev/null
+++ b/android/utils/bufprint.h
@@ -0,0 +1,76 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UTILS_BUFPRINT_H
+#define _ANDROID_UTILS_BUFPRINT_H
+
+#include <stdarg.h>
+
+/** FORMATTED BUFFER PRINTING
+ **
+ **  bufprint() allows your to easily and safely append formatted string
+ **  content to a given bounded character buffer, in a way that is easier
+ **  to use than raw snprintf()
+ **
+ **  'buffer'  is the start position in the buffer,
+ **  'buffend' is the end of the buffer, the function assumes (buffer <= buffend)
+ **  'format'  is a standard printf-style format string, followed by any number
+ **            of formatting arguments
+ **
+ **  the function returns the next position in the buffer if everything fits
+ **  in it. in case of overflow or formatting error, it will always return "buffend"
+ **
+ **  this allows you to chain several calls to bufprint() and only check for
+ **  overflow at the end, for exemple:
+ **
+ **     char   buffer[1024];
+ **     char*  p   = buffer;
+ **     char*  end = p + sizeof(buffer);
+ **
+ **     p = bufprint(p, end, "%s/%s", first, second);
+ **     p = bufprint(p, end, "/%s", third);
+ **     if (p >= end) ---> overflow
+ **
+ **  as a convenience, the appended string is zero-terminated if there is no overflow.
+ **  (this means that even if p >= end, the content of "buffer" is zero-terminated)
+ **
+ **  vbufprint() is a variant that accepts a va_list argument
+ **/
+
+extern char*   vbufprint(char*  buffer, char*  buffend, const char*  fmt, va_list  args );
+extern char*   bufprint (char*  buffer, char*  buffend, const char*  fmt, ... );
+
+/** USEFUL DIRECTORY SUPPORT
+ **
+ **  bufprint_add_dir() appends the application's directory to a given bounded buffer
+ **
+ **  bufprint_config_path() appends the applications' user-specific configuration directory
+ **  to a bounded buffer. on Unix this is usually ~/.android, and something a bit more
+ **  complex on Windows
+ **
+ **  bufprint_config_file() appends the name of a file or directory relative to the
+ **  user-specific configuration directory to a bounded buffer. this really is equivalent
+ **  to concat-ing the config path + path separator + 'suffix'
+ **
+ **  bufprint_temp_dir() appends the temporary directory's path to a given bounded buffer
+ **
+ **  bufprint_temp_file() appens the name of a file or directory relative to the
+ **  temporary directory. equivalent to concat-ing the temp path + path separator + 'suffix'
+ **/
+
+extern char*  bufprint_app_dir    (char*  buffer, char*  buffend);
+extern char*  bufprint_config_path(char*  buffer, char*  buffend);
+extern char*  bufprint_config_file(char*  buffer, char*  buffend, const char*  suffix);
+extern char*  bufprint_temp_dir   (char*  buffer, char*  buffend);
+extern char*  bufprint_temp_file  (char*  buffer, char*  buffend, const char*  suffix);
+
+#endif /* _ANDROID_UTILS_BUFPRINT_H */
diff --git a/android/utils/debug.c b/android/utils/debug.c
new file mode 100644
index 0000000..32936d2
--- /dev/null
+++ b/android/utils/debug.c
@@ -0,0 +1,141 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/debug.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+void
+dprint( const char*  format,  ... )
+{
+    va_list  args;
+    va_start( args, format );
+    fprintf( stdout, "emulator: ");
+    vfprintf( stdout, format, args );
+    fprintf( stdout, "\n" );
+    va_end( args );
+}
+
+void
+dprintn( const char*  format, ... )
+{
+    va_list  args;
+    va_start( args, format );
+    vfprintf( stdout, format, args );
+    va_end( args );
+}
+
+void
+dprintnv( const char*  format, va_list args )
+{
+    vfprintf( stdout, format, args );
+}
+
+
+void
+dwarning( const char*  format, ... )
+{
+    va_list  args;
+    va_start( args, format );
+    dprintn( "emulator: WARNING: " );
+    dprintnv( format, args );
+    dprintn( "\n" );
+    va_end( args );
+}
+
+
+void
+derror( const char*  format, ... )
+{
+    va_list  args;
+    va_start( args, format );
+    dprintn( "emulator: ERROR: " );
+    dprintnv( format, args );
+    dprintn( "\n" );
+    va_end( args );
+}
+
+/** STDOUT/STDERR REDIRECTION
+ **
+ ** allows you to shut temporarily shutdown stdout/stderr
+ ** this is useful to get rid of debug messages from ALSA and esd
+ ** on Linux.
+ **/
+static int    stdio_disable_count;
+static int    stdio_save_out_fd;
+static int    stdio_save_err_fd;
+
+#ifdef _WIN32
+extern void
+stdio_disable( void )
+{
+    if (++stdio_disable_count == 1) {
+        int  null_fd, out_fd, err_fd;
+        fflush(stdout);
+        out_fd = _fileno(stdout);
+        err_fd = _fileno(stderr);
+        stdio_save_out_fd = _dup(out_fd);
+        stdio_save_err_fd = _dup(err_fd);
+        null_fd = _open( "NUL", _O_WRONLY );
+        _dup2(null_fd, out_fd);
+        _dup2(null_fd, err_fd);
+        close(null_fd);
+    }
+}
+
+extern void
+stdio_enable( void )
+{
+    if (--stdio_disable_count == 0) {
+        int  out_fd, err_fd;
+        fflush(stdout);
+        out_fd = _fileno(stdout);
+        err_fd = _fileno(stderr);
+        _dup2(stdio_save_out_fd, out_fd);
+        _dup2(stdio_save_err_fd, err_fd);
+        _close(stdio_save_out_fd);
+        _close(stdio_save_err_fd);
+    }
+}
+#else
+extern void
+stdio_disable( void )
+{
+    if (++stdio_disable_count == 1) {
+        int  null_fd, out_fd, err_fd;
+        fflush(stdout);
+        out_fd = fileno(stdout);
+        err_fd = fileno(stderr);
+        stdio_save_out_fd = dup(out_fd);
+        stdio_save_err_fd = dup(err_fd);
+        null_fd = open( "/dev/null", O_WRONLY );
+        dup2(null_fd, out_fd);
+        dup2(null_fd, err_fd);
+        close(null_fd);
+    }
+}
+
+extern void
+stdio_enable( void )
+{
+    if (--stdio_disable_count == 0) {
+        int  out_fd, err_fd;
+        fflush(stdout);
+        out_fd = fileno(stdout);
+        err_fd = fileno(stderr);
+        dup2(stdio_save_out_fd, out_fd);
+        dup2(stdio_save_err_fd, err_fd);
+        close(stdio_save_out_fd);
+        close(stdio_save_err_fd);
+    }
+}
+#endif
diff --git a/android/utils/debug.h b/android/utils/debug.h
new file mode 100644
index 0000000..fdf93c9
--- /dev/null
+++ b/android/utils/debug.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_DEBUG_H
+#define _ANDROID_UTILS_DEBUG_H
+
+#include <stdarg.h>
+
+#define  VERBOSE_TAG_LIST    \
+    _VERBOSE_TAG(init,         "emulator initialization")  \
+    _VERBOSE_TAG(console,      "control console")  \
+    _VERBOSE_TAG(modem,        "emulated GSM modem")  \
+    _VERBOSE_TAG(radio,        "emulated GSM AT Command channel") \
+    _VERBOSE_TAG(keys,         "key bindings & presses") \
+    _VERBOSE_TAG(slirp,        "internal router/firewall") \
+    _VERBOSE_TAG(timezone,     "host timezone detection" ) \
+    _VERBOSE_TAG(socket,       "network sockets") \
+    _VERBOSE_TAG(proxy,        "network proxy support") \
+    _VERBOSE_TAG(audio,        "audio sub-system") \
+    _VERBOSE_TAG(audioin,      "audio input backend") \
+    _VERBOSE_TAG(audioout,     "audio output backend") \
+    _VERBOSE_TAG(surface,      "video surface support") \
+    _VERBOSE_TAG(qemud,        "qemud multiplexer daemon") \
+    _VERBOSE_TAG(gps,          "emulated GPS") \
+    _VERBOSE_TAG(nand_limits,  "nand/flash read/write thresholding") \
+    _VERBOSE_TAG(hw_control,   "emulated power/flashlight/led/vibrator") \
+    _VERBOSE_TAG(avd_config,   "android virtual device configuration") \
+
+#define  _VERBOSE_TAG(x,y)  VERBOSE_##x,
+typedef enum {
+    VERBOSE_TAG_LIST
+    VERBOSE_MAX  /* do not remove */
+} VerboseTag;
+#undef  _VERBOSE_TAG
+
+/* defined in android_main.c */
+extern unsigned long  android_verbose;
+
+#define  VERBOSE_ENABLE(tag)    \
+    android_verbose |= (1 << VERBOSE_##tag)
+
+#define  VERBOSE_DISABLE(tag)   \
+    android_verbose &= (1 << VERBOSE_##tag)
+
+#define  VERBOSE_CHECK(tag)    \
+    ((android_verbose & (1 << VERBOSE_##tag)) != 0)
+
+#define  VERBOSE_CHECK_ANY()    \
+    (android_verbose != 0)
+
+#define  VERBOSE_PRINT(tag,...)  \
+    do { if (VERBOSE_CHECK(tag)) dprint(__VA_ARGS__); } while (0)
+
+/** DEBUG TRACE SUPPORT
+ **
+ ** debug messages can be sent by calling these function
+ **
+ ** 'dprint' prints the message, then appends a '\n\
+ ** 'dprintn' simply prints the message as is
+ ** 'dprintnv' allows you to use a va_list argument
+ ** 'dwarning' prints a warning message, then appends a '\n'
+ ** 'derror' prints a severe error message, then appends a '\n'
+ */
+
+extern void   dprint( const char*  format, ... );
+extern void   dprintn( const char*  format, ... );
+extern void   dprintnv( const char*  format, va_list  args );
+extern void   dwarning( const char*  format, ... );
+extern void   derror( const char*  format, ... );
+
+/** STDOUT/STDERR REDIRECTION
+ **
+ ** allows you to shut temporarily shutdown stdout/stderr
+ ** this is useful to get rid of debug messages from ALSA and esd
+ ** on Linux.
+ **/
+
+extern void  stdio_disable( void );
+extern void  stdio_enable( void );
+
+/* */
+
+#endif /* _ANDROID_UTILS_DEBUG_H */
diff --git a/android/utils/dirscanner.c b/android/utils/dirscanner.c
new file mode 100644
index 0000000..fc63ef0
--- /dev/null
+++ b/android/utils/dirscanner.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/dirscanner.h"
+#include "android/utils/bufprint.h"
+#include "qemu-common.h"
+#include <stddef.h>
+
+#define  DIRSCANNER_BASE     \
+    char  root[PATH_MAX];    \
+    int   rootLen;           \
+    char  full[PATH_MAX];    \
+
+
+#if _WIN32
+
+#include <io.h>
+
+struct DirScanner {
+    DIRSCANNER_BASE
+    intptr_t            findIndex1;
+    struct _finddata_t  findData;
+};
+
+/* note: findIndex1 contains the find index + 1
+ *       so a value of 0 means 'invalid'
+ */
+
+static int
+_dirScannerInit( DirScanner*  s )
+{
+    char*  p   = s->root + s->rootLen;
+    char*  end = s->root + sizeof s->root;
+    int    ret;
+
+    /* create file spec by appending \* to root */
+    p = bufprint(p, end, "\\*");
+    if (p >= end)
+        return -1;
+
+    ret = _findfirst(s->root, &s->findData);
+
+    s->findIndex1 = ret+1;
+    return ret;
+}
+
+static void
+_dirScanner_done( DirScanner*  s )
+{
+    if (s->findIndex1 > 0) {
+        _findclose(s->findIndex1-1);
+        s->findIndex1 = 0;
+    }
+}
+
+const char*
+dirScanner_next( DirScanner*  s )
+{
+    char*  ret = NULL;
+
+    if (!s || s->findIndex1 <= 0)
+        return NULL;
+
+    while (ret == NULL) {
+        ret = s->findData.name;
+
+        /* ignore special directories */
+        if (!strcmp(ret, ".") || !strcmp(ret, "..")) {
+            ret = NULL;
+        }
+        /* find next one */
+        if (_findnext(s->findIndex1-1, &s->findData) < 0) {
+            _dirScanner_done(s);
+            break;
+        }
+    }
+    return ret;
+}
+
+#else /* !_WIN32 */
+
+#include <dirent.h>
+struct DirScanner {
+    DIRSCANNER_BASE
+    DIR*            dir;
+    struct dirent*  entry;
+};
+
+static int
+_dirScannerInit( DirScanner*  s )
+{
+    s->dir = opendir(s->root);
+
+    if (s->dir == NULL)
+        return -1;
+
+    s->entry = NULL;
+    return 0;
+}
+
+static void
+_dirScanner_done( DirScanner*  s )
+{
+    if (s->dir) {
+        closedir(s->dir);
+        s->dir = NULL;
+    }
+}
+
+const char*
+dirScanner_next( DirScanner*  s )
+{
+    char*  ret = NULL;
+
+    if (!s || s->dir == NULL)
+        return NULL;
+
+    for (;;)
+    {
+        /* read new entry if needed */
+        s->entry = readdir(s->dir);
+        if (s->entry == NULL) {
+            _dirScanner_done(s);
+            break;
+        }
+
+        /* ignore special directories */
+        ret = s->entry->d_name;
+
+        if (!strcmp(ret,".") || !strcmp(ret,"..")) {
+            ret = NULL;
+            continue;
+        }
+        break;
+    }
+    return ret;
+}
+
+#endif /* !_WIN32 */
+
+DirScanner*
+dirScanner_new ( const char*  rootPath )
+{
+    DirScanner*  s   = qemu_mallocz(sizeof *s);
+    char*        p   = s->root;
+    char*        end = p + sizeof s->root;
+
+    p = bufprint(p, end, "%s", rootPath);
+    if (p >= end)
+        goto FAIL;
+
+    s->rootLen = (p - s->root);
+
+    if (_dirScannerInit(s) < 0)
+        goto FAIL;
+
+    return s;
+
+FAIL:
+    dirScanner_free(s);
+    return NULL;
+}
+
+
+void
+dirScanner_free( DirScanner*  s )
+{
+    if (!s)
+        return;
+
+    _dirScanner_done(s);
+    qemu_free(s);
+}
+
+
+const char*
+dirScanner_nextFull( DirScanner*  s )
+{
+    const char*  name = dirScanner_next(s);
+    char*        p;
+    char*        end;
+
+    if (name == NULL)
+        return NULL;
+
+    p   = s->full;
+    end = p + sizeof s->full;
+
+    p = bufprint(p, end, "%.*s/%s", s->rootLen, s->root, name);
+    if (p >= end) {
+        /* ignore if the full name is too long */
+        return dirScanner_nextFull(s);
+    }
+    return s->full;
+}
diff --git a/android/utils/dirscanner.h b/android/utils/dirscanner.h
new file mode 100644
index 0000000..871b05e
--- /dev/null
+++ b/android/utils/dirscanner.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_DIR_H
+#define _ANDROID_UTILS_DIR_H
+
+/* simple utility to parse directories for files            */
+/* needed because Unix and Windows don't use the same stuff */
+
+typedef struct DirScanner  DirScanner;
+
+/* Create a new directory scanner object from a given rootPath.
+ * returns NULL in case of failure (error code in errno)
+ */
+DirScanner*    dirScanner_new ( const char*  rootPath );
+
+/* Destroy a given directory scanner. You must always call
+ * this function to release proper system resources.
+ */
+void           dirScanner_free( DirScanner*  s );
+
+/* Get the name of the next file from a directory scanner.
+ * Returns NULL when there is no more elements in the list.
+ *
+ * This is only the file name, use dirScanner_nextFull to
+ * get its full path.
+ *
+ * This will never return '.' and '..'.
+ *
+ * The returned string is owned by the scanner, and will
+ * change on the next call to this function or when the
+ * scanner is destroyed.
+ */
+const char*    dirScanner_next( DirScanner*  s );
+
+/* A variant of dirScanner_next() which returns the full path
+ * to the next directory element.
+ */
+const char*    dirScanner_nextFull( DirScanner*  s );
+
+/* */
+
+#endif /* _ANDROID_UTILS_DIR_H */
diff --git a/android/utils/display-quartz.m b/android/utils/display-quartz.m
new file mode 100644
index 0000000..594657e
--- /dev/null
+++ b/android/utils/display-quartz.m
@@ -0,0 +1,111 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+/* this is the Quartz-specific implementation of
+ * <android/utils/display.h>
+ */
+
+#include "android/utils/display.h"
+#include "android/utils/debug.h"
+
+#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
+
+#include <stdio.h>
+#include <Cocoa/Cocoa.h>
+#include <Carbon/Carbon.h>
+#include <SDL_syswm.h>
+
+int
+get_monitor_resolution( int  *px_dpi, int  *py_dpi )
+{
+    fprintf(stderr, "emulator: FIXME: implement get_monitor_resolution on OS X\n" );
+    return -1;
+}
+
+int
+get_nearest_monitor_rect( int  *x, int  *y, int  *width, int  *height )
+{
+    SDL_SysWMinfo  info;
+    NSWindow*      window;
+
+    SDL_VERSION(&info.version);
+    if ( SDL_GetWMInfo(&info) < 0 ) {
+        D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError());
+        return -1;
+    }
+    window = info.nsWindowPtr;
+    if (window == NULL) {
+        D( "%s: SDL_GetWMInfo() returned NULL NSWindow ptr",
+           __FUNCTION__ );
+        return -1;
+    }
+    else
+    {
+        NSRect        frame   = [ window frame ];
+        int           fx1     = frame.origin.x;
+        int           fy1     = frame.origin.y;
+        int           fx2     = frame.size.width + fx1;
+        int           fy2     = frame.size.height + fy1; 
+        NSArray*      screens = [ NSScreen screens ];
+        unsigned int  count   = [ screens count ];
+        int           bestScreen = -1;
+        int           bestArea = 0;
+
+        unsigned int  n;
+        printf( "window frame (%d,%d) (%d,%d)\n", fx1, fy1, fx2, fy2 );
+
+        /* we need to compute which screen has the most window pixels */
+        for (n = 0; n < count; n++) {
+            NSScreen*  screen = [ screens objectAtIndex: n ];
+            NSRect     vis    = [ screen visibleFrame ];
+            int        vx1    = vis.origin.x;
+            int        vy1    = vis.origin.y;
+            int        vx2    = vis.size.width + vx1;
+            int        vy2    = vis.size.height + vy1;
+            int        cx1, cx2, cy1, cy2, cArea;
+
+            //printf( "screen %d/%d  frame (%d,%d) (%d,%d)\n", n+1, count,
+            //        vx1, vy1, vx2, vy2 );
+
+            if (fx1 >= vx2 || vx1 >= fx2 || fy1 >= vy2 || vy1 >= fy2)
+                continue;
+
+            cx1 = (fx1 < vx1) ? vx1 : fx1;
+            cx2 = (fx2 > vx2) ? vx2 : fx2;
+            cy1 = (fy1 < vy1) ? vy1 : fy1;
+            cy2 = (fy2 > vy2) ? vy2 : fy2;
+
+            if (cx1 >= cx2 || cy1 >= cy2)
+                continue;
+
+            cArea = (cx2-cx1)*(cy2-cy1);
+
+            if (bestScreen < 0 || cArea > bestArea) {
+                bestScreen = n;
+                bestArea   = cArea;
+            }
+        }
+        if (bestScreen < 0)
+            bestScreen = 0;
+
+        {
+            NSScreen*  screen = [ screens objectAtIndex: bestScreen ];
+            NSRect     vis    = [ screen visibleFrame ];
+
+            *x      = vis.origin.x;
+            *y      = vis.origin.y;
+            *width  = vis.size.width;
+            *height = vis.size.height; 
+        }
+        return 0;
+    }
+};
diff --git a/android/utils/display.c b/android/utils/display.c
new file mode 100644
index 0000000..e1ba507
--- /dev/null
+++ b/android/utils/display.c
@@ -0,0 +1,245 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/utils/display.h"
+#include "android/utils/debug.h"
+
+#define  D(...)   VERBOSE_PRINT(init,__VA_ARGS__)
+
+/** HOST RESOLUTION SETTINGS
+ **
+ ** return the main monitor's DPI resolution according to the host device
+ ** beware: this is not always reliable or even obtainable.
+ **
+ ** returns 0 on success, or -1 in case of error (e.g. the system returns funky values)
+ **/
+
+/** NOTE: the following code assumes that we exclusively use X11 on Linux, and Quartz on OS X
+ **/
+
+#ifdef _WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <SDL_syswm.h>
+
+int
+get_monitor_resolution( int  *px_dpi, int  *py_dpi )
+{
+    HDC  displayDC = CreateDC( "DISPLAY", NULL, NULL, NULL );
+    int  xdpi, ydpi;
+
+    if (displayDC == NULL) {
+        D( "%s: could not get display DC\n", __FUNCTION__ );
+        return -1;
+    }
+    xdpi = GetDeviceCaps( displayDC, LOGPIXELSX );
+    ydpi = GetDeviceCaps( displayDC, LOGPIXELSY );
+
+    /* sanity checks */
+    if (xdpi < 20 || xdpi > 400 || ydpi < 20 || ydpi > 400) {
+        D( "%s: bad resolution: xpi=%d ydpi=%d", __FUNCTION__,
+                xdpi, ydpi );
+        return -1;
+    }
+
+    *px_dpi = xdpi;
+    *py_dpi = ydpi;
+    return 0;
+}
+
+int
+get_nearest_monitor_rect( int  *x, int  *y, int  *width, int  *height )
+{
+    SDL_SysWMinfo  info;
+    HMONITOR       monitor;
+    MONITORINFO    monitorInfo;
+
+    SDL_VERSION(&info.version);
+
+    if ( !SDL_GetWMInfo(&info) ) {
+        D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError());
+        return -1;
+    }
+
+    monitor = MonitorFromWindow( info.window, MONITOR_DEFAULTTONEAREST );
+    monitorInfo.cbSize = sizeof(monitorInfo);
+    GetMonitorInfo( monitor, &monitorInfo );
+
+    *x      = monitorInfo.rcMonitor.left;
+    *y      = monitorInfo.rcMonitor.top;
+    *width  = monitorInfo.rcMonitor.right - *x;
+    *height = monitorInfo.rcMonitor.bottom - *y;
+
+    D("%s: found (x,y,w,h)=(%d,%d,%d,%d)", __FUNCTION__,
+      *x, *y, *width, *height);
+
+    return 0;
+}
+
+
+#elif defined __APPLE__
+
+/* the real implementation is in display-quartz.m, but
+ * unfortunately, the Android build system doesn't know
+ * how to build Objective-C sources, so provide stubs
+ * here instead.
+ *
+ * CONFIG_NO_COCOA is set by Makefile.android
+ */
+
+#ifdef CONFIG_NO_COCOA
+int
+get_monitor_resolution( int  *px_dpi, int  *py_dpi )
+{
+    return -1;
+}
+
+int
+get_nearest_monitor_rect( int  *x, int  *y, int  *width, int  *height )
+{
+    return -1;
+}
+#endif /* CONFIG_NO_COCOA */
+
+#else  /* Linux and others */
+#include <SDL.h>
+#include <SDL_syswm.h>
+#include <dlfcn.h>
+#include <X11/Xlib.h>
+#define  MM_PER_INCH   25.4
+
+#define  DYNLINK_FUNCTIONS  \
+    DYNLINK_FUNC(int,XDefaultScreen,(Display*)) \
+    DYNLINK_FUNC(int,XDisplayWidth,(Display*,int)) \
+    DYNLINK_FUNC(int,XDisplayWidthMM,(Display*,int)) \
+    DYNLINK_FUNC(int,XDisplayHeight,(Display*,int)) \
+    DYNLINK_FUNC(int,XDisplayHeightMM,(Display*,int)) \
+
+#define  DYNLINK_FUNCTIONS_INIT \
+    x11_dynlink_init
+
+#include "dynlink.h"
+
+static int    x11_lib_inited;
+static void*  x11_lib;
+
+int
+x11_lib_init( void )
+{
+    if (!x11_lib_inited) {
+        x11_lib_inited = 1;
+
+        x11_lib = dlopen( "libX11.so", RTLD_NOW );
+
+        if (x11_lib == NULL) {
+            x11_lib = dlopen( "libX11.so.6", RTLD_NOW );
+        }
+        if (x11_lib == NULL) {
+            D("%s: Could not find libX11.so on this machine",
+              __FUNCTION__);
+            return -1;
+        }
+
+        if (x11_dynlink_init(x11_lib) < 0) {
+            D("%s: didn't find necessary symbols in libX11.so",
+              __FUNCTION__);
+            dlclose(x11_lib);
+            x11_lib = NULL;
+        }
+    }
+    return x11_lib ? 0 : -1;
+}
+
+
+int
+get_monitor_resolution( int  *px_dpi, int  *py_dpi )
+{
+    SDL_SysWMinfo info;
+    Display*      display;
+    int           screen;
+    int           width, width_mm, height, height_mm, xdpi, ydpi;
+
+    SDL_VERSION(&info.version);
+
+    if ( !SDL_GetWMInfo(&info) ) {
+        D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError());
+        return -1;
+    }
+
+    if (x11_lib_init() < 0)
+        return -1;
+
+    display = info.info.x11.display;
+    screen  = FF(XDefaultScreen)(display);
+
+    width     = FF(XDisplayWidth)(display, screen);
+    width_mm  = FF(XDisplayWidthMM)(display, screen);
+    height    = FF(XDisplayHeight)(display, screen);
+    height_mm = FF(XDisplayHeightMM)(display, screen);
+
+    if (width_mm <= 0 || height_mm <= 0) {
+        D( "%s: bad screen dimensions: width_mm = %d, height_mm = %d",
+                __FUNCTION__, width_mm, height_mm);
+        return -1;
+    }
+
+    D( "%s: found screen width=%d height=%d width_mm=%d height_mm=%d",
+            __FUNCTION__, width, height, width_mm, height_mm );
+
+    xdpi = width  * MM_PER_INCH / width_mm;
+    ydpi = height * MM_PER_INCH / height_mm;
+
+    if (xdpi < 20 || xdpi > 400 || ydpi < 20 || ydpi > 400) {
+        D( "%s: bad resolution: xpi=%d ydpi=%d", __FUNCTION__,
+                xdpi, ydpi );
+        return -1;
+    }
+
+    *px_dpi = xdpi;
+    *py_dpi = ydpi;
+
+    return 0;
+}
+
+int
+get_nearest_monitor_rect( int  *x, int *y, int  *width, int  *height )
+{
+    SDL_SysWMinfo info;
+    Display*      display;
+    int           screen;
+
+    SDL_VERSION(&info.version);
+
+    if ( !SDL_GetWMInfo(&info) ) {
+        D( "%s: SDL_GetWMInfo() failed: %s", __FUNCTION__, SDL_GetError());
+        return -1;
+    }
+
+    if (x11_lib_init() < 0)
+        return -1;
+
+    display = info.info.x11.display;
+    screen  = FF(XDefaultScreen)(display);
+
+    *x      = 0;
+    *y      = 0;
+    *width  = FF(XDisplayWidth)(display, screen);
+    *height = FF(XDisplayHeight)(display, screen);
+
+    D("%s: found (x,y,w,h)=(%d,%d,%d,%d)", __FUNCTION__,
+      *x, *y, *width, *height);
+
+    return 0;
+}
+
+#endif
diff --git a/android/utils/display.h b/android/utils/display.h
new file mode 100644
index 0000000..7254aca
--- /dev/null
+++ b/android/utils/display.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UTILS_DISPLAY_H
+#define _ANDROID_UTILS_DISPLAY_H
+
+/** HOST RESOLUTION SETTINGS
+ **
+ ** return the main monitor's DPI resolution according to the host device
+ ** beware: this is not always reliable or even obtainable.
+ **
+ ** returns 0 on success, or -1 in case of error (e.g. the system returns funky values)
+ **/
+extern  int    get_monitor_resolution( int  *px_dpi, int  *py_dpi );
+
+/** return the size in pixels of the nearest monitor for the current window.
+ ** this is used to implement full-screen presentation mode.
+ **/
+
+extern  int    get_nearest_monitor_rect( int  *x, int *y, int *width, int *height );
+
+#endif /* _ANDROID_UTILS_DISPLAY_H */
diff --git a/android/utils/filelock.c b/android/utils/filelock.c
new file mode 100644
index 0000000..a8b18da
--- /dev/null
+++ b/android/utils/filelock.c
@@ -0,0 +1,414 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/utils/filelock.h"
+#include "android/utils/path.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#ifdef _WIN32
+#  include <process.h>
+#  include <windows.h>
+#  include <tlhelp32.h>
+#else
+#  include <sys/types.h>
+#  include <unistd.h>
+#  include <signal.h>
+#endif
+
+#define  D(...)  ((void)0)
+
+#ifndef CHECKED
+#  ifdef _WIN32
+#    define   CHECKED(ret, call)    (ret) = (call)
+#  else
+#    define   CHECKED(ret, call)    do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
+#  endif
+#endif
+
+/** FILE LOCKS SUPPORT
+ **
+ ** a FileLock is useful to prevent several emulator instances from using the same
+ ** writable file (e.g. the userdata.img disk images).
+ **
+ ** create a FileLock object with filelock_create(), ithis function should return NULL
+ ** only if the corresponding file path could not be locked.
+ **
+ ** all file locks are automatically released and destroyed when the program exits.
+ ** the filelock_lock() function can also detect stale file locks that can linger
+ ** when the emulator crashes unexpectedly, and will happily clean them for you
+ **
+ **  here's how it works, three files are used:
+ **     file  - the data file accessed by the emulator
+ **     lock  - a lock file  (file + '.lock')
+ **     temp  - a temporary file make unique with mkstemp
+ **
+ **  when locking:
+ **      create 'temp' and store our pid in it
+ **      attemp to link 'lock' to 'temp'
+ **         if the link succeeds, we obtain the lock
+ **      unlink 'temp'
+ **
+ **  when unlocking:
+ **      unlink 'lock'
+ **
+ **
+ **  on Windows, 'lock' is a directory name. locking is equivalent to
+ **  creating it...
+ **
+ **/
+
+struct FileLock
+{
+  const char*  file;
+  const char*  lock;
+  char*        temp;
+  int          locked;
+  FileLock*    next;
+};
+
+/* used to cleanup all locks at emulator exit */
+static FileLock*   _all_filelocks;
+
+
+#define  LOCK_NAME   ".lock"
+#define  TEMP_NAME   ".tmp-XXXXXX"
+
+#ifdef _WIN32
+#define  PIDFILE_NAME  "pid"
+#endif
+
+/* returns 0 on success, -1 on failure */
+static int
+filelock_lock( FileLock*  lock )
+{
+    int    ret;
+#ifdef _WIN32
+    int  pidfile_fd = -1;
+
+    ret = _mkdir( lock->lock );
+    if (ret < 0) {
+        if (errno == ENOENT) {
+            D( "could not access directory '%s', check path elements", lock->lock );
+            return -1;
+        } else if (errno != EEXIST) {
+            D( "_mkdir(%s): %s", lock->lock, strerror(errno) );
+            return -1;
+        }
+
+        /* if we get here, it's because the .lock directory already exists */
+        /* check to see if there is a pid file in it                       */
+        D("directory '%s' already exist, waiting a bit to ensure that no other emulator instance is starting", lock->lock );
+        {
+            int  _sleep = 200;
+            int  tries;
+
+            for ( tries = 4; tries > 0; tries-- )
+            {
+                pidfile_fd = open( lock->temp, O_RDONLY );
+
+                if (pidfile_fd >= 0)
+                    break;
+
+                Sleep( _sleep );
+                _sleep *= 2;
+            }
+        }
+
+        if (pidfile_fd < 0) {
+            D( "no pid file in '%s', assuming stale directory", lock->lock );
+        }
+        else
+        {
+            /* read the pidfile, and check wether the corresponding process is still running */
+            char            buf[16];
+            int             len, lockpid;
+            HANDLE          processSnapshot;
+            PROCESSENTRY32  pe32;
+            int             is_locked = 0;
+
+            len = read( pidfile_fd, buf, sizeof(buf)-1 );
+            if (len < 0) {
+                D( "could not read pid file '%s'", lock->temp );
+                close( pidfile_fd );
+                return -1;
+            }
+            buf[len] = 0;
+            lockpid  = atoi(buf);
+
+            /* PID 0 is the IDLE process, and 0 is returned in case of invalid input */
+            if (lockpid == 0)
+                lockpid = -1;
+
+            close( pidfile_fd );
+
+            pe32.dwSize     = sizeof( PROCESSENTRY32 );
+            processSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
+
+            if ( processSnapshot == INVALID_HANDLE_VALUE ) {
+                D( "could not retrieve the list of currently active processes\n" );
+                is_locked = 1;
+            }
+            else if ( !Process32First( processSnapshot, &pe32 ) )
+            {
+                D( "could not retrieve first process id\n" );
+                CloseHandle( processSnapshot );
+                is_locked = 1;
+            }
+            else
+            {
+                do {
+                    if (pe32.th32ProcessID == lockpid) {
+                        is_locked = 1;
+                        break;
+                    }
+                } while (Process32Next( processSnapshot, &pe32 ) );
+
+                CloseHandle( processSnapshot );
+            }
+
+            if (is_locked) {
+                D( "the file '%s' is locked by process ID %d\n", lock->file, lockpid );
+                return -1;
+            }
+        }
+    }
+
+    /* write our PID into the pid file */
+    pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC );
+    if (pidfile_fd < 0) {
+        if (errno == EACCES) {
+            if ( path_delete_file( lock->temp ) < 0 ) {
+                D( "could not remove '%s': %s\n", lock->temp, strerror(errno) );
+                return -1;
+            }
+            pidfile_fd = open( lock->temp, O_WRONLY | O_CREAT | O_TRUNC );
+        }
+        if (pidfile_fd < 0) {
+            D( "could not create '%s': %s\n", lock->temp, strerror(errno) );
+            return -1;
+        }
+    }
+
+    {
+        char  buf[16];
+        sprintf( buf, "%ld", GetCurrentProcessId() );
+        ret = write( pidfile_fd, buf, strlen(buf) );
+        close(pidfile_fd);
+        if (ret < 0) {
+            D( "could not write PID to '%s'\n", lock->temp );
+            return -1;
+        }
+    }
+
+    lock->locked = 1;
+    return 0;
+#else
+    int    temp_fd = -1;
+    int    lock_fd = -1;
+    int    rc, tries, _sleep;
+    FILE*  f = NULL;
+    char   pid[8];
+    struct stat  st_temp;
+
+    strcpy( lock->temp, lock->file );
+    strcat( lock->temp, TEMP_NAME );
+    temp_fd = mkstemp( lock->temp );
+
+    if (temp_fd < 0) {
+        D("cannot create locking temp file '%s'", lock->temp );
+        goto Fail;
+    }
+
+    sprintf( pid, "%d", getpid() );
+    ret = write( temp_fd, pid, strlen(pid)+1 );
+    if (ret < 0) {
+        D( "cannot write to locking temp file '%s'", lock->temp);
+        goto Fail;
+    }
+    close( temp_fd );
+    temp_fd = -1;
+
+    CHECKED(rc, lstat( lock->temp, &st_temp ));
+    if (rc < 0) {
+        D( "can't properly stat our locking temp file '%s'", lock->temp );
+        goto Fail;
+    }
+
+    /* now attempt to link the temp file to the lock file */
+    _sleep = 0;
+    for ( tries = 4; tries > 0; tries-- )
+    {
+        struct stat  st_lock;
+        int          rc;
+
+        if (_sleep > 0) {
+            if (_sleep > 2000000) {
+                D( "cannot acquire lock file '%s'", lock->lock );
+                goto Fail;
+            }
+            usleep( _sleep );
+        }
+        _sleep += 200000;
+
+        /* the return value of link() is buggy on NFS */
+        CHECKED(rc, link( lock->temp, lock->lock ));
+
+        CHECKED(rc, lstat( lock->lock, &st_lock ));
+        if (rc == 0 &&
+            st_temp.st_rdev == st_lock.st_rdev &&
+            st_temp.st_ino  == st_lock.st_ino  )
+        {
+            /* SUCCESS */
+            lock->locked = 1;
+            CHECKED(rc, unlink( lock->temp ));
+            return 0;
+        }
+
+        /* if we get there, it means that the link() call failed */
+        /* check the lockfile to see if it is stale              */
+        if (rc == 0) {
+            char    buf[16];
+            time_t  now;
+            int     lockpid = 0;
+            int     lockfd;
+            int     stale = 2;  /* means don't know */
+            struct stat  st;
+
+            CHECKED(rc, time( &now));
+            st.st_mtime = now - 120;
+
+            CHECKED(lockfd, open( lock->lock,O_RDONLY ));
+            if ( lockfd >= 0 ) {
+                int  len;
+
+                CHECKED(len, read( lockfd, buf, sizeof(buf)-1 ));
+                buf[len] = 0;
+                lockpid = atoi(buf);
+
+                CHECKED(rc, fstat( lockfd, &st ));
+                if (rc == 0)
+                  now = st.st_atime;
+
+                CHECKED(rc, close(lockfd));
+            }
+            /* if there is a PID, check that it is still alive */
+            if (lockpid > 0) {
+                CHECKED(rc, kill( lockpid, 0 ));
+                if (rc == 0 || errno == EPERM) {
+                    stale = 0;
+                } else if (rc < 0 && errno == ESRCH) {
+                    stale = 1;
+                }
+            }
+            if (stale == 2) {
+                /* no pid, stale if the file is older than 1 minute */
+                stale = (now >= st.st_mtime + 60);
+            }
+
+            if (stale) {
+                D( "removing stale lockfile '%s'", lock->lock );
+                CHECKED(rc, unlink( lock->lock ));
+                _sleep = 0;
+                tries++;
+            }
+        }
+    }
+    D("file '%s' is already in use by another process", lock->file );
+
+Fail:
+    if (f)
+        fclose(f);
+
+    if (temp_fd >= 0) {
+        close(temp_fd);
+    }
+
+    if (lock_fd >= 0) {
+        close(lock_fd);
+    }
+
+    unlink( lock->lock );
+    unlink( lock->temp );
+    return -1;
+#endif
+}
+
+void
+filelock_release( FileLock*  lock )
+{
+    if (lock->locked) {
+#ifdef _WIN32
+        path_delete_file( (char*)lock->temp );
+        rmdir( (char*)lock->lock );
+#else
+        unlink( (char*)lock->lock );
+#endif
+        lock->locked = 0;
+    }
+}
+
+static void
+filelock_atexit( void )
+{
+  FileLock*  lock;
+
+  for (lock = _all_filelocks; lock != NULL; lock = lock->next)
+     filelock_release( lock );
+}
+
+/* create a file lock */
+FileLock*
+filelock_create( const char*  file )
+{
+    int    file_len = strlen(file);
+    int    lock_len = file_len + sizeof(LOCK_NAME);
+#ifdef _WIN32
+    int    temp_len = lock_len + 1 + sizeof(PIDFILE_NAME);
+#else
+    int    temp_len = file_len + sizeof(TEMP_NAME);
+#endif
+    int    total_len = sizeof(FileLock) + file_len + lock_len + temp_len + 3;
+
+    FileLock*  lock = malloc(total_len);
+
+    lock->file = (const char*)(lock + 1);
+    memcpy( (char*)lock->file, file, file_len+1 );
+
+    lock->lock = lock->file + file_len + 1;
+    memcpy( (char*)lock->lock, file, file_len+1 );
+    strcat( (char*)lock->lock, LOCK_NAME );
+
+    lock->temp    = (char*)lock->lock + lock_len + 1;
+#ifdef _WIN32
+    snprintf( (char*)lock->temp, temp_len, "%s\\" PIDFILE_NAME, lock->lock );
+#else
+    lock->temp[0] = 0;
+#endif
+    lock->locked = 0;
+
+    if (filelock_lock(lock) < 0) {
+        free(lock);
+        return NULL;
+    }
+
+    lock->next     = _all_filelocks;
+    _all_filelocks = lock;
+
+    if (lock->next == NULL)
+        atexit( filelock_atexit );
+
+    return lock;
+}
diff --git a/android/utils/filelock.h b/android/utils/filelock.h
new file mode 100644
index 0000000..9bc86b5
--- /dev/null
+++ b/android/utils/filelock.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UTILS_FILELOCK_H
+#define _ANDROID_UTILS_FILELOCK_H
+
+/** FILE LOCKS SUPPORT
+ **
+ ** a FileLock is useful to prevent several emulator instances from using the same
+ ** writable file (e.g. the userdata.img disk images).
+ **
+ ** create a FileLock object with filelock_create(), the function will return
+ ** NULL only if the corresponding path is already locked by another emulator
+ ** of if the path is read-only.
+ **
+ ** note that 'path' can designate a non-existing path and that the lock creation
+ ** function can detect stale file locks that can longer when the emulator
+ ** crashes unexpectedly, and will happily clean them for you.
+ **
+ ** you can call filelock_release() to release a file lock explicitely. otherwise
+ ** all file locks are automatically released when the program exits.
+ **/
+
+typedef struct FileLock  FileLock;
+
+extern FileLock*  filelock_create ( const char*  path );
+extern void       filelock_release( FileLock*  lock );
+
+#endif /* _ANDROID_UTILS_FILELOCK_H */
diff --git a/android/utils/ini.c b/android/utils/ini.c
new file mode 100644
index 0000000..95bb4e3
--- /dev/null
+++ b/android/utils/ini.c
@@ -0,0 +1,416 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/ini.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+#include "android/utils/debug.h"
+#include "android/utils/system.h" /* for ASTRDUP */
+#include "android/utils/bufprint.h"
+#include "osdep.h"
+
+/* W() is used to print warnings, D() to print debugging info */
+#define  W(...)   dwarning(__VA_ARGS__)
+#define  D(...)   VERBOSE_PRINT(avd_config,__VA_ARGS__)
+
+/* a simple .ini file parser and container for Android
+ * no sections support. see android/utils/ini.h for
+ * more details on the supported file format.
+ */
+typedef struct {
+    char*  key;
+    char*  value;
+} IniPair;
+
+struct IniFile {
+    int       numPairs;
+    int       maxPairs;
+    IniPair*  pairs;
+};
+
+void
+iniFile_free( IniFile*  i )
+{
+    int  nn;
+    for (nn = 0; nn < i->numPairs; nn++) {
+        AFREE(i->pairs[nn].key);
+        i->pairs[nn].key   = NULL;
+        i->pairs[nn].value = NULL;
+    }
+    AFREE(i->pairs);
+    AFREE(i);
+}
+
+static IniFile*
+iniFile_alloc( void )
+{
+    IniFile*  i;
+
+    ANEW0(i);
+    return i;
+}
+
+static void
+iniFile_addPair( IniFile*  i, const char*  key,   int  keyLen,
+                                    const char*  value, int  valueLen )
+{
+    IniPair*  pair;
+
+    if (i->numPairs >= i->maxPairs) {
+        int       oldMax = i->maxPairs;
+        int       newMax = oldMax + (oldMax >> 1) + 4;
+
+        AARRAY_RENEW(i->pairs, newMax);
+        i->maxPairs = newMax;
+    }
+
+    pair = i->pairs + i->numPairs;
+
+    AARRAY_NEW(pair->key, keyLen + valueLen + 2);
+    memcpy(pair->key, key, keyLen);
+    pair->key[keyLen] = 0;
+
+    pair->value = pair->key + keyLen + 1;
+    memcpy(pair->value, value, valueLen);
+    pair->value[valueLen] = 0;
+
+    i->numPairs += 1;
+}
+
+const char*
+iniFile_getValue( IniFile*  i, const char*  key )
+{
+    if (i && key) {
+        int  nn;
+
+        for (nn = 0; nn < i->numPairs; nn++) {
+            if (!strcmp(i->pairs[nn].key,key))
+                return i->pairs[nn].value;
+        }
+    }
+    return NULL;
+}
+
+int
+iniFile_getPairCount( IniFile*  i )
+{
+    return i ? i->numPairs : 0;
+}
+
+void
+iniFile_getPair( IniFile*   i, 
+                    int           index, 
+                    const char*  *pKey,
+                    const char*  *pValue )
+{
+    const char*  key   = NULL;
+    const char*  value = NULL;
+
+    if (i && index >= 0 && index < i->numPairs) {
+        key   = i->pairs[index].key;
+        value = i->pairs[index].value;
+    }
+    *pKey   = key;
+    *pValue = value;
+}
+
+/* NOTE: we avoid using <ctype.h> functions to avoid locale-specific
+ *       behaviour that can be the source of strange bugs.
+ */
+
+static const char*
+skipSpaces( const char* p )
+{
+    while (*p == ' ' || *p == '\t')
+        p ++;
+    return p;
+}
+
+static const char*
+skipToEOL( const char*  p )
+{
+    while (*p && (*p != '\n' && *p != '\r'))
+        p ++;
+
+    if (*p) {
+        p ++;
+        if (p[-1] == '\r' && p[0] == '\n')
+            p ++;
+    }
+    return p;
+}
+
+static int
+isKeyStartChar( int  c )
+{
+    return ((unsigned)(c-'a') < 26 ||
+            (unsigned)(c-'A') < 26 ||
+            c == '_');
+}
+
+static int
+isKeyChar( int  c )
+{
+    return isKeyStartChar(c) || ((unsigned)(c-'0') < 10) || (c == '.') || (c == '-');
+}
+
+IniFile*
+iniFile_newFromMemory( const char*  text, const char*  fileName )
+{
+    const char*  p      = text;
+    IniFile*     ini    = iniFile_alloc();
+    int          lineno = 0;
+
+    if (!fileName)
+        fileName = "<memoryFile>";
+
+    D("%s: parsing as .ini file", fileName);
+
+    while (*p) {
+        const char*  key;
+        int          keyLen;
+        const char*  value;
+        int          valueLen;
+
+        lineno += 1;
+
+        /* skip leading whitespace */
+        p = skipSpaces(p);
+
+        /* skip comments and empty lines */
+        if (*p == 0 || *p == ';' || *p == '#' || *p == '\n' || *p == '\r') {
+            p = skipToEOL(p);
+            continue;
+        }
+
+        /* check the key name */
+        key = p++;
+        if (!isKeyStartChar(*key)) {
+            p = skipToEOL(p);
+            W("%4d: key name doesn't start with valid character. line ignored",
+              lineno);
+            continue;
+        }
+
+        while (isKeyChar(*p))
+            p++;
+
+        keyLen = p - key;
+        p      = skipSpaces(p);
+
+        /* check the equal */
+        if (*p != '=') {
+            W("%4d: missing expected assignment operator (=). line ignored",
+              lineno);
+            p = skipToEOL(p);
+            continue;
+        }
+        p += 1;
+
+        /* skip spaces before the value */
+        p     = skipSpaces(p);
+        value = p;
+
+        /* find the value */
+        while (*p && (*p != '\n' && *p != '\r'))
+            p += 1;
+
+        /* remove trailing spaces */
+        while (p > value && (p[-1] == ' ' || p[-1] == '\t'))
+            p --;
+
+        valueLen = p - value;
+
+        iniFile_addPair(ini, key, keyLen, value, valueLen);
+        D("%4d: KEY='%.*s' VALUE='%.*s'", lineno,
+          keyLen, key, valueLen, value);
+
+        p = skipToEOL(p);
+    }
+
+    D("%s: parsing finished", fileName);
+
+    return ini;
+}
+
+IniFile*
+iniFile_newFromFile( const char*  filepath )
+{
+    FILE*        fp = fopen(filepath, "rt");
+    char*        text;
+    long         size;
+    IniFile*     ini = NULL;
+
+    if (fp == NULL) {
+        D("could not open .ini file: %s: %s",
+          filepath, strerror(errno));
+        return NULL;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    size = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+
+    /* avoid reading a very large file that was passed by mistake
+     * this threshold is quite liberal.
+     */
+#define  MAX_INI_FILE_SIZE  655360
+
+    if (size < 0 || size > MAX_INI_FILE_SIZE) {
+        W("hardware configuration file '%s' too large (%ld bytes)",
+          filepath, size);
+        goto EXIT;
+    }
+
+    /* read the file, add a sentinel at the end of it */
+    AARRAY_NEW(text, size+1);
+    fread(text, 1, size, fp);
+    text[size] = 0;
+
+    ini = iniFile_newFromMemory(text, filepath);
+    AFREE(text);
+
+EXIT:
+    fclose(fp);
+    return ini;
+}
+
+int
+iniFile_saveToFile( IniFile*  f, const char*  filepath )
+{
+    FILE*  fp = fopen(filepath, "wt");
+    IniPair*  pair    = f->pairs;
+    IniPair*  pairEnd = pair + f->numPairs;
+    int       result  = 0;
+
+    if (fp == NULL) {
+        D("could not create .ini file: %s: %s",
+          filepath, strerror(errno));
+        return -1;
+    }
+
+    for ( ; pair < pairEnd; pair++ ) {
+        char  temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+        p = bufprint(temp, end, "%s = %s\n", pair->key, pair->value);
+        if (fwrite(temp, p - temp, 1, fp) != 1) {
+            result = -1;
+            break;
+        }
+    }
+
+    fclose(fp);
+    return result;
+}
+
+char*
+iniFile_getString( IniFile*  f, const char*  key )
+{
+    const char*  val = iniFile_getValue(f, key);
+
+    if (!val)
+        return NULL;
+
+    return ASTRDUP(val);
+}
+
+int
+iniFile_getInteger( IniFile*  f, const char*  key, int  defaultValue )
+{
+    const char*  valueStr = iniFile_getValue(f, key);
+    int          value    = defaultValue;
+
+    if (valueStr != NULL) {
+        char*  end;
+        long   l = strtol(valueStr, &end, 10);
+        if (end != NULL && end[0] == 0 && (int)l == l)
+            value = l;
+    }
+    return value;
+}
+
+double
+iniFile_getDouble( IniFile*  f, const char*  key, double  defaultValue )
+{
+    const char*  valueStr = iniFile_getValue(f, key);
+    double       value    = defaultValue;
+
+    if (valueStr != NULL) {
+        char*   end;
+        double  d = strtod(valueStr, &end);
+        if (end != NULL && end[0] == 0)
+            value = d;
+    }
+    return value;
+}
+
+int
+iniFile_getBoolean( IniFile*  f, const char*  key, const char*  defaultValue )
+{
+    const char*  value  = iniFile_getValue(f, key);
+
+    if (!value)
+        value = defaultValue;
+
+    if (!strcmp(value,"1")    ||
+        !strcmp(value,"yes")  ||
+        !strcmp(value,"YES")  ||
+        !strcmp(value,"true") ||
+        !strcmp(value,"TRUE"))
+    {
+        return 1;
+    }
+    else
+        return 0;
+}
+
+int64_t
+iniFile_getDiskSize( IniFile*  f, const char*  key, const char*  defaultValue )
+{
+    const char*  valStr = iniFile_getValue(f, key);
+    int64_t      value  = 0;
+
+    if (!valStr)
+        valStr = defaultValue;
+
+    if (valStr != NULL) {
+        char*  end;
+
+        value = strtoll(valStr, &end, 10);
+        if (*end == 'k' || *end == 'K')
+            value *= 1024ULL;
+        else if (*end == 'm' || *end == 'M')
+            value *= 1024*1024ULL;
+        else if (*end == 'g' || *end == 'G')
+            value *= 1024*1024*1024ULL;
+    }
+    return value;
+}
+
+int64_t
+iniFile_getInt64( IniFile*  f, const char*  key, int64_t  defaultValue )
+{
+    const char*  valStr = iniFile_getValue(f, key);
+    int64_t      value  = defaultValue;
+
+    if (valStr != NULL) {
+        char*    end;
+        int64_t  d;
+
+        d = strtoll(valStr, &end, 10);
+        if (end != NULL && end[0] == 0)
+            value = d;
+    }
+    return value;
+}
+
diff --git a/android/utils/ini.h b/android/utils/ini.h
new file mode 100644
index 0000000..a176bfe
--- /dev/null
+++ b/android/utils/ini.h
@@ -0,0 +1,131 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_INI_H
+#define _ANDROID_UTILS_INI_H
+
+#include <stdint.h>
+
+/* the emulator supports a simple .ini file format for its configuration
+ * files. Here's the BNF for it:
+ *
+ *  file             := <line>*
+ *  line             := <comment> | <LF> | <assignment>
+ *  comment          := (';'|'#') <noLF>* <LF>
+ *  assignment       := <space>* <keyName> <space>* '=' <space>* <valueString> <space>* <LF>
+ *  keyName          := <keyNameStartChar> <keyNameChar>*
+ *  keyNameStartChar := [A-Za-z_]
+ *  keyNameChar      := [A-Za-z0-9_.-]
+ *  valueString      := <noLF>*
+ *  space            := ' ' | '\t'
+ *  LF               := '\r\n' | '\n' | '\r'
+ *  noLF             := [^<LF>]
+ *
+ * Or, in English:
+ *
+ * - no support for sections
+ * - empty lines are ignored, as well as lines beginning with ';' or '#'
+ * - lines must be of the form: "<keyName> = <value>"
+ * - key names must start with a letter or an underscore
+ * - other key name characters can be letters, digits, underscores, dots or dashes
+ *
+ * - leading and trailing space are allowed and ignored before/after the key name
+ *   and before/after the value
+ *
+ * - there is no restriction on the value, except that it can't contain
+ *   leading/trailing space/tab characters or newline/charfeed characters
+ *
+ * - empty values are possible, and will be stored as an empty string.
+ * - any badly formatted line is discarded (and will print a warning)
+ *
+ */
+
+/* an opaque structure used to model an .ini configuration file */
+typedef struct IniFile   IniFile;
+
+/* creates a new IniFile object from a config file loaded in memory.
+ *  'fileName' is only used when writing a warning to stderr in case
+ * of badly formed output
+ */
+IniFile*  iniFile_newFromMemory( const char*  text, const char*  fileName  );
+
+/* creates a new IniFile object from a file path,
+ * returns NULL if the file cannot be opened.
+ */
+IniFile*  iniFile_newFromFile( const char*  filePath);
+
+/* try to write an IniFile into a given file.
+ * returns 0 on success, -1 on error (see errno for error code)
+ */
+int       iniFile_saveToFile( IniFile*  f, const char*  filePath );
+
+/* free an IniFile object */
+void      iniFile_free( IniFile*  f );
+
+/* returns the number of (key.value) pairs in an IniFile */
+int       iniFile_getPairCount( IniFile*  f );
+
+/* return a specific (key,value) pair from an IniFile.
+ * if the index is not correct, both '*pKey' and '*pValue' will be
+ * set to NULL.
+ *
+ * you should probably use iniFile_getValue() and its variants instead
+ */
+void      iniFile_getPair( IniFile*     f,
+                           int          index,
+                           const char* *pKey,
+                           const char* *pValue );
+
+/* returns the value of a given key from an IniFile.
+ * NULL if the key is not assigned in the corresponding configuration file
+ */
+const char*  iniFile_getValue( IniFile*  f, const char*  key );
+
+/* returns a copy of the value of a given key, or NULL
+ */
+char*   iniFile_getString( IniFile*  f, const char*  key );
+
+/* returns an integer value, or a default in case the value string is
+ * missing or badly formatted
+ */
+int     iniFile_getInteger( IniFile*  f, const char*  key, int  defaultValue );
+
+/* returns a 64-bit integer value, or a default in case the value string is
+ * missing or badly formatted
+ */
+int64_t iniFile_getInt64( IniFile*  f, const char*  key, int64_t  defaultValue );
+
+/* returns a double value, or a default in case the value string is
+ * missing or badly formatted
+ */
+double  iniFile_getDouble( IniFile*  f, const char*  key, double  defaultValue );
+
+/* returns a copy of a given key's value, if any, or NULL if it is missing
+ * caller must call free() to release it */
+char*   iniFile_getString( IniFile*  f, const char*  key );
+
+/* parses a key value as a boolean. Accepted values are "1", "0", "yes", "YES",
+ * "no" and "NO". Returns either 1 or 0.
+ * note that the default value must be provided as a string too
+ */
+int     iniFile_getBoolean( IniFile*  f, const char*  key, const char*  defaultValue );
+
+/* parses a key value as a disk size. this means it can be an integer followed
+ * by a suffix that can be one of "mMkKgG" which correspond to KiB, MiB and GiB
+ * multipliers.
+ *
+ * NOTE: we consider that 1K = 1024, not 1000.
+ */
+int64_t  iniFile_getDiskSize( IniFile*  f, const char*  key, const char*  defaultValue );
+
+/* */
+
+#endif /* _ANDROID_UTILS_INI_H */
diff --git a/android/utils/misc.c b/android/utils/misc.c
new file mode 100644
index 0000000..818ab78
--- /dev/null
+++ b/android/utils/misc.c
@@ -0,0 +1,193 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/utils/misc.h"
+#include "android/utils/stralloc.h"
+#include "android/utils/debug.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern void
+print_tabular( const char** strings, int  count,
+               const char*  prefix,  int  width )
+{
+    int  nrows, ncols, r, c, n, maxw = 0;
+
+    for (n = 0; n < count; n++) {
+        int  len = strlen(strings[n]);
+        if (len > maxw)
+            maxw = len;
+    }
+    maxw += 2;
+    ncols = width/maxw;
+    nrows = (count + ncols-1)/ncols;
+
+    for (r = 0; r < nrows; r++) {
+        printf( "%s", prefix );
+        for (c = 0; c < ncols; c++) {
+            int  index = c*nrows + r;
+            if (index >= count) {
+                break;
+            }
+            printf( "%-*s", maxw, strings[index] );
+        }
+        printf( "\n" );
+    }
+}
+
+extern void
+string_translate_char( char*  str, char from, char to )
+{
+    char*  p = str;
+    while (p != NULL && (p = strchr(p, from)) != NULL)
+        *p++ = to;
+}
+
+extern void
+buffer_translate_char( char*        buff,
+                       unsigned     buffLen,
+                       const char*  src,
+                       char         fromChar,
+                       char         toChar )
+{
+    int    len = strlen(src);
+
+    if (len >= buffLen)
+        len = buffLen-1;
+
+    memcpy(buff, src, len);
+    buff[len] = 0;
+
+    string_translate_char( buff, fromChar, toChar );
+}
+
+
+/** TEMP CHAR STRINGS
+ **
+ ** implement a circular ring of temporary string buffers
+ **/
+
+typedef struct Temptring {
+    struct TempString*  next;
+    char*               buffer;
+    int                 size;
+} TempString;
+
+#define  MAX_TEMP_STRINGS   16
+
+static TempString  _temp_strings[ MAX_TEMP_STRINGS ];
+static int         _temp_string_n;
+
+extern char*
+tempstr_get( int  size )
+{
+    TempString*  t = &_temp_strings[_temp_string_n];
+
+    if ( ++_temp_string_n >= MAX_TEMP_STRINGS )
+        _temp_string_n = 0;
+
+    size += 1;  /* reserve 1 char for terminating zero */
+
+    if (t->size < size) {
+        t->buffer = realloc( t->buffer, size );
+        if (t->buffer == NULL) {
+            derror( "%s: could not allocate %d bytes",
+                    __FUNCTION__, size );
+            exit(1);
+        }
+        t->size   = size;
+    }
+    return  t->buffer;
+}
+
+extern char*
+tempstr_format( const char*  fmt, ... )
+{
+    va_list  args;
+    char*    result;
+    STRALLOC_DEFINE(s);
+    va_start(args, fmt);
+    stralloc_formatv(s, fmt, args);
+    va_end(args);
+    result = stralloc_to_tempstr(s);
+    stralloc_reset(s);
+    return result;
+}
+
+/** QUOTING
+ **
+ ** dumps a human-readable version of a string. this replaces
+ ** newlines with \n, etc...
+ **/
+
+extern const char*
+quote_bytes( const char*  str, int  len )
+{
+    STRALLOC_DEFINE(s);
+    char*  q;
+
+    stralloc_add_quote_bytes( s, str, len );
+    q = stralloc_to_tempstr( s );
+    stralloc_reset(s);
+    return q;
+}
+
+extern const char*
+quote_str( const char*  str )
+{
+    int  len = strlen(str);
+    return quote_bytes( str, len );
+}
+
+/** HEXADECIMAL CHARACTER SEQUENCES
+ **/
+
+static int
+hexdigit( int  c )
+{
+    unsigned  d;
+
+    d = (unsigned)(c - '0');
+    if (d < 10) return d;
+
+    d = (unsigned)(c - 'a');
+    if (d < 6) return d+10;
+
+    d = (unsigned)(c - 'A');
+    if (d < 6) return d+10;
+
+    return -1;
+}
+
+int
+hex2int( const uint8_t*  hex, int  len )
+{
+    int  result = 0;
+    while (len > 0) {
+        int  c = hexdigit(*hex++);
+        if (c < 0)
+            return -1;
+
+        result = (result << 4) | c;
+        len --;
+    }
+    return result;
+}
+
+void
+int2hex( uint8_t*  hex, int  len, int  val )
+{
+    static const uint8_t  hexchars[16] = "0123456789abcdef";
+    while ( --len >= 0 )
+        *hex++ = hexchars[(val >> (len*4)) & 15];
+}
diff --git a/android/utils/misc.h b/android/utils/misc.h
new file mode 100644
index 0000000..0db1e28
--- /dev/null
+++ b/android/utils/misc.h
@@ -0,0 +1,67 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_MISC_H
+#define _ANDROID_UTILS_MISC_H
+
+#include <stdint.h>
+
+/** TABULAR OUTPUT
+ **
+ ** prints a list of strings in row/column format
+ **
+ **/
+
+extern void   print_tabular( const char** strings, int  count,
+                             const char*  prefix,  int  width );
+
+/** CHARACTER TRANSLATION
+ **
+ ** converts one character into another in strings
+ **/
+
+extern void   buffer_translate_char( char*        buff,
+                                     unsigned     buffLen,
+                                     const char*  src,
+                                     char         fromChar,
+                                     char         toChar );
+
+extern void   string_translate_char( char*  str, char from, char to );
+
+/** TEMP CHAR STRINGS
+ **
+ ** implement a circular ring of temporary string buffers
+ **/
+
+extern char*  tempstr_get( int   size );
+extern char*  tempstr_format( const char*  fmt, ... );
+
+/** QUOTING
+ **
+ ** dumps a human-readable version of a string. this replaces
+ ** newlines with \n, etc...
+ **/
+
+extern const char*   quote_bytes( const char*  str, int  len );
+extern const char*   quote_str( const char*  str );
+
+/** DECIMAL AND HEXADECIMAL CHARACTER SEQUENCES
+ **/
+
+/* decodes a sequence of 'len' hexadecimal chars from 'hex' into
+ * an integer. returns -1 in case of error (i.e. badly formed chars)
+ */
+extern int    hex2int( const uint8_t*  hex, int  len );
+
+/* encodes an integer 'val' into 'len' hexadecimal charaters into 'hex' */
+extern void   int2hex( uint8_t*  hex, int  len, int  val );
+
+#endif /* _ANDROID_UTILS_MISC_H */
diff --git a/android/utils/path.c b/android/utils/path.c
new file mode 100644
index 0000000..b15b6de
--- /dev/null
+++ b/android/utils/path.c
@@ -0,0 +1,559 @@
+/* Copyright (C) 2007-2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/path.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <process.h>
+#include <shlobj.h>
+#include <tlhelp32.h>
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <limits.h>
+#include <winbase.h>
+#else
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#endif
+
+#define  D(...)  ((void)0)
+
+#ifndef CHECKED
+#  ifdef _WIN32
+#    define   CHECKED(ret, call)    (ret) = (call)
+#  else
+#    define   CHECKED(ret, call)    do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
+#  endif
+#endif
+
+/** PATH HANDLING ROUTINES
+ **
+ **  path_parent() can be used to return the n-level parent of a given directory
+ **  this understands . and .. when encountered in the input path
+ **/
+
+static __inline__ int
+ispathsep(int  c)
+{
+#ifdef _WIN32
+    return (c == '/' || c == '\\');
+#else
+    return (c == '/');
+#endif
+}
+
+char*
+path_parent( const char*  path, int  levels )
+{
+    const char*  end = path + strlen(path);
+    char*        result;
+
+    while (levels > 0) {
+        const char*  base;
+
+        /* trim any trailing path separator */
+        while (end > path && ispathsep(end[-1]))
+            end--;
+
+        base = end;
+        while (base > path && !ispathsep(base[-1]))
+            base--;
+
+        if (base <= path) /* we can't go that far */
+            return NULL;
+
+        if (end == base+1 && base[0] == '.')
+            goto Next;
+
+        if (end == base+2 && base[0] == '.' && base[1] == '.') {
+            levels += 1;
+            goto Next;
+        }
+
+        levels -= 1;
+
+    Next:
+        end = base - 1;
+    }
+    result = malloc( end-path+1 );
+    if (result != NULL) {
+        memcpy( result, path, end-path );
+        result[end-path] = 0;
+    }
+    return result;
+}
+
+static char*
+substring_dup( const char*  start, const char*  end )
+{
+    int    len    = end - start;
+    char*  result = android_alloc(len+1);
+    memcpy(result, start, len);
+    result[len] = 0;
+    return result;
+}
+
+int
+path_split( const char*  path, char* *pdirname, char* *pbasename )
+{
+    const char*  end = path + strlen(path);
+    const char*  last;
+    char*        basename;
+
+    /* prepare for errors */
+    if (pdirname)
+        *pdirname = NULL;
+    if (pbasename)
+        *pbasename = NULL;
+
+    /* handle empty path case */
+    if (end == path) {
+        return -1;
+    }
+
+    /* strip trailing path separators */
+    while (end > path && ispathsep(end[-1]))
+        end -= 1;
+
+    /* handle "/" and degenerate cases like "////" */
+    if (end == path) {
+        return -1;
+    }
+
+    /* find last separator */
+    last = end;
+    while (last > path && !ispathsep(last[-1]))
+        last -= 1;
+
+    /* handle cases where there is no path separator */
+    if (last == path) {
+        if (pdirname)
+            *pdirname  = ASTRDUP(".");
+        if (pbasename)
+            *pbasename = substring_dup(path,end);
+        return 0;
+    }
+
+    /* handle "/foo" */
+    if (last == path+1) {
+        if (pdirname)
+            *pdirname  = ASTRDUP("/");
+        if (pbasename)
+            *pbasename = substring_dup(path+1,end);
+        return 0;
+    }
+
+    /* compute basename */
+    basename = substring_dup(last,end);
+    if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) {
+        AFREE(basename);
+        return -1;
+    }
+
+    if (pbasename)
+        *pbasename = basename;
+    else {
+        AFREE(basename);
+    }
+
+    /* compute dirname */
+    if (pdirname != NULL)
+        *pdirname = substring_dup(path,last-1);
+
+    return 0;
+}
+
+char*
+path_basename( const char*  path )
+{
+    char*  basename;
+
+    if (path_split(path, NULL, &basename) < 0)
+        return NULL;
+
+    return basename;
+}
+
+char*
+path_dirname( const char*  path )
+{
+    char*  dirname;
+
+    if (path_split(path, &dirname, NULL) < 0)
+        return NULL;
+
+    return dirname;
+}
+
+
+
+
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
+
+ABool
+path_exists( const char*  path )
+{
+    int  ret;
+    CHECKED(ret, access(path, F_OK));
+    return (ret == 0) || (errno != ENOENT);
+}
+
+/* checks that a path points to a regular file */
+ABool
+path_is_regular( const char*  path )
+{
+    int          ret;
+    struct stat  st;
+
+    CHECKED(ret, stat(path, &st));
+    if (ret < 0)
+        return 0;
+
+    return S_ISREG(st.st_mode);
+}
+
+
+/* checks that a path points to a directory */
+ABool
+path_is_dir( const char*  path )
+{
+    int          ret;
+    struct stat  st;
+
+    CHECKED(ret, stat(path, &st));
+    if (ret < 0)
+        return 0;
+
+    return S_ISDIR(st.st_mode);
+}
+
+/* checks that one can read/write a given (regular) file */
+ABool
+path_can_read( const char*  path )
+{
+    int  ret;
+    CHECKED(ret, access(path, R_OK));
+    return (ret == 0);
+}
+
+ABool
+path_can_write( const char*  path )
+{
+    int  ret;
+    CHECKED(ret, access(path, R_OK));
+    return (ret == 0);
+}
+
+/* try to make a directory. returns 0 on success, -1 on failure
+ * (error code in errno) */
+APosixStatus
+path_mkdir( const char*  path, int  mode )
+{
+#ifdef _WIN32
+    (void)mode;
+    return _mkdir(path);
+#else
+    int  ret;
+    CHECKED(ret, mkdir(path, mode));
+    return ret;
+#endif
+}
+
+static APosixStatus
+path_mkdir_recursive( char*  path, unsigned  len, int  mode )
+{
+    char      old_c;
+    int       ret;
+    unsigned  len2;
+
+    /* get rid of trailing separators */
+    while (len > 0 && ispathsep(path[len-1]))
+        len -= 1;
+
+    if (len == 0) {
+        errno = ENOENT;
+        return -1;
+    }
+
+    /* check that the parent exists, 'len2' is the length of
+     * the parent part of the path */
+    len2 = len-1;
+    while (len2 > 0 && !ispathsep(path[len2-1]))
+        len2 -= 1;
+
+    if (len2 > 0) {
+        old_c      = path[len2];
+        path[len2] = 0;
+        ret        = 0;
+        if ( !path_exists(path) ) {
+            /* the parent doesn't exist, so try to create it */
+            ret = path_mkdir_recursive( path, len2, mode );
+        }
+        path[len2] = old_c;
+
+        if (ret < 0)
+            return ret;
+    }
+
+    /* at this point, we now the parent exists */
+    old_c     = path[len];
+    path[len] = 0;
+    ret       = path_mkdir( path, mode );
+    path[len] = old_c;
+
+    return ret;
+}
+
+/* ensure that a given directory exists, create it if not, 
+   0 on success, -1 on failure (error code in errno) */
+APosixStatus
+path_mkdir_if_needed( const char*  path, int  mode )
+{
+    int  ret = 0;
+
+    if (!path_exists(path)) {
+        ret = path_mkdir(path, mode);
+
+        if (ret < 0 && errno == ENOENT) {
+            char      temp[MAX_PATH];
+            unsigned  len = (unsigned)strlen(path);
+
+            if (len > sizeof(temp)-1) {
+                errno = EINVAL;
+                return -1;
+            }
+            memcpy( temp, path, len );
+            temp[len] = 0;
+
+            return path_mkdir_recursive(temp, len, mode);
+        }
+    }
+    return ret;
+}
+
+/* return the size of a given file in '*psize'. returns 0 on
+ * success, -1 on failure (error code in errno) */
+APosixStatus
+path_get_size( const char*  path, uint64_t  *psize )
+{
+#ifdef _WIN32
+    /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */
+    /* do not use OpenFile() because it has strange search behaviour that could */
+    /* result in getting the size of a different file */
+    LARGE_INTEGER  size;
+    HANDLE  file = CreateFile( /* lpFilename */        path,
+                               /* dwDesiredAccess */   GENERIC_READ,    
+                               /* dwSharedMode */     FILE_SHARE_READ|FILE_SHARE_WRITE, 
+                               /* lpSecurityAttributes */  NULL, 
+                               /* dwCreationDisposition */ OPEN_EXISTING,
+                               /* dwFlagsAndAttributes */  0,
+                               /* hTemplateFile */      NULL );
+    if (file == INVALID_HANDLE_VALUE) {
+        /* ok, just to play fair */
+        errno = ENOENT;
+        return -1;
+    }
+    if (!GetFileSizeEx(file, &size)) {
+        /* maybe we tried to get the size of a pipe or something like that ? */
+        *psize = 0;
+    }
+    else {
+        *psize = (uint64_t) size.QuadPart;
+    }
+    CloseHandle(file);
+    return 0;
+#else
+    int    ret;
+    struct stat  st;
+
+    CHECKED(ret, stat(path, &st));
+    if (ret == 0) {
+        *psize = (uint64_t) st.st_size;
+    }
+    return ret;
+#endif
+}
+
+
+ABool
+path_is_absolute( const char*  path )
+{
+#ifdef _WIN32
+    if (path == NULL)
+        return 0;
+
+    if (path[0] == '/' || path[0] == '\\')
+        return 1;
+
+    /* 'C:' is always considered to be absolute
+     * even if used with a relative path like C:foo which
+     * is different from C:\foo
+     */
+    if (path[0] != 0 && path[1] == ':')
+        return 1;
+
+    return 0;
+#else
+    return (path != NULL && path[0] == '/');
+#endif
+}
+
+
+/** OTHER FILE UTILITIES
+ **
+ **  path_empty_file() creates an empty file at a given path location.
+ **  if the file already exists, it is truncated without warning
+ **
+ **  path_copy_file() copies one file into another.
+ **
+ **  both functions return 0 on success, and -1 on error
+ **/
+
+APosixStatus
+path_empty_file( const char*  path )
+{
+#ifdef _WIN32
+    int  fd = _creat( path, S_IWRITE );
+#else
+    /* on Unix, only allow the owner to read/write, since the file *
+     * may contain some personal data we don't want to see exposed */
+    int  fd = creat(path, S_IRUSR | S_IWUSR);
+#endif
+    if (fd >= 0) {
+        close(fd);
+        return 0;
+    }
+    return -1;
+}
+
+APosixStatus
+path_copy_file( const char*  dest, const char*  source )
+{
+    int  fd, fs, result = -1;
+
+    /* if the destination doesn't exist, create it */
+    if ( access(source, F_OK)  < 0 ||
+         path_empty_file(dest) < 0) {
+        return -1;
+    }
+
+#ifdef _WIN32
+    fd = _open(dest, _O_RDWR | _O_BINARY);
+    fs = _open(source, _O_RDONLY |  _O_BINARY);
+#else
+    fd = creat(dest, S_IRUSR | S_IWUSR);
+    fs = open(source, S_IREAD);
+#endif
+    if (fs >= 0 && fd >= 0) {
+        char buf[4096];
+        ssize_t total = 0;
+        ssize_t n;
+        result = 0; /* success */
+        while ((n = read(fs, buf, 4096)) > 0) {
+            if (write(fd, buf, n) != n) {
+                /* write failed. Make it return -1 so that an
+                 * empty file be created. */
+                D("Failed to copy '%s' to '%s': %s (%d)",
+                       source, dest, strerror(errno), errno);
+                result = -1;
+                break;
+            }
+            total += n;
+        }
+    }
+
+    if (fs >= 0) {
+        close(fs);
+    }
+    if (fd >= 0) {
+        close(fd);
+    }
+    return result;
+}
+
+
+APosixStatus
+path_delete_file( const char*  path )
+{
+#ifdef _WIN32
+    int  ret = _unlink( path );
+    if (ret == -1 && errno == EACCES) {
+        /* a first call to _unlink will fail if the file is set read-only */
+        /* we can however try to change its mode first and call unlink    */
+        /* again...                                                       */
+        ret = _chmod( path, _S_IREAD | _S_IWRITE );
+        if (ret == 0)
+            ret = _unlink( path );
+    }
+    return ret;
+#else
+    return  unlink(path);
+#endif
+}
+
+
+void*
+path_load_file(const char *fn, size_t  *pSize)
+{
+    char*  data;
+    int    sz;
+    int    fd;
+
+    if (pSize)
+        *pSize = 0;
+
+    data   = NULL;
+
+    fd = open(fn, O_BINARY | O_RDONLY);
+    if(fd < 0) return NULL;
+
+    do {
+        sz = lseek(fd, 0, SEEK_END);
+        if(sz < 0) break;
+
+        if (pSize)
+            *pSize = (size_t) sz;
+
+        if (lseek(fd, 0, SEEK_SET) != 0)
+            break;
+
+        data = (char*) malloc(sz + 1);
+        if(data == NULL) break;
+
+        if (read(fd, data, sz) != sz)
+            break;
+
+        close(fd);
+        data[sz] = 0;
+
+        return data;
+    } while (0);
+
+    close(fd);
+
+    if(data != NULL)
+        free(data);
+
+    return NULL;
+}
+
diff --git a/android/utils/path.h b/android/utils/path.h
new file mode 100644
index 0000000..e822834
--- /dev/null
+++ b/android/utils/path.h
@@ -0,0 +1,152 @@
+/* Copyright (C) 2007-2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_PATH_H
+#define _ANDROID_UTILS_PATH_H
+
+#include <android/utils/system.h>
+#include <stdint.h>  /* for uint64_t */
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
+
+/* O_BINARY is required in the MS C library to avoid opening file
+ * in text mode (the default, ahhhhh)
+ */
+#if !defined(_WIN32) && !defined(O_BINARY)
+#  define  O_BINARY  0
+#endif
+
+/* define  PATH_SEP as a string containing the directory separateor */
+#ifdef _WIN32
+#  define  PATH_SEP   "\\"
+#else
+#  define  PATH_SEP   "/"
+#endif
+
+/* get MAX_PATH, note that PATH_MAX is set to 260 on Windows for
+ * stupid backwards-compatibility reason, though any 32-bit version
+ * of the OS handles much much longer paths
+ */
+#ifdef _WIN32
+#  undef   MAX_PATH
+#  define  MAX_PATH    1024
+#  undef   PATH_MAX
+#  define  PATH_MAX    MAX_PATH
+#else
+#  include <limits.h>
+#  define  MAX_PATH    PATH_MAX
+#endif
+
+/* checks that a given file exists */
+extern ABool  path_exists( const char*  path );
+
+/* checks that a path points to a regular file */
+extern ABool  path_is_regular( const char*  path );
+
+/* checks that a path points to a directory */
+extern ABool  path_is_dir( const char*  path );
+
+/* checks that a path is absolute or not */
+extern ABool  path_is_absolute( const char*  path );
+
+/* checks that one can read/write a given (regular) file */
+extern ABool  path_can_read( const char*  path );
+extern ABool  path_can_write( const char*  path );
+
+/* try to make a directory */
+extern APosixStatus   path_mkdir( const char*  path, int  mode );
+
+/* ensure that a given directory exists, create it if not, 
+   0 on success, -1 on error */
+extern APosixStatus   path_mkdir_if_needed( const char*  path, int  mode );
+
+/* return the size of a given file in '*psize'. returns 0 on
+ * success, -1 on failure (error code in errno) */
+extern APosixStatus   path_get_size( const char*  path, uint64_t  *psize );
+
+/*  path_parent() can be used to return the n-level parent of a given directory
+ *  this understands . and .. when encountered in the input path.
+ *
+ *  the returned string must be freed by the caller.
+ */
+extern char*  path_parent( const char*  path, int  levels );
+
+/* split a path into a (dirname,basename) pair. the result strings must be freed
+ * by the caller. Return 0 on success, or -1 on error. Error conditions include
+ * the following:
+ *   - 'path' is empty
+ *   - 'path' is "/" or degenerate cases like "////"
+ *   - basename is "." or ".."
+ *
+ * if there is no directory separator in path, *dirname will be set to "."
+ * if the path is of type "/foo", then *dirname will be set to "/"
+ *
+ * pdirname can be NULL if you don't want the directory name
+ * pbasename can be NULL if you don't want the base name
+ */
+extern int    path_split( const char*  path, char* *pdirname, char* *pbasename );
+
+/* a convenience function to retrieve the directory name as returned by
+ * path_split(). Returns NULL if path_split() returns an error.
+ * the result string must be freed by the caller
+ */
+extern char*  path_dirname( const char*  path );
+
+/* a convenience function to retrieve the base name as returned by
+ * path_split(). Returns NULL if path_split() returns an error.
+ * the result must be freed by the caller.
+ */
+extern char*  path_basename( const char*  path );
+
+/** OTHER FILE UTILITIES
+ **
+ **  path_empty_file() creates an empty file at a given path location.
+ **  if the file already exists, it is truncated without warning
+ **
+ **  path_copy_file() copies one file into another.
+ **
+ **  unlink_file() is equivalent to unlink() on Unix, on Windows,
+ **  it will handle the case where _unlink() fails because the file is
+ **  read-only by trying to change its access rights then calling _unlink()
+ **  again.
+ **
+ **  these functions return 0 on success, and -1 on error
+ **
+ **  load_text_file() reads a file into a heap-allocated memory block,
+ **  and appends a 0 to it. the caller must free it
+ **/
+
+/* creates an empty file at a given location. If the file already
+ * exists, it is truncated without warning. returns 0 on success,
+ * or -1 on failure.
+ */
+extern APosixStatus   path_empty_file( const char*  path );
+
+/* copies on file into another one. 0 on success, -1 on failure
+ * (error code in errno). Does not work on directories */
+extern APosixStatus   path_copy_file( const char*  dest, const char*  source );
+
+/* unlink/delete a given file. Note that on Win32, this will
+ * fail if the program has an opened handle to the file
+ */
+extern APosixStatus   path_delete_file( const char*  path );
+
+/* try to load a given file into a heap-allocated block.
+ * if 'pSize' is not NULL, this will set the file's size in '*pSize'
+ * note that this actually zero-terminates the file for convenience.
+ * In case of failure, NULL is returned and the error code is in errno
+ */
+extern void*          path_load_file( const char*  path, size_t  *pSize );
+
+/* */
+
+#endif /* _ANDROID_UTILS_PATH_H */
diff --git a/android/utils/reflist.c b/android/utils/reflist.c
new file mode 100644
index 0000000..bc604cd
--- /dev/null
+++ b/android/utils/reflist.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/reflist.h"
+#include <stdlib.h>
+#include <string.h>
+
+void
+areflist_setEmpty(ARefList*  l)
+{
+    if (l->iteration > 0) {
+        /* deferred empty, set all items to NULL
+         * to stop iterations */
+        void**  items = areflist_items(l);
+        memset(items, 0, l->count*sizeof(items[0]));
+        l->iteration |= 1;
+    } else {
+        /* direct empty */
+        if (l->max > 1) {
+            free(l->u.items);
+            l->max = 1;
+        }
+    }
+    l->count = 0;
+}
+
+int
+areflist_indexOf(ARefList*  l, void*  item)
+{
+    if (item) {
+        void**  items = (l->max == 1) ? &l->u.item0 : l->u.items;
+        void**  end   = items + l->count;
+        void**  ii    = items;
+
+        for ( ; ii < end; ii += 1 )
+            if (*ii == item)
+                return (ii - items);
+    }
+    return -1;
+}
+
+static void
+areflist_grow(ARefList*  l, int  count)
+{
+    int   newcount = l->count + count;
+    if (newcount > l->max) {
+        int  oldmax = l->max == 1 ? 0 : l->max;
+        int  newmax = oldmax;
+        void**  olditems = l->max == 1 ? NULL : l->u.items;
+        void**  newitems;
+
+        while (oldmax < newcount)
+            oldmax += (oldmax >> 1) + 4;
+
+        newitems = realloc(olditems, newmax*sizeof(newitems[0]));
+
+        l->u.items = newitems;
+        l->max     = (uint16_t) newmax;
+    }
+}
+
+
+void
+areflist_add(ARefList*  l, void*  item)
+{
+    if (item) {
+        void**  items;
+
+        if (l->count >= l->max) {
+            areflist_grow(l, 1);
+        }
+        items = areflist_items(l);
+        items[l->count] = item;
+        l->count += 1;
+    }
+}
+
+void*
+areflist_pop(ARefList*  l)
+{
+    void*   item = NULL;
+    void**  items = areflist_items(l);
+
+    if (l->count > 0) {
+        if (l->iteration > 0) {
+            /* deferred deletion */
+            int  nn;
+            for (nn = l->count-1; nn > 0; nn--) {
+                item = items[nn];
+                if (item != NULL) {
+                    l->count -= 1;
+                    return item;
+                }
+            }
+        } else {
+            /* normal pop */
+            item = items[--l->count];
+            if (l->count <= 0 && l->max > 1) {
+                free(l->u.items);
+                l->max = 1;
+            }
+        }
+    }
+    return item;
+}
+
+ABool
+areflist_del(ARefList*  l, void*  item)
+{
+    if (item) {
+        int  index = areflist_indexOf(l, item);
+        if (index >= 0) {
+            void** items = areflist_items(l);
+
+            if (l->iteration > 0) {
+                /* deferred deletion */
+                items[index]  = NULL;
+                l->iteration |= 1;
+            } else {
+                /* direct deletion */
+                if (l->max > 1) {
+                    memmove(items + index, items + index + 1, l->count - index - 1);
+                    if (--l->count == 0) {
+                        free(l->u.items);
+                        l->max = 1;
+                    }
+                } else {
+                    l->u.item0 = NULL;
+                    l->count   = 0;
+                }
+            }
+            return 1;
+        }
+    }
+    return 0;
+}
+
+void
+_areflist_remove_deferred(ARefList*  l)
+{
+    if (l->iteration & 1) {
+        /* remove all NULL elements from the array */
+        void**  items = areflist_items(l);
+        int     rr = 0;
+        int     ww = 0;
+        for ( ; rr < l->count; rr++ ) {
+            if (items[rr] != NULL)
+                items[ww++] = items[rr];
+        }
+        l->count = (int16_t) ww;
+
+        /* if the list is empty, release its array */
+        if (l->count == 0 && l->max > 1) {
+            free(l->u.items);
+            l->max = 1;
+        }
+    }
+    l->iteration = 0;
+}
+
+void  
+areflist_copy(ARefList*  dst, ARefList*  src)
+{
+    dst[0] = src[0];
+
+    if (src->max > 1) {
+        dst->u.items = malloc( dst->count*sizeof(void*) );
+        dst->max     = dst->count;
+    }
+}
+
+void*
+areflist_get(ARefList*  l, int  n)
+{
+    if ((unsigned)n >= (unsigned)l->count)
+        return NULL;
+
+    if (l->max == 1)
+        return l->u.item0;
+
+    return l->u.items[n];
+}
+
+void**
+areflist_at(ARefList*  l, int  n)
+{
+    void**  items;
+
+    if ((unsigned)n >= (unsigned)l->count)
+        return NULL;
+
+    items = (l->max == 1) ? &l->u.item0 : l->u.items;
+
+    return items + n;
+}
diff --git a/android/utils/reflist.h b/android/utils/reflist.h
new file mode 100644
index 0000000..dffaef8
--- /dev/null
+++ b/android/utils/reflist.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_REFLIST_H
+#define _ANDROID_UTILS_REFLIST_H
+
+#include "android/utils/system.h"
+
+/* definitions for a smart list of references to generic objects.
+ * supports safe deletion and addition while they are being iterated
+ * with AREFLIST_FOREACH() macro
+ */
+
+typedef struct ARefList {
+    uint16_t   count, max;
+    uint16_t   iteration;
+    union {
+        struct ARefList*  next;
+        void*             item0;
+        void**            items;
+    } u;
+} ARefList;
+
+AINLINED void  
+areflist_init(ARefList*  l)
+{
+    l->count     = 0;
+    l->max       = 1;
+    l->iteration = 0;
+}
+
+void  areflist_setEmpty(ARefList*  l);
+
+AINLINED void
+areflist_done(ARefList*  l)
+{
+    areflist_setEmpty(l);
+}
+
+AINLINED ABool
+areflist_isEmpty(ARefList*  l)
+{
+    return (l->count == 0);
+}
+
+int    areflist_indexOf(ARefList*  l, void*  item);
+
+AINLINED ABool 
+areflist_has(ARefList*  l, void*  item)
+{
+    return areflist_indexOf(l, item) >= 0;
+}
+
+/* if 'item' is not NULL, append it to the list. An item
+ * can be added several times to a list */
+void    areflist_add(ARefList*  l, void*  item);
+
+/* if 'item' is not NULL, try to remove it from the list */
+/* returns TRUE iff the item was found in the list */
+ABool   areflist_del(ARefList*  l, void*  item);
+
+AINLINED void
+areflist_push(ARefList*  l, void*  item)
+{
+    areflist_add(l, item);
+}
+
+void*  areflist_pop(ARefList*  l);
+
+AINLINED void**
+areflist_items(ARefList*  l)
+{
+    return (l->max == 1) ? &l->u.item0 : l->u.items;
+}
+
+AINLINED int
+areflist_count(ARefList*  l)
+{
+    return l->count;
+}
+
+/* return a pointer to the n-th list array entry, 
+   or NULL in case of invalid index */
+void**  areflist_at(ARefList*  l, int  n);
+
+/* return the n-th array entry, or NULL in case of invalid index */
+void*   areflist_get(ARefList*  l, int  n);
+
+/* used internally */
+void    _areflist_remove_deferred(ARefList*  l);
+
+#define  AREFLIST_FOREACH(list_,item_,statement_) \
+    ({ ARefList*  _reflist   = (list_); \
+       int        _reflist_i = 0; \
+       int        _reflist_n = _reflist->count; \
+       _reflist->iteration += 2; \
+       for ( ; _reflist_i < _reflist_n; _reflist_i++ ) { \
+           void**  __reflist_at   = areflist_at(_reflist, _reflist_i); \
+           void*  item_ = *__reflist_at; \
+           if (item_ != NULL) { \
+               statement_; \
+           } \
+       } \
+       _reflist->iteration -= 2; \
+       if (_reflist->iteration == 1) \
+           _areflist_remove_deferred(_reflist); \
+    })
+
+/* use this to delete the currently iterated element */
+#define  AREFLIST_DEL_ITERATED()  \
+    ({ *_reflist_at = NULL; \
+       _reflist->iteration |= 1; })
+
+/* use this to replace the currently iterated element */
+#define  AREFLIST_SET_ITERATED(item) \
+    ({ *_reflist_at = (item); \
+       if (item == NULL) _reflist->iteration |= 1; })
+
+void  areflist_copy(ARefList*  dst, ARefList*  src);
+
+#endif /* _ANDROID_UTILS_REFLIST_H */
diff --git a/android/utils/stralloc.c b/android/utils/stralloc.c
new file mode 100644
index 0000000..2a924e4
--- /dev/null
+++ b/android/utils/stralloc.c
@@ -0,0 +1,300 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/utils/stralloc.h"
+#include "android/utils/debug.h"
+#include "android/utils/misc.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+
+extern void
+stralloc_tabular( stralloc_t*  out, 
+                  const char** strings, int  count,
+                  const char*  prefix,  int  width )
+{
+    int  nrows, ncols, r, c, n, maxw = 0;
+
+    for (n = 0; n < count; n++) {
+        int  len = strlen(strings[n]);
+        if (len > maxw)
+            maxw = len;
+    }
+    maxw += 2;
+    ncols = width/maxw;
+    nrows = (count + ncols-1)/ncols;
+
+    for (r = 0; r < nrows; r++) {
+        stralloc_add_str( out, prefix );
+        for (c = 0; c < ncols; c++) {
+            int  index = c*nrows + r;
+            if (index >= count) {
+                break;
+            }
+            stralloc_add_format( out, "%-*s", maxw, strings[index] );
+        }
+        stralloc_add_str( out, "\n" );
+    }
+}
+
+/** DYNAMIC STRINGS
+ **/
+
+extern void
+stralloc_reset( stralloc_t*  s )
+{
+    free(s->s);
+    s->s = NULL;
+    s->n = 0;
+    s->a = 0;
+}
+
+extern void
+stralloc_ready( stralloc_t*  s, unsigned int  len )
+{
+    unsigned  old_max = s->a;
+    unsigned  new_max = old_max;
+
+    while (new_max < len) {
+        unsigned  new_max2 = new_max + (new_max >> 1) + 16;
+        if (new_max2 < new_max)
+            new_max2 = UINT_MAX;
+        new_max = new_max2;
+    }
+
+    s->s = realloc( s->s, new_max );
+    if (s->s == NULL) {
+        derror( "%s: not enough memory to reallocate %ld bytes",
+                __FUNCTION__, new_max );
+        exit(1);
+    }
+    s->a = new_max;
+}
+
+extern void
+stralloc_readyplus( stralloc_t*  s, unsigned int  len )
+{
+    unsigned  len2 = s->n + len;
+
+    if (len2 < s->n) { /* overflow ? */
+        derror("%s: trying to grow by too many bytes: %ld",
+               __FUNCTION__, len);
+        exit(1);
+    }
+    stralloc_ready( s, len2 );
+}
+
+extern void
+stralloc_copy( stralloc_t*  s, stralloc_t*  from )
+{
+    stralloc_ready(s, from->n);
+    memcpy( s->s, from->s, from->n );
+    s->n = from->n;
+}
+
+extern void
+stralloc_append( stralloc_t*  s, stralloc_t*  from )
+{
+    stralloc_readyplus( s, from->n );
+    memcpy( s->s + s->n, from->s, from->n );
+    s->n += from->n;
+}
+
+extern void
+stralloc_add_c( stralloc_t*  s, int  c )
+{
+    stralloc_add_bytes( s, (char*)&c, 1 );
+}
+
+extern void
+stralloc_add_str( stralloc_t*  s, const char*  str )
+{
+    stralloc_add_bytes( s, str, strlen(str) );
+}
+
+extern void
+stralloc_add_bytes( stralloc_t*  s, const void*  from, unsigned len )
+{
+    stralloc_readyplus( s, len );
+    memcpy( s->s + s->n, from, len );
+    s->n += len;
+}
+
+extern char*
+stralloc_cstr( stralloc_t*  s )
+{
+    stralloc_readyplus( s, 1 );
+    s->s[s->n] = 0;
+    return s->s;
+}
+
+extern char*
+stralloc_to_tempstr( stralloc_t*  s )
+{
+    char*  q = tempstr_get( s->n );
+
+    memcpy( q, s->s, s->n );
+    q[s->n] = 0;
+    return q;
+}
+
+extern void
+stralloc_formatv( stralloc_t*  s, const char*  fmt, va_list  args )
+{
+    stralloc_reset(s);
+    stralloc_ready(s,10);
+
+    while (1) {
+        int      n;
+        va_list  args2;
+
+        va_copy(args2, args);
+        n = vsnprintf( s->s, s->a, fmt, args2 );
+        va_end(args2);
+
+        /* funky old C libraries returns -1 when truncation occurs */
+        if (n > -1 && n < s->a) {
+            s->n = n;
+            break;
+        }
+        if (n > -1) {  /* we now precisely what we need */
+            stralloc_ready( s, n+1 );
+        } else {
+            stralloc_ready( s, s->a*2 );
+        }
+    }
+}
+
+
+extern void
+stralloc_format( stralloc_t*  s, const char*  fmt, ... )
+{
+    va_list  args;
+    va_start(args, fmt);
+    stralloc_formatv(s, fmt, args);
+    va_end(args);
+}
+
+extern void
+stralloc_add_formatv( stralloc_t*  s, const char*  fmt, va_list  args )
+{
+    STRALLOC_DEFINE(s2);
+    stralloc_formatv(s2, fmt, args);
+    stralloc_append( s, s2 );
+    stralloc_reset( s2 );
+}
+
+extern void
+stralloc_add_format( stralloc_t*  s, const char*  fmt, ... )
+{
+    va_list  args;
+    va_start(args, fmt);
+    stralloc_add_formatv( s, fmt, args );
+    va_end(args);
+}
+
+extern void
+stralloc_add_quote_c( stralloc_t*  s, int  c )
+{
+    stralloc_add_quote_bytes( s, (char*)&c, 1 );
+}
+
+extern void
+stralloc_add_quote_str( stralloc_t*  s, const char*  str )
+{
+    stralloc_add_quote_bytes( s, str, strlen(str) );
+}
+
+extern void
+stralloc_add_quote_bytes( stralloc_t*  s, const void*  from, unsigned  len )
+{
+    uint8_t*   p   = (uint8_t*) from;
+    uint8_t*   end = p + len;
+
+    for ( ; p < end; p++ ) {
+        int  c = p[0];
+
+        if (c == '\\') {
+            stralloc_add_str( s, "\\\\" );
+        } else if (c >= ' ' && c < 128) {
+            stralloc_add_c( s, c );
+        } else if (c == '\n') {
+            stralloc_add_str( s, "\\n" );
+        } else if (c == '\t') {
+            stralloc_add_str( s, "\\t" );
+        } else if (c == '\r') {
+            stralloc_add_str( s, "\\r" );
+        } else {
+            stralloc_add_format( s, "\\x%02x", c );
+        }
+    }
+}
+
+extern void
+stralloc_add_hex( stralloc_t*  s, unsigned  value, int  num_digits )
+{
+    const char   hexdigits[16] = "0123456789abcdef";
+    int          nn;
+
+    if (num_digits <= 0)
+        return;
+
+    stralloc_readyplus(s, num_digits);
+    for (nn = num_digits-1; nn >= 0; nn--) {
+        s->s[s->n+nn] = hexdigits[value & 15];
+        value >>= 4;
+    }
+    s->n += num_digits;
+}
+
+extern void
+stralloc_add_hexdump( stralloc_t*  s, void*  base, int  size, const char*  prefix )
+{
+    uint8_t*   p          = (uint8_t*)base;
+    const int  max_count  = 16;
+    int        prefix_len = strlen(prefix);
+
+    while (size > 0) {
+        int          count = size > max_count ? max_count : size;
+        int          count2;
+        int          n;
+
+        stralloc_add_bytes( s, prefix, prefix_len );
+        stralloc_add_hex( s, p[0], 2 );
+
+        for (n = 1; n < count; n++) {
+            stralloc_add_c( s, ' ' );
+            stralloc_add_hex( s, p[n], 2 );
+        }
+
+        count2 = 4 + 3*(max_count - count);
+        stralloc_readyplus( s, count2 );
+        memset( s->s + s->n, ' ', count2 );
+        s->n += count2;
+
+        stralloc_readyplus(s, count+1);
+        for (n = 0; n < count; n++) {
+            int  c = p[n];
+
+            if (c < 32 || c > 127)
+                c = '.';
+
+            s->s[s->n++] = c;
+        }
+        s->s[s->n++] = '\n';
+
+        size -= count;
+        p    += count;
+    }
+}
+
diff --git a/android/utils/stralloc.h b/android/utils/stralloc.h
new file mode 100644
index 0000000..4d17060
--- /dev/null
+++ b/android/utils/stralloc.h
@@ -0,0 +1,60 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UTILS_STRALLOC_H
+#define _ANDROID_UTILS_STRALLOC_H
+
+#include <stddef.h>
+#include <stdarg.h>
+
+/** DYNAMIC STRINGS
+ **/
+
+typedef struct {
+    char*     s;
+    unsigned  n;
+    unsigned  a;
+} stralloc_t;
+
+#define  STRALLOC_INIT        { NULL, 0, 0 }
+#define  STRALLOC_DEFINE(s)   stralloc_t   s[1] = { STRALLOC_INIT }
+
+extern void   stralloc_reset( stralloc_t*  s );
+extern void   stralloc_ready( stralloc_t*  s, unsigned  len );
+extern void   stralloc_readyplus( stralloc_t*  s, unsigned  len );
+
+extern void   stralloc_copy( stralloc_t*  s, stralloc_t*  from );
+extern void   stralloc_append( stralloc_t*  s, stralloc_t*  from );
+
+extern void   stralloc_add_c( stralloc_t*  s, int  c );
+extern void   stralloc_add_str( stralloc_t*  s, const char*  str );
+extern void   stralloc_add_bytes( stralloc_t*  s, const void*  from, unsigned  len );
+
+extern char*  stralloc_cstr( stralloc_t*  s );
+
+extern void   stralloc_format( stralloc_t*  s, const char*  fmt, ... );
+extern void   stralloc_formatv( stralloc_t*  s, const char*  fmt, va_list  args );
+extern void   stralloc_add_format( stralloc_t*  s, const char*  fmt, ... );
+
+extern void   stralloc_add_quote_c( stralloc_t*  s, int  c );
+extern void   stralloc_add_quote_str( stralloc_t*  s, const char*  str );
+extern void   stralloc_add_quote_bytes( stralloc_t*  s, const void*  from, unsigned   len );
+
+extern void   stralloc_add_hex( stralloc_t*  s, unsigned  value, int  num_digits );
+extern void   stralloc_add_hexdump( stralloc_t*  s, void*  base, int  size, const char*  prefix );
+
+extern void   stralloc_tabular( stralloc_t*  s, const char** strings, int  count,
+                                                const char*  prefix,  int  width );
+
+extern char*  stralloc_to_tempstr( stralloc_t*  s );
+
+#endif /* ANDROID_UTILS_STRALLOC_H */
diff --git a/android/utils/system.c b/android/utils/system.c
new file mode 100644
index 0000000..e09fd6b
--- /dev/null
+++ b/android/utils/system.c
@@ -0,0 +1,172 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/system.h"
+#include <stdlib.h>
+#include <stdio.h>
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include <windows.h>  /* for Sleep */
+#else
+#  include <unistd.h>  /* for usleep */
+#endif
+
+void*
+android_alloc( size_t  size )
+{
+    void*   block;
+
+    if (size == 0)
+        return NULL;
+
+    block = malloc(size);
+    if (block != NULL)
+        return block;
+
+    fprintf(stderr, "PANIC: not enough memory\n");
+    exit(1);
+    return NULL;
+}
+
+void*
+android_alloc0( size_t  size )
+{
+    void*   block;
+
+    if (size == 0)
+        return NULL;
+
+    block = calloc(1, size);
+    if (block != NULL)
+        return block;
+
+    fprintf(stderr, "PANIC: not enough memory\n");
+    exit(1);
+    return NULL;
+}
+
+void*
+android_realloc( void*  block, size_t  size )
+{
+    void*   block2;
+
+    if (size == 0) {
+        free(block);
+        return NULL;
+    }
+    block2 = realloc(block, size);
+    if (block2 != NULL)
+        return block2;
+
+    fprintf(stderr, "PANIC: not enough memory to reallocate %d bytes\n", size);
+    exit(1);
+    return NULL;
+}
+
+void
+android_free( void*  block )
+{
+    if (block)
+        free(block);
+}
+
+char*
+android_strdup( const char*  str )
+{
+    int    len;
+    char*  copy;
+
+    if (str == NULL)
+        return NULL;
+
+    len  = strlen(str);
+    copy = malloc(len+1);
+    memcpy(copy, str, len);
+    copy[len] = 0;
+
+    return copy;
+}
+
+#ifdef _WIN32
+char*
+win32_strsep(char**  pline, const char*  delim)
+{
+    char*  line = *pline;
+    char*  p    = line;
+
+    if (p == NULL)
+        return NULL;
+
+    for (;;) {
+        int          c = *p++;
+        const char*  q = delim;
+
+        if (c == 0) {
+            p = NULL;
+            break;
+        }
+
+        while (*q) {
+            if (*q == c) {
+                p[-1] = 0;
+                goto Exit;
+            }
+            q++;
+        }
+    }
+Exit:	
+    *pline = p;
+    return line;
+}
+#endif
+
+
+void
+disable_sigalrm( signal_state_t  *state )
+{
+#ifdef _WIN32
+    (void)state;
+#else
+    sigset_t  set;
+
+    sigemptyset(&set);
+    sigaddset(&set, SIGALRM);
+    pthread_sigmask (SIG_BLOCK, &set, &state->old);
+#endif
+}
+
+void
+restore_sigalrm( signal_state_t  *state )
+{
+#ifdef _WIN32
+    (void)state;
+#else
+    pthread_sigmask (SIG_SETMASK, &state->old, NULL);
+#endif
+}
+
+void
+sleep_ms( int  timeout_ms )
+{
+#ifdef _WIN32
+    if (timeout_ms <= 0)
+        return;
+
+    Sleep( timeout_ms );
+#else
+    if (timeout_ms <= 0)
+        return;
+
+    BEGIN_NOSIGALRM
+    usleep( timeout_ms*1000 );
+    END_NOSIGALRM
+#endif
+}
diff --git a/android/utils/system.h b/android/utils/system.h
new file mode 100644
index 0000000..804aa7d
--- /dev/null
+++ b/android/utils/system.h
@@ -0,0 +1,161 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_SYSTEM_H
+#define _ANDROID_UTILS_SYSTEM_H
+
+#include <string.h>
+#include <stdint.h>
+
+/* the following functions perform 'checked allocations', i.e.
+ * they abort if there is not enough memory.
+ */
+
+/* checked malloc, only returns NULL if size is 0 */
+void*  android_alloc( size_t  size );
+
+/* checked calloc, only returns NULL if size is 0 */
+void*  android_alloc0( size_t  size );
+
+/* checked realloc, only returns NULL if size if 0 */
+void*  android_realloc( void*  block, size_t  size );
+
+/* free memory block */
+void   android_free( void*  block );
+
+/* convenience macros */
+
+#define  AZERO(p)             memset((char*)(p),0,sizeof(*(p)))
+#define  ANEW(p)              (p = android_alloc(sizeof(*p)))
+#define  ANEW0(p)             (p = android_alloc0(sizeof(*p)))
+#define  AFREE(p)             android_free(p)
+
+#define  AMEM_ZERO(dst,size)      memset((char*)(dst), 0, (size_t)(size))
+#define  AMEM_COPY(dst,src,size)  memcpy((char*)(dst),(const char*)(src),(size_t)(size))
+#define  AMEM_MOVE(dst,src,size)  memmove((char*)(dst),(const char*)(src),(size_t)(size))
+
+#define  AARRAY_NEW(p,count)          ((p) = android_alloc(sizeof(*p)*(count)))
+#define  AARRAY_NEW0(p,count)         ((p) = android_alloc0(sizeof(*p)*(count)))
+
+#define  AARRAY_RENEW(p,count)        ((p) = android_realloc((p),sizeof(*(p))*(count)))
+
+#define  AARRAY_COPY(dst,src,count)   AMEM_COPY(dst,src,(count)*sizeof((dst)[0]))
+#define  AARRAY_MOVE(dst,src,count)   AMEM_MOVE(dst,src,(count)*sizeof((dst)[0]))
+#define  AARRAY_ZERO(dst,count)       AMEM_ZERO(dst,(count)*sizeof((dst)[0]))
+
+#define  AARRAY_STATIC_LEN(a)         (sizeof((a))/sizeof((a)[0]))
+
+#define  AINLINED  static __inline__
+
+/* unlike strdup(), this accepts NULL as valid input (and will return NULL then) */
+char*   android_strdup(const char*  src);
+
+#define  ASTRDUP(str)  android_strdup(str)
+
+/* used for functions that return a Posix-style status code, i.e.
+ * 0 means success, -1 means failure with the error code in 'errno'
+ */
+typedef int  APosixStatus;
+
+/* used for functions that return or accept a boolean type */
+typedef int  ABool;
+
+/** Stringification macro
+ **/
+#ifndef STRINGIFY
+#define  _STRINGIFY(x)  #x
+#define  STRINGIFY(x)  _STRINGIFY(x)
+#endif
+
+/** Concatenation macros
+ **/
+#ifndef GLUE
+#define  _GLUE(x,y)  x##y
+#define  GLUE(x,y)   _GLUE(x,y)
+
+#define  _GLUE3(x,y,z)  x##y##z
+#define  GLUE3(x,y,z)    _GLUE3(x,y,z)
+#endif
+
+/** Handle strsep() on Win32
+ **/
+#ifdef _WIN32
+#  undef   strsep
+#  define  strsep    win32_strsep
+extern char*  win32_strsep(char**  pline, const char*  delim);
+#endif
+
+/** Handle strcasecmp on Windows
+ **/
+#ifdef _WIN32
+#  define  strcasecmp  stricmp
+#endif
+
+/** EINTR HANDLING
+ **
+ ** since QEMU uses SIGALRM pretty extensively, having a system call returning
+ ** EINTR on Unix happens very frequently. provide a simple macro to guard against
+ ** this.
+ **/
+
+#ifdef _WIN32
+#  define   CHECKED(ret, call)    (ret) = (call)
+#else
+#  define   CHECKED(ret, call)    do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
+#endif
+
+/** SIGNAL HANDLING
+ **
+ ** the following can be used to block SIGALRM for a given period of time.
+ ** use with caution, the QEMU execution loop uses SIGALRM extensively
+ **
+ **/
+#ifdef _WIN32
+typedef struct { int  dumy; }      signal_state_t;
+#else
+#include <signal.h>
+typedef struct { sigset_t  old; }  signal_state_t;
+#endif
+
+extern  void   disable_sigalrm( signal_state_t  *state );
+extern  void   restore_sigalrm( signal_state_t  *state );
+
+#ifdef _WIN32
+
+#define   BEGIN_NOSIGALRM  \
+    {
+
+#define   END_NOSIGALRM  \
+    }
+
+#else /* !WIN32 */
+
+#define   BEGIN_NOSIGALRM  \
+    { signal_state_t  __sigalrm_state; \
+      disable_sigalrm( &__sigalrm_state );
+
+#define   END_NOSIGALRM  \
+      restore_sigalrm( &__sigalrm_state );  \
+    }
+
+#endif /* !WIN32 */
+
+/** TIME HANDLING
+ **
+ ** sleep for a given time in milliseconds. note: this uses
+ ** disable_sigalrm()/restore_sigalrm()
+ **/
+
+extern  void   sleep_ms( int  timeout );
+
+/* */
+
+#endif /* _ANDROID_UTILS_SYSTEM_H */
diff --git a/android/utils/tempfile.c b/android/utils/tempfile.c
new file mode 100644
index 0000000..6374c12
--- /dev/null
+++ b/android/utils/tempfile.c
@@ -0,0 +1,198 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include "android/utils/tempfile.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/debug.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  include <windows.h>
+#else
+#  include <unistd.h>
+#endif
+
+#define  D(...)  ((void)0)
+
+/** TEMP FILE SUPPORT
+ **
+ ** simple interface to create an empty temporary file on the system.
+ **
+ ** create the file with tempfile_create(), which returns a reference to a TempFile
+ ** object, or NULL if your system is so weird it doesn't have a temporary directory.
+ **
+ ** you can then call tempfile_path() to retrieve the TempFile's real path to open
+ ** it. the returned path is owned by the TempFile object and should not be freed.
+ **
+ ** all temporary files are destroyed when the program quits, unless you explicitely
+ ** close them before that with tempfile_close()
+ **/
+
+struct TempFile
+{
+    const char*  name;
+    TempFile*    next;
+};
+
+static void       tempfile_atexit();
+static TempFile*  _all_tempfiles;
+
+TempFile*
+tempfile_create( void )
+{
+    TempFile*    tempfile;
+    const char*  tempname = NULL;
+
+#ifdef _WIN32
+    char  temp_namebuff[MAX_PATH];
+    char  temp_dir[MAX_PATH];
+    char  *p = temp_dir, *end = p + sizeof(temp_dir);
+    UINT  retval;
+
+    p = bufprint_temp_dir( p, end );
+    if (p >= end) {
+        D( "TEMP directory path is too long" );
+        return NULL;
+    }
+
+    retval = GetTempFileName(temp_dir, "TMP", 0, temp_namebuff);
+    if (retval == 0) {
+        D( "can't create temporary file in '%s'", temp_dir );
+        return NULL;
+    }
+
+    tempname = temp_namebuff;
+#else
+#define  TEMPLATE  "/tmp/.android-emulator-XXXXXX"
+    int   tempfd = -1;
+    char  template[512];
+    char  *p = template, *end = p + sizeof(template);
+
+    p = bufprint_temp_file( p, end, "emulator-XXXXXX" );
+    if (p >= end) {
+        D( "Xcannot create temporary file in /tmp/android !!" );
+        return NULL;
+    }
+
+    D( "template: %s", template );
+    tempfd = mkstemp( template );
+    if (tempfd < 0) {
+        D("cannot create temporary file in /tmp/android !!");
+        return NULL;
+    }
+    close(tempfd);
+    tempname = template;
+#endif
+    tempfile = malloc( sizeof(*tempfile) + strlen(tempname) + 1 );
+    tempfile->name = (char*)(tempfile + 1);
+    strcpy( (char*)tempfile->name, tempname );
+
+    tempfile->next = _all_tempfiles;
+    _all_tempfiles = tempfile;
+
+    if ( !tempfile->next ) {
+        atexit( tempfile_atexit );
+    }
+
+    return tempfile;
+}
+
+const char*
+tempfile_path(TempFile*  temp)
+{
+    return temp ? temp->name : NULL;
+}
+
+void
+tempfile_close(TempFile*  tempfile)
+{
+#ifdef _WIN32
+    DeleteFile(tempfile->name);
+#else
+    unlink(tempfile->name);
+#endif
+}
+
+/** TEMP FILE CLEANUP
+ **
+ **/
+
+/* we don't expect to use many temporary files */
+#define MAX_ATEXIT_FDS  16
+
+typedef struct {
+    int   count;
+    int   fds[ MAX_ATEXIT_FDS ];
+} AtExitFds;
+
+static void
+atexit_fds_add( AtExitFds*  t, int  fd )
+{
+    if (t->count < MAX_ATEXIT_FDS)
+        t->fds[t->count++] = fd;
+    else {
+        dwarning("%s: over %d calls. Program exit may not cleanup all temporary files",
+            __FUNCTION__, MAX_ATEXIT_FDS);
+    }
+}
+
+static void
+atexit_fds_del( AtExitFds*  t, int  fd )
+{
+    int  nn;
+    for (nn = 0; nn < t->count; nn++)
+        if (t->fds[nn] == fd) {
+            /* move the last element to the current position */
+            t->count  -= 1;
+            t->fds[nn] = t->fds[t->count];
+            break;
+        }
+}
+
+static void
+atexit_fds_close_all( AtExitFds*  t )
+{
+    int  nn;
+    for (nn = 0; nn < t->count; nn++)
+        close(t->fds[nn]);
+}
+
+static AtExitFds   _atexit_fds[1];
+
+void
+atexit_close_fd(int  fd)
+{
+    if (fd >= 0)
+        atexit_fds_add(_atexit_fds, fd);
+}
+
+void
+atexit_close_fd_remove(int  fd)
+{
+    if (fd >= 0)
+        atexit_fds_del(_atexit_fds, fd);
+}
+
+static void
+tempfile_atexit( void )
+{
+    TempFile*  tempfile;
+
+    atexit_fds_close_all( _atexit_fds );
+
+    for (tempfile = _all_tempfiles; tempfile; tempfile = tempfile->next)
+        tempfile_close(tempfile);
+}
diff --git a/android/utils/tempfile.h b/android/utils/tempfile.h
new file mode 100644
index 0000000..4264a84
--- /dev/null
+++ b/android/utils/tempfile.h
@@ -0,0 +1,52 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef _ANDROID_UTILS_TEMPFILE_H
+#define _ANDROID_UTILS_TEMPFILE_H
+
+/** TEMP FILE SUPPORT
+ **
+ ** simple interface to create an empty temporary file on the system.
+ **
+ ** create the file with tempfile_create(), which returns a reference to a TempFile
+ ** object, or NULL if your system is so weird it doesn't have a temporary directory.
+ **
+ ** you can then call tempfile_path() to retrieve the TempFile's real path to open
+ ** it. the returned path is owned by the TempFile object and should not be freed.
+ **
+ ** all temporary files are destroyed when the program quits, unless you explicitely
+ ** close them before that with tempfile_close()
+ **/
+
+typedef struct TempFile   TempFile;
+
+extern  TempFile*    tempfile_create( void );
+extern  const char*  tempfile_path( TempFile*  temp );
+extern  void         tempfile_close( TempFile*  temp );
+
+/** TEMP FILE CLEANUP
+ **
+ ** We delete all temporary files in atexit()-registered callbacks.
+ ** however, the Win32 DeleteFile is unable to remove a file unless
+ ** all HANDLEs to it are closed in the terminating process.
+ **
+ ** Call 'atexit_close_fd' on a newly open-ed file descriptor to indicate
+ ** that you want it closed in atexit() time. You should always call
+ ** this function unless you're certain that the corresponding file
+ ** cannot be temporary.
+ **
+ ** Call 'atexit_close_fd_remove' before explicitely closing a 'fd'
+ **/
+extern void          atexit_close_fd(int  fd);
+extern void          atexit_close_fd_remove(int  fd);
+
+#endif /* _ANDROID_UTILS_TEMPFILE_H */
diff --git a/android/utils/timezone.c b/android/utils/timezone.c
new file mode 100644
index 0000000..b5588a3
--- /dev/null
+++ b/android/utils/timezone.c
@@ -0,0 +1,721 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/utils/debug.h"
+#include "android/utils/timezone.h"
+#include "android/utils/bufprint.h"
+#include "android/android.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "qemu-common.h"
+
+#define  DEBUG  1
+
+#if 1
+#  define  D_ACTIVE   VERBOSE_CHECK(timezone)
+#else
+#  define  D_ACTIVE   DEBUG
+#endif
+
+#if DEBUG
+#  define  D(...)  do { if (D_ACTIVE) fprintf(stderr, __VA_ARGS__); } while (0)
+#else
+#  define  D(...)  ((void)0)
+#endif
+
+
+
+static const char* get_zoneinfo_timezone( void );  /* forward */
+
+static char         android_timezone0[256];
+static const char*  android_timezone;
+static int          android_timezone_init;
+
+static int
+check_timezone_is_zoneinfo(const char*  tz)
+{
+    const char*  slash1 = NULL, *slash2 = NULL;
+
+    if (tz == NULL)
+        return 0;
+
+    /* the name must be of the form Area/Location or Area/Location/SubLocation */
+    slash1 = strchr( tz, '/' );
+    if (slash1 == NULL || slash1[1] == 0)
+        return 0;
+
+    slash2 = strchr( slash1+1, '/');
+    if (slash2 != NULL) {
+        if (slash2[1] == 0 || strchr(slash2+1, '/') != NULL)
+            return 0;
+    }
+
+    return 1;
+}
+
+int
+timezone_set( const char*  tzname )
+{
+    int   len;
+
+    if ( !check_timezone_is_zoneinfo(tzname) )
+        return -1;
+
+    len = strlen(tzname);
+    if (len > sizeof(android_timezone0)-1)
+        return -1;
+
+    strcpy( android_timezone0, tzname );
+    android_timezone      = android_timezone0;
+    android_timezone_init = 1;
+
+    return 0;
+}
+
+
+char*
+bufprint_zoneinfo_timezone( char*  p, char*  end )
+{
+    const char*  tz = get_zoneinfo_timezone();
+
+    if (tz == NULL || !check_timezone_is_zoneinfo(tz))
+        return bufprint(p, end, "Unknown/Unknown");
+    else
+        return bufprint(p, end, "%s", tz);
+}
+
+/* on OS X, the timezone directory is always /usr/share/zoneinfo
+ * this makes things easy.
+ */
+#ifdef __APPLE__
+
+#include <unistd.h>
+#include <limits.h>
+#define  LOCALTIME_FILE  "/etc/localtime"
+#define  ZONEINFO_DIR    "/usr/share/zoneinfo/"
+static const char*
+get_zoneinfo_timezone( void )
+{
+    if (!android_timezone_init) {
+        const char*  tz = getenv("TZ");
+        char         buff[PATH_MAX+1];
+
+        android_timezone_init = 1;
+        if (tz == NULL) {
+            int   len = readlink(LOCALTIME_FILE, buff, sizeof(buff));
+            if (len < 0) {
+                dprint( "### WARNING: Could not read %s, something is very wrong on your system",
+                        LOCALTIME_FILE);
+                return NULL;
+            }
+
+            buff[len] = 0;
+            D("%s: %s points to %s\n", __FUNCTION__, LOCALTIME_FILE, buff);
+            if ( memcmp(buff, ZONEINFO_DIR, sizeof(ZONEINFO_DIR)-1) ) {
+                dprint( "### WARNING: %s does not point to %s, can't determine zoneinfo timezone name",
+                        LOCALTIME_FILE, ZONEINFO_DIR );
+                return NULL;
+            }
+            tz = buff + sizeof(ZONEINFO_DIR)-1;
+            if ( !check_timezone_is_zoneinfo(tz) ) {
+                dprint( "### WARNING: %s does not point to zoneinfo-compatible timezone name\n", LOCALTIME_FILE );
+                return NULL;
+            }
+        }
+        pstrcpy( android_timezone0, sizeof(android_timezone0), tz );
+        android_timezone = android_timezone0;
+    }
+    D( "found timezone %s", android_timezone );
+    return android_timezone;
+}
+
+#endif  /* __APPLE__ */
+
+/* on Linux, with glibc2, the zoneinfo directory can be changed with TZDIR environment variable
+ * but should be /usr/share/zoneinfo by default. /etc/localtime is not guaranteed to exist on
+ * all platforms, so if it doesn't, try $TZDIR/localtime, then /usr/share/zoneinfo/locatime
+ * ugly, isn't it ?
+ *
+ * besides, modern Linux distribution don't make /etc/localtime a symlink but a straight copy of
+ * the original timezone file. the only way to know which zoneinfo name to retrieve is to compare
+ * it with all files in $TZDIR (at least those that match Area/Location or Area/Location/SubLocation
+ */
+#ifdef __linux__
+
+#include <unistd.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#define  ZONEINFO_DIR  "/usr/share/zoneinfo/"
+#define  LOCALTIME_FILE1  "/etc/localtime"
+
+typedef struct {
+    const char*   localtime;
+    struct stat   localtime_st;
+    char*         path_end;
+    char*         path_root;
+    char          path[ PATH_MAX ];
+} ScanDataRec;
+
+static int
+compare_timezone_to_localtime( ScanDataRec*  scan,
+                               const char*   path )
+{
+    struct  stat  st;
+    int           fd1, fd2, result = 0;
+
+    D( "%s: comparing %s:", __FUNCTION__, path );
+
+    if ( stat( path, &st ) < 0 ) {
+        D( " can't stat: %s\n", strerror(errno) );
+        return 0;
+    }
+
+    if ( st.st_size != scan->localtime_st.st_size ) {
+        D( " size mistmatch (%lld != %lld)\n", st.st_size, scan->localtime_st.st_size );
+        return 0;
+    }
+
+    fd1 = open( scan->localtime, O_RDONLY );
+    if (fd1 < 0) {
+        D(" can't open %s: %s\n", scan->localtime, strerror(errno) );
+        return 0;
+    }
+    fd2 = open( path, O_RDONLY );
+    if (fd2 < 0) {
+        D(" can't open %s: %s\n", path, strerror(errno) );
+        close(fd1);
+        return 0;
+    }
+    do {
+        off_t  nn;
+
+        for (nn = 0; nn < st.st_size; nn++) {
+            char  temp[2];
+            int   ret;
+
+            do { ret = read(fd1, &temp[0], 1); } while (ret < 0 && errno == EINTR);
+            if (ret < 0) break;
+
+            do { ret = read(fd2, &temp[1], 1); } while (ret < 0 && errno == EINTR);
+            if (ret < 0) break;
+
+            if (temp[0] != temp[1])
+                break;
+        }
+
+        result = (nn == st.st_size);
+
+    } while (0);
+
+    D( result ? " MATCH\n" : "no match\n" );
+
+    close(fd2);
+    close(fd1);
+
+    return result;
+}
+
+static const char*
+scan_timezone_dir( ScanDataRec*  scan,
+                   char*         top,
+                   int           depth )
+{
+    DIR*         d = opendir( scan->path );
+    const char*  result = NULL;
+
+    D( "%s: entering '%s\n", __FUNCTION__, scan->path );
+    if (d != NULL) {
+        struct  dirent*  ent;
+        while ((ent = readdir(d)) != NULL) {
+            struct stat   ent_st;
+            char*         p = top;
+
+            if  (ent->d_name[0] == '.')  /* avoid hidden and special files */
+                continue;
+
+            p = bufprint( p, scan->path_end, "/%s", ent->d_name );
+            if (p >= scan->path_end)
+                continue;
+
+            //D( "%s: scanning '%s'\n", __FUNCTION__, scan->path );
+
+            if ( stat( scan->path, &ent_st ) < 0 )
+                continue;
+
+            if ( S_ISDIR(ent_st.st_mode) && depth < 2 )
+            {
+                //D( "%s: directory '%s'\n", __FUNCTION__, scan->path );
+                result = scan_timezone_dir( scan, p, depth + 1 );
+                if (result != NULL)
+                    break;
+            }
+            else if ( S_ISREG(ent_st.st_mode) && (depth >= 1 && depth <= 2) )
+            {
+                char*   name = scan->path_root + 1;
+
+                if ( check_timezone_is_zoneinfo( name ) )
+                {
+                    if (compare_timezone_to_localtime( scan, scan->path ))
+                    {
+                        result = strdup( name );
+                        D( "%s: found '%s'\n", __FUNCTION__, result );
+                        break;
+                    }
+                }
+                else
+                {
+                    //D( "%s: ignoring '%s'\n", __FUNCTION__, scan->path );
+                }
+            }
+        }
+        closedir(d);
+    }
+    return  result;
+}
+
+static const char*
+get_zoneinfo_timezone( void )
+{
+    if (!android_timezone_init)
+    {
+        const char*  tz = getenv( "TZ" );
+
+        android_timezone_init = 1;
+
+        if ( tz != NULL && !check_timezone_is_zoneinfo(tz) ) {
+            D( "%s: ignoring non zoneinfo formatted TZ environment variable: '%s'\n",
+               __FUNCTION__, tz );
+            tz = NULL;
+        }
+
+        if (tz == NULL) {
+            char*        tzdir     = NULL;
+            int          tzdirlen  = 0;
+            char*        localtime = NULL;
+            int          len;
+            char         temp[ PATH_MAX ];
+
+            /* determine the correct timezone directory */
+            {
+                const char*  env = getenv("TZDIR");
+                const char*  zoneinfo_dir = ZONEINFO_DIR;
+
+                if (env == NULL)
+                    env = zoneinfo_dir;
+
+                if ( access( env, R_OK ) != 0 ) {
+                    if ( env == zoneinfo_dir ) {
+                        fprintf( stderr,
+                                 "### WARNING: could not find %s directory. unable to determine host timezone\n", env );
+                    } else {
+                        D( "%s: TZDIR does not point to valid directory, using %s instead\n",
+                           __FUNCTION__, zoneinfo_dir );
+                        env = zoneinfo_dir;
+                    }
+                    return NULL;
+                }
+                tzdir = strdup(env);
+            }
+
+            /* remove trailing slash, if any */
+            len = strlen(tzdir);
+            if (len > 0 && tzdir[len-1] == '/') {
+                tzdir[len-1] = 0;
+                len         -= 1;
+            }
+            tzdirlen = len;
+            D( "%s: found timezone dir as %s\n", __FUNCTION__, tzdir );
+
+            /* try to find the localtime file */
+            localtime = LOCALTIME_FILE1;
+            if ( access( localtime, R_OK ) != 0 ) {
+                char  *p = temp, *end = p + sizeof(temp);
+
+                p = bufprint( p, end, "%s/%s", tzdir, "localtime" );
+                if (p >= end || access( temp, R_OK ) != 0 ) {
+                    fprintf( stderr, "### WARNING: could not find %s or %s. unable to determine host timezone\n",
+                                     LOCALTIME_FILE1, temp );
+                    goto Exit;
+                }
+                localtime = temp;
+            }
+            localtime = strdup(localtime);
+            D( "%s: found localtime file as %s\n", __FUNCTION__, localtime );
+
+#if 1
+            /* if the localtime file is a link, make a quick check */
+            len = readlink( localtime, temp, sizeof(temp)-1 );
+            if (len >= 0 && len > tzdirlen + 2) {
+                temp[len] = 0;
+
+                /* verify that the link points to tzdir/<something> where <something> is a valid zoneinfo name */
+                if ( !memcmp( temp, tzdir, tzdirlen ) && temp[tzdirlen] == '/' ) {
+                    if ( check_timezone_is_zoneinfo( temp + tzdirlen + 1 ) ) {
+                        /* we have it ! */
+                        tz = temp + tzdirlen + 1;
+                        D( "%s: found zoneinfo timezone %s from %s symlink\n", __FUNCTION__, tz, localtime );
+                        goto Exit;
+                    }
+                    D( "%s: %s link points to non-zoneinfo filename %s, comparing contents\n",
+                       __FUNCTION__, localtime, temp );
+                }
+            }
+#endif
+
+            /* otherwise, parse all files under tzdir and see if we have something that looks like it */
+            {
+                ScanDataRec  scan[1];
+
+                if ( stat( localtime, &scan->localtime_st ) < 0 ) {
+                    fprintf( stderr, "### WARNING: can't access '%s', unable to determine host timezone\n",
+                             localtime );
+                    goto Exit;
+                }
+
+                scan->localtime = localtime;
+                scan->path_end  = scan->path + sizeof(scan->path);
+                scan->path_root = bufprint( scan->path, scan->path_end, "%s", tzdir );
+
+                tz = scan_timezone_dir( scan, scan->path_root, 0 );
+            }
+
+        Exit:
+            if (tzdir)
+                free(tzdir);
+            if (localtime)
+                free(localtime);
+
+            if (tz == NULL)
+                return NULL;
+
+            pstrcpy( android_timezone0, sizeof(android_timezone0), tz );
+            android_timezone = android_timezone0;
+        }
+        D( "found timezone %s\n", android_timezone );
+    }
+    return android_timezone;
+}
+
+#endif /* __linux__ */
+
+
+/* on Windows, we need to translate the Windows timezone into a ZoneInfo one */
+#ifdef _WIN32
+#include <time.h>
+
+typedef struct {
+    const char*  win_name;
+    const char*  zoneinfo_name;
+} Win32Timezone;
+
+/* table generated from http://unicode.org/cldr/data/diff/supplemental/windows_tzid.html */
+static const Win32Timezone  _win32_timezones[] = {
+    { "AUS Central Standard Time"             , "Australia/Darwin" },
+    { "AUS Eastern Standard Time"             , "Australia/Sydney" },
+    { "Acre Standard Time"                    , "America/Rio_Branco" },
+    { "Afghanistan Standard Time"             , "Asia/Kabul" },
+    { "Africa_Central Standard Time"          , "Africa/Kigali" },
+    { "Africa_Eastern Standard Time"          , "Africa/Kampala" },
+    { "Africa_FarWestern Standard Time"       , "Africa/El_Aaiun" },
+    { "Africa_Southern Standard Time"         , "Africa/Johannesburg" },
+    { "Africa_Western Standard Time"          , "Africa/Niamey" },
+    { "Aktyubinsk Standard Time"              , "Asia/Aqtobe" },
+    { "Alaska Standard Time"                  , "America/Juneau" },
+    { "Alaska_Hawaii Standard Time"           , "America/Anchorage" },
+    { "Alaskan Standard Time"                 , "America/Anchorage" },
+    { "Almaty Standard Time"                  , "Asia/Almaty" },
+    { "Amazon Standard Time"                  , "America/Manaus" },
+    { "America_Central Standard Time"         , "America/Winnipeg" },
+    { "America_Eastern Standard Time"         , "America/Panama" },
+    { "America_Mountain Standard Time"        , "America/Edmonton" },
+    { "America_Pacific Standard Time"         , "America/Vancouver" },
+    { "Anadyr Standard Time"                  , "Asia/Anadyr" },
+    { "Aqtau Standard Time"                   , "Asia/Aqtau" },
+    { "Aqtobe Standard Time"                  , "Asia/Aqtobe" },
+    { "Arab Standard Time"                    , "Asia/Riyadh" },
+    { "Arabian Standard Time"                 , "Asia/Bahrain" },
+    { "Arabic Standard Time"                  , "Asia/Baghdad" },
+    { "Argentina Standard Time"               , "America/Buenos_Aires" },
+    { "Argentina_Western Standard Time"       , "America/Mendoza" },
+    { "Armenia Standard Time"                 , "Asia/Yerevan" },
+    { "Ashkhabad Standard Time"               , "Asia/Ashgabat" },
+    { "Atlantic Standard Time"                , "America/Curacao" },
+    { "Australia_Central Standard Time"       , "Australia/Adelaide" },
+    { "Australia_CentralWestern Standard Time", "Australia/Eucla" },
+    { "Australia_Eastern Standard Time"       , "Australia/Sydney" },
+    { "Australia_Western Standard Time"       , "Australia/Perth" },
+    { "Azerbaijan Standard Time"              , "Asia/Baku" },
+    { "Azores Standard Time"                  , "Atlantic/Azores" },
+    { "Baku Standard Time"                    , "Asia/Baku" },
+    { "Bangladesh Standard Time"              , "Asia/Dhaka" },
+    { "Bering Standard Time"                  , "America/Adak" },
+    { "Bhutan Standard Time"                  , "Asia/Thimphu" },
+    { "Bolivia Standard Time"                 , "America/La_Paz" },
+    { "Borneo Standard Time"                  , "Asia/Kuching" },
+    { "Brasilia Standard Time"                , "America/Sao_Paulo" },
+    { "British Standard Time"                 , "Europe/London" },
+    { "Brunei Standard Time"                  , "Asia/Brunei" },
+    { "Canada Central Standard Time"          , "America/Regina" },
+    { "Cape Verde Standard Time"              , "Atlantic/Cape_Verde" },
+    { "Cape_Verde Standard Time"              , "Atlantic/Cape_Verde" },
+    { "Caucasus Standard Time"                , "Asia/Yerevan" },
+    { "Cen. Australia Standard Time"          , "Australia/Adelaide" },
+    { "Central Standard Time"                 , "America/Chicago" },
+    { "Central America Standard Time"         , "America/Guatemala" },
+    { "Central Asia Standard Time"            , "Asia/Dhaka" },
+    { "Central Brazilian Standard Time"       , "America/Manaus" },
+    { "Central Europe Standard Time"          , "Europe/Prague" },
+    { "Central European Standard Time"        , "Europe/Warsaw" },
+    { "Central Pacific Standard Time"         , "Pacific/Guadalcanal" },
+    { "Central Standard Time (Mexico)"        , "America/Mexico_City" },
+    { "Chamorro Standard Time"                , "Pacific/Guam" },
+    { "Changbai Standard Time"                , "Asia/Harbin" },
+    { "Chatham Standard Time"                 , "Pacific/Chatham" },
+    { "Chile Standard Time"                   , "America/Santiago" },
+    { "China Standard Time"                   , "Asia/Taipei" },
+    { "Choibalsan Standard Time"              , "Asia/Choibalsan" },
+    { "Christmas Standard Time"               , "Indian/Christmas" },
+    { "Cocos Standard Time"                   , "Indian/Cocos" },
+    { "Colombia Standard Time"                , "America/Bogota" },
+    { "Cook Standard Time"                    , "Pacific/Rarotonga" },
+    { "Cuba Standard Time"                    , "America/Havana" },
+    { "Dacca Standard Time"                   , "Asia/Dhaka" },
+    { "Dateline Standard Time"                , "Pacific/Kwajalein" },
+    { "Davis Standard Time"                   , "Antarctica/Davis" },
+    { "Dominican Standard Time"               , "America/Santo_Domingo" },
+    { "DumontDUrville Standard Time"          , "Antarctica/DumontDUrville" },
+    { "Dushanbe Standard Time"                , "Asia/Dushanbe" },
+    { "Dutch_Guiana Standard Time"            , "America/Paramaribo" },
+    { "E. Africa Standard Time"               , "Africa/Nairobi" },
+    { "E. Australia Standard Time"            , "Australia/Brisbane" },
+    { "E. Europe Standard Time"               , "Europe/Minsk" },
+    { "E. South America Standard Time"        , "America/Sao_Paulo" },
+    { "East_Timor Standard Time"              , "Asia/Dili" },
+    { "Easter Standard Time"                  , "Pacific/Easter" },
+    { "Eastern Standard Time"                 , "America/New_York" },
+    { "Ecuador Standard Time"                 , "America/Guayaquil" },
+    { "Egypt Standard Time"                   , "Africa/Cairo" },
+    { "Ekaterinburg Standard Time"            , "Asia/Yekaterinburg" },
+    { "Europe_Central Standard Time"          , "Europe/Oslo" },
+    { "Europe_Eastern Standard Time"          , "Europe/Vilnius" },
+    { "Europe_Western Standard Time"          , "Atlantic/Canary" },
+    { "FLE Standard Time"                     , "Europe/Helsinki" },
+    { "Falkland Standard Time"                , "Atlantic/Stanley" },
+    { "Fiji Standard Time"                    , "Pacific/Fiji" },
+    { "French_Guiana Standard Time"           , "America/Cayenne" },
+    { "French_Southern Standard Time"         , "Indian/Kerguelen" },
+    { "Frunze Standard Time"                  , "Asia/Bishkek" },
+    { "GMT Standard Time"                     , "Europe/Dublin" },
+    { "GTB Standard Time"                     , "Europe/Istanbul" },
+    { "Galapagos Standard Time"               , "Pacific/Galapagos" },
+    { "Gambier Standard Time"                 , "Pacific/Gambier" },
+    { "Georgia Standard Time"                 , "Asia/Tbilisi" },
+    { "Georgian Standard Time"                , "Asia/Tbilisi" },
+    { "Gilbert_Islands Standard Time"         , "Pacific/Tarawa" },
+    { "Goose_Bay Standard Time"               , "America/Goose_Bay" },
+    { "Greenland Standard Time"               , "America/Godthab" },
+    { "Greenland_Central Standard Time"       , "America/Scoresbysund" },
+    { "Greenland_Eastern Standard Time"       , "America/Scoresbysund" },
+    { "Greenland_Western Standard Time"       , "America/Godthab" },
+    { "Greenwich Standard Time"               , "Africa/Casablanca" },
+    { "Guam Standard Time"                    , "Pacific/Guam" },
+    { "Gulf Standard Time"                    , "Asia/Muscat" },
+    { "Guyana Standard Time"                  , "America/Guyana" },
+    { "Hawaii_Aleutian Standard Time"         , "Pacific/Honolulu" },
+    { "Hawaiian Standard Time"                , "Pacific/Honolulu" },
+    { "Hong_Kong Standard Time"               , "Asia/Hong_Kong" },
+    { "Hovd Standard Time"                    , "Asia/Hovd" },
+    { "India Standard Time"                   , "Asia/Calcutta" },
+    { "Indian_Ocean Standard Time"            , "Indian/Chagos" },
+    { "Indochina Standard Time"               , "Asia/Vientiane" },
+    { "Indonesia_Central Standard Time"       , "Asia/Makassar" },
+    { "Indonesia_Eastern Standard Time"       , "Asia/Jayapura" },
+    { "Indonesia_Western Standard Time"       , "Asia/Jakarta" },
+    { "Iran Standard Time"                    , "Asia/Tehran" },
+    { "Irish Standard Time"                   , "Europe/Dublin" },
+    { "Irkutsk Standard Time"                 , "Asia/Irkutsk" },
+    { "Israel Standard Time"                  , "Asia/Jerusalem" },
+    { "Japan Standard Time"                   , "Asia/Tokyo" },
+    { "Jordan Standard Time"                  , "Asia/Amman" },
+    { "Kamchatka Standard Time"               , "Asia/Kamchatka" },
+    { "Karachi Standard Time"                 , "Asia/Karachi" },
+    { "Kashgar Standard Time"                 , "Asia/Kashgar" },
+    { "Kazakhstan_Eastern Standard Time"      , "Asia/Almaty" },
+    { "Kazakhstan_Western Standard Time"      , "Asia/Aqtobe" },
+    { "Kizilorda Standard Time"               , "Asia/Qyzylorda" },
+    { "Korea Standard Time"                   , "Asia/Seoul" },
+    { "Kosrae Standard Time"                  , "Pacific/Kosrae" },
+    { "Krasnoyarsk Standard Time"             , "Asia/Krasnoyarsk" },
+    { "Kuybyshev Standard Time"               , "Europe/Samara" },
+    { "Kwajalein Standard Time"               , "Pacific/Kwajalein" },
+    { "Kyrgystan Standard Time"               , "Asia/Bishkek" },
+    { "Lanka Standard Time"                   , "Asia/Colombo" },
+    { "Liberia Standard Time"                 , "Africa/Monrovia" },
+    { "Line_Islands Standard Time"            , "Pacific/Kiritimati" },
+    { "Long_Shu Standard Time"                , "Asia/Chongqing" },
+    { "Lord_Howe Standard Time"               , "Australia/Lord_Howe" },
+    { "Macau Standard Time"                   , "Asia/Macau" },
+    { "Magadan Standard Time"                 , "Asia/Magadan" },
+    { "Malaya Standard Time"                  , "Asia/Kuala_Lumpur" },
+    { "Malaysia Standard Time"                , "Asia/Kuching" },
+    { "Maldives Standard Time"                , "Indian/Maldives" },
+    { "Marquesas Standard Time"               , "Pacific/Marquesas" },
+    { "Marshall_Islands Standard Time"        , "Pacific/Majuro" },
+    { "Mauritius Standard Time"               , "Indian/Mauritius" },
+    { "Mawson Standard Time"                  , "Antarctica/Mawson" },
+    { "Mexico Standard Time"                  , "America/Mexico_City" },
+    { "Mexico Standard Time 2 Standard Time"  , "America/Chihuahua" },
+    { "Mid-Atlantic Standard Time"            , "America/Noronha" },
+    { "Middle East Standard Time"             , "Asia/Beirut" },
+    { "Mongolia Standard Time"                , "Asia/Ulaanbaatar" },
+    { "Montevideo Standard Time"              , "America/Montevideo" },
+    { "Moscow Standard Time"                  , "Europe/Moscow" },
+    { "Mountain Standard Time"                , "America/Denver" },
+    { "Mountain Standard Time (Mexico)"       , "America/Chihuahua" },
+    { "Myanmar Standard Time"                 , "Asia/Rangoon" },
+    { "N. Central Asia Standard Time"         , "Asia/Novosibirsk" },
+    { "Namibia Standard Time"                 , "Africa/Windhoek" },
+    { "Nauru Standard Time"                   , "Pacific/Nauru" },
+    { "Nepal Standard Time"                   , "Asia/Katmandu" },
+    { "New Zealand Standard Time"             , "Pacific/Auckland" },
+    { "New_Caledonia Standard Time"           , "Pacific/Noumea" },
+    { "New_Zealand Standard Time"             , "Pacific/Auckland" },
+    { "Newfoundland Standard Time"            , "America/St_Johns" },
+    { "Niue Standard Time"                    , "Pacific/Niue" },
+    { "Norfolk Standard Time"                 , "Pacific/Norfolk" },
+    { "Noronha Standard Time"                 , "America/Noronha" },
+    { "North Asia Standard Time"              , "Asia/Krasnoyarsk" },
+    { "North Asia East Standard Time"         , "Asia/Ulaanbaatar" },
+    { "North_Mariana Standard Time"           , "Pacific/Saipan" },
+    { "Novosibirsk Standard Time"             , "Asia/Novosibirsk" },
+    { "Omsk Standard Time"                    , "Asia/Omsk" },
+    { "Oral Standard Time"                    , "Asia/Oral" },
+    { "Pacific Standard Time"                 , "America/Los_Angeles" },
+    { "Pacific SA Standard Time"              , "America/Santiago" },
+    { "Pacific Standard Time (Mexico)"        , "America/Tijuana" },
+    { "Pakistan Standard Time"                , "Asia/Karachi" },
+    { "Palau Standard Time"                   , "Pacific/Palau" },
+    { "Papua_New_Guinea Standard Time"        , "Pacific/Port_Moresby" },
+    { "Paraguay Standard Time"                , "America/Asuncion" },
+    { "Peru Standard Time"                    , "America/Lima" },
+    { "Philippines Standard Time"             , "Asia/Manila" },
+    { "Phoenix_Islands Standard Time"         , "Pacific/Enderbury" },
+    { "Pierre_Miquelon Standard Time"         , "America/Miquelon" },
+    { "Pitcairn Standard Time"                , "Pacific/Pitcairn" },
+    { "Ponape Standard Time"                  , "Pacific/Ponape" },
+    { "Qyzylorda Standard Time"               , "Asia/Qyzylorda" },
+    { "Reunion Standard Time"                 , "Indian/Reunion" },
+    { "Romance Standard Time"                 , "Europe/Paris" },
+    { "Rothera Standard Time"                 , "Antarctica/Rothera" },
+    { "Russian Standard Time"                 , "Europe/Moscow" },
+    { "SA Eastern Standard Time"              , "America/Buenos_Aires" },
+    { "SA Pacific Standard Time"              , "America/Bogota" },
+    { "SA Western Standard Time"              , "America/Caracas" },
+    { "SE Asia Standard Time"                 , "Asia/Bangkok" },
+    { "Sakhalin Standard Time"                , "Asia/Sakhalin" },
+    { "Samara Standard Time"                  , "Europe/Samara" },
+    { "Samarkand Standard Time"               , "Asia/Samarkand" },
+    { "Samoa Standard Time"                   , "Pacific/Apia" },
+    { "Seychelles Standard Time"              , "Indian/Mahe" },
+    { "Shevchenko Standard Time"              , "Asia/Aqtau" },
+    { "Singapore Standard Time"               , "Asia/Singapore" },
+    { "Solomon Standard Time"                 , "Pacific/Guadalcanal" },
+    { "South Africa Standard Time"            , "Africa/Johannesburg" },
+    { "South_Georgia Standard Time"           , "Atlantic/South_Georgia" },
+    { "Sri Lanka Standard Time"               , "Asia/Colombo" },
+    { "Suriname Standard Time"                , "America/Paramaribo" },
+    { "Sverdlovsk Standard Time"              , "Asia/Yekaterinburg" },
+    { "Syowa Standard Time"                   , "Antarctica/Syowa" },
+    { "Tahiti Standard Time"                  , "Pacific/Tahiti" },
+    { "Taipei Standard Time"                  , "Asia/Taipei" },
+    { "Tajikistan Standard Time"              , "Asia/Dushanbe" },
+    { "Tashkent Standard Time"                , "Asia/Tashkent" },
+    { "Tasmania Standard Time"                , "Australia/Hobart" },
+    { "Tbilisi Standard Time"                 , "Asia/Tbilisi" },
+    { "Tokelau Standard Time"                 , "Pacific/Fakaofo" },
+    { "Tokyo Standard Time"                   , "Asia/Tokyo" },
+    { "Tonga Standard Time"                   , "Pacific/Tongatapu" },
+    { "Truk Standard Time"                    , "Pacific/Truk" },
+    { "Turkey Standard Time"                  , "Europe/Istanbul" },
+    { "Turkmenistan Standard Time"            , "Asia/Ashgabat" },
+    { "Tuvalu Standard Time"                  , "Pacific/Funafuti" },
+    { "US Eastern Standard Time"              , "America/Indianapolis" },
+    { "US Mountain Standard Time"             , "America/Phoenix" },
+    { "Uralsk Standard Time"                  , "Asia/Oral" },
+    { "Uruguay Standard Time"                 , "America/Montevideo" },
+    { "Urumqi Standard Time"                  , "Asia/Urumqi" },
+    { "Uzbekistan Standard Time"              , "Asia/Tashkent" },
+    { "Vanuatu Standard Time"                 , "Pacific/Efate" },
+    { "Venezuela Standard Time"               , "America/Caracas" },
+    { "Vladivostok Standard Time"             , "Asia/Vladivostok" },
+    { "Volgograd Standard Time"               , "Europe/Volgograd" },
+    { "Vostok Standard Time"                  , "Antarctica/Vostok" },
+    { "W. Australia Standard Time"            , "Australia/Perth" },
+    { "W. Central Africa Standard Time"       , "Africa/Lagos" },
+    { "W. Europe Standard Time"               , "Europe/Berlin" },
+    { "Wake Standard Time"                    , "Pacific/Wake" },
+    { "Wallis Standard Time"                  , "Pacific/Wallis" },
+    { "West Asia Standard Time"               , "Asia/Karachi" },
+    { "West Pacific Standard Time"            , "Pacific/Guam" },
+    { "Yakutsk Standard Time"                 , "Asia/Yakutsk" },
+    { "Yekaterinburg Standard Time"           , "Asia/Yekaterinburg" },
+    { "Yerevan Standard Time"                 , "Asia/Yerevan" },
+    { "Yukon Standard Time"                   , "America/Yakutat" },
+    { NULL, NULL }
+};
+
+static const char*
+get_zoneinfo_timezone( void )
+{
+    if (!android_timezone_init)
+    {
+        char		          tzname[128];
+        time_t		          t = time(NULL);
+        struct tm*            tm = localtime(&t);
+        const Win32Timezone*  win32tz = _win32_timezones;
+
+        android_timezone_init = 1;
+
+        if (!tm) {
+            D("%s: could not determine current date/time\n", __FUNCTION__);
+            return NULL;
+        }
+
+        memset(tzname, 0, sizeof(tzname));
+        strftime(tzname, sizeof(tzname) - 1, "%Z", tm);
+
+        for (win32tz = _win32_timezones; win32tz->win_name != NULL; win32tz++)
+            if ( !strcmp(win32tz->win_name, tzname) ) {
+                android_timezone = win32tz->zoneinfo_name;
+                goto Exit;
+            }
+
+#if 0  /* TODO */
+    /* we didn't find it, this may come from localized versions of Windows. we're going to explore the registry,
+    * as the code in Postgresql does...
+    */
+#endif
+        D( "%s: could not determine current timezone\n", __FUNCTION__ );
+        return NULL;
+    }
+Exit:
+    D( "emulator: found timezone %s\n", android_timezone );
+    return android_timezone;
+}
+
+#endif /* _WIN32 */
+
diff --git a/android/utils/timezone.h b/android/utils/timezone.h
new file mode 100644
index 0000000..bf5f731
--- /dev/null
+++ b/android/utils/timezone.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _ANDROID_UTILS_TIMEZONE_H
+#define _ANDROID_UTILS_TIMEZONE_H
+
+/* try to set the default host timezone, returns 0 on success, or -1 if
+ * 'tzname' is not in zoneinfo format (e.g. Area/Location)
+ */
+extern int  timezone_set( const char*  tzname );
+
+/* append the current host "zoneinfo" timezone name to a given buffer. note
+ * that this is something like "America/Los_Angeles", and not the human-friendly "PST"
+ * this is required by the Android emulated system...
+ *
+ * the implementation of this function is really tricky and is OS-dependent
+ * on Unix systems, it needs to cater to the TZ environment variable, uhhh
+ *
+ * if TZ is defined to something like "CET" or "PST", this will return the name "Unknown/Unknown"
+ */
+extern char*  bufprint_zoneinfo_timezone( char*  buffer, char*  end );
+
+#endif /* _ANDROID_UTILS_TIMEZONE_H */
diff --git a/arm-dis.c b/arm-dis.c
new file mode 100644
index 0000000..ee44292
--- /dev/null
+++ b/arm-dis.c
@@ -0,0 +1,4165 @@
+/* Instruction printing code for the ARM
+   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   2007, Free Software Foundation, Inc.
+   Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+   Modification by James G. Smith (jsmith@cygnus.co.uk)
+
+   This file is part of libopcodes.
+
+   This program is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Start of qemu specific additions.  Mostly this is stub definitions
+   for things we don't care about.  */
+
+#include "dis-asm.h"
+#define FALSE 0
+#define TRUE (!FALSE)
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#define ISSPACE(x) ((x) == ' ' || (x) == '\t' || (x) == '\n')
+
+#define ARM_EXT_V1	 0
+#define ARM_EXT_V2	 0
+#define ARM_EXT_V2S	 0
+#define ARM_EXT_V3	 0
+#define ARM_EXT_V3M	 0
+#define ARM_EXT_V4	 0
+#define ARM_EXT_V4T	 0
+#define ARM_EXT_V5	 0
+#define ARM_EXT_V5T	 0
+#define ARM_EXT_V5ExP	 0
+#define ARM_EXT_V5E	 0
+#define ARM_EXT_V5J	 0
+#define ARM_EXT_V6       0
+#define ARM_EXT_V6K      0
+#define ARM_EXT_V6Z      0
+#define ARM_EXT_V6T2	 0
+#define ARM_EXT_V7	 0
+#define ARM_EXT_DIV	 0
+
+/* Co-processor space extensions.  */
+#define ARM_CEXT_XSCALE   0
+#define ARM_CEXT_MAVERICK 0
+#define ARM_CEXT_IWMMXT   0
+
+#define FPU_FPA_EXT_V1	 0
+#define FPU_FPA_EXT_V2	 0
+#define FPU_VFP_EXT_NONE 0
+#define FPU_VFP_EXT_V1xD 0
+#define FPU_VFP_EXT_V1	 0
+#define FPU_VFP_EXT_V2	 0
+#define FPU_MAVERICK	 0
+#define FPU_VFP_EXT_V3	 0
+#define FPU_NEON_EXT_V1	 0
+
+int floatformat_ieee_single_little;
+/* Assume host uses ieee float.  */
+static void floatformat_to_double (int *ignored, unsigned char *data,
+                                   double *dest)
+{
+    union {
+        uint32_t i;
+        float f;
+    } u;
+    u.i = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+    *dest = u.f;
+}
+
+/* End of qemu specific additions.  */
+
+/* FIXME: Belongs in global header.  */
+#ifndef strneq
+#define strneq(a,b,n)	(strncmp ((a), (b), (n)) == 0)
+#endif
+
+#ifndef NUM_ELEM
+#define NUM_ELEM(a)     (sizeof (a) / sizeof (a)[0])
+#endif
+
+struct opcode32
+{
+  unsigned long arch;		/* Architecture defining this insn.  */
+  unsigned long value, mask;	/* Recognise insn if (op&mask)==value.  */
+  const char *assembler;	/* How to disassemble this insn.  */
+};
+
+struct opcode16
+{
+  unsigned long arch;		/* Architecture defining this insn.  */
+  unsigned short value, mask;	/* Recognise insn if (op&mask)==value.  */
+  const char *assembler;	/* How to disassemble this insn.  */
+};
+
+/* print_insn_coprocessor recognizes the following format control codes:
+
+   %%			%
+
+   %c			print condition code (always bits 28-31 in ARM mode)
+   %q			print shifter argument
+   %u			print condition code (unconditional in ARM mode)
+   %A			print address for ldc/stc/ldf/stf instruction
+   %B			print vstm/vldm register list
+   %C			print vstr/vldr address operand
+   %I                   print cirrus signed shift immediate: bits 0..3|4..6
+   %F			print the COUNT field of a LFM/SFM instruction.
+   %P			print floating point precision in arithmetic insn
+   %Q			print floating point precision in ldf/stf insn
+   %R			print floating point rounding mode
+
+   %<bitfield>r		print as an ARM register
+   %<bitfield>d		print the bitfield in decimal
+   %<bitfield>k		print immediate for VFPv3 conversion instruction
+   %<bitfield>x		print the bitfield in hex
+   %<bitfield>X		print the bitfield as 1 hex digit without leading "0x"
+   %<bitfield>f		print a floating point constant if >7 else a
+			floating point register
+   %<bitfield>w         print as an iWMMXt width field - [bhwd]ss/us
+   %<bitfield>g         print as an iWMMXt 64-bit register
+   %<bitfield>G         print as an iWMMXt general purpose or control register
+   %<bitfield>D		print as a NEON D register
+   %<bitfield>Q		print as a NEON Q register
+
+   %y<code>		print a single precision VFP reg.
+			  Codes: 0=>Sm, 1=>Sd, 2=>Sn, 3=>multi-list, 4=>Sm pair
+   %z<code>		print a double precision VFP reg
+			  Codes: 0=>Dm, 1=>Dd, 2=>Dn, 3=>multi-list
+
+   %<bitfield>'c	print specified char iff bitfield is all ones
+   %<bitfield>`c	print specified char iff bitfield is all zeroes
+   %<bitfield>?ab...    select from array of values in big endian order
+
+   %L			print as an iWMMXt N/M width field.
+   %Z			print the Immediate of a WSHUFH instruction.
+   %l			like 'A' except use byte offsets for 'B' & 'H'
+			versions.
+   %i			print 5-bit immediate in bits 8,3..0
+			(print "32" when 0)
+   %r			print register offset address for wldt/wstr instruction
+*/
+
+/* Common coprocessor opcodes shared between Arm and Thumb-2.  */
+
+static const struct opcode32 coprocessor_opcodes[] =
+{
+  /* XScale instructions.  */
+  {ARM_CEXT_XSCALE, 0x0e200010, 0x0fff0ff0, "mia%c\tacc0, %0-3r, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e280010, 0x0fff0ff0, "miaph%c\tacc0, %0-3r, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e2c0010, 0x0ffc0ff0, "mia%17'T%17`B%16'T%16`B%c\tacc0, %0-3r, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0c400000, 0x0ff00fff, "mar%c\tacc0, %12-15r, %16-19r"},
+  {ARM_CEXT_XSCALE, 0x0c500000, 0x0ff00fff, "mra%c\t%12-15r, %16-19r, acc0"},
+
+  /* Intel Wireless MMX technology instructions.  */
+#define FIRST_IWMMXT_INSN 0x0e130130
+#define IWMMXT_INSN_COUNT 73
+  {ARM_CEXT_IWMMXT, 0x0e130130, 0x0f3f0fff, "tandc%22-23w%c\t%12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e400010, 0x0ff00f3f, "tbcst%6-7w%c\t%16-19g, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e130170, 0x0f3f0ff8, "textrc%22-23w%c\t%12-15r, #%0-2d"},
+  {ARM_CEXT_XSCALE, 0x0e100070, 0x0f300ff0, "textrm%3?su%22-23w%c\t%12-15r, %16-19g, #%0-2d"},
+  {ARM_CEXT_XSCALE, 0x0e600010, 0x0ff00f38, "tinsr%6-7w%c\t%16-19g, %12-15r, #%0-2d"},
+  {ARM_CEXT_XSCALE, 0x0e000110, 0x0ff00fff, "tmcr%c\t%16-19G, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0c400000, 0x0ff00ff0, "tmcrr%c\t%0-3g, %12-15r, %16-19r"},
+  {ARM_CEXT_XSCALE, 0x0e2c0010, 0x0ffc0e10, "tmia%17?tb%16?tb%c\t%5-8g, %0-3r, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e200010, 0x0fff0e10, "tmia%c\t%5-8g, %0-3r, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e280010, 0x0fff0e10, "tmiaph%c\t%5-8g, %0-3r, %12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e100030, 0x0f300fff, "tmovmsk%22-23w%c\t%12-15r, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e100110, 0x0ff00ff0, "tmrc%c\t%12-15r, %16-19G"},
+  {ARM_CEXT_XSCALE, 0x0c500000, 0x0ff00ff0, "tmrrc%c\t%12-15r, %16-19r, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e130150, 0x0f3f0fff, "torc%22-23w%c\t%12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e130190, 0x0f3f0fff, "torvsc%22-23w%c\t%12-15r"},
+  {ARM_CEXT_XSCALE, 0x0e2001c0, 0x0f300fff, "wabs%22-23w%c\t%12-15g, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e0001c0, 0x0f300fff, "wacc%22-23w%c\t%12-15g, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e000180, 0x0f000ff0, "wadd%20-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e2001a0, 0x0f300ff0, "waddbhus%22?ml%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ea001a0, 0x0ff00ff0, "waddsubhx%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000020, 0x0f800ff0, "waligni%c\t%12-15g, %16-19g, %0-3g, #%20-22d"},
+  {ARM_CEXT_XSCALE, 0x0e800020, 0x0fc00ff0, "walignr%20-21d%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e200000, 0x0fe00ff0, "wand%20'n%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e800000, 0x0fa00ff0, "wavg2%22?hb%20'r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e400000, 0x0fe00ff0, "wavg4%20'r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000060, 0x0f300ff0, "wcmpeq%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e100060, 0x0f100ff0, "wcmpgt%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0xfc500100, 0xfe500f00, "wldrd\t%12-15g, %r"},
+  {ARM_CEXT_XSCALE, 0xfc100100, 0xfe500f00, "wldrw\t%12-15G, %A"},
+  {ARM_CEXT_XSCALE, 0x0c100000, 0x0e100e00, "wldr%L%c\t%12-15g, %l"},
+  {ARM_CEXT_XSCALE, 0x0e400100, 0x0fc00ff0, "wmac%21?su%20'z%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e800100, 0x0fc00ff0, "wmadd%21?su%20'x%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ec00100, 0x0fd00ff0, "wmadd%21?sun%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000160, 0x0f100ff0, "wmax%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000080, 0x0f100fe0, "wmerge%c\t%12-15g, %16-19g, %0-3g, #%21-23d"},
+  {ARM_CEXT_XSCALE, 0x0e0000a0, 0x0f800ff0, "wmia%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e800120, 0x0f800ff0, "wmiaw%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e100160, 0x0f100ff0, "wmin%21?su%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000100, 0x0fc00ff0, "wmul%21?su%20?ml%23'r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ed00100, 0x0fd00ff0, "wmul%21?sumr%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ee000c0, 0x0fe00ff0, "wmulwsm%20`r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ec000c0, 0x0fe00ff0, "wmulwum%20`r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0eb000c0, 0x0ff00ff0, "wmulwl%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e8000a0, 0x0f800ff0, "wqmia%21?tb%20?tb%22'n%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e100080, 0x0fd00ff0, "wqmulm%21'r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ec000e0, 0x0fd00ff0, "wqmulwm%21'r%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000000, 0x0ff00ff0, "wor%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000080, 0x0f000ff0, "wpack%20-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0xfe300040, 0xff300ef0, "wror%22-23w\t%12-15g, %16-19g, #%i"},
+  {ARM_CEXT_XSCALE, 0x0e300040, 0x0f300ff0, "wror%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e300140, 0x0f300ff0, "wror%22-23wg%c\t%12-15g, %16-19g, %0-3G"},
+  {ARM_CEXT_XSCALE, 0x0e000120, 0x0fa00ff0, "wsad%22?hb%20'z%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e0001e0, 0x0f000ff0, "wshufh%c\t%12-15g, %16-19g, #%Z"},
+  {ARM_CEXT_XSCALE, 0xfe100040, 0xff300ef0, "wsll%22-23w\t%12-15g, %16-19g, #%i"},
+  {ARM_CEXT_XSCALE, 0x0e100040, 0x0f300ff0, "wsll%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e100148, 0x0f300ffc, "wsll%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"},
+  {ARM_CEXT_XSCALE, 0xfe000040, 0xff300ef0, "wsra%22-23w\t%12-15g, %16-19g, #%i"},
+  {ARM_CEXT_XSCALE, 0x0e000040, 0x0f300ff0, "wsra%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e000148, 0x0f300ffc, "wsra%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"},
+  {ARM_CEXT_XSCALE, 0xfe200040, 0xff300ef0, "wsrl%22-23w\t%12-15g, %16-19g, #%i"},
+  {ARM_CEXT_XSCALE, 0x0e200040, 0x0f300ff0, "wsrl%22-23w%8'g%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e200148, 0x0f300ffc, "wsrl%22-23w%8'g%c\t%12-15g, %16-19g, %0-3G"},
+  {ARM_CEXT_XSCALE, 0xfc400100, 0xfe500f00, "wstrd\t%12-15g, %r"},
+  {ARM_CEXT_XSCALE, 0xfc000100, 0xfe500f00, "wstrw\t%12-15G, %A"},
+  {ARM_CEXT_XSCALE, 0x0c000000, 0x0e100e00, "wstr%L%c\t%12-15g, %l"},
+  {ARM_CEXT_XSCALE, 0x0e0001a0, 0x0f000ff0, "wsub%20-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0ed001c0, 0x0ff00ff0, "wsubaddhx%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e1001c0, 0x0f300ff0, "wabsdiff%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e0000c0, 0x0fd00fff, "wunpckeh%21?sub%c\t%12-15g, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e4000c0, 0x0fd00fff, "wunpckeh%21?suh%c\t%12-15g, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e8000c0, 0x0fd00fff, "wunpckeh%21?suw%c\t%12-15g, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e0000e0, 0x0f100fff, "wunpckel%21?su%22-23w%c\t%12-15g, %16-19g"},
+  {ARM_CEXT_XSCALE, 0x0e1000c0, 0x0f300ff0, "wunpckih%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e1000e0, 0x0f300ff0, "wunpckil%22-23w%c\t%12-15g, %16-19g, %0-3g"},
+  {ARM_CEXT_XSCALE, 0x0e100000, 0x0ff00ff0, "wxor%c\t%12-15g, %16-19g, %0-3g"},
+
+  /* Floating point coprocessor (FPA) instructions */
+  {FPU_FPA_EXT_V1, 0x0e000100, 0x0ff08f10, "adf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e100100, 0x0ff08f10, "muf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e200100, 0x0ff08f10, "suf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e300100, 0x0ff08f10, "rsf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e400100, 0x0ff08f10, "dvf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e500100, 0x0ff08f10, "rdf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e600100, 0x0ff08f10, "pow%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e700100, 0x0ff08f10, "rpw%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e800100, 0x0ff08f10, "rmf%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e900100, 0x0ff08f10, "fml%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ea00100, 0x0ff08f10, "fdv%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0eb00100, 0x0ff08f10, "frd%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ec00100, 0x0ff08f10, "pol%c%P%R\t%12-14f, %16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e008100, 0x0ff08f10, "mvf%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e108100, 0x0ff08f10, "mnf%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e208100, 0x0ff08f10, "abs%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e308100, 0x0ff08f10, "rnd%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e408100, 0x0ff08f10, "sqt%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e508100, 0x0ff08f10, "log%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e608100, 0x0ff08f10, "lgn%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e708100, 0x0ff08f10, "exp%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e808100, 0x0ff08f10, "sin%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e908100, 0x0ff08f10, "cos%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ea08100, 0x0ff08f10, "tan%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0eb08100, 0x0ff08f10, "asn%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ec08100, 0x0ff08f10, "acs%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ed08100, 0x0ff08f10, "atn%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ee08100, 0x0ff08f10, "urd%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ef08100, 0x0ff08f10, "nrm%c%P%R\t%12-14f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0e000110, 0x0ff00f1f, "flt%c%P%R\t%16-18f, %12-15r"},
+  {FPU_FPA_EXT_V1, 0x0e100110, 0x0fff0f98, "fix%c%R\t%12-15r, %0-2f"},
+  {FPU_FPA_EXT_V1, 0x0e200110, 0x0fff0fff, "wfs%c\t%12-15r"},
+  {FPU_FPA_EXT_V1, 0x0e300110, 0x0fff0fff, "rfs%c\t%12-15r"},
+  {FPU_FPA_EXT_V1, 0x0e400110, 0x0fff0fff, "wfc%c\t%12-15r"},
+  {FPU_FPA_EXT_V1, 0x0e500110, 0x0fff0fff, "rfc%c\t%12-15r"},
+  {FPU_FPA_EXT_V1, 0x0e90f110, 0x0ff8fff0, "cmf%c\t%16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0eb0f110, 0x0ff8fff0, "cnf%c\t%16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ed0f110, 0x0ff8fff0, "cmfe%c\t%16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0ef0f110, 0x0ff8fff0, "cnfe%c\t%16-18f, %0-3f"},
+  {FPU_FPA_EXT_V1, 0x0c000100, 0x0e100f00, "stf%c%Q\t%12-14f, %A"},
+  {FPU_FPA_EXT_V1, 0x0c100100, 0x0e100f00, "ldf%c%Q\t%12-14f, %A"},
+  {FPU_FPA_EXT_V2, 0x0c000200, 0x0e100f00, "sfm%c\t%12-14f, %F, %A"},
+  {FPU_FPA_EXT_V2, 0x0c100200, 0x0e100f00, "lfm%c\t%12-14f, %F, %A"},
+
+  /* Register load/store */
+  {FPU_NEON_EXT_V1, 0x0d200b00, 0x0fb00f01, "vstmdb%c\t%16-19r%21'!, %B"},
+  {FPU_NEON_EXT_V1, 0x0d300b00, 0x0fb00f01, "vldmdb%c\t%16-19r%21'!, %B"},
+  {FPU_NEON_EXT_V1, 0x0c800b00, 0x0f900f01, "vstmia%c\t%16-19r%21'!, %B"},
+  {FPU_NEON_EXT_V1, 0x0c900b00, 0x0f900f01, "vldmia%c\t%16-19r%21'!, %B"},
+  {FPU_NEON_EXT_V1, 0x0d000b00, 0x0f300f00, "vstr%c\t%12-15,22D, %C"},
+  {FPU_NEON_EXT_V1, 0x0d100b00, 0x0f300f00, "vldr%c\t%12-15,22D, %C"},
+
+  /* Data transfer between ARM and NEON registers */
+  {FPU_NEON_EXT_V1, 0x0e800b10, 0x0ff00f70, "vdup%c.32\t%16-19,7D, %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0e800b30, 0x0ff00f70, "vdup%c.16\t%16-19,7D, %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0ea00b10, 0x0ff00f70, "vdup%c.32\t%16-19,7Q, %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0ea00b30, 0x0ff00f70, "vdup%c.16\t%16-19,7Q, %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0ec00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7D, %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0ee00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7Q, %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0c400b10, 0x0ff00fd0, "vmov%c\t%0-3,5D, %12-15r, %16-19r"},
+  {FPU_NEON_EXT_V1, 0x0c500b10, 0x0ff00fd0, "vmov%c\t%12-15r, %16-19r, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0x0e000b10, 0x0fd00f70, "vmov%c.32\t%16-19,7D[%21d], %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0e100b10, 0x0f500f70, "vmov%c.32\t%12-15r, %16-19,7D[%21d]"},
+  {FPU_NEON_EXT_V1, 0x0e000b30, 0x0fd00f30, "vmov%c.16\t%16-19,7D[%6,21d], %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0e100b30, 0x0f500f30, "vmov%c.%23?us16\t%12-15r, %16-19,7D[%6,21d]"},
+  {FPU_NEON_EXT_V1, 0x0e400b10, 0x0fd00f10, "vmov%c.8\t%16-19,7D[%5,6,21d], %12-15r"},
+  {FPU_NEON_EXT_V1, 0x0e500b10, 0x0f500f10, "vmov%c.%23?us8\t%12-15r, %16-19,7D[%5,6,21d]"},
+
+  /* Floating point coprocessor (VFP) instructions */
+  {FPU_VFP_EXT_V1xD, 0x0ef1fa10, 0x0fffffff, "fmstat%c"},
+  {FPU_VFP_EXT_V1xD, 0x0ee00a10, 0x0fff0fff, "fmxr%c\tfpsid, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0ee10a10, 0x0fff0fff, "fmxr%c\tfpscr, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0ee60a10, 0x0fff0fff, "fmxr%c\tmvfr1, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0ee70a10, 0x0fff0fff, "fmxr%c\tmvfr0, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0ee80a10, 0x0fff0fff, "fmxr%c\tfpexc, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0ee90a10, 0x0fff0fff, "fmxr%c\tfpinst, %12-15r\t@ Impl def"},
+  {FPU_VFP_EXT_V1xD, 0x0eea0a10, 0x0fff0fff, "fmxr%c\tfpinst2, %12-15r\t@ Impl def"},
+  {FPU_VFP_EXT_V1xD, 0x0ef00a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpsid"},
+  {FPU_VFP_EXT_V1xD, 0x0ef10a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpscr"},
+  {FPU_VFP_EXT_V1xD, 0x0ef60a10, 0x0fff0fff, "fmrx%c\t%12-15r, mvfr1"},
+  {FPU_VFP_EXT_V1xD, 0x0ef70a10, 0x0fff0fff, "fmrx%c\t%12-15r, mvfr0"},
+  {FPU_VFP_EXT_V1xD, 0x0ef80a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpexc"},
+  {FPU_VFP_EXT_V1xD, 0x0ef90a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst\t@ Impl def"},
+  {FPU_VFP_EXT_V1xD, 0x0efa0a10, 0x0fff0fff, "fmrx%c\t%12-15r, fpinst2\t@ Impl def"},
+  {FPU_VFP_EXT_V1, 0x0e000b10, 0x0ff00fff, "fmdlr%c\t%z2, %12-15r"},
+  {FPU_VFP_EXT_V1, 0x0e100b10, 0x0ff00fff, "fmrdl%c\t%12-15r, %z2"},
+  {FPU_VFP_EXT_V1, 0x0e200b10, 0x0ff00fff, "fmdhr%c\t%z2, %12-15r"},
+  {FPU_VFP_EXT_V1, 0x0e300b10, 0x0ff00fff, "fmrdh%c\t%12-15r, %z2"},
+  {FPU_VFP_EXT_V1xD, 0x0ee00a10, 0x0ff00fff, "fmxr%c\t<impl def %16-19x>, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0ef00a10, 0x0ff00fff, "fmrx%c\t%12-15r, <impl def %16-19x>"},
+  {FPU_VFP_EXT_V1xD, 0x0e000a10, 0x0ff00f7f, "fmsr%c\t%y2, %12-15r"},
+  {FPU_VFP_EXT_V1xD, 0x0e100a10, 0x0ff00f7f, "fmrs%c\t%12-15r, %y2"},
+  {FPU_VFP_EXT_V1xD, 0x0eb50a40, 0x0fbf0f70, "fcmp%7'ezs%c\t%y1"},
+  {FPU_VFP_EXT_V1, 0x0eb50b40, 0x0fbf0f70, "fcmp%7'ezd%c\t%z1"},
+  {FPU_VFP_EXT_V1xD, 0x0eb00a40, 0x0fbf0fd0, "fcpys%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0eb00ac0, 0x0fbf0fd0, "fabss%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0eb00b40, 0x0fbf0fd0, "fcpyd%c\t%z1, %z0"},
+  {FPU_VFP_EXT_V1, 0x0eb00bc0, 0x0fbf0fd0, "fabsd%c\t%z1, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0eb10a40, 0x0fbf0fd0, "fnegs%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0eb10ac0, 0x0fbf0fd0, "fsqrts%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0eb10b40, 0x0fbf0fd0, "fnegd%c\t%z1, %z0"},
+  {FPU_VFP_EXT_V1, 0x0eb10bc0, 0x0fbf0fd0, "fsqrtd%c\t%z1, %z0"},
+  {FPU_VFP_EXT_V1, 0x0eb70ac0, 0x0fbf0fd0, "fcvtds%c\t%z1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0eb70bc0, 0x0fbf0fd0, "fcvtsd%c\t%y1, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0eb80a40, 0x0fbf0fd0, "fuitos%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0eb80ac0, 0x0fbf0fd0, "fsitos%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0eb80b40, 0x0fbf0fd0, "fuitod%c\t%z1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0eb80bc0, 0x0fbf0fd0, "fsitod%c\t%z1, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0eb40a40, 0x0fbf0f50, "fcmp%7'es%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0eb40b40, 0x0fbf0f50, "fcmp%7'ed%c\t%z1, %z0"},
+  {FPU_VFP_EXT_V3, 0x0eba0a40, 0x0fbe0f50, "f%16?us%7?lhtos%c\t%y1, #%5,0-3k"},
+  {FPU_VFP_EXT_V3, 0x0eba0b40, 0x0fbe0f50, "f%16?us%7?lhtod%c\t%z1, #%5,0-3k"},
+  {FPU_VFP_EXT_V1xD, 0x0ebc0a40, 0x0fbe0f50, "fto%16?sui%7'zs%c\t%y1, %y0"},
+  {FPU_VFP_EXT_V1, 0x0ebc0b40, 0x0fbe0f50, "fto%16?sui%7'zd%c\t%y1, %z0"},
+  {FPU_VFP_EXT_V3, 0x0ebe0a40, 0x0fbe0f50, "fto%16?us%7?lhs%c\t%y1, #%5,0-3k"},
+  {FPU_VFP_EXT_V3, 0x0ebe0b40, 0x0fbe0f50, "fto%16?us%7?lhd%c\t%z1, #%5,0-3k"},
+  {FPU_VFP_EXT_V1, 0x0c500b10, 0x0fb00ff0, "fmrrd%c\t%12-15r, %16-19r, %z0"},
+  {FPU_VFP_EXT_V3, 0x0eb00a00, 0x0fb00ff0, "fconsts%c\t%y1, #%0-3,16-19d"},
+  {FPU_VFP_EXT_V3, 0x0eb00b00, 0x0fb00ff0, "fconstd%c\t%z1, #%0-3,16-19d"},
+  {FPU_VFP_EXT_V2, 0x0c400a10, 0x0ff00fd0, "fmsrr%c\t%y4, %12-15r, %16-19r"},
+  {FPU_VFP_EXT_V2, 0x0c400b10, 0x0ff00fd0, "fmdrr%c\t%z0, %12-15r, %16-19r"},
+  {FPU_VFP_EXT_V2, 0x0c500a10, 0x0ff00fd0, "fmrrs%c\t%12-15r, %16-19r, %y4"},
+  {FPU_VFP_EXT_V1xD, 0x0e000a00, 0x0fb00f50, "fmacs%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0e000a40, 0x0fb00f50, "fnmacs%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1, 0x0e000b00, 0x0fb00f50, "fmacd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1, 0x0e000b40, 0x0fb00f50, "fnmacd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0e100a00, 0x0fb00f50, "fmscs%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0e100a40, 0x0fb00f50, "fnmscs%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1, 0x0e100b00, 0x0fb00f50, "fmscd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1, 0x0e100b40, 0x0fb00f50, "fnmscd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0e200a00, 0x0fb00f50, "fmuls%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0e200a40, 0x0fb00f50, "fnmuls%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1, 0x0e200b00, 0x0fb00f50, "fmuld%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1, 0x0e200b40, 0x0fb00f50, "fnmuld%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0e300a00, 0x0fb00f50, "fadds%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1xD, 0x0e300a40, 0x0fb00f50, "fsubs%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1, 0x0e300b00, 0x0fb00f50, "faddd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1, 0x0e300b40, 0x0fb00f50, "fsubd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0e800a00, 0x0fb00f50, "fdivs%c\t%y1, %y2, %y0"},
+  {FPU_VFP_EXT_V1, 0x0e800b00, 0x0fb00f50, "fdivd%c\t%z1, %z2, %z0"},
+  {FPU_VFP_EXT_V1xD, 0x0d200a00, 0x0fb00f00, "fstmdbs%c\t%16-19r!, %y3"},
+  {FPU_VFP_EXT_V1xD, 0x0d200b00, 0x0fb00f00, "fstmdb%0?xd%c\t%16-19r!, %z3"},
+  {FPU_VFP_EXT_V1xD, 0x0d300a00, 0x0fb00f00, "fldmdbs%c\t%16-19r!, %y3"},
+  {FPU_VFP_EXT_V1xD, 0x0d300b00, 0x0fb00f00, "fldmdb%0?xd%c\t%16-19r!, %z3"},
+  {FPU_VFP_EXT_V1xD, 0x0d000a00, 0x0f300f00, "fsts%c\t%y1, %A"},
+  {FPU_VFP_EXT_V1, 0x0d000b00, 0x0f300f00, "fstd%c\t%z1, %A"},
+  {FPU_VFP_EXT_V1xD, 0x0d100a00, 0x0f300f00, "flds%c\t%y1, %A"},
+  {FPU_VFP_EXT_V1, 0x0d100b00, 0x0f300f00, "fldd%c\t%z1, %A"},
+  {FPU_VFP_EXT_V1xD, 0x0c800a00, 0x0f900f00, "fstmias%c\t%16-19r%21'!, %y3"},
+  {FPU_VFP_EXT_V1xD, 0x0c800b00, 0x0f900f00, "fstmia%0?xd%c\t%16-19r%21'!, %z3"},
+  {FPU_VFP_EXT_V1xD, 0x0c900a00, 0x0f900f00, "fldmias%c\t%16-19r%21'!, %y3"},
+  {FPU_VFP_EXT_V1xD, 0x0c900b00, 0x0f900f00, "fldmia%0?xd%c\t%16-19r%21'!, %z3"},
+
+  /* Cirrus coprocessor instructions.  */
+  {ARM_CEXT_MAVERICK, 0x0d100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c100400, 0x0f500f00, "cfldrs%c\tmvf%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c500400, 0x0f500f00, "cfldrd%c\tmvd%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c100500, 0x0f500f00, "cfldr32%c\tmvfx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c500500, 0x0f500f00, "cfldr64%c\tmvdx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c000400, 0x0f500f00, "cfstrs%c\tmvf%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c400400, 0x0f500f00, "cfstrd%c\tmvd%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c000500, 0x0f500f00, "cfstr32%c\tmvfx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0d400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0c400500, 0x0f500f00, "cfstr64%c\tmvdx%12-15d, %A"},
+  {ARM_CEXT_MAVERICK, 0x0e000450, 0x0ff00ff0, "cfmvsr%c\tmvf%16-19d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e100450, 0x0ff00ff0, "cfmvrs%c\t%12-15r, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000410, 0x0ff00ff0, "cfmvdlr%c\tmvd%16-19d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e100410, 0x0ff00ff0, "cfmvrdl%c\t%12-15r, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000430, 0x0ff00ff0, "cfmvdhr%c\tmvd%16-19d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e100430, 0x0ff00fff, "cfmvrdh%c\t%12-15r, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000510, 0x0ff00fff, "cfmv64lr%c\tmvdx%16-19d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e100510, 0x0ff00fff, "cfmvr64l%c\t%12-15r, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000530, 0x0ff00fff, "cfmv64hr%c\tmvdx%16-19d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e100530, 0x0ff00fff, "cfmvr64h%c\t%12-15r, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e200440, 0x0ff00fff, "cfmval32%c\tmvax%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e100440, 0x0ff00fff, "cfmv32al%c\tmvfx%12-15d, mvax%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e200460, 0x0ff00fff, "cfmvam32%c\tmvax%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e100460, 0x0ff00fff, "cfmv32am%c\tmvfx%12-15d, mvax%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e200480, 0x0ff00fff, "cfmvah32%c\tmvax%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e100480, 0x0ff00fff, "cfmv32ah%c\tmvfx%12-15d, mvax%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e2004a0, 0x0ff00fff, "cfmva32%c\tmvax%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e1004a0, 0x0ff00fff, "cfmv32a%c\tmvfx%12-15d, mvax%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e2004c0, 0x0ff00fff, "cfmva64%c\tmvax%12-15d, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e1004c0, 0x0ff00fff, "cfmv64a%c\tmvdx%12-15d, mvax%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e2004e0, 0x0fff0fff, "cfmvsc32%c\tdspsc, mvdx%12-15d"},
+  {ARM_CEXT_MAVERICK, 0x0e1004e0, 0x0fff0fff, "cfmv32sc%c\tmvdx%12-15d, dspsc"},
+  {ARM_CEXT_MAVERICK, 0x0e000400, 0x0ff00fff, "cfcpys%c\tmvf%12-15d, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000420, 0x0ff00fff, "cfcpyd%c\tmvd%12-15d, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000460, 0x0ff00fff, "cfcvtsd%c\tmvd%12-15d, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000440, 0x0ff00fff, "cfcvtds%c\tmvf%12-15d, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000480, 0x0ff00fff, "cfcvt32s%c\tmvf%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e0004a0, 0x0ff00fff, "cfcvt32d%c\tmvd%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e0004c0, 0x0ff00fff, "cfcvt64s%c\tmvf%12-15d, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e0004e0, 0x0ff00fff, "cfcvt64d%c\tmvd%12-15d, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e100580, 0x0ff00fff, "cfcvts32%c\tmvfx%12-15d, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e1005a0, 0x0ff00fff, "cfcvtd32%c\tmvfx%12-15d, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e1005c0, 0x0ff00fff, "cftruncs32%c\tmvfx%12-15d, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e1005e0, 0x0ff00fff, "cftruncd32%c\tmvfx%12-15d, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e000550, 0x0ff00ff0, "cfrshl32%c\tmvfx%16-19d, mvfx%0-3d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e000570, 0x0ff00ff0, "cfrshl64%c\tmvdx%16-19d, mvdx%0-3d, %12-15r"},
+  {ARM_CEXT_MAVERICK, 0x0e000500, 0x0ff00f10, "cfsh32%c\tmvfx%12-15d, mvfx%16-19d, #%I"},
+  {ARM_CEXT_MAVERICK, 0x0e200500, 0x0ff00f10, "cfsh64%c\tmvdx%12-15d, mvdx%16-19d, #%I"},
+  {ARM_CEXT_MAVERICK, 0x0e100490, 0x0ff00ff0, "cfcmps%c\t%12-15r, mvf%16-19d, mvf%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e1004b0, 0x0ff00ff0, "cfcmpd%c\t%12-15r, mvd%16-19d, mvd%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100590, 0x0ff00ff0, "cfcmp32%c\t%12-15r, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e1005b0, 0x0ff00ff0, "cfcmp64%c\t%12-15r, mvdx%16-19d, mvdx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e300400, 0x0ff00fff, "cfabss%c\tmvf%12-15d, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300420, 0x0ff00fff, "cfabsd%c\tmvd%12-15d, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300440, 0x0ff00fff, "cfnegs%c\tmvf%12-15d, mvf%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300460, 0x0ff00fff, "cfnegd%c\tmvd%12-15d, mvd%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300480, 0x0ff00ff0, "cfadds%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e3004a0, 0x0ff00ff0, "cfaddd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e3004c0, 0x0ff00ff0, "cfsubs%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e3004e0, 0x0ff00ff0, "cfsubd%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100400, 0x0ff00ff0, "cfmuls%c\tmvf%12-15d, mvf%16-19d, mvf%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100420, 0x0ff00ff0, "cfmuld%c\tmvd%12-15d, mvd%16-19d, mvd%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e300500, 0x0ff00fff, "cfabs32%c\tmvfx%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300520, 0x0ff00fff, "cfabs64%c\tmvdx%12-15d, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300540, 0x0ff00fff, "cfneg32%c\tmvfx%12-15d, mvfx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300560, 0x0ff00fff, "cfneg64%c\tmvdx%12-15d, mvdx%16-19d"},
+  {ARM_CEXT_MAVERICK, 0x0e300580, 0x0ff00ff0, "cfadd32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e3005a0, 0x0ff00ff0, "cfadd64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e3005c0, 0x0ff00ff0, "cfsub32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e3005e0, 0x0ff00ff0, "cfsub64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100500, 0x0ff00ff0, "cfmul32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100520, 0x0ff00ff0, "cfmul64%c\tmvdx%12-15d, mvdx%16-19d, mvdx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100540, 0x0ff00ff0, "cfmac32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100560, 0x0ff00ff0, "cfmsc32%c\tmvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e000600, 0x0ff00f10, "cfmadd32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e100600, 0x0ff00f10, "cfmsub32%c\tmvax%5-7d, mvfx%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e200600, 0x0ff00f10, "cfmadda32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"},
+  {ARM_CEXT_MAVERICK, 0x0e300600, 0x0ff00f10, "cfmsuba32%c\tmvax%5-7d, mvax%12-15d, mvfx%16-19d, mvfx%0-3d"},
+
+  /* Generic coprocessor instructions */
+  {ARM_EXT_V2, 0x0c400000, 0x0ff00000, "mcrr%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+  {ARM_EXT_V2, 0x0c500000, 0x0ff00000, "mrrc%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+  {ARM_EXT_V2, 0x0e000000, 0x0f000010, "cdp%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+  {ARM_EXT_V2, 0x0e100010, 0x0f100010, "mrc%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+  {ARM_EXT_V2, 0x0e000010, 0x0f100010, "mcr%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+  {ARM_EXT_V2, 0x0c000000, 0x0e100000, "stc%22'l%c\t%8-11d, cr%12-15d, %A"},
+  {ARM_EXT_V2, 0x0c100000, 0x0e100000, "ldc%22'l%c\t%8-11d, cr%12-15d, %A"},
+
+  /* V6 coprocessor instructions */
+  {ARM_EXT_V6, 0xfc500000, 0xfff00000, "mrrc2%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+  {ARM_EXT_V6, 0xfc400000, 0xfff00000, "mcrr2%c\t%8-11d, %4-7d, %12-15r, %16-19r, cr%0-3d"},
+
+  /* V5 coprocessor instructions */
+  {ARM_EXT_V5, 0xfc100000, 0xfe100000, "ldc2%22'l%c\t%8-11d, cr%12-15d, %A"},
+  {ARM_EXT_V5, 0xfc000000, 0xfe100000, "stc2%22'l%c\t%8-11d, cr%12-15d, %A"},
+  {ARM_EXT_V5, 0xfe000000, 0xff000010, "cdp2%c\t%8-11d, %20-23d, cr%12-15d, cr%16-19d, cr%0-3d, {%5-7d}"},
+  {ARM_EXT_V5, 0xfe000010, 0xff100010, "mcr2%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+  {ARM_EXT_V5, 0xfe100010, 0xff100010, "mrc2%c\t%8-11d, %21-23d, %12-15r, cr%16-19d, cr%0-3d, {%5-7d}"},
+
+  {0, 0, 0, 0}
+};
+
+/* Neon opcode table:  This does not encode the top byte -- that is
+   checked by the print_insn_neon routine, as it depends on whether we are
+   doing thumb32 or arm32 disassembly.  */
+
+/* print_insn_neon recognizes the following format control codes:
+
+   %%			%
+
+   %c			print condition code
+   %A			print v{st,ld}[1234] operands
+   %B			print v{st,ld}[1234] any one operands
+   %C			print v{st,ld}[1234] single->all operands
+   %D			print scalar
+   %E			print vmov, vmvn, vorr, vbic encoded constant
+   %F			print vtbl,vtbx register list
+
+   %<bitfield>r		print as an ARM register
+   %<bitfield>d		print the bitfield in decimal
+   %<bitfield>e         print the 2^N - bitfield in decimal
+   %<bitfield>D		print as a NEON D register
+   %<bitfield>Q		print as a NEON Q register
+   %<bitfield>R		print as a NEON D or Q register
+   %<bitfield>Sn	print byte scaled width limited by n
+   %<bitfield>Tn	print short scaled width limited by n
+   %<bitfield>Un	print long scaled width limited by n
+
+   %<bitfield>'c	print specified char iff bitfield is all ones
+   %<bitfield>`c	print specified char iff bitfield is all zeroes
+   %<bitfield>?ab...    select from array of values in big endian order  */
+
+static const struct opcode32 neon_opcodes[] =
+{
+  /* Extract */
+  {FPU_NEON_EXT_V1, 0xf2b00840, 0xffb00850, "vext%c.8\t%12-15,22R, %16-19,7R, %0-3,5R, #%8-11d"},
+  {FPU_NEON_EXT_V1, 0xf2b00000, 0xffb00810, "vext%c.8\t%12-15,22R, %16-19,7R, %0-3,5R, #%8-11d"},
+
+  /* Move data element to all lanes */
+  {FPU_NEON_EXT_V1, 0xf3b40c00, 0xffb70f90, "vdup%c.32\t%12-15,22R, %0-3,5D[%19d]"},
+  {FPU_NEON_EXT_V1, 0xf3b20c00, 0xffb30f90, "vdup%c.16\t%12-15,22R, %0-3,5D[%18-19d]"},
+  {FPU_NEON_EXT_V1, 0xf3b10c00, 0xffb10f90, "vdup%c.8\t%12-15,22R, %0-3,5D[%17-19d]"},
+
+  /* Table lookup */
+  {FPU_NEON_EXT_V1, 0xf3b00800, 0xffb00c50, "vtbl%c.8\t%12-15,22D, %F, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf3b00840, 0xffb00c50, "vtbx%c.8\t%12-15,22D, %F, %0-3,5D"},
+
+  /* Two registers, miscellaneous */
+  {FPU_NEON_EXT_V1, 0xf2880a10, 0xfebf0fd0, "vmovl%c.%24?us8\t%12-15,22Q, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2900a10, 0xfebf0fd0, "vmovl%c.%24?us16\t%12-15,22Q, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2a00a10, 0xfebf0fd0, "vmovl%c.%24?us32\t%12-15,22Q, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf3b00500, 0xffbf0f90, "vcnt%c.8\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00580, 0xffbf0f90, "vmvn%c\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b20000, 0xffbf0f90, "vswp%c\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b20200, 0xffb30fd0, "vmovn%c.i%18-19T2\t%12-15,22D, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf3b20240, 0xffb30fd0, "vqmovun%c.s%18-19T2\t%12-15,22D, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf3b20280, 0xffb30fd0, "vqmovn%c.s%18-19T2\t%12-15,22D, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf3b202c0, 0xffb30fd0, "vqmovn%c.u%18-19T2\t%12-15,22D, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf3b20300, 0xffb30fd0, "vshll%c.i%18-19S2\t%12-15,22Q, %0-3,5D, #%18-19S2"},
+  {FPU_NEON_EXT_V1, 0xf3bb0400, 0xffbf0e90, "vrecpe%c.%8?fu%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3bb0480, 0xffbf0e90, "vrsqrte%c.%8?fu%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00000, 0xffb30f90, "vrev64%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00080, 0xffb30f90, "vrev32%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00100, 0xffb30f90, "vrev16%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00400, 0xffb30f90, "vcls%c.s%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00480, 0xffb30f90, "vclz%c.i%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00700, 0xffb30f90, "vqabs%c.s%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00780, 0xffb30f90, "vqneg%c.s%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b20080, 0xffb30f90, "vtrn%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b20100, 0xffb30f90, "vuzp%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b20180, 0xffb30f90, "vzip%c.%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b10000, 0xffb30b90, "vcgt%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+  {FPU_NEON_EXT_V1, 0xf3b10080, 0xffb30b90, "vcge%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+  {FPU_NEON_EXT_V1, 0xf3b10100, 0xffb30b90, "vceq%c.%10?fi%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+  {FPU_NEON_EXT_V1, 0xf3b10180, 0xffb30b90, "vcle%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+  {FPU_NEON_EXT_V1, 0xf3b10200, 0xffb30b90, "vclt%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R, #0"},
+  {FPU_NEON_EXT_V1, 0xf3b10300, 0xffb30b90, "vabs%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b10380, 0xffb30b90, "vneg%c.%10?fs%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00200, 0xffb30f10, "vpaddl%c.%7?us%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b00600, 0xffb30f10, "vpadal%c.%7?us%18-19S2\t%12-15,22R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3b30600, 0xffb30e10, "vcvt%c.%7-8?usff%18-19Sa.%7-8?ffus%18-19Sa\t%12-15,22R, %0-3,5R"},
+
+  /* Three registers of the same length */
+  {FPU_NEON_EXT_V1, 0xf2000110, 0xffb00f10, "vand%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2100110, 0xffb00f10, "vbic%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2200110, 0xffb00f10, "vorr%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2300110, 0xffb00f10, "vorn%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000110, 0xffb00f10, "veor%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3100110, 0xffb00f10, "vbsl%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3200110, 0xffb00f10, "vbit%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3300110, 0xffb00f10, "vbif%c\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000d00, 0xffa00f10, "vadd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000d10, 0xffa00f10, "vmla%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000e00, 0xffa00f10, "vceq%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000f00, 0xffa00f10, "vmax%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000f10, 0xffa00f10, "vrecps%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2200d00, 0xffa00f10, "vsub%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2200d10, 0xffa00f10, "vmls%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2200f00, 0xffa00f10, "vmin%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2200f10, 0xffa00f10, "vrsqrts%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000d00, 0xffa00f10, "vpadd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000d10, 0xffa00f10, "vmul%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000e00, 0xffa00f10, "vcge%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000e10, 0xffa00f10, "vacge%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000f00, 0xffa00f10, "vpmax%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3200d00, 0xffa00f10, "vabd%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3200e00, 0xffa00f10, "vcgt%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3200e10, 0xffa00f10, "vacgt%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3200f00, 0xffa00f10, "vpmin%c.f%20U0\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000800, 0xff800f10, "vadd%c.i%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000810, 0xff800f10, "vtst%c.%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000900, 0xff800f10, "vmla%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000b00, 0xff800f10, "vqdmulh%c.s%20-21S6\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000b10, 0xff800f10, "vpadd%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000800, 0xff800f10, "vsub%c.i%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000810, 0xff800f10, "vceq%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000900, 0xff800f10, "vmls%c.i%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf3000b00, 0xff800f10, "vqrdmulh%c.s%20-21S6\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000000, 0xfe800f10, "vhadd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000010, 0xfe800f10, "vqadd%c.%24?us%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000100, 0xfe800f10, "vrhadd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000200, 0xfe800f10, "vhsub%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000210, 0xfe800f10, "vqsub%c.%24?us%20-21S3\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000300, 0xfe800f10, "vcgt%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000310, 0xfe800f10, "vcge%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000400, 0xfe800f10, "vshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+  {FPU_NEON_EXT_V1, 0xf2000410, 0xfe800f10, "vqshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+  {FPU_NEON_EXT_V1, 0xf2000500, 0xfe800f10, "vrshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+  {FPU_NEON_EXT_V1, 0xf2000510, 0xfe800f10, "vqrshl%c.%24?us%20-21S3\t%12-15,22R, %0-3,5R, %16-19,7R"},
+  {FPU_NEON_EXT_V1, 0xf2000600, 0xfe800f10, "vmax%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000610, 0xfe800f10, "vmin%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000700, 0xfe800f10, "vabd%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000710, 0xfe800f10, "vaba%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000910, 0xfe800f10, "vmul%c.%24?pi%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000a00, 0xfe800f10, "vpmax%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+  {FPU_NEON_EXT_V1, 0xf2000a10, 0xfe800f10, "vpmin%c.%24?us%20-21S2\t%12-15,22R, %16-19,7R, %0-3,5R"},
+
+  /* One register and an immediate value */
+  {FPU_NEON_EXT_V1, 0xf2800e10, 0xfeb80fb0, "vmov%c.i8\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800e30, 0xfeb80fb0, "vmov%c.i64\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800f10, 0xfeb80fb0, "vmov%c.f32\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800810, 0xfeb80db0, "vmov%c.i16\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800830, 0xfeb80db0, "vmvn%c.i16\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800910, 0xfeb80db0, "vorr%c.i16\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800930, 0xfeb80db0, "vbic%c.i16\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800c10, 0xfeb80eb0, "vmov%c.i32\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800c30, 0xfeb80eb0, "vmvn%c.i32\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800110, 0xfeb809b0, "vorr%c.i32\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800130, 0xfeb809b0, "vbic%c.i32\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800010, 0xfeb808b0, "vmov%c.i32\t%12-15,22R, %E"},
+  {FPU_NEON_EXT_V1, 0xf2800030, 0xfeb808b0, "vmvn%c.i32\t%12-15,22R, %E"},
+
+  /* Two registers and a shift amount */
+  {FPU_NEON_EXT_V1, 0xf2880810, 0xffb80fd0, "vshrn%c.i16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880850, 0xffb80fd0, "vrshrn%c.i16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880810, 0xfeb80fd0, "vqshrun%c.s16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880850, 0xfeb80fd0, "vqrshrun%c.s16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880910, 0xfeb80fd0, "vqshrn%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880950, 0xfeb80fd0, "vqrshrn%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880a10, 0xfeb80fd0, "vshll%c.%24?us8\t%12-15,22D, %0-3,5Q, #%16-18d"},
+  {FPU_NEON_EXT_V1, 0xf2900810, 0xffb00fd0, "vshrn%c.i32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900850, 0xffb00fd0, "vrshrn%c.i32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2880510, 0xffb80f90, "vshl%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18d"},
+  {FPU_NEON_EXT_V1, 0xf3880410, 0xffb80f90, "vsri%c.8\t%12-15,22R, %0-3,5R, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf3880510, 0xffb80f90, "vsli%c.8\t%12-15,22R, %0-3,5R, #%16-18d"},
+  {FPU_NEON_EXT_V1, 0xf3880610, 0xffb80f90, "vqshlu%c.s8\t%12-15,22R, %0-3,5R, #%16-18d"},
+  {FPU_NEON_EXT_V1, 0xf2900810, 0xfeb00fd0, "vqshrun%c.s32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900850, 0xfeb00fd0, "vqrshrun%c.s32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900910, 0xfeb00fd0, "vqshrn%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900950, 0xfeb00fd0, "vqrshrn%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900a10, 0xfeb00fd0, "vshll%c.%24?us16\t%12-15,22D, %0-3,5Q, #%16-19d"},
+  {FPU_NEON_EXT_V1, 0xf2880010, 0xfeb80f90, "vshr%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880110, 0xfeb80f90, "vsra%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880210, 0xfeb80f90, "vrshr%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880310, 0xfeb80f90, "vrsra%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18e"},
+  {FPU_NEON_EXT_V1, 0xf2880710, 0xfeb80f90, "vqshl%c.%24?us8\t%12-15,22R, %0-3,5R, #%16-18d"},
+  {FPU_NEON_EXT_V1, 0xf2a00810, 0xffa00fd0, "vshrn%c.i64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2a00850, 0xffa00fd0, "vrshrn%c.i64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2900510, 0xffb00f90, "vshl%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19d"},
+  {FPU_NEON_EXT_V1, 0xf3900410, 0xffb00f90, "vsri%c.16\t%12-15,22R, %0-3,5R, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf3900510, 0xffb00f90, "vsli%c.16\t%12-15,22R, %0-3,5R, #%16-19d"},
+  {FPU_NEON_EXT_V1, 0xf3900610, 0xffb00f90, "vqshlu%c.s16\t%12-15,22R, %0-3,5R, #%16-19d"},
+  {FPU_NEON_EXT_V1, 0xf2a00a10, 0xfea00fd0, "vshll%c.%24?us32\t%12-15,22D, %0-3,5Q, #%16-20d"},
+  {FPU_NEON_EXT_V1, 0xf2900010, 0xfeb00f90, "vshr%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900110, 0xfeb00f90, "vsra%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900210, 0xfeb00f90, "vrshr%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900310, 0xfeb00f90, "vrsra%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19e"},
+  {FPU_NEON_EXT_V1, 0xf2900710, 0xfeb00f90, "vqshl%c.%24?us16\t%12-15,22R, %0-3,5R, #%16-19d"},
+  {FPU_NEON_EXT_V1, 0xf2800810, 0xfec00fd0, "vqshrun%c.s64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2800850, 0xfec00fd0, "vqrshrun%c.s64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2800910, 0xfec00fd0, "vqshrn%c.%24?us64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2800950, 0xfec00fd0, "vqrshrn%c.%24?us64\t%12-15,22D, %0-3,5Q, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2a00510, 0xffa00f90, "vshl%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20d"},
+  {FPU_NEON_EXT_V1, 0xf3a00410, 0xffa00f90, "vsri%c.32\t%12-15,22R, %0-3,5R, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf3a00510, 0xffa00f90, "vsli%c.32\t%12-15,22R, %0-3,5R, #%16-20d"},
+  {FPU_NEON_EXT_V1, 0xf3a00610, 0xffa00f90, "vqshlu%c.s32\t%12-15,22R, %0-3,5R, #%16-20d"},
+  {FPU_NEON_EXT_V1, 0xf2a00010, 0xfea00f90, "vshr%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2a00110, 0xfea00f90, "vsra%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2a00210, 0xfea00f90, "vrshr%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2a00310, 0xfea00f90, "vrsra%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20e"},
+  {FPU_NEON_EXT_V1, 0xf2a00710, 0xfea00f90, "vqshl%c.%24?us32\t%12-15,22R, %0-3,5R, #%16-20d"},
+  {FPU_NEON_EXT_V1, 0xf2800590, 0xff800f90, "vshl%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21d"},
+  {FPU_NEON_EXT_V1, 0xf3800490, 0xff800f90, "vsri%c.64\t%12-15,22R, %0-3,5R, #%16-21e"},
+  {FPU_NEON_EXT_V1, 0xf3800590, 0xff800f90, "vsli%c.64\t%12-15,22R, %0-3,5R, #%16-21d"},
+  {FPU_NEON_EXT_V1, 0xf3800690, 0xff800f90, "vqshlu%c.s64\t%12-15,22R, %0-3,5R, #%16-21d"},
+  {FPU_NEON_EXT_V1, 0xf2800090, 0xfe800f90, "vshr%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+  {FPU_NEON_EXT_V1, 0xf2800190, 0xfe800f90, "vsra%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+  {FPU_NEON_EXT_V1, 0xf2800290, 0xfe800f90, "vrshr%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+  {FPU_NEON_EXT_V1, 0xf2800390, 0xfe800f90, "vrsra%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21e"},
+  {FPU_NEON_EXT_V1, 0xf2800790, 0xfe800f90, "vqshl%c.%24?us64\t%12-15,22R, %0-3,5R, #%16-21d"},
+  {FPU_NEON_EXT_V1, 0xf2a00e10, 0xfea00e90, "vcvt%c.%24,8?usff32.%24,8?ffus32\t%12-15,22R, %0-3,5R, #%16-20e"},
+
+  /* Three registers of different lengths */
+  {FPU_NEON_EXT_V1, 0xf2800e00, 0xfea00f50, "vmull%c.p%20S0\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800400, 0xff800f50, "vaddhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf2800600, 0xff800f50, "vsubhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf2800900, 0xff800f50, "vqdmlal%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800b00, 0xff800f50, "vqdmlsl%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800d00, 0xff800f50, "vqdmull%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf3800400, 0xff800f50, "vraddhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf3800600, 0xff800f50, "vrsubhn%c.i%20-21T2\t%12-15,22D, %16-19,7Q, %0-3,5Q"},
+  {FPU_NEON_EXT_V1, 0xf2800000, 0xfe800f50, "vaddl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800100, 0xfe800f50, "vaddw%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7Q, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800200, 0xfe800f50, "vsubl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800300, 0xfe800f50, "vsubw%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7Q, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800500, 0xfe800f50, "vabal%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800700, 0xfe800f50, "vabdl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800800, 0xfe800f50, "vmlal%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800a00, 0xfe800f50, "vmlsl%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+  {FPU_NEON_EXT_V1, 0xf2800c00, 0xfe800f50, "vmull%c.%24?us%20-21S2\t%12-15,22Q, %16-19,7D, %0-3,5D"},
+
+  /* Two registers and a scalar */
+  {FPU_NEON_EXT_V1, 0xf2800040, 0xff800f50, "vmla%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800140, 0xff800f50, "vmla%c.f%20-21Sa\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800340, 0xff800f50, "vqdmlal%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800440, 0xff800f50, "vmls%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800540, 0xff800f50, "vmls%c.f%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800740, 0xff800f50, "vqdmlsl%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800840, 0xff800f50, "vmul%c.i%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800940, 0xff800f50, "vmul%c.f%20-21Sa\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800b40, 0xff800f50, "vqdmull%c.s%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800c40, 0xff800f50, "vqdmulh%c.s%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800d40, 0xff800f50, "vqrdmulh%c.s%20-21S6\t%12-15,22D, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800040, 0xff800f50, "vmla%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800140, 0xff800f50, "vmla%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800440, 0xff800f50, "vmls%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800540, 0xff800f50, "vmls%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800840, 0xff800f50, "vmul%c.i%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800940, 0xff800f50, "vmul%c.f%20-21Sa\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800c40, 0xff800f50, "vqdmulh%c.s%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf3800d40, 0xff800f50, "vqrdmulh%c.s%20-21S6\t%12-15,22Q, %16-19,7Q, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800240, 0xfe800f50, "vmlal%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800640, 0xfe800f50, "vmlsl%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+  {FPU_NEON_EXT_V1, 0xf2800a40, 0xfe800f50, "vmull%c.%24?us%20-21S6\t%12-15,22Q, %16-19,7D, %D"},
+
+  /* Element and structure load/store */
+  {FPU_NEON_EXT_V1, 0xf4a00fc0, 0xffb00fc0, "vld4%c.32\t%C"},
+  {FPU_NEON_EXT_V1, 0xf4a00c00, 0xffb00f00, "vld1%c.%6-7S2\t%C"},
+  {FPU_NEON_EXT_V1, 0xf4a00d00, 0xffb00f00, "vld2%c.%6-7S2\t%C"},
+  {FPU_NEON_EXT_V1, 0xf4a00e00, 0xffb00f00, "vld3%c.%6-7S2\t%C"},
+  {FPU_NEON_EXT_V1, 0xf4a00f00, 0xffb00f00, "vld4%c.%6-7S2\t%C"},
+  {FPU_NEON_EXT_V1, 0xf4000200, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000300, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000400, 0xff900f00, "v%21?ls%21?dt3%c.%6-7S2\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000500, 0xff900f00, "v%21?ls%21?dt3%c.%6-7S2\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000600, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000700, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000800, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000900, 0xff900f00, "v%21?ls%21?dt2%c.%6-7S2\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000a00, 0xff900f00, "v%21?ls%21?dt1%c.%6-7S3\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4000000, 0xff900e00, "v%21?ls%21?dt4%c.%6-7S2\t%A"},
+  {FPU_NEON_EXT_V1, 0xf4800000, 0xff900300, "v%21?ls%21?dt1%c.%10-11S2\t%B"},
+  {FPU_NEON_EXT_V1, 0xf4800100, 0xff900300, "v%21?ls%21?dt2%c.%10-11S2\t%B"},
+  {FPU_NEON_EXT_V1, 0xf4800200, 0xff900300, "v%21?ls%21?dt3%c.%10-11S2\t%B"},
+  {FPU_NEON_EXT_V1, 0xf4800300, 0xff900300, "v%21?ls%21?dt4%c.%10-11S2\t%B"},
+
+  {0,0 ,0, 0}
+};
+
+/* Opcode tables: ARM, 16-bit Thumb, 32-bit Thumb.  All three are partially
+   ordered: they must be searched linearly from the top to obtain a correct
+   match.  */
+
+/* print_insn_arm recognizes the following format control codes:
+
+   %%			%
+
+   %a			print address for ldr/str instruction
+   %s                   print address for ldr/str halfword/signextend instruction
+   %b			print branch destination
+   %c			print condition code (always bits 28-31)
+   %m			print register mask for ldm/stm instruction
+   %o			print operand2 (immediate or register + shift)
+   %p			print 'p' iff bits 12-15 are 15
+   %t			print 't' iff bit 21 set and bit 24 clear
+   %B			print arm BLX(1) destination
+   %C			print the PSR sub type.
+   %U			print barrier type.
+   %P			print address for pli instruction.
+
+   %<bitfield>r		print as an ARM register
+   %<bitfield>d		print the bitfield in decimal
+   %<bitfield>W         print the bitfield plus one in decimal
+   %<bitfield>x		print the bitfield in hex
+   %<bitfield>X		print the bitfield as 1 hex digit without leading "0x"
+
+   %<bitfield>'c	print specified char iff bitfield is all ones
+   %<bitfield>`c	print specified char iff bitfield is all zeroes
+   %<bitfield>?ab...    select from array of values in big endian order
+
+   %e                   print arm SMI operand (bits 0..7,8..19).
+   %E			print the LSB and WIDTH fields of a BFI or BFC instruction.
+   %V                   print the 16-bit immediate field of a MOVT or MOVW instruction.  */
+
+static const struct opcode32 arm_opcodes[] =
+{
+  /* ARM instructions.  */
+  {ARM_EXT_V1, 0xe1a00000, 0xffffffff, "nop\t\t\t(mov r0,r0)"},
+  {ARM_EXT_V4T | ARM_EXT_V5, 0x012FFF10, 0x0ffffff0, "bx%c\t%0-3r"},
+  {ARM_EXT_V2, 0x00000090, 0x0fe000f0, "mul%20's%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V2, 0x00200090, 0x0fe000f0, "mla%20's%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V2S, 0x01000090, 0x0fb00ff0, "swp%22'b%c\t%12-15r, %0-3r, [%16-19r]"},
+  {ARM_EXT_V3M, 0x00800090, 0x0fa000f0, "%22?sumull%20's%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V3M, 0x00a00090, 0x0fa000f0, "%22?sumlal%20's%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+
+  /* V7 instructions.  */
+  {ARM_EXT_V7, 0xf450f000, 0xfd70f000, "pli\t%P"},
+  {ARM_EXT_V7, 0x0320f0f0, 0x0ffffff0, "dbg%c\t#%0-3d"},
+  {ARM_EXT_V7, 0xf57ff050, 0xfffffff0, "dmb\t%U"},
+  {ARM_EXT_V7, 0xf57ff040, 0xfffffff0, "dsb\t%U"},
+  {ARM_EXT_V7, 0xf57ff060, 0xfffffff0, "isb\t%U"},
+
+  /* ARM V6T2 instructions.  */
+  {ARM_EXT_V6T2, 0x07c0001f, 0x0fe0007f, "bfc%c\t%12-15r, %E"},
+  {ARM_EXT_V6T2, 0x07c00010, 0x0fe00070, "bfi%c\t%12-15r, %0-3r, %E"},
+  {ARM_EXT_V6T2, 0x00600090, 0x0ff000f0, "mls%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V6T2, 0x006000b0, 0x0f7000f0, "strht%c\t%12-15r, %s"},
+  {ARM_EXT_V6T2, 0x00300090, 0x0f300090, "ldr%6's%5?hbt%c\t%12-15r, %s"},
+  {ARM_EXT_V6T2, 0x03000000, 0x0ff00000, "movw%c\t%12-15r, %V"},
+  {ARM_EXT_V6T2, 0x03400000, 0x0ff00000, "movt%c\t%12-15r, %V"},
+  {ARM_EXT_V6T2, 0x06ff0f30, 0x0fff0ff0, "rbit%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6T2, 0x07a00050, 0x0fa00070, "%22?usbfx%c\t%12-15r, %0-3r, #%7-11d, #%16-20W"},
+
+  /* ARM V6Z instructions.  */
+  {ARM_EXT_V6Z, 0x01600070, 0x0ff000f0, "smc%c\t%e"},
+
+  /* ARM V6K instructions.  */
+  {ARM_EXT_V6K, 0xf57ff01f, 0xffffffff, "clrex"},
+  {ARM_EXT_V6K, 0x01d00f9f, 0x0ff00fff, "ldrexb%c\t%12-15r, [%16-19r]"},
+  {ARM_EXT_V6K, 0x01b00f9f, 0x0ff00fff, "ldrexd%c\t%12-15r, [%16-19r]"},
+  {ARM_EXT_V6K, 0x01f00f9f, 0x0ff00fff, "ldrexh%c\t%12-15r, [%16-19r]"},
+  {ARM_EXT_V6K, 0x01c00f90, 0x0ff00ff0, "strexb%c\t%12-15r, %0-3r, [%16-19r]"},
+  {ARM_EXT_V6K, 0x01a00f90, 0x0ff00ff0, "strexd%c\t%12-15r, %0-3r, [%16-19r]"},
+  {ARM_EXT_V6K, 0x01e00f90, 0x0ff00ff0, "strexh%c\t%12-15r, %0-3r, [%16-19r]"},
+
+  /* ARM V6K NOP hints.  */
+  {ARM_EXT_V6K, 0x0320f001, 0x0fffffff, "yield%c"},
+  {ARM_EXT_V6K, 0x0320f002, 0x0fffffff, "wfe%c"},
+  {ARM_EXT_V6K, 0x0320f003, 0x0fffffff, "wfi%c"},
+  {ARM_EXT_V6K, 0x0320f004, 0x0fffffff, "sev%c"},
+  {ARM_EXT_V6K, 0x0320f000, 0x0fffff00, "nop%c\t{%0-7d}"},
+
+  /* ARM V6 instructions. */
+  {ARM_EXT_V6, 0xf1080000, 0xfffffe3f, "cpsie\t%8'a%7'i%6'f"},
+  {ARM_EXT_V6, 0xf10a0000, 0xfffffe20, "cpsie\t%8'a%7'i%6'f,#%0-4d"},
+  {ARM_EXT_V6, 0xf10C0000, 0xfffffe3f, "cpsid\t%8'a%7'i%6'f"},
+  {ARM_EXT_V6, 0xf10e0000, 0xfffffe20, "cpsid\t%8'a%7'i%6'f,#%0-4d"},
+  {ARM_EXT_V6, 0xf1000000, 0xfff1fe20, "cps\t#%0-4d"},
+  {ARM_EXT_V6, 0x06800010, 0x0ff00ff0, "pkhbt%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06800010, 0x0ff00070, "pkhbt%c\t%12-15r, %16-19r, %0-3r, lsl #%7-11d"},
+  {ARM_EXT_V6, 0x06800050, 0x0ff00ff0, "pkhtb%c\t%12-15r, %16-19r, %0-3r, asr #32"},
+  {ARM_EXT_V6, 0x06800050, 0x0ff00070, "pkhtb%c\t%12-15r, %16-19r, %0-3r, asr #%7-11d"},
+  {ARM_EXT_V6, 0x01900f9f, 0x0ff00fff, "ldrex%c\tr%12-15d, [%16-19r]"},
+  {ARM_EXT_V6, 0x06200f10, 0x0ff00ff0, "qadd16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06200f90, 0x0ff00ff0, "qadd8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06200f30, 0x0ff00ff0, "qaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06200f70, 0x0ff00ff0, "qsub16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06200ff0, 0x0ff00ff0, "qsub8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06200f50, 0x0ff00ff0, "qsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06100f10, 0x0ff00ff0, "sadd16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06100f90, 0x0ff00ff0, "sadd8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06100f30, 0x0ff00ff0, "saddaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06300f10, 0x0ff00ff0, "shadd16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06300f90, 0x0ff00ff0, "shadd8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06300f30, 0x0ff00ff0, "shaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06300f70, 0x0ff00ff0, "shsub16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06300ff0, 0x0ff00ff0, "shsub8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06300f50, 0x0ff00ff0, "shsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06100f70, 0x0ff00ff0, "ssub16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06100ff0, 0x0ff00ff0, "ssub8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06100f50, 0x0ff00ff0, "ssubaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06500f10, 0x0ff00ff0, "uadd16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06500f90, 0x0ff00ff0, "uadd8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06500f30, 0x0ff00ff0, "uaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06700f10, 0x0ff00ff0, "uhadd16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06700f90, 0x0ff00ff0, "uhadd8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06700f30, 0x0ff00ff0, "uhaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06700f70, 0x0ff00ff0, "uhsub16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06700ff0, 0x0ff00ff0, "uhsub8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06700f50, 0x0ff00ff0, "uhsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06600f10, 0x0ff00ff0, "uqadd16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06600f90, 0x0ff00ff0, "uqadd8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06600f30, 0x0ff00ff0, "uqaddsubx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06600f70, 0x0ff00ff0, "uqsub16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06600ff0, 0x0ff00ff0, "uqsub8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06600f50, 0x0ff00ff0, "uqsubaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06500f70, 0x0ff00ff0, "usub16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06500ff0, 0x0ff00ff0, "usub8%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06500f50, 0x0ff00ff0, "usubaddx%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06bf0f30, 0x0fff0ff0, "rev%c\t\%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06bf0fb0, 0x0fff0ff0, "rev16%c\t\%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06ff0fb0, 0x0fff0ff0, "revsh%c\t\%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0xf8100a00, 0xfe50ffff, "rfe%23?id%24?ba\t\%16-19r%21'!"},
+  {ARM_EXT_V6, 0x06bf0070, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06bf0470, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06bf0870, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06bf0c70, 0x0fff0ff0, "sxth%c\t%12-15r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x068f0070, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x068f0470, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x068f0870, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x068f0c70, 0x0fff0ff0, "sxtb16%c\t%12-15r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06af0070, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06af0470, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06af0870, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06af0c70, 0x0fff0ff0, "sxtb%c\t%12-15r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06ff0070, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06ff0470, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06ff0870, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06ff0c70, 0x0fff0ff0, "uxth%c\t%12-15r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06cf0070, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06cf0470, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06cf0870, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06cf0c70, 0x0fff0ff0, "uxtb16%c\t%12-15r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06ef0070, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V6, 0x06ef0470, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06ef0870, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06ef0c70, 0x0fff0ff0, "uxtb%c\t%12-15r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06b00070, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06b00470, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06b00870, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06b00c70, 0x0ff00ff0, "sxtah%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06800070, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06800470, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06800870, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06800c70, 0x0ff00ff0, "sxtab16%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06a00070, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06a00470, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06a00870, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06a00c70, 0x0ff00ff0, "sxtab%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06f00070, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06f00470, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06f00870, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06f00c70, 0x0ff00ff0, "uxtah%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06c00070, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06c00470, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06c00870, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06c00c70, 0x0ff00ff0, "uxtab16%c\t%12-15r, %16-19r, %0-3r, ROR #24"},
+  {ARM_EXT_V6, 0x06e00070, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0x06e00470, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #8"},
+  {ARM_EXT_V6, 0x06e00870, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #16"},
+  {ARM_EXT_V6, 0x06e00c70, 0x0ff00ff0, "uxtab%c\t%12-15r, %16-19r, %0-3r, ror #24"},
+  {ARM_EXT_V6, 0x06800fb0, 0x0ff00ff0, "sel%c\t%12-15r, %16-19r, %0-3r"},
+  {ARM_EXT_V6, 0xf1010000, 0xfffffc00, "setend\t%9?ble"},
+  {ARM_EXT_V6, 0x0700f010, 0x0ff0f0d0, "smuad%5'x%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x0700f050, 0x0ff0f0d0, "smusd%5'x%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x07000010, 0x0ff000d0, "smlad%5'x%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V6, 0x07400010, 0x0ff000d0, "smlald%5'x%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x07000050, 0x0ff000d0, "smlsd%5'x%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V6, 0x07400050, 0x0ff000d0, "smlsld%5'x%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x0750f010, 0x0ff0f0d0, "smmul%5'r%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x07500010, 0x0ff000d0, "smmla%5'r%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V6, 0x075000d0, 0x0ff000d0, "smmls%5'r%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V6, 0xf84d0500, 0xfe5fffe0, "srs%23?id%24?ba\t%16-19r%21'!, #%0-4d"},
+  {ARM_EXT_V6, 0x06a00010, 0x0fe00ff0, "ssat%c\t%12-15r, #%16-20W, %0-3r"},
+  {ARM_EXT_V6, 0x06a00010, 0x0fe00070, "ssat%c\t%12-15r, #%16-20W, %0-3r, lsl #%7-11d"},
+  {ARM_EXT_V6, 0x06a00050, 0x0fe00070, "ssat%c\t%12-15r, #%16-20W, %0-3r, asr #%7-11d"},
+  {ARM_EXT_V6, 0x06a00f30, 0x0ff00ff0, "ssat16%c\t%12-15r, #%16-19W, %0-3r"},
+  {ARM_EXT_V6, 0x01800f90, 0x0ff00ff0, "strex%c\t%12-15r, %0-3r, [%16-19r]"},
+  {ARM_EXT_V6, 0x00400090, 0x0ff000f0, "umaal%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x0780f010, 0x0ff0f0f0, "usad8%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V6, 0x07800010, 0x0ff000f0, "usada8%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V6, 0x06e00010, 0x0fe00ff0, "usat%c\t%12-15r, #%16-20d, %0-3r"},
+  {ARM_EXT_V6, 0x06e00010, 0x0fe00070, "usat%c\t%12-15r, #%16-20d, %0-3r, lsl #%7-11d"},
+  {ARM_EXT_V6, 0x06e00050, 0x0fe00070, "usat%c\t%12-15r, #%16-20d, %0-3r, asr #%7-11d"},
+  {ARM_EXT_V6, 0x06e00f30, 0x0ff00ff0, "usat16%c\t%12-15r, #%16-19d, %0-3r"},
+
+  /* V5J instruction.  */
+  {ARM_EXT_V5J, 0x012fff20, 0x0ffffff0, "bxj%c\t%0-3r"},
+
+  /* V5 Instructions.  */
+  {ARM_EXT_V5, 0xe1200070, 0xfff000f0, "bkpt\t0x%16-19X%12-15X%8-11X%0-3X"},
+  {ARM_EXT_V5, 0xfa000000, 0xfe000000, "blx\t%B"},
+  {ARM_EXT_V5, 0x012fff30, 0x0ffffff0, "blx%c\t%0-3r"},
+  {ARM_EXT_V5, 0x016f0f10, 0x0fff0ff0, "clz%c\t%12-15r, %0-3r"},
+
+  /* V5E "El Segundo" Instructions.  */
+  {ARM_EXT_V5E, 0x000000d0, 0x0e1000f0, "ldrd%c\t%12-15r, %s"},
+  {ARM_EXT_V5E, 0x000000f0, 0x0e1000f0, "strd%c\t%12-15r, %s"},
+  {ARM_EXT_V5E, 0xf450f000, 0xfc70f000, "pld\t%a"},
+  {ARM_EXT_V5ExP, 0x01000080, 0x0ff000f0, "smlabb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V5ExP, 0x010000a0, 0x0ff000f0, "smlatb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V5ExP, 0x010000c0, 0x0ff000f0, "smlabt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V5ExP, 0x010000e0, 0x0ff000f0, "smlatt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+
+  {ARM_EXT_V5ExP, 0x01200080, 0x0ff000f0, "smlawb%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+  {ARM_EXT_V5ExP, 0x012000c0, 0x0ff000f0, "smlawt%c\t%16-19r, %0-3r, %8-11r, %12-15r"},
+
+  {ARM_EXT_V5ExP, 0x01400080, 0x0ff000f0, "smlalbb%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x014000a0, 0x0ff000f0, "smlaltb%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x014000c0, 0x0ff000f0, "smlalbt%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x014000e0, 0x0ff000f0, "smlaltt%c\t%12-15r, %16-19r, %0-3r, %8-11r"},
+
+  {ARM_EXT_V5ExP, 0x01600080, 0x0ff0f0f0, "smulbb%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x016000a0, 0x0ff0f0f0, "smultb%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x016000c0, 0x0ff0f0f0, "smulbt%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x016000e0, 0x0ff0f0f0, "smultt%c\t%16-19r, %0-3r, %8-11r"},
+
+  {ARM_EXT_V5ExP, 0x012000a0, 0x0ff0f0f0, "smulwb%c\t%16-19r, %0-3r, %8-11r"},
+  {ARM_EXT_V5ExP, 0x012000e0, 0x0ff0f0f0, "smulwt%c\t%16-19r, %0-3r, %8-11r"},
+
+  {ARM_EXT_V5ExP, 0x01000050, 0x0ff00ff0,  "qadd%c\t%12-15r, %0-3r, %16-19r"},
+  {ARM_EXT_V5ExP, 0x01400050, 0x0ff00ff0, "qdadd%c\t%12-15r, %0-3r, %16-19r"},
+  {ARM_EXT_V5ExP, 0x01200050, 0x0ff00ff0,  "qsub%c\t%12-15r, %0-3r, %16-19r"},
+  {ARM_EXT_V5ExP, 0x01600050, 0x0ff00ff0, "qdsub%c\t%12-15r, %0-3r, %16-19r"},
+
+  /* ARM Instructions.  */
+  {ARM_EXT_V1, 0x00000090, 0x0e100090, "str%6's%5?hb%c\t%12-15r, %s"},
+  {ARM_EXT_V1, 0x00100090, 0x0e100090, "ldr%6's%5?hb%c\t%12-15r, %s"},
+  {ARM_EXT_V1, 0x00000000, 0x0de00000, "and%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00200000, 0x0de00000, "eor%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00400000, 0x0de00000, "sub%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00600000, 0x0de00000, "rsb%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00800000, 0x0de00000, "add%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00a00000, 0x0de00000, "adc%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00c00000, 0x0de00000, "sbc%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x00e00000, 0x0de00000, "rsc%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V3, 0x0120f000, 0x0db0f000, "msr%c\t%22?SCPSR%C, %o"},
+  {ARM_EXT_V3, 0x010f0000, 0x0fbf0fff, "mrs%c\t%12-15r, %22?SCPSR"},
+  {ARM_EXT_V1, 0x01000000, 0x0de00000, "tst%p%c\t%16-19r, %o"},
+  {ARM_EXT_V1, 0x01200000, 0x0de00000, "teq%p%c\t%16-19r, %o"},
+  {ARM_EXT_V1, 0x01400000, 0x0de00000, "cmp%p%c\t%16-19r, %o"},
+  {ARM_EXT_V1, 0x01600000, 0x0de00000, "cmn%p%c\t%16-19r, %o"},
+  {ARM_EXT_V1, 0x01800000, 0x0de00000, "orr%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x03a00000, 0x0fef0000, "mov%20's%c\t%12-15r, %o"},
+  {ARM_EXT_V1, 0x01a00000, 0x0def0ff0, "mov%20's%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V1, 0x01a00000, 0x0def0060, "lsl%20's%c\t%12-15r, %q"},
+  {ARM_EXT_V1, 0x01a00020, 0x0def0060, "lsr%20's%c\t%12-15r, %q"},
+  {ARM_EXT_V1, 0x01a00040, 0x0def0060, "asr%20's%c\t%12-15r, %q"},
+  {ARM_EXT_V1, 0x01a00060, 0x0def0ff0, "rrx%20's%c\t%12-15r, %0-3r"},
+  {ARM_EXT_V1, 0x01a00060, 0x0def0060, "ror%20's%c\t%12-15r, %q"},
+  {ARM_EXT_V1, 0x01c00000, 0x0de00000, "bic%20's%c\t%12-15r, %16-19r, %o"},
+  {ARM_EXT_V1, 0x01e00000, 0x0de00000, "mvn%20's%c\t%12-15r, %o"},
+  {ARM_EXT_V1, 0x052d0004, 0x0fff0fff, "push%c\t{%12-15r}\t\t; (str%c %12-15r, %a)"},
+  {ARM_EXT_V1, 0x04000000, 0x0e100000, "str%22'b%t%c\t%12-15r, %a"},
+  {ARM_EXT_V1, 0x06000000, 0x0e100ff0, "str%22'b%t%c\t%12-15r, %a"},
+  {ARM_EXT_V1, 0x04000000, 0x0c100010, "str%22'b%t%c\t%12-15r, %a"},
+  {ARM_EXT_V1, 0x06000010, 0x0e000010, "undefined"},
+  {ARM_EXT_V1, 0x049d0004, 0x0fff0fff, "pop%c\t{%12-15r}\t\t; (ldr%c %12-15r, %a)"},
+  {ARM_EXT_V1, 0x04100000, 0x0c100000, "ldr%22'b%t%c\t%12-15r, %a"},
+  {ARM_EXT_V1, 0x092d0000, 0x0fff0000, "push%c\t%m"},
+  {ARM_EXT_V1, 0x08800000, 0x0ff00000, "stm%c\t%16-19r%21'!, %m%22'^"},
+  {ARM_EXT_V1, 0x08000000, 0x0e100000, "stm%23?id%24?ba%c\t%16-19r%21'!, %m%22'^"},
+  {ARM_EXT_V1, 0x08bd0000, 0x0fff0000, "pop%c\t%m"},
+  {ARM_EXT_V1, 0x08900000, 0x0f900000, "ldm%c\t%16-19r%21'!, %m%22'^"},
+  {ARM_EXT_V1, 0x08100000, 0x0e100000, "ldm%23?id%24?ba%c\t%16-19r%21'!, %m%22'^"},
+  {ARM_EXT_V1, 0x0a000000, 0x0e000000, "b%24'l%c\t%b"},
+  {ARM_EXT_V1, 0x0f000000, 0x0f000000, "svc%c\t%0-23x"},
+
+  /* The rest.  */
+  {ARM_EXT_V1, 0x00000000, 0x00000000, "undefined instruction %0-31x"},
+  {0, 0x00000000, 0x00000000, 0}
+};
+
+/* print_insn_thumb16 recognizes the following format control codes:
+
+   %S                   print Thumb register (bits 3..5 as high number if bit 6 set)
+   %D                   print Thumb register (bits 0..2 as high number if bit 7 set)
+   %<bitfield>I         print bitfield as a signed decimal
+   				(top bit of range being the sign bit)
+   %N                   print Thumb register mask (with LR)
+   %O                   print Thumb register mask (with PC)
+   %M                   print Thumb register mask
+   %b			print CZB's 6-bit unsigned branch destination
+   %s			print Thumb right-shift immediate (6..10; 0 == 32).
+   %c			print the condition code
+   %C			print the condition code, or "s" if not conditional
+   %x			print warning if conditional an not at end of IT block"
+   %X			print "\t; unpredictable <IT:code>" if conditional
+   %I			print IT instruction suffix and operands
+   %<bitfield>r		print bitfield as an ARM register
+   %<bitfield>d		print bitfield as a decimal
+   %<bitfield>H         print (bitfield * 2) as a decimal
+   %<bitfield>W         print (bitfield * 4) as a decimal
+   %<bitfield>a         print (bitfield * 4) as a pc-rel offset + decoded symbol
+   %<bitfield>B         print Thumb branch destination (signed displacement)
+   %<bitfield>c         print bitfield as a condition code
+   %<bitnum>'c		print specified char iff bit is one
+   %<bitnum>?ab		print a if bit is one else print b.  */
+
+static const struct opcode16 thumb_opcodes[] =
+{
+  /* Thumb instructions.  */
+
+  /* ARM V6K no-argument instructions.  */
+  {ARM_EXT_V6K, 0xbf00, 0xffff, "nop%c"},
+  {ARM_EXT_V6K, 0xbf10, 0xffff, "yield%c"},
+  {ARM_EXT_V6K, 0xbf20, 0xffff, "wfe%c"},
+  {ARM_EXT_V6K, 0xbf30, 0xffff, "wfi%c"},
+  {ARM_EXT_V6K, 0xbf40, 0xffff, "sev%c"},
+  {ARM_EXT_V6K, 0xbf00, 0xff0f, "nop%c\t{%4-7d}"},
+
+  /* ARM V6T2 instructions.  */
+  {ARM_EXT_V6T2, 0xb900, 0xfd00, "cbnz\t%0-2r, %b%X"},
+  {ARM_EXT_V6T2, 0xb100, 0xfd00, "cbz\t%0-2r, %b%X"},
+  {ARM_EXT_V6T2, 0xbf00, 0xff00, "it%I%X"},
+
+  /* ARM V6.  */
+  {ARM_EXT_V6, 0xb660, 0xfff8, "cpsie\t%2'a%1'i%0'f%X"},
+  {ARM_EXT_V6, 0xb670, 0xfff8, "cpsid\t%2'a%1'i%0'f%X"},
+  {ARM_EXT_V6, 0x4600, 0xffc0, "mov%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xba00, 0xffc0, "rev%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xba40, 0xffc0, "rev16%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xbac0, 0xffc0, "revsh%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xb650, 0xfff7, "setend\t%3?ble%X"},
+  {ARM_EXT_V6, 0xb200, 0xffc0, "sxth%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xb240, 0xffc0, "sxtb%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xb280, 0xffc0, "uxth%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V6, 0xb2c0, 0xffc0, "uxtb%c\t%0-2r, %3-5r"},
+
+  /* ARM V5 ISA extends Thumb.  */
+  {ARM_EXT_V5T, 0xbe00, 0xff00, "bkpt\t%0-7x"}, /* Is always unconditional.  */
+  /* This is BLX(2).  BLX(1) is a 32-bit instruction.  */
+  {ARM_EXT_V5T, 0x4780, 0xff87, "blx%c\t%3-6r%x"},	/* note: 4 bit register number.  */
+  /* ARM V4T ISA (Thumb v1).  */
+  {ARM_EXT_V4T, 0x46C0, 0xFFFF, "nop%c\t\t\t(mov r8, r8)"},
+  /* Format 4.  */
+  {ARM_EXT_V4T, 0x4000, 0xFFC0, "and%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4040, 0xFFC0, "eor%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4080, 0xFFC0, "lsl%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x40C0, 0xFFC0, "lsr%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4100, 0xFFC0, "asr%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4140, 0xFFC0, "adc%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4180, 0xFFC0, "sbc%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x41C0, 0xFFC0, "ror%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4200, 0xFFC0, "tst%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4240, 0xFFC0, "neg%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4280, 0xFFC0, "cmp%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x42C0, 0xFFC0, "cmn%c\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4300, 0xFFC0, "orr%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4340, 0xFFC0, "mul%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x4380, 0xFFC0, "bic%C\t%0-2r, %3-5r"},
+  {ARM_EXT_V4T, 0x43C0, 0xFFC0, "mvn%C\t%0-2r, %3-5r"},
+  /* format 13 */
+  {ARM_EXT_V4T, 0xB000, 0xFF80, "add%c\tsp, #%0-6W"},
+  {ARM_EXT_V4T, 0xB080, 0xFF80, "sub%c\tsp, #%0-6W"},
+  /* format 5 */
+  {ARM_EXT_V4T, 0x4700, 0xFF80, "bx%c\t%S%x"},
+  {ARM_EXT_V4T, 0x4400, 0xFF00, "add%c\t%D, %S"},
+  {ARM_EXT_V4T, 0x4500, 0xFF00, "cmp%c\t%D, %S"},
+  {ARM_EXT_V4T, 0x4600, 0xFF00, "mov%c\t%D, %S"},
+  /* format 14 */
+  {ARM_EXT_V4T, 0xB400, 0xFE00, "push%c\t%N"},
+  {ARM_EXT_V4T, 0xBC00, 0xFE00, "pop%c\t%O"},
+  /* format 2 */
+  {ARM_EXT_V4T, 0x1800, 0xFE00, "add%C\t%0-2r, %3-5r, %6-8r"},
+  {ARM_EXT_V4T, 0x1A00, 0xFE00, "sub%C\t%0-2r, %3-5r, %6-8r"},
+  {ARM_EXT_V4T, 0x1C00, 0xFE00, "add%C\t%0-2r, %3-5r, #%6-8d"},
+  {ARM_EXT_V4T, 0x1E00, 0xFE00, "sub%C\t%0-2r, %3-5r, #%6-8d"},
+  /* format 8 */
+  {ARM_EXT_V4T, 0x5200, 0xFE00, "strh%c\t%0-2r, [%3-5r, %6-8r]"},
+  {ARM_EXT_V4T, 0x5A00, 0xFE00, "ldrh%c\t%0-2r, [%3-5r, %6-8r]"},
+  {ARM_EXT_V4T, 0x5600, 0xF600, "ldrs%11?hb%c\t%0-2r, [%3-5r, %6-8r]"},
+  /* format 7 */
+  {ARM_EXT_V4T, 0x5000, 0xFA00, "str%10'b%c\t%0-2r, [%3-5r, %6-8r]"},
+  {ARM_EXT_V4T, 0x5800, 0xFA00, "ldr%10'b%c\t%0-2r, [%3-5r, %6-8r]"},
+  /* format 1 */
+  {ARM_EXT_V4T, 0x0000, 0xF800, "lsl%C\t%0-2r, %3-5r, #%6-10d"},
+  {ARM_EXT_V4T, 0x0800, 0xF800, "lsr%C\t%0-2r, %3-5r, %s"},
+  {ARM_EXT_V4T, 0x1000, 0xF800, "asr%C\t%0-2r, %3-5r, %s"},
+  /* format 3 */
+  {ARM_EXT_V4T, 0x2000, 0xF800, "mov%C\t%8-10r, #%0-7d"},
+  {ARM_EXT_V4T, 0x2800, 0xF800, "cmp%c\t%8-10r, #%0-7d"},
+  {ARM_EXT_V4T, 0x3000, 0xF800, "add%C\t%8-10r, #%0-7d"},
+  {ARM_EXT_V4T, 0x3800, 0xF800, "sub%C\t%8-10r, #%0-7d"},
+  /* format 6 */
+  {ARM_EXT_V4T, 0x4800, 0xF800, "ldr%c\t%8-10r, [pc, #%0-7W]\t(%0-7a)"},  /* TODO: Disassemble PC relative "LDR rD,=<symbolic>" */
+  /* format 9 */
+  {ARM_EXT_V4T, 0x6000, 0xF800, "str%c\t%0-2r, [%3-5r, #%6-10W]"},
+  {ARM_EXT_V4T, 0x6800, 0xF800, "ldr%c\t%0-2r, [%3-5r, #%6-10W]"},
+  {ARM_EXT_V4T, 0x7000, 0xF800, "strb%c\t%0-2r, [%3-5r, #%6-10d]"},
+  {ARM_EXT_V4T, 0x7800, 0xF800, "ldrb%c\t%0-2r, [%3-5r, #%6-10d]"},
+  /* format 10 */
+  {ARM_EXT_V4T, 0x8000, 0xF800, "strh%c\t%0-2r, [%3-5r, #%6-10H]"},
+  {ARM_EXT_V4T, 0x8800, 0xF800, "ldrh%c\t%0-2r, [%3-5r, #%6-10H]"},
+  /* format 11 */
+  {ARM_EXT_V4T, 0x9000, 0xF800, "str%c\t%8-10r, [sp, #%0-7W]"},
+  {ARM_EXT_V4T, 0x9800, 0xF800, "ldr%c\t%8-10r, [sp, #%0-7W]"},
+  /* format 12 */
+  {ARM_EXT_V4T, 0xA000, 0xF800, "add%c\t%8-10r, pc, #%0-7W\t(adr %8-10r, %0-7a)"},
+  {ARM_EXT_V4T, 0xA800, 0xF800, "add%c\t%8-10r, sp, #%0-7W"},
+  /* format 15 */
+  {ARM_EXT_V4T, 0xC000, 0xF800, "stmia%c\t%8-10r!, %M"},
+  {ARM_EXT_V4T, 0xC800, 0xF800, "ldmia%c\t%8-10r!, %M"},
+  /* format 17 */
+  {ARM_EXT_V4T, 0xDF00, 0xFF00, "svc%c\t%0-7d"},
+  /* format 16 */
+  {ARM_EXT_V4T, 0xDE00, 0xFE00, "undefined"},
+  {ARM_EXT_V4T, 0xD000, 0xF000, "b%8-11c.n\t%0-7B%X"},
+  /* format 18 */
+  {ARM_EXT_V4T, 0xE000, 0xF800, "b%c.n\t%0-10B%x"},
+
+  /* The E800 .. FFFF range is unconditionally redirected to the
+     32-bit table, because even in pre-V6T2 ISAs, BL and BLX(1) pairs
+     are processed via that table.  Thus, we can never encounter a
+     bare "second half of BL/BLX(1)" instruction here.  */
+  {ARM_EXT_V1,  0x0000, 0x0000, "undefined"},
+  {0, 0, 0, 0}
+};
+
+/* Thumb32 opcodes use the same table structure as the ARM opcodes.
+   We adopt the convention that hw1 is the high 16 bits of .value and
+   .mask, hw2 the low 16 bits.
+
+   print_insn_thumb32 recognizes the following format control codes:
+
+       %%		%
+
+       %I		print a 12-bit immediate from hw1[10],hw2[14:12,7:0]
+       %M		print a modified 12-bit immediate (same location)
+       %J		print a 16-bit immediate from hw1[3:0,10],hw2[14:12,7:0]
+       %K		print a 16-bit immediate from hw2[3:0],hw1[3:0],hw2[11:4]
+       %S		print a possibly-shifted Rm
+
+       %a		print the address of a plain load/store
+       %w		print the width and signedness of a core load/store
+       %m		print register mask for ldm/stm
+
+       %E		print the lsb and width fields of a bfc/bfi instruction
+       %F		print the lsb and width fields of a sbfx/ubfx instruction
+       %b		print a conditional branch offset
+       %B		print an unconditional branch offset
+       %s		print the shift field of an SSAT instruction
+       %R		print the rotation field of an SXT instruction
+       %U		print barrier type.
+       %P		print address for pli instruction.
+       %c		print the condition code
+       %x		print warning if conditional an not at end of IT block"
+       %X		print "\t; unpredictable <IT:code>" if conditional
+
+       %<bitfield>d	print bitfield in decimal
+       %<bitfield>W	print bitfield*4 in decimal
+       %<bitfield>r	print bitfield as an ARM register
+       %<bitfield>c	print bitfield as a condition code
+
+       %<bitfield>'c	print specified char iff bitfield is all ones
+       %<bitfield>`c	print specified char iff bitfield is all zeroes
+       %<bitfield>?ab... select from array of values in big endian order
+
+   With one exception at the bottom (done because BL and BLX(1) need
+   to come dead last), this table was machine-sorted first in
+   decreasing order of number of bits set in the mask, then in
+   increasing numeric order of mask, then in increasing numeric order
+   of opcode.  This order is not the clearest for a human reader, but
+   is guaranteed never to catch a special-case bit pattern with a more
+   general mask, which is important, because this instruction encoding
+   makes heavy use of special-case bit patterns.  */
+static const struct opcode32 thumb32_opcodes[] =
+{
+  /* V7 instructions.  */
+  {ARM_EXT_V7, 0xf910f000, 0xff70f000, "pli%c\t%a"},
+  {ARM_EXT_V7, 0xf3af80f0, 0xfffffff0, "dbg%c\t#%0-3d"},
+  {ARM_EXT_V7, 0xf3bf8f50, 0xfffffff0, "dmb%c\t%U"},
+  {ARM_EXT_V7, 0xf3bf8f40, 0xfffffff0, "dsb%c\t%U"},
+  {ARM_EXT_V7, 0xf3bf8f60, 0xfffffff0, "isb%c\t%U"},
+  {ARM_EXT_DIV, 0xfb90f0f0, 0xfff0f0f0, "sdiv%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_DIV, 0xfbb0f0f0, 0xfff0f0f0, "udiv%c\t%8-11r, %16-19r, %0-3r"},
+
+  /* Instructions defined in the basic V6T2 set.  */
+  {ARM_EXT_V6T2, 0xf3af8000, 0xffffffff, "nop%c.w"},
+  {ARM_EXT_V6T2, 0xf3af8001, 0xffffffff, "yield%c.w"},
+  {ARM_EXT_V6T2, 0xf3af8002, 0xffffffff, "wfe%c.w"},
+  {ARM_EXT_V6T2, 0xf3af8003, 0xffffffff, "wfi%c.w"},
+  {ARM_EXT_V6T2, 0xf3af9004, 0xffffffff, "sev%c.w"},
+  {ARM_EXT_V6T2, 0xf3af8000, 0xffffff00, "nop%c.w\t{%0-7d}"},
+
+  {ARM_EXT_V6T2, 0xf3bf8f2f, 0xffffffff, "clrex%c"},
+  {ARM_EXT_V6T2, 0xf3af8400, 0xffffff1f, "cpsie.w\t%7'a%6'i%5'f%X"},
+  {ARM_EXT_V6T2, 0xf3af8600, 0xffffff1f, "cpsid.w\t%7'a%6'i%5'f%X"},
+  {ARM_EXT_V6T2, 0xf3c08f00, 0xfff0ffff, "bxj%c\t%16-19r%x"},
+  {ARM_EXT_V6T2, 0xe810c000, 0xffd0ffff, "rfedb%c\t%16-19r%21'!"},
+  {ARM_EXT_V6T2, 0xe990c000, 0xffd0ffff, "rfeia%c\t%16-19r%21'!"},
+  {ARM_EXT_V6T2, 0xf3ef8000, 0xffeff000, "mrs%c\t%8-11r, %D"},
+  {ARM_EXT_V6T2, 0xf3af8100, 0xffffffe0, "cps\t#%0-4d%X"},
+  {ARM_EXT_V6T2, 0xe8d0f000, 0xfff0fff0, "tbb%c\t[%16-19r, %0-3r]%x"},
+  {ARM_EXT_V6T2, 0xe8d0f010, 0xfff0fff0, "tbh%c\t[%16-19r, %0-3r, lsl #1]%x"},
+  {ARM_EXT_V6T2, 0xf3af8500, 0xffffff00, "cpsie\t%7'a%6'i%5'f, #%0-4d%X"},
+  {ARM_EXT_V6T2, 0xf3af8700, 0xffffff00, "cpsid\t%7'a%6'i%5'f, #%0-4d%X"},
+  {ARM_EXT_V6T2, 0xf3de8f00, 0xffffff00, "subs%c\tpc, lr, #%0-7d"},
+  {ARM_EXT_V6T2, 0xf3808000, 0xffe0f000, "msr%c\t%C, %16-19r"},
+  {ARM_EXT_V6T2, 0xe8500f00, 0xfff00fff, "ldrex%c\t%12-15r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xe8d00f4f, 0xfff00fef, "ldrex%4?hb%c\t%12-15r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xe800c000, 0xffd0ffe0, "srsdb%c\t%16-19r%21'!, #%0-4d"},
+  {ARM_EXT_V6T2, 0xe980c000, 0xffd0ffe0, "srsia%c\t%16-19r%21'!, #%0-4d"},
+  {ARM_EXT_V6T2, 0xfa0ff080, 0xfffff0c0, "sxth%c.w\t%8-11r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa1ff080, 0xfffff0c0, "uxth%c.w\t%8-11r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa2ff080, 0xfffff0c0, "sxtb16%c\t%8-11r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa3ff080, 0xfffff0c0, "uxtb16%c\t%8-11r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa4ff080, 0xfffff0c0, "sxtb%c.w\t%8-11r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa5ff080, 0xfffff0c0, "uxtb%c.w\t%8-11r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xe8400000, 0xfff000ff, "strex%c\t%8-11r, %12-15r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xe8d0007f, 0xfff000ff, "ldrexd%c\t%12-15r, %8-11r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xfa80f000, 0xfff0f0f0, "sadd8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa80f010, 0xfff0f0f0, "qadd8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa80f020, 0xfff0f0f0, "shadd8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa80f040, 0xfff0f0f0, "uadd8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa80f050, 0xfff0f0f0, "uqadd8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa80f060, 0xfff0f0f0, "uhadd8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa80f080, 0xfff0f0f0, "qadd%c\t%8-11r, %0-3r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa80f090, 0xfff0f0f0, "qdadd%c\t%8-11r, %0-3r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa80f0a0, 0xfff0f0f0, "qsub%c\t%8-11r, %0-3r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa80f0b0, 0xfff0f0f0, "qdsub%c\t%8-11r, %0-3r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa90f000, 0xfff0f0f0, "sadd16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa90f010, 0xfff0f0f0, "qadd16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa90f020, 0xfff0f0f0, "shadd16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa90f040, 0xfff0f0f0, "uadd16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa90f050, 0xfff0f0f0, "uqadd16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa90f060, 0xfff0f0f0, "uhadd16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa90f080, 0xfff0f0f0, "rev%c.w\t%8-11r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa90f090, 0xfff0f0f0, "rev16%c.w\t%8-11r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa90f0a0, 0xfff0f0f0, "rbit%c\t%8-11r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfa90f0b0, 0xfff0f0f0, "revsh%c.w\t%8-11r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfaa0f000, 0xfff0f0f0, "saddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f010, 0xfff0f0f0, "qaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f020, 0xfff0f0f0, "shaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f040, 0xfff0f0f0, "uaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f050, 0xfff0f0f0, "uqaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f060, 0xfff0f0f0, "uhaddsubx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfaa0f080, 0xfff0f0f0, "sel%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfab0f080, 0xfff0f0f0, "clz%c\t%8-11r, %16-19r"},
+  {ARM_EXT_V6T2, 0xfac0f000, 0xfff0f0f0, "ssub8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfac0f010, 0xfff0f0f0, "qsub8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfac0f020, 0xfff0f0f0, "shsub8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfac0f040, 0xfff0f0f0, "usub8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfac0f050, 0xfff0f0f0, "uqsub8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfac0f060, 0xfff0f0f0, "uhsub8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfad0f000, 0xfff0f0f0, "ssub16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfad0f010, 0xfff0f0f0, "qsub16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfad0f020, 0xfff0f0f0, "shsub16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfad0f040, 0xfff0f0f0, "usub16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfad0f050, 0xfff0f0f0, "uqsub16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfad0f060, 0xfff0f0f0, "uhsub16%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f000, 0xfff0f0f0, "ssubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f010, 0xfff0f0f0, "qsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f020, 0xfff0f0f0, "shsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f040, 0xfff0f0f0, "usubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f050, 0xfff0f0f0, "uqsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfae0f060, 0xfff0f0f0, "uhsubaddx%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfb00f000, 0xfff0f0f0, "mul%c.w\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfb70f000, 0xfff0f0f0, "usad8%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa00f000, 0xffe0f0f0, "lsl%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa20f000, 0xffe0f0f0, "lsr%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa40f000, 0xffe0f0f0, "asr%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa60f000, 0xffe0f0f0, "ror%20's%c.w\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xe8c00f40, 0xfff00fe0, "strex%4?hb%c\t%0-3r, %12-15r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xf3200000, 0xfff0f0e0, "ssat16%c\t%8-11r, #%0-4d, %16-19r"},
+  {ARM_EXT_V6T2, 0xf3a00000, 0xfff0f0e0, "usat16%c\t%8-11r, #%0-4d, %16-19r"},
+  {ARM_EXT_V6T2, 0xfb20f000, 0xfff0f0e0, "smuad%4'x%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfb30f000, 0xfff0f0e0, "smulw%4?tb%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfb40f000, 0xfff0f0e0, "smusd%4'x%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfb50f000, 0xfff0f0e0, "smmul%4'r%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfa00f080, 0xfff0f0c0, "sxtah%c\t%8-11r, %16-19r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa10f080, 0xfff0f0c0, "uxtah%c\t%8-11r, %16-19r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa20f080, 0xfff0f0c0, "sxtab16%c\t%8-11r, %16-19r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa30f080, 0xfff0f0c0, "uxtab16%c\t%8-11r, %16-19r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa40f080, 0xfff0f0c0, "sxtab%c\t%8-11r, %16-19r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfa50f080, 0xfff0f0c0, "uxtab%c\t%8-11r, %16-19r, %0-3r%R"},
+  {ARM_EXT_V6T2, 0xfb10f000, 0xfff0f0c0, "smul%5?tb%4?tb%c\t%8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xf36f0000, 0xffff8020, "bfc%c\t%8-11r, %E"},
+  {ARM_EXT_V6T2, 0xea100f00, 0xfff08f00, "tst%c.w\t%16-19r, %S"},
+  {ARM_EXT_V6T2, 0xea900f00, 0xfff08f00, "teq%c\t%16-19r, %S"},
+  {ARM_EXT_V6T2, 0xeb100f00, 0xfff08f00, "cmn%c.w\t%16-19r, %S"},
+  {ARM_EXT_V6T2, 0xebb00f00, 0xfff08f00, "cmp%c.w\t%16-19r, %S"},
+  {ARM_EXT_V6T2, 0xf0100f00, 0xfbf08f00, "tst%c.w\t%16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf0900f00, 0xfbf08f00, "teq%c\t%16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1100f00, 0xfbf08f00, "cmn%c.w\t%16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1b00f00, 0xfbf08f00, "cmp%c.w\t%16-19r, %M"},
+  {ARM_EXT_V6T2, 0xea4f0000, 0xffef8000, "mov%20's%c.w\t%8-11r, %S"},
+  {ARM_EXT_V6T2, 0xea6f0000, 0xffef8000, "mvn%20's%c.w\t%8-11r, %S"},
+  {ARM_EXT_V6T2, 0xe8c00070, 0xfff000f0, "strexd%c\t%0-3r, %12-15r, %8-11r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xfb000000, 0xfff000f0, "mla%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb000010, 0xfff000f0, "mls%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb700000, 0xfff000f0, "usada8%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb800000, 0xfff000f0, "smull%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfba00000, 0xfff000f0, "umull%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfbc00000, 0xfff000f0, "smlal%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfbe00000, 0xfff000f0, "umlal%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfbe00060, 0xfff000f0, "umaal%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xe8500f00, 0xfff00f00, "ldrex%c\t%12-15r, [%16-19r, #%0-7W]"},
+  {ARM_EXT_V6T2, 0xf7f08000, 0xfff0f000, "smc%c\t%K"},
+  {ARM_EXT_V6T2, 0xf04f0000, 0xfbef8000, "mov%20's%c.w\t%8-11r, %M"},
+  {ARM_EXT_V6T2, 0xf06f0000, 0xfbef8000, "mvn%20's%c.w\t%8-11r, %M"},
+  {ARM_EXT_V6T2, 0xf810f000, 0xff70f000, "pld%c\t%a"},
+  {ARM_EXT_V6T2, 0xfb200000, 0xfff000e0, "smlad%4'x%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb300000, 0xfff000e0, "smlaw%4?tb%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb400000, 0xfff000e0, "smlsd%4'x%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb500000, 0xfff000e0, "smmla%4'r%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfb600000, 0xfff000e0, "smmls%4'r%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfbc000c0, 0xfff000e0, "smlald%4'x%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xfbd000c0, 0xfff000e0, "smlsld%4'x%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xeac00000, 0xfff08030, "pkhbt%c\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xeac00020, 0xfff08030, "pkhtb%c\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xf3400000, 0xfff08020, "sbfx%c\t%8-11r, %16-19r, %F"},
+  {ARM_EXT_V6T2, 0xf3c00000, 0xfff08020, "ubfx%c\t%8-11r, %16-19r, %F"},
+  {ARM_EXT_V6T2, 0xf8000e00, 0xff900f00, "str%wt%c\t%12-15r, %a"},
+  {ARM_EXT_V6T2, 0xfb100000, 0xfff000c0, "smla%5?tb%4?tb%c\t%8-11r, %16-19r, %0-3r, %12-15r"},
+  {ARM_EXT_V6T2, 0xfbc00080, 0xfff000c0, "smlal%5?tb%4?tb%c\t%12-15r, %8-11r, %16-19r, %0-3r"},
+  {ARM_EXT_V6T2, 0xf3600000, 0xfff08020, "bfi%c\t%8-11r, %16-19r, %E"},
+  {ARM_EXT_V6T2, 0xf8100e00, 0xfe900f00, "ldr%wt%c\t%12-15r, %a"},
+  {ARM_EXT_V6T2, 0xf3000000, 0xffd08020, "ssat%c\t%8-11r, #%0-4d, %16-19r%s"},
+  {ARM_EXT_V6T2, 0xf3800000, 0xffd08020, "usat%c\t%8-11r, #%0-4d, %16-19r%s"},
+  {ARM_EXT_V6T2, 0xf2000000, 0xfbf08000, "addw%c\t%8-11r, %16-19r, %I"},
+  {ARM_EXT_V6T2, 0xf2400000, 0xfbf08000, "movw%c\t%8-11r, %J"},
+  {ARM_EXT_V6T2, 0xf2a00000, 0xfbf08000, "subw%c\t%8-11r, %16-19r, %I"},
+  {ARM_EXT_V6T2, 0xf2c00000, 0xfbf08000, "movt%c\t%8-11r, %J"},
+  {ARM_EXT_V6T2, 0xea000000, 0xffe08000, "and%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xea200000, 0xffe08000, "bic%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xea400000, 0xffe08000, "orr%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xea600000, 0xffe08000, "orn%20's%c\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xea800000, 0xffe08000, "eor%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xeb000000, 0xffe08000, "add%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xeb400000, 0xffe08000, "adc%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xeb600000, 0xffe08000, "sbc%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xeba00000, 0xffe08000, "sub%20's%c.w\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xebc00000, 0xffe08000, "rsb%20's%c\t%8-11r, %16-19r, %S"},
+  {ARM_EXT_V6T2, 0xe8400000, 0xfff00000, "strex%c\t%8-11r, %12-15r, [%16-19r, #%0-7W]"},
+  {ARM_EXT_V6T2, 0xf0000000, 0xfbe08000, "and%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf0200000, 0xfbe08000, "bic%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf0400000, 0xfbe08000, "orr%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf0600000, 0xfbe08000, "orn%20's%c\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf0800000, 0xfbe08000, "eor%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1000000, 0xfbe08000, "add%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1400000, 0xfbe08000, "adc%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1600000, 0xfbe08000, "sbc%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1a00000, 0xfbe08000, "sub%20's%c.w\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xf1c00000, 0xfbe08000, "rsb%20's%c\t%8-11r, %16-19r, %M"},
+  {ARM_EXT_V6T2, 0xe8800000, 0xffd00000, "stmia%c.w\t%16-19r%21'!, %m"},
+  {ARM_EXT_V6T2, 0xe8900000, 0xffd00000, "ldmia%c.w\t%16-19r%21'!, %m"},
+  {ARM_EXT_V6T2, 0xe9000000, 0xffd00000, "stmdb%c\t%16-19r%21'!, %m"},
+  {ARM_EXT_V6T2, 0xe9100000, 0xffd00000, "ldmdb%c\t%16-19r%21'!, %m"},
+  {ARM_EXT_V6T2, 0xe9c00000, 0xffd000ff, "strd%c\t%12-15r, %8-11r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xe9d00000, 0xffd000ff, "ldrd%c\t%12-15r, %8-11r, [%16-19r]"},
+  {ARM_EXT_V6T2, 0xe9400000, 0xff500000, "strd%c\t%12-15r, %8-11r, [%16-19r, #%23`-%0-7W]%21'!"},
+  {ARM_EXT_V6T2, 0xe9500000, 0xff500000, "ldrd%c\t%12-15r, %8-11r, [%16-19r, #%23`-%0-7W]%21'!"},
+  {ARM_EXT_V6T2, 0xe8600000, 0xff700000, "strd%c\t%12-15r, %8-11r, [%16-19r], #%23`-%0-7W"},
+  {ARM_EXT_V6T2, 0xe8700000, 0xff700000, "ldrd%c\t%12-15r, %8-11r, [%16-19r], #%23`-%0-7W"},
+  {ARM_EXT_V6T2, 0xf8000000, 0xff100000, "str%w%c.w\t%12-15r, %a"},
+  {ARM_EXT_V6T2, 0xf8100000, 0xfe100000, "ldr%w%c.w\t%12-15r, %a"},
+
+  /* Filter out Bcc with cond=E or F, which are used for other instructions.  */
+  {ARM_EXT_V6T2, 0xf3c08000, 0xfbc0d000, "undefined (bcc, cond=0xF)"},
+  {ARM_EXT_V6T2, 0xf3808000, 0xfbc0d000, "undefined (bcc, cond=0xE)"},
+  {ARM_EXT_V6T2, 0xf0008000, 0xf800d000, "b%22-25c.w\t%b%X"},
+  {ARM_EXT_V6T2, 0xf0009000, 0xf800d000, "b%c.w\t%B%x"},
+
+  /* These have been 32-bit since the invention of Thumb.  */
+  {ARM_EXT_V4T,  0xf000c000, 0xf800d000, "blx%c\t%B%x"},
+  {ARM_EXT_V4T,  0xf000d000, 0xf800d000, "bl%c\t%B%x"},
+
+  /* Fallback.  */
+  {ARM_EXT_V1,   0x00000000, 0x00000000, "undefined"},
+  {0, 0, 0, 0}
+};
+
+static const char *const arm_conditional[] =
+{"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt", "gt", "le", "al", "<und>", ""};
+
+static const char *const arm_fp_const[] =
+{"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0"};
+
+static const char *const arm_shift[] =
+{"lsl", "lsr", "asr", "ror"};
+
+typedef struct
+{
+  const char *name;
+  const char *description;
+  const char *reg_names[16];
+}
+arm_regname;
+
+static const arm_regname regnames[] =
+{
+  { "raw" , "Select raw register names",
+    { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}},
+  { "gcc",  "Select register names used by GCC",
+    { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl",  "fp",  "ip",  "sp",  "lr",  "pc" }},
+  { "std",  "Select register names used in ARM's ISA documentation",
+    { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp",  "lr",  "pc" }},
+  { "apcs", "Select register names used in the APCS",
+    { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "sl",  "fp",  "ip",  "sp",  "lr",  "pc" }},
+  { "atpcs", "Select register names used in the ATPCS",
+    { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7",  "v8",  "IP",  "SP",  "LR",  "PC" }},
+  { "special-atpcs", "Select special register names used in the ATPCS",
+    { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL",  "FP",  "IP",  "SP",  "LR",  "PC" }},
+};
+
+static const char *const iwmmxt_wwnames[] =
+{"b", "h", "w", "d"};
+
+static const char *const iwmmxt_wwssnames[] =
+{"b", "bus", "bc", "bss",
+ "h", "hus", "hc", "hss",
+ "w", "wus", "wc", "wss",
+ "d", "dus", "dc", "dss"
+};
+
+static const char *const iwmmxt_regnames[] =
+{ "wr0", "wr1", "wr2", "wr3", "wr4", "wr5", "wr6", "wr7",
+  "wr8", "wr9", "wr10", "wr11", "wr12", "wr13", "wr14", "wr15"
+};
+
+static const char *const iwmmxt_cregnames[] =
+{ "wcid", "wcon", "wcssf", "wcasf", "reserved", "reserved", "reserved", "reserved",
+  "wcgr0", "wcgr1", "wcgr2", "wcgr3", "reserved", "reserved", "reserved", "reserved"
+};
+
+/* Default to GCC register name set.  */
+static unsigned int regname_selected = 1;
+
+#define NUM_ARM_REGNAMES  NUM_ELEM (regnames)
+#define arm_regnames      regnames[regname_selected].reg_names
+
+static bfd_boolean force_thumb = FALSE;
+
+/* Current IT instruction state.  This contains the same state as the IT
+   bits in the CPSR.  */
+static unsigned int ifthen_state;
+/* IT state for the next instruction.  */
+static unsigned int ifthen_next_state;
+/* The address of the insn for which the IT state is valid.  */
+static bfd_vma ifthen_address;
+#define IFTHEN_COND ((ifthen_state >> 4) & 0xf)
+
+/* Cached mapping symbol state.  */
+enum map_type {
+  MAP_ARM,
+  MAP_THUMB,
+  MAP_DATA
+};
+
+enum map_type last_type;
+int last_mapping_sym = -1;
+bfd_vma last_mapping_addr = 0;
+
+
+/* Functions.  */
+int
+get_arm_regname_num_options (void)
+{
+  return NUM_ARM_REGNAMES;
+}
+
+int
+set_arm_regname_option (int option)
+{
+  int old = regname_selected;
+  regname_selected = option;
+  return old;
+}
+
+int
+get_arm_regnames (int option, const char **setname, const char **setdescription,
+		  const char *const **register_names)
+{
+  *setname = regnames[option].name;
+  *setdescription = regnames[option].description;
+  *register_names = regnames[option].reg_names;
+  return 16;
+}
+
+/* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?.
+   Returns pointer to following character of the format string and
+   fills in *VALUEP and *WIDTHP with the extracted value and number of
+   bits extracted.  WIDTHP can be NULL. */
+
+static const char *
+arm_decode_bitfield (const char *ptr, unsigned long insn,
+		     unsigned long *valuep, int *widthp)
+{
+  unsigned long value = 0;
+  int width = 0;
+
+  do
+    {
+      int start, end;
+      int bits;
+
+      for (start = 0; *ptr >= '0' && *ptr <= '9'; ptr++)
+	start = start * 10 + *ptr - '0';
+      if (*ptr == '-')
+	for (end = 0, ptr++; *ptr >= '0' && *ptr <= '9'; ptr++)
+	  end = end * 10 + *ptr - '0';
+      else
+	end = start;
+      bits = end - start;
+      if (bits < 0)
+	abort ();
+      value |= ((insn >> start) & ((2ul << bits) - 1)) << width;
+      width += bits + 1;
+    }
+  while (*ptr++ == ',');
+  *valuep = value;
+  if (widthp)
+    *widthp = width;
+  return ptr - 1;
+}
+
+static void
+arm_decode_shift (long given, fprintf_ftype func, void *stream,
+		  int print_shift)
+{
+  func (stream, "%s", arm_regnames[given & 0xf]);
+
+  if ((given & 0xff0) != 0)
+    {
+      if ((given & 0x10) == 0)
+	{
+	  int amount = (given & 0xf80) >> 7;
+	  int shift = (given & 0x60) >> 5;
+
+	  if (amount == 0)
+	    {
+	      if (shift == 3)
+		{
+		  func (stream, ", rrx");
+		  return;
+		}
+
+	      amount = 32;
+	    }
+
+	  if (print_shift)
+	    func (stream, ", %s #%d", arm_shift[shift], amount);
+	  else
+	    func (stream, ", #%d", amount);
+	}
+      else if (print_shift)
+	func (stream, ", %s %s", arm_shift[(given & 0x60) >> 5],
+	      arm_regnames[(given & 0xf00) >> 8]);
+      else
+	func (stream, ", %s", arm_regnames[(given & 0xf00) >> 8]);
+    }
+}
+
+/* Print one coprocessor instruction on INFO->STREAM.
+   Return TRUE if the instuction matched, FALSE if this is not a
+   recognised coprocessor instruction.  */
+
+static bfd_boolean
+print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given,
+			bfd_boolean thumb)
+{
+  const struct opcode32 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+  unsigned long mask;
+  unsigned long value;
+  int cond;
+
+  for (insn = coprocessor_opcodes; insn->assembler; insn++)
+    {
+      if (insn->value == FIRST_IWMMXT_INSN
+	  && info->mach != bfd_mach_arm_XScale
+	  && info->mach != bfd_mach_arm_iWMMXt
+	  && info->mach != bfd_mach_arm_iWMMXt2)
+	insn = insn + IWMMXT_INSN_COUNT;
+
+      mask = insn->mask;
+      value = insn->value;
+      if (thumb)
+	{
+	  /* The high 4 bits are 0xe for Arm conditional instructions, and
+	     0xe for arm unconditional instructions.  The rest of the
+	     encoding is the same.  */
+	  mask |= 0xf0000000;
+	  value |= 0xe0000000;
+	  if (ifthen_state)
+	    cond = IFTHEN_COND;
+	  else
+	    cond = 16;
+	}
+      else
+	{
+	  /* Only match unconditional instuctions against unconditional
+	     patterns.  */
+	  if ((given & 0xf0000000) == 0xf0000000)
+	    {
+	      mask |= 0xf0000000;
+	      cond = 16;
+	    }
+	  else
+	    {
+	      cond = (given >> 28) & 0xf;
+	      if (cond == 0xe)
+		cond = 16;
+	    }
+	}
+      if ((given & mask) == value)
+	{
+	  const char *c;
+
+	  for (c = insn->assembler; *c; c++)
+	    {
+	      if (*c == '%')
+		{
+		  switch (*++c)
+		    {
+		    case '%':
+		      func (stream, "%%");
+		      break;
+
+		    case 'A':
+		      func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+
+		      if ((given & (1 << 24)) != 0)
+			{
+			  int offset = given & 0xff;
+
+			  if (offset)
+			    func (stream, ", #%s%d]%s",
+				  ((given & 0x00800000) == 0 ? "-" : ""),
+				  offset * 4,
+				  ((given & 0x00200000) != 0 ? "!" : ""));
+			  else
+			    func (stream, "]");
+			}
+		      else
+			{
+			  int offset = given & 0xff;
+
+			  func (stream, "]");
+
+			  if (given & (1 << 21))
+			    {
+			      if (offset)
+				func (stream, ", #%s%d",
+				      ((given & 0x00800000) == 0 ? "-" : ""),
+				      offset * 4);
+			    }
+			  else
+			    func (stream, ", {%d}", offset);
+			}
+		      break;
+
+		    case 'B':
+		      {
+			int regno = ((given >> 12) & 0xf) | ((given >> (22 - 4)) & 0x10);
+			int offset = (given >> 1) & 0x3f;
+
+			if (offset == 1)
+			  func (stream, "{d%d}", regno);
+			else if (regno + offset > 32)
+			  func (stream, "{d%d-<overflow reg d%d>}", regno, regno + offset - 1);
+			else
+			  func (stream, "{d%d-d%d}", regno, regno + offset - 1);
+		      }
+		      break;
+
+		    case 'C':
+		      {
+			int rn = (given >> 16) & 0xf;
+			int offset = (given & 0xff) * 4;
+			int add = (given >> 23) & 1;
+
+			func (stream, "[%s", arm_regnames[rn]);
+
+			if (offset)
+			  {
+			    if (!add)
+			      offset = -offset;
+			    func (stream, ", #%d", offset);
+			  }
+			func (stream, "]");
+			if (rn == 15)
+			  {
+			    func (stream, "\t; ");
+                            /* FIXME: Unsure if info->bytes_per_chunk is the
+                               right thing to use here.  */
+			    info->print_address_func (offset + pc
+                              + info->bytes_per_chunk * 2, info);
+			  }
+		      }
+		      break;
+
+		    case 'c':
+		      func (stream, "%s", arm_conditional[cond]);
+		      break;
+
+		    case 'I':
+		      /* Print a Cirrus/DSP shift immediate.  */
+		      /* Immediates are 7bit signed ints with bits 0..3 in
+			 bits 0..3 of opcode and bits 4..6 in bits 5..7
+			 of opcode.  */
+		      {
+			int imm;
+
+			imm = (given & 0xf) | ((given & 0xe0) >> 1);
+
+			/* Is ``imm'' a negative number?  */
+			if (imm & 0x40)
+			  imm |= (-1 << 7);
+
+			func (stream, "%d", imm);
+		      }
+
+		      break;
+
+		    case 'F':
+		      switch (given & 0x00408000)
+			{
+			case 0:
+			  func (stream, "4");
+			  break;
+			case 0x8000:
+			  func (stream, "1");
+			  break;
+			case 0x00400000:
+			  func (stream, "2");
+			  break;
+			default:
+			  func (stream, "3");
+			}
+		      break;
+
+		    case 'P':
+		      switch (given & 0x00080080)
+			{
+			case 0:
+			  func (stream, "s");
+			  break;
+			case 0x80:
+			  func (stream, "d");
+			  break;
+			case 0x00080000:
+			  func (stream, "e");
+			  break;
+			default:
+			  func (stream, _("<illegal precision>"));
+			  break;
+			}
+		      break;
+		    case 'Q':
+		      switch (given & 0x00408000)
+			{
+			case 0:
+			  func (stream, "s");
+			  break;
+			case 0x8000:
+			  func (stream, "d");
+			  break;
+			case 0x00400000:
+			  func (stream, "e");
+			  break;
+			default:
+			  func (stream, "p");
+			  break;
+			}
+		      break;
+		    case 'R':
+		      switch (given & 0x60)
+			{
+			case 0:
+			  break;
+			case 0x20:
+			  func (stream, "p");
+			  break;
+			case 0x40:
+			  func (stream, "m");
+			  break;
+			default:
+			  func (stream, "z");
+			  break;
+			}
+		      break;
+
+		    case '0': case '1': case '2': case '3': case '4':
+		    case '5': case '6': case '7': case '8': case '9':
+		      {
+			int width;
+			unsigned long value;
+
+			c = arm_decode_bitfield (c, given, &value, &width);
+
+			switch (*c)
+			  {
+			  case 'r':
+			    func (stream, "%s", arm_regnames[value]);
+			    break;
+			  case 'D':
+			    func (stream, "d%ld", value);
+			    break;
+			  case 'Q':
+			    if (value & 1)
+			      func (stream, "<illegal reg q%ld.5>", value >> 1);
+			    else
+			      func (stream, "q%ld", value >> 1);
+			    break;
+			  case 'd':
+			    func (stream, "%ld", value);
+			    break;
+                          case 'k':
+                            {
+                              int from = (given & (1 << 7)) ? 32 : 16;
+                              func (stream, "%ld", from - value);
+                            }
+                            break;
+
+			  case 'f':
+			    if (value > 7)
+			      func (stream, "#%s", arm_fp_const[value & 7]);
+			    else
+			      func (stream, "f%ld", value);
+			    break;
+
+			  case 'w':
+			    if (width == 2)
+			      func (stream, "%s", iwmmxt_wwnames[value]);
+			    else
+			      func (stream, "%s", iwmmxt_wwssnames[value]);
+			    break;
+
+			  case 'g':
+			    func (stream, "%s", iwmmxt_regnames[value]);
+			    break;
+			  case 'G':
+			    func (stream, "%s", iwmmxt_cregnames[value]);
+			    break;
+
+			  case 'x':
+			    func (stream, "0x%lx", value);
+			    break;
+
+			  case '`':
+			    c++;
+			    if (value == 0)
+			      func (stream, "%c", *c);
+			    break;
+			  case '\'':
+			    c++;
+			    if (value == ((1ul << width) - 1))
+			      func (stream, "%c", *c);
+			    break;
+			  case '?':
+			    func (stream, "%c", c[(1 << width) - (int)value]);
+			    c += 1 << width;
+			    break;
+			  default:
+			    abort ();
+			  }
+			break;
+
+		      case 'y':
+		      case 'z':
+			{
+			  int single = *c++ == 'y';
+			  int regno;
+
+			  switch (*c)
+			    {
+			    case '4': /* Sm pair */
+			      func (stream, "{");
+			      /* Fall through.  */
+			    case '0': /* Sm, Dm */
+			      regno = given & 0x0000000f;
+			      if (single)
+				{
+				  regno <<= 1;
+				  regno += (given >> 5) & 1;
+				}
+                              else
+                                regno += ((given >> 5) & 1) << 4;
+			      break;
+
+			    case '1': /* Sd, Dd */
+			      regno = (given >> 12) & 0x0000000f;
+			      if (single)
+				{
+				  regno <<= 1;
+				  regno += (given >> 22) & 1;
+				}
+                              else
+                                regno += ((given >> 22) & 1) << 4;
+			      break;
+
+			    case '2': /* Sn, Dn */
+			      regno = (given >> 16) & 0x0000000f;
+			      if (single)
+				{
+				  regno <<= 1;
+				  regno += (given >> 7) & 1;
+				}
+                              else
+                                regno += ((given >> 7) & 1) << 4;
+			      break;
+
+			    case '3': /* List */
+			      func (stream, "{");
+			      regno = (given >> 12) & 0x0000000f;
+			      if (single)
+				{
+				  regno <<= 1;
+				  regno += (given >> 22) & 1;
+				}
+                              else
+                                regno += ((given >> 22) & 1) << 4;
+			      break;
+
+			    default:
+			      abort ();
+			    }
+
+			  func (stream, "%c%d", single ? 's' : 'd', regno);
+
+			  if (*c == '3')
+			    {
+			      int count = given & 0xff;
+
+			      if (single == 0)
+				count >>= 1;
+
+			      if (--count)
+				{
+				  func (stream, "-%c%d",
+					single ? 's' : 'd',
+					regno + count);
+				}
+
+			      func (stream, "}");
+			    }
+			  else if (*c == '4')
+			    func (stream, ", %c%d}", single ? 's' : 'd',
+				  regno + 1);
+			}
+			break;
+
+		      case 'L':
+			switch (given & 0x00400100)
+			  {
+			  case 0x00000000: func (stream, "b"); break;
+			  case 0x00400000: func (stream, "h"); break;
+			  case 0x00000100: func (stream, "w"); break;
+			  case 0x00400100: func (stream, "d"); break;
+			  default:
+			    break;
+			  }
+			break;
+
+		      case 'Z':
+			{
+			  int value;
+			  /* given (20, 23) | given (0, 3) */
+			  value = ((given >> 16) & 0xf0) | (given & 0xf);
+			  func (stream, "%d", value);
+			}
+			break;
+
+		      case 'l':
+			/* This is like the 'A' operator, except that if
+			   the width field "M" is zero, then the offset is
+			   *not* multiplied by four.  */
+			{
+			  int offset = given & 0xff;
+			  int multiplier = (given & 0x00000100) ? 4 : 1;
+
+			  func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+
+			  if (offset)
+			    {
+			      if ((given & 0x01000000) != 0)
+				func (stream, ", #%s%d]%s",
+				      ((given & 0x00800000) == 0 ? "-" : ""),
+				      offset * multiplier,
+				      ((given & 0x00200000) != 0 ? "!" : ""));
+			      else
+				func (stream, "], #%s%d",
+				      ((given & 0x00800000) == 0 ? "-" : ""),
+				      offset * multiplier);
+			    }
+			  else
+			    func (stream, "]");
+			}
+			break;
+
+		      case 'r':
+			{
+			  int imm4 = (given >> 4) & 0xf;
+			  int puw_bits = ((given >> 22) & 6) | ((given >> 21) & 1);
+			  int ubit = (given >> 23) & 1;
+			  const char *rm = arm_regnames [given & 0xf];
+			  const char *rn = arm_regnames [(given >> 16) & 0xf];
+
+			  switch (puw_bits)
+			    {
+			    case 1:
+			      /* fall through */
+			    case 3:
+			      func (stream, "[%s], %c%s", rn, ubit ? '+' : '-', rm);
+			      if (imm4)
+				func (stream, ", lsl #%d", imm4);
+			      break;
+
+			    case 4:
+			      /* fall through */
+			    case 5:
+			      /* fall through */
+			    case 6:
+			      /* fall through */
+			    case 7:
+			      func (stream, "[%s, %c%s", rn, ubit ? '+' : '-', rm);
+			      if (imm4 > 0)
+				func (stream, ", lsl #%d", imm4);
+			      func (stream, "]");
+			      if (puw_bits == 5 || puw_bits == 7)
+				func (stream, "!");
+			      break;
+
+			    default:
+			      func (stream, "INVALID");
+			    }
+			}
+			break;
+
+		      case 'i':
+			{
+			  long imm5;
+			  imm5 = ((given & 0x100) >> 4) | (given & 0xf);
+			  func (stream, "%ld", (imm5 == 0) ? 32 : imm5);
+			}
+			break;
+
+		      default:
+			abort ();
+		      }
+		    }
+		}
+	      else
+		func (stream, "%c", *c);
+	    }
+	  return TRUE;
+	}
+    }
+  return FALSE;
+}
+
+static void
+print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
+{
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  if (((given & 0x000f0000) == 0x000f0000)
+      && ((given & 0x02000000) == 0))
+    {
+      int offset = given & 0xfff;
+
+      func (stream, "[pc");
+
+      if (given & 0x01000000)
+	{
+	  if ((given & 0x00800000) == 0)
+	    offset = - offset;
+
+	  /* Pre-indexed.  */
+	  func (stream, ", #%d]", offset);
+
+	  offset += pc + 8;
+
+	  /* Cope with the possibility of write-back
+	     being used.  Probably a very dangerous thing
+	     for the programmer to do, but who are we to
+	     argue ?  */
+	  if (given & 0x00200000)
+	    func (stream, "!");
+	}
+      else
+	{
+	  /* Post indexed.  */
+	  func (stream, "], #%d", offset);
+
+	  /* ie ignore the offset.  */
+	  offset = pc + 8;
+	}
+
+      func (stream, "\t; ");
+      info->print_address_func (offset, info);
+    }
+  else
+    {
+      func (stream, "[%s",
+	    arm_regnames[(given >> 16) & 0xf]);
+      if ((given & 0x01000000) != 0)
+	{
+	  if ((given & 0x02000000) == 0)
+	    {
+	      int offset = given & 0xfff;
+	      if (offset)
+		func (stream, ", #%s%d",
+		      (((given & 0x00800000) == 0)
+		       ? "-" : ""), offset);
+	    }
+	  else
+	    {
+	      func (stream, ", %s",
+		    (((given & 0x00800000) == 0)
+		     ? "-" : ""));
+	      arm_decode_shift (given, func, stream, 1);
+	    }
+
+	  func (stream, "]%s",
+		((given & 0x00200000) != 0) ? "!" : "");
+	}
+      else
+	{
+	  if ((given & 0x02000000) == 0)
+	    {
+	      int offset = given & 0xfff;
+	      if (offset)
+		func (stream, "], #%s%d",
+		      (((given & 0x00800000) == 0)
+		       ? "-" : ""), offset);
+	      else
+		func (stream, "]");
+	    }
+	  else
+	    {
+	      func (stream, "], %s",
+		    (((given & 0x00800000) == 0)
+		     ? "-" : ""));
+	      arm_decode_shift (given, func, stream, 1);
+	    }
+	}
+    }
+}
+
+/* Print one neon instruction on INFO->STREAM.
+   Return TRUE if the instuction matched, FALSE if this is not a
+   recognised neon instruction.  */
+
+static bfd_boolean
+print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
+{
+  const struct opcode32 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  if (thumb)
+    {
+      if ((given & 0xef000000) == 0xef000000)
+	{
+	  /* move bit 28 to bit 24 to translate Thumb2 to ARM encoding.  */
+	  unsigned long bit28 = given & (1 << 28);
+
+	  given &= 0x00ffffff;
+	  if (bit28)
+            given |= 0xf3000000;
+          else
+	    given |= 0xf2000000;
+	}
+      else if ((given & 0xff000000) == 0xf9000000)
+	given ^= 0xf9000000 ^ 0xf4000000;
+      else
+	return FALSE;
+    }
+
+  for (insn = neon_opcodes; insn->assembler; insn++)
+    {
+      if ((given & insn->mask) == insn->value)
+	{
+	  const char *c;
+
+	  for (c = insn->assembler; *c; c++)
+	    {
+	      if (*c == '%')
+		{
+		  switch (*++c)
+		    {
+		    case '%':
+		      func (stream, "%%");
+		      break;
+
+		    case 'c':
+		      if (thumb && ifthen_state)
+			func (stream, "%s", arm_conditional[IFTHEN_COND]);
+		      break;
+
+		    case 'A':
+		      {
+			static const unsigned char enc[16] =
+			{
+			  0x4, 0x14, /* st4 0,1 */
+			  0x4, /* st1 2 */
+			  0x4, /* st2 3 */
+			  0x3, /* st3 4 */
+			  0x13, /* st3 5 */
+			  0x3, /* st1 6 */
+			  0x1, /* st1 7 */
+			  0x2, /* st2 8 */
+			  0x12, /* st2 9 */
+			  0x2, /* st1 10 */
+			  0, 0, 0, 0, 0
+			};
+			int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4);
+			int rn = ((given >> 16) & 0xf);
+			int rm = ((given >> 0) & 0xf);
+			int align = ((given >> 4) & 0x3);
+			int type = ((given >> 8) & 0xf);
+			int n = enc[type] & 0xf;
+			int stride = (enc[type] >> 4) + 1;
+			int ix;
+
+			func (stream, "{");
+			if (stride > 1)
+			  for (ix = 0; ix != n; ix++)
+			    func (stream, "%sd%d", ix ? "," : "", rd + ix * stride);
+			else if (n == 1)
+			  func (stream, "d%d", rd);
+			else
+			  func (stream, "d%d-d%d", rd, rd + n - 1);
+			func (stream, "}, [%s", arm_regnames[rn]);
+			if (align)
+			  func (stream, ", :%d", 32 << align);
+			func (stream, "]");
+			if (rm == 0xd)
+			  func (stream, "!");
+			else if (rm != 0xf)
+			  func (stream, ", %s", arm_regnames[rm]);
+		      }
+		      break;
+
+		    case 'B':
+		      {
+			int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4);
+			int rn = ((given >> 16) & 0xf);
+			int rm = ((given >> 0) & 0xf);
+			int idx_align = ((given >> 4) & 0xf);
+                        int align = 0;
+			int size = ((given >> 10) & 0x3);
+			int idx = idx_align >> (size + 1);
+                        int length = ((given >> 8) & 3) + 1;
+                        int stride = 1;
+                        int i;
+
+                        if (length > 1 && size > 0)
+                          stride = (idx_align & (1 << size)) ? 2 : 1;
+
+                        switch (length)
+                          {
+                          case 1:
+                            {
+                              int amask = (1 << size) - 1;
+                              if ((idx_align & (1 << size)) != 0)
+                                return FALSE;
+                              if (size > 0)
+                                {
+                                  if ((idx_align & amask) == amask)
+                                    align = 8 << size;
+                                  else if ((idx_align & amask) != 0)
+                                    return FALSE;
+                                }
+                              }
+                            break;
+
+                          case 2:
+                            if (size == 2 && (idx_align & 2) != 0)
+                              return FALSE;
+                            align = (idx_align & 1) ? 16 << size : 0;
+                            break;
+
+                          case 3:
+                            if ((size == 2 && (idx_align & 3) != 0)
+                                || (idx_align & 1) != 0)
+                              return FALSE;
+                            break;
+
+                          case 4:
+                            if (size == 2)
+                              {
+                                if ((idx_align & 3) == 3)
+                                  return FALSE;
+                                align = (idx_align & 3) * 64;
+                              }
+                            else
+                              align = (idx_align & 1) ? 32 << size : 0;
+                            break;
+
+                          default:
+                            abort ();
+                          }
+
+			func (stream, "{");
+                        for (i = 0; i < length; i++)
+                          func (stream, "%sd%d[%d]", (i == 0) ? "" : ",",
+                            rd + i * stride, idx);
+                        func (stream, "}, [%s", arm_regnames[rn]);
+			if (align)
+			  func (stream, ", :%d", align);
+			func (stream, "]");
+			if (rm == 0xd)
+			  func (stream, "!");
+			else if (rm != 0xf)
+			  func (stream, ", %s", arm_regnames[rm]);
+		      }
+		      break;
+
+		    case 'C':
+		      {
+			int rd = ((given >> 12) & 0xf) | (((given >> 22) & 1) << 4);
+			int rn = ((given >> 16) & 0xf);
+			int rm = ((given >> 0) & 0xf);
+			int align = ((given >> 4) & 0x1);
+			int size = ((given >> 6) & 0x3);
+			int type = ((given >> 8) & 0x3);
+			int n = type + 1;
+			int stride = ((given >> 5) & 0x1);
+			int ix;
+
+			if (stride && (n == 1))
+			  n++;
+			else
+			  stride++;
+
+			func (stream, "{");
+			if (stride > 1)
+			  for (ix = 0; ix != n; ix++)
+			    func (stream, "%sd%d[]", ix ? "," : "", rd + ix * stride);
+			else if (n == 1)
+			  func (stream, "d%d[]", rd);
+			else
+			  func (stream, "d%d[]-d%d[]", rd, rd + n - 1);
+			func (stream, "}, [%s", arm_regnames[rn]);
+			if (align)
+			  {
+                            int align = (8 * (type + 1)) << size;
+                            if (type == 3)
+                              align = (size > 1) ? align >> 1 : align;
+			    if (type == 2 || (type == 0 && !size))
+			      func (stream, ", :<bad align %d>", align);
+			    else
+			      func (stream, ", :%d", align);
+			  }
+			func (stream, "]");
+			if (rm == 0xd)
+			  func (stream, "!");
+			else if (rm != 0xf)
+			  func (stream, ", %s", arm_regnames[rm]);
+		      }
+		      break;
+
+		    case 'D':
+		      {
+			int raw_reg = (given & 0xf) | ((given >> 1) & 0x10);
+			int size = (given >> 20) & 3;
+			int reg = raw_reg & ((4 << size) - 1);
+			int ix = raw_reg >> size >> 2;
+
+			func (stream, "d%d[%d]", reg, ix);
+		      }
+		      break;
+
+		    case 'E':
+		      /* Neon encoded constant for mov, mvn, vorr, vbic */
+		      {
+			int bits = 0;
+			int cmode = (given >> 8) & 0xf;
+			int op = (given >> 5) & 0x1;
+			unsigned long value = 0, hival = 0;
+			unsigned shift;
+                        int size = 0;
+                        int isfloat = 0;
+
+			bits |= ((given >> 24) & 1) << 7;
+			bits |= ((given >> 16) & 7) << 4;
+			bits |= ((given >> 0) & 15) << 0;
+
+			if (cmode < 8)
+			  {
+			    shift = (cmode >> 1) & 3;
+			    value = (unsigned long)bits << (8 * shift);
+                            size = 32;
+			  }
+			else if (cmode < 12)
+			  {
+			    shift = (cmode >> 1) & 1;
+			    value = (unsigned long)bits << (8 * shift);
+                            size = 16;
+			  }
+			else if (cmode < 14)
+			  {
+			    shift = (cmode & 1) + 1;
+			    value = (unsigned long)bits << (8 * shift);
+			    value |= (1ul << (8 * shift)) - 1;
+                            size = 32;
+			  }
+			else if (cmode == 14)
+			  {
+			    if (op)
+			      {
+				/* bit replication into bytes */
+				int ix;
+				unsigned long mask;
+
+				value = 0;
+                                hival = 0;
+				for (ix = 7; ix >= 0; ix--)
+				  {
+				    mask = ((bits >> ix) & 1) ? 0xff : 0;
+                                    if (ix <= 3)
+				      value = (value << 8) | mask;
+                                    else
+                                      hival = (hival << 8) | mask;
+				  }
+                                size = 64;
+			      }
+                            else
+                              {
+                                /* byte replication */
+                                value = (unsigned long)bits;
+                                size = 8;
+                              }
+			  }
+			else if (!op)
+			  {
+			    /* floating point encoding */
+			    int tmp;
+
+			    value = (unsigned long)(bits & 0x7f) << 19;
+			    value |= (unsigned long)(bits & 0x80) << 24;
+			    tmp = bits & 0x40 ? 0x3c : 0x40;
+			    value |= (unsigned long)tmp << 24;
+                            size = 32;
+                            isfloat = 1;
+			  }
+			else
+			  {
+			    func (stream, "<illegal constant %.8x:%x:%x>",
+                                  bits, cmode, op);
+                            size = 32;
+			    break;
+			  }
+                        switch (size)
+                          {
+                          case 8:
+			    func (stream, "#%ld\t; 0x%.2lx", value, value);
+                            break;
+
+                          case 16:
+                            func (stream, "#%ld\t; 0x%.4lx", value, value);
+                            break;
+
+                          case 32:
+                            if (isfloat)
+                              {
+                                unsigned char valbytes[4];
+                                double fvalue;
+
+                                /* Do this a byte at a time so we don't have to
+                                   worry about the host's endianness.  */
+                                valbytes[0] = value & 0xff;
+                                valbytes[1] = (value >> 8) & 0xff;
+                                valbytes[2] = (value >> 16) & 0xff;
+                                valbytes[3] = (value >> 24) & 0xff;
+
+                                floatformat_to_double
+                                  (&floatformat_ieee_single_little, valbytes,
+                                  &fvalue);
+
+                                func (stream, "#%.7g\t; 0x%.8lx", fvalue,
+                                      value);
+                              }
+                            else
+                              func (stream, "#%ld\t; 0x%.8lx",
+				(long) ((value & 0x80000000)
+					? value | ~0xffffffffl : value), value);
+                            break;
+
+                          case 64:
+                            func (stream, "#0x%.8lx%.8lx", hival, value);
+                            break;
+
+                          default:
+                            abort ();
+                          }
+		      }
+		      break;
+
+		    case 'F':
+		      {
+			int regno = ((given >> 16) & 0xf) | ((given >> (7 - 4)) & 0x10);
+			int num = (given >> 8) & 0x3;
+
+			if (!num)
+			  func (stream, "{d%d}", regno);
+			else if (num + regno >= 32)
+			  func (stream, "{d%d-<overflow reg d%d}", regno, regno + num);
+			else
+			  func (stream, "{d%d-d%d}", regno, regno + num);
+		      }
+		      break;
+
+
+		    case '0': case '1': case '2': case '3': case '4':
+		    case '5': case '6': case '7': case '8': case '9':
+		      {
+			int width;
+			unsigned long value;
+
+			c = arm_decode_bitfield (c, given, &value, &width);
+
+			switch (*c)
+			  {
+			  case 'r':
+			    func (stream, "%s", arm_regnames[value]);
+			    break;
+			  case 'd':
+			    func (stream, "%ld", value);
+			    break;
+			  case 'e':
+			    func (stream, "%ld", (1ul << width) - value);
+			    break;
+
+			  case 'S':
+			  case 'T':
+			  case 'U':
+			    /* various width encodings */
+			    {
+			      int base = 8 << (*c - 'S'); /* 8,16 or 32 */
+			      int limit;
+			      unsigned low, high;
+
+			      c++;
+			      if (*c >= '0' && *c <= '9')
+				limit = *c - '0';
+			      else if (*c >= 'a' && *c <= 'f')
+				limit = *c - 'a' + 10;
+			      else
+				abort ();
+			      low = limit >> 2;
+			      high = limit & 3;
+
+			      if (value < low || value > high)
+				func (stream, "<illegal width %d>", base << value);
+			      else
+				func (stream, "%d", base << value);
+			    }
+			    break;
+			  case 'R':
+			    if (given & (1 << 6))
+			      goto Q;
+			    /* FALLTHROUGH */
+			  case 'D':
+			    func (stream, "d%ld", value);
+			    break;
+			  case 'Q':
+			  Q:
+			    if (value & 1)
+			      func (stream, "<illegal reg q%ld.5>", value >> 1);
+			    else
+			      func (stream, "q%ld", value >> 1);
+			    break;
+
+			  case '`':
+			    c++;
+			    if (value == 0)
+			      func (stream, "%c", *c);
+			    break;
+			  case '\'':
+			    c++;
+			    if (value == ((1ul << width) - 1))
+			      func (stream, "%c", *c);
+			    break;
+			  case '?':
+			    func (stream, "%c", c[(1 << width) - (int)value]);
+			    c += 1 << width;
+			    break;
+			  default:
+			    abort ();
+			  }
+			break;
+
+		      default:
+			abort ();
+		      }
+		    }
+		}
+	      else
+		func (stream, "%c", *c);
+	    }
+	  return TRUE;
+	}
+    }
+  return FALSE;
+}
+
+/* Print one ARM instruction from PC on INFO->STREAM.  */
+
+static void
+print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given)
+{
+  const struct opcode32 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  if (print_insn_coprocessor (pc, info, given, FALSE))
+    return;
+
+  if (print_insn_neon (info, given, FALSE))
+    return;
+
+  for (insn = arm_opcodes; insn->assembler; insn++)
+    {
+      if (insn->value == FIRST_IWMMXT_INSN
+	  && info->mach != bfd_mach_arm_XScale
+	  && info->mach != bfd_mach_arm_iWMMXt)
+	insn = insn + IWMMXT_INSN_COUNT;
+
+      if ((given & insn->mask) == insn->value
+	  /* Special case: an instruction with all bits set in the condition field
+	     (0xFnnn_nnnn) is only matched if all those bits are set in insn->mask,
+	     or by the catchall at the end of the table.  */
+	  && ((given & 0xF0000000) != 0xF0000000
+	      || (insn->mask & 0xF0000000) == 0xF0000000
+	      || (insn->mask == 0 && insn->value == 0)))
+	{
+	  const char *c;
+
+	  for (c = insn->assembler; *c; c++)
+	    {
+	      if (*c == '%')
+		{
+		  switch (*++c)
+		    {
+		    case '%':
+		      func (stream, "%%");
+		      break;
+
+		    case 'a':
+		      print_arm_address (pc, info, given);
+		      break;
+
+		    case 'P':
+		      /* Set P address bit and use normal address
+			 printing routine.  */
+		      print_arm_address (pc, info, given | (1 << 24));
+		      break;
+
+		    case 's':
+                      if ((given & 0x004f0000) == 0x004f0000)
+			{
+                          /* PC relative with immediate offset.  */
+			  int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+
+			  if ((given & 0x00800000) == 0)
+			    offset = -offset;
+
+			  func (stream, "[pc, #%d]\t; ", offset);
+			  info->print_address_func (offset + pc + 8, info);
+			}
+		      else
+			{
+			  func (stream, "[%s",
+				arm_regnames[(given >> 16) & 0xf]);
+			  if ((given & 0x01000000) != 0)
+			    {
+                              /* Pre-indexed.  */
+			      if ((given & 0x00400000) == 0x00400000)
+				{
+                                  /* Immediate.  */
+                                  int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+				  if (offset)
+				    func (stream, ", #%s%d",
+					  (((given & 0x00800000) == 0)
+					   ? "-" : ""), offset);
+				}
+			      else
+				{
+                                  /* Register.  */
+				  func (stream, ", %s%s",
+					(((given & 0x00800000) == 0)
+					 ? "-" : ""),
+                                        arm_regnames[given & 0xf]);
+				}
+
+			      func (stream, "]%s",
+				    ((given & 0x00200000) != 0) ? "!" : "");
+			    }
+			  else
+			    {
+                              /* Post-indexed.  */
+			      if ((given & 0x00400000) == 0x00400000)
+				{
+                                  /* Immediate.  */
+                                  int offset = ((given & 0xf00) >> 4) | (given & 0xf);
+				  if (offset)
+				    func (stream, "], #%s%d",
+					  (((given & 0x00800000) == 0)
+					   ? "-" : ""), offset);
+				  else
+				    func (stream, "]");
+				}
+			      else
+				{
+                                  /* Register.  */
+				  func (stream, "], %s%s",
+					(((given & 0x00800000) == 0)
+					 ? "-" : ""),
+                                        arm_regnames[given & 0xf]);
+				}
+			    }
+			}
+		      break;
+
+		    case 'b':
+		      {
+			int disp = (((given & 0xffffff) ^ 0x800000) - 0x800000);
+			info->print_address_func (disp*4 + pc + 8, info);
+		      }
+		      break;
+
+		    case 'c':
+		      if (((given >> 28) & 0xf) != 0xe)
+			func (stream, "%s",
+			      arm_conditional [(given >> 28) & 0xf]);
+		      break;
+
+		    case 'm':
+		      {
+			int started = 0;
+			int reg;
+
+			func (stream, "{");
+			for (reg = 0; reg < 16; reg++)
+			  if ((given & (1 << reg)) != 0)
+			    {
+			      if (started)
+				func (stream, ", ");
+			      started = 1;
+			      func (stream, "%s", arm_regnames[reg]);
+			    }
+			func (stream, "}");
+		      }
+		      break;
+
+		    case 'q':
+		      arm_decode_shift (given, func, stream, 0);
+		      break;
+
+		    case 'o':
+		      if ((given & 0x02000000) != 0)
+			{
+			  int rotate = (given & 0xf00) >> 7;
+			  int immed = (given & 0xff);
+			  immed = (((immed << (32 - rotate))
+				    | (immed >> rotate)) & 0xffffffff);
+			  func (stream, "#%d\t; 0x%x", immed, immed);
+			}
+		      else
+			arm_decode_shift (given, func, stream, 1);
+		      break;
+
+		    case 'p':
+		      if ((given & 0x0000f000) == 0x0000f000)
+			func (stream, "p");
+		      break;
+
+		    case 't':
+		      if ((given & 0x01200000) == 0x00200000)
+			func (stream, "t");
+		      break;
+
+		    case 'A':
+		      func (stream, "[%s", arm_regnames [(given >> 16) & 0xf]);
+
+		      if ((given & (1 << 24)) != 0)
+			{
+			  int offset = given & 0xff;
+
+			  if (offset)
+			    func (stream, ", #%s%d]%s",
+				  ((given & 0x00800000) == 0 ? "-" : ""),
+				  offset * 4,
+				  ((given & 0x00200000) != 0 ? "!" : ""));
+			  else
+			    func (stream, "]");
+			}
+		      else
+			{
+			  int offset = given & 0xff;
+
+			  func (stream, "]");
+
+			  if (given & (1 << 21))
+			    {
+			      if (offset)
+				func (stream, ", #%s%d",
+				      ((given & 0x00800000) == 0 ? "-" : ""),
+				      offset * 4);
+			    }
+			  else
+			    func (stream, ", {%d}", offset);
+			}
+		      break;
+
+		    case 'B':
+		      /* Print ARM V5 BLX(1) address: pc+25 bits.  */
+		      {
+			bfd_vma address;
+			bfd_vma offset = 0;
+
+			if (given & 0x00800000)
+			  /* Is signed, hi bits should be ones.  */
+			  offset = (-1) ^ 0x00ffffff;
+
+			/* Offset is (SignExtend(offset field)<<2).  */
+			offset += given & 0x00ffffff;
+			offset <<= 2;
+			address = offset + pc + 8;
+
+			if (given & 0x01000000)
+			  /* H bit allows addressing to 2-byte boundaries.  */
+			  address += 2;
+
+		        info->print_address_func (address, info);
+		      }
+		      break;
+
+		    case 'C':
+		      func (stream, "_");
+		      if (given & 0x80000)
+			func (stream, "f");
+		      if (given & 0x40000)
+			func (stream, "s");
+		      if (given & 0x20000)
+			func (stream, "x");
+		      if (given & 0x10000)
+			func (stream, "c");
+		      break;
+
+		    case 'U':
+		      switch (given & 0xf)
+			{
+			case 0xf: func(stream, "sy"); break;
+			case 0x7: func(stream, "un"); break;
+			case 0xe: func(stream, "st"); break;
+			case 0x6: func(stream, "unst"); break;
+			default:
+			  func(stream, "#%d", (int)given & 0xf);
+			  break;
+			}
+		      break;
+
+		    case '0': case '1': case '2': case '3': case '4':
+		    case '5': case '6': case '7': case '8': case '9':
+		      {
+			int width;
+			unsigned long value;
+
+			c = arm_decode_bitfield (c, given, &value, &width);
+
+			switch (*c)
+			  {
+			  case 'r':
+			    func (stream, "%s", arm_regnames[value]);
+			    break;
+			  case 'd':
+			    func (stream, "%ld", value);
+			    break;
+			  case 'b':
+			    func (stream, "%ld", value * 8);
+			    break;
+			  case 'W':
+			    func (stream, "%ld", value + 1);
+			    break;
+			  case 'x':
+			    func (stream, "0x%08lx", value);
+
+			    /* Some SWI instructions have special
+			       meanings.  */
+			    if ((given & 0x0fffffff) == 0x0FF00000)
+			      func (stream, "\t; IMB");
+			    else if ((given & 0x0fffffff) == 0x0FF00001)
+			      func (stream, "\t; IMBRange");
+			    break;
+			  case 'X':
+			    func (stream, "%01lx", value & 0xf);
+			    break;
+			  case '`':
+			    c++;
+			    if (value == 0)
+			      func (stream, "%c", *c);
+			    break;
+			  case '\'':
+			    c++;
+			    if (value == ((1ul << width) - 1))
+			      func (stream, "%c", *c);
+			    break;
+			  case '?':
+			    func (stream, "%c", c[(1 << width) - (int)value]);
+			    c += 1 << width;
+			    break;
+			  default:
+			    abort ();
+			  }
+			break;
+
+		      case 'e':
+			{
+			  int imm;
+
+			  imm = (given & 0xf) | ((given & 0xfff00) >> 4);
+			  func (stream, "%d", imm);
+			}
+			break;
+
+		      case 'E':
+			/* LSB and WIDTH fields of BFI or BFC.  The machine-
+			   language instruction encodes LSB and MSB.  */
+			{
+			  long msb = (given & 0x001f0000) >> 16;
+			  long lsb = (given & 0x00000f80) >> 7;
+
+			  long width = msb - lsb + 1;
+			  if (width > 0)
+			    func (stream, "#%lu, #%lu", lsb, width);
+			  else
+			    func (stream, "(invalid: %lu:%lu)", lsb, msb);
+			}
+			break;
+
+		      case 'V':
+			/* 16-bit unsigned immediate from a MOVT or MOVW
+			   instruction, encoded in bits 0:11 and 15:19.  */
+			{
+			  long hi = (given & 0x000f0000) >> 4;
+			  long lo = (given & 0x00000fff);
+			  long imm16 = hi | lo;
+			  func (stream, "#%lu\t; 0x%lx", imm16, imm16);
+			}
+			break;
+
+		      default:
+			abort ();
+		      }
+		    }
+		}
+	      else
+		func (stream, "%c", *c);
+	    }
+	  return;
+	}
+    }
+  abort ();
+}
+
+/* Print one 16-bit Thumb instruction from PC on INFO->STREAM.  */
+
+static void
+print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given)
+{
+  const struct opcode16 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  for (insn = thumb_opcodes; insn->assembler; insn++)
+    if ((given & insn->mask) == insn->value)
+      {
+	const char *c = insn->assembler;
+	for (; *c; c++)
+	  {
+	    int domaskpc = 0;
+	    int domasklr = 0;
+
+	    if (*c != '%')
+	      {
+		func (stream, "%c", *c);
+		continue;
+	      }
+
+	    switch (*++c)
+	      {
+	      case '%':
+		func (stream, "%%");
+		break;
+
+	      case 'c':
+		if (ifthen_state)
+		  func (stream, "%s", arm_conditional[IFTHEN_COND]);
+		break;
+
+	      case 'C':
+		if (ifthen_state)
+		  func (stream, "%s", arm_conditional[IFTHEN_COND]);
+		else
+		  func (stream, "s");
+		break;
+
+	      case 'I':
+		{
+		  unsigned int tmp;
+
+		  ifthen_next_state = given & 0xff;
+		  for (tmp = given << 1; tmp & 0xf; tmp <<= 1)
+		    func (stream, ((given ^ tmp) & 0x10) ? "e" : "t");
+		  func (stream, "\t%s", arm_conditional[(given >> 4) & 0xf]);
+		}
+		break;
+
+	      case 'x':
+		if (ifthen_next_state)
+		  func (stream, "\t; unpredictable branch in IT block\n");
+		break;
+
+	      case 'X':
+		if (ifthen_state)
+		  func (stream, "\t; unpredictable <IT:%s>",
+			arm_conditional[IFTHEN_COND]);
+		break;
+
+	      case 'S':
+		{
+		  long reg;
+
+		  reg = (given >> 3) & 0x7;
+		  if (given & (1 << 6))
+		    reg += 8;
+
+		  func (stream, "%s", arm_regnames[reg]);
+		}
+		break;
+
+	      case 'D':
+		{
+		  long reg;
+
+		  reg = given & 0x7;
+		  if (given & (1 << 7))
+		    reg += 8;
+
+		  func (stream, "%s", arm_regnames[reg]);
+		}
+		break;
+
+	      case 'N':
+		if (given & (1 << 8))
+		  domasklr = 1;
+		/* Fall through.  */
+	      case 'O':
+		if (*c == 'O' && (given & (1 << 8)))
+		  domaskpc = 1;
+		/* Fall through.  */
+	      case 'M':
+		{
+		  int started = 0;
+		  int reg;
+
+		  func (stream, "{");
+
+		  /* It would be nice if we could spot
+		     ranges, and generate the rS-rE format: */
+		  for (reg = 0; (reg < 8); reg++)
+		    if ((given & (1 << reg)) != 0)
+		      {
+			if (started)
+			  func (stream, ", ");
+			started = 1;
+			func (stream, "%s", arm_regnames[reg]);
+		      }
+
+		  if (domasklr)
+		    {
+		      if (started)
+			func (stream, ", ");
+		      started = 1;
+		      func (stream, arm_regnames[14] /* "lr" */);
+		    }
+
+		  if (domaskpc)
+		    {
+		      if (started)
+			func (stream, ", ");
+		      func (stream, arm_regnames[15] /* "pc" */);
+		    }
+
+		  func (stream, "}");
+		}
+		break;
+
+	      case 'b':
+		/* Print ARM V6T2 CZB address: pc+4+6 bits.  */
+		{
+		  bfd_vma address = (pc + 4
+				     + ((given & 0x00f8) >> 2)
+				     + ((given & 0x0200) >> 3));
+		  info->print_address_func (address, info);
+		}
+		break;
+
+	      case 's':
+		/* Right shift immediate -- bits 6..10; 1-31 print
+		   as themselves, 0 prints as 32.  */
+		{
+		  long imm = (given & 0x07c0) >> 6;
+		  if (imm == 0)
+		    imm = 32;
+		  func (stream, "#%ld", imm);
+		}
+		break;
+
+	      case '0': case '1': case '2': case '3': case '4':
+	      case '5': case '6': case '7': case '8': case '9':
+		{
+		  int bitstart = *c++ - '0';
+		  int bitend = 0;
+
+		  while (*c >= '0' && *c <= '9')
+		    bitstart = (bitstart * 10) + *c++ - '0';
+
+		  switch (*c)
+		    {
+		    case '-':
+		      {
+			long reg;
+
+			c++;
+			while (*c >= '0' && *c <= '9')
+			  bitend = (bitend * 10) + *c++ - '0';
+			if (!bitend)
+			  abort ();
+			reg = given >> bitstart;
+			reg &= (2 << (bitend - bitstart)) - 1;
+			switch (*c)
+			  {
+			  case 'r':
+			    func (stream, "%s", arm_regnames[reg]);
+			    break;
+
+			  case 'd':
+			    func (stream, "%ld", reg);
+			    break;
+
+			  case 'H':
+			    func (stream, "%ld", reg << 1);
+			    break;
+
+			  case 'W':
+			    func (stream, "%ld", reg << 2);
+			    break;
+
+			  case 'a':
+			    /* PC-relative address -- the bottom two
+			       bits of the address are dropped
+			       before the calculation.  */
+			    info->print_address_func
+			      (((pc + 4) & ~3) + (reg << 2), info);
+			    break;
+
+			  case 'x':
+			    func (stream, "0x%04lx", reg);
+			    break;
+
+			  case 'B':
+			    reg = ((reg ^ (1 << bitend)) - (1 << bitend));
+			    info->print_address_func (reg * 2 + pc + 4, info);
+			    break;
+
+			  case 'c':
+			    func (stream, "%s", arm_conditional [reg]);
+			    break;
+
+			  default:
+			    abort ();
+			  }
+		      }
+		      break;
+
+		    case '\'':
+		      c++;
+		      if ((given & (1 << bitstart)) != 0)
+			func (stream, "%c", *c);
+		      break;
+
+		    case '?':
+		      ++c;
+		      if ((given & (1 << bitstart)) != 0)
+			func (stream, "%c", *c++);
+		      else
+			func (stream, "%c", *++c);
+		      break;
+
+		    default:
+		      abort ();
+		    }
+		}
+		break;
+
+	      default:
+		abort ();
+	      }
+	  }
+	return;
+      }
+
+  /* No match.  */
+  abort ();
+}
+
+/* Return the name of an V7M special register.  */
+static const char *
+psr_name (int regno)
+{
+  switch (regno)
+    {
+    case 0: return "APSR";
+    case 1: return "IAPSR";
+    case 2: return "EAPSR";
+    case 3: return "PSR";
+    case 5: return "IPSR";
+    case 6: return "EPSR";
+    case 7: return "IEPSR";
+    case 8: return "MSP";
+    case 9: return "PSP";
+    case 16: return "PRIMASK";
+    case 17: return "BASEPRI";
+    case 18: return "BASEPRI_MASK";
+    case 19: return "FAULTMASK";
+    case 20: return "CONTROL";
+    default: return "<unknown>";
+    }
+}
+
+/* Print one 32-bit Thumb instruction from PC on INFO->STREAM.  */
+
+static void
+print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
+{
+  const struct opcode32 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  if (print_insn_coprocessor (pc, info, given, TRUE))
+    return;
+
+  if (print_insn_neon (info, given, TRUE))
+    return;
+
+  for (insn = thumb32_opcodes; insn->assembler; insn++)
+    if ((given & insn->mask) == insn->value)
+      {
+	const char *c = insn->assembler;
+	for (; *c; c++)
+	  {
+	    if (*c != '%')
+	      {
+		func (stream, "%c", *c);
+		continue;
+	      }
+
+	    switch (*++c)
+	      {
+	      case '%':
+		func (stream, "%%");
+		break;
+
+	      case 'c':
+		if (ifthen_state)
+		  func (stream, "%s", arm_conditional[IFTHEN_COND]);
+		break;
+
+	      case 'x':
+		if (ifthen_next_state)
+		  func (stream, "\t; unpredictable branch in IT block\n");
+		break;
+
+	      case 'X':
+		if (ifthen_state)
+		  func (stream, "\t; unpredictable <IT:%s>",
+			arm_conditional[IFTHEN_COND]);
+		break;
+
+	      case 'I':
+		{
+		  unsigned int imm12 = 0;
+		  imm12 |= (given & 0x000000ffu);
+		  imm12 |= (given & 0x00007000u) >> 4;
+		  imm12 |= (given & 0x04000000u) >> 15;
+		  func (stream, "#%u\t; 0x%x", imm12, imm12);
+		}
+		break;
+
+	      case 'M':
+		{
+		  unsigned int bits = 0, imm, imm8, mod;
+		  bits |= (given & 0x000000ffu);
+		  bits |= (given & 0x00007000u) >> 4;
+		  bits |= (given & 0x04000000u) >> 15;
+		  imm8 = (bits & 0x0ff);
+		  mod = (bits & 0xf00) >> 8;
+		  switch (mod)
+		    {
+		    case 0: imm = imm8; break;
+		    case 1: imm = ((imm8<<16) | imm8); break;
+		    case 2: imm = ((imm8<<24) | (imm8 << 8)); break;
+		    case 3: imm = ((imm8<<24) | (imm8 << 16) | (imm8 << 8) | imm8); break;
+		    default:
+		      mod  = (bits & 0xf80) >> 7;
+		      imm8 = (bits & 0x07f) | 0x80;
+		      imm  = (((imm8 << (32 - mod)) | (imm8 >> mod)) & 0xffffffff);
+		    }
+		  func (stream, "#%u\t; 0x%x", imm, imm);
+		}
+		break;
+
+	      case 'J':
+		{
+		  unsigned int imm = 0;
+		  imm |= (given & 0x000000ffu);
+		  imm |= (given & 0x00007000u) >> 4;
+		  imm |= (given & 0x04000000u) >> 15;
+		  imm |= (given & 0x000f0000u) >> 4;
+		  func (stream, "#%u\t; 0x%x", imm, imm);
+		}
+		break;
+
+	      case 'K':
+		{
+		  unsigned int imm = 0;
+		  imm |= (given & 0x000f0000u) >> 16;
+		  imm |= (given & 0x00000ff0u) >> 0;
+		  imm |= (given & 0x0000000fu) << 12;
+		  func (stream, "#%u\t; 0x%x", imm, imm);
+		}
+		break;
+
+	      case 'S':
+		{
+		  unsigned int reg = (given & 0x0000000fu);
+		  unsigned int stp = (given & 0x00000030u) >> 4;
+		  unsigned int imm = 0;
+		  imm |= (given & 0x000000c0u) >> 6;
+		  imm |= (given & 0x00007000u) >> 10;
+
+		  func (stream, "%s", arm_regnames[reg]);
+		  switch (stp)
+		    {
+		    case 0:
+		      if (imm > 0)
+			func (stream, ", lsl #%u", imm);
+		      break;
+
+		    case 1:
+		      if (imm == 0)
+			imm = 32;
+		      func (stream, ", lsr #%u", imm);
+		      break;
+
+		    case 2:
+		      if (imm == 0)
+			imm = 32;
+		      func (stream, ", asr #%u", imm);
+		      break;
+
+		    case 3:
+		      if (imm == 0)
+			func (stream, ", rrx");
+		      else
+			func (stream, ", ror #%u", imm);
+		    }
+		}
+		break;
+
+	      case 'a':
+		{
+		  unsigned int Rn  = (given & 0x000f0000) >> 16;
+		  unsigned int U   = (given & 0x00800000) >> 23;
+		  unsigned int op  = (given & 0x00000f00) >> 8;
+		  unsigned int i12 = (given & 0x00000fff);
+		  unsigned int i8  = (given & 0x000000ff);
+		  bfd_boolean writeback = FALSE, postind = FALSE;
+		  int offset = 0;
+
+		  func (stream, "[%s", arm_regnames[Rn]);
+		  if (U) /* 12-bit positive immediate offset */
+		    offset = i12;
+		  else if (Rn == 15) /* 12-bit negative immediate offset */
+		    offset = -(int)i12;
+		  else if (op == 0x0) /* shifted register offset */
+		    {
+		      unsigned int Rm = (i8 & 0x0f);
+		      unsigned int sh = (i8 & 0x30) >> 4;
+		      func (stream, ", %s", arm_regnames[Rm]);
+		      if (sh)
+			func (stream, ", lsl #%u", sh);
+		      func (stream, "]");
+		      break;
+		    }
+		  else switch (op)
+		    {
+		    case 0xE:  /* 8-bit positive immediate offset */
+		      offset = i8;
+		      break;
+
+		    case 0xC:  /* 8-bit negative immediate offset */
+		      offset = -i8;
+		      break;
+
+		    case 0xF:  /* 8-bit + preindex with wb */
+		      offset = i8;
+		      writeback = TRUE;
+		      break;
+
+		    case 0xD:  /* 8-bit - preindex with wb */
+		      offset = -i8;
+		      writeback = TRUE;
+		      break;
+
+		    case 0xB:  /* 8-bit + postindex */
+		      offset = i8;
+		      postind = TRUE;
+		      break;
+
+		    case 0x9:  /* 8-bit - postindex */
+		      offset = -i8;
+		      postind = TRUE;
+		      break;
+
+		    default:
+		      func (stream, ", <undefined>]");
+		      goto skip;
+		    }
+
+		  if (postind)
+		    func (stream, "], #%d", offset);
+		  else
+		    {
+		      if (offset)
+			func (stream, ", #%d", offset);
+		      func (stream, writeback ? "]!" : "]");
+		    }
+
+		  if (Rn == 15)
+		    {
+		      func (stream, "\t; ");
+		      info->print_address_func (((pc + 4) & ~3) + offset, info);
+		    }
+		}
+	      skip:
+		break;
+
+	      case 'A':
+		{
+		  unsigned int P   = (given & 0x01000000) >> 24;
+		  unsigned int U   = (given & 0x00800000) >> 23;
+		  unsigned int W   = (given & 0x00400000) >> 21;
+		  unsigned int Rn  = (given & 0x000f0000) >> 16;
+		  unsigned int off = (given & 0x000000ff);
+
+		  func (stream, "[%s", arm_regnames[Rn]);
+		  if (P)
+		    {
+		      if (off || !U)
+			func (stream, ", #%c%u", U ? '+' : '-', off * 4);
+		      func (stream, "]");
+		      if (W)
+			func (stream, "!");
+		    }
+		  else
+		    {
+		      func (stream, "], ");
+		      if (W)
+			func (stream, "#%c%u", U ? '+' : '-', off * 4);
+		      else
+			func (stream, "{%u}", off);
+		    }
+		}
+		break;
+
+	      case 'w':
+		{
+		  unsigned int Sbit = (given & 0x01000000) >> 24;
+		  unsigned int type = (given & 0x00600000) >> 21;
+		  switch (type)
+		    {
+		    case 0: func (stream, Sbit ? "sb" : "b"); break;
+		    case 1: func (stream, Sbit ? "sh" : "h"); break;
+		    case 2:
+		      if (Sbit)
+			func (stream, "??");
+		      break;
+		    case 3:
+		      func (stream, "??");
+		      break;
+		    }
+		}
+		break;
+
+	      case 'm':
+		{
+		  int started = 0;
+		  int reg;
+
+		  func (stream, "{");
+		  for (reg = 0; reg < 16; reg++)
+		    if ((given & (1 << reg)) != 0)
+		      {
+			if (started)
+			  func (stream, ", ");
+			started = 1;
+			func (stream, "%s", arm_regnames[reg]);
+		      }
+		  func (stream, "}");
+		}
+		break;
+
+	      case 'E':
+		{
+		  unsigned int msb = (given & 0x0000001f);
+		  unsigned int lsb = 0;
+		  lsb |= (given & 0x000000c0u) >> 6;
+		  lsb |= (given & 0x00007000u) >> 10;
+		  func (stream, "#%u, #%u", lsb, msb - lsb + 1);
+		}
+		break;
+
+	      case 'F':
+		{
+		  unsigned int width = (given & 0x0000001f) + 1;
+		  unsigned int lsb = 0;
+		  lsb |= (given & 0x000000c0u) >> 6;
+		  lsb |= (given & 0x00007000u) >> 10;
+		  func (stream, "#%u, #%u", lsb, width);
+		}
+		break;
+
+	      case 'b':
+		{
+		  unsigned int S = (given & 0x04000000u) >> 26;
+		  unsigned int J1 = (given & 0x00002000u) >> 13;
+		  unsigned int J2 = (given & 0x00000800u) >> 11;
+		  int offset = 0;
+
+		  offset |= !S << 20;
+		  offset |= J2 << 19;
+		  offset |= J1 << 18;
+		  offset |= (given & 0x003f0000) >> 4;
+		  offset |= (given & 0x000007ff) << 1;
+		  offset -= (1 << 20);
+
+		  info->print_address_func (pc + 4 + offset, info);
+		}
+		break;
+
+	      case 'B':
+		{
+		  unsigned int S = (given & 0x04000000u) >> 26;
+		  unsigned int I1 = (given & 0x00002000u) >> 13;
+		  unsigned int I2 = (given & 0x00000800u) >> 11;
+		  int offset = 0;
+
+		  offset |= !S << 24;
+		  offset |= !(I1 ^ S) << 23;
+		  offset |= !(I2 ^ S) << 22;
+		  offset |= (given & 0x03ff0000u) >> 4;
+		  offset |= (given & 0x000007ffu) << 1;
+		  offset -= (1 << 24);
+		  offset += pc + 4;
+
+		  /* BLX target addresses are always word aligned.  */
+		  if ((given & 0x00001000u) == 0)
+		      offset &= ~2u;
+
+		  info->print_address_func (offset, info);
+		}
+		break;
+
+	      case 's':
+		{
+		  unsigned int shift = 0;
+		  shift |= (given & 0x000000c0u) >> 6;
+		  shift |= (given & 0x00007000u) >> 10;
+		  if (given & 0x00200000u)
+		    func (stream, ", asr #%u", shift);
+		  else if (shift)
+		    func (stream, ", lsl #%u", shift);
+		  /* else print nothing - lsl #0 */
+		}
+		break;
+
+	      case 'R':
+		{
+		  unsigned int rot = (given & 0x00000030) >> 4;
+		  if (rot)
+		    func (stream, ", ror #%u", rot * 8);
+		}
+		break;
+
+	      case 'U':
+		switch (given & 0xf)
+		  {
+		  case 0xf: func(stream, "sy"); break;
+		  case 0x7: func(stream, "un"); break;
+		  case 0xe: func(stream, "st"); break;
+		  case 0x6: func(stream, "unst"); break;
+		  default:
+		    func(stream, "#%d", (int)given & 0xf);
+		    break;
+		  }
+		break;
+
+	      case 'C':
+		if ((given & 0xff) == 0)
+		  {
+		    func (stream, "%cPSR_", (given & 0x100000) ? 'S' : 'C');
+		    if (given & 0x800)
+		      func (stream, "f");
+		    if (given & 0x400)
+		      func (stream, "s");
+		    if (given & 0x200)
+		      func (stream, "x");
+		    if (given & 0x100)
+		      func (stream, "c");
+		  }
+		else
+		  {
+		    func (stream, psr_name (given & 0xff));
+		  }
+		break;
+
+	      case 'D':
+		if ((given & 0xff) == 0)
+		  func (stream, "%cPSR", (given & 0x100000) ? 'S' : 'C');
+		else
+		  func (stream, psr_name (given & 0xff));
+		break;
+
+	      case '0': case '1': case '2': case '3': case '4':
+	      case '5': case '6': case '7': case '8': case '9':
+		{
+		  int width;
+		  unsigned long val;
+
+		  c = arm_decode_bitfield (c, given, &val, &width);
+
+		  switch (*c)
+		    {
+		    case 'd': func (stream, "%lu", val); break;
+		    case 'W': func (stream, "%lu", val * 4); break;
+		    case 'r': func (stream, "%s", arm_regnames[val]); break;
+
+		    case 'c':
+		      func (stream, "%s", arm_conditional[val]);
+		      break;
+
+		    case '\'':
+		      c++;
+		      if (val == ((1ul << width) - 1))
+			func (stream, "%c", *c);
+		      break;
+
+		    case '`':
+		      c++;
+		      if (val == 0)
+			func (stream, "%c", *c);
+		      break;
+
+		    case '?':
+		      func (stream, "%c", c[(1 << width) - (int)val]);
+		      c += 1 << width;
+		      break;
+
+		    default:
+		      abort ();
+		    }
+		}
+		break;
+
+	      default:
+		abort ();
+	      }
+	  }
+	return;
+      }
+
+  /* No match.  */
+  abort ();
+}
+
+/* Print data bytes on INFO->STREAM.  */
+
+static void
+print_insn_data (bfd_vma pc ATTRIBUTE_UNUSED, struct disassemble_info *info,
+		 long given)
+{
+  switch (info->bytes_per_chunk)
+    {
+    case 1:
+      info->fprintf_func (info->stream, ".byte\t0x%02lx", given);
+      break;
+    case 2:
+      info->fprintf_func (info->stream, ".short\t0x%04lx", given);
+      break;
+    case 4:
+      info->fprintf_func (info->stream, ".word\t0x%08lx", given);
+      break;
+    default:
+      abort ();
+    }
+}
+
+/* Search back through the insn stream to determine if this instruction is
+   conditionally executed.  */
+static void
+find_ifthen_state (bfd_vma pc, struct disassemble_info *info,
+		   bfd_boolean little)
+{
+  unsigned char b[2];
+  unsigned int insn;
+  int status;
+  /* COUNT is twice the number of instructions seen.  It will be odd if we
+     just crossed an instruction boundary.  */
+  int count;
+  int it_count;
+  unsigned int seen_it;
+  bfd_vma addr;
+
+  ifthen_address = pc;
+  ifthen_state = 0;
+
+  addr = pc;
+  count = 1;
+  it_count = 0;
+  seen_it = 0;
+  /* Scan backwards looking for IT instructions, keeping track of where
+     instruction boundaries are.  We don't know if something is actually an
+     IT instruction until we find a definite instruction boundary.  */
+  for (;;)
+    {
+      if (addr == 0 || info->symbol_at_address_func(addr, info))
+	{
+	  /* A symbol must be on an instruction boundary, and will not
+	     be within an IT block.  */
+	  if (seen_it && (count & 1))
+	    break;
+
+	  return;
+	}
+      addr -= 2;
+      status = info->read_memory_func (addr, (bfd_byte *)b, 2, info);
+      if (status)
+	return;
+
+      if (little)
+	insn = (b[0]) | (b[1] << 8);
+      else
+	insn = (b[1]) | (b[0] << 8);
+      if (seen_it)
+	{
+	  if ((insn & 0xf800) < 0xe800)
+	    {
+	      /* Addr + 2 is an instruction boundary.  See if this matches
+	         the expected boundary based on the position of the last
+		 IT candidate.  */
+	      if (count & 1)
+		break;
+	      seen_it = 0;
+	    }
+	}
+      if ((insn & 0xff00) == 0xbf00 && (insn & 0xf) != 0)
+	{
+	  /* This could be an IT instruction.  */
+	  seen_it = insn;
+	  it_count = count >> 1;
+	}
+      if ((insn & 0xf800) >= 0xe800)
+	count++;
+      else
+	count = (count + 2) | 1;
+      /* IT blocks contain at most 4 instructions.  */
+      if (count >= 8 && !seen_it)
+	return;
+    }
+  /* We found an IT instruction.  */
+  ifthen_state = (seen_it & 0xe0) | ((seen_it << it_count) & 0x1f);
+  if ((ifthen_state & 0xf) == 0)
+    ifthen_state = 0;
+}
+
+/* NOTE: There are no checks in these routines that
+   the relevant number of data bytes exist.  */
+
+int
+print_insn_arm (bfd_vma pc, struct disassemble_info *info)
+{
+  unsigned char b[4];
+  long		given;
+  int           status;
+  int           is_thumb = FALSE;
+  int           is_data = FALSE;
+  unsigned int	size = 4;
+  void	 	(*printer) (bfd_vma, struct disassemble_info *, long);
+#if 0
+  bfd_boolean   found = FALSE;
+
+  if (info->disassembler_options)
+    {
+      parse_disassembler_options (info->disassembler_options);
+
+      /* To avoid repeated parsing of these options, we remove them here.  */
+      info->disassembler_options = NULL;
+    }
+
+  /* First check the full symtab for a mapping symbol, even if there
+     are no usable non-mapping symbols for this address.  */
+  if (info->symtab != NULL
+      && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour)
+    {
+      bfd_vma addr;
+      int n;
+      int last_sym = -1;
+      enum map_type type = MAP_ARM;
+
+      if (pc <= last_mapping_addr)
+	last_mapping_sym = -1;
+      is_thumb = (last_type == MAP_THUMB);
+      found = FALSE;
+      /* Start scanning at the start of the function, or wherever
+	 we finished last time.  */
+      n = info->symtab_pos + 1;
+      if (n < last_mapping_sym)
+	n = last_mapping_sym;
+
+      /* Scan up to the location being disassembled.  */
+      for (; n < info->symtab_size; n++)
+	{
+	  addr = bfd_asymbol_value (info->symtab[n]);
+	  if (addr > pc)
+	    break;
+	  if ((info->section == NULL
+	       || info->section == info->symtab[n]->section)
+	      && get_sym_code_type (info, n, &type))
+	    {
+	      last_sym = n;
+	      found = TRUE;
+	    }
+	}
+
+      if (!found)
+	{
+	  n = info->symtab_pos;
+	  if (n < last_mapping_sym - 1)
+	    n = last_mapping_sym - 1;
+
+	  /* No mapping symbol found at this address.  Look backwards
+	     for a preceeding one.  */
+	  for (; n >= 0; n--)
+	    {
+	      if (get_sym_code_type (info, n, &type))
+		{
+		  last_sym = n;
+		  found = TRUE;
+		  break;
+		}
+	    }
+	}
+
+      last_mapping_sym = last_sym;
+      last_type = type;
+      is_thumb = (last_type == MAP_THUMB);
+      is_data = (last_type == MAP_DATA);
+
+      /* Look a little bit ahead to see if we should print out
+	 two or four bytes of data.  If there's a symbol,
+	 mapping or otherwise, after two bytes then don't
+	 print more.  */
+      if (is_data)
+	{
+	  size = 4 - (pc & 3);
+	  for (n = last_sym + 1; n < info->symtab_size; n++)
+	    {
+	      addr = bfd_asymbol_value (info->symtab[n]);
+	      if (addr > pc)
+		{
+		  if (addr - pc < size)
+		    size = addr - pc;
+		  break;
+		}
+	    }
+	  /* If the next symbol is after three bytes, we need to
+	     print only part of the data, so that we can use either
+	     .byte or .short.  */
+	  if (size == 3)
+	    size = (pc & 1) ? 1 : 2;
+	}
+    }
+
+  if (info->symbols != NULL)
+    {
+      if (bfd_asymbol_flavour (*info->symbols) == bfd_target_coff_flavour)
+	{
+	  coff_symbol_type * cs;
+
+	  cs = coffsymbol (*info->symbols);
+	  is_thumb = (   cs->native->u.syment.n_sclass == C_THUMBEXT
+		      || cs->native->u.syment.n_sclass == C_THUMBSTAT
+		      || cs->native->u.syment.n_sclass == C_THUMBLABEL
+		      || cs->native->u.syment.n_sclass == C_THUMBEXTFUNC
+		      || cs->native->u.syment.n_sclass == C_THUMBSTATFUNC);
+	}
+      else if (bfd_asymbol_flavour (*info->symbols) == bfd_target_elf_flavour
+	       && !found)
+	{
+	  /* If no mapping symbol has been found then fall back to the type
+	     of the function symbol.  */
+	  elf_symbol_type *  es;
+	  unsigned int       type;
+
+	  es = *(elf_symbol_type **)(info->symbols);
+	  type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
+
+	  is_thumb = (type == STT_ARM_TFUNC) || (type == STT_ARM_16BIT);
+	}
+    }
+#else
+  int little;
+
+  little = (info->endian == BFD_ENDIAN_LITTLE);
+  is_thumb |= (pc & 1);
+  pc &= ~(bfd_vma)1;
+#endif
+
+  if (force_thumb)
+    is_thumb = TRUE;
+
+  info->bytes_per_line = 4;
+
+  if (is_data)
+    {
+      int i;
+
+      /* size was already set above.  */
+      info->bytes_per_chunk = size;
+      printer = print_insn_data;
+
+      status = info->read_memory_func (pc, (bfd_byte *)b, size, info);
+      given = 0;
+      if (little)
+	for (i = size - 1; i >= 0; i--)
+	  given = b[i] | (given << 8);
+      else
+	for (i = 0; i < (int) size; i++)
+	  given = b[i] | (given << 8);
+    }
+  else if (!is_thumb)
+    {
+      /* In ARM mode endianness is a straightforward issue: the instruction
+	 is four bytes long and is either ordered 0123 or 3210.  */
+      printer = print_insn_arm_internal;
+      info->bytes_per_chunk = 4;
+      size = 4;
+
+      status = info->read_memory_func (pc, (bfd_byte *)b, 4, info);
+      if (little)
+	given = (b[0]) | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
+      else
+	given = (b[3]) | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
+    }
+  else
+    {
+      /* In Thumb mode we have the additional wrinkle of two
+	 instruction lengths.  Fortunately, the bits that determine
+	 the length of the current instruction are always to be found
+	 in the first two bytes.  */
+      printer = print_insn_thumb16;
+      info->bytes_per_chunk = 2;
+      size = 2;
+
+      status = info->read_memory_func (pc, (bfd_byte *)b, 2, info);
+      if (little)
+	given = (b[0]) | (b[1] << 8);
+      else
+	given = (b[1]) | (b[0] << 8);
+
+      if (!status)
+	{
+	  /* These bit patterns signal a four-byte Thumb
+	     instruction.  */
+	  if ((given & 0xF800) == 0xF800
+	      || (given & 0xF800) == 0xF000
+	      || (given & 0xF800) == 0xE800)
+	    {
+	      status = info->read_memory_func (pc + 2, (bfd_byte *)b, 2, info);
+	      if (little)
+		given = (b[0]) | (b[1] << 8) | (given << 16);
+	      else
+		given = (b[1]) | (b[0] << 8) | (given << 16);
+
+	      printer = print_insn_thumb32;
+	      size = 4;
+	    }
+	}
+
+      if (ifthen_address != pc)
+	find_ifthen_state(pc, info, little);
+
+      if (ifthen_state)
+	{
+	  if ((ifthen_state & 0xf) == 0x8)
+	    ifthen_next_state = 0;
+	  else
+	    ifthen_next_state = (ifthen_state & 0xe0)
+				| ((ifthen_state & 0xf) << 1);
+	}
+    }
+
+  if (status)
+    {
+      info->memory_error_func (status, pc, info);
+      return -1;
+    }
+  if (info->flags & INSN_HAS_RELOC)
+    /* If the instruction has a reloc associated with it, then
+       the offset field in the instruction will actually be the
+       addend for the reloc.  (We are using REL type relocs).
+       In such cases, we can ignore the pc when computing
+       addresses, since the addend is not currently pc-relative.  */
+    pc = 0;
+
+  printer (pc, info, given);
+
+  if (is_thumb)
+    {
+      ifthen_state = ifthen_next_state;
+      ifthen_address += size;
+    }
+  return size;
+}
+
+void
+print_arm_disassembler_options (FILE *stream)
+{
+  int i;
+
+  fprintf (stream, _("\n\
+The following ARM specific disassembler options are supported for use with\n\
+the -M switch:\n"));
+
+  for (i = NUM_ARM_REGNAMES; i--;)
+    fprintf (stream, "  reg-names-%s %*c%s\n",
+	     regnames[i].name,
+	     (int)(14 - strlen (regnames[i].name)), ' ',
+	     regnames[i].description);
+
+  fprintf (stream, "  force-thumb              Assume all insns are Thumb insns\n");
+  fprintf (stream, "  no-force-thumb           Examine preceeding label to determine an insn's type\n\n");
+}
diff --git a/arm-semi.c b/arm-semi.c
new file mode 100644
index 0000000..cd77d1d
--- /dev/null
+++ b/arm-semi.c
@@ -0,0 +1,469 @@
+/*
+ *  Arm "Angel" semihosting syscalls
+ *
+ *  Copyright (c) 2005, 2007 CodeSourcery.
+ *  Written by Paul Brook.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "cpu.h"
+#ifdef CONFIG_USER_ONLY
+#include "qemu.h"
+
+#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
+#else
+#include "qemu-common.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#endif
+
+#define SYS_OPEN        0x01
+#define SYS_CLOSE       0x02
+#define SYS_WRITEC      0x03
+#define SYS_WRITE0      0x04
+#define SYS_WRITE       0x05
+#define SYS_READ        0x06
+#define SYS_READC       0x07
+#define SYS_ISTTY       0x09
+#define SYS_SEEK        0x0a
+#define SYS_FLEN        0x0c
+#define SYS_TMPNAM      0x0d
+#define SYS_REMOVE      0x0e
+#define SYS_RENAME      0x0f
+#define SYS_CLOCK       0x10
+#define SYS_TIME        0x11
+#define SYS_SYSTEM      0x12
+#define SYS_ERRNO       0x13
+#define SYS_GET_CMDLINE 0x15
+#define SYS_HEAPINFO    0x16
+#define SYS_EXIT        0x18
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#define GDB_O_RDONLY  0x000
+#define GDB_O_WRONLY  0x001
+#define GDB_O_RDWR    0x002
+#define GDB_O_APPEND  0x008
+#define GDB_O_CREAT   0x200
+#define GDB_O_TRUNC   0x400
+#define GDB_O_BINARY  0
+
+static int gdb_open_modeflags[12] = {
+    GDB_O_RDONLY,
+    GDB_O_RDONLY | GDB_O_BINARY,
+    GDB_O_RDWR,
+    GDB_O_RDWR | GDB_O_BINARY,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_TRUNC | GDB_O_BINARY,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND,
+    GDB_O_WRONLY | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND,
+    GDB_O_RDWR | GDB_O_CREAT | GDB_O_APPEND | GDB_O_BINARY
+};
+
+static int open_modeflags[12] = {
+    O_RDONLY,
+    O_RDONLY | O_BINARY,
+    O_RDWR,
+    O_RDWR | O_BINARY,
+    O_WRONLY | O_CREAT | O_TRUNC,
+    O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+    O_RDWR | O_CREAT | O_TRUNC,
+    O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
+    O_WRONLY | O_CREAT | O_APPEND,
+    O_WRONLY | O_CREAT | O_APPEND | O_BINARY,
+    O_RDWR | O_CREAT | O_APPEND,
+    O_RDWR | O_CREAT | O_APPEND | O_BINARY
+};
+
+#ifdef CONFIG_USER_ONLY
+static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
+{
+    if (code == (uint32_t)-1)
+        ts->swi_errno = errno;
+    return code;
+}
+#else
+static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
+{
+    return code;
+}
+
+#include "softmmu-semi.h"
+#endif
+
+static target_ulong arm_semi_syscall_len;
+
+#if !defined(CONFIG_USER_ONLY)
+static target_ulong syscall_err;
+#endif
+
+static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+#ifdef CONFIG_USER_ONLY
+    TaskState *ts = env->opaque;
+#endif
+
+    if (ret == (target_ulong)-1) {
+#ifdef CONFIG_USER_ONLY
+        ts->swi_errno = err;
+#else
+	syscall_err = err;
+#endif
+        env->regs[0] = ret;
+    } else {
+        /* Fixup syscalls that use nonstardard return conventions.  */
+        switch (env->regs[0]) {
+        case SYS_WRITE:
+        case SYS_READ:
+            env->regs[0] = arm_semi_syscall_len - ret;
+            break;
+        case SYS_SEEK:
+            env->regs[0] = 0;
+            break;
+        default:
+            env->regs[0] = ret;
+            break;
+        }
+    }
+}
+
+static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
+{
+    /* The size is always stored in big-endian order, extract
+       the value. We assume the size always fit in 32 bits.  */
+    uint32_t size;
+    cpu_memory_rw_debug(env, env->regs[13]-64+32, (uint8_t *)&size, 4, 0);
+    env->regs[0] = be32_to_cpu(size);
+#ifdef CONFIG_USER_ONLY
+    ((TaskState *)env->opaque)->swi_errno = err;
+#else
+    syscall_err = err;
+#endif
+}
+
+#define ARG(n)					\
+({						\
+    target_ulong __arg;				\
+    /* FIXME - handle get_user() failure */	\
+    get_user_ual(__arg, args + (n) * 4);	\
+    __arg;					\
+})
+#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
+uint32_t do_arm_semihosting(CPUState *env)
+{
+    target_ulong args;
+    char * s;
+    int nr;
+    uint32_t ret;
+    uint32_t len;
+#ifdef CONFIG_USER_ONLY
+    TaskState *ts = env->opaque;
+#else
+    CPUState *ts = env;
+#endif
+
+    nr = env->regs[0];
+    args = env->regs[1];
+    switch (nr) {
+    case SYS_OPEN:
+        if (!(s = lock_user_string(ARG(0))))
+            /* FIXME - should this error code be -TARGET_EFAULT ? */
+            return (uint32_t)-1;
+        if (ARG(1) >= 12)
+            return (uint32_t)-1;
+        if (strcmp(s, ":tt") == 0) {
+            if (ARG(1) < 4)
+                return STDIN_FILENO;
+            else
+                return STDOUT_FILENO;
+        }
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "open,%s,%x,1a4", ARG(0),
+			   (int)ARG(2)+1, gdb_open_modeflags[ARG(1)]);
+            return env->regs[0];
+        } else {
+            ret = set_swi_errno(ts, open(s, open_modeflags[ARG(1)], 0644));
+        }
+        unlock_user(s, ARG(0), 0);
+        return ret;
+    case SYS_CLOSE:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
+            return env->regs[0];
+        } else {
+            return set_swi_errno(ts, close(ARG(0)));
+        }
+    case SYS_WRITEC:
+        {
+          char c;
+
+          if (get_user_u8(c, args))
+              /* FIXME - should this error code be -TARGET_EFAULT ? */
+              return (uint32_t)-1;
+          /* Write to debug console.  stderr is near enough.  */
+          if (use_gdb_syscalls()) {
+                gdb_do_syscall(arm_semi_cb, "write,2,%x,1", args);
+                return env->regs[0];
+          } else {
+                return write(STDERR_FILENO, &c, 1);
+          }
+        }
+    case SYS_WRITE0:
+        if (!(s = lock_user_string(args)))
+            /* FIXME - should this error code be -TARGET_EFAULT ? */
+            return (uint32_t)-1;
+        len = strlen(s);
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "write,2,%x,%x\n", args, len);
+            ret = env->regs[0];
+        } else {
+            ret = write(STDERR_FILENO, s, len);
+        }
+        unlock_user(s, args, 0);
+        return ret;
+    case SYS_WRITE:
+        len = ARG(2);
+        if (use_gdb_syscalls()) {
+            arm_semi_syscall_len = len;
+            gdb_do_syscall(arm_semi_cb, "write,%x,%x,%x", ARG(0), ARG(1), len);
+            return env->regs[0];
+        } else {
+            if (!(s = lock_user(VERIFY_READ, ARG(1), len, 1)))
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+            ret = set_swi_errno(ts, write(ARG(0), s, len));
+            unlock_user(s, ARG(1), 0);
+            if (ret == (uint32_t)-1)
+                return -1;
+            return len - ret;
+        }
+    case SYS_READ:
+        len = ARG(2);
+        if (use_gdb_syscalls()) {
+            arm_semi_syscall_len = len;
+            gdb_do_syscall(arm_semi_cb, "read,%x,%x,%x", ARG(0), ARG(1), len);
+            return env->regs[0];
+        } else {
+            if (!(s = lock_user(VERIFY_WRITE, ARG(1), len, 0)))
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+            do
+              ret = set_swi_errno(ts, read(ARG(0), s, len));
+            while (ret == -1 && errno == EINTR);
+            unlock_user(s, ARG(1), len);
+            if (ret == (uint32_t)-1)
+                return -1;
+            return len - ret;
+        }
+    case SYS_READC:
+       /* XXX: Read from debug cosole. Not implemented.  */
+        return 0;
+    case SYS_ISTTY:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
+            return env->regs[0];
+        } else {
+            return isatty(ARG(0));
+        }
+    case SYS_SEEK:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
+            return env->regs[0];
+        } else {
+            ret = set_swi_errno(ts, lseek(ARG(0), ARG(1), SEEK_SET));
+            if (ret == (uint32_t)-1)
+              return -1;
+            return 0;
+        }
+    case SYS_FLEN:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
+			   ARG(0), env->regs[13]-64);
+            return env->regs[0];
+        } else {
+            struct stat buf;
+            ret = set_swi_errno(ts, fstat(ARG(0), &buf));
+            if (ret == (uint32_t)-1)
+                return -1;
+            return buf.st_size;
+        }
+    case SYS_TMPNAM:
+        /* XXX: Not implemented.  */
+        return -1;
+    case SYS_REMOVE:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
+            ret = env->regs[0];
+        } else {
+            if (!(s = lock_user_string(ARG(0))))
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+            ret =  set_swi_errno(ts, remove(s));
+            unlock_user(s, ARG(0), 0);
+        }
+        return ret;
+    case SYS_RENAME:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
+                           ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
+            return env->regs[0];
+        } else {
+            char *s2;
+            s = lock_user_string(ARG(0));
+            s2 = lock_user_string(ARG(2));
+            if (!s || !s2)
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                ret = (uint32_t)-1;
+            else
+                ret = set_swi_errno(ts, rename(s, s2));
+            if (s2)
+                unlock_user(s2, ARG(2), 0);
+            if (s)
+                unlock_user(s, ARG(0), 0);
+            return ret;
+        }
+    case SYS_CLOCK:
+        return clock() / (CLOCKS_PER_SEC / 100);
+    case SYS_TIME:
+        return set_swi_errno(ts, time(NULL));
+    case SYS_SYSTEM:
+        if (use_gdb_syscalls()) {
+            gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
+            return env->regs[0];
+        } else {
+            if (!(s = lock_user_string(ARG(0))))
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+            ret = set_swi_errno(ts, system(s));
+            unlock_user(s, ARG(0), 0);
+            return ret;
+        }
+    case SYS_ERRNO:
+#ifdef CONFIG_USER_ONLY
+        return ts->swi_errno;
+#else
+        return syscall_err;
+#endif
+    case SYS_GET_CMDLINE:
+#ifdef CONFIG_USER_ONLY
+        /* Build a commandline from the original argv.  */
+        {
+            char **arg = ts->info->host_argv;
+            int len = ARG(1);
+            /* lock the buffer on the ARM side */
+            char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
+
+            if (!cmdline_buffer)
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+
+            s = cmdline_buffer;
+            while (*arg && len > 2) {
+                int n = strlen(*arg);
+
+                if (s != cmdline_buffer) {
+                    *(s++) = ' ';
+                    len--;
+                }
+                if (n >= len)
+                    n = len - 1;
+                memcpy(s, *arg, n);
+                s += n;
+                len -= n;
+                arg++;
+            }
+            /* Null terminate the string.  */
+            *s = 0;
+            len = s - cmdline_buffer;
+
+            /* Unlock the buffer on the ARM side.  */
+            unlock_user(cmdline_buffer, ARG(0), len);
+
+            /* Adjust the commandline length argument.  */
+            SET_ARG(1, len);
+
+            /* Return success if commandline fit into buffer.  */
+            return *arg ? -1 : 0;
+        }
+#else
+      return -1;
+#endif
+    case SYS_HEAPINFO:
+        {
+            uint32_t *ptr;
+            uint32_t limit;
+
+#ifdef CONFIG_USER_ONLY
+            /* Some C libraries assume the heap immediately follows .bss, so
+               allocate it using sbrk.  */
+            if (!ts->heap_limit) {
+                long ret;
+
+                ts->heap_base = do_brk(0);
+                limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
+                /* Try a big heap, and reduce the size if that fails.  */
+                for (;;) {
+                    ret = do_brk(limit);
+                    if (ret != -1)
+                        break;
+                    limit = (ts->heap_base >> 1) + (limit >> 1);
+                }
+                ts->heap_limit = limit;
+            }
+
+            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+            ptr[0] = tswap32(ts->heap_base);
+            ptr[1] = tswap32(ts->heap_limit);
+            ptr[2] = tswap32(ts->stack_base);
+            ptr[3] = tswap32(0); /* Stack limit.  */
+            unlock_user(ptr, ARG(0), 16);
+#else
+            limit = ram_size;
+            if (!(ptr = lock_user(VERIFY_WRITE, ARG(0), 16, 0)))
+                /* FIXME - should this error code be -TARGET_EFAULT ? */
+                return (uint32_t)-1;
+            /* TODO: Make this use the limit of the loaded application.  */
+            ptr[0] = tswap32(limit / 2);
+            ptr[1] = tswap32(limit);
+            ptr[2] = tswap32(limit); /* Stack base */
+            ptr[3] = tswap32(0); /* Stack limit.  */
+            unlock_user(ptr, ARG(0), 16);
+#endif
+            return 0;
+        }
+    case SYS_EXIT:
+        exit(0);
+    default:
+        fprintf(stderr, "qemu: Unsupported SemiHosting SWI 0x%02x\n", nr);
+        cpu_dump_state(env, stderr, fprintf, 0);
+        abort();
+    }
+}
diff --git a/arm.ld b/arm.ld
new file mode 100644
index 0000000..93285d6
--- /dev/null
+++ b/arm.ld
@@ -0,0 +1,154 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm",
+	      "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp     : { *(.interp) 	}
+  .hash          : { *(.hash)		}
+  .dynsym        : { *(.dynsym)		}
+  .dynstr        : { *(.dynstr)		}
+  .gnu.version   : { *(.gnu.version)	}
+  .gnu.version_d   : { *(.gnu.version_d)	}
+  .gnu.version_r   : { *(.gnu.version_r)	}
+  .rel.text      :
+    { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+  .rela.text     :
+    { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+  .rel.data      :
+    { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+  .rela.data     :
+    { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+  .rel.rodata    :
+    { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+  .rela.rodata   :
+    { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+  .rel.got       : { *(.rel.got)		}
+  .rela.got      : { *(.rela.got)		}
+  .rel.ctors     : { *(.rel.ctors)	}
+  .rela.ctors    : { *(.rela.ctors)	}
+  .rel.dtors     : { *(.rel.dtors)	}
+  .rela.dtors    : { *(.rela.dtors)	}
+  .rel.init      : { *(.rel.init)	}
+  .rela.init     : { *(.rela.init)	}
+  .rel.fini      : { *(.rel.fini)	}
+  .rela.fini     : { *(.rela.fini)	}
+  .rel.bss       : { *(.rel.bss)		}
+  .rela.bss      : { *(.rela.bss)		}
+  .rel.plt       : { *(.rel.plt)		}
+  .rela.plt      : { *(.rela.plt)		}
+  .init          : { *(.init)	} =0x47ff041f
+  .text      :
+  {
+    *(.text)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.gnu.linkonce.t*)
+  } =0x47ff041f
+  _etext = .;
+  PROVIDE (etext = .);
+  .fini      : { *(.fini)    } =0x47ff041f
+  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
+  .rodata1   : { *(.rodata1) }
+  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
+   __exidx_start = .;
+  .ARM.exidx   : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
+   __exidx_end = .;
+  .reginfo : { *(.reginfo) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x100000) + (. & (0x100000 - 1));
+  .data    :
+  {
+    *(.gen_code)
+    *(.data)
+    *(.gnu.linkonce.d*)
+    CONSTRUCTORS
+  }
+  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .data1   : { *(.data1) }
+  .preinit_array     :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  }
+  .init_array     :
+  {
+     PROVIDE_HIDDEN (__init_array_start = .);
+     KEEP (*(SORT(.init_array.*)))
+     KEEP (*(.init_array))
+     PROVIDE_HIDDEN (__init_array_end = .);
+  }
+  .fini_array     :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(.fini_array))
+    KEEP (*(SORT(.fini_array.*)))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  }
+  .ctors         :
+  {
+    *(.ctors)
+  }
+  .dtors         :
+  {
+    *(.dtors)
+  }
+  .plt      : { *(.plt)	}
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  _edata  =  .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+  }
+  _end = . ;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* These must appear regardless of  .  */
+}
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
new file mode 100644
index 0000000..1cc4d6e
--- /dev/null
+++ b/audio/alsaaudio.c
@@ -0,0 +1,1067 @@
+/*
+ * QEMU ALSA audio driver
+ *
+ * Copyright (c) 2008 The Android Open Source Project
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <alsa/asoundlib.h>
+#include "audio.h"
+
+#define AUDIO_CAP "alsa"
+#include "audio_int.h"
+#include <dlfcn.h>
+#include <pthread.h>
+#include "qemu_debug.h"
+
+#define  DEBUG  1
+
+#if DEBUG
+#  include <stdio.h>
+#  define D(...)  VERBOSE_PRINT(audio,__VA_ARGS__)
+#  define D_ACTIVE  VERBOSE_CHECK(audio)
+#  define O(...)  VERBOSE_PRINT(audioout,__VA_ARGS__)
+#  define I(...)  VERBOSE_PRINT(audioin,__VA_ARGS__)
+#else
+#  define D(...)  ((void)0)
+#  define D_ACTIVE  0
+#  define O(...)  ((void)0)
+#  define I(...)  ((void)0)
+#endif
+
+
+#define  STRINGIFY_(x)  #x
+#define  STRINGIFY(x)   STRINGIFY_(x)
+
+#define  DYNLINK_FUNCTIONS   \
+    DYNLINK_FUNC(size_t,snd_pcm_sw_params_sizeof,(void))    \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_current,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \
+    DYNLINK_FUNC(int,snd_pcm_sw_params_set_start_threshold,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val))  \
+    DYNLINK_FUNC(int,snd_pcm_sw_params,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params))  \
+    DYNLINK_FUNC(int,snd_pcm_sw_params_current,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)) \
+    DYNLINK_FUNC(size_t,snd_pcm_hw_params_sizeof,(void))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_any,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_access,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_get_format,(const snd_pcm_hw_params_t *params, snd_pcm_format_t *val)) \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_format,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_rate_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_channels_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_buffer_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_get_buffer_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val))  \
+    DYNLINK_FUNC(int,snd_pcm_prepare,(snd_pcm_t *pcm))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_get_period_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_get_period_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_period_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_get_buffer_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_buffer_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_period_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir))  \
+    DYNLINK_FUNC(snd_pcm_sframes_t,snd_pcm_avail_update,(snd_pcm_t *pcm)) \
+    DYNLINK_FUNC(int,snd_pcm_drop,(snd_pcm_t *pcm))  \
+    DYNLINK_FUNC(snd_pcm_sframes_t,snd_pcm_writei,(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size))  \
+    DYNLINK_FUNC(snd_pcm_sframes_t,snd_pcm_readi,(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size))  \
+    DYNLINK_FUNC(snd_pcm_state_t,snd_pcm_state,(snd_pcm_t *pcm))  \
+    DYNLINK_FUNC(const char*,snd_strerror,(int errnum)) \
+    DYNLINK_FUNC(int,snd_pcm_open,(snd_pcm_t **pcm, const char *name,snd_pcm_stream_t stream, int mode)) \
+    DYNLINK_FUNC(int,snd_pcm_close,(snd_pcm_t *pcm))  \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_buffer_size_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_set_period_size_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir)) \
+    DYNLINK_FUNC(int,snd_pcm_hw_params_get_format,(const snd_pcm_hw_params_t *params, snd_pcm_format_t *val)) \
+
+#define DYNLINK_FUNCTIONS_INIT \
+    alsa_dynlink_init
+
+#include "dynlink.h"
+
+/* these are inlined functions in the original headers */
+#define FF_snd_pcm_hw_params_alloca(ptr) \
+    do { assert(ptr); *ptr = (snd_pcm_hw_params_t *) alloca(FF(snd_pcm_hw_params_sizeof)()); memset(*ptr, 0, FF(snd_pcm_hw_params_sizeof)()); } while (0)
+
+#define FF_snd_pcm_sw_params_alloca(ptr) \
+    do { assert(ptr); *ptr = (snd_pcm_sw_params_t *) alloca(FF(snd_pcm_sw_params_sizeof)()); memset(*ptr, 0, FF(snd_pcm_sw_params_sizeof)()); } while (0)
+
+static void*  alsa_lib;
+
+typedef struct ALSAVoiceOut {
+    HWVoiceOut hw;
+    void *pcm_buf;
+    snd_pcm_t *handle;
+} ALSAVoiceOut;
+
+typedef struct ALSAVoiceIn {
+    HWVoiceIn hw;
+    snd_pcm_t *handle;
+    void *pcm_buf;
+} ALSAVoiceIn;
+
+static struct {
+    int size_in_usec_in;
+    int size_in_usec_out;
+    const char *pcm_name_in;
+    const char *pcm_name_out;
+    unsigned int buffer_size_in;
+    unsigned int period_size_in;
+    unsigned int buffer_size_out;
+    unsigned int period_size_out;
+    unsigned int threshold;
+
+    int buffer_size_in_overridden;
+    int period_size_in_overridden;
+
+    int buffer_size_out_overridden;
+    int period_size_out_overridden;
+    int verbose;
+} conf = {
+    .buffer_size_out = 1024,
+    .pcm_name_out = "default",
+    .pcm_name_in = "default",
+};
+
+struct alsa_params_req {
+    int freq;
+    snd_pcm_format_t fmt;
+    int nchannels;
+    int size_in_usec;
+    int override_mask;
+    unsigned int buffer_size;
+    unsigned int period_size;
+};
+
+struct alsa_params_obt {
+    int freq;
+    audfmt_e fmt;
+    int endianness;
+    int nchannels;
+    snd_pcm_uframes_t samples;
+};
+
+static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", FF(snd_strerror) (err));
+}
+
+static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
+    int err,
+    const char *typ,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", FF(snd_strerror) (err));
+}
+
+static void alsa_anal_close (snd_pcm_t **handlep)
+{
+    int err = FF(snd_pcm_close) (*handlep);
+    if (err) {
+        alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
+    }
+    *handlep = NULL;
+}
+
+static int alsa_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt)
+{
+    switch (fmt) {
+    case AUD_FMT_S8:
+        return SND_PCM_FORMAT_S8;
+
+    case AUD_FMT_U8:
+        return SND_PCM_FORMAT_U8;
+
+    case AUD_FMT_S16:
+        return SND_PCM_FORMAT_S16_LE;
+
+    case AUD_FMT_U16:
+        return SND_PCM_FORMAT_U16_LE;
+
+    case AUD_FMT_S32:
+        return SND_PCM_FORMAT_S32_LE;
+
+    case AUD_FMT_U32:
+        return SND_PCM_FORMAT_U32_LE;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+        abort ();
+#endif
+        return SND_PCM_FORMAT_U8;
+    }
+}
+
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+                           int *endianness)
+{
+    switch (alsafmt) {
+    case SND_PCM_FORMAT_S8:
+        *endianness = 0;
+        *fmt = AUD_FMT_S8;
+        break;
+
+    case SND_PCM_FORMAT_U8:
+        *endianness = 0;
+        *fmt = AUD_FMT_U8;
+        break;
+
+    case SND_PCM_FORMAT_S16_LE:
+        *endianness = 0;
+        *fmt = AUD_FMT_S16;
+        break;
+
+    case SND_PCM_FORMAT_U16_LE:
+        *endianness = 0;
+        *fmt = AUD_FMT_U16;
+        break;
+
+    case SND_PCM_FORMAT_S16_BE:
+        *endianness = 1;
+        *fmt = AUD_FMT_S16;
+        break;
+
+    case SND_PCM_FORMAT_U16_BE:
+        *endianness = 1;
+        *fmt = AUD_FMT_U16;
+        break;
+
+    case SND_PCM_FORMAT_S32_LE:
+        *endianness = 0;
+        *fmt = AUD_FMT_S32;
+        break;
+
+    case SND_PCM_FORMAT_U32_LE:
+        *endianness = 0;
+        *fmt = AUD_FMT_U32;
+        break;
+
+    case SND_PCM_FORMAT_S32_BE:
+        *endianness = 1;
+        *fmt = AUD_FMT_S32;
+        break;
+
+    case SND_PCM_FORMAT_U32_BE:
+        *endianness = 1;
+        *fmt = AUD_FMT_U32;
+        break;
+
+    default:
+        dolog ("Unrecognized audio format %d\n", alsafmt);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void alsa_dump_info (struct alsa_params_req *req,
+                            struct alsa_params_obt *obt)
+{
+    dolog ("parameter | requested value | obtained value\n");
+    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
+    dolog ("channels  |      %10d |     %10d\n",
+           req->nchannels, obt->nchannels);
+    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
+    dolog ("============================================\n");
+    dolog ("requested: buffer size %d period size %d\n",
+           req->buffer_size, req->period_size);
+    dolog ("obtained: samples %ld\n", obt->samples);
+}
+
+static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
+{
+    int err;
+    snd_pcm_sw_params_t *sw_params;
+
+    FF_snd_pcm_sw_params_alloca (&sw_params);
+
+    err = FF(snd_pcm_sw_params_current) (handle, sw_params);
+    if (err < 0) {
+        dolog ("Could not fully initialize DAC\n");
+        alsa_logerr (err, "Failed to get current software parameters\n");
+        return;
+    }
+
+    err = FF(snd_pcm_sw_params_set_start_threshold) (handle, sw_params, threshold);
+    if (err < 0) {
+        dolog ("Could not fully initialize DAC\n");
+        alsa_logerr (err, "Failed to set software threshold to %ld\n",
+                     threshold);
+        return;
+    }
+
+    err = FF(snd_pcm_sw_params) (handle, sw_params);
+    if (err < 0) {
+        dolog ("Could not fully initialize DAC\n");
+        alsa_logerr (err, "Failed to set software parameters\n");
+        return;
+    }
+}
+
+static int alsa_open (int in, struct alsa_params_req *req,
+                      struct alsa_params_obt *obt, snd_pcm_t **handlep)
+{
+    snd_pcm_t *handle;
+    snd_pcm_hw_params_t *hw_params;
+    int err;
+    int size_in_usec;
+    unsigned int freq, nchannels;
+    const char *pcm_name = in ? conf.pcm_name_in : conf.pcm_name_out;
+    snd_pcm_uframes_t obt_buffer_size;
+    const char *typ = in ? "ADC" : "DAC";
+    snd_pcm_format_t obtfmt;
+
+    freq = req->freq;
+    nchannels = req->nchannels;
+    size_in_usec = req->size_in_usec;
+
+    FF_snd_pcm_hw_params_alloca (&hw_params);
+
+    err = FF(snd_pcm_open) (
+        &handle,
+        pcm_name,
+        in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
+        SND_PCM_NONBLOCK
+        );
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to open `%s':\n", pcm_name);
+        return -1;
+    }
+
+    err = FF(snd_pcm_hw_params_any) (handle, hw_params);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
+        goto err;
+    }
+
+    err = FF(snd_pcm_hw_params_set_access) (
+        handle,
+        hw_params,
+        SND_PCM_ACCESS_RW_INTERLEAVED
+        );
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to set access type\n");
+        goto err;
+    }
+
+    err = FF(snd_pcm_hw_params_set_format) (handle, hw_params, req->fmt);
+    if (err < 0 && conf.verbose) {
+        alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
+        goto err;
+    }
+
+    err = FF(snd_pcm_hw_params_set_rate_near) (handle, hw_params, &freq, 0);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
+        goto err;
+    }
+
+    err = FF(snd_pcm_hw_params_set_channels_near) (
+        handle,
+        hw_params,
+        &nchannels
+        );
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
+                      req->nchannels);
+        goto err;
+    }
+
+    if (nchannels != 1 && nchannels != 2) {
+        alsa_logerr2 (err, typ,
+                      "Can not handle obtained number of channels %d\n",
+                      nchannels);
+        goto err;
+    }
+
+    if (req->buffer_size) {
+        unsigned long obt;
+
+        if (size_in_usec) {
+            int dir = 0;
+            unsigned int btime = req->buffer_size;
+
+            err = FF(snd_pcm_hw_params_set_buffer_time_near) (
+                handle,
+                hw_params,
+                &btime,
+                &dir
+                );
+            obt = btime;
+        }
+        else {
+            snd_pcm_uframes_t bsize = req->buffer_size;
+
+            err = FF(snd_pcm_hw_params_set_buffer_size_near) (
+                handle,
+                hw_params,
+                &bsize
+                );
+            obt = bsize;
+        }
+        if (err < 0) {
+            alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n",
+                          size_in_usec ? "time" : "size", req->buffer_size);
+            goto err;
+        }
+
+        if ((req->override_mask & 2) && (obt - req->buffer_size))
+            dolog ("Requested buffer %s %u was rejected, using %lu\n",
+                   size_in_usec ? "time" : "size", req->buffer_size, obt);
+    }
+
+    if (req->period_size) {
+        unsigned long obt;
+
+        if (size_in_usec) {
+            int dir = 0;
+            unsigned int ptime = req->period_size;
+
+            err = FF(snd_pcm_hw_params_set_period_time_near) (
+                handle,
+                hw_params,
+                &ptime,
+                &dir
+                );
+            obt = ptime;
+        }
+        else {
+            int dir = 0;
+            snd_pcm_uframes_t psize = req->period_size;
+
+            err = FF(snd_pcm_hw_params_set_period_size_near) (
+                handle,
+                hw_params,
+                &psize,
+                &dir
+                );
+            obt = psize;
+        }
+
+        if (err < 0) {
+            alsa_logerr2 (err, typ, "Failed to set period %s to %d\n",
+                          size_in_usec ? "time" : "size", req->period_size);
+            goto err;
+        }
+
+        if ((req->override_mask & 1) && (obt - req->period_size))
+            dolog ("Requested period %s %u was rejected, using %lu\n",
+                   size_in_usec ? "time" : "size", req->period_size, obt);
+    }
+
+    err = FF(snd_pcm_hw_params) (handle, hw_params);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
+        goto err;
+    }
+
+    err = FF(snd_pcm_hw_params_get_buffer_size) (hw_params, &obt_buffer_size);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to get buffer size\n");
+        goto err;
+    }
+
+    err = FF(snd_pcm_hw_params_get_format)(hw_params, &obtfmt);
+    err = FF(snd_pcm_hw_params_get_format) (hw_params, &obtfmt);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to get format\n");
+        goto err;
+    }
+
+    if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
+        dolog ("Invalid format was returned %d\n", obtfmt);
+        goto err;
+    }
+
+    err = FF(snd_pcm_prepare) (handle);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
+        goto err;
+    }
+
+    if (!in && conf.threshold) {
+        snd_pcm_uframes_t threshold;
+        int bytes_per_sec;
+
+        bytes_per_sec = freq << (nchannels == 2);
+
+        switch (obt->fmt) {
+        case AUD_FMT_S8:
+        case AUD_FMT_U8:
+            break;
+
+        case AUD_FMT_S16:
+        case AUD_FMT_U16:
+            bytes_per_sec <<= 1;
+            break;
+
+        case AUD_FMT_S32:
+        case AUD_FMT_U32:
+            bytes_per_sec <<= 2;
+            break;
+        }
+
+        threshold = (conf.threshold * bytes_per_sec) / 1000;
+        alsa_set_threshold (handle, threshold);
+    }
+
+    obt->nchannels = nchannels;
+    obt->freq = freq;
+    obt->samples = obt_buffer_size;
+
+    *handlep = handle;
+
+    if (conf.verbose &&
+        (obt->fmt != req->fmt ||
+         obt->nchannels != req->nchannels ||
+         obt->freq != req->freq)) {
+        dolog ("Audio paramters for %s\n", typ);
+        alsa_dump_info (req, obt);
+    }
+
+#ifdef DEBUG
+    alsa_dump_info (req, obt);
+#endif
+    return 0;
+
+ err:
+    alsa_anal_close (&handle);
+    return -1;
+}
+
+static int alsa_recover (snd_pcm_t *handle)
+{
+    int err = FF(snd_pcm_prepare) (handle);
+    if (err < 0) {
+        alsa_logerr (err, "Failed to prepare handle %p\n", handle);
+        return -1;
+    }
+    return 0;
+}
+
+static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
+{
+    snd_pcm_sframes_t avail;
+
+    avail = FF(snd_pcm_avail_update) (handle);
+    if (avail < 0) {
+        if (avail == -EPIPE) {
+            if (!alsa_recover (handle)) {
+                avail = FF(snd_pcm_avail_update) (handle);
+            }
+        }
+
+        if (avail < 0) {
+            alsa_logerr (avail,
+                         "Could not obtain number of available frames\n");
+            return -1;
+        }
+    }
+
+    return avail;
+}
+
+static int alsa_run_out (HWVoiceOut *hw)
+{
+    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+    int rpos, live, decr;
+    int samples;
+    uint8_t *dst;
+    st_sample_t *src;
+    snd_pcm_sframes_t avail;
+
+    live = audio_pcm_hw_get_live_out (hw);
+    if (!live) {
+        return 0;
+    }
+
+    avail = alsa_get_avail (alsa->handle);
+    if (avail < 0) {
+        dolog ("Could not get number of available playback frames\n");
+        return 0;
+    }
+
+    decr = audio_MIN (live, avail);
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int len = audio_MIN (samples, left_till_end_samples);
+        snd_pcm_sframes_t written;
+
+        src = hw->mix_buf + rpos;
+        dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
+
+        hw->clip (dst, src, len);
+
+        while (len) {
+            written = FF(snd_pcm_writei) (alsa->handle, dst, len);
+
+            if (written <= 0) {
+                switch (written) {
+                case 0:
+                    if (conf.verbose) {
+                        dolog ("Failed to write %d frames (wrote zero)\n", len);
+                    }
+                    goto exit;
+
+                case -EPIPE:
+                    if (alsa_recover (alsa->handle)) {
+                        alsa_logerr (written, "Failed to write %d frames\n",
+                                     len);
+                        goto exit;
+                    }
+                    if (conf.verbose) {
+                        dolog ("Recovering from playback xrun\n");
+                    }
+                    continue;
+
+                case -EAGAIN:
+                    goto exit;
+
+                default:
+                    alsa_logerr (written, "Failed to write %d frames to %p\n",
+                                 len, dst);
+                    goto exit;
+                }
+            }
+
+            rpos = (rpos + written) % hw->samples;
+            samples -= written;
+            len -= written;
+            dst = advance (dst, written << hw->info.shift);
+            src += written;
+        }
+    }
+
+ exit:
+    hw->rpos = rpos;
+    return decr;
+}
+
+static void alsa_fini_out (HWVoiceOut *hw)
+{
+    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+    ldebug ("alsa_fini\n");
+    alsa_anal_close (&alsa->handle);
+
+    if (alsa->pcm_buf) {
+        qemu_free (alsa->pcm_buf);
+        alsa->pcm_buf = NULL;
+    }
+}
+
+static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+    struct alsa_params_req req;
+    struct alsa_params_obt obt;
+    snd_pcm_t *handle;
+    audsettings_t obt_as;
+    int  result = -1;
+
+    /* shut alsa debug spew */
+    if (!D_ACTIVE)
+        stdio_disable();
+
+    req.fmt = aud_to_alsafmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
+    req.period_size = conf.period_size_out;
+    req.buffer_size = conf.buffer_size_out;
+    req.size_in_usec = conf.size_in_usec_out;
+    req.override_mask = !!conf.period_size_out_overridden
+        | (!!conf.buffer_size_out_overridden << 1);
+
+    if (alsa_open (0, &req, &obt, &handle)) {
+        goto Exit;
+    }
+
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = obt.fmt;
+    obt_as.endianness = obt.endianness;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+    hw->samples = obt.samples;
+
+    alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
+    if (!alsa->pcm_buf) {
+        dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
+               hw->samples, 1 << hw->info.shift);
+        alsa_anal_close (&handle);
+        goto Exit;
+    }
+
+    alsa->handle = handle;
+    result       = 0;  /* success */
+
+Exit:
+    if (!D_ACTIVE)
+        stdio_enable();
+
+    return result;
+}
+
+static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
+{
+    int err;
+
+    if (pause) {
+        err = FF(snd_pcm_drop) (handle);
+        if (err < 0) {
+            alsa_logerr (err, "Could not stop %s\n", typ);
+            return -1;
+        }
+    }
+    else {
+        err = FF(snd_pcm_prepare) (handle);
+        if (err < 0) {
+            alsa_logerr (err, "Could not prepare handle for %s\n", typ);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        ldebug ("enabling voice\n");
+        return alsa_voice_ctl (alsa->handle, "playback", 0);
+
+    case VOICE_DISABLE:
+        ldebug ("disabling voice\n");
+        return alsa_voice_ctl (alsa->handle, "playback", 1);
+    }
+
+    return -1;
+}
+
+static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+    struct alsa_params_req req;
+    struct alsa_params_obt obt;
+    snd_pcm_t *handle;
+    audsettings_t obt_as;
+    int result = -1;
+
+    /* shut alsa debug spew */
+    if (!D_ACTIVE)
+        stdio_disable();
+
+    req.fmt = aud_to_alsafmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
+    req.period_size = conf.period_size_in;
+    req.buffer_size = conf.buffer_size_in;
+    req.size_in_usec = conf.size_in_usec_in;
+    req.override_mask = !!conf.period_size_in_overridden
+        | (!!conf.buffer_size_in_overridden << 1);
+
+    if (alsa_open (1, &req, &obt, &handle)) {
+        goto Exit;
+    }
+
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = obt.fmt;
+    obt_as.endianness = obt.endianness;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+    hw->samples = obt.samples;
+
+    alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!alsa->pcm_buf) {
+        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
+               hw->samples, 1 << hw->info.shift);
+        alsa_anal_close (&handle);
+        goto Exit;
+    }
+
+    alsa->handle = handle;
+    result       = 0;  /* success */
+
+Exit:
+    if (!D_ACTIVE)
+        stdio_enable();
+
+    return result;
+}
+
+static void alsa_fini_in (HWVoiceIn *hw)
+{
+    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+    alsa_anal_close (&alsa->handle);
+
+    if (alsa->pcm_buf) {
+        qemu_free (alsa->pcm_buf);
+        alsa->pcm_buf = NULL;
+    }
+}
+
+static int alsa_run_in (HWVoiceIn *hw)
+{
+    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+    int hwshift = hw->info.shift;
+    int i;
+    int live = audio_pcm_hw_get_live_in (hw);
+    int dead = hw->samples - live;
+    int decr;
+    struct {
+        int add;
+        int len;
+    } bufs[2] = {
+        { hw->wpos, 0 },
+        { 0, 0 }
+    };
+    snd_pcm_sframes_t avail;
+    snd_pcm_uframes_t read_samples = 0;
+
+    if (!dead) {
+        return 0;
+    }
+
+    avail = alsa_get_avail (alsa->handle);
+    if (avail < 0) {
+        dolog ("Could not get number of captured frames\n");
+        return 0;
+    }
+
+    if (!avail && (FF(snd_pcm_state) (alsa->handle) == SND_PCM_STATE_PREPARED)) {
+        avail = hw->samples;
+    }
+
+    decr = audio_MIN (dead, avail);
+    if (!decr) {
+        return 0;
+    }
+
+    if (hw->wpos + decr > hw->samples) {
+        bufs[0].len = (hw->samples - hw->wpos);
+        bufs[1].len = (decr - (hw->samples - hw->wpos));
+    }
+    else {
+        bufs[0].len = decr;
+    }
+
+    for (i = 0; i < 2; ++i) {
+        void *src;
+        st_sample_t *dst;
+        snd_pcm_sframes_t nread;
+        snd_pcm_uframes_t len;
+
+        len = bufs[i].len;
+
+        src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
+        dst = hw->conv_buf + bufs[i].add;
+
+        while (len) {
+            nread = FF(snd_pcm_readi) (alsa->handle, src, len);
+
+            if (nread <= 0) {
+                switch (nread) {
+                case 0:
+                    if (conf.verbose) {
+                        dolog ("Failed to read %ld frames (read zero)\n", len);
+                    }
+                    goto exit;
+
+                case -EPIPE:
+                    if (alsa_recover (alsa->handle)) {
+                        alsa_logerr (nread, "Failed to read %ld frames\n", len);
+                        goto exit;
+                    }
+                    if (conf.verbose) {
+                        dolog ("Recovering from capture xrun\n");
+                    }
+                    continue;
+
+                case -EAGAIN:
+                    goto exit;
+
+                default:
+                    alsa_logerr (
+                        nread,
+                        "Failed to read %ld frames from %p\n",
+                        len,
+                        src
+                        );
+                    goto exit;
+                }
+            }
+
+            hw->conv (dst, src, nread, &nominal_volume);
+
+            src = advance (src, nread << hwshift);
+            dst += nread;
+
+            read_samples += nread;
+            len -= nread;
+        }
+    }
+
+ exit:
+    hw->wpos = (hw->wpos + read_samples) % hw->samples;
+    return read_samples;
+}
+
+static int alsa_read (SWVoiceIn *sw, void *buf, int size)
+{
+    return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        ldebug ("enabling voice\n");
+        return alsa_voice_ctl (alsa->handle, "capture", 0);
+
+    case VOICE_DISABLE:
+        ldebug ("disabling voice\n");
+        return alsa_voice_ctl (alsa->handle, "capture", 1);
+    }
+
+    return -1;
+}
+
+static void *alsa_audio_init (void)
+{
+    void*    result = NULL;
+
+    alsa_lib = dlopen( "libasound.so", RTLD_NOW );
+    if (alsa_lib == NULL)
+        alsa_lib = dlopen( "libasound.so.2", RTLD_NOW );
+
+    if (alsa_lib == NULL) {
+        ldebug("could not find libasound on this system\n");
+        goto Exit;
+    }
+
+    if (alsa_dynlink_init(alsa_lib) < 0)
+        goto Fail;
+
+    result = &conf;
+    goto Exit;
+
+Fail:
+    ldebug("%s: failed to open library\n", __FUNCTION__);
+    dlclose(alsa_lib);
+
+Exit:
+    return result;
+}
+
+static void alsa_audio_fini (void *opaque)
+{
+    if (alsa_lib != NULL) {
+        dlclose(alsa_lib);
+        alsa_lib = NULL;
+    }
+    (void) opaque;
+}
+
+static struct audio_option alsa_options[] = {
+    {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out,
+     "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+    {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out,
+     "DAC period size (0 to go with system default)",
+     &conf.period_size_out_overridden, 0},
+    {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out,
+     "DAC buffer size (0 to go with system default)",
+     &conf.buffer_size_out_overridden, 0},
+
+    {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in,
+     "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0},
+    {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in,
+     "ADC period size (0 to go with system default)",
+     &conf.period_size_in_overridden, 0},
+    {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in,
+     "ADC buffer size (0 to go with system default)",
+     &conf.buffer_size_in_overridden, 0},
+
+    {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
+     "(undocumented)", NULL, 0},
+
+    {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out,
+     "DAC device name (for instance dmix)", NULL, 0},
+
+    {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
+     "ADC device name", NULL, 0},
+
+    {"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
+     "Behave in a more verbose way", NULL, 0},
+
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops alsa_pcm_ops = {
+    alsa_init_out,
+    alsa_fini_out,
+    alsa_run_out,
+    alsa_write,
+    alsa_ctl_out,
+
+    alsa_init_in,
+    alsa_fini_in,
+    alsa_run_in,
+    alsa_read,
+    alsa_ctl_in
+};
+
+struct audio_driver alsa_audio_driver = {
+    INIT_FIELD (name           = ) "alsa",
+    INIT_FIELD (descr          = ) "ALSA audio (www.alsa-project.org)",
+    INIT_FIELD (options        = ) alsa_options,
+    INIT_FIELD (init           = ) alsa_audio_init,
+    INIT_FIELD (fini           = ) alsa_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &alsa_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) INT_MAX,
+    INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (ALSAVoiceIn)
+};
diff --git a/audio/audio.c b/audio/audio.c
new file mode 100644
index 0000000..5a77dac
--- /dev/null
+++ b/audio/audio.c
@@ -0,0 +1,2172 @@
+/*
+ * QEMU Audio subsystem
+ *
+ * Copyright (c) 2007-2008 The Android Open Source Project
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "audio.h"
+#include "console.h"
+#include "qemu-timer.h"
+#include "sysemu.h"
+
+#define AUDIO_CAP "audio"
+#include "audio_int.h"
+#include "android/utils/system.h"
+#include "qemu_debug.h"
+#include "android/android.h"
+
+/* #define DEBUG_PLIVE */
+/* #define DEBUG_LIVE */
+/* #define DEBUG_OUT */
+/* #define DEBUG_CAPTURE */
+
+#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
+
+static struct audio_driver *drvtab[] = {
+#ifdef CONFIG_ESD
+    &esd_audio_driver,
+#endif
+#ifdef CONFIG_ALSA
+    &alsa_audio_driver,
+#endif
+#ifdef CONFIG_COREAUDIO
+    &coreaudio_audio_driver,
+#endif
+#ifdef CONFIG_DSOUND
+    &dsound_audio_driver,
+#endif
+#ifdef CONFIG_FMOD
+    &fmod_audio_driver,
+#endif
+#ifdef CONFIG_WINAUDIO
+    &win_audio_driver,
+#endif
+#ifdef CONFIG_SDL
+    &sdl_audio_driver,
+#endif
+#ifdef CONFIG_OSS
+    &oss_audio_driver,
+#endif
+    &no_audio_driver,
+#if 0  /* disabled WAV audio for now - until we find a user-friendly way to use it */
+    &wav_audio_driver
+#endif
+};
+
+
+int
+audio_get_backend_count( int  is_input )
+{
+    int  nn, count = 0;
+
+    for (nn = 0; nn < sizeof(drvtab)/sizeof(drvtab[0]); nn++)
+    {
+        if (is_input) {
+            if ( drvtab[nn]->max_voices_in > 0 )
+                count += 1;
+        } else {
+            if ( drvtab[nn]->max_voices_out > 0 )
+                count += 1;
+        }
+    }
+    return  count;
+}
+
+const char*
+audio_get_backend_name( int   is_input, int  index, const char*  *pinfo )
+{
+    int  nn;
+
+    index += 1;
+    for (nn = 0; nn < sizeof(drvtab)/sizeof(drvtab[0]); nn++)
+    {
+        if (is_input) {
+            if ( drvtab[nn]->max_voices_in > 0 ) {
+                if ( --index == 0 ) {
+                    *pinfo = drvtab[nn]->descr;
+                    return drvtab[nn]->name;
+                }
+            }
+        } else {
+            if ( drvtab[nn]->max_voices_out > 0 ) {
+                if ( --index == 0 ) {
+                    *pinfo = drvtab[nn]->descr;
+                    return drvtab[nn]->name;
+                }
+            }
+        }
+    }
+    *pinfo = NULL;
+    return  NULL;
+}
+
+
+int
+audio_check_backend_name( int  is_input, const char*  name )
+{
+    int  nn;
+
+    for (nn = 0; nn < sizeof(drvtab)/sizeof(drvtab[0]); nn++)
+    {
+        if ( !strcmp(drvtab[nn]->name, name) ) {
+            if (is_input) {
+                if (drvtab[nn]->max_voices_in > 0)
+                    return 1;
+            } else {
+                if (drvtab[nn]->max_voices_out > 0)
+                    return 1;
+            }
+            break;
+        }
+    }
+    return 0;
+}
+
+
+struct fixed_settings {
+    int enabled;
+    int nb_voices;
+    int greedy;
+    audsettings_t settings;
+};
+
+static struct {
+    struct fixed_settings fixed_out;
+    struct fixed_settings fixed_in;
+    union {
+        int hz;
+        int64_t ticks;
+    } period;
+    int plive;
+    int log_to_monitor;
+} conf = {
+    {                           /* DAC fixed settings */
+        1,                      /* enabled */
+        1,                      /* nb_voices */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16,        /* fmt */
+            AUDIO_HOST_ENDIANNESS
+        }
+    },
+
+    {                           /* ADC fixed settings */
+        1,                      /* enabled */
+        1,                      /* nb_voices */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16,        /* fmt */
+            AUDIO_HOST_ENDIANNESS
+        }
+    },
+
+    { 0 },                      /* period */
+    0,                          /* plive */
+    0                           /* log_to_monitor */
+};
+
+AudioState glob_audio_state;
+
+volume_t nominal_volume = {
+    0,
+#ifdef FLOAT_MIXENG
+    1.0,
+    1.0
+#else
+    1ULL << 32,
+    1ULL << 32
+#endif
+};
+
+/* http://www.df.lth.se/~john_e/gems/gem002d.html */
+/* http://www.multi-platforms.com/Tips/PopCount.htm */
+uint32_t popcount (uint32_t u)
+{
+    u = ((u&0x55555555) + ((u>>1)&0x55555555));
+    u = ((u&0x33333333) + ((u>>2)&0x33333333));
+    u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+    u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+    u = ( u&0x0000ffff) + (u>>16);
+    return u;
+}
+
+inline uint32_t lsbindex (uint32_t u)
+{
+    return popcount ((u&-u)-1);
+}
+
+#ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED
+#error No its not
+#else
+int audio_bug (const char *funcname, int cond)
+{
+    if (cond) {
+        static int shown;
+
+        AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
+        if (!shown) {
+            shown = 1;
+            AUD_log (NULL, "Save all your work and restart without audio\n");
+            AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n");
+            AUD_log (NULL, "I am sorry\n");
+        }
+        AUD_log (NULL, "Context:\n");
+
+#if defined AUDIO_BREAKPOINT_ON_BUG
+#  if defined HOST_I386
+#    if defined __GNUC__
+        __asm__ ("int3");
+#    elif defined _MSC_VER
+        _asm _emit 0xcc;
+#    else
+        abort ();
+#    endif
+#  else
+        abort ();
+#  endif
+#endif
+    }
+
+    return cond;
+}
+#endif
+
+static inline int audio_bits_to_index (int bits)
+{
+    switch (bits) {
+    case 8:
+        return 0;
+
+    case 16:
+        return 1;
+
+    case 32:
+        return 2;
+
+    default:
+        audio_bug ("bits_to_index", 1);
+        AUD_log (NULL, "invalid bits %d\n", bits);
+        return 0;
+    }
+}
+
+void *audio_calloc (const char *funcname, int nmemb, size_t size)
+{
+    int cond;
+    size_t len;
+
+    len = nmemb * size;
+    cond = !nmemb || !size;
+    cond |= nmemb < 0;
+    cond |= len < size;
+
+    if (audio_bug ("audio_calloc", cond)) {
+        AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
+                 funcname);
+        AUD_log (NULL, "nmemb=%d size=%zu (len=%zu)\n", nmemb, size, len);
+        return NULL;
+    }
+
+    return qemu_mallocz (len);
+}
+
+static char *audio_alloc_prefix (const char *s)
+{
+    const char qemu_prefix[] = "QEMU_";
+    size_t len;
+    char *r;
+
+    if (!s) {
+        return NULL;
+    }
+
+    len = strlen (s);
+    r = qemu_malloc (len + sizeof (qemu_prefix));
+
+    if (r) {
+        size_t i;
+        char *u = r + sizeof (qemu_prefix) - 1;
+
+        strcpy (r, qemu_prefix);
+        strcat (r, s);
+
+        for (i = 0; i < len; ++i) {
+            u[i] = toupper (u[i]);
+        }
+    }
+    return r;
+}
+
+static const char *audio_audfmt_to_string (audfmt_e fmt)
+{
+    switch (fmt) {
+    case AUD_FMT_U8:
+        return "U8";
+
+    case AUD_FMT_U16:
+        return "U16";
+
+    case AUD_FMT_S8:
+        return "S8";
+
+    case AUD_FMT_S16:
+        return "S16";
+
+    case AUD_FMT_U32:
+        return "U32";
+
+    case AUD_FMT_S32:
+        return "S32";
+    }
+
+    dolog ("Bogus audfmt %d returning S16\n", fmt);
+    return "S16";
+}
+
+static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval,
+                                        int *defaultp)
+{
+    if (!strcasecmp (s, "u8")) {
+        *defaultp = 0;
+        return AUD_FMT_U8;
+    }
+    else if (!strcasecmp (s, "u16")) {
+        *defaultp = 0;
+        return AUD_FMT_U16;
+    }
+    else if (!strcasecmp (s, "u32")) {
+        *defaultp = 0;
+        return AUD_FMT_U32;
+    }
+    else if (!strcasecmp (s, "s8")) {
+        *defaultp = 0;
+        return AUD_FMT_S8;
+    }
+    else if (!strcasecmp (s, "s16")) {
+        *defaultp = 0;
+        return AUD_FMT_S16;
+    }
+    else if (!strcasecmp (s, "s32")) {
+        *defaultp = 0;
+        return AUD_FMT_S32;
+    }
+    else {
+        dolog ("Bogus audio format `%s' using %s\n",
+               s, audio_audfmt_to_string (defval));
+        *defaultp = 1;
+        return defval;
+    }
+}
+
+static audfmt_e audio_get_conf_fmt (const char *envname,
+                                    audfmt_e defval,
+                                    int *defaultp)
+{
+    const char *var = getenv (envname);
+    if (!var) {
+        *defaultp = 1;
+        return defval;
+    }
+    return audio_string_to_audfmt (var, defval, defaultp);
+}
+
+static int audio_get_conf_int (const char *key, int defval, int *defaultp)
+{
+    int val;
+    char *strval;
+
+    strval = getenv (key);
+    if (strval) {
+        *defaultp = 0;
+        val = atoi (strval);
+        return val;
+    }
+    else {
+        *defaultp = 1;
+        return defval;
+    }
+}
+
+static const char *audio_get_conf_str (const char *key,
+                                       const char *defval,
+                                       int *defaultp)
+{
+    const char *val = getenv (key);
+    if (!val) {
+        *defaultp = 1;
+        return defval;
+    }
+    else {
+        *defaultp = 0;
+        return val;
+    }
+}
+
+/* defined in android_sdl.c */
+extern void  dprintn(const char*  fmt, ...);
+extern void  dprintnv(const char*  fmt, va_list  args);
+
+void AUD_vlog (const char *cap, const char *fmt, va_list ap)
+{
+    if (conf.log_to_monitor) {
+        if (cap) {
+            term_printf ("%s: ", cap);
+        }
+
+        term_vprintf (fmt, ap);
+    }
+    else {
+        if (!VERBOSE_CHECK(audio))
+            return;
+
+        if (cap) {
+            dprintn("%s: ", cap);
+        }
+
+        dprintnv(fmt, ap);
+    }
+}
+
+void AUD_log (const char *cap, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (cap, fmt, ap);
+    va_end (ap);
+}
+
+static void audio_print_options (const char *prefix,
+                                 struct audio_option *opt)
+{
+    char *uprefix;
+
+    if (!prefix) {
+        dolog ("No prefix specified\n");
+        return;
+    }
+
+    if (!opt) {
+        dolog ("No options\n");
+        return;
+    }
+
+    uprefix = audio_alloc_prefix (prefix);
+
+    for (; opt->name; opt++) {
+        const char *state = "default";
+        printf ("  %s_%s: ", uprefix, opt->name);
+
+        if (opt->overriddenp && *opt->overriddenp) {
+            state = "current";
+        }
+
+        switch (opt->tag) {
+        case AUD_OPT_BOOL:
+            {
+                int *intp = opt->valp;
+                printf ("boolean, %s = %d\n", state, *intp ? 1 : 0);
+            }
+            break;
+
+        case AUD_OPT_INT:
+            {
+                int *intp = opt->valp;
+                printf ("integer, %s = %d\n", state, *intp);
+            }
+            break;
+
+        case AUD_OPT_FMT:
+            {
+                audfmt_e *fmtp = opt->valp;
+                printf (
+                    "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
+                    state,
+                    audio_audfmt_to_string (*fmtp)
+                    );
+            }
+            break;
+
+        case AUD_OPT_STR:
+            {
+                const char **strp = opt->valp;
+                printf ("string, %s = %s\n",
+                        state,
+                        *strp ? *strp : "(not set)");
+            }
+            break;
+
+        default:
+            printf ("???\n");
+            dolog ("Bad value tag for option %s_%s %d\n",
+                   uprefix, opt->name, opt->tag);
+            break;
+        }
+        printf ("    %s\n", opt->descr);
+    }
+
+    qemu_free (uprefix);
+}
+
+static void audio_process_options (const char *prefix,
+                                   struct audio_option *opt)
+{
+    char *optname;
+    const char qemu_prefix[] = "QEMU_";
+    size_t preflen;
+
+    if (audio_bug (AUDIO_FUNC, !prefix)) {
+        dolog ("prefix = NULL\n");
+        return;
+    }
+
+    if (audio_bug (AUDIO_FUNC, !opt)) {
+        dolog ("opt = NULL\n");
+        return;
+    }
+
+    preflen = strlen (prefix);
+
+    for (; opt->name; opt++) {
+        size_t len, i;
+        int def;
+
+        if (!opt->valp) {
+            dolog ("Option value pointer for `%s' is not set\n",
+                   opt->name);
+            continue;
+        }
+
+        len = strlen (opt->name);
+        /* len of opt->name + len of prefix + size of qemu_prefix
+         * (includes trailing zero) + zero + underscore (on behalf of
+         * sizeof) */
+        optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1);
+        if (!optname) {
+            dolog ("Could not allocate memory for option name `%s'\n",
+                   opt->name);
+            continue;
+        }
+
+        strcpy (optname, qemu_prefix);
+
+        /* copy while upper-casing, including trailing zero */
+        for (i = 0; i <= preflen; ++i) {
+            optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]);
+        }
+        strcat (optname, "_");
+        strcat (optname, opt->name);
+
+        def = 1;
+        switch (opt->tag) {
+        case AUD_OPT_BOOL:
+        case AUD_OPT_INT:
+            {
+                int *intp = opt->valp;
+                *intp = audio_get_conf_int (optname, *intp, &def);
+            }
+            break;
+
+        case AUD_OPT_FMT:
+            {
+                audfmt_e *fmtp = opt->valp;
+                *fmtp = audio_get_conf_fmt (optname, *fmtp, &def);
+            }
+            break;
+
+        case AUD_OPT_STR:
+            {
+                const char **strp = opt->valp;
+                *strp = audio_get_conf_str (optname, *strp, &def);
+            }
+            break;
+
+        default:
+            dolog ("Bad value tag for option `%s' - %d\n",
+                   optname, opt->tag);
+            break;
+        }
+
+        if (!opt->overriddenp) {
+            opt->overriddenp = &opt->overridden;
+        }
+        *opt->overriddenp = !def;
+        qemu_free (optname);
+    }
+}
+
+static void audio_print_settings (audsettings_t *as)
+{
+    dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+        AUD_log (NULL, "S8");
+        break;
+    case AUD_FMT_U8:
+        AUD_log (NULL, "U8");
+        break;
+    case AUD_FMT_S16:
+        AUD_log (NULL, "S16");
+        break;
+    case AUD_FMT_U16:
+        AUD_log (NULL, "U16");
+        break;
+    default:
+        AUD_log (NULL, "invalid(%d)", as->fmt);
+        break;
+    }
+
+    AUD_log (NULL, " endianness=");
+    switch (as->endianness) {
+    case 0:
+        AUD_log (NULL, "little");
+        break;
+    case 1:
+        AUD_log (NULL, "big");
+        break;
+    default:
+        AUD_log (NULL, "invalid");
+        break;
+    }
+    AUD_log (NULL, "\n");
+}
+
+static int audio_validate_settings (audsettings_t *as)
+{
+    int invalid;
+
+    invalid = as->nchannels != 1 && as->nchannels != 2;
+    invalid |= as->endianness != 0 && as->endianness != 1;
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        break;
+    default:
+        invalid = 1;
+        break;
+    }
+
+    invalid |= as->freq <= 0;
+    return invalid ? -1 : 0;
+}
+
+static int audio_pcm_info_eq (struct audio_pcm_info *info, audsettings_t *as)
+{
+    int bits = 8, sign = 0;
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+        sign = 1;
+    case AUD_FMT_U8:
+        break;
+
+    case AUD_FMT_S16:
+        sign = 1;
+    case AUD_FMT_U16:
+        bits = 16;
+        break;
+    case AUD_FMT_S32:
+        sign = 1;
+    case AUD_FMT_U32:
+        bits = 32;
+        break;
+    }
+    return info->freq == as->freq
+        && info->nchannels == as->nchannels
+        && info->sign == sign
+        && info->bits == bits
+        && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
+}
+
+void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as)
+{
+    int bits = 8, sign = 0, shift = 0;
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+        sign = 1;
+    case AUD_FMT_U8:
+        break;
+
+    case AUD_FMT_S16:
+        sign = 1;
+    case AUD_FMT_U16:
+        bits = 16;
+        shift = 1;
+        break;
+
+    case AUD_FMT_S32:
+        sign = 1;
+    case AUD_FMT_U32:
+        bits = 32;
+        shift = 2;
+        break;
+    }
+
+    info->freq = as->freq;
+    info->bits = bits;
+    info->sign = sign;
+    info->nchannels = as->nchannels;
+    info->shift = (as->nchannels == 2) + shift;
+    info->align = (1 << info->shift) - 1;
+    info->bytes_per_second = info->freq << info->shift;
+    info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
+}
+
+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
+{
+    if (!len) {
+        return;
+    }
+
+    if (info->sign) {
+        memset (buf, 0x00, len << info->shift);
+    }
+    else {
+        switch (info->bits) {
+        case 8:
+            memset (buf, 0x80, len << info->shift);
+            break;
+
+        case 16:
+            {
+                int i;
+                uint16_t *p = buf;
+                int shift = info->nchannels - 1;
+                short s = INT16_MAX;
+
+                if (info->swap_endianness) {
+                    s = bswap16 (s);
+                }
+
+                for (i = 0; i < len << shift; i++) {
+                    p[i] = s;
+                }
+            }
+            break;
+
+        case 32:
+            {
+                int i;
+                uint32_t *p = buf;
+                int shift = info->nchannels - 1;
+                int32_t s = INT32_MAX;
+
+                if (info->swap_endianness) {
+                    s = bswap32 (s);
+                }
+
+                for (i = 0; i < len << shift; i++) {
+                    p[i] = s;
+                }
+            }
+            break;
+
+        default:
+            AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n",
+                     info->bits);
+            break;
+        }
+    }
+}
+
+/*
+ * Capture
+ */
+static void noop_conv (st_sample_t *dst, const void *src,
+                       int samples, volume_t *vol)
+{
+    (void) src;
+    (void) dst;
+    (void) samples;
+    (void) vol;
+}
+
+static CaptureVoiceOut *audio_pcm_capture_find_specific (
+    AudioState *s,
+    audsettings_t *as
+    )
+{
+    CaptureVoiceOut *cap;
+
+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        if (audio_pcm_info_eq (&cap->hw.info, as)) {
+            return cap;
+        }
+    }
+    return NULL;
+}
+
+static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
+{
+    struct capture_callback *cb;
+
+#ifdef DEBUG_CAPTURE
+    dolog ("notification %d sent\n", cmd);
+#endif
+    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+        cb->ops.notify (cb->opaque, cmd);
+    }
+}
+
+static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
+{
+    if (cap->hw.enabled != enabled) {
+        audcnotification_e cmd;
+        cap->hw.enabled = enabled;
+        cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
+        audio_notify_capture (cap, cmd);
+    }
+}
+
+static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
+{
+    HWVoiceOut *hw = &cap->hw;
+    SWVoiceOut *sw;
+    int enabled = 0;
+
+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+        if (sw->active) {
+            enabled = 1;
+            break;
+        }
+    }
+    audio_capture_maybe_changed (cap, enabled);
+}
+
+static void audio_detach_capture (HWVoiceOut *hw)
+{
+    SWVoiceCap *sc = hw->cap_head.lh_first;
+
+    while (sc) {
+        SWVoiceCap *sc1 = sc->entries.le_next;
+        SWVoiceOut *sw = &sc->sw;
+        CaptureVoiceOut *cap = sc->cap;
+        int was_active = sw->active;
+
+        if (sw->rate) {
+            st_rate_stop (sw->rate);
+            sw->rate = NULL;
+        }
+
+        LIST_REMOVE (sw, entries);
+        LIST_REMOVE (sc, entries);
+        qemu_free (sc);
+        if (was_active) {
+            /* We have removed soft voice from the capture:
+               this might have changed the overall status of the capture
+               since this might have been the only active voice */
+            audio_recalc_and_notify_capture (cap);
+        }
+        sc = sc1;
+    }
+}
+
+static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
+{
+    CaptureVoiceOut *cap;
+
+    audio_detach_capture (hw);
+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        SWVoiceCap *sc;
+        SWVoiceOut *sw;
+        HWVoiceOut *hw_cap = &cap->hw;
+
+        sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
+        if (!sc) {
+            dolog ("Could not allocate soft capture voice (%zu bytes)\n",
+                   sizeof (*sc));
+            return -1;
+        }
+
+        sc->cap = cap;
+        sw = &sc->sw;
+        sw->hw = hw_cap;
+        sw->info = hw->info;
+        sw->empty = 1;
+        sw->active = hw->enabled;
+        sw->conv = noop_conv;
+        sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
+        sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
+        if (!sw->rate) {
+            dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
+            qemu_free (sw);
+            return -1;
+        }
+        LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
+        LIST_INSERT_HEAD (&hw->cap_head, sc, entries);
+#ifdef DEBUG_CAPTURE
+        asprintf (&sw->name, "for %p %d,%d,%d",
+                  hw, sw->info.freq, sw->info.bits, sw->info.nchannels);
+        dolog ("Added %s active = %d\n", sw->name, sw->active);
+#endif
+        if (sw->active) {
+            audio_capture_maybe_changed (cap, 1);
+        }
+    }
+    return 0;
+}
+
+/*
+ * Hard voice (capture)
+ */
+static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
+{
+    SWVoiceIn *sw;
+    int m = hw->total_samples_captured;
+
+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+        if (sw->active) {
+            m = audio_MIN (m, sw->total_hw_samples_acquired);
+        }
+    }
+    return m;
+}
+
+int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
+{
+    int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+        dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+        return 0;
+    }
+    return live;
+}
+
+/*
+ * Soft voice (capture)
+ */
+static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
+{
+    HWVoiceIn *hw = sw->hw;
+    int live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+    int rpos;
+
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+        dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+        return 0;
+    }
+
+    rpos = hw->wpos - live;
+    if (rpos >= 0) {
+        return rpos;
+    }
+    else {
+        return hw->samples + rpos;
+    }
+}
+
+int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
+{
+    HWVoiceIn *hw = sw->hw;
+    int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
+    st_sample_t *src, *dst = sw->buf;
+
+    rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
+
+    live = hw->total_samples_captured - sw->total_hw_samples_acquired;
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+        dolog ("live_in=%d hw->samples=%d\n", live, hw->samples);
+        return 0;
+    }
+
+    samples = size >> sw->info.shift;
+    if (!live) {
+        return 0;
+    }
+
+    swlim = (live * sw->ratio) >> 32;
+    swlim = audio_MIN (swlim, samples);
+
+    while (swlim) {
+        src = hw->conv_buf + rpos;
+        isamp = hw->wpos - rpos;
+        /* XXX: <= ? */
+        if (isamp <= 0) {
+            isamp = hw->samples - rpos;
+        }
+
+        if (!isamp) {
+            break;
+        }
+        osamp = swlim;
+
+        if (audio_bug (AUDIO_FUNC, osamp < 0)) {
+            dolog ("osamp=%d\n", osamp);
+            return 0;
+        }
+
+        st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
+        swlim -= osamp;
+        rpos = (rpos + isamp) % hw->samples;
+        dst += osamp;
+        ret += osamp;
+        total += isamp;
+    }
+
+    sw->clip (buf, sw->buf, ret);
+    sw->total_hw_samples_acquired += total;
+    return ret << sw->info.shift;
+}
+
+/*
+ * Hard voice (playback)
+ */
+static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
+{
+    SWVoiceOut *sw;
+    int m = INT_MAX;
+    int nb_live = 0;
+
+    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+        if (sw->active || !sw->empty) {
+            m = audio_MIN (m, sw->total_hw_samples_mixed);
+            nb_live += 1;
+        }
+    }
+
+    *nb_livep = nb_live;
+    return m;
+}
+
+int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
+{
+    int smin;
+
+    smin = audio_pcm_hw_find_min_out (hw, nb_live);
+
+    if (!*nb_live) {
+        return 0;
+    }
+    else {
+        int live = smin;
+
+        if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+            dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+            return 0;
+        }
+        return live;
+    }
+}
+
+int audio_pcm_hw_get_live_out (HWVoiceOut *hw)
+{
+    int nb_live;
+    int live;
+
+    live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+        dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+        return 0;
+    }
+    return live;
+}
+
+/*
+ * Soft voice (playback)
+ */
+int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
+{
+    int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
+    int ret = 0, pos = 0, total = 0;
+
+    if (!sw) {
+        return size;
+    }
+
+    hwsamples = sw->hw->samples;
+
+    live = sw->total_hw_samples_mixed;
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){
+        dolog ("live=%d hw->samples=%d\n", live, hwsamples);
+        return 0;
+    }
+
+    if (live == hwsamples) {
+#ifdef DEBUG_OUT
+        dolog ("%s is full %d\n", sw->name, live);
+#endif
+        return 0;
+    }
+
+    wpos = (sw->hw->rpos + live) % hwsamples;
+    samples = size >> sw->info.shift;
+
+    dead = hwsamples - live;
+    swlim = ((int64_t) dead << 32) / sw->ratio;
+    swlim = audio_MIN (swlim, samples);
+    if (swlim) {
+        sw->conv (sw->buf, buf, swlim, &sw->vol);
+    }
+
+    while (swlim) {
+        dead = hwsamples - live;
+        left = hwsamples - wpos;
+        blck = audio_MIN (dead, left);
+        if (!blck) {
+            break;
+        }
+        isamp = swlim;
+        osamp = blck;
+        st_rate_flow_mix (
+            sw->rate,
+            sw->buf + pos,
+            sw->hw->mix_buf + wpos,
+            &isamp,
+            &osamp
+            );
+        ret += isamp;
+        swlim -= isamp;
+        pos += isamp;
+        live += osamp;
+        wpos = (wpos + osamp) % hwsamples;
+        total += osamp;
+    }
+
+    sw->total_hw_samples_mixed += total;
+    sw->empty = sw->total_hw_samples_mixed == 0;
+
+#ifdef DEBUG_OUT
+    dolog (
+        "%s: write size %d ret %d total sw %d\n",
+        SW_NAME (sw),
+        size >> sw->info.shift,
+        ret,
+        sw->total_hw_samples_mixed
+        );
+#endif
+
+    return ret << sw->info.shift;
+}
+
+#ifdef DEBUG_AUDIO
+static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
+{
+    dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n",
+           cap, info->bits, info->sign, info->freq, info->nchannels);
+}
+#endif
+
+#define DAC
+#include "audio_template.h"
+#undef DAC
+#include "audio_template.h"
+
+int AUD_write (SWVoiceOut *sw, void *buf, int size)
+{
+    int bytes;
+
+    if (!sw) {
+        /* XXX: Consider options */
+        return size;
+    }
+
+    if (!sw->hw->enabled) {
+        dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
+        return 0;
+    }
+
+    BEGIN_NOSIGALRM
+        bytes = sw->hw->pcm_ops->write (sw, buf, size);
+    END_NOSIGALRM
+    return bytes;
+}
+
+int AUD_read (SWVoiceIn *sw, void *buf, int size)
+{
+    int bytes;
+
+    if (!sw) {
+        /* XXX: Consider options */
+        return size;
+    }
+
+    if (!sw->hw->enabled) {
+        dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
+        return 0;
+    }
+
+    BEGIN_NOSIGALRM
+        bytes = sw->hw->pcm_ops->read (sw, buf, size);
+    END_NOSIGALRM
+    return bytes;
+}
+
+int AUD_get_buffer_size_out (SWVoiceOut *sw)
+{
+    return sw->hw->samples << sw->hw->info.shift;
+}
+
+void AUD_set_active_out (SWVoiceOut *sw, int on)
+{
+    HWVoiceOut *hw;
+
+    if (!sw) {
+        return;
+    }
+
+    hw = sw->hw;
+    if (sw->active != on) {
+        SWVoiceOut *temp_sw;
+        SWVoiceCap *sc;
+
+        if (on) {
+            hw->pending_disable = 0;
+            if (!hw->enabled) {
+                hw->enabled = 1;
+                BEGIN_NOSIGALRM
+                    hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
+                END_NOSIGALRM
+            }
+        }
+        else {
+            if (hw->enabled) {
+                int nb_active = 0;
+
+                for (temp_sw = hw->sw_head.lh_first; temp_sw;
+                     temp_sw = temp_sw->entries.le_next) {
+                    nb_active += temp_sw->active != 0;
+                }
+
+                hw->pending_disable = nb_active == 1;
+            }
+        }
+
+        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+            sc->sw.active = hw->enabled;
+            if (hw->enabled) {
+                audio_capture_maybe_changed (sc->cap, 1);
+            }
+        }
+        sw->active = on;
+    }
+}
+
+void AUD_set_active_in (SWVoiceIn *sw, int on)
+{
+    HWVoiceIn *hw;
+
+    if (!sw) {
+        return;
+    }
+
+    hw = sw->hw;
+    if (sw->active != on) {
+        SWVoiceIn *temp_sw;
+
+        if (on) {
+            if (!hw->enabled) {
+                hw->enabled = 1;
+                BEGIN_NOSIGALRM
+                    hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
+                END_NOSIGALRM
+            }
+            sw->total_hw_samples_acquired = hw->total_samples_captured;
+        }
+        else {
+            if (hw->enabled) {
+                int nb_active = 0;
+
+                for (temp_sw = hw->sw_head.lh_first; temp_sw;
+                     temp_sw = temp_sw->entries.le_next) {
+                    nb_active += temp_sw->active != 0;
+                }
+
+                if (nb_active == 1) {
+                    hw->enabled = 0;
+                    BEGIN_NOSIGALRM
+                        hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
+                    END_NOSIGALRM
+                }
+            }
+        }
+        sw->active = on;
+    }
+}
+
+static int audio_get_avail (SWVoiceIn *sw)
+{
+    int live;
+
+    if (!sw) {
+        return 0;
+    }
+
+    live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+        dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+        return 0;
+    }
+
+    ldebug (
+        "%s: get_avail live %d ret %" PRId64 "\n",
+        SW_NAME (sw),
+        live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift
+        );
+
+    return (((int64_t) live << 32) / sw->ratio) << sw->info.shift;
+}
+
+static int audio_get_free (SWVoiceOut *sw)
+{
+    int live, dead;
+
+    if (!sw) {
+        return 0;
+    }
+
+    live = sw->total_hw_samples_mixed;
+
+    if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+        dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+        return 0;
+    }
+
+    dead = sw->hw->samples - live;
+
+#ifdef DEBUG_OUT
+    dolog ("%s: get_free live %d dead %d ret %" PRId64 "\n",
+           SW_NAME (sw),
+           live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);
+#endif
+
+    return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
+}
+
+static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
+{
+    int n;
+
+    if (hw->enabled) {
+        SWVoiceCap *sc;
+
+        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+            SWVoiceOut *sw = &sc->sw;
+            int rpos2 = rpos;
+
+            n = samples;
+            while (n) {
+                int till_end_of_hw = hw->samples - rpos2;
+                int to_write = audio_MIN (till_end_of_hw, n);
+                int bytes = to_write << hw->info.shift;
+                int written;
+
+                sw->buf = hw->mix_buf + rpos2;
+                written = audio_pcm_sw_write (sw, NULL, bytes);
+                if (written - bytes) {
+                    dolog ("Could not mix %d bytes into a capture "
+                           "buffer, mixed %d\n",
+                           bytes, written);
+                    break;
+                }
+                n -= to_write;
+                rpos2 = (rpos2 + to_write) % hw->samples;
+            }
+        }
+    }
+
+    n = audio_MIN (samples, hw->samples - rpos);
+    mixeng_clear (hw->mix_buf + rpos, n);
+    mixeng_clear (hw->mix_buf, samples - n);
+}
+
+static void audio_run_out (AudioState *s)
+{
+    HWVoiceOut *hw = NULL;
+    SWVoiceOut *sw;
+
+    while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
+        int played;
+        int live, free, nb_live, cleanup_required, prev_rpos;
+
+        live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+        if (!nb_live) {
+            live = 0;
+        }
+
+        if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+            dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+            continue;
+        }
+
+        if (hw->pending_disable && !nb_live) {
+            SWVoiceCap *sc;
+#ifdef DEBUG_OUT
+            dolog ("Disabling voice\n");
+#endif
+            hw->enabled = 0;
+            hw->pending_disable = 0;
+            BEGIN_NOSIGALRM
+                hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
+            END_NOSIGALRM
+            for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+                sc->sw.active = 0;
+                audio_recalc_and_notify_capture (sc->cap);
+            }
+            continue;
+        }
+
+        if (!live) {
+            for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+                if (sw->active) {
+                    free = audio_get_free (sw);
+                    if (free > 0) {
+                        sw->callback.fn (sw->callback.opaque, free);
+                    }
+                }
+            }
+            continue;
+        }
+
+        prev_rpos = hw->rpos;
+        BEGIN_NOSIGALRM
+            played = hw->pcm_ops->run_out (hw);
+        END_NOSIGALRM
+        if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
+            dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
+                   hw->rpos, hw->samples, played);
+            hw->rpos = 0;
+        }
+
+#ifdef DEBUG_OUT
+        dolog ("played=%d\n", played);
+#endif
+
+        if (played) {
+            hw->ts_helper += played;
+            audio_capture_mix_and_clear (hw, prev_rpos, played);
+        }
+
+        cleanup_required = 0;
+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+            if (!sw->active && sw->empty) {
+                continue;
+            }
+
+            if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) {
+                dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
+                       played, sw->total_hw_samples_mixed);
+                played = sw->total_hw_samples_mixed;
+            }
+
+            sw->total_hw_samples_mixed -= played;
+
+            if (!sw->total_hw_samples_mixed) {
+                sw->empty = 1;
+                cleanup_required |= !sw->active && !sw->callback.fn;
+            }
+
+            if (sw->active) {
+                free = audio_get_free (sw);
+                if (free > 0) {
+                    sw->callback.fn (sw->callback.opaque, free);
+                }
+            }
+        }
+
+        if (cleanup_required) {
+            SWVoiceOut *sw1;
+
+            sw = hw->sw_head.lh_first;
+            while (sw) {
+                sw1 = sw->entries.le_next;
+                if (!sw->active && !sw->callback.fn) {
+#ifdef DEBUG_PLIVE
+                    dolog ("Finishing with old voice\n");
+#endif
+                    audio_close_out (s, sw);
+                }
+                sw = sw1;
+            }
+        }
+    }
+}
+
+static void audio_run_in (AudioState *s)
+{
+    HWVoiceIn *hw = NULL;
+
+    while ((hw = audio_pcm_hw_find_any_enabled_in (s, hw))) {
+        SWVoiceIn *sw;
+        int captured, min;
+
+        BEGIN_NOSIGALRM
+            captured = hw->pcm_ops->run_in (hw);
+        END_NOSIGALRM
+
+        min = audio_pcm_hw_find_min_in (hw);
+        hw->total_samples_captured += captured - min;
+        hw->ts_helper += captured;
+
+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+            sw->total_hw_samples_acquired -= min;
+
+            if (sw->active) {
+                int avail;
+
+                avail = audio_get_avail (sw);
+                if (avail > 0) {
+                    sw->callback.fn (sw->callback.opaque, avail);
+                }
+            }
+        }
+    }
+}
+
+static void audio_run_capture (AudioState *s)
+{
+    CaptureVoiceOut *cap;
+
+    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
+        int live, rpos, captured;
+        HWVoiceOut *hw = &cap->hw;
+        SWVoiceOut *sw;
+
+        captured = live = audio_pcm_hw_get_live_out (hw);
+        rpos = hw->rpos;
+        while (live) {
+            int left = hw->samples - rpos;
+            int to_capture = audio_MIN (live, left);
+            st_sample_t *src;
+            struct capture_callback *cb;
+
+            src = hw->mix_buf + rpos;
+            hw->clip (cap->buf, src, to_capture);
+            mixeng_clear (src, to_capture);
+
+            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+                cb->ops.capture (cb->opaque, cap->buf,
+                                 to_capture << hw->info.shift);
+            }
+            rpos = (rpos + to_capture) % hw->samples;
+            live -= to_capture;
+        }
+        hw->rpos = rpos;
+
+        for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+            if (!sw->active && sw->empty) {
+                continue;
+            }
+
+            if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
+                dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
+                       captured, sw->total_hw_samples_mixed);
+                captured = sw->total_hw_samples_mixed;
+            }
+
+            sw->total_hw_samples_mixed -= captured;
+            sw->empty = sw->total_hw_samples_mixed == 0;
+        }
+    }
+}
+
+static void audio_timer (void *opaque)
+{
+    AudioState* s = opaque;
+#if 0
+#define  MAX_DIFFS  1000
+    int64_t         now  = qemu_get_clock(vm_clock);
+	static int64_t  last = 0;
+	static float    diffs[MAX_DIFFS];
+	static int      num_diffs;
+
+    if (last == 0)
+	    last = now;
+	else {
+		diffs[num_diffs] = (float)((now-last)/1e6);  /* last diff in ms */
+		if (++num_diffs == MAX_DIFFS) {
+			double  min_diff = 1e6, max_diff = -1e6;
+			double  all_diff = 0.;
+			int     nn;
+
+			for (nn = 0; nn < num_diffs; nn++) {
+				if (diffs[nn] < min_diff) min_diff = diffs[nn];
+				if (diffs[nn] > max_diff) max_diff = diffs[nn];
+				all_diff += diffs[nn];
+			}
+			all_diff *= 1.0/num_diffs;
+			printf("audio timer: min_diff=%6.2g max_diff=%6.2g avg_diff=%6.2g samples=%d\n",
+					min_diff, max_diff, all_diff, num_diffs);
+			num_diffs = 0;
+		}
+	}
+	last = now;
+#endif
+    audio_run_out (s);
+    audio_run_in (s);
+    audio_run_capture (s);
+
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+}
+
+static struct audio_option audio_options[] = {
+    /* DAC */
+    {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
+     "Use fixed settings for host DAC", NULL, 0},
+
+    {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq,
+     "Frequency for fixed host DAC", NULL, 0},
+
+    {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt,
+     "Format for fixed host DAC", NULL, 0},
+
+    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels,
+     "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
+
+    {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices,
+     "Number of voices for DAC", NULL, 0},
+
+    /* ADC */
+    {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled,
+     "Use fixed settings for host ADC", NULL, 0},
+
+    {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq,
+     "Frequency for fixed host ADC", NULL, 0},
+
+    {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt,
+     "Format for fixed host ADC", NULL, 0},
+
+    {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels,
+     "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
+
+    {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices,
+     "Number of voices for ADC", NULL, 0},
+
+    /* Misc */
+    {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hz,
+     "Timer period in HZ (0 - use lowest possible)", NULL, 0},
+
+    {"PLIVE", AUD_OPT_BOOL, &conf.plive,
+     "(undocumented)", NULL, 0},
+
+    {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor,
+     "print logging messages to monitor instead of stderr", NULL, 0},
+
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static void audio_pp_nb_voices (const char *typ, int nb)
+{
+    switch (nb) {
+    case 0:
+        printf ("Does not support %s\n", typ);
+        break;
+    case 1:
+        printf ("One %s voice\n", typ);
+        break;
+    case INT_MAX:
+        printf ("Theoretically supports many %s voices\n", typ);
+        break;
+    default:
+        printf ("Theoretically supports upto %d %s voices\n", nb, typ);
+        break;
+    }
+
+}
+
+void AUD_help (void)
+{
+    size_t i;
+
+    audio_process_options ("AUDIO", audio_options);
+    for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+        struct audio_driver *d = drvtab[i];
+        if (d->options) {
+            audio_process_options (d->name, d->options);
+        }
+    }
+
+    printf ("Audio options:\n");
+    audio_print_options ("AUDIO", audio_options);
+    printf ("\n");
+
+    printf ("Available drivers:\n");
+
+    for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+        struct audio_driver *d = drvtab[i];
+
+        printf ("Name: %s\n", d->name);
+        printf ("Description: %s\n", d->descr);
+
+        audio_pp_nb_voices ("playback", d->max_voices_out);
+        audio_pp_nb_voices ("capture", d->max_voices_in);
+
+        if (d->options) {
+            printf ("Options:\n");
+            audio_print_options (d->name, d->options);
+        }
+        else {
+            printf ("No options\n");
+        }
+        printf ("\n");
+    }
+
+    printf (
+        "Options are settable through environment variables.\n"
+        "Example:\n"
+#ifdef _WIN32
+        "  set QEMU_AUDIO_DRV=wav\n"
+        "  set QEMU_WAV_PATH=c:\\tune.wav\n"
+#else
+        "  export QEMU_AUDIO_DRV=wav\n"
+        "  export QEMU_WAV_PATH=$HOME/tune.wav\n"
+        "(for csh replace export with setenv in the above)\n"
+#endif
+        "  qemu ...\n\n"
+        );
+}
+
+static int audio_driver_init (AudioState *s, struct audio_driver *drv, int  out)
+{
+    void*   opaque;
+
+    if (drv->options) {
+        audio_process_options (drv->name, drv->options);
+    }
+
+    /* is the driver already initialized ? */
+    if (out) {
+        if (drv == s->drv_in) {
+            s->drv_out        = drv;
+            s->drv_out_opaque = s->drv_in_opaque;
+            return 0;
+        }
+    } else {
+        if (drv == s->drv_out) {
+            s->drv_in        = drv;
+            s->drv_in_opaque = s->drv_out_opaque;
+            return 0;
+        }
+    }
+
+    BEGIN_NOSIGALRM
+    opaque = drv->init();
+    END_NOSIGALRM
+
+    if (opaque != NULL) {
+        audio_init_nb_voices_out (s, drv);
+        audio_init_nb_voices_in (s, drv);
+        if (out) {
+            s->drv_out         = drv;
+            s->drv_out_opaque  = opaque;
+        } else {
+            s->drv_in        = drv;
+            s->drv_in_opaque = opaque;
+        }
+        return 0;
+    }
+    else {
+        dolog ("Could not init `%s' audio driver\n", drv->name);
+        return -1;
+    }
+}
+
+static void audio_vm_change_state_handler (void *opaque, int running)
+{
+    AudioState *s = opaque;
+    HWVoiceOut *hwo = NULL;
+    HWVoiceIn *hwi = NULL;
+    int op = running ? VOICE_ENABLE : VOICE_DISABLE;
+
+    BEGIN_NOSIGALRM
+        while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
+            hwo->pcm_ops->ctl_out (hwo, op);
+        }
+
+        while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
+            hwi->pcm_ops->ctl_in (hwi, op);
+        }
+    END_NOSIGALRM
+}
+
+// to make sure audio_atexit() is only called once
+static int initialized = 0;
+
+static void audio_atexit (void)
+{
+    AudioState *s = &glob_audio_state;
+    HWVoiceOut *hwo = NULL;
+    HWVoiceIn *hwi = NULL;
+
+    if (!initialized) return;
+    initialized = 0;
+
+    BEGIN_NOSIGALRM
+    while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
+        SWVoiceCap *sc;
+
+        hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
+        hwo->pcm_ops->fini_out (hwo);
+
+        for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
+            CaptureVoiceOut *cap = sc->cap;
+            struct capture_callback *cb;
+
+            for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+                cb->ops.destroy (cb->opaque);
+            }
+        }
+    }
+
+    while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
+        hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
+        hwi->pcm_ops->fini_in (hwi);
+    }
+
+    if (s->drv_in) {
+        s->drv_in->fini (s->drv_in_opaque);
+    }
+    if (s->drv_out) {
+        s->drv_out->fini (s->drv_out_opaque);
+    }
+    END_NOSIGALRM
+}
+
+static void audio_save (QEMUFile *f, void *opaque)
+{
+    (void) f;
+    (void) opaque;
+}
+
+static int audio_load (QEMUFile *f, void *opaque, int version_id)
+{
+    (void) f;
+    (void) opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card)
+{
+    card->audio = s;
+    card->name = qemu_strdup (name);
+    memset (&card->entries, 0, sizeof (card->entries));
+    LIST_INSERT_HEAD (&s->card_head, card, entries);
+}
+
+void AUD_remove_card (QEMUSoundCard *card)
+{
+    LIST_REMOVE (card, entries);
+    card->audio = NULL;
+    qemu_free (card->name);
+}
+
+static int
+find_audio_driver( AudioState*  s, int  out )
+{
+    int                   i, done = 0, def;
+    const char*           envname;
+    const char*           drvname;
+    struct audio_driver*  drv     = NULL;
+    const char*           drvtype = out ? "output" : "input";
+
+    envname = out ? "QEMU_AUDIO_OUT_DRV" : "QEMU_AUDIO_IN_DRV";
+    drvname = audio_get_conf_str(envname, NULL, &def);
+    if (drvname == NULL) {
+        drvname = audio_get_conf_str("QEMU_AUDIO_DRV", NULL, &def);
+    }
+
+    if (drvname != NULL) {  /* look for a specific driver */
+        for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+            if (!strcmp (drvname, drvtab[i]->name)) {
+                drv   = drvtab[i];
+                break;
+            }
+        }
+    }
+
+    if (drv != NULL) {
+        done  = !audio_driver_init (s, drv, out);
+        if (!done) {
+            dolog ("Could not initialize '%s' %s audio backend, trying default one.\n",
+                    drvname, drvtype);
+            dolog ("Run with -qemu -audio-help to list available backends\n");
+            drv = NULL;
+        }
+    }
+
+    if (!drv) {
+        for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+            if (drvtab[i]->can_be_default) {
+                drv   = drvtab[i];
+                done  = !audio_driver_init (s, drv, out);
+                if (done)
+                    break;
+            }
+        }
+    }
+
+    if (!done) {
+        drv  = &no_audio_driver;
+        done = !audio_driver_init (s, drv, out);
+        if (!done) {
+            /* this should never happen */
+            dolog ("Could not initialize audio subsystem\n");
+            return -1;
+        }
+        dolog ("warning: Could not find suitable audio %s backend\n", drvtype);
+    }
+
+    if (VERBOSE_CHECK(init))
+        dprint("using '%s' audio %s backend", drv->name, drvtype );
+    return 0;
+}
+
+
+AudioState *AUD_init (void)
+{
+    AudioState *s = &glob_audio_state;
+
+    LIST_INIT (&s->hw_head_out);
+    LIST_INIT (&s->hw_head_in);
+    LIST_INIT (&s->cap_head);
+    atexit (audio_atexit);
+
+    s->ts = qemu_new_timer (vm_clock, audio_timer, s);
+    if (!s->ts) {
+        dolog ("Could not create audio timer\n");
+        return NULL;
+    }
+
+    audio_process_options ("AUDIO", audio_options);
+
+    s->nb_hw_voices_out = conf.fixed_out.nb_voices;
+    s->nb_hw_voices_in = conf.fixed_in.nb_voices;
+
+    if (s->nb_hw_voices_out <= 0) {
+        dolog ("Bogus number of playback voices %d, setting to 1\n",
+               s->nb_hw_voices_out);
+        s->nb_hw_voices_out = 1;
+    }
+
+    if (s->nb_hw_voices_in <= 0) {
+        dolog ("Bogus number of capture voices %d, setting to 0\n",
+               s->nb_hw_voices_in);
+        s->nb_hw_voices_in = 0;
+    }
+
+    if ( find_audio_driver (s, 0) == 0 &&
+         find_audio_driver (s, 1) == 0 )
+    {
+        VMChangeStateEntry *e;
+
+        if (conf.period.hz <= 0) {
+            if (conf.period.hz < 0) {
+                dolog ("warning: Timer period is negative - %d "
+                       "treating as zero\n",
+                       conf.period.hz);
+            }
+            conf.period.ticks = 1;
+        }
+        else {
+            conf.period.ticks = ticks_per_sec / conf.period.hz;
+        }
+
+        e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
+        if (!e) {
+            dolog ("warning: Could not register change state handler\n"
+                   "(Audio can continue looping even after stopping the VM)\n");
+        }
+    }
+    else {
+        qemu_del_timer (s->ts);
+        return NULL;
+    }
+
+    initialized = 1;
+
+    LIST_INIT (&s->card_head);
+    register_savevm ("audio", 0, 1, audio_save, audio_load, s);
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+    return s;
+}
+
+// this was added to work around a deadlock in SDL when quitting
+void AUD_cleanup()
+{
+    audio_atexit();
+}
+
+CaptureVoiceOut *AUD_add_capture (
+    AudioState *s,
+    audsettings_t *as,
+    struct audio_capture_ops *ops,
+    void *cb_opaque
+    )
+{
+    CaptureVoiceOut *cap;
+    struct capture_callback *cb;
+
+    if (!s) {
+        /* XXX suppress */
+        s = &glob_audio_state;
+    }
+
+    if (audio_validate_settings (as)) {
+        dolog ("Invalid settings were passed when trying to add capture\n");
+        audio_print_settings (as);
+        goto err0;
+    }
+
+    cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
+    if (!cb) {
+        dolog ("Could not allocate capture callback information, size %zu\n",
+               sizeof (*cb));
+        goto err0;
+    }
+    cb->ops = *ops;
+    cb->opaque = cb_opaque;
+
+    cap = audio_pcm_capture_find_specific (s, as);
+    if (cap) {
+        LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+        return cap;
+    }
+    else {
+        HWVoiceOut *hw;
+        CaptureVoiceOut *cap;
+
+        cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
+        if (!cap) {
+            dolog ("Could not allocate capture voice, size %zu\n",
+                   sizeof (*cap));
+            goto err1;
+        }
+
+        hw = &cap->hw;
+        LIST_INIT (&hw->sw_head);
+        LIST_INIT (&cap->cb_head);
+
+        /* XXX find a more elegant way */
+        hw->samples = 4096 * 4;
+        hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
+                                    sizeof (st_sample_t));
+        if (!hw->mix_buf) {
+            dolog ("Could not allocate capture mix buffer (%d samples)\n",
+                   hw->samples);
+            goto err2;
+        }
+
+        audio_pcm_init_info (&hw->info, as);
+
+        cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+        if (!cap->buf) {
+            dolog ("Could not allocate capture buffer "
+                   "(%d samples, each %d bytes)\n",
+                   hw->samples, 1 << hw->info.shift);
+            goto err3;
+        }
+
+        hw->clip = mixeng_clip
+            [hw->info.nchannels == 2]
+            [hw->info.sign]
+            [hw->info.swap_endianness]
+            [audio_bits_to_index (hw->info.bits)];
+
+        LIST_INSERT_HEAD (&s->cap_head, cap, entries);
+        LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
+
+        hw = NULL;
+        while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
+            audio_attach_capture (s, hw);
+        }
+        return cap;
+
+    err3:
+        qemu_free (cap->hw.mix_buf);
+    err2:
+        qemu_free (cap);
+    err1:
+        qemu_free (cb);
+    err0:
+        return NULL;
+    }
+}
+
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
+{
+    struct capture_callback *cb;
+
+    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
+        if (cb->opaque == cb_opaque) {
+            cb->ops.destroy (cb_opaque);
+            LIST_REMOVE (cb, entries);
+            qemu_free (cb);
+
+            if (!cap->cb_head.lh_first) {
+                SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;
+
+                while (sw) {
+                    SWVoiceCap *sc = (SWVoiceCap *) sw;
+#ifdef DEBUG_CAPTURE
+                    dolog ("freeing %s\n", sw->name);
+#endif
+
+                    sw1 = sw->entries.le_next;
+                    if (sw->rate) {
+                        st_rate_stop (sw->rate);
+                        sw->rate = NULL;
+                    }
+                    LIST_REMOVE (sw, entries);
+                    LIST_REMOVE (sc, entries);
+                    qemu_free (sc);
+                    sw = sw1;
+                }
+                LIST_REMOVE (cap, entries);
+                qemu_free (cap);
+            }
+            return;
+        }
+    }
+}
+
+void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
+{
+    if (sw) {
+        sw->vol.mute = mute;
+        sw->vol.l = nominal_volume.l * lvol / 255;
+        sw->vol.r = nominal_volume.r * rvol / 255;
+    }
+}
+
+void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
+{
+    if (sw) {
+        sw->vol.mute = mute;
+        sw->vol.l = nominal_volume.l * lvol / 255;
+        sw->vol.r = nominal_volume.r * rvol / 255;
+    }
+}
diff --git a/audio/audio.h b/audio/audio.h
new file mode 100644
index 0000000..2c347bf
--- /dev/null
+++ b/audio/audio.h
@@ -0,0 +1,185 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_AUDIO_H
+#define QEMU_AUDIO_H
+
+#include "config.h"
+#include "qemu-common.h"
+#include "sys-queue.h"
+
+typedef void (*audio_callback_fn_t) (void *opaque, int avail);
+
+typedef enum {
+    AUD_FMT_U8,
+    AUD_FMT_S8,
+    AUD_FMT_U16,
+    AUD_FMT_S16,
+    AUD_FMT_U32,
+    AUD_FMT_S32
+} audfmt_e;
+
+#ifdef WORDS_BIGENDIAN
+#define AUDIO_HOST_ENDIANNESS 1
+#else
+#define AUDIO_HOST_ENDIANNESS 0
+#endif
+
+typedef struct {
+    int freq;
+    int nchannels;
+    audfmt_e fmt;
+    int endianness;
+} audsettings_t;
+
+typedef enum {
+    AUD_CNOTIFY_ENABLE,
+    AUD_CNOTIFY_DISABLE
+} audcnotification_e;
+
+struct audio_capture_ops {
+    void (*notify) (void *opaque, audcnotification_e cmd);
+    void (*capture) (void *opaque, void *buf, int size);
+    void (*destroy) (void *opaque);
+};
+
+struct capture_ops {
+    void (*info) (void *opaque);
+    void (*destroy) (void *opaque);
+};
+
+typedef struct CaptureState {
+    void *opaque;
+    struct capture_ops ops;
+    LIST_ENTRY (CaptureState) entries;
+} CaptureState;
+
+typedef struct SWVoiceOut SWVoiceOut;
+typedef struct CaptureVoiceOut CaptureVoiceOut;
+typedef struct SWVoiceIn SWVoiceIn;
+
+typedef struct QEMUSoundCard {
+    AudioState *audio;
+    char *name;
+    LIST_ENTRY (QEMUSoundCard) entries;
+} QEMUSoundCard;
+
+typedef struct QEMUAudioTimeStamp {
+    uint64_t old_ts;
+} QEMUAudioTimeStamp;
+
+void AUD_vlog (const char *cap, const char *fmt, va_list ap);
+void AUD_log (const char *cap, const char *fmt, ...)
+#ifdef __GNUC__
+    __attribute__ ((__format__ (__printf__, 2, 3)))
+#endif
+    ;
+
+extern AudioState glob_audio_state;
+
+AudioState *AUD_init (void);
+void AUD_help (void);
+void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
+void AUD_remove_card (QEMUSoundCard *card);
+CaptureVoiceOut *AUD_add_capture (
+    AudioState *s,
+    audsettings_t *as,
+    struct audio_capture_ops *ops,
+    void *opaque
+    );
+void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque);
+
+SWVoiceOut *AUD_open_out (
+    QEMUSoundCard *card,
+    SWVoiceOut *sw,
+    const char *name,
+    void *callback_opaque,
+    audio_callback_fn_t callback_fn,
+    audsettings_t *settings
+    );
+
+void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
+int  AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
+int  AUD_get_buffer_size_out (SWVoiceOut *sw);
+void AUD_set_active_out (SWVoiceOut *sw, int on);
+int  AUD_is_active_out (SWVoiceOut *sw);
+
+void     AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+
+void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
+void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
+
+SWVoiceIn *AUD_open_in (
+    QEMUSoundCard *card,
+    SWVoiceIn *sw,
+    const char *name,
+    void *callback_opaque,
+    audio_callback_fn_t callback_fn,
+    audsettings_t *settings
+    );
+
+void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
+int  AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
+void AUD_set_active_in (SWVoiceIn *sw, int on);
+int  AUD_is_active_in (SWVoiceIn *sw);
+
+void     AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+
+static inline void *advance (void *p, int incr)
+{
+    uint8_t *d = p;
+    return (d + incr);
+}
+
+uint32_t popcount (uint32_t u);
+uint32_t lsbindex (uint32_t u);
+
+#ifdef __GNUC__
+#define audio_MIN(a, b) ( __extension__ ({      \
+    __typeof (a) ta = a;                        \
+    __typeof (b) tb = b;                        \
+    ((ta)>(tb)?(tb):(ta));                      \
+}))
+
+#define audio_MAX(a, b) ( __extension__ ({      \
+    __typeof (a) ta = a;                        \
+    __typeof (b) tb = b;                        \
+    ((ta)<(tb)?(tb):(ta));                      \
+}))
+#else
+#define audio_MIN(a, b) ((a)>(b)?(b):(a))
+#define audio_MAX(a, b) ((a)<(b)?(b):(a))
+#endif
+
+extern int
+audio_get_backend_count( int  is_input );
+
+extern const char*
+audio_get_backend_name( int   is_input, int  index, const char*  *pinfo );
+
+extern int
+audio_check_backend_name( int  is_input, const char*  name );
+
+#endif  /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
new file mode 100644
index 0000000..6677ec1
--- /dev/null
+++ b/audio/audio_int.h
@@ -0,0 +1,287 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_AUDIO_INT_H
+#define QEMU_AUDIO_INT_H
+
+#include "audio/audio.h"
+
+#ifdef CONFIG_COREAUDIO
+#define FLOAT_MIXENG
+/* #define RECIPROCAL */
+#endif
+#include "mixeng.h"
+
+struct audio_pcm_ops;
+
+typedef enum {
+    AUD_OPT_INT,
+    AUD_OPT_FMT,
+    AUD_OPT_STR,
+    AUD_OPT_BOOL
+} audio_option_tag_e;
+
+struct audio_option {
+    const char *name;
+    audio_option_tag_e tag;
+    void *valp;
+    const char *descr;
+    int *overriddenp;
+    int overridden;
+};
+
+struct audio_callback {
+    void *opaque;
+    audio_callback_fn_t fn;
+};
+
+struct audio_pcm_info {
+    int bits;
+    int sign;
+    int freq;
+    int nchannels;
+    int align;
+    int shift;
+    int bytes_per_second;
+    int swap_endianness;
+};
+
+typedef struct SWVoiceCap SWVoiceCap;
+
+typedef struct HWVoiceOut {
+    int enabled;
+    int pending_disable;
+    struct audio_pcm_info info;
+
+    f_sample *clip;
+
+    int rpos;
+    uint64_t ts_helper;
+
+    st_sample_t *mix_buf;
+
+    int samples;
+    LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
+    LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
+    struct audio_pcm_ops *pcm_ops;
+    LIST_ENTRY (HWVoiceOut) entries;
+} HWVoiceOut;
+
+typedef struct HWVoiceIn {
+    int enabled;
+    struct audio_pcm_info info;
+
+    t_sample *conv;
+
+    int wpos;
+    int total_samples_captured;
+    uint64_t ts_helper;
+
+    st_sample_t *conv_buf;
+
+    int samples;
+    LIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
+    struct audio_pcm_ops *pcm_ops;
+    LIST_ENTRY (HWVoiceIn) entries;
+} HWVoiceIn;
+
+struct SWVoiceOut {
+    struct audio_pcm_info info;
+    t_sample *conv;
+    int64_t ratio;
+    st_sample_t *buf;
+    void *rate;
+    int total_hw_samples_mixed;
+    int active;
+    int empty;
+    HWVoiceOut *hw;
+    char *name;
+    volume_t vol;
+    struct audio_callback callback;
+    LIST_ENTRY (SWVoiceOut) entries;
+};
+
+struct SWVoiceIn {
+    int active;
+    struct audio_pcm_info info;
+    int64_t ratio;
+    void *rate;
+    int total_hw_samples_acquired;
+    st_sample_t *buf;
+    f_sample *clip;
+    HWVoiceIn *hw;
+    char *name;
+    volume_t vol;
+    struct audio_callback callback;
+    LIST_ENTRY (SWVoiceIn) entries;
+};
+
+struct audio_driver {
+    const char *name;
+    const char *descr;
+    struct audio_option *options;
+    void *(*init) (void);
+    void (*fini) (void *);
+    struct audio_pcm_ops *pcm_ops;
+    int can_be_default;
+    int max_voices_out;
+    int max_voices_in;
+    int voice_size_out;
+    int voice_size_in;
+};
+
+struct audio_pcm_ops {
+    int  (*init_out)(HWVoiceOut *hw, audsettings_t *as);
+    void (*fini_out)(HWVoiceOut *hw);
+    int  (*run_out) (HWVoiceOut *hw);
+    int  (*write)   (SWVoiceOut *sw, void *buf, int size);
+    int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
+
+    int  (*init_in) (HWVoiceIn *hw, audsettings_t *as);
+    void (*fini_in) (HWVoiceIn *hw);
+    int  (*run_in)  (HWVoiceIn *hw);
+    int  (*read)    (SWVoiceIn *sw, void *buf, int size);
+    int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
+};
+
+struct capture_callback {
+    struct audio_capture_ops ops;
+    void *opaque;
+    LIST_ENTRY (capture_callback) entries;
+};
+
+struct CaptureVoiceOut {
+    HWVoiceOut hw;
+    void *buf;
+    LIST_HEAD (cb_listhead, capture_callback) cb_head;
+    LIST_ENTRY (CaptureVoiceOut) entries;
+};
+
+struct SWVoiceCap {
+    SWVoiceOut sw;
+    CaptureVoiceOut *cap;
+    LIST_ENTRY (SWVoiceCap) entries;
+};
+
+struct AudioState {
+    struct audio_driver*  drv_in;
+    void*                 drv_in_opaque;
+    struct audio_driver*  drv_out;
+    void*                 drv_out_opaque;
+
+    QEMUTimer *ts;
+    LIST_HEAD (card_listhead, QEMUSoundCard) card_head;
+    LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
+    LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
+    LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
+    int nb_hw_voices_out;
+    int nb_hw_voices_in;
+};
+
+extern struct audio_driver no_audio_driver;
+extern struct audio_driver oss_audio_driver;
+extern struct audio_driver sdl_audio_driver;
+extern struct audio_driver win_audio_driver;
+extern struct audio_driver wav_audio_driver;
+extern struct audio_driver fmod_audio_driver;
+extern struct audio_driver esd_audio_driver;
+extern struct audio_driver alsa_audio_driver;
+extern struct audio_driver coreaudio_audio_driver;
+extern struct audio_driver dsound_audio_driver;
+extern struct audio_driver esd_audio_driver;
+extern volume_t nominal_volume;
+
+void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as);
+void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
+
+int  audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
+int  audio_pcm_hw_get_live_in (HWVoiceIn *hw);
+
+int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
+int  audio_pcm_hw_get_live_out (HWVoiceOut *hw);
+int  audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
+
+int audio_bug (const char *funcname, int cond);
+void *audio_calloc (const char *funcname, int nmemb, size_t size);
+
+#define VOICE_ENABLE 1
+#define VOICE_DISABLE 2
+
+static inline int audio_ring_dist (int dst, int src, int len)
+{
+    return (dst >= src) ? (dst - src) : (len - src + dst);
+}
+
+#if defined __GNUC__
+#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
+#define INIT_FIELD(f) . f
+#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m)))
+#else
+#define GCC_ATTR /**/
+#define INIT_FIELD(f) /**/
+#define GCC_FMT_ATTR(n, m)
+#endif
+
+static void GCC_ATTR dolog (const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+}
+
+#ifdef DEBUG
+static void GCC_ATTR ldebug (const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+}
+#else
+#if defined NDEBUG && defined __GNUC__
+#define ldebug(...)
+#elif defined NDEBUG && defined _MSC_VER
+#define ldebug __noop
+#else
+static void GCC_ATTR ldebug (const char *fmt, ...)
+{
+    (void) fmt;
+}
+#endif
+#endif
+
+#undef GCC_ATTR
+
+#define AUDIO_STRINGIFY_(n) #n
+#define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n)
+
+#if defined _MSC_VER || defined __GNUC__
+#define AUDIO_FUNC __FUNCTION__
+#else
+#define AUDIO_FUNC __FILE__ ":" AUDIO_STRINGIFY (__LINE__)
+#endif
+
+#endif /* audio_int.h */
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
new file mode 100644
index 0000000..c753774
--- /dev/null
+++ b/audio/audio_pt_int.c
@@ -0,0 +1,148 @@
+#include "audio.h"
+
+#define AUDIO_CAP "audio-pt"
+
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (pt->drv, fmt, ap);
+    va_end (ap);
+
+    AUD_log (NULL, "\n");
+    AUD_log (pt->drv, "Reason: %s\n", strerror (err));
+}
+
+int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
+                   void *opaque, const char *drv, const char *cap)
+{
+    int err, err2;
+    const char *efunc;
+
+    p->drv = drv;
+
+    err = pthread_mutex_init (&p->mutex, NULL);
+    if (err) {
+        efunc = "pthread_mutex_init";
+        goto err0;
+    }
+
+    err = pthread_cond_init (&p->cond, NULL);
+    if (err) {
+        efunc = "pthread_cond_init";
+        goto err1;
+    }
+
+    err = pthread_create (&p->thread, NULL, func, opaque);
+    if (err) {
+        efunc = "pthread_create";
+        goto err2;
+    }
+
+    return 0;
+
+ err2:
+    err2 = pthread_cond_destroy (&p->cond);
+    if (err2) {
+        logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+    }
+
+ err1:
+    err2 = pthread_mutex_destroy (&p->mutex);
+    if (err2) {
+        logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+    }
+
+ err0:
+    logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
+    return -1;
+}
+
+int audio_pt_fini (struct audio_pt *p, const char *cap)
+{
+    int err, ret = 0;
+
+    err = pthread_cond_destroy (&p->cond);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+        ret = -1;
+    }
+
+    err = pthread_mutex_destroy (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+        ret = -1;
+    }
+    return ret;
+}
+
+int audio_pt_lock (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_mutex_lock (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_unlock (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_mutex_unlock (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_wait (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_cond_wait (&p->cond, &p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_mutex_unlock (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    err = pthread_cond_signal (&p->cond);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
+{
+    int err;
+    void *ret;
+
+    err = pthread_join (p->thread, &ret);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    *arg = ret;
+    return 0;
+}
diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h
new file mode 100644
index 0000000..0dfff76
--- /dev/null
+++ b/audio/audio_pt_int.h
@@ -0,0 +1,22 @@
+#ifndef QEMU_AUDIO_PT_INT_H
+#define QEMU_AUDIO_PT_INT_H
+
+#include <pthread.h>
+
+struct audio_pt {
+    const char *drv;
+    pthread_t thread;
+    pthread_cond_t cond;
+    pthread_mutex_t mutex;
+};
+
+int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
+                   const char *, const char *);
+int audio_pt_fini (struct audio_pt *, const char *);
+int audio_pt_lock (struct audio_pt *, const char *);
+int audio_pt_unlock (struct audio_pt *, const char *);
+int audio_pt_wait (struct audio_pt *, const char *);
+int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
+int audio_pt_join (struct audio_pt *, void **, const char *);
+
+#endif /* audio_pt_int.h */
diff --git a/audio/audio_template.h b/audio/audio_template.h
new file mode 100644
index 0000000..6f8e166
--- /dev/null
+++ b/audio/audio_template.h
@@ -0,0 +1,577 @@
+/*
+ * QEMU Audio subsystem header
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifdef DAC
+#define NAME "playback"
+#define HWBUF hw->mix_buf
+#define TYPE out
+#define HW HWVoiceOut
+#define SW SWVoiceOut
+#else
+#define NAME "capture"
+#define TYPE in
+#define HW HWVoiceIn
+#define SW SWVoiceIn
+#define HWBUF hw->conv_buf
+#endif
+
+static void glue (audio_init_nb_voices_, TYPE) (
+    AudioState *s,
+    struct audio_driver *drv
+    )
+{
+    int max_voices = glue (drv->max_voices_, TYPE);
+    int voice_size = glue (drv->voice_size_, TYPE);
+
+    if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
+        if (!max_voices) {
+#ifdef DAC
+            dolog ("Driver `%s' does not support " NAME "\n", drv->name);
+#endif
+        }
+        else {
+            dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
+                   drv->name,
+                   glue (s->nb_hw_voices_, TYPE),
+                   max_voices);
+        }
+        glue (s->nb_hw_voices_, TYPE) = max_voices;
+    }
+
+    if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
+        dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
+               drv->name, max_voices);
+        glue (s->nb_hw_voices_, TYPE) = 0;
+    }
+
+    if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
+        dolog ("drv=`%s' voice_size=%d max_voices=0\n",
+               drv->name, voice_size);
+    }
+}
+
+static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
+{
+    if (HWBUF) {
+        qemu_free (HWBUF);
+    }
+
+    HWBUF = NULL;
+}
+
+static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
+{
+    HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
+    if (!HWBUF) {
+        dolog ("Could not allocate " NAME " buffer (%d samples)\n",
+               hw->samples);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
+{
+    if (sw->buf) {
+        qemu_free (sw->buf);
+    }
+
+    if (sw->rate) {
+        st_rate_stop (sw->rate);
+    }
+
+    sw->buf = NULL;
+    sw->rate = NULL;
+}
+
+static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
+{
+    int samples;
+
+#ifdef DAC
+    samples = sw->hw->samples;
+#else
+    samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
+#endif
+
+    sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
+    if (!sw->buf) {
+        dolog ("Could not allocate buffer for `%s' (%d samples)\n",
+               SW_NAME (sw), samples);
+        return -1;
+    }
+
+#ifdef DAC
+    sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
+#else
+    sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
+#endif
+    if (!sw->rate) {
+        qemu_free (sw->buf);
+        sw->buf = NULL;
+        return -1;
+    }
+    return 0;
+}
+
+static int glue (audio_pcm_sw_init_, TYPE) (
+    SW *sw,
+    HW *hw,
+    const char *name,
+    audsettings_t *as
+    )
+{
+    int err;
+
+    audio_pcm_init_info (&sw->info, as);
+    sw->hw = hw;
+    sw->active = 0;
+#ifdef DAC
+    sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
+    sw->total_hw_samples_mixed = 0;
+    sw->empty = 1;
+#else
+    sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
+#endif
+
+#ifdef DAC
+    sw->conv = mixeng_conv
+#else
+    sw->clip = mixeng_clip
+#endif
+        [sw->info.nchannels == 2]
+        [sw->info.sign]
+        [sw->info.swap_endianness]
+        [audio_bits_to_index (sw->info.bits)];
+
+    sw->name = qemu_strdup (name);
+    err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
+    if (err) {
+        qemu_free (sw->name);
+        sw->name = NULL;
+    }
+    return err;
+}
+
+static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
+{
+    glue (audio_pcm_sw_free_resources_, TYPE) (sw);
+    if (sw->name) {
+        qemu_free (sw->name);
+        sw->name = NULL;
+    }
+}
+
+static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
+{
+    LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
+}
+
+static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
+{
+    LIST_REMOVE (sw, entries);
+}
+
+static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
+{
+    HW *hw = *hwp;
+
+    if (!hw->sw_head.lh_first) {
+#ifdef DAC
+        audio_detach_capture (hw);
+#endif
+        LIST_REMOVE (hw, entries);
+        glue (s->nb_hw_voices_, TYPE) += 1;
+        glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
+        BEGIN_NOSIGALRM
+        glue (hw->pcm_ops->fini_, TYPE) (hw);
+        END_NOSIGALRM
+        qemu_free (hw);
+        *hwp = NULL;
+    }
+}
+
+static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw)
+{
+    return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first;
+}
+
+static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
+{
+    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
+        if (hw->enabled) {
+            return hw;
+        }
+    }
+    return NULL;
+}
+
+static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
+    AudioState *s,
+    HW *hw,
+    audsettings_t *as
+    )
+{
+    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
+        if (audio_pcm_info_eq (&hw->info, as)) {
+            return hw;
+        }
+    }
+    return NULL;
+}
+
+static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
+{
+    HW *hw;
+    struct audio_driver *drv = glue(s->drv_, TYPE);
+    int  err;
+
+    if (!glue (s->nb_hw_voices_, TYPE)) {
+        return NULL;
+    }
+
+    if (audio_bug (AUDIO_FUNC, !drv)) {
+        dolog ("No host audio driver\n");
+        return NULL;
+    }
+
+    if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
+        dolog ("Host audio driver without pcm_ops\n");
+        return NULL;
+    }
+
+    hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
+    if (!hw) {
+        dolog ("Can not allocate voice `%s' size %d\n",
+               drv->name, glue (drv->voice_size_, TYPE));
+        return NULL;
+    }
+
+    hw->pcm_ops = drv->pcm_ops;
+    LIST_INIT (&hw->sw_head);
+#ifdef DAC
+    LIST_INIT (&hw->cap_head);
+#endif
+    BEGIN_NOSIGALRM
+    err = glue (hw->pcm_ops->init_, TYPE) (hw, as);
+    END_NOSIGALRM
+    if (err)
+        goto err0;
+
+    if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
+        dolog ("hw->samples=%d\n", hw->samples);
+        goto err1;
+    }
+
+#ifdef DAC
+    hw->clip = mixeng_clip
+#else
+    hw->conv = mixeng_conv
+#endif
+        [hw->info.nchannels == 2]
+        [hw->info.sign]
+        [hw->info.swap_endianness]
+        [audio_bits_to_index (hw->info.bits)];
+
+    if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
+        goto err1;
+    }
+
+    LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
+    glue (s->nb_hw_voices_, TYPE) -= 1;
+#ifdef DAC
+    audio_attach_capture (s, hw);
+#endif
+    return hw;
+
+ err1:
+    BEGIN_NOSIGALRM
+    glue (hw->pcm_ops->fini_, TYPE) (hw);
+    END_NOSIGALRM
+ err0:
+    qemu_free (hw);
+    return NULL;
+}
+
+static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
+{
+    HW *hw;
+
+    if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+        hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
+        if (hw) {
+            return hw;
+        }
+    }
+
+    hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as);
+    if (hw) {
+        return hw;
+    }
+
+    hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
+    if (hw) {
+        return hw;
+    }
+
+    return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL);
+}
+
+static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
+    AudioState *s,
+    const char *sw_name,
+    audsettings_t *as
+    )
+{
+    SW *sw;
+    HW *hw;
+    audsettings_t hw_as;
+
+    if (glue (conf.fixed_, TYPE).enabled) {
+        hw_as = glue (conf.fixed_, TYPE).settings;
+    }
+    else {
+        hw_as = *as;
+    }
+
+    sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
+    if (!sw) {
+        dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
+               sw_name ? sw_name : "unknown", sizeof (*sw));
+        goto err1;
+    }
+
+    hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
+    if (!hw) {
+        goto err2;
+    }
+
+    glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
+
+    if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
+        goto err3;
+    }
+
+    return sw;
+
+err3:
+    glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+    glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
+err2:
+    qemu_free (sw);
+err1:
+    return NULL;
+}
+
+static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
+{
+    glue (audio_pcm_sw_fini_, TYPE) (sw);
+    glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+    glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
+    qemu_free (sw);
+}
+
+void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
+{
+    if (sw) {
+        if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
+            dolog ("card=%p card->audio=%p\n",
+                   card, card ? card->audio : NULL);
+            return;
+        }
+
+        glue (audio_close_, TYPE) (card->audio, sw);
+    }
+}
+
+SW *glue (AUD_open_, TYPE) (
+    QEMUSoundCard *card,
+    SW *sw,
+    const char *name,
+    void *callback_opaque ,
+    audio_callback_fn_t callback_fn,
+    audsettings_t *as
+    )
+{
+    AudioState *s;
+#ifdef DAC
+    int live = 0;
+    SW *old_sw = NULL;
+#endif
+
+    ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
+            name, as->freq, as->nchannels, as->fmt);
+
+    if (audio_bug (AUDIO_FUNC,
+                   !card || !card->audio || !name || !callback_fn || !as)) {
+        dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n",
+               card, card ? card->audio : NULL, name, callback_fn, as);
+        goto fail;
+    }
+
+    s = card->audio;
+
+    if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) {
+        audio_print_settings (as);
+        goto fail;
+    }
+
+    if (audio_bug (AUDIO_FUNC, !glue (s->drv_, TYPE))) {
+        dolog ("Can not open `%s' (no host audio driver)\n", name);
+        goto fail;
+    }
+
+    if (sw && audio_pcm_info_eq (&sw->info, as)) {
+        return sw;
+    }
+
+#ifdef DAC
+    if (conf.plive && sw && (!sw->active && !sw->empty)) {
+        live = sw->total_hw_samples_mixed;
+
+#ifdef DEBUG_PLIVE
+        dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
+        dolog ("Old %s freq %d, bits %d, channels %d\n",
+               SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
+        dolog ("New %s freq %d, bits %d, channels %d\n",
+               name,
+               freq,
+               (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
+               nchannels);
+#endif
+
+        if (live) {
+            old_sw = sw;
+            old_sw->callback.fn = NULL;
+            sw = NULL;
+        }
+    }
+#endif
+
+    if (!glue (conf.fixed_, TYPE).enabled && sw) {
+        glue (AUD_close_, TYPE) (card, sw);
+        sw = NULL;
+    }
+
+    if (sw) {
+        HW *hw = sw->hw;
+
+        if (!hw) {
+            dolog ("Internal logic error voice `%s' has no hardware store\n",
+                   SW_NAME (sw));
+            goto fail;
+        }
+
+        glue (audio_pcm_sw_fini_, TYPE) (sw);
+        if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
+            goto fail;
+        }
+    }
+    else {
+        sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as);
+        if (!sw) {
+            dolog ("Failed to create voice `%s'\n", name);
+            return NULL;
+        }
+    }
+
+    if (sw) {
+        sw->vol = nominal_volume;
+        sw->callback.fn = callback_fn;
+        sw->callback.opaque = callback_opaque;
+
+#ifdef DAC
+        if (live) {
+            int mixed =
+                (live << old_sw->info.shift)
+                * old_sw->info.bytes_per_second
+                / sw->info.bytes_per_second;
+
+#ifdef DEBUG_PLIVE
+            dolog ("Silence will be mixed %d\n", mixed);
+#endif
+            sw->total_hw_samples_mixed += mixed;
+        }
+#endif
+
+#ifdef DEBUG_AUDIO
+        dolog ("%s\n", name);
+        audio_pcm_print_info ("hw", &sw->hw->info);
+        audio_pcm_print_info ("sw", &sw->info);
+#endif
+    }
+
+    return sw;
+
+ fail:
+    glue (AUD_close_, TYPE) (card, sw);
+    return NULL;
+}
+
+int glue (AUD_is_active_, TYPE) (SW *sw)
+{
+    return sw ? sw->active : 0;
+}
+
+void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
+{
+    if (!sw) {
+        return;
+    }
+
+    ts->old_ts = sw->hw->ts_helper;
+}
+
+uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
+{
+    uint64_t delta, cur_ts, old_ts;
+
+    if (!sw) {
+        return 0;
+    }
+
+    cur_ts = sw->hw->ts_helper;
+    old_ts = ts->old_ts;
+    /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
+
+    if (cur_ts >= old_ts) {
+        delta = cur_ts - old_ts;
+    }
+    else {
+        delta = UINT64_MAX - old_ts + cur_ts;
+    }
+
+    if (!delta) {
+        return 0;
+    }
+
+    return (delta * sw->hw->info.freq) / 1000000;
+}
+
+#undef TYPE
+#undef HW
+#undef SW
+#undef HWBUF
+#undef NAME
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
new file mode 100644
index 0000000..f23ebee
--- /dev/null
+++ b/audio/coreaudio.c
@@ -0,0 +1,821 @@
+/*
+ * QEMU OS X CoreAudio audio driver
+ *
+ * Copyright (c) 2008 The Android Open Source Project
+ * Copyright (c) 2005 Mike Kronenberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <CoreAudio/CoreAudio.h>
+#include <string.h>             /* strerror */
+#include <pthread.h>            /* pthread_X */
+
+#include "audio.h"
+
+#define AUDIO_CAP "coreaudio"
+#include "audio_int.h"
+
+#define  ENABLE_IN  1
+
+#if 0
+#  define  D(...)  fprintf(stderr, __VA_ARGS__)
+#else
+#  define  D(...)  ((void)0)
+#endif
+
+struct {
+    int out_buffer_frames;
+    int out_nbuffers;
+    int in_buffer_frames;
+    int in_nbuffers;
+    int isAtexit;
+} conf = {
+    .out_buffer_frames = 512,
+    .out_nbuffers = 4,
+    .in_buffer_frames = 512,
+    .in_nbuffers = 4,
+    .isAtexit = 0
+};
+
+/***************************************************************************************/
+/***************************************************************************************/
+/***                                                                                 ***/
+/***       U T I L I T Y   R O U T I N E S                                           ***/
+/***                                                                                 ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+static void coreaudio_logstatus (OSStatus status)
+{
+    char *str = "BUG";
+
+    switch(status) {
+    case kAudioHardwareNoError:
+        str = "kAudioHardwareNoError";
+        break;
+
+    case kAudioHardwareNotRunningError:
+        str = "kAudioHardwareNotRunningError";
+        break;
+
+    case kAudioHardwareUnspecifiedError:
+        str = "kAudioHardwareUnspecifiedError";
+        break;
+
+    case kAudioHardwareUnknownPropertyError:
+        str = "kAudioHardwareUnknownPropertyError";
+        break;
+
+    case kAudioHardwareBadPropertySizeError:
+        str = "kAudioHardwareBadPropertySizeError";
+        break;
+
+    case kAudioHardwareIllegalOperationError:
+        str = "kAudioHardwareIllegalOperationError";
+        break;
+
+    case kAudioHardwareBadDeviceError:
+        str = "kAudioHardwareBadDeviceError";
+        break;
+
+    case kAudioHardwareBadStreamError:
+        str = "kAudioHardwareBadStreamError";
+        break;
+
+    case kAudioHardwareUnsupportedOperationError:
+        str = "kAudioHardwareUnsupportedOperationError";
+        break;
+
+    case kAudioDeviceUnsupportedFormatError:
+        str = "kAudioDeviceUnsupportedFormatError";
+        break;
+
+    case kAudioDevicePermissionsError:
+        str = "kAudioDevicePermissionsError";
+        break;
+
+    default:
+        AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
+        return;
+    }
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
+    OSStatus status,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_log (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    coreaudio_logstatus (status);
+}
+
+static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
+    OSStatus status,
+    const char *typ,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    coreaudio_logstatus (status);
+}
+
+static void coreaudio_atexit (void)
+{
+    conf.isAtexit = 1;
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/***                                                                                 ***/
+/***       S H A R E D   I N / O U T   V O I C E                                     ***/
+/***                                                                                 ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+typedef struct coreAudioVoice {
+    pthread_mutex_t              mutex;
+    AudioDeviceID                deviceID;
+    Boolean                      isInput;
+    UInt32                       bufferFrameSize;
+    AudioStreamBasicDescription  streamBasicDescription;
+    AudioDeviceIOProc            ioproc;
+    int                          live;
+    int                          decr;
+    int                          pos;
+} coreaudioVoice;
+
+
+static inline UInt32
+coreaudio_voice_isPlaying (coreaudioVoice*  core)
+{
+    OSStatus status;
+    UInt32 result = 0;
+    UInt32 propertySize = sizeof(core->deviceID);
+    status = AudioDeviceGetProperty(
+        core->deviceID, 0, core->isInput,
+        kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr(status,
+                         "Could not determine whether Device is playing\n");
+    }
+    return result;
+}
+
+static int
+coreaudio_voice_lock (coreaudioVoice*  core, const char *fn_name)
+{
+    int err;
+
+    err = pthread_mutex_lock (&core->mutex);
+    if (err) {
+        dolog ("Could not lock voice for %s\nReason: %s\n",
+               fn_name, strerror (err));
+        return -1;
+    }
+    return 0;
+}
+
+static int
+coreaudio_voice_unlock (coreaudioVoice*  core, const char *fn_name)
+{
+    int err;
+
+    err = pthread_mutex_unlock (&core->mutex);
+    if (err) {
+        dolog ("Could not unlock voice for %s\nReason: %s\n",
+               fn_name, strerror (err));
+        return -1;
+    }
+    return 0;
+}
+
+static int
+coreaudio_voice_ctl (coreaudioVoice*  core, int cmd)
+{
+    OSStatus status;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        /* start playback */
+        D("%s: %s started\n", __FUNCTION__, core->isInput ? "input" : "output");
+        if (!coreaudio_voice_isPlaying(core)) {
+            status = AudioDeviceStart(core->deviceID, core->ioproc);
+            if (status != kAudioHardwareNoError) {
+                coreaudio_logerr (status, "Could not resume playback\n");
+            }
+        }
+        break;
+
+    case VOICE_DISABLE:
+        /* stop playback */
+        D("%s: %s stopped\n", __FUNCTION__, core->isInput ? "input" : "output");
+        if (!conf.isAtexit) {
+            if (coreaudio_voice_isPlaying(core)) {
+                status = AudioDeviceStop(core->deviceID, core->ioproc);
+                if (status != kAudioHardwareNoError) {
+                    coreaudio_logerr (status, "Could not pause playback\n");
+                }
+            }
+        }
+        break;
+    }
+    return 0;
+}
+
+static void
+coreaudio_voice_fini (coreaudioVoice*  core)
+{
+    OSStatus status;
+    int err;
+
+    if (!conf.isAtexit) {
+        /* stop playback */
+        coreaudio_voice_ctl(core, VOICE_DISABLE);
+
+        /* remove callback */
+        status = AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+        if (status != kAudioHardwareNoError) {
+            coreaudio_logerr (status, "Could not remove IOProc\n");
+        }
+    }
+    core->deviceID = kAudioDeviceUnknown;
+
+    /* destroy mutex */
+    err = pthread_mutex_destroy(&core->mutex);
+    if (err) {
+        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+    }
+}
+
+
+static int
+coreaudio_voice_init (coreaudioVoice*    core,
+                      audsettings_t*     as,
+                      int                frameSize,
+                      AudioDeviceIOProc  ioproc,
+                      void*              hw,
+                      int                input)
+{
+    OSStatus  status;
+    UInt32    propertySize;
+    int       err;
+    int       bits = 8;
+    AudioValueRange frameRange;
+    const char*  typ = input ? "input" : "playback";
+
+    core->isInput = input ? true : false;
+
+    /* create mutex */
+    err = pthread_mutex_init(&core->mutex, NULL);
+    if (err) {
+        dolog("Could not create mutex\nReason: %s\n", strerror (err));
+        return -1;
+    }
+
+    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
+        bits = 16;
+    }
+
+    // TODO: audio_pcm_init_info (&hw->info, as);
+    /* open default output device */
+   /* note: we use DefaultSystemOutputDevice because DefaultOutputDevice seems to
+    * always link to the internal speakers, and not the ones selected through system properties
+    * go figure...
+    */
+    propertySize = sizeof(core->deviceID);
+    status = AudioHardwareGetProperty(
+        input ? kAudioHardwarePropertyDefaultInputDevice :
+                kAudioHardwarePropertyDefaultSystemOutputDevice,
+        &propertySize,
+        &core->deviceID);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ,
+                           "Could not get default %s device\n", typ);
+        return -1;
+    }
+    if (core->deviceID == kAudioDeviceUnknown) {
+        dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
+        return -1;
+    }
+
+    /* get minimum and maximum buffer frame sizes */
+    propertySize = sizeof(frameRange);
+    status = AudioDeviceGetProperty(
+        core->deviceID,
+        0,
+        core->isInput,
+        kAudioDevicePropertyBufferFrameSizeRange,
+        &propertySize,
+        &frameRange);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ,
+                           "Could not get device buffer frame range\n");
+        return -1;
+    }
+
+    if (frameRange.mMinimum > frameSize) {
+        core->bufferFrameSize = (UInt32) frameRange.mMinimum;
+        dolog ("warning: Upsizing Output Buffer Frames to %f\n", frameRange.mMinimum);
+    }
+    else if (frameRange.mMaximum < frameSize) {
+        core->bufferFrameSize = (UInt32) frameRange.mMaximum;
+        dolog ("warning: Downsizing Output Buffer Frames to %f\n", frameRange.mMaximum);
+    }
+    else {
+        core->bufferFrameSize = frameSize;
+    }
+
+    /* set Buffer Frame Size */
+    propertySize = sizeof(core->bufferFrameSize);
+    status = AudioDeviceSetProperty(
+        core->deviceID,
+        NULL,
+        0,
+        core->isInput,
+        kAudioDevicePropertyBufferFrameSize,
+        propertySize,
+        &core->bufferFrameSize);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ,
+                           "Could not set device buffer frame size %ld\n",
+                           core->bufferFrameSize);
+        return -1;
+    }
+
+    /* get Buffer Frame Size */
+    propertySize = sizeof(core->bufferFrameSize);
+    status = AudioDeviceGetProperty(
+        core->deviceID,
+        0,
+        core->isInput,
+        kAudioDevicePropertyBufferFrameSize,
+        &propertySize,
+        &core->bufferFrameSize);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ,
+                           "Could not get device buffer frame size\n");
+        return -1;
+    }
+    // TODO: hw->samples = *pNBuffers * core->bufferFrameSize;
+
+    /* get StreamFormat */
+    propertySize = sizeof(core->streamBasicDescription);
+    status = AudioDeviceGetProperty(
+        core->deviceID,
+        0,
+        core->isInput,
+        kAudioDevicePropertyStreamFormat,
+        &propertySize,
+        &core->streamBasicDescription);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ,
+                           "Could not get Device Stream properties\n");
+        core->deviceID = kAudioDeviceUnknown;
+        return -1;
+    }
+
+    /* set Samplerate */
+    core->streamBasicDescription.mSampleRate = (Float64) as->freq;
+    propertySize = sizeof(core->streamBasicDescription);
+    status = AudioDeviceSetProperty(
+        core->deviceID,
+        0,
+        0,
+        core->isInput,
+        kAudioDevicePropertyStreamFormat,
+        propertySize,
+        &core->streamBasicDescription);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
+                           as->freq);
+        core->deviceID = kAudioDeviceUnknown;
+        return -1;
+    }
+
+    /* set Callback */
+    core->ioproc = ioproc;
+    status = AudioDeviceAddIOProc(core->deviceID, ioproc, hw);
+    if (status != kAudioHardwareNoError) {
+        coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
+        core->deviceID = kAudioDeviceUnknown;
+        return -1;
+    }
+
+    /* start Playback */
+    if (!input && !coreaudio_voice_isPlaying(core)) {
+        status = AudioDeviceStart(core->deviceID, core->ioproc);
+        if (status != kAudioHardwareNoError) {
+            coreaudio_logerr2 (status, typ, "Could not start playback\n");
+            AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+            core->deviceID = kAudioDeviceUnknown;
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+
+/***************************************************************************************/
+/***************************************************************************************/
+/***                                                                                 ***/
+/***       O U T P U T   V O I C E                                                   ***/
+/***                                                                                 ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+typedef struct coreaudioVoiceOut {
+    HWVoiceOut                   hw;
+    coreaudioVoice               core[1];
+} coreaudioVoiceOut;
+
+#define  CORE_OUT(hw)  ((coreaudioVoiceOut*)(hw))->core
+
+
+static int
+coreaudio_run_out (HWVoiceOut *hw)
+{
+    int live, decr;
+    coreaudioVoice  *core = CORE_OUT(hw);
+
+    if (coreaudio_voice_lock (core, "coreaudio_run_out")) {
+        return 0;
+    }
+
+    live = audio_pcm_hw_get_live_out (hw);
+
+    if (core->decr > live) {
+        ldebug ("core->decr %d live %d core->live %d\n",
+                core->decr,
+                live,
+                core->live);
+    }
+
+    decr        = audio_MIN (core->decr, live);
+    core->decr -= decr;
+    core->live  = live - decr;
+    hw->rpos    = core->pos;
+
+    coreaudio_voice_unlock (core, "coreaudio_run_out");
+    return decr;
+}
+
+
+/* callback to feed audiooutput buffer */
+static OSStatus
+audioOutDeviceIOProc(
+    AudioDeviceID inDevice,
+    const AudioTimeStamp* inNow,
+    const AudioBufferList* inInputData,
+    const AudioTimeStamp* inInputTime,
+    AudioBufferList* outOutputData,
+    const AudioTimeStamp* inOutputTime,
+    void* hwptr)
+{
+    UInt32 frame, frameCount;
+    float *out = outOutputData->mBuffers[0].mData;
+    HWVoiceOut *hw = hwptr;
+    coreaudioVoice *core = CORE_OUT(hw);
+    int rpos, live;
+    st_sample_t *src;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+    const float scale = 1.f / UINT_MAX;
+#else
+    const float scale = UINT_MAX;
+#endif
+#endif
+
+    if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
+        inInputTime = 0;
+        return 0;
+    }
+
+    frameCount = core->bufferFrameSize;
+    live = core->live;
+
+    /* if there are not enough samples, set signal and return */
+    if (live < frameCount) {
+        inInputTime = 0;
+        coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
+        return 0;
+    }
+
+    rpos = core->pos;
+    src = hw->mix_buf + rpos;
+
+    /* fill buffer */
+    for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+        *out++ = src[frame].l; /* left channel */
+        *out++ = src[frame].r; /* right channel */
+#else
+#ifdef RECIPROCAL
+        *out++ = src[frame].l * scale; /* left channel */
+        *out++ = src[frame].r * scale; /* right channel */
+#else
+        *out++ = src[frame].l / scale; /* left channel */
+        *out++ = src[frame].r / scale; /* right channel */
+#endif
+#endif
+    }
+
+    rpos = (rpos + frameCount) % hw->samples;
+    core->decr += frameCount;
+    core->pos  = rpos;
+
+    coreaudio_voice_unlock (core, "audioDeviceIOProc");
+    return 0;
+}
+
+static int
+coreaudio_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int
+coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    coreaudioVoice*  core = CORE_OUT(hw);
+    int              err;
+
+    audio_pcm_init_info (&hw->info, as);
+
+    err = coreaudio_voice_init( core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0 );
+    if (err < 0)
+        return err;
+
+    hw->samples = core->bufferFrameSize * conf.out_nbuffers;
+    return 0;
+}
+
+static void
+coreaudio_fini_out (HWVoiceOut *hw)
+{
+
+    coreaudioVoice*  core = CORE_OUT(hw);
+
+    coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    coreaudioVoice*  core = CORE_OUT(hw);
+
+    return coreaudio_voice_ctl(core, cmd);
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/***                                                                                 ***/
+/***       I N P U T   V O I C E                                                     ***/
+/***                                                                                 ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+
+
+typedef struct coreaudioVoiceIn {
+    HWVoiceIn        hw;
+    coreaudioVoice   core[1];
+} coreaudioVoiceIn;
+
+#define  CORE_IN(hw)  ((coreaudioVoiceIn*)(hw))->core
+
+
+static int
+coreaudio_run_in (HWVoiceIn *hw)
+{
+    int decr;
+
+    coreaudioVoice  *core = CORE_IN(hw);
+
+    if (coreaudio_voice_lock (core, "coreaudio_run_in")) {
+        return 0;
+    }
+    D("%s: core.decr=%d core.pos=%d\n", __FUNCTION__, core->decr, core->pos);
+    decr        = core->decr;
+    core->decr -= decr;
+    hw->wpos    = core->pos;
+
+    coreaudio_voice_unlock (core, "coreaudio_run_in");
+    return decr;
+}
+
+
+/* callback to feed audiooutput buffer */
+static OSStatus
+audioInDeviceIOProc(
+    AudioDeviceID inDevice,
+    const AudioTimeStamp* inNow,
+    const AudioBufferList* inInputData,
+    const AudioTimeStamp* inInputTime,
+    AudioBufferList* outOutputData,
+    const AudioTimeStamp* inOutputTime,
+    void* hwptr)
+{
+    UInt32 frame, frameCount;
+    float *in = inInputData->mBuffers[0].mData;
+    HWVoiceIn *hw = hwptr;
+    coreaudioVoice *core = CORE_IN(hw);
+    int wpos, avail;
+    st_sample_t *dst;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+    const float scale = 1.f / UINT_MAX;
+#else
+    const float scale = UINT_MAX;
+#endif
+#endif
+
+    if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
+        inInputTime = 0;
+        return 0;
+    }
+
+    frameCount = core->bufferFrameSize;
+    avail      = hw->samples - hw->total_samples_captured - core->decr;
+
+    D("%s: enter avail=%d core.decr=%d core.pos=%d hw.samples=%d hw.total_samples_captured=%d frameCount=%d\n",
+      __FUNCTION__, avail, core->decr, core->pos, hw->samples, hw->total_samples_captured, (int)frameCount);
+
+    /* if there are not enough samples, set signal and return */
+    if (avail < frameCount) {
+        inInputTime = 0;
+        coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
+        return 0;
+    }
+
+    wpos = core->pos;
+    dst  = hw->conv_buf + wpos;
+
+    /* fill buffer */
+    for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+        dst[frame].l = *in++; /* left channel */
+        dst[frame].r = *in++; /* right channel */
+#else
+#ifdef RECIPROCAL
+        dst[frame].l = *in++ * scale; /* left channel */
+        dst[frame].r = *in++ * scale; /* right channel */
+#else
+        dst[frame].l = *in++ / scale; /* left channel */
+        dst[frame].r = *in++ / scale; /* right channel */
+#endif
+#endif
+    }
+
+    wpos = (wpos + frameCount) % hw->samples;
+    core->decr += frameCount;
+    core->pos   = wpos;
+
+    D("exit: core.decr=%d core.pos=%d\n", core->decr, core->pos);
+    coreaudio_voice_unlock (core, "audioDeviceIOProc");
+    return 0;
+}
+
+static int
+coreaudio_read (SWVoiceIn *sw, void *buf, int len)
+{
+    int  result = audio_pcm_sw_read(sw, buf, len);
+    D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result);
+    return result;
+}
+
+static int
+coreaudio_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    coreaudioVoice*  core = CORE_IN(hw);
+    int              err;
+
+    audio_pcm_init_info (&hw->info, as);
+
+    err = coreaudio_voice_init( core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1 );
+    if (err < 0) {
+        return err;
+    }
+
+    hw->samples = core->bufferFrameSize * conf.in_nbuffers;
+    return 0;
+}
+
+static void
+coreaudio_fini_in (HWVoiceIn *hw)
+{
+
+    coreaudioVoice*  core = CORE_IN(hw);
+
+    coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    coreaudioVoice*  core = CORE_IN(hw);
+
+    return coreaudio_voice_ctl(core, cmd);
+}
+
+static void*
+coreaudio_audio_init (void)
+{
+    atexit(coreaudio_atexit);
+    return &coreaudio_audio_init;
+}
+
+static void
+coreaudio_audio_fini (void *opaque)
+{
+    (void) opaque;
+}
+
+static struct audio_option coreaudio_options[] = {
+    {"OUT_BUFFER_SIZE", AUD_OPT_INT, &conf.out_buffer_frames,
+     "Size of the output buffer in frames", NULL, 0},
+    {"OUT_BUFFER_COUNT", AUD_OPT_INT, &conf.out_nbuffers,
+     "Number of output buffers", NULL, 0},
+    {"IN_BUFFER_SIZE", AUD_OPT_INT, &conf.in_buffer_frames,
+     "Size of the input buffer in frames", NULL, 0},
+    {"IN_BUFFER_COUNT", AUD_OPT_INT, &conf.in_nbuffers,
+     "Number of input buffers", NULL, 0},
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops coreaudio_pcm_ops = {
+    coreaudio_init_out,
+    coreaudio_fini_out,
+    coreaudio_run_out,
+    coreaudio_write,
+    coreaudio_ctl_out,
+
+#if ENABLE_IN
+    coreaudio_init_in,
+    coreaudio_fini_in,
+    coreaudio_run_in,
+    coreaudio_read,
+    coreaudio_ctl_in
+#else
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+#endif
+};
+
+struct audio_driver coreaudio_audio_driver = {
+    INIT_FIELD (name           = ) "coreaudio",
+    INIT_FIELD (descr          = )
+    "CoreAudio (developer.apple.com/audio/coreaudio.html)",
+    INIT_FIELD (options        = ) coreaudio_options,
+    INIT_FIELD (init           = ) coreaudio_audio_init,
+    INIT_FIELD (fini           = ) coreaudio_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &coreaudio_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+#if ENABLE_IN
+    INIT_FIELD (max_voices_out = ) 1,
+    INIT_FIELD (max_voices_in  = ) 1,
+    INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (coreaudioVoiceIn),
+#else
+    INIT_FIELD (max_voices_out = ) 1,
+    INIT_FIELD (max_voices_in  = ) 0,
+    INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
+    INIT_FIELD (voice_size_in  = ) 0,
+#endif
+};
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
new file mode 100644
index 0000000..9cc0b9d
--- /dev/null
+++ b/audio/dsound_template.h
@@ -0,0 +1,291 @@
+/*
+ * QEMU DirectSound audio driver header
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef DSBTYPE_IN
+#define NAME "capture buffer"
+#define NAME2 "DirectSoundCapture"
+#define TYPE in
+#define IFACE IDirectSoundCaptureBuffer
+#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
+#define FIELD dsound_capture_buffer
+#define FIELD2 dsound_capture
+#else
+#define NAME "playback buffer"
+#define NAME2 "DirectSound"
+#define TYPE out
+#define IFACE IDirectSoundBuffer
+#define BUFPTR LPDIRECTSOUNDBUFFER
+#define FIELD dsound_buffer
+#define FIELD2 dsound
+#endif
+
+static int glue (dsound_unlock_, TYPE) (
+    BUFPTR buf,
+    LPVOID p1,
+    LPVOID p2,
+    DWORD blen1,
+    DWORD blen2
+    )
+{
+    HRESULT hr;
+
+    hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not unlock " NAME "\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static int glue (dsound_lock_, TYPE) (
+    BUFPTR buf,
+    struct audio_pcm_info *info,
+    DWORD pos,
+    DWORD len,
+    LPVOID *p1p,
+    LPVOID *p2p,
+    DWORD *blen1p,
+    DWORD *blen2p,
+    int entire
+    )
+{
+    HRESULT hr;
+    int i;
+    LPVOID p1 = NULL, p2 = NULL;
+    DWORD blen1 = 0, blen2 = 0;
+    DWORD flag;
+
+#ifdef DSBTYPE_IN
+    flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
+#else
+    flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
+#endif
+    for (i = 0; i < conf.lock_retries; ++i) {
+        hr = glue (IFACE, _Lock) (
+            buf,
+            pos,
+            len,
+            &p1,
+            &blen1,
+            &p2,
+            &blen2,
+            flag
+            );
+
+        if (FAILED (hr)) {
+#ifndef DSBTYPE_IN
+            if (hr == DSERR_BUFFERLOST) {
+                if (glue (dsound_restore_, TYPE) (buf)) {
+                    dsound_logerr (hr, "Could not lock " NAME "\n");
+                    goto fail;
+                }
+                continue;
+            }
+#endif
+            dsound_logerr (hr, "Could not lock " NAME "\n");
+            goto fail;
+        }
+
+        break;
+    }
+
+    if (i == conf.lock_retries) {
+        dolog ("%d attempts to lock " NAME " failed\n", i);
+        goto fail;
+    }
+
+    if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
+        dolog ("DirectSound returned misaligned buffer %ld %ld\n",
+               blen1, blen2);
+        glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
+        goto fail;
+    }
+
+    if (!p1 && blen1) {
+        dolog ("warning: !p1 && blen1=%ld\n", blen1);
+        blen1 = 0;
+    }
+
+    if (!p2 && blen2) {
+        dolog ("warning: !p2 && blen2=%ld\n", blen2);
+        blen2 = 0;
+    }
+
+    *p1p = p1;
+    *p2p = p2;
+    *blen1p = blen1;
+    *blen2p = blen2;
+    return 0;
+
+ fail:
+    *p1p = NULL - 1;
+    *p2p = NULL - 1;
+    *blen1p = -1;
+    *blen2p = -1;
+    return -1;
+}
+
+#ifdef DSBTYPE_IN
+static void dsound_fini_in (HWVoiceIn *hw)
+#else
+static void dsound_fini_out (HWVoiceOut *hw)
+#endif
+{
+    HRESULT hr;
+#ifdef DSBTYPE_IN
+    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+#else
+    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+#endif
+
+    if (ds->FIELD) {
+        hr = glue (IFACE, _Stop) (ds->FIELD);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not stop " NAME "\n");
+        }
+
+        hr = glue (IFACE, _Release) (ds->FIELD);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not release " NAME "\n");
+        }
+        ds->FIELD = NULL;
+    }
+}
+
+#ifdef DSBTYPE_IN
+static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
+#else
+static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
+#endif
+{
+    int err;
+    HRESULT hr;
+    dsound *s = &glob_dsound;
+    WAVEFORMATEX wfx;
+    audsettings_t obt_as;
+#ifdef DSBTYPE_IN
+    const char *typ = "ADC";
+    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+    DSCBUFFERDESC bd;
+    DSCBCAPS bc;
+#else
+    const char *typ = "DAC";
+    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+    DSBUFFERDESC bd;
+    DSBCAPS bc;
+#endif
+
+    if (!s->FIELD2) {
+        dolog ("Attempt to initialize voice without " NAME2 " object\n");
+        return -1;
+    }
+
+    err = waveformat_from_audio_settings (&wfx, as);
+    if (err) {
+        return -1;
+    }
+
+    memset (&bd, 0, sizeof (bd));
+    bd.dwSize = sizeof (bd);
+    bd.lpwfxFormat = &wfx;
+#ifdef DSBTYPE_IN
+    bd.dwBufferBytes = conf.bufsize_in;
+    hr = IDirectSoundCapture_CreateCaptureBuffer (
+        s->dsound_capture,
+        &bd,
+        &ds->dsound_capture_buffer,
+        NULL
+        );
+#else
+    bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2;
+    bd.dwBufferBytes = conf.bufsize_out;
+    hr = IDirectSound_CreateSoundBuffer (
+        s->dsound,
+        &bd,
+        &ds->dsound_buffer,
+        NULL
+        );
+#endif
+
+    if (FAILED (hr)) {
+        dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
+        return -1;
+    }
+
+    hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
+    if (FAILED (hr)) {
+        dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
+        goto fail0;
+    }
+
+#ifdef DEBUG_DSOUND
+    dolog (NAME "\n");
+    print_wave_format (&wfx);
+#endif
+
+    memset (&bc, 0, sizeof (bc));
+    bc.dwSize = sizeof (bc);
+
+    hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
+    if (FAILED (hr)) {
+        dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
+        goto fail0;
+    }
+
+    err = waveformat_to_audio_settings (&wfx, &obt_as);
+    if (err) {
+        goto fail0;
+    }
+
+    ds->first_time = 1;
+    obt_as.endianness = 0;
+    audio_pcm_init_info (&hw->info, &obt_as);
+
+    if (bc.dwBufferBytes & hw->info.align) {
+        dolog (
+            "GetCaps returned misaligned buffer size %ld, alignment %d\n",
+            bc.dwBufferBytes, hw->info.align + 1
+            );
+    }
+    hw->samples = bc.dwBufferBytes >> hw->info.shift;
+
+#ifdef DEBUG_DSOUND
+    dolog ("caps %ld, desc %ld\n",
+           bc.dwBufferBytes, bd.dwBufferBytes);
+
+    dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
+           hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
+#endif
+    return 0;
+
+ fail0:
+    glue (dsound_fini_, TYPE) (hw);
+    return -1;
+}
+
+#undef NAME
+#undef TYPE
+#undef IFACE
+#undef BUFPTR
+#undef FIELD
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
new file mode 100644
index 0000000..8284067
--- /dev/null
+++ b/audio/dsoundaudio.c
@@ -0,0 +1,1086 @@
+/*
+ * QEMU DirectSound audio driver
+ *
+ * Copyright (c) 2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation
+ */
+
+#include "audio.h"
+
+#define AUDIO_CAP "dsound"
+#include "audio_int.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+#include <objbase.h>
+#include <dsound.h>
+
+/* #define DEBUG_DSOUND */
+
+static struct {
+    int lock_retries;
+    int restore_retries;
+    int getstatus_retries;
+    int set_primary;
+    int bufsize_in;
+    int bufsize_out;
+    audsettings_t settings;
+    int latency_millis;
+} conf = {
+    1,
+    1,
+    1,
+    0,
+    16384,
+    16384,
+    {
+        44100,
+        2,
+        AUD_FMT_S16
+    },
+    10
+};
+
+typedef struct {
+    LPDIRECTSOUND dsound;
+    LPDIRECTSOUNDCAPTURE dsound_capture;
+    LPDIRECTSOUNDBUFFER dsound_primary_buffer;
+    audsettings_t settings;
+} dsound;
+
+static dsound glob_dsound;
+
+typedef struct {
+    HWVoiceOut hw;
+    LPDIRECTSOUNDBUFFER dsound_buffer;
+    DWORD old_pos;
+    int first_time;
+#ifdef DEBUG_DSOUND
+    DWORD old_ppos;
+    DWORD played;
+    DWORD mixed;
+#endif
+} DSoundVoiceOut;
+
+typedef struct {
+    HWVoiceIn hw;
+    int first_time;
+    LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
+} DSoundVoiceIn;
+
+static void dsound_log_hresult (HRESULT hr)
+{
+    const char *str = "BUG";
+
+    switch (hr) {
+    case DS_OK:
+        str = "The method succeeded";
+        break;
+#ifdef DS_NO_VIRTUALIZATION
+    case DS_NO_VIRTUALIZATION:
+        str = "The buffer was created, but another 3D algorithm was substituted";
+        break;
+#endif
+#ifdef DS_INCOMPLETE
+    case DS_INCOMPLETE:
+        str = "The method succeeded, but not all the optional effects were obtained";
+        break;
+#endif
+#ifdef DSERR_ACCESSDENIED
+    case DSERR_ACCESSDENIED:
+        str = "The request failed because access was denied";
+        break;
+#endif
+#ifdef DSERR_ALLOCATED
+    case DSERR_ALLOCATED:
+        str = "The request failed because resources, such as a priority level, were already in use by another caller";
+        break;
+#endif
+#ifdef DSERR_ALREADYINITIALIZED
+    case DSERR_ALREADYINITIALIZED:
+        str = "The object is already initialized";
+        break;
+#endif
+#ifdef DSERR_BADFORMAT
+    case DSERR_BADFORMAT:
+        str = "The specified wave format is not supported";
+        break;
+#endif
+#ifdef DSERR_BADSENDBUFFERGUID
+    case DSERR_BADSENDBUFFERGUID:
+        str = "The GUID specified in an audiopath file does not match a valid mix-in buffer";
+        break;
+#endif
+#ifdef DSERR_BUFFERLOST
+    case DSERR_BUFFERLOST:
+        str = "The buffer memory has been lost and must be restored";
+        break;
+#endif
+#ifdef DSERR_BUFFERTOOSMALL
+    case DSERR_BUFFERTOOSMALL:
+        str = "The buffer size is not great enough to enable effects processing";
+        break;
+#endif
+#ifdef DSERR_CONTROLUNAVAIL
+    case DSERR_CONTROLUNAVAIL:
+        str = "The buffer control (volume, pan, and so on) requested by the caller is not available. Controls must be specified when the buffer is created, using the dwFlags member of DSBUFFERDESC";
+        break;
+#endif
+#ifdef DSERR_DS8_REQUIRED
+    case DSERR_DS8_REQUIRED:
+        str = "A DirectSound object of class CLSID_DirectSound8 or later is required for the requested functionality. For more information, see IDirectSound8 Interface";
+        break;
+#endif
+#ifdef DSERR_FXUNAVAILABLE
+    case DSERR_FXUNAVAILABLE:
+        str = "The effects requested could not be found on the system, or they are in the wrong order or in the wrong location; for example, an effect expected in hardware was found in software";
+        break;
+#endif
+#ifdef DSERR_GENERIC
+    case DSERR_GENERIC :
+        str = "An undetermined error occurred inside the DirectSound subsystem";
+        break;
+#endif
+#ifdef DSERR_INVALIDCALL
+    case DSERR_INVALIDCALL:
+        str = "This function is not valid for the current state of this object";
+        break;
+#endif
+#ifdef DSERR_INVALIDPARAM
+    case DSERR_INVALIDPARAM:
+        str = "An invalid parameter was passed to the returning function";
+        break;
+#endif
+#ifdef DSERR_NOAGGREGATION
+    case DSERR_NOAGGREGATION:
+        str = "The object does not support aggregation";
+        break;
+#endif
+#ifdef DSERR_NODRIVER
+    case DSERR_NODRIVER:
+        str = "No sound driver is available for use, or the given GUID is not a valid DirectSound device ID";
+        break;
+#endif
+#ifdef DSERR_NOINTERFACE
+    case DSERR_NOINTERFACE:
+        str = "The requested COM interface is not available";
+        break;
+#endif
+#ifdef DSERR_OBJECTNOTFOUND
+    case DSERR_OBJECTNOTFOUND:
+        str = "The requested object was not found";
+        break;
+#endif
+#ifdef DSERR_OTHERAPPHASPRIO
+    case DSERR_OTHERAPPHASPRIO:
+        str = "Another application has a higher priority level, preventing this call from succeeding";
+        break;
+#endif
+#ifdef DSERR_OUTOFMEMORY
+    case DSERR_OUTOFMEMORY:
+        str = "The DirectSound subsystem could not allocate sufficient memory to complete the caller's request";
+        break;
+#endif
+#ifdef DSERR_PRIOLEVELNEEDED
+    case DSERR_PRIOLEVELNEEDED:
+        str = "A cooperative level of DSSCL_PRIORITY or higher is required";
+        break;
+#endif
+#ifdef DSERR_SENDLOOP
+    case DSERR_SENDLOOP:
+        str = "A circular loop of send effects was detected";
+        break;
+#endif
+#ifdef DSERR_UNINITIALIZED
+    case DSERR_UNINITIALIZED:
+        str = "The Initialize method has not been called or has not been called successfully before other methods were called";
+        break;
+#endif
+#ifdef DSERR_UNSUPPORTED
+    case DSERR_UNSUPPORTED:
+        str = "The function called is not supported at this time";
+        break;
+#endif
+    default:
+        AUD_log (AUDIO_CAP, "Reason: Unknown (HRESULT %#lx)\n", hr);
+        return;
+    }
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+}
+
+static void GCC_FMT_ATTR (2, 3) dsound_logerr (
+    HRESULT hr,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    dsound_log_hresult (hr);
+}
+
+static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
+    HRESULT hr,
+    const char *typ,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    dsound_log_hresult (hr);
+}
+
+static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis)
+{
+    return (millis * info->bytes_per_second) / 1000;
+}
+
+#ifdef DEBUG_DSOUND
+static void print_wave_format (WAVEFORMATEX *wfx)
+{
+    dolog ("tag             = %d\n", wfx->wFormatTag);
+    dolog ("nChannels       = %d\n", wfx->nChannels);
+    dolog ("nSamplesPerSec  = %ld\n", wfx->nSamplesPerSec);
+    dolog ("nAvgBytesPerSec = %ld\n", wfx->nAvgBytesPerSec);
+    dolog ("nBlockAlign     = %d\n", wfx->nBlockAlign);
+    dolog ("wBitsPerSample  = %d\n", wfx->wBitsPerSample);
+    dolog ("cbSize          = %d\n", wfx->cbSize);
+}
+#endif
+
+static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb)
+{
+    HRESULT hr;
+    int i;
+
+    for (i = 0; i < conf.restore_retries; ++i) {
+        hr = IDirectSoundBuffer_Restore (dsb);
+
+        switch (hr) {
+        case DS_OK:
+            return 0;
+
+        case DSERR_BUFFERLOST:
+            continue;
+
+        default:
+            dsound_logerr (hr, "Could not restore playback buffer\n");
+            return -1;
+        }
+    }
+
+    dolog ("%d attempts to restore playback buffer failed\n", i);
+    return -1;
+}
+
+static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
+{
+    memset (wfx, 0, sizeof (*wfx));
+
+    wfx->wFormatTag = WAVE_FORMAT_PCM;
+    wfx->nChannels = as->nchannels;
+    wfx->nSamplesPerSec = as->freq;
+    wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
+    wfx->nBlockAlign = 1 << (as->nchannels == 2);
+    wfx->cbSize = 0;
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        wfx->wBitsPerSample = 8;
+        break;
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+        wfx->wBitsPerSample = 16;
+        wfx->nAvgBytesPerSec <<= 1;
+        wfx->nBlockAlign <<= 1;
+        break;
+
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        wfx->wBitsPerSample = 32;
+        wfx->nAvgBytesPerSec <<= 2;
+        wfx->nBlockAlign <<= 2;
+        break;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", as->freq);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
+{
+    if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
+        dolog ("Invalid wave format, tag is not PCM, but %d\n",
+               wfx->wFormatTag);
+        return -1;
+    }
+
+    if (!wfx->nSamplesPerSec) {
+        dolog ("Invalid wave format, frequency is zero\n");
+        return -1;
+    }
+    as->freq = wfx->nSamplesPerSec;
+
+    switch (wfx->nChannels) {
+    case 1:
+        as->nchannels = 1;
+        break;
+
+    case 2:
+        as->nchannels = 2;
+        break;
+
+    default:
+        dolog (
+            "Invalid wave format, number of channels is not 1 or 2, but %d\n",
+            wfx->nChannels
+            );
+        return -1;
+    }
+
+    switch (wfx->wBitsPerSample) {
+    case 8:
+        as->fmt = AUD_FMT_U8;
+        break;
+
+    case 16:
+        as->fmt = AUD_FMT_S16;
+        break;
+
+    case 32:
+        as->fmt = AUD_FMT_S32;
+        break;
+
+    default:
+        dolog ("Invalid wave format, bits per sample is not "
+               "8, 16 or 32, but %d\n",
+               wfx->wBitsPerSample);
+        return -1;
+    }
+
+    return 0;
+}
+
+#include "dsound_template.h"
+#define DSBTYPE_IN
+#include "dsound_template.h"
+#undef DSBTYPE_IN
+
+static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp)
+{
+    HRESULT hr;
+    int i;
+
+    for (i = 0; i < conf.getstatus_retries; ++i) {
+        hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not get playback buffer status\n");
+            return -1;
+        }
+
+        if (*statusp & DSERR_BUFFERLOST) {
+            if (dsound_restore_out (dsb)) {
+                return -1;
+            }
+            continue;
+        }
+        break;
+    }
+
+    return 0;
+}
+
+static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
+                                 DWORD *statusp)
+{
+    HRESULT hr;
+
+    hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not get capture buffer status\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
+{
+    int src_len1 = dst_len;
+    int src_len2 = 0;
+    int pos = hw->rpos + dst_len;
+    st_sample_t *src1 = hw->mix_buf + hw->rpos;
+    st_sample_t *src2 = NULL;
+
+    if (pos > hw->samples) {
+        src_len1 = hw->samples - hw->rpos;
+        src2 = hw->mix_buf;
+        src_len2 = dst_len - src_len1;
+        pos = src_len2;
+    }
+
+    if (src_len1) {
+        hw->clip (dst, src1, src_len1);
+    }
+
+    if (src_len2) {
+        dst = advance (dst, src_len1 << hw->info.shift);
+        hw->clip (dst, src2, src_len2);
+    }
+
+    hw->rpos = pos % hw->samples;
+}
+
+static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb)
+{
+    int err;
+    LPVOID p1, p2;
+    DWORD blen1, blen2, len1, len2;
+
+    err = dsound_lock_out (
+        dsb,
+        &hw->info,
+        0,
+        hw->samples << hw->info.shift,
+        &p1, &p2,
+        &blen1, &blen2,
+        1
+        );
+    if (err) {
+        return;
+    }
+
+    len1 = blen1 >> hw->info.shift;
+    len2 = blen2 >> hw->info.shift;
+
+#ifdef DEBUG_DSOUND
+    dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
+           p1, blen1, len1,
+           p2, blen2, len2);
+#endif
+
+    if (p1 && len1) {
+        audio_pcm_info_clear_buf (&hw->info, p1, len1);
+    }
+
+    if (p2 && len2) {
+        audio_pcm_info_clear_buf (&hw->info, p2, len2);
+    }
+
+    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
+}
+
+static void dsound_close (dsound *s)
+{
+    HRESULT hr;
+
+    if (s->dsound_primary_buffer) {
+        hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not release primary buffer\n");
+        }
+        s->dsound_primary_buffer = NULL;
+    }
+}
+
+static int dsound_open (dsound *s)
+{
+    int err;
+    HRESULT hr;
+    WAVEFORMATEX wfx;
+    DSBUFFERDESC dsbd;
+    HWND hwnd;
+
+    hwnd = GetForegroundWindow ();
+    hr = IDirectSound_SetCooperativeLevel (
+        s->dsound,
+        hwnd,
+        DSSCL_PRIORITY
+        );
+
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not set cooperative level for window %p\n",
+                       hwnd);
+        return -1;
+    }
+
+    if (!conf.set_primary) {
+        return 0;
+    }
+
+    err = waveformat_from_audio_settings (&wfx, &conf.settings);
+    if (err) {
+        return -1;
+    }
+
+    memset (&dsbd, 0, sizeof (dsbd));
+    dsbd.dwSize = sizeof (dsbd);
+    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
+    dsbd.dwBufferBytes = 0;
+    dsbd.lpwfxFormat = NULL;
+
+    hr = IDirectSound_CreateSoundBuffer (
+        s->dsound,
+        &dsbd,
+        &s->dsound_primary_buffer,
+        NULL
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not create primary playback buffer\n");
+        return -1;
+    }
+
+    hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not set primary playback buffer format\n");
+    }
+
+    hr = IDirectSoundBuffer_GetFormat (
+        s->dsound_primary_buffer,
+        &wfx,
+        sizeof (wfx),
+        NULL
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not get primary playback buffer format\n");
+        goto fail0;
+    }
+
+#ifdef DEBUG_DSOUND
+    dolog ("Primary\n");
+    print_wave_format (&wfx);
+#endif
+
+    err = waveformat_to_audio_settings (&wfx, &s->settings);
+    if (err) {
+        goto fail0;
+    }
+
+    return 0;
+
+ fail0:
+    dsound_close (s);
+    return -1;
+}
+
+static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    HRESULT hr;
+    DWORD status;
+    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+
+    if (!dsb) {
+        dolog ("Attempt to control voice without a buffer\n");
+        return 0;
+    }
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        if (dsound_get_status_out (dsb, &status)) {
+            return -1;
+        }
+
+        if (status & DSBSTATUS_PLAYING) {
+            dolog ("warning: Voice is already playing\n");
+            return 0;
+        }
+
+        dsound_clear_sample (hw, dsb);
+
+        hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not start playing buffer\n");
+            return -1;
+        }
+        break;
+
+    case VOICE_DISABLE:
+        if (dsound_get_status_out (dsb, &status)) {
+            return -1;
+        }
+
+        if (status & DSBSTATUS_PLAYING) {
+            hr = IDirectSoundBuffer_Stop (dsb);
+            if (FAILED (hr)) {
+                dsound_logerr (hr, "Could not stop playing buffer\n");
+                return -1;
+            }
+        }
+        else {
+            dolog ("warning: Voice is not playing\n");
+        }
+        break;
+    }
+    return 0;
+}
+
+static int dsound_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int dsound_run_out (HWVoiceOut *hw)
+{
+    int err;
+    HRESULT hr;
+    DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
+    LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
+    int live, len, hwshift;
+    DWORD blen1, blen2;
+    DWORD len1, len2;
+    DWORD decr;
+    DWORD wpos, ppos, old_pos;
+    LPVOID p1, p2;
+    int bufsize;
+
+    if (!dsb) {
+        dolog ("Attempt to run empty with playback buffer\n");
+        return 0;
+    }
+
+    hwshift = hw->info.shift;
+    bufsize = hw->samples << hwshift;
+
+    live = audio_pcm_hw_get_live_out (hw);
+
+    hr = IDirectSoundBuffer_GetCurrentPosition (
+        dsb,
+        &ppos,
+        ds->first_time ? &wpos : NULL
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not get playback buffer position\n");
+        return 0;
+    }
+
+    len = live << hwshift;
+
+    if (ds->first_time) {
+        if (conf.latency_millis) {
+            DWORD cur_blat;
+
+            cur_blat = audio_ring_dist (wpos, ppos, bufsize);
+            ds->first_time = 0;
+            old_pos = wpos;
+            old_pos +=
+                millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
+            old_pos %= bufsize;
+            old_pos &= ~hw->info.align;
+        }
+        else {
+            old_pos = wpos;
+        }
+#ifdef DEBUG_DSOUND
+        ds->played = 0;
+        ds->mixed = 0;
+#endif
+    }
+    else {
+        if (ds->old_pos == ppos) {
+#ifdef DEBUG_DSOUND
+            dolog ("old_pos == ppos\n");
+#endif
+            return 0;
+        }
+
+#ifdef DEBUG_DSOUND
+        ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
+#endif
+        old_pos = ds->old_pos;
+    }
+
+    if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
+        len = ppos - old_pos;
+    }
+    else {
+        if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
+            len = bufsize - old_pos + ppos;
+        }
+    }
+
+    if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
+        dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
+               len, bufsize, old_pos, ppos);
+        return 0;
+    }
+
+    len &= ~hw->info.align;
+    if (!len) {
+        return 0;
+    }
+
+#ifdef DEBUG_DSOUND
+    ds->old_ppos = ppos;
+#endif
+    err = dsound_lock_out (
+        dsb,
+        &hw->info,
+        old_pos,
+        len,
+        &p1, &p2,
+        &blen1, &blen2,
+        0
+        );
+    if (err) {
+        return 0;
+    }
+
+    len1 = blen1 >> hwshift;
+    len2 = blen2 >> hwshift;
+    decr = len1 + len2;
+
+    if (p1 && len1) {
+        dsound_write_sample (hw, p1, len1);
+    }
+
+    if (p2 && len2) {
+        dsound_write_sample (hw, p2, len2);
+    }
+
+    dsound_unlock_out (dsb, p1, p2, blen1, blen2);
+    ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
+
+#ifdef DEBUG_DSOUND
+    ds->mixed += decr << hwshift;
+
+    dolog ("played %lu mixed %lu diff %ld sec %f\n",
+           ds->played,
+           ds->mixed,
+           ds->mixed - ds->played,
+           abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
+#endif
+    return decr;
+}
+
+static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    HRESULT hr;
+    DWORD status;
+    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
+
+    if (!dscb) {
+        dolog ("Attempt to control capture voice without a buffer\n");
+        return -1;
+    }
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        if (dsound_get_status_in (dscb, &status)) {
+            return -1;
+        }
+
+        if (status & DSCBSTATUS_CAPTURING) {
+            dolog ("warning: Voice is already capturing\n");
+            return 0;
+        }
+
+        /* clear ?? */
+
+        hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not start capturing\n");
+            return -1;
+        }
+        break;
+
+    case VOICE_DISABLE:
+        if (dsound_get_status_in (dscb, &status)) {
+            return -1;
+        }
+
+        if (status & DSCBSTATUS_CAPTURING) {
+            hr = IDirectSoundCaptureBuffer_Stop (dscb);
+            if (FAILED (hr)) {
+                dsound_logerr (hr, "Could not stop capturing\n");
+                return -1;
+            }
+        }
+        else {
+            dolog ("warning: Voice is not capturing\n");
+        }
+        break;
+    }
+    return 0;
+}
+
+static int dsound_read (SWVoiceIn *sw, void *buf, int len)
+{
+    return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int dsound_run_in (HWVoiceIn *hw)
+{
+    int err;
+    HRESULT hr;
+    DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
+    LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
+    int live, len, dead;
+    DWORD blen1, blen2;
+    DWORD len1, len2;
+    DWORD decr;
+    DWORD cpos, rpos;
+    LPVOID p1, p2;
+    int hwshift;
+
+    if (!dscb) {
+        dolog ("Attempt to run without capture buffer\n");
+        return 0;
+    }
+
+    hwshift = hw->info.shift;
+
+    live = audio_pcm_hw_get_live_in (hw);
+    dead = hw->samples - live;
+    if (!dead) {
+        return 0;
+    }
+
+    hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
+        dscb,
+        &cpos,
+        ds->first_time ? &rpos : NULL
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not get capture buffer position\n");
+        return 0;
+    }
+
+    if (ds->first_time) {
+        ds->first_time = 0;
+        if (rpos & hw->info.align) {
+            ldebug ("warning: Misaligned capture read position %ld(%d)\n",
+                    rpos, hw->info.align);
+        }
+        hw->wpos = rpos >> hwshift;
+    }
+
+    if (cpos & hw->info.align) {
+        ldebug ("warning: Misaligned capture position %ld(%d)\n",
+                cpos, hw->info.align);
+    }
+    cpos >>= hwshift;
+
+    len = audio_ring_dist (cpos, hw->wpos, hw->samples);
+    if (!len) {
+        return 0;
+    }
+    len = audio_MIN (len, dead);
+
+    err = dsound_lock_in (
+        dscb,
+        &hw->info,
+        hw->wpos << hwshift,
+        len << hwshift,
+        &p1,
+        &p2,
+        &blen1,
+        &blen2,
+        0
+        );
+    if (err) {
+        return 0;
+    }
+
+    len1 = blen1 >> hwshift;
+    len2 = blen2 >> hwshift;
+    decr = len1 + len2;
+
+    if (p1 && len1) {
+        hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
+    }
+
+    if (p2 && len2) {
+        hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
+    }
+
+    dsound_unlock_in (dscb, p1, p2, blen1, blen2);
+    hw->wpos = (hw->wpos + decr) % hw->samples;
+    return decr;
+}
+
+static void dsound_audio_fini (void *opaque)
+{
+    HRESULT hr;
+    dsound *s = opaque;
+
+    if (!s->dsound) {
+        return;
+    }
+
+    hr = IDirectSound_Release (s->dsound);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not release DirectSound\n");
+    }
+    s->dsound = NULL;
+
+    if (!s->dsound_capture) {
+        return;
+    }
+
+    hr = IDirectSoundCapture_Release (s->dsound_capture);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+    }
+    s->dsound_capture = NULL;
+}
+
+static void *dsound_audio_init (void)
+{
+    int err;
+    HRESULT hr;
+    dsound *s = &glob_dsound;
+
+    hr = CoInitialize (NULL);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not initialize COM\n");
+        return NULL;
+    }
+
+    hr = CoCreateInstance (
+        &CLSID_DirectSound,
+        NULL,
+        CLSCTX_ALL,
+        &IID_IDirectSound,
+        (void **) &s->dsound
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not create DirectSound instance\n");
+        return NULL;
+    }
+
+    hr = IDirectSound_Initialize (s->dsound, NULL);
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not initialize DirectSound\n");
+
+        hr = IDirectSound_Release (s->dsound);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not release DirectSound\n");
+        }
+        s->dsound = NULL;
+        return NULL;
+    }
+
+    hr = CoCreateInstance (
+        &CLSID_DirectSoundCapture,
+        NULL,
+        CLSCTX_ALL,
+        &IID_IDirectSoundCapture,
+        (void **) &s->dsound_capture
+        );
+    if (FAILED (hr)) {
+        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
+    }
+    else {
+        hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
+        if (FAILED (hr)) {
+            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
+
+            hr = IDirectSoundCapture_Release (s->dsound_capture);
+            if (FAILED (hr)) {
+                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
+            }
+            s->dsound_capture = NULL;
+        }
+    }
+
+    err = dsound_open (s);
+    if (err) {
+        dsound_audio_fini (s);
+        return NULL;
+    }
+
+    return s;
+}
+
+static struct audio_option dsound_options[] = {
+    {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries,
+     "Number of times to attempt locking the buffer", NULL, 0},
+    {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries,
+     "Number of times to attempt restoring the buffer", NULL, 0},
+    {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries,
+     "Number of times to attempt getting status of the buffer", NULL, 0},
+    {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary,
+     "Set the parameters of primary buffer", NULL, 0},
+    {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
+     "(undocumented)", NULL, 0},
+    {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
+     "Primary buffer frequency", NULL, 0},
+    {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
+     "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
+    {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
+     "Primary buffer format", NULL, 0},
+    {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
+     "(undocumented)", NULL, 0},
+    {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in,
+     "(undocumented)", NULL, 0},
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops dsound_pcm_ops = {
+    dsound_init_out,
+    dsound_fini_out,
+    dsound_run_out,
+    dsound_write,
+    dsound_ctl_out,
+
+    dsound_init_in,
+    dsound_fini_in,
+    dsound_run_in,
+    dsound_read,
+    dsound_ctl_in
+};
+
+struct audio_driver dsound_audio_driver = {
+    INIT_FIELD (name           = ) "dsound",
+    INIT_FIELD (descr          = )
+    "DirectSound audio (www.wikipedia.org/wiki/DirectSound)",
+    INIT_FIELD (options        = ) dsound_options,
+    INIT_FIELD (init           = ) dsound_audio_init,
+    INIT_FIELD (fini           = ) dsound_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &dsound_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) 1,
+    INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (DSoundVoiceIn)
+};
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
new file mode 100644
index 0000000..05c030f
--- /dev/null
+++ b/audio/esdaudio.c
@@ -0,0 +1,682 @@
+/*
+ * QEMU ESD audio driver
+ *
+ * Copyright (c) 2008 The Android Open Source Project
+ * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <esd.h>
+#include "audio.h"
+#include <signal.h>
+
+#define AUDIO_CAP "esd"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+#include <dlfcn.h>
+
+#include "qemu_debug.h"
+
+#define  DEBUG  1
+
+#if DEBUG
+#  include <stdio.h>
+#  define D(...)  VERBOSE_PRINT(audio,__VA_ARGS__)
+#  define D_ACTIVE  VERBOSE_CHECK(audio)
+#  define O(...)  VERBOSE_PRINT(audioout,__VA_ARGS__)
+#  define I(...)  VERBOSE_PRINT(audioin,__VA_ARGS__)
+#else
+#  define D(...)  ((void)0)
+#  define D_ACTIVE 0
+#  define O(...)  ((void)0)
+#  define I(...)  ((void)0)
+#endif
+
+#define  STRINGIFY_(x)  #x
+#define  STRINGIFY(x)   STRINGIFY_(x)
+
+typedef struct {
+    HWVoiceOut hw;
+    int done;
+    int live;
+    int decr;
+    int rpos;
+    void *pcm_buf;
+    int fd;
+    struct audio_pt pt;
+} ESDVoiceOut;
+
+typedef struct {
+    HWVoiceIn hw;
+    int done;
+    int dead;
+    int incr;
+    int wpos;
+    void *pcm_buf;
+    int fd;
+    struct audio_pt pt;
+} ESDVoiceIn;
+
+static struct {
+    int samples;
+    int divisor;
+    char *dac_host;
+    char *adc_host;
+} conf = {
+    1024,
+    2,
+    NULL,
+    NULL
+};
+
+/* link dynamically to the libesd.so */
+
+#define  DYNLINK_FUNCTIONS   \
+    DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*))   \
+    DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
+    DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \
+    DYNLINK_FUNC(int,esd_close,(int)) \
+
+#define  DYNLINK_FUNCTIONS_INIT \
+    esd_dynlink_init
+
+#include "dynlink.h"
+
+static void*    esd_lib;
+
+static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+/* playback */
+static void *qesd_thread_out (void *arg)
+{
+    ESDVoiceOut* esd = arg;
+    HWVoiceOut*  hw  = &esd->hw;
+    int threshold;
+    sigset_t  set;
+
+    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+        return NULL;
+    }
+
+    /* ignore SIGALRM in this thread */
+    sigemptyset(&set);
+    sigaddset(&set, SIGALRM);
+
+    pthread_sigmask( SIG_BLOCK, &set, NULL);
+
+    O("EsounD output thread starting, threshold is %d samples", threshold);
+    for (;;) {
+        int decr, to_mix, rpos;
+
+        for (;;) {
+            if (esd->done) {
+                goto exit;
+            }
+
+            if (esd->live > threshold) {
+                break;
+            }
+
+            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+                O("EsounD output thread aborting on wait error");
+                goto exit;
+            }
+        }
+
+        decr = to_mix = esd->live;
+        rpos = hw->rpos;
+
+        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+            O("EsounD output thread aborting on unlock error");
+            return NULL;
+        }
+
+        while (to_mix) {
+            ssize_t written;
+            int chunk = audio_MIN (to_mix, hw->samples - rpos);
+            st_sample_t *src = hw->mix_buf + rpos;
+
+            hw->clip (esd->pcm_buf, src, chunk);
+
+        again:
+            written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
+            if (written == -1) {
+                if (errno == EINTR || errno == EAGAIN) {
+                    goto again;
+                }
+                qesd_logerr (errno, "write failed\n");
+                O("EsounD output thread aborting on write error: %s", strerror(errno));
+                return NULL;
+            }
+
+            if (written != chunk << hw->info.shift) {
+                int wsamples = written >> hw->info.shift;
+                int wbytes = wsamples << hw->info.shift;
+                if (wbytes != written) {
+                    dolog ("warning: Misaligned write %d (requested %d), "
+                           "alignment %d\n",
+                           wbytes, written, hw->info.align + 1);
+                }
+                to_mix -= wsamples;
+                rpos = (rpos + wsamples) % hw->samples;
+                break;
+            }
+
+            rpos = (rpos + chunk) % hw->samples;
+            to_mix -= chunk;
+        }
+
+        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+            O("EsounD output thread aborting on lock error\n");
+            return NULL;
+        }
+
+        esd->rpos = rpos;
+        esd->live -= decr;
+        esd->decr += decr;
+    }
+    O("EsounD output thread exiting");
+
+ exit:
+    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    return NULL;
+}
+
+static int qesd_run_out (HWVoiceOut *hw)
+{
+    int live, decr;
+    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+        O("%s: exiting on lock error", __FUNCTION__);
+        return 0;
+    }
+
+    live = audio_pcm_hw_get_live_out (hw);
+    decr = audio_MIN (live, esd->decr);
+    esd->decr -= decr;
+    esd->live = live - decr;
+    hw->rpos = esd->rpos;
+    if (esd->live > 0) {
+        O("%s: signaling %d samples\n", esd->live);
+        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    }
+    else {
+        O(".");
+        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    }
+    return decr;
+}
+
+static int qesd_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int qesd_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+    audsettings_t obt_as = *as;
+    int esdfmt = ESD_STREAM | ESD_PLAY;
+    int result = -1;
+
+    /* shut down verbose debug spew */
+    if (!D_ACTIVE)
+        stdio_disable();
+
+    O("initializing EsoundD audio output");
+    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        esdfmt |= ESD_BITS8;
+        obt_as.fmt = AUD_FMT_U8;
+        break;
+#if 0
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        dolog ("Will use 16 instead of 32 bit samples\n");
+#endif
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+    deffmt:
+        esdfmt |= ESD_BITS16;
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
+        goto deffmt;
+
+    }
+    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+
+    hw->samples = conf.samples;
+    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!esd->pcm_buf) {
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        goto exit;
+    }
+
+    esd->fd = FF(esd_play_stream) (esdfmt, as->freq, conf.dac_host, NULL);
+    if (esd->fd < 0) {
+        if (conf.dac_host == NULL) {
+            esd->fd = FF(esd_play_stream) (esdfmt, as->freq, "localhost", NULL);
+        }
+        if (esd->fd < 0) {
+            qesd_logerr (errno, "esd_play_stream failed\n");
+            goto fail2;
+        }
+    }
+
+    if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
+        goto fail3;
+    }
+
+    result = 0;  /* success */
+    goto exit;
+
+ fail3:
+    if (close (esd->fd)) {
+        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+                     AUDIO_FUNC, esd->fd);
+    }
+    esd->fd = -1;
+
+ fail2:
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+
+ exit:
+    if (!D_ACTIVE)
+        stdio_enable();
+
+    return result;
+}
+
+static void qesd_fini_out (HWVoiceOut *hw)
+{
+    void *ret;
+    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+    audio_pt_lock (&esd->pt, AUDIO_FUNC);
+    esd->done = 1;
+    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+    if (esd->fd >= 0) {
+        if (close (esd->fd)) {
+            qesd_logerr (errno, "failed to close esd socket\n");
+        }
+        esd->fd = -1;
+    }
+
+    audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+/* capture */
+static void *qesd_thread_in (void *arg)
+{
+    ESDVoiceIn *esd = arg;
+    HWVoiceIn *hw = &esd->hw;
+    int threshold;
+
+    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+        return NULL;
+    }
+
+    for (;;) {
+        int incr, to_grab, wpos;
+
+        for (;;) {
+            if (esd->done) {
+                goto exit;
+            }
+
+            if (esd->dead > threshold) {
+                break;
+            }
+
+            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+                goto exit;
+            }
+        }
+
+        incr = to_grab = esd->dead;
+        wpos = hw->wpos;
+
+        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        while (to_grab) {
+            ssize_t nread;
+            int chunk = audio_MIN (to_grab, hw->samples - wpos);
+            void *buf = advance (esd->pcm_buf, wpos);
+
+        again:
+            nread = read (esd->fd, buf, chunk << hw->info.shift);
+            if (nread == -1) {
+                if (errno == EINTR || errno == EAGAIN) {
+                    goto again;
+                }
+                qesd_logerr (errno, "read failed\n");
+                return NULL;
+            }
+
+            if (nread != chunk << hw->info.shift) {
+                int rsamples = nread >> hw->info.shift;
+                int rbytes = rsamples << hw->info.shift;
+                if (rbytes != nread) {
+                    dolog ("warning: Misaligned write %d (requested %d), "
+                           "alignment %d\n",
+                           rbytes, nread, hw->info.align + 1);
+                }
+                to_grab -= rsamples;
+                wpos = (wpos + rsamples) % hw->samples;
+                break;
+            }
+
+            hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
+                      &nominal_volume);
+            wpos = (wpos + chunk) % hw->samples;
+            to_grab -= chunk;
+        }
+
+        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        esd->wpos = wpos;
+        esd->dead -= incr;
+        esd->incr += incr;
+    }
+
+ exit:
+    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    return NULL;
+}
+
+static int qesd_run_in (HWVoiceIn *hw)
+{
+    int live, incr, dead;
+    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+        return 0;
+    }
+
+    live = audio_pcm_hw_get_live_in (hw);
+    dead = hw->samples - live;
+    incr = audio_MIN (dead, esd->incr);
+    esd->incr -= incr;
+    esd->dead = dead - incr;
+    hw->wpos = esd->wpos;
+    if (esd->dead > 0) {
+        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    }
+    else {
+        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    }
+    return incr;
+}
+
+static int qesd_read (SWVoiceIn *sw, void *buf, int len)
+{
+    return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int qesd_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+    audsettings_t obt_as = *as;
+    int esdfmt = ESD_STREAM | ESD_RECORD;
+    int result = -1;
+
+    /* shut down verbose debug spew */
+    if (!D_ACTIVE)
+        stdio_disable();
+
+    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        esdfmt |= ESD_BITS8;
+        obt_as.fmt = AUD_FMT_U8;
+        break;
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+        esdfmt |= ESD_BITS16;
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        dolog ("Will use 16 instead of 32 bit samples\n");
+        esdfmt |= ESD_BITS16;
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+    }
+    obt_as.endianness = AUDIO_HOST_ENDIANNESS;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+
+    hw->samples = conf.samples;
+    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!esd->pcm_buf) {
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        goto exit;
+    }
+
+    esd->fd = FF(esd_record_stream) (esdfmt, as->freq, conf.adc_host, NULL);
+    if (esd->fd < 0) {
+        if (conf.adc_host == NULL) {
+            esd->fd = FF(esd_record_stream) (esdfmt, as->freq, "localhost", NULL);
+        }
+        if (esd->fd < 0) {
+            qesd_logerr (errno, "esd_record_stream failed\n");
+            goto fail2;
+        }
+    }
+
+    if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
+        goto fail3;
+    }
+
+    result = 0;  /* success */
+    goto exit;
+
+ fail3:
+    if (close (esd->fd)) {
+        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+                     AUDIO_FUNC, esd->fd);
+    }
+    esd->fd = -1;
+
+ fail2:
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+
+ exit:
+    if (!D_ACTIVE)
+        stdio_enable();
+
+    return result;
+}
+
+static void qesd_fini_in (HWVoiceIn *hw)
+{
+    void *ret;
+    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+    audio_pt_lock (&esd->pt, AUDIO_FUNC);
+    esd->done = 1;
+    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+    if (esd->fd >= 0) {
+        if (close (esd->fd)) {
+            qesd_logerr (errno, "failed to close esd socket\n");
+        }
+        esd->fd = -1;
+    }
+
+    audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+/* common */
+static void *qesd_audio_init (void)
+{
+    void*    result = NULL;
+
+    D("%s: entering", __FUNCTION__);
+
+    if (esd_lib == NULL) {
+        int  fd;
+
+        esd_lib = dlopen( "libesd.so", RTLD_NOW );
+        if (esd_lib == NULL)
+            esd_lib = dlopen( "libesd.so.0", RTLD_NOW );
+
+        if (esd_lib == NULL) {
+            D("could not find libesd on this system");
+            goto Exit;
+        }
+
+        if (esd_dynlink_init(esd_lib) < 0)
+            goto Fail;
+
+        fd = FF(esd_open_sound)(conf.dac_host);
+        if (fd < 0) {
+            D("%s: could not open direct sound server connection, trying localhost",
+              __FUNCTION__);
+            fd = FF(esd_open_sound)("localhost");
+            if (fd < 0) {
+                D("%s: could not open localhost sound server connection", __FUNCTION__);
+                goto Fail;
+            }
+        }
+
+        D("%s: EsounD server connection succeeded", __FUNCTION__);
+        /* FF(esd_close)(fd); */
+    }
+    result = &conf;
+    goto Exit;
+
+Fail:
+    D("%s: failed to open library", __FUNCTION__);
+    dlclose(esd_lib);
+    esd_lib = NULL;
+
+Exit:
+    return  result;
+}
+
+static void qesd_audio_fini (void *opaque)
+{
+    (void) opaque;
+    if (esd_lib != NULL) {
+        dlclose(esd_lib);
+        esd_lib = NULL;
+    }
+    ldebug ("esd_fini");
+}
+
+struct audio_option qesd_options[] = {
+    {"SAMPLES", AUD_OPT_INT, &conf.samples,
+     "buffer size in samples", NULL, 0},
+
+    {"DIVISOR", AUD_OPT_INT, &conf.divisor,
+     "threshold divisor", NULL, 0},
+
+    {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
+     "playback host", NULL, 0},
+
+    {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
+     "capture host", NULL, 0},
+
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+struct audio_pcm_ops qesd_pcm_ops = {
+    qesd_init_out,
+    qesd_fini_out,
+    qesd_run_out,
+    qesd_write,
+    qesd_ctl_out,
+
+    qesd_init_in,
+    qesd_fini_in,
+    qesd_run_in,
+    qesd_read,
+    qesd_ctl_in,
+};
+
+struct audio_driver esd_audio_driver = {
+    INIT_FIELD (name           = ) "esd",
+    INIT_FIELD (descr          = )
+    "EsounD audio (en.wikipedia.org/wiki/Esound)",
+    INIT_FIELD (options        = ) qesd_options,
+    INIT_FIELD (init           = ) qesd_audio_init,
+    INIT_FIELD (fini           = ) qesd_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &qesd_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) 1,
+    INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (ESDVoiceIn)
+};
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
new file mode 100644
index 0000000..e230e8b
--- /dev/null
+++ b/audio/fmodaudio.c
@@ -0,0 +1,685 @@
+/*
+ * QEMU FMOD audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <fmod.h>
+#include <fmod_errors.h>
+#include "audio.h"
+
+#define AUDIO_CAP "fmod"
+#include "audio_int.h"
+
+typedef struct FMODVoiceOut {
+    HWVoiceOut hw;
+    unsigned int old_pos;
+    FSOUND_SAMPLE *fmod_sample;
+    int channel;
+} FMODVoiceOut;
+
+typedef struct FMODVoiceIn {
+    HWVoiceIn hw;
+    FSOUND_SAMPLE *fmod_sample;
+} FMODVoiceIn;
+
+static struct {
+    const char *drvname;
+    int nb_samples;
+    int freq;
+    int nb_channels;
+    int bufsize;
+    int threshold;
+    int broken_adc;
+} conf = {
+    NULL,
+    2048 * 2,
+    44100,
+    2,
+    0,
+    0,
+    0
+};
+
+static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n",
+             FMOD_ErrorString (FSOUND_GetError ()));
+}
+
+static void GCC_FMT_ATTR (2, 3) fmod_logerr2 (
+    const char *typ,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n",
+             FMOD_ErrorString (FSOUND_GetError ()));
+}
+
+static int fmod_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static void fmod_clear_sample (FMODVoiceOut *fmd)
+{
+    HWVoiceOut *hw = &fmd->hw;
+    int status;
+    void *p1 = 0, *p2 = 0;
+    unsigned int len1 = 0, len2 = 0;
+
+    status = FSOUND_Sample_Lock (
+        fmd->fmod_sample,
+        0,
+        hw->samples << hw->info.shift,
+        &p1,
+        &p2,
+        &len1,
+        &len2
+        );
+
+    if (!status) {
+        fmod_logerr ("Failed to lock sample\n");
+        return;
+    }
+
+    if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
+        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
+               len1, len2, hw->info.align + 1);
+        goto fail;
+    }
+
+    if ((len1 + len2) - (hw->samples << hw->info.shift)) {
+        dolog ("Lock returned incomplete length %d, %d\n",
+               len1 + len2, hw->samples << hw->info.shift);
+        goto fail;
+    }
+
+    audio_pcm_info_clear_buf (&hw->info, p1, hw->samples);
+
+ fail:
+    status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2);
+    if (!status) {
+        fmod_logerr ("Failed to unlock sample\n");
+    }
+}
+
+static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
+{
+    int src_len1 = dst_len;
+    int src_len2 = 0;
+    int pos = hw->rpos + dst_len;
+    st_sample_t *src1 = hw->mix_buf + hw->rpos;
+    st_sample_t *src2 = NULL;
+
+    if (pos > hw->samples) {
+        src_len1 = hw->samples - hw->rpos;
+        src2 = hw->mix_buf;
+        src_len2 = dst_len - src_len1;
+        pos = src_len2;
+    }
+
+    if (src_len1) {
+        hw->clip (dst, src1, src_len1);
+    }
+
+    if (src_len2) {
+        dst = advance (dst, src_len1 << hw->info.shift);
+        hw->clip (dst, src2, src_len2);
+    }
+
+    hw->rpos = pos % hw->samples;
+}
+
+static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2,
+                               unsigned int blen1, unsigned int blen2)
+{
+    int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
+    if (!status) {
+        fmod_logerr ("Failed to unlock sample\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int fmod_lock_sample (
+    FSOUND_SAMPLE *sample,
+    struct audio_pcm_info *info,
+    int pos,
+    int len,
+    void **p1,
+    void **p2,
+    unsigned int *blen1,
+    unsigned int *blen2
+    )
+{
+    int status;
+
+    status = FSOUND_Sample_Lock (
+        sample,
+        pos << info->shift,
+        len << info->shift,
+        p1,
+        p2,
+        blen1,
+        blen2
+        );
+
+    if (!status) {
+        fmod_logerr ("Failed to lock sample\n");
+        return -1;
+    }
+
+    if ((*blen1 & info->align) || (*blen2 & info->align)) {
+        dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
+               *blen1, *blen2, info->align + 1);
+
+        fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2);
+
+        *p1 = NULL - 1;
+        *p2 = NULL - 1;
+        *blen1 = ~0U;
+        *blen2 = ~0U;
+        return -1;
+    }
+
+    if (!*p1 && *blen1) {
+        dolog ("warning: !p1 && blen1=%d\n", *blen1);
+        *blen1 = 0;
+    }
+
+    if (!p2 && *blen2) {
+        dolog ("warning: !p2 && blen2=%d\n", *blen2);
+        *blen2 = 0;
+    }
+
+    return 0;
+}
+
+static int fmod_run_out (HWVoiceOut *hw)
+{
+    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+    int live, decr;
+    void *p1 = 0, *p2 = 0;
+    unsigned int blen1 = 0, blen2 = 0;
+    unsigned int len1 = 0, len2 = 0;
+    int nb_live;
+
+    live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
+    if (!live) {
+        return 0;
+    }
+
+    if (!hw->pending_disable
+        && nb_live
+        && (conf.threshold && live <= conf.threshold)) {
+        ldebug ("live=%d nb_live=%d\n", live, nb_live);
+        return 0;
+    }
+
+    decr = live;
+
+    if (fmd->channel >= 0) {
+        int len = decr;
+        int old_pos = fmd->old_pos;
+        int ppos = FSOUND_GetCurrentPosition (fmd->channel);
+
+        if (ppos == old_pos || !ppos) {
+            return 0;
+        }
+
+        if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
+            len = ppos - old_pos;
+        }
+        else {
+            if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
+                len = hw->samples - old_pos + ppos;
+            }
+        }
+        decr = len;
+
+        if (audio_bug (AUDIO_FUNC, decr < 0)) {
+            dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
+                   decr, live, ppos, old_pos, len);
+            return 0;
+        }
+    }
+
+
+    if (!decr) {
+        return 0;
+    }
+
+    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
+                          fmd->old_pos, decr,
+                          &p1, &p2,
+                          &blen1, &blen2)) {
+        return 0;
+    }
+
+    len1 = blen1 >> hw->info.shift;
+    len2 = blen2 >> hw->info.shift;
+    ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
+    decr = len1 + len2;
+
+    if (p1 && len1) {
+        fmod_write_sample (hw, p1, len1);
+    }
+
+    if (p2 && len2) {
+        fmod_write_sample (hw, p2, len2);
+    }
+
+    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
+
+    fmd->old_pos = (fmd->old_pos + decr) % hw->samples;
+    return decr;
+}
+
+static int aud_to_fmodfmt (audfmt_e fmt, int stereo)
+{
+    int mode = FSOUND_LOOP_NORMAL;
+
+    switch (fmt) {
+    case AUD_FMT_S8:
+        mode |= FSOUND_SIGNED | FSOUND_8BITS;
+        break;
+
+    case AUD_FMT_U8:
+        mode |= FSOUND_UNSIGNED | FSOUND_8BITS;
+        break;
+
+    case AUD_FMT_S16:
+        mode |= FSOUND_SIGNED | FSOUND_16BITS;
+        break;
+
+    case AUD_FMT_U16:
+        mode |= FSOUND_UNSIGNED | FSOUND_16BITS;
+        break;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_FMOD
+        abort ();
+#endif
+        mode |= FSOUND_8BITS;
+    }
+    mode |= stereo ? FSOUND_STEREO : FSOUND_MONO;
+    return mode;
+}
+
+static void fmod_fini_out (HWVoiceOut *hw)
+{
+    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+
+    if (fmd->fmod_sample) {
+        FSOUND_Sample_Free (fmd->fmod_sample);
+        fmd->fmod_sample = 0;
+
+        if (fmd->channel >= 0) {
+            FSOUND_StopSound (fmd->channel);
+        }
+    }
+}
+
+static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    int bits16, mode, channel;
+    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+    audsettings_t obt_as = *as;
+
+    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
+    fmd->fmod_sample = FSOUND_Sample_Alloc (
+        FSOUND_FREE,            /* index */
+        conf.nb_samples,        /* length */
+        mode,                   /* mode */
+        as->freq,               /* freq */
+        255,                    /* volume */
+        128,                    /* pan */
+        255                     /* priority */
+        );
+
+    if (!fmd->fmod_sample) {
+        fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n");
+        return -1;
+    }
+
+    channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1);
+    if (channel < 0) {
+        fmod_logerr2 ("DAC", "Failed to start playing sound\n");
+        FSOUND_Sample_Free (fmd->fmod_sample);
+        return -1;
+    }
+    fmd->channel = channel;
+
+    /* FMOD always operates on little endian frames? */
+    obt_as.endianness = 0;
+    audio_pcm_init_info (&hw->info, &obt_as);
+    bits16 = (mode & FSOUND_16BITS) != 0;
+    hw->samples = conf.nb_samples;
+    return 0;
+}
+
+static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    int status;
+    FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        fmod_clear_sample (fmd);
+        status = FSOUND_SetPaused (fmd->channel, 0);
+        if (!status) {
+            fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
+        }
+        break;
+
+    case VOICE_DISABLE:
+        status = FSOUND_SetPaused (fmd->channel, 1);
+        if (!status) {
+            fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
+        }
+        break;
+    }
+    return 0;
+}
+
+static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    int bits16, mode;
+    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+    audsettings_t obt_as = *as;
+
+    if (conf.broken_adc) {
+        return -1;
+    }
+
+    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
+    fmd->fmod_sample = FSOUND_Sample_Alloc (
+        FSOUND_FREE,            /* index */
+        conf.nb_samples,        /* length */
+        mode,                   /* mode */
+        as->freq,               /* freq */
+        255,                    /* volume */
+        128,                    /* pan */
+        255                     /* priority */
+        );
+
+    if (!fmd->fmod_sample) {
+        fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n");
+        return -1;
+    }
+
+    /* FMOD always operates on little endian frames? */
+    obt_as.endianness = 0;
+    audio_pcm_init_info (&hw->info, &obt_as);
+    bits16 = (mode & FSOUND_16BITS) != 0;
+    hw->samples = conf.nb_samples;
+    return 0;
+}
+
+static void fmod_fini_in (HWVoiceIn *hw)
+{
+    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+
+    if (fmd->fmod_sample) {
+        FSOUND_Record_Stop ();
+        FSOUND_Sample_Free (fmd->fmod_sample);
+        fmd->fmod_sample = 0;
+    }
+}
+
+static int fmod_run_in (HWVoiceIn *hw)
+{
+    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+    int hwshift = hw->info.shift;
+    int live, dead, new_pos, len;
+    unsigned int blen1 = 0, blen2 = 0;
+    unsigned int len1, len2;
+    unsigned int decr;
+    void *p1, *p2;
+
+    live = audio_pcm_hw_get_live_in (hw);
+    dead = hw->samples - live;
+    if (!dead) {
+        return 0;
+    }
+
+    new_pos = FSOUND_Record_GetPosition ();
+    if (new_pos < 0) {
+        fmod_logerr ("Could not get recording position\n");
+        return 0;
+    }
+
+    len = audio_ring_dist (new_pos,  hw->wpos, hw->samples);
+    if (!len) {
+        return 0;
+    }
+    len = audio_MIN (len, dead);
+
+    if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
+                          hw->wpos, len,
+                          &p1, &p2,
+                          &blen1, &blen2)) {
+        return 0;
+    }
+
+    len1 = blen1 >> hwshift;
+    len2 = blen2 >> hwshift;
+    decr = len1 + len2;
+
+    if (p1 && blen1) {
+        hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
+    }
+    if (p2 && len2) {
+        hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
+    }
+
+    fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);
+    hw->wpos = (hw->wpos + decr) % hw->samples;
+    return decr;
+}
+
+static struct {
+    const char *name;
+    int type;
+} drvtab[] = {
+    {"none", FSOUND_OUTPUT_NOSOUND},
+#ifdef _WIN32
+    {"winmm", FSOUND_OUTPUT_WINMM},
+    {"dsound", FSOUND_OUTPUT_DSOUND},
+    {"a3d", FSOUND_OUTPUT_A3D},
+    {"asio", FSOUND_OUTPUT_ASIO},
+#endif
+#ifdef __linux__
+    {"oss", FSOUND_OUTPUT_OSS},
+    {"alsa", FSOUND_OUTPUT_ALSA},
+    {"esd", FSOUND_OUTPUT_ESD},
+#endif
+#ifdef __APPLE__
+    {"mac", FSOUND_OUTPUT_MAC},
+#endif
+#if 0
+    {"xbox", FSOUND_OUTPUT_XBOX},
+    {"ps2", FSOUND_OUTPUT_PS2},
+    {"gcube", FSOUND_OUTPUT_GC},
+#endif
+    {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
+};
+
+static void *fmod_audio_init (void)
+{
+    size_t i;
+    double ver;
+    int status;
+    int output_type = -1;
+    const char *drv = conf.drvname;
+
+    ver = FSOUND_GetVersion ();
+    if (ver < FMOD_VERSION) {
+        dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
+        return NULL;
+    }
+
+#ifdef __linux__
+    if (ver < 3.75) {
+        dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
+               "ADC will be disabled.\n");
+        conf.broken_adc = 1;
+    }
+#endif
+
+    if (drv) {
+        int found = 0;
+        for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+            if (!strcmp (drv, drvtab[i].name)) {
+                output_type = drvtab[i].type;
+                found = 1;
+                break;
+            }
+        }
+        if (!found) {
+            dolog ("Unknown FMOD driver `%s'\n", drv);
+            dolog ("Valid drivers:\n");
+            for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+                dolog ("  %s\n", drvtab[i].name);
+            }
+        }
+    }
+
+    if (output_type != -1) {
+        status = FSOUND_SetOutput (output_type);
+        if (!status) {
+            fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
+            return NULL;
+        }
+    }
+
+    if (conf.bufsize) {
+        status = FSOUND_SetBufferSize (conf.bufsize);
+        if (!status) {
+            fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
+        }
+    }
+
+    status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
+    if (!status) {
+        fmod_logerr ("FSOUND_Init failed\n");
+        return NULL;
+    }
+
+    return &conf;
+}
+
+static int fmod_read (SWVoiceIn *sw, void *buf, int size)
+{
+    return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    int status;
+    FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
+        if (!status) {
+            fmod_logerr ("Failed to start recording\n");
+        }
+        break;
+
+    case VOICE_DISABLE:
+        status = FSOUND_Record_Stop ();
+        if (!status) {
+            fmod_logerr ("Failed to stop recording\n");
+        }
+        break;
+    }
+    return 0;
+}
+
+static void fmod_audio_fini (void *opaque)
+{
+    (void) opaque;
+    FSOUND_Close ();
+}
+
+static struct audio_option fmod_options[] = {
+    {"DRV", AUD_OPT_STR, &conf.drvname,
+     "FMOD driver", NULL, 0},
+    {"FREQ", AUD_OPT_INT, &conf.freq,
+     "Default frequency", NULL, 0},
+    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+     "Buffer size in samples", NULL, 0},
+    {"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
+     "Number of default channels (1 - mono, 2 - stereo)", NULL, 0},
+    {"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
+     "(undocumented)", NULL, 0},
+#if 0
+    {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
+     "(undocumented)"},
+#endif
+
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops fmod_pcm_ops = {
+    fmod_init_out,
+    fmod_fini_out,
+    fmod_run_out,
+    fmod_write,
+    fmod_ctl_out,
+
+    fmod_init_in,
+    fmod_fini_in,
+    fmod_run_in,
+    fmod_read,
+    fmod_ctl_in
+};
+
+struct audio_driver fmod_audio_driver = {
+    INIT_FIELD (name           = ) "fmod",
+    INIT_FIELD (descr          = ) "FMOD 3.xx http://www.fmod.org",
+    INIT_FIELD (options        = ) fmod_options,
+    INIT_FIELD (init           = ) fmod_audio_init,
+    INIT_FIELD (fini           = ) fmod_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &fmod_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) INT_MAX,
+    INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (FMODVoiceIn)
+};
diff --git a/audio/mixeng.c b/audio/mixeng.c
new file mode 100644
index 0000000..34fc6df
--- /dev/null
+++ b/audio/mixeng.c
@@ -0,0 +1,336 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "audio.h"
+
+#define AUDIO_CAP "mixeng"
+#include "audio_int.h"
+
+#define NOVOL
+
+/* 8 bit */
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+
+/* Signed 8 bit */
+#define IN_T int8_t
+#define IN_MIN SCHAR_MIN
+#define IN_MAX SCHAR_MAX
+#define SIGNED
+#define SHIFT 8
+#include "mixeng_template.h"
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 8 bit */
+#define IN_T uint8_t
+#define IN_MIN 0
+#define IN_MAX UCHAR_MAX
+#define SHIFT 8
+#include "mixeng_template.h"
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+
+/* Signed 16 bit */
+#define IN_T int16_t
+#define IN_MIN SHRT_MIN
+#define IN_MAX SHRT_MAX
+#define SIGNED
+#define SHIFT 16
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap16 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 16 bit */
+#define IN_T uint16_t
+#define IN_MIN 0
+#define IN_MAX USHRT_MAX
+#define SHIFT 16
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap16 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Signed 32 bit */
+#define IN_T int32_t
+#define IN_MIN INT32_MIN
+#define IN_MAX INT32_MAX
+#define SIGNED
+#define SHIFT 32
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap32 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+/* Unsigned 16 bit */
+#define IN_T uint32_t
+#define IN_MIN 0
+#define IN_MAX UINT32_MAX
+#define SHIFT 32
+#define ENDIAN_CONVERSION natural
+#define ENDIAN_CONVERT(v) (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#define ENDIAN_CONVERSION swap
+#define ENDIAN_CONVERT(v) bswap32 (v)
+#include "mixeng_template.h"
+#undef ENDIAN_CONVERT
+#undef ENDIAN_CONVERSION
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+#undef SHIFT
+
+t_sample *mixeng_conv[2][2][2][3] = {
+    {
+        {
+            {
+                conv_natural_uint8_t_to_mono,
+                conv_natural_uint16_t_to_mono,
+                conv_natural_uint32_t_to_mono
+            },
+            {
+                conv_natural_uint8_t_to_mono,
+                conv_swap_uint16_t_to_mono,
+                conv_swap_uint32_t_to_mono,
+            }
+        },
+        {
+            {
+                conv_natural_int8_t_to_mono,
+                conv_natural_int16_t_to_mono,
+                conv_natural_int32_t_to_mono
+            },
+            {
+                conv_natural_int8_t_to_mono,
+                conv_swap_int16_t_to_mono,
+                conv_swap_int32_t_to_mono
+            }
+        }
+    },
+    {
+        {
+            {
+                conv_natural_uint8_t_to_stereo,
+                conv_natural_uint16_t_to_stereo,
+                conv_natural_uint32_t_to_stereo
+            },
+            {
+                conv_natural_uint8_t_to_stereo,
+                conv_swap_uint16_t_to_stereo,
+                conv_swap_uint32_t_to_stereo
+            }
+        },
+        {
+            {
+                conv_natural_int8_t_to_stereo,
+                conv_natural_int16_t_to_stereo,
+                conv_natural_int32_t_to_stereo
+            },
+            {
+                conv_natural_int8_t_to_stereo,
+                conv_swap_int16_t_to_stereo,
+                conv_swap_int32_t_to_stereo,
+            }
+        }
+    }
+};
+
+f_sample *mixeng_clip[2][2][2][3] = {
+    {
+        {
+            {
+                clip_natural_uint8_t_from_mono,
+                clip_natural_uint16_t_from_mono,
+                clip_natural_uint32_t_from_mono
+            },
+            {
+                clip_natural_uint8_t_from_mono,
+                clip_swap_uint16_t_from_mono,
+                clip_swap_uint32_t_from_mono
+            }
+        },
+        {
+            {
+                clip_natural_int8_t_from_mono,
+                clip_natural_int16_t_from_mono,
+                clip_natural_int32_t_from_mono
+            },
+            {
+                clip_natural_int8_t_from_mono,
+                clip_swap_int16_t_from_mono,
+                clip_swap_int32_t_from_mono
+            }
+        }
+    },
+    {
+        {
+            {
+                clip_natural_uint8_t_from_stereo,
+                clip_natural_uint16_t_from_stereo,
+                clip_natural_uint32_t_from_stereo
+            },
+            {
+                clip_natural_uint8_t_from_stereo,
+                clip_swap_uint16_t_from_stereo,
+                clip_swap_uint32_t_from_stereo
+            }
+        },
+        {
+            {
+                clip_natural_int8_t_from_stereo,
+                clip_natural_int16_t_from_stereo,
+                clip_natural_int32_t_from_stereo
+            },
+            {
+                clip_natural_int8_t_from_stereo,
+                clip_swap_int16_t_from_stereo,
+                clip_swap_int32_t_from_stereo
+            }
+        }
+    }
+};
+
+/*
+ * August 21, 1998
+ * Copyright 1998 Fabrice Bellard.
+ *
+ * [Rewrote completly the code of Lance Norskog And Sundry
+ * Contributors with a more efficient algorithm.]
+ *
+ * This source code is freely redistributable and may be used for
+ * any purpose.  This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
+ * the consequences of using this software.
+ */
+
+/*
+ * Sound Tools rate change effect file.
+ */
+/*
+ * Linear Interpolation.
+ *
+ * The use of fractional increment allows us to use no buffer. It
+ * avoid the problems at the end of the buffer we had with the old
+ * method which stored a possibly big buffer of size
+ * lcm(in_rate,out_rate).
+ *
+ * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
+ * the input & output frequencies are equal, a delay of one sample is
+ * introduced.  Limited to processing 32-bit count worth of samples.
+ *
+ * 1 << FRAC_BITS evaluating to zero in several places.  Changed with
+ * an (unsigned long) cast to make it safe.  MarkMLl 2/1/99
+ */
+
+/* Private data */
+struct rate {
+    uint64_t opos;
+    uint64_t opos_inc;
+    uint32_t ipos;              /* position in the input stream (integer) */
+    st_sample_t ilast;          /* last sample in the input stream */
+};
+
+/*
+ * Prepare processing.
+ */
+void *st_rate_start (int inrate, int outrate)
+{
+    struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
+
+    if (!rate) {
+        dolog ("Could not allocate resampler (%zu bytes)\n", sizeof (*rate));
+        return NULL;
+    }
+
+    rate->opos = 0;
+
+    /* increment */
+    rate->opos_inc = ((uint64_t) inrate << 32) / outrate;
+
+    rate->ipos = 0;
+    rate->ilast.l = 0;
+    rate->ilast.r = 0;
+    return rate;
+}
+
+#define NAME st_rate_flow_mix
+#define OP(a, b) a += b
+#include "rate_template.h"
+
+#define NAME st_rate_flow
+#define OP(a, b) a = b
+#include "rate_template.h"
+
+void st_rate_stop (void *opaque)
+{
+    qemu_free (opaque);
+}
+
+void mixeng_clear (st_sample_t *buf, int len)
+{
+    memset (buf, 0, len * sizeof (st_sample_t));
+}
diff --git a/audio/mixeng.h b/audio/mixeng.h
new file mode 100644
index 0000000..95b68df
--- /dev/null
+++ b/audio/mixeng.h
@@ -0,0 +1,51 @@
+/*
+ * QEMU Mixing engine header
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef QEMU_MIXENG_H
+#define QEMU_MIXENG_H
+
+#ifdef FLOAT_MIXENG
+typedef float real_t;
+typedef struct { int mute; real_t r; real_t l; } volume_t;
+typedef struct { real_t l; real_t r; } st_sample_t;
+#else
+typedef struct { int mute; int64_t r; int64_t l; } volume_t;
+typedef struct { int64_t l; int64_t r; } st_sample_t;
+#endif
+
+typedef void (t_sample) (st_sample_t *dst, const void *src,
+                         int samples, volume_t *vol);
+typedef void (f_sample) (void *dst, const st_sample_t *src, int samples);
+
+extern t_sample *mixeng_conv[2][2][2][3];
+extern f_sample *mixeng_clip[2][2][2][3];
+
+void *st_rate_start (int inrate, int outrate);
+void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+                   int *isamp, int *osamp);
+void st_rate_flow_mix (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+                       int *isamp, int *osamp);
+void st_rate_stop (void *opaque);
+void mixeng_clear (st_sample_t *buf, int len);
+
+#endif  /* mixeng.h */
diff --git a/audio/mixeng_template.h b/audio/mixeng_template.h
new file mode 100644
index 0000000..d726441
--- /dev/null
+++ b/audio/mixeng_template.h
@@ -0,0 +1,177 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Tusen tack till Mike Nordell
+ * dec++'ified by Dscho
+ */
+
+#ifndef SIGNED
+#define HALF (IN_MAX >> 1)
+#endif
+
+#ifdef NOVOL
+#define VOL(a, b) a
+#else
+#ifdef FLOAT_MIXENG
+#define VOL(a, b) ((a) * (b))
+#else
+#define VOL(a, b) ((a) * (b)) >> 32
+#endif
+#endif
+
+#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
+
+#ifdef FLOAT_MIXENG
+static real_t inline glue (conv_, ET) (IN_T v)
+{
+    IN_T nv = ENDIAN_CONVERT (v);
+
+#ifdef RECIPROCAL
+#ifdef SIGNED
+    return nv * (1.f / (real_t) (IN_MAX - IN_MIN));
+#else
+    return (nv - HALF) * (1.f / (real_t) IN_MAX);
+#endif
+#else  /* !RECIPROCAL */
+#ifdef SIGNED
+    return nv / (real_t) (IN_MAX - IN_MIN);
+#else
+    return (nv - HALF) / (real_t) IN_MAX;
+#endif
+#endif
+}
+
+static IN_T inline glue (clip_, ET) (real_t v)
+{
+    if (v >= 0.5) {
+        return IN_MAX;
+    }
+    else if (v < -0.5) {
+        return IN_MIN;
+    }
+
+#ifdef SIGNED
+    return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
+#else
+    return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
+#endif
+}
+
+#else  /* !FLOAT_MIXENG */
+
+static inline int64_t glue (conv_, ET) (IN_T v)
+{
+    IN_T nv = ENDIAN_CONVERT (v);
+#ifdef SIGNED
+    return ((int64_t) nv) << (32 - SHIFT);
+#else
+    return ((int64_t) nv - HALF) << (32 - SHIFT);
+#endif
+}
+
+static inline IN_T glue (clip_, ET) (int64_t v)
+{
+    if (v >= 0x7f000000) {
+        return IN_MAX;
+    }
+    else if (v < -2147483648LL) {
+        return IN_MIN;
+    }
+
+#ifdef SIGNED
+    return ENDIAN_CONVERT ((IN_T) (v >> (32 - SHIFT)));
+#else
+    return ENDIAN_CONVERT ((IN_T) ((v >> (32 - SHIFT)) + HALF));
+#endif
+}
+#endif
+
+static void glue (glue (conv_, ET), _to_stereo)
+    (st_sample_t *dst, const void *src, int samples, volume_t *vol)
+{
+    st_sample_t *out = dst;
+    IN_T *in = (IN_T *) src;
+#ifndef NOVOL
+    if (vol->mute) {
+        mixeng_clear (dst, samples);
+        return;
+    }
+#else
+    (void) vol;
+#endif
+    while (samples--) {
+        out->l = VOL (glue (conv_, ET) (*in++), vol->l);
+        out->r = VOL (glue (conv_, ET) (*in++), vol->r);
+        out += 1;
+    }
+}
+
+static void glue (glue (conv_, ET), _to_mono)
+    (st_sample_t *dst, const void *src, int samples, volume_t *vol)
+{
+    st_sample_t *out = dst;
+    IN_T *in = (IN_T *) src;
+#ifndef NOVOL
+    if (vol->mute) {
+        mixeng_clear (dst, samples);
+        return;
+    }
+#else
+    (void) vol;
+#endif
+    while (samples--) {
+        out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
+        out->r = out->l;
+        out += 1;
+        in += 1;
+    }
+}
+
+static void glue (glue (clip_, ET), _from_stereo)
+    (void *dst, const st_sample_t *src, int samples)
+{
+    const st_sample_t *in = src;
+    IN_T *out = (IN_T *) dst;
+    while (samples--) {
+        *out++ = glue (clip_, ET) (in->l);
+        *out++ = glue (clip_, ET) (in->r);
+        in += 1;
+    }
+}
+
+static void glue (glue (clip_, ET), _from_mono)
+    (void *dst, const st_sample_t *src, int samples)
+{
+    const st_sample_t *in = src;
+    IN_T *out = (IN_T *) dst;
+    while (samples--) {
+        *out++ = glue (clip_, ET) (in->l + in->r);
+        in += 1;
+    }
+}
+
+#undef ET
+#undef HALF
+#undef VOL
diff --git a/audio/noaudio.c b/audio/noaudio.c
new file mode 100644
index 0000000..8788a41
--- /dev/null
+++ b/audio/noaudio.c
@@ -0,0 +1,172 @@
+/*
+ * QEMU Timer based audio emulation
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-timer.h"
+
+#define AUDIO_CAP "noaudio"
+#include "audio_int.h"
+
+typedef struct NoVoiceOut {
+    HWVoiceOut hw;
+    int64_t old_ticks;
+} NoVoiceOut;
+
+typedef struct NoVoiceIn {
+    HWVoiceIn hw;
+    int64_t old_ticks;
+} NoVoiceIn;
+
+static int no_run_out (HWVoiceOut *hw)
+{
+    NoVoiceOut *no = (NoVoiceOut *) hw;
+    int live, decr, samples;
+    int64_t now;
+    int64_t ticks;
+    int64_t bytes;
+
+    live = audio_pcm_hw_get_live_out (&no->hw);
+    if (!live) {
+        return 0;
+    }
+
+    now = qemu_get_clock (vm_clock);
+    ticks = now - no->old_ticks;
+    bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+    bytes = audio_MIN (bytes, INT_MAX);
+    samples = bytes >> hw->info.shift;
+
+    no->old_ticks = now;
+    decr = audio_MIN (live, samples);
+    hw->rpos = (hw->rpos + decr) % hw->samples;
+    return decr;
+}
+
+static int no_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = 1024;
+    return 0;
+}
+
+static void no_fini_out (HWVoiceOut *hw)
+{
+    (void) hw;
+}
+
+static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+static int no_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = 1024;
+    return 0;
+}
+
+static void no_fini_in (HWVoiceIn *hw)
+{
+    (void) hw;
+}
+
+static int no_run_in (HWVoiceIn *hw)
+{
+    NoVoiceIn *no = (NoVoiceIn *) hw;
+    int live = audio_pcm_hw_get_live_in (hw);
+    int dead = hw->samples - live;
+    int samples = 0;
+
+    if (dead) {
+        int64_t now = qemu_get_clock (vm_clock);
+        int64_t ticks = now - no->old_ticks;
+        int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+
+        no->old_ticks = now;
+        bytes = audio_MIN (bytes, INT_MAX);
+        samples = bytes >> hw->info.shift;
+        samples = audio_MIN (samples, dead);
+    }
+    return samples;
+}
+
+static int no_read (SWVoiceIn *sw, void *buf, int size)
+{
+    int samples = size >> sw->info.shift;
+    int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
+    int to_clear = audio_MIN (samples, total);
+    audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
+    return to_clear;
+}
+
+static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+static void *no_audio_init (void)
+{
+    return &no_audio_init;
+}
+
+static void no_audio_fini (void *opaque)
+{
+    (void) opaque;
+}
+
+static struct audio_pcm_ops no_pcm_ops = {
+    no_init_out,
+    no_fini_out,
+    no_run_out,
+    no_write,
+    no_ctl_out,
+
+    no_init_in,
+    no_fini_in,
+    no_run_in,
+    no_read,
+    no_ctl_in
+};
+
+struct audio_driver no_audio_driver = {
+    INIT_FIELD (name           = ) "none",
+    INIT_FIELD (descr          = ) "disabled audio",
+    INIT_FIELD (options        = ) NULL,
+    INIT_FIELD (init           = ) no_audio_init,
+    INIT_FIELD (fini           = ) no_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &no_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) INT_MAX,
+    INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (NoVoiceIn)
+};
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
new file mode 100644
index 0000000..2ccaade
--- /dev/null
+++ b/audio/ossaudio.c
@@ -0,0 +1,773 @@
+/*
+ * QEMU OSS audio driver
+ *
+ * Copyright (c) 2003-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+
+#define AUDIO_CAP "oss"
+#include "audio_int.h"
+
+typedef struct OSSVoiceOut {
+    HWVoiceOut hw;
+    void *pcm_buf;
+    int fd;
+    int nfrags;
+    int fragsize;
+    int mmapped;
+    int old_optr;
+} OSSVoiceOut;
+
+typedef struct OSSVoiceIn {
+    HWVoiceIn hw;
+    void *pcm_buf;
+    int fd;
+    int nfrags;
+    int fragsize;
+    int old_optr;
+} OSSVoiceIn;
+
+static struct {
+    int try_mmap;
+    int nfrags;
+    int fragsize;
+    const char *devpath_out;
+    const char *devpath_in;
+    int debug;
+} conf = {
+    .try_mmap = 0,
+    .nfrags = 4,
+    .fragsize = 4096,
+    .devpath_out = "/dev/dsp",
+    .devpath_in = "/dev/dsp",
+    .debug = 0
+};
+
+struct oss_params {
+    int freq;
+    audfmt_e fmt;
+    int nchannels;
+    int nfrags;
+    int fragsize;
+};
+
+static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
+    int err,
+    const char *typ,
+    const char *fmt,
+    ...
+    )
+{
+    va_list ap;
+
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+static void oss_anal_close (int *fdp)
+{
+    int err = close (*fdp);
+    if (err) {
+        oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp);
+    }
+    *fdp = -1;
+}
+
+static int oss_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int aud_to_ossfmt (audfmt_e fmt)
+{
+    switch (fmt) {
+    case AUD_FMT_S8:
+        return AFMT_S8;
+
+    case AUD_FMT_U8:
+        return AFMT_U8;
+
+    case AUD_FMT_S16:
+        return AFMT_S16_LE;
+
+    case AUD_FMT_U16:
+        return AFMT_U16_LE;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+        abort ();
+#endif
+        return AFMT_U8;
+    }
+}
+
+static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
+{
+    switch (ossfmt) {
+    case AFMT_S8:
+        *endianness =0;
+        *fmt = AUD_FMT_S8;
+        break;
+
+    case AFMT_U8:
+        *endianness = 0;
+        *fmt = AUD_FMT_U8;
+        break;
+
+    case AFMT_S16_LE:
+        *endianness = 0;
+        *fmt = AUD_FMT_S16;
+        break;
+
+    case AFMT_U16_LE:
+        *endianness = 0;
+        *fmt = AUD_FMT_U16;
+        break;
+
+    case AFMT_S16_BE:
+        *endianness = 1;
+        *fmt = AUD_FMT_S16;
+        break;
+
+    case AFMT_U16_BE:
+        *endianness = 1;
+        *fmt = AUD_FMT_U16;
+        break;
+
+    default:
+        dolog ("Unrecognized audio format %d\n", ossfmt);
+        return -1;
+    }
+
+    return 0;
+}
+
+#if defined DEBUG_MISMATCHES || defined DEBUG
+static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
+{
+    dolog ("parameter | requested value | obtained value\n");
+    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
+    dolog ("channels  |      %10d |     %10d\n",
+           req->nchannels, obt->nchannels);
+    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
+    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
+    dolog ("fragsize  |      %10d |     %10d\n",
+           req->fragsize, obt->fragsize);
+}
+#endif
+
+static int oss_open (int in, struct oss_params *req,
+                     struct oss_params *obt, int *pfd)
+{
+    int fd;
+    int mmmmssss;
+    audio_buf_info abinfo;
+    int fmt, freq, nchannels;
+    const char *dspname = in ? conf.devpath_in : conf.devpath_out;
+    const char *typ = in ? "ADC" : "DAC";
+
+    fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK);
+    if (-1 == fd) {
+        oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname);
+        return -1;
+    }
+
+    freq = req->freq;
+    nchannels = req->nchannels;
+    fmt = req->fmt;
+
+    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
+        oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt);
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
+        oss_logerr2 (errno, typ, "Failed to set number of channels %d\n",
+                     req->nchannels);
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
+        oss_logerr2 (errno, typ, "Failed to set frequency %d\n", req->freq);
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_NONBLOCK, NULL)) {
+        oss_logerr2 (errno, typ, "Failed to set non-blocking mode\n");
+        goto err;
+    }
+
+    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
+    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
+        oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n",
+                     req->nfrags, req->fragsize);
+        goto err;
+    }
+
+    if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) {
+        oss_logerr2 (errno, typ, "Failed to get buffer length\n");
+        goto err;
+    }
+
+    if (!abinfo.fragstotal || !abinfo.fragsize) {
+        AUD_log(AUDIO_CAP, "Returned bogus buffer information(%d, %d) for %s\n",
+                abinfo.fragstotal, abinfo.fragsize, typ);
+        goto err;
+    }
+
+    obt->fmt = fmt;
+    obt->nchannels = nchannels;
+    obt->freq = freq;
+    obt->nfrags = abinfo.fragstotal;
+    obt->fragsize = abinfo.fragsize;
+    *pfd = fd;
+
+#ifdef DEBUG_MISMATCHES
+    if ((req->fmt != obt->fmt) ||
+        (req->nchannels != obt->nchannels) ||
+        (req->freq != obt->freq) ||
+        (req->fragsize != obt->fragsize) ||
+        (req->nfrags != obt->nfrags)) {
+        dolog ("Audio parameters mismatch\n");
+        oss_dump_info (req, obt);
+    }
+#endif
+
+#ifdef DEBUG
+    oss_dump_info (req, obt);
+#endif
+    return 0;
+
+ err:
+    oss_anal_close (&fd);
+    return -1;
+}
+
+static int oss_run_out (HWVoiceOut *hw)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    int err, rpos, live, decr;
+    int samples;
+    uint8_t *dst;
+    st_sample_t *src;
+    struct audio_buf_info abinfo;
+    struct count_info cntinfo;
+    int bufsize;
+
+    live = audio_pcm_hw_get_live_out (hw);
+    if (!live) {
+        return 0;
+    }
+
+    bufsize = hw->samples << hw->info.shift;
+
+    if (oss->mmapped) {
+        int bytes;
+
+        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
+        if (err < 0) {
+            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
+            return 0;
+        }
+
+        if (cntinfo.ptr == oss->old_optr) {
+            if (abs (hw->samples - live) < 64) {
+                dolog ("warning: Overrun\n");
+            }
+            return 0;
+        }
+
+        if (cntinfo.ptr > oss->old_optr) {
+            bytes = cntinfo.ptr - oss->old_optr;
+        }
+        else {
+            bytes = bufsize + cntinfo.ptr - oss->old_optr;
+        }
+
+        decr = audio_MIN (bytes >> hw->info.shift, live);
+    }
+    else {
+        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
+        if (err < 0) {
+            oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
+            return 0;
+        }
+
+        if (abinfo.bytes > bufsize) {
+            if (conf.debug) {
+                dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
+                       "please report your OS/audio hw to malc@pulsesoft.com\n",
+                       abinfo.bytes, bufsize);
+            }
+            abinfo.bytes = bufsize;
+        }
+
+        if (abinfo.bytes < 0) {
+            if (conf.debug) {
+                dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
+                       abinfo.bytes, bufsize);
+            }
+            return 0;
+        }
+
+        decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
+        if (!decr) {
+            return 0;
+        }
+    }
+
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+        src = hw->mix_buf + rpos;
+        dst = advance (oss->pcm_buf, rpos << hw->info.shift);
+
+        hw->clip (dst, src, convert_samples);
+        if (!oss->mmapped) {
+            int written;
+
+            written = write (oss->fd, dst, convert_samples << hw->info.shift);
+            /* XXX: follow errno recommendations ? */
+            if (written == -1) {
+                oss_logerr (
+                    errno,
+                    "Failed to write %d bytes of audio data from %p\n",
+                    convert_samples << hw->info.shift,
+                    dst
+                    );
+                continue;
+            }
+
+            if (written != convert_samples << hw->info.shift) {
+                int wsamples = written >> hw->info.shift;
+                int wbytes = wsamples << hw->info.shift;
+                if (wbytes != written) {
+                    dolog ("warning: Misaligned write %d (requested %d), "
+                           "alignment %d\n",
+                           wbytes, written, hw->info.align + 1);
+                }
+                decr -= wsamples;
+                rpos = (rpos + wsamples) % hw->samples;
+                break;
+            }
+        }
+
+        rpos = (rpos + convert_samples) % hw->samples;
+        samples -= convert_samples;
+    }
+    if (oss->mmapped) {
+        oss->old_optr = cntinfo.ptr;
+    }
+
+    hw->rpos = rpos;
+    return decr;
+}
+
+static void oss_fini_out (HWVoiceOut *hw)
+{
+    int err;
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+    ldebug ("oss_fini\n");
+    oss_anal_close (&oss->fd);
+
+    if (oss->pcm_buf) {
+        if (oss->mmapped) {
+            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+            if (err) {
+                oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
+                            oss->pcm_buf, hw->samples << hw->info.shift);
+            }
+        }
+        else {
+            qemu_free (oss->pcm_buf);
+        }
+        oss->pcm_buf = NULL;
+    }
+}
+
+static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+    struct oss_params req, obt;
+    int endianness;
+    int err;
+    int fd;
+    audfmt_e effective_fmt;
+    audsettings_t obt_as;
+
+    oss->fd = -1;
+
+    req.fmt = aud_to_ossfmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
+    req.fragsize = conf.fragsize;
+    req.nfrags = conf.nfrags;
+
+    if (oss_open (0, &req, &obt, &fd)) {
+        return -1;
+    }
+
+    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+    if (err) {
+        oss_anal_close (&fd);
+        return -1;
+    }
+
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = effective_fmt;
+    obt_as.endianness = endianness;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+    oss->nfrags = obt.nfrags;
+    oss->fragsize = obt.fragsize;
+
+    if (obt.nfrags * obt.fragsize & hw->info.align) {
+        dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
+               obt.nfrags * obt.fragsize, hw->info.align + 1);
+    }
+
+    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+
+    oss->mmapped = 0;
+    if (conf.try_mmap) {
+        oss->pcm_buf = mmap (
+            0,
+            hw->samples << hw->info.shift,
+            PROT_READ | PROT_WRITE,
+            MAP_SHARED,
+            fd,
+            0
+            );
+        if (oss->pcm_buf == MAP_FAILED) {
+            oss_logerr (errno, "Failed to map %d bytes of DAC\n",
+                        hw->samples << hw->info.shift);
+        } else {
+            int err;
+            int trig = 0;
+            if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+                oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
+            }
+            else {
+                trig = PCM_ENABLE_OUTPUT;
+                if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+                    oss_logerr (
+                        errno,
+                        "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+                        );
+                }
+                else {
+                    oss->mmapped = 1;
+                }
+            }
+
+            if (!oss->mmapped) {
+                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
+                if (err) {
+                    oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
+                                oss->pcm_buf, hw->samples << hw->info.shift);
+                }
+            }
+        }
+    }
+
+    if (!oss->mmapped) {
+        oss->pcm_buf = audio_calloc (
+            AUDIO_FUNC,
+            hw->samples,
+            1 << hw->info.shift
+            );
+        if (!oss->pcm_buf) {
+            dolog (
+                "Could not allocate DAC buffer (%d samples, each %d bytes)\n",
+                hw->samples,
+                1 << hw->info.shift
+                );
+            oss_anal_close (&fd);
+            return -1;
+        }
+    }
+
+    oss->fd = fd;
+    return 0;
+}
+
+static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    int trig;
+    OSSVoiceOut *oss = (OSSVoiceOut *) hw;
+
+    if (!oss->mmapped) {
+        return 0;
+    }
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        ldebug ("enabling voice\n");
+        audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
+        trig = PCM_ENABLE_OUTPUT;
+        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+            oss_logerr (
+                errno,
+                "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+                );
+            return -1;
+        }
+        break;
+
+    case VOICE_DISABLE:
+        ldebug ("disabling voice\n");
+        trig = 0;
+        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+            oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
+            return -1;
+        }
+        break;
+    }
+    return 0;
+}
+
+static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+    struct oss_params req, obt;
+    int endianness;
+    int err;
+    int fd;
+    audfmt_e effective_fmt;
+    audsettings_t obt_as;
+
+    oss->fd = -1;
+
+    req.fmt = aud_to_ossfmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
+    req.fragsize = conf.fragsize;
+    req.nfrags = conf.nfrags;
+    if (oss_open (1, &req, &obt, &fd)) {
+        return -1;
+    }
+
+    err = oss_to_audfmt (obt.fmt, &effective_fmt, &endianness);
+    if (err) {
+        oss_anal_close (&fd);
+        return -1;
+    }
+
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = effective_fmt;
+    obt_as.endianness = endianness;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+    oss->nfrags = obt.nfrags;
+    oss->fragsize = obt.fragsize;
+
+    if (obt.nfrags * obt.fragsize & hw->info.align) {
+        dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
+               obt.nfrags * obt.fragsize, hw->info.align + 1);
+    }
+
+    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!oss->pcm_buf) {
+        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
+               hw->samples, 1 << hw->info.shift);
+        oss_anal_close (&fd);
+        return -1;
+    }
+
+    oss->fd = fd;
+    return 0;
+}
+
+static void oss_fini_in (HWVoiceIn *hw)
+{
+    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+
+    oss_anal_close (&oss->fd);
+
+    if (oss->pcm_buf) {
+        qemu_free (oss->pcm_buf);
+        oss->pcm_buf = NULL;
+    }
+}
+
+static int oss_run_in (HWVoiceIn *hw)
+{
+    OSSVoiceIn *oss = (OSSVoiceIn *) hw;
+    int hwshift = hw->info.shift;
+    int i;
+    int live = audio_pcm_hw_get_live_in (hw);
+    int dead = hw->samples - live;
+    size_t read_samples = 0;
+    struct {
+        int add;
+        int len;
+    } bufs[2] = {
+        { hw->wpos, 0 },
+        { 0, 0 }
+    };
+
+    if (!dead) {
+        return 0;
+    }
+
+    if (hw->wpos + dead > hw->samples) {
+        bufs[0].len = (hw->samples - hw->wpos) << hwshift;
+        bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
+    }
+    else {
+        bufs[0].len = dead << hwshift;
+    }
+
+
+    for (i = 0; i < 2; ++i) {
+        ssize_t nread;
+
+        if (bufs[i].len) {
+            void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
+            nread = read (oss->fd, p, bufs[i].len);
+
+            if (nread > 0) {
+                if (nread & hw->info.align) {
+                    dolog ("warning: Misaligned read %zd (requested %d), "
+                           "alignment %d\n", nread, bufs[i].add << hwshift,
+                           hw->info.align + 1);
+                }
+                read_samples += nread >> hwshift;
+                hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
+                          &nominal_volume);
+            }
+
+            if (bufs[i].len - nread) {
+                if (nread == -1) {
+                    switch (errno) {
+                    case EINTR:
+                    case EAGAIN:
+                        break;
+                    default:
+                        oss_logerr (
+                            errno,
+                            "Failed to read %d bytes of audio (to %p)\n",
+                            bufs[i].len, p
+                            );
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    hw->wpos = (hw->wpos + read_samples) % hw->samples;
+    return read_samples;
+}
+
+static int oss_read (SWVoiceIn *sw, void *buf, int size)
+{
+    return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+static void *oss_audio_init (void)
+{
+    return &conf;
+}
+
+static void oss_audio_fini (void *opaque)
+{
+    (void) opaque;
+}
+
+static struct audio_option oss_options[] = {
+    {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize,
+     "Fragment size in bytes", NULL, 0},
+    {"NFRAGS", AUD_OPT_INT, &conf.nfrags,
+     "Number of fragments", NULL, 0},
+    {"MMAP", AUD_OPT_BOOL, &conf.try_mmap,
+     "Try using memory mapped access", NULL, 0},
+    {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out,
+     "Path to DAC device", NULL, 0},
+    {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
+     "Path to ADC device", NULL, 0},
+    {"DEBUG", AUD_OPT_BOOL, &conf.debug,
+     "Turn on some debugging messages", NULL, 0},
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops oss_pcm_ops = {
+    oss_init_out,
+    oss_fini_out,
+    oss_run_out,
+    oss_write,
+    oss_ctl_out,
+
+    oss_init_in,
+    oss_fini_in,
+    oss_run_in,
+    oss_read,
+    oss_ctl_in
+};
+
+struct audio_driver oss_audio_driver = {
+    INIT_FIELD (name           = ) "oss",
+    INIT_FIELD (descr          = ) "OSS audio (www.opensound.com)",
+    INIT_FIELD (options        = ) oss_options,
+    INIT_FIELD (init           = ) oss_audio_init,
+    INIT_FIELD (fini           = ) oss_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &oss_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) INT_MAX,
+    INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (OSSVoiceIn)
+};
diff --git a/audio/rate_template.h b/audio/rate_template.h
new file mode 100644
index 0000000..398d305
--- /dev/null
+++ b/audio/rate_template.h
@@ -0,0 +1,111 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+           int *isamp, int *osamp)
+{
+    struct rate *rate = opaque;
+    st_sample_t *istart, *iend;
+    st_sample_t *ostart, *oend;
+    st_sample_t ilast, icur, out;
+#ifdef FLOAT_MIXENG
+    real_t t;
+#else
+    int64_t t;
+#endif
+
+    ilast = rate->ilast;
+
+    istart = ibuf;
+    iend = ibuf + *isamp;
+
+    ostart = obuf;
+    oend = obuf + *osamp;
+
+    if (rate->opos_inc == (1ULL + UINT_MAX)) {
+        int i, n = *isamp > *osamp ? *osamp : *isamp;
+        for (i = 0; i < n; i++) {
+            OP (obuf[i].l, ibuf[i].l);
+            OP (obuf[i].r, ibuf[i].r);
+        }
+        *isamp = n;
+        *osamp = n;
+        return;
+    }
+
+    while (obuf < oend) {
+
+        /* Safety catch to make sure we have input samples.  */
+        if (ibuf >= iend) {
+            break;
+        }
+
+        /* read as many input samples so that ipos > opos */
+
+        while (rate->ipos <= (rate->opos >> 32)) {
+            ilast = *ibuf++;
+            rate->ipos++;
+            /* See if we finished the input buffer yet */
+            if (ibuf >= iend) {
+                goto the_end;
+            }
+        }
+
+        icur = *ibuf;
+
+        /* interpolate */
+#ifdef FLOAT_MIXENG
+#ifdef RECIPROCAL
+        t = (rate->opos & UINT_MAX) * (1.f / UINT_MAX);
+#else
+        t = (rate->opos & UINT_MAX) / (real_t) UINT_MAX;
+#endif
+        out.l = (ilast.l * (1.0 - t)) + icur.l * t;
+        out.r = (ilast.r * (1.0 - t)) + icur.r * t;
+#else
+        t = rate->opos & 0xffffffff;
+        out.l = (ilast.l * ((int64_t) UINT_MAX - t) + icur.l * t) >> 32;
+        out.r = (ilast.r * ((int64_t) UINT_MAX - t) + icur.r * t) >> 32;
+#endif
+
+        /* output sample & increment position */
+        OP (obuf->l, out.l);
+        OP (obuf->r, out.r);
+        obuf += 1;
+        rate->opos += rate->opos_inc;
+    }
+
+the_end:
+    *isamp = ibuf - istart;
+    *osamp = obuf - ostart;
+    rate->ilast = ilast;
+}
+
+#undef NAME
+#undef OP
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
new file mode 100644
index 0000000..ea5bccd
--- /dev/null
+++ b/audio/sdlaudio.c
@@ -0,0 +1,660 @@
+/*
+ * QEMU SDL audio driver
+ *
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <SDL.h>
+#include <SDL_thread.h>
+
+#ifndef _WIN32
+#ifdef __sun__
+#define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+#include <signal.h>
+#endif
+
+#define AUDIO_CAP "sdl"
+#include "audio_int.h"
+
+/* define DEBUG to 1 to dump audio debugging info at runtime to stderr */
+#define  DEBUG  0
+
+/* define NEW_AUDIO to 1 to activate the new audio thread callback */
+#define  NEW_AUDIO 1
+
+#if DEBUG
+#  define  D(...)   fprintf(stderr, __VA_ARGS__)
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+static struct {
+    int nb_samples;
+} conf = {
+    1024
+};
+
+#if DEBUG
+int64_t  start_time;
+#endif
+
+#if NEW_AUDIO
+
+#define  AUDIO_BUFFER_SIZE  (8192)
+
+typedef HWVoiceOut  SDLVoiceOut;
+
+struct SDLAudioState {
+    int         exit;
+    SDL_mutex*  mutex;
+    int         initialized;
+    uint8_t     data[ AUDIO_BUFFER_SIZE ];
+    int         pos, count;
+} glob_sdl;
+#else /* !NEW_AUDIO */
+
+typedef struct SDLVoiceOut {
+    HWVoiceOut hw;
+    int live;
+    int rpos;
+    int decr;
+} SDLVoiceOut;
+
+struct SDLAudioState {
+    int exit;
+    SDL_mutex *mutex;
+    SDL_sem *sem;
+    int initialized;
+} glob_sdl;
+
+#endif /* !NEW_AUDIO */
+
+typedef struct SDLAudioState SDLAudioState;
+
+static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ());
+}
+
+static int sdl_lock (SDLAudioState *s, const char *forfn)
+{
+    if (SDL_LockMutex (s->mutex)) {
+        sdl_logerr ("SDL_LockMutex for %s failed\n", forfn);
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_unlock (SDLAudioState *s, const char *forfn)
+{
+    if (SDL_UnlockMutex (s->mutex)) {
+        sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn);
+        return -1;
+    }
+    return 0;
+}
+
+#if !NEW_AUDIO
+static int sdl_post (SDLAudioState *s, const char *forfn)
+{
+    if (SDL_SemPost (s->sem)) {
+        sdl_logerr ("SDL_SemPost for %s failed\n", forfn);
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_wait (SDLAudioState *s, const char *forfn)
+{
+    if (SDL_SemWait (s->sem)) {
+        sdl_logerr ("SDL_SemWait for %s failed\n", forfn);
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
+{
+    if (sdl_unlock (s, forfn)) {
+        return -1;
+    }
+
+    return sdl_post (s, forfn);
+}
+#endif
+
+static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
+{
+    switch (fmt) {
+    case AUD_FMT_S8:
+        *shift = 0;
+        return AUDIO_S8;
+
+    case AUD_FMT_U8:
+        *shift = 0;
+        return AUDIO_U8;
+
+    case AUD_FMT_S16:
+        *shift = 1;
+        return AUDIO_S16LSB;
+
+    case AUD_FMT_U16:
+        *shift = 1;
+        return AUDIO_U16LSB;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", fmt);
+#ifdef DEBUG_AUDIO
+        abort ();
+#endif
+        return AUDIO_U8;
+    }
+}
+
+static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
+{
+    switch (sdlfmt) {
+    case AUDIO_S8:
+        *endianess = 0;
+        *fmt = AUD_FMT_S8;
+        break;
+
+    case AUDIO_U8:
+        *endianess = 0;
+        *fmt = AUD_FMT_U8;
+        break;
+
+    case AUDIO_S16LSB:
+        *endianess = 0;
+        *fmt = AUD_FMT_S16;
+        break;
+
+    case AUDIO_U16LSB:
+        *endianess = 0;
+        *fmt = AUD_FMT_U16;
+        break;
+
+    case AUDIO_S16MSB:
+        *endianess = 1;
+        *fmt = AUD_FMT_S16;
+        break;
+
+    case AUDIO_U16MSB:
+        *endianess = 1;
+        *fmt = AUD_FMT_U16;
+        break;
+
+    default:
+        dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
+{
+    int status;
+#ifndef _WIN32
+    sigset_t new, old;
+
+    /* Make sure potential threads created by SDL don't hog signals.  */
+    sigfillset (&new);
+    pthread_sigmask (SIG_BLOCK, &new, &old);
+#endif
+
+    status = SDL_OpenAudio (req, obt);
+    if (status) {
+        sdl_logerr ("SDL_OpenAudio failed\n");
+    }
+#ifndef _WIN32
+    pthread_sigmask (SIG_SETMASK, &old, 0);
+#endif
+    return status;
+}
+
+static void sdl_close (SDLAudioState *s)
+{
+    if (s->initialized) {
+        sdl_lock (s, "sdl_close");
+        s->exit = 1;
+#if NEW_AUDIO
+        sdl_unlock (s, "sdl_close");
+#else
+        sdl_unlock_and_post (s, "sdl_close");
+#endif
+        SDL_PauseAudio (1);
+        SDL_CloseAudio ();
+        s->initialized = 0;
+    }
+}
+
+#if NEW_AUDIO
+
+static void sdl_callback (void *opaque, Uint8 *buf, int len)
+{
+#if DEBUG
+    int64_t    now;
+#endif
+    SDLAudioState *s = &glob_sdl;
+
+    if (s->exit) {
+        return;
+    }
+
+    sdl_lock (s, "sdl_callback");
+#if DEBUG
+    if (s->count > 0) {
+        now = qemu_get_clock(vm_clock);
+        if (start_time == 0)
+            start_time = now;
+        now = now - start_time;
+        D( "R %6.3f: pos:%5d count:%5d len:%5d\n", now/1e9, s->pos, s->count, len );
+    }
+#endif
+    while (len > 0) {
+        int  avail = audio_MIN( AUDIO_BUFFER_SIZE - s->pos, s->count );
+
+        if (avail == 0)
+            break;
+
+        if (avail > len)
+            avail = len;
+
+        memcpy( buf, s->data + s->pos, avail );
+        buf += avail;
+        len -= avail;
+
+        s->count -= avail;
+        s->pos   += avail;
+        if (s->pos == AUDIO_BUFFER_SIZE)
+            s->pos = 0;
+    }
+    sdl_unlock (s, "sdl_callback");
+}
+
+#else /* !NEW_AUDIO */
+static void sdl_callback (void *opaque, Uint8 *buf, int len)
+{
+    SDLVoiceOut *sdl = opaque;
+    SDLAudioState *s = &glob_sdl;
+    HWVoiceOut *hw = &sdl->hw;
+    int samples = len >> hw->info.shift;
+
+    if (s->exit) {
+        return;
+    }
+
+    while (samples) {
+        int to_mix, decr;
+
+        /* dolog ("in callback samples=%d\n", samples); */
+        sdl_wait (s, "sdl_callback");
+        if (s->exit) {
+            return;
+        }
+
+        if (sdl_lock (s, "sdl_callback")) {
+            return;
+        }
+
+        if (audio_bug (AUDIO_FUNC, sdl->live < 0 || sdl->live > hw->samples)) {
+            dolog ("sdl->live=%d hw->samples=%d\n",
+                   sdl->live, hw->samples);
+            return;
+        }
+
+        if (!sdl->live) {
+            goto again;
+        }
+
+        /* dolog ("in callback live=%d\n", live); */
+        to_mix = audio_MIN (samples, sdl->live);
+        decr = to_mix;
+        while (to_mix) {
+            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
+            st_sample_t *src = hw->mix_buf + hw->rpos;
+
+            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
+            hw->clip (buf, src, chunk);
+            sdl->rpos = (sdl->rpos + chunk) % hw->samples;
+            to_mix -= chunk;
+            buf += chunk << hw->info.shift;
+        }
+        samples -= decr;
+        sdl->live -= decr;
+        sdl->decr += decr;
+
+    again:
+        if (sdl_unlock (s, "sdl_callback")) {
+            return;
+        }
+    }
+    /* dolog ("done len=%d\n", len); */
+}
+#endif /* !NEW_AUDIO */
+
+static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+#if NEW_AUDIO
+
+static int sdl_run_out (HWVoiceOut *hw)
+{
+    SDLAudioState *s = &glob_sdl;
+    int  live, avail, end, total;
+
+    if (sdl_lock (s, "sdl_run_out")) {
+        return 0;
+    }
+    avail = AUDIO_BUFFER_SIZE - s->count;
+    end   = s->pos + s->count;
+    if (end >= AUDIO_BUFFER_SIZE)
+        end -= AUDIO_BUFFER_SIZE;
+    sdl_unlock (s, "sdl_run_out");
+
+    live = audio_pcm_hw_get_live_out (hw);
+
+    total = 0;
+    while (live > 0) {
+        int           bytes     = audio_MIN(AUDIO_BUFFER_SIZE - end, avail);
+        int           samples   = bytes >> hw->info.shift;
+        int           hwsamples = audio_MIN(hw->samples - hw->rpos, live);
+        uint8_t*      dst       = s->data + end;
+        st_sample_t*  src       = hw->mix_buf + hw->rpos;
+
+        if (samples == 0)
+            break;
+
+        if (samples > hwsamples) {
+            samples = hwsamples;
+            bytes   = hwsamples << hw->info.shift;
+        }
+
+        hw->clip (dst, src, samples);
+        hw->rpos += samples;
+        if (hw->rpos == hw->samples)
+            hw->rpos = 0;
+
+        live  -= samples;
+        avail -= bytes;
+        end   += bytes;
+        if (end == AUDIO_BUFFER_SIZE)
+            end = 0;
+
+        total += bytes;
+    }
+
+    sdl_lock (s, "sdl_run_out");
+    s->count += total;
+    sdl_unlock (s, "sdl_run_out");
+
+    return  total >> hw->info.shift;
+}
+
+#else /* !NEW_AUDIO */
+static int sdl_run_out (HWVoiceOut *hw)
+{
+    int decr, live;
+    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+    SDLAudioState *s = &glob_sdl;
+
+    if (sdl_lock (s, "sdl_callback")) {
+        return 0;
+    }
+
+    live = audio_pcm_hw_get_live_out (hw);
+
+    if (sdl->decr > live) {
+        ldebug ("sdl->decr %d live %d sdl->live %d\n",
+                sdl->decr,
+                live,
+                sdl->live);
+    }
+
+    decr = audio_MIN (sdl->decr, live);
+    sdl->decr -= decr;
+
+    sdl->live = live - decr;
+    hw->rpos = sdl->rpos;
+
+    if (sdl->live > 0) {
+        sdl_unlock_and_post (s, "sdl_callback");
+    }
+    else {
+        sdl_unlock (s, "sdl_callback");
+    }
+    return decr;
+}
+#endif /* !NEW_AUDIO */
+
+static void sdl_fini_out (HWVoiceOut *hw)
+{
+    (void) hw;
+
+    sdl_close (&glob_sdl);
+}
+
+#if DEBUG
+
+typedef struct { int  value; const char*  name; } MatchRec;
+typedef const MatchRec*                           Match;
+
+static const char*
+match_find( Match  matches, int  value, char*  temp )
+{
+    int  nn;
+    for ( nn = 0; matches[nn].name != NULL; nn++ ) {
+        if ( matches[nn].value == value )
+            return matches[nn].name;
+    }
+    sprintf( temp, "(%d?)", value );
+    return temp;
+}
+
+static const MatchRec   sdl_audio_format_matches[] = {
+    { AUDIO_U8, "AUDIO_U8" },
+    { AUDIO_S8, "AUDIO_S8" },
+    { AUDIO_U16, "AUDIO_U16LE" },
+    { AUDIO_S16, "AUDIO_S16LE" },
+    { AUDIO_U16MSB, "AUDIO_U16BE" },
+    { AUDIO_S16MSB, "AUDIO_S16BE" },
+    { 0, NULL }
+};
+
+static void
+print_sdl_audiospec( SDL_AudioSpec*  spec, const char*  prefix )
+{
+    char         temp[64];
+    const char*  fmt;
+
+    if (!prefix)
+        prefix = "";
+
+    printf( "%s audiospec [freq:%d format:%s channels:%d samples:%d bytes:%d",
+            prefix,
+            spec->freq,
+            match_find( sdl_audio_format_matches, spec->format, temp ),
+            spec->channels,
+            spec->samples,
+            spec->size
+          );
+    printf( "]\n" );
+}
+#endif
+
+static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
+    SDLAudioState *s = &glob_sdl;
+    SDL_AudioSpec req, obt;
+    int shift;
+    int endianess;
+    int err;
+    audfmt_e effective_fmt;
+    audsettings_t obt_as;
+
+    shift <<= as->nchannels == 2;
+
+    req.freq = as->freq;
+    req.format = aud_to_sdlfmt (as->fmt, &shift);
+    req.channels = as->nchannels;
+    req.samples = conf.nb_samples;
+    req.callback = sdl_callback;
+    req.userdata = sdl;
+
+#if DEBUG
+    print_sdl_audiospec( &req, "wanted" );
+#endif
+
+    if (sdl_open (&req, &obt)) {
+        return -1;
+    }
+
+#if DEBUG
+    print_sdl_audiospec( &req, "obtained" );
+#endif
+
+    err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
+    if (err) {
+        sdl_close (s);
+        return -1;
+    }
+
+    obt_as.freq       = obt.freq;
+    obt_as.nchannels  = obt.channels;
+    obt_as.fmt        = effective_fmt;
+    obt_as.endianness = endianess;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+    hw->samples = obt.samples;
+
+#if DEBUG
+    start_time = qemu_get_clock(vm_clock);
+#endif
+
+    s->initialized = 1;
+    s->exit        = 0;
+    SDL_PauseAudio (0);
+    return 0;
+}
+
+static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    (void) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        SDL_PauseAudio (0);
+        break;
+
+    case VOICE_DISABLE:
+        SDL_PauseAudio (1);
+        break;
+    }
+    return 0;
+}
+
+static void *sdl_audio_init (void)
+{
+    SDLAudioState *s = &glob_sdl;
+
+    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
+        sdl_logerr ("SDL failed to initialize audio subsystem\n");
+        return NULL;
+    }
+
+    s->mutex = SDL_CreateMutex ();
+    if (!s->mutex) {
+        sdl_logerr ("Failed to create SDL mutex\n");
+        SDL_QuitSubSystem (SDL_INIT_AUDIO);
+        return NULL;
+    }
+#if !NEW_AUDIO
+    s->sem = SDL_CreateSemaphore (0);
+    if (!s->sem) {
+        sdl_logerr ("Failed to create SDL semaphore\n");
+        SDL_DestroyMutex (s->mutex);
+        SDL_QuitSubSystem (SDL_INIT_AUDIO);
+        return NULL;
+    }
+#endif
+    return s;
+}
+
+static void sdl_audio_fini (void *opaque)
+{
+    SDLAudioState *s = opaque;
+    sdl_close (s);
+#if !NEW_AUDIO
+    if (s->sem) {
+        SDL_DestroySemaphore (s->sem);
+        s->sem = NULL;
+    }
+#endif
+    if (s->mutex) {
+        SDL_DestroyMutex (s->mutex);
+        s->mutex = NULL;
+    }
+    SDL_QuitSubSystem (SDL_INIT_AUDIO);
+}
+
+static struct audio_option sdl_options[] = {
+    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+     "Size of SDL buffer in samples", NULL, 0},
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops sdl_pcm_ops = {
+    sdl_init_out,
+    sdl_fini_out,
+    sdl_run_out,
+    sdl_write_out,
+    sdl_ctl_out,
+
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+struct audio_driver sdl_audio_driver = {
+    INIT_FIELD (name           = ) "sdl",
+    INIT_FIELD (descr          = ) "SDL audio (www.libsdl.org)",
+    INIT_FIELD (options        = ) sdl_options,
+    INIT_FIELD (init           = ) sdl_audio_init,
+    INIT_FIELD (fini           = ) sdl_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &sdl_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) 1,
+    INIT_FIELD (max_voices_in  = ) 0,
+    INIT_FIELD (voice_size_out = ) sizeof (SDLVoiceOut),
+    INIT_FIELD (voice_size_in  = ) 0
+};
diff --git a/audio/sys-queue.h b/audio/sys-queue.h
new file mode 100644
index 0000000..5b6e2a0
--- /dev/null
+++ b/audio/sys-queue.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)queue.h	8.3 (Berkeley) 12/13/93
+ */
+
+#ifndef	_SYS_QUEUE_H
+#define	_SYS_QUEUE_H 1
+
+/*
+ * This file defines three types of data structures: lists, tail queues,
+ * and circular queues.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element or at the head of the list. A list may only be
+ * traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list after
+ * an existing element, at the head of the list, or at the end of the
+ * list. A tail queue may only be traversed in the forward direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ */
+
+/*
+ * List definitions.
+ */
+#define LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+#define	LIST_INIT(head) {						\
+	(head)->lh_first = NULL;					\
+}
+
+#define LIST_INSERT_AFTER(listelm, elm, field) {			\
+	if (((elm)->field.le_next = (listelm)->field.le_next) != NULL)	\
+		(listelm)->field.le_next->field.le_prev =		\
+		    &(elm)->field.le_next;				\
+	(listelm)->field.le_next = (elm);				\
+	(elm)->field.le_prev = &(listelm)->field.le_next;		\
+}
+
+#define LIST_INSERT_HEAD(head, elm, field) {				\
+	if (((elm)->field.le_next = (head)->lh_first) != NULL)		\
+		(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
+	(head)->lh_first = (elm);					\
+	(elm)->field.le_prev = &(head)->lh_first;			\
+}
+
+#define LIST_REMOVE(elm, field) {					\
+	if ((elm)->field.le_next != NULL)				\
+		(elm)->field.le_next->field.le_prev = 			\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = (elm)->field.le_next;			\
+}
+
+/*
+ * Tail queue definitions.
+ */
+#define TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_INIT(head) {						\
+	(head)->tqh_first = NULL;					\
+	(head)->tqh_last = &(head)->tqh_first;				\
+}
+
+#define TAILQ_INSERT_HEAD(head, elm, field) {				\
+	if (((elm)->field.tqe_next = (head)->tqh_first) != NULL)	\
+		(elm)->field.tqe_next->field.tqe_prev =			\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(head)->tqh_first = (elm);					\
+	(elm)->field.tqe_prev = &(head)->tqh_first;			\
+}
+
+#define TAILQ_INSERT_TAIL(head, elm, field) {				\
+	(elm)->field.tqe_next = NULL;					\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &(elm)->field.tqe_next;			\
+}
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) {			\
+	if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    &(elm)->field.tqe_next;				\
+	else								\
+		(head)->tqh_last = &(elm)->field.tqe_next;		\
+	(listelm)->field.tqe_next = (elm);				\
+	(elm)->field.tqe_prev = &(listelm)->field.tqe_next;		\
+}
+
+#define TAILQ_REMOVE(head, elm, field) {				\
+	if (((elm)->field.tqe_next) != NULL)				\
+		(elm)->field.tqe_next->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = (elm)->field.tqe_next;			\
+}
+
+/*
+ * Circular queue definitions.
+ */
+#define CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_INIT(head) {						\
+	(head)->cqh_first = (void *)(head);				\
+	(head)->cqh_last = (void *)(head);				\
+}
+
+#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) {		\
+	(elm)->field.cqe_next = (listelm)->field.cqe_next;		\
+	(elm)->field.cqe_prev = (listelm);				\
+	if ((listelm)->field.cqe_next == (void *)(head))		\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(listelm)->field.cqe_next->field.cqe_prev = (elm);	\
+	(listelm)->field.cqe_next = (elm);				\
+}
+
+#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) {		\
+	(elm)->field.cqe_next = (listelm);				\
+	(elm)->field.cqe_prev = (listelm)->field.cqe_prev;		\
+	if ((listelm)->field.cqe_prev == (void *)(head))		\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(listelm)->field.cqe_prev->field.cqe_next = (elm);	\
+	(listelm)->field.cqe_prev = (elm);				\
+}
+
+#define CIRCLEQ_INSERT_HEAD(head, elm, field) {				\
+	(elm)->field.cqe_next = (head)->cqh_first;			\
+	(elm)->field.cqe_prev = (void *)(head);				\
+	if ((head)->cqh_last == (void *)(head))				\
+		(head)->cqh_last = (elm);				\
+	else								\
+		(head)->cqh_first->field.cqe_prev = (elm);		\
+	(head)->cqh_first = (elm);					\
+}
+
+#define CIRCLEQ_INSERT_TAIL(head, elm, field) {				\
+	(elm)->field.cqe_next = (void *)(head);				\
+	(elm)->field.cqe_prev = (head)->cqh_last;			\
+	if ((head)->cqh_first == (void *)(head))			\
+		(head)->cqh_first = (elm);				\
+	else								\
+		(head)->cqh_last->field.cqe_next = (elm);		\
+	(head)->cqh_last = (elm);					\
+}
+
+#define	CIRCLEQ_REMOVE(head, elm, field) {				\
+	if ((elm)->field.cqe_next == (void *)(head))			\
+		(head)->cqh_last = (elm)->field.cqe_prev;		\
+	else								\
+		(elm)->field.cqe_next->field.cqe_prev =			\
+		    (elm)->field.cqe_prev;				\
+	if ((elm)->field.cqe_prev == (void *)(head))			\
+		(head)->cqh_first = (elm)->field.cqe_next;		\
+	else								\
+		(elm)->field.cqe_prev->field.cqe_next =			\
+		    (elm)->field.cqe_next;				\
+}
+#endif	/* sys/queue.h */
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
new file mode 100644
index 0000000..8a500b9
--- /dev/null
+++ b/audio/wavaudio.c
@@ -0,0 +1,482 @@
+/*
+ * QEMU WAV audio driver
+ *
+ * Copyright (c) 2007 The Android Open Source Project
+ * Copyright (c) 2004-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define AUDIO_CAP "wav"
+#include "qemu-timer.h"
+#include "audio_int.h"
+#include "qemu_file.h"
+
+#define  WAV_AUDIO_IN  1
+
+/** VOICE OUT  (Saving to a .WAV file)
+ **/
+typedef struct WAVVoiceOut {
+    HWVoiceOut hw;
+    QEMUFile *f;
+    int64_t old_ticks;
+    void *pcm_buf;
+    int total_samples;
+} WAVVoiceOut;
+
+static struct {
+    audsettings_t settings;
+    const char *wav_path;
+} conf_out = {
+    {
+        44100,
+        2,
+        AUD_FMT_S16,
+        0
+    },
+    "qemu.wav"
+};
+
+static int wav_out_run (HWVoiceOut *hw)
+{
+    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+    int rpos, live, decr, samples;
+    uint8_t *dst;
+    st_sample_t *src;
+    int64_t now = qemu_get_clock (vm_clock);
+    int64_t ticks = now - wav->old_ticks;
+    int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+
+    if (bytes > INT_MAX) {
+        samples = INT_MAX >> hw->info.shift;
+    }
+    else {
+        samples = bytes >> hw->info.shift;
+    }
+
+    live = audio_pcm_hw_get_live_out (hw);
+    if (!live) {
+        return 0;
+    }
+
+    wav->old_ticks = now;
+    decr = audio_MIN (live, samples);
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+        src = hw->mix_buf + rpos;
+        dst = advance (wav->pcm_buf, rpos << hw->info.shift);
+
+        hw->clip (dst, src, convert_samples);
+        qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
+
+        rpos = (rpos + convert_samples) % hw->samples;
+        samples -= convert_samples;
+        wav->total_samples += convert_samples;
+    }
+
+    hw->rpos = rpos;
+    return decr;
+}
+
+static int wav_out_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+    int i;
+    for (i = 0; i < len; i++) {
+        buf[i] = (uint8_t) (val & 0xff);
+        val >>= 8;
+    }
+}
+
+static int wav_out_init (HWVoiceOut *hw, audsettings_t *as)
+{
+    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+    int bits16 = 0, stereo = 0;
+    uint8_t hdr[] = {
+        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+    };
+    audsettings_t wav_as = conf_out.settings;
+
+    (void) as;
+
+    stereo = wav_as.nchannels == 2;
+    switch (wav_as.fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        bits16 = 0;
+        break;
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+        bits16 = 1;
+        break;
+
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        dolog ("WAVE files can not handle 32bit formats\n");
+        return -1;
+    }
+
+    hdr[34] = bits16 ? 0x10 : 0x08;
+
+    wav_as.endianness = 0;
+    audio_pcm_init_info (&hw->info, &wav_as);
+
+    hw->samples = 1024;
+    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!wav->pcm_buf) {
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        return -1;
+    }
+
+    le_store (hdr + 22, hw->info.nchannels, 2);
+    le_store (hdr + 24, hw->info.freq, 4);
+    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
+    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
+
+    wav->f = qemu_fopen (conf_out.wav_path, "wb");
+    if (!wav->f) {
+        dolog ("Failed to open wave file `%s'\nReason: %s\n",
+               conf_out.wav_path, strerror (errno));
+        qemu_free (wav->pcm_buf);
+        wav->pcm_buf = NULL;
+        return -1;
+    }
+
+    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+    return 0;
+}
+
+static void wav_out_fini (HWVoiceOut *hw)
+{
+    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
+    uint8_t rlen[4];
+    uint8_t dlen[4];
+    uint32_t datalen = wav->total_samples << hw->info.shift;
+    uint32_t rifflen = datalen + 36;
+
+    if (!wav->f) {
+        return;
+    }
+
+    le_store (rlen, rifflen, 4);
+    le_store (dlen, datalen, 4);
+
+    qemu_fseek (wav->f, 4, SEEK_SET);
+    qemu_put_buffer (wav->f, rlen, 4);
+
+    qemu_fseek (wav->f, 32, SEEK_CUR);
+    qemu_put_buffer (wav->f, dlen, 4);
+
+    qemu_fclose (wav->f);
+    wav->f = NULL;
+
+    qemu_free (wav->pcm_buf);
+    wav->pcm_buf = NULL;
+}
+
+static int wav_out_ctl (HWVoiceOut *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+
+#if WAV_AUDIO_IN
+
+/** WAV IN (Reading from a .WAV file)
+ **/
+
+ static struct {
+    const char *wav_path;
+} conf_in = {
+    "qemu.wav"
+};
+
+typedef struct WAVVoiceIn {
+    HWVoiceIn  hw;
+    QEMUFile*  f;
+    int64_t    old_ticks;
+    void*      pcm_buf;
+    int        total_samples;
+    int        total_size;
+} WAVVoiceIn;
+
+
+static int
+le_read( const uint8_t*  p, int  size ) {
+    int  shift  = 0;
+    int  result = 0;
+    for ( ; size > 0; size-- ) {
+        result = result | (p[0] << shift);
+        p     += 1;
+        shift += 8;
+    }
+    return  result;
+}
+
+static int
+wav_in_init (HWVoiceIn *hw, audsettings_t *as)
+{
+    WAVVoiceIn*  wav = (WAVVoiceIn *) hw;
+    const char*  path = conf_in.wav_path;
+    uint8_t      hdr[44];
+    audsettings_t wav_as = *as;
+    int           nchannels, freq, format, bits;
+
+    wav->f = qemu_fopen (path, "rb");
+    if (wav->f == NULL) {
+        dolog("Failed to open wave file '%s'\nReason: %s\n", path,
+              strerror(errno));
+        return -1;
+    }
+
+    if (qemu_get_buffer (wav->f, hdr, sizeof(hdr)) != (int)sizeof(hdr)) {
+        dolog("File '%s' to be a .wav file\n", path);
+        goto Fail;
+    }
+
+    /* check that this is a wave file */
+    if ( hdr[0] != 'R' || hdr[1] != 'I' || hdr[2] != 'F' || hdr[3] != 'F' ||
+         hdr[8] != 'W' || hdr[9] != 'A' || hdr[10]!= 'V' || hdr[11]!= 'E' ||
+         hdr[12]!= 'f' || hdr[13]!= 'm' || hdr[14]!= 't' || hdr[15]!= ' ' ||
+         hdr[40]!= 'd' || hdr[41]!= 'a' || hdr[42]!= 't' || hdr[43]!= 'a') {
+         dolog("File '%s' is not a valid .wav file\n", path);
+         goto Fail;
+    }
+
+    nchannels   = le_read( hdr+22, 2 );
+    freq        = le_read( hdr+24, 4 );
+    format      = le_read( hdr+32, 2 );
+    bits        = le_read( hdr+34, 2 );
+
+    wav->total_size = le_read( hdr+40, 4 );
+
+    /* perform some sainty checks */
+    switch (nchannels) {
+        case 1:
+        case 2: break;
+        default:
+            dolog("unsupported number of channels (%d) in '%s'\n",
+                  nchannels, path);
+            goto Fail;
+    }
+
+    switch (format) {
+        case 1:
+        case 2:
+        case 4: break;
+        default:
+            dolog("unsupported bytes per sample (%d) in '%s'\n",
+                  format, path);
+            goto Fail;
+    }
+
+    if (format*8/nchannels != bits) {
+        dolog("invalid bits per sample (%d, expected %d) in '%s'\n",
+              bits, format*8/nchannels, path);
+        goto Fail;
+    }
+
+    wav_as.nchannels  = nchannels;
+    wav_as.fmt        = (bits == 8) ? AUD_FMT_U8 : AUD_FMT_S16;
+    wav_as.freq       = freq;
+    wav_as.endianness = 0;  /* always little endian */
+
+    audio_pcm_init_info (&hw->info, &wav_as);
+
+    hw->samples  = 1024;
+    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!wav->pcm_buf) {
+        goto Fail;
+    }
+    return 0;
+
+Fail:
+    qemu_fclose (wav->f);
+    wav->f = NULL;
+    return -1;
+}
+
+
+static void wav_in_fini (HWVoiceIn *hw)
+{
+    WAVVoiceIn *wav = (WAVVoiceIn *) hw;
+
+    if (!wav->f) {
+        return;
+    }
+
+    qemu_fclose (wav->f);
+    wav->f = NULL;
+
+    qemu_free (wav->pcm_buf);
+    wav->pcm_buf = NULL;
+}
+
+static int wav_in_run (HWVoiceIn *hw)
+{
+    WAVVoiceIn*   wav = (WAVVoiceIn *) hw;
+    int           wpos, live, decr, samples;
+    uint8_t*      src;
+    st_sample_t*  dst;
+
+    int64_t  now   = qemu_get_clock (vm_clock);
+    int64_t  ticks = now - wav->old_ticks;
+    int64_t  bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+
+    if (bytes > INT_MAX) {
+        samples = INT_MAX >> hw->info.shift;
+    }
+    else {
+        samples = bytes >> hw->info.shift;
+    }
+
+    live = audio_pcm_hw_get_live_in (hw);
+    if (!live) {
+        return 0;
+    }
+
+    wav->old_ticks = now;
+
+    decr    = audio_MIN (live, samples);
+    samples = decr;
+    wpos    = hw->wpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - wpos;
+        int convert_samples       = audio_MIN (samples, left_till_end_samples);
+
+        dst = hw->conv_buf + wpos;
+        src = advance (wav->pcm_buf, wpos << hw->info.shift);
+
+        qemu_get_buffer (wav->f, src, convert_samples << hw->info.shift);
+        memcpy (dst, src, convert_samples << hw->info.shift);
+
+        wpos                = (wpos + convert_samples) % hw->samples;
+        samples            -= convert_samples;
+        wav->total_samples += convert_samples;
+    }
+
+    hw->wpos = wpos;
+    return decr;
+}
+
+static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
+{
+    return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int wav_in_ctl (HWVoiceIn *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+#endif  /* WAV_AUDIO_IN */
+
+/** COMMON CODE
+ **/
+static void *wav_audio_init (void)
+{
+    return &conf_out;
+}
+
+static void wav_audio_fini (void *opaque)
+{
+    (void) opaque;
+    ldebug ("wav_fini");
+}
+
+struct audio_option wav_options[] = {
+    {"FREQUENCY", AUD_OPT_INT, &conf_out.settings.freq,
+     "Frequency", NULL, 0},
+
+    {"FORMAT", AUD_OPT_FMT, &conf_out.settings.fmt,
+     "Format", NULL, 0},
+
+    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf_out.settings.nchannels,
+     "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
+
+    {"PATH", AUD_OPT_STR, &conf_out.wav_path,
+     "Path to output .wav file", NULL, 0},
+
+#if WAV_AUDIO_IN
+    {"IN_PATH", AUD_OPT_STR, &conf_in.wav_path,
+     "Path to input .wav file", NULL, 0},
+#endif
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+struct audio_pcm_ops wav_pcm_ops = {
+    wav_out_init,
+    wav_out_fini,
+    wav_out_run,
+    wav_out_write,
+    wav_out_ctl,
+
+#if WAV_AUDIO_IN
+    wav_in_init,
+    wav_in_fini,
+    wav_in_run,
+    wav_in_read,
+    wav_in_ctl
+#else
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+#endif
+};
+
+struct audio_driver wav_audio_driver = {
+    INIT_FIELD (name           = ) "wav",
+    INIT_FIELD (descr          = )
+    "WAV file read/write (www.wikipedia.org/wiki/WAV)",
+    INIT_FIELD (options        = ) wav_options,
+    INIT_FIELD (init           = ) wav_audio_init,
+    INIT_FIELD (fini           = ) wav_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &wav_pcm_ops,
+    INIT_FIELD (can_be_default = ) 0,
+#if WAV_AUDIO_IN
+    INIT_FIELD (max_voices_in  = ) 1,
+    INIT_FIELD (max_voices_out = ) 1,
+    INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (WAVVoiceIn)
+#else
+    INIT_FIELD (max_voices_out = ) 1,
+    INIT_FIELD (max_voices_in  = ) 0,
+    INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
+    INIT_FIELD (voice_size_in  = ) 0
+#endif
+};
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
new file mode 100644
index 0000000..d6f733e
--- /dev/null
+++ b/audio/wavcapture.c
@@ -0,0 +1,166 @@
+#include "audio/audio.h"
+#include "qemu_file.h"
+#include "console.h"
+
+typedef struct {
+    QEMUFile *f;
+    int bytes;
+    char *path;
+    int freq;
+    int bits;
+    int nchannels;
+    CaptureVoiceOut *cap;
+} WAVState;
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+    int i;
+    for (i = 0; i < len; i++) {
+        buf[i] = (uint8_t) (val & 0xff);
+        val >>= 8;
+    }
+}
+
+static void wav_notify (void *opaque, audcnotification_e cmd)
+{
+    (void) opaque;
+    (void) cmd;
+}
+
+static void wav_destroy (void *opaque)
+{
+    WAVState *wav = opaque;
+    uint8_t rlen[4];
+    uint8_t dlen[4];
+    uint32_t datalen = wav->bytes;
+    uint32_t rifflen = datalen + 36;
+
+    if (!wav->f) {
+        return;
+    }
+
+    le_store (rlen, rifflen, 4);
+    le_store (dlen, datalen, 4);
+
+    qemu_fseek (wav->f, 4, SEEK_SET);
+    qemu_put_buffer (wav->f, rlen, 4);
+
+    qemu_fseek (wav->f, 32, SEEK_CUR);
+    qemu_put_buffer (wav->f, dlen, 4);
+    qemu_fclose (wav->f);
+    if (wav->path) {
+        qemu_free (wav->path);
+    }
+}
+
+static void wav_capture (void *opaque, void *buf, int size)
+{
+    WAVState *wav = opaque;
+
+    qemu_put_buffer (wav->f, buf, size);
+    wav->bytes += size;
+}
+
+static void wav_capture_destroy (void *opaque)
+{
+    WAVState *wav = opaque;
+
+    AUD_del_capture (wav->cap, wav);
+}
+
+static void wav_capture_info (void *opaque)
+{
+    WAVState *wav = opaque;
+    char *path = wav->path;
+
+    term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n",
+                 wav->freq, wav->bits, wav->nchannels,
+                 path ? path : "<not available>", wav->bytes);
+}
+
+static struct capture_ops wav_capture_ops = {
+    .destroy = wav_capture_destroy,
+    .info = wav_capture_info
+};
+
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+                       int bits, int nchannels)
+{
+    WAVState *wav;
+    uint8_t hdr[] = {
+        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+    };
+    audsettings_t as;
+    struct audio_capture_ops ops;
+    int stereo, bits16, shift;
+    CaptureVoiceOut *cap;
+
+    if (bits != 8 && bits != 16) {
+        term_printf ("incorrect bit count %d, must be 8 or 16\n", bits);
+        return -1;
+    }
+
+    if (nchannels != 1 && nchannels != 2) {
+        term_printf ("incorrect channel count %d, must be 1 or 2\n", bits);
+        return -1;
+    }
+
+    stereo = nchannels == 2;
+    bits16 = bits == 16;
+
+    as.freq = freq;
+    as.nchannels = 1 << stereo;
+    as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+    as.endianness = 0;
+
+    ops.notify = wav_notify;
+    ops.capture = wav_capture;
+    ops.destroy = wav_destroy;
+
+    wav = qemu_mallocz (sizeof (*wav));
+    if (!wav) {
+        AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav));
+        return -1;
+    }
+
+    shift = bits16 + stereo;
+    hdr[34] = bits16 ? 0x10 : 0x08;
+
+    le_store (hdr + 22, as.nchannels, 2);
+    le_store (hdr + 24, freq, 4);
+    le_store (hdr + 28, freq << shift, 4);
+    le_store (hdr + 32, 1 << shift, 2);
+
+    wav->f = qemu_fopen (path, "wb");
+    if (!wav->f) {
+        term_printf ("Failed to open wave file `%s'\nReason: %s\n",
+                     path, strerror (errno));
+        qemu_free (wav);
+        return -1;
+    }
+
+    wav->path = qemu_strdup (path);
+    wav->bits = bits;
+    wav->nchannels = nchannels;
+    wav->freq = freq;
+
+    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+
+    cap = AUD_add_capture (NULL, &as, &ops, wav);
+    if (!cap) {
+        term_printf ("Failed to add audio capture\n");
+        qemu_free (wav->path);
+        qemu_fclose (wav->f);
+        qemu_free (wav);
+        return -1;
+    }
+
+    wav->cap = cap;
+    s->opaque = wav;
+    s->ops = wav_capture_ops;
+    return 0;
+}
diff --git a/audio/winaudio.c b/audio/winaudio.c
new file mode 100644
index 0000000..6e8daef
--- /dev/null
+++ b/audio/winaudio.c
@@ -0,0 +1,666 @@
+/*

+ * QEMU "simple" Windows audio driver

+ *

+ * Copyright (c) 2007 The Android Open Source Project

+ *

+ * Permission is hereby granted, free of charge, to any person obtaining a copy

+ * of this software and associated documentation files (the "Software"), to deal

+ * in the Software without restriction, including without limitation the rights

+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

+ * copies of the Software, and to permit persons to whom the Software is

+ * furnished to do so, subject to the following conditions:

+ *

+ * The above copyright notice and this permission notice shall be included in

+ * all copies or substantial portions of the Software.

+ *

+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL

+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

+ * THE SOFTWARE.

+ */

+#define WIN32_LEAN_AND_MEAN

+#include <windows.h>

+#include <mmsystem.h>

+

+#define AUDIO_CAP "winaudio"

+#include "audio_int.h"

+

+/* define DEBUG to 1 to dump audio debugging info at runtime to stderr */

+#define  DEBUG  0

+

+#if 1

+#  define  D_ACTIVE    1

+#else

+#  define  D_ACTIVE  DEBUG

+#endif

+

+#if DEBUG

+#  define  D(...)   do{ if (D_ACTIVE) printf(__VA_ARGS__); } while(0)

+#else

+#  define  D(...)   ((void)0)

+#endif

+

+static struct {

+    int nb_samples;

+} conf = {

+    1024

+};

+

+#if DEBUG

+int64_t  start_time;

+int64_t  last_time;

+#endif

+

+#define  NUM_OUT_BUFFERS  8  /* must be at least 2 */

+

+/** COMMON UTILITIES

+ **/

+

+#if DEBUG

+static void

+dump_mmerror( const char*  func, MMRESULT  error )

+{

+    const char*  reason = NULL;

+

+    fprintf(stderr, "%s returned error: ", func);

+    switch (error) {

+            case MMSYSERR_ALLOCATED:   reason="specified resource is already allocated"; break;

+            case MMSYSERR_BADDEVICEID: reason="bad device id"; break;

+            case MMSYSERR_NODRIVER:    reason="no driver is present"; break;

+            case MMSYSERR_NOMEM:       reason="unable to allocate or lock memory"; break;

+            case WAVERR_BADFORMAT:     reason="unsupported waveform-audio format"; break;

+            case WAVERR_SYNC:          reason="device is synchronous"; break;

+            default:

+                    fprintf(stderr, "unknown(%d)\n", error);

+    }

+    if (reason)

+            fprintf(stderr, "%s\n", reason);

+}

+#else

+#  define  dump_mmerror(func,error)  ((void)0)

+#endif

+

+

+/** AUDIO OUT

+ **/

+

+typedef struct WinAudioOut {

+    HWVoiceOut        hw;

+    HWAVEOUT          waveout;

+    int               silence;

+    CRITICAL_SECTION  lock;

+    unsigned char*    buffer_bytes;

+    WAVEHDR           buffers[ NUM_OUT_BUFFERS ];

+    int               write_index;   /* starting first writable buffer      */

+    int               write_count;   /* available writable buffers count    */

+    int               write_pos;     /* position in current writable buffer */

+    int               write_size;    /* size in bytes of each buffer        */

+} WinAudioOut;

+

+/* The Win32 callback that is called when a buffer has finished playing */

+static void CALLBACK

+winaudio_out_buffer_done (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,

+			              DWORD dwParam1, DWORD dwParam2)

+{

+    WinAudioOut*  s = (WinAudioOut*) dwInstance;

+

+    /* Only service "buffer done playing" messages */

+    if ( uMsg != WOM_DONE )

+            return;

+

+    /* Signal that we are done playing a buffer */

+    EnterCriticalSection( &s->lock );

+    if (s->write_count < NUM_OUT_BUFFERS)

+        s->write_count += 1;

+    LeaveCriticalSection( &s->lock );

+}

+

+static int

+winaudio_out_write (SWVoiceOut *sw, void *buf, int len)

+{

+    return audio_pcm_sw_write (sw, buf, len);

+}

+

+static void

+winaudio_out_fini (HWVoiceOut *hw)

+{

+    WinAudioOut*  s = (WinAudioOut*) hw;

+    int           i;

+

+    if (s->waveout) {

+        waveOutReset(s->waveout);

+        s->waveout = 0;

+    }

+

+    for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {

+        if ( s->buffers[i].dwUser != 0xFFFF ) {

+            waveOutUnprepareHeader(

+                s->waveout, &s->buffers[i], sizeof(s->buffers[i]) );

+                s->buffers[i].dwUser = 0xFFFF;

+        }

+    }

+

+    if (s->buffer_bytes != NULL) {

+        qemu_free(s->buffer_bytes);

+        s->buffer_bytes = NULL;

+    }

+

+    if (s->waveout) {

+        waveOutClose(s->waveout);

+        s->waveout = NULL;

+    }

+}

+

+

+static int

+winaudio_out_init (HWVoiceOut *hw, audsettings_t *as)

+{

+    WinAudioOut*   s = (WinAudioOut*) hw;

+    MMRESULT       result;

+    WAVEFORMATEX   format;

+    int            shift, i, samples_size;

+

+    s->waveout = NULL;

+    InitializeCriticalSection( &s->lock );

+    for (i = 0; i < NUM_OUT_BUFFERS; i++) {

+            s->buffers[i].dwUser = 0xFFFF;

+    }

+    s->buffer_bytes = NULL;

+

+    /* compute desired wave output format */

+    format.wFormatTag      = WAVE_FORMAT_PCM;

+    format.nChannels       = as->nchannels;

+    format.nSamplesPerSec  = as->freq;

+    format.nAvgBytesPerSec = as->freq*as->nchannels;

+

+    s->silence = 0;

+

+    switch (as->fmt) {

+        case AUD_FMT_S8:   shift = 0; break;

+        case AUD_FMT_U8:   shift = 0; s->silence = 0x80; break;

+        case AUD_FMT_S16:  shift = 1; break;

+        case AUD_FMT_U16:  shift = 1; s->silence = 0x8000; break;

+        default:

+            fprintf(stderr, "qemu: winaudio: Bad output audio format: %d\n",

+                    as->fmt);

+                return -1;

+    }

+

+    format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels) << shift;

+    format.nBlockAlign     = format.nChannels << shift;

+    format.wBitsPerSample  = 8 << shift;

+    format.cbSize          = 0;

+

+    /* open the wave out device */

+    result = waveOutOpen( &s->waveout, WAVE_MAPPER, &format,

+                                  (DWORD_PTR)winaudio_out_buffer_done, (DWORD_PTR) hw,

+                                              CALLBACK_FUNCTION);

+    if ( result != MMSYSERR_NOERROR ) {

+        dump_mmerror( "qemu: winaudio: waveOutOpen()", result);

+            return -1;

+    }

+

+    samples_size    = format.nBlockAlign * conf.nb_samples;

+    s->buffer_bytes = qemu_malloc( NUM_OUT_BUFFERS * samples_size );

+    if (s->buffer_bytes == NULL) {

+            waveOutClose( s->waveout );

+            s->waveout = NULL;

+            fprintf(stderr, "not enough memory for Windows audio buffers\n");

+            return -1;

+    }

+

+    for (i = 0; i < NUM_OUT_BUFFERS; i++) {

+        memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );

+        s->buffers[i].lpData         = (LPSTR)(s->buffer_bytes + i*samples_size);

+        s->buffers[i].dwBufferLength = samples_size;

+        s->buffers[i].dwFlags        = WHDR_DONE;

+

+        result = waveOutPrepareHeader( s->waveout, &s->buffers[i],

+                               sizeof(s->buffers[i]) );

+        if ( result != MMSYSERR_NOERROR ) {

+            dump_mmerror("waveOutPrepareHeader()", result);

+            return -1;

+        }

+    }

+

+#if DEBUG

+    /* Check the sound device we retrieved */

+    {

+        WAVEOUTCAPS caps;

+

+        result = waveOutGetDevCaps((UINT) s->waveout, &caps, sizeof(caps));

+        if ( result != MMSYSERR_NOERROR ) {

+            dump_mmerror("waveOutGetDevCaps()", result);

+        } else

+            printf("Audio out device: %s\n", caps.szPname);

+    }

+#endif

+

+    audio_pcm_init_info (&hw->info, as);

+    hw->samples = conf.nb_samples*2;

+

+    s->write_index = 0;

+    s->write_count = NUM_OUT_BUFFERS;

+    s->write_pos   = 0;

+    s->write_size  = samples_size;

+    return 0;

+}

+

+

+static int

+winaudio_out_run (HWVoiceOut *hw)

+{

+    WinAudioOut*  s      = (WinAudioOut*) hw;

+    int           played = 0;

+    int           has_buffer;

+    int           live = audio_pcm_hw_get_live_out (hw);

+

+    if (!live) {

+        return 0;

+    }

+

+    EnterCriticalSection( &s->lock );

+    has_buffer = (s->write_count > 0);

+    LeaveCriticalSection( &s->lock );

+

+    if (has_buffer) {

+        while (live > 0) {

+            WAVEHDR*      wav_buffer  = s->buffers + s->write_index;

+            int           wav_bytes   = (s->write_size - s->write_pos);

+            int           wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);

+            int           hw_samples  = audio_MIN(hw->samples - hw->rpos, live);

+            st_sample_t*  src         = hw->mix_buf + hw->rpos;

+            uint8_t*      dst         = (uint8_t*)wav_buffer->lpData + s->write_pos;

+

+            if (wav_samples > hw_samples) {

+                    wav_samples = hw_samples;

+            }

+

+            wav_bytes = wav_samples << hw->info.shift;

+

+            //D("run_out: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d rpos:%d hwsamples:%d\n", s->write_index,

+            //   s->write_pos, s->write_size, wav_samples, wav_bytes, live, hw->rpos, hw->samples);

+            hw->clip (dst, src, wav_samples);

+            hw->rpos += wav_samples;

+            if (hw->rpos >= hw->samples)

+                    hw->rpos -= hw->samples;

+

+            live         -= wav_samples;

+            played       += wav_samples;

+            s->write_pos += wav_bytes;

+            if (s->write_pos == s->write_size) {

+#if xxDEBUG

+                int64_t  now  = qemu_get_clock(vm_clock) - start_time;

+                int64_t  diff = now - last_time;

+

+                D("run_out: (%7.3f:%7d):waveOutWrite buffer:%d\n",

+                   now/1e9, (now-last_time)/1e9, s->write_index);

+                last_time = now;

+#endif

+                waveOutWrite( s->waveout, wav_buffer, sizeof(*wav_buffer) );

+                s->write_pos    = 0;

+                s->write_index += 1;

+                if (s->write_index == NUM_OUT_BUFFERS)

+                    s->write_index = 0;

+

+                EnterCriticalSection( &s->lock );

+                if (--s->write_count == 0) {

+                        live = 0;

+                }

+                LeaveCriticalSection( &s->lock );

+            }

+        }

+

+    }

+    return played;

+}

+

+static int

+winaudio_out_ctl (HWVoiceOut *hw, int cmd, ...)

+{

+    WinAudioOut*  s = (WinAudioOut*) hw;

+

+    switch (cmd) {

+    case VOICE_ENABLE:

+        waveOutRestart( s->waveout );

+        break;

+

+    case VOICE_DISABLE:

+        waveOutPause( s->waveout );

+        break;

+    }

+    return 0;

+}

+

+/** AUDIO IN

+ **/

+

+#define  NUM_IN_BUFFERS  2

+

+typedef struct WinAudioIn {

+    HWVoiceIn         hw;

+    HWAVEIN           wavein;

+    CRITICAL_SECTION  lock;

+    unsigned char*    buffer_bytes;

+    WAVEHDR           buffers[ NUM_IN_BUFFERS ];

+    int               read_index;

+    int               read_count;

+    int               read_pos;

+    int               read_size;

+} WinAudioIn;

+

+/* The Win32 callback that is called when a buffer has finished playing */

+static void CALLBACK

+winaudio_in_buffer_done (HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,

+                         DWORD dwParam1, DWORD dwParam2)

+{

+    WinAudioIn*  s = (WinAudioIn*) dwInstance;

+

+    /* Only service "buffer done playing" messages */

+    if ( uMsg != WIM_DATA )

+        return;

+

+    /* Signal that we are done playing a buffer */

+    EnterCriticalSection( &s->lock );

+    if (s->read_count < NUM_IN_BUFFERS)

+        s->read_count += 1;

+        //D(".%c",s->read_count + '0'); fflush(stdout);

+    LeaveCriticalSection( &s->lock );

+}

+

+static void

+winaudio_in_fini (HWVoiceIn *hw)

+{

+    WinAudioIn*  s = (WinAudioIn*) hw;

+    int           i;

+

+    if (s->wavein) {

+        waveInReset(s->wavein);

+            s->wavein = 0;

+    }

+

+    for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {

+        if ( s->buffers[i].dwUser != 0xFFFF ) {

+            waveInUnprepareHeader(

+                s->wavein, &s->buffers[i], sizeof(s->buffers[i]) );

+                s->buffers[i].dwUser = 0xFFFF;

+        }

+    }

+

+    if (s->buffer_bytes != NULL) {

+        qemu_free(s->buffer_bytes);

+        s->buffer_bytes = NULL;

+    }

+

+    if (s->wavein) {

+        waveInClose(s->wavein);

+        s->wavein = NULL;

+    }

+}

+

+

+static int

+winaudio_in_init (HWVoiceIn *hw, audsettings_t *as)

+{

+    WinAudioIn*   s = (WinAudioIn*) hw;

+    MMRESULT       result;

+    WAVEFORMATEX   format;

+    int            shift, i, samples_size;

+

+    s->wavein = NULL;

+    InitializeCriticalSection( &s->lock );

+    for (i = 0; i < NUM_OUT_BUFFERS; i++) {

+            s->buffers[i].dwUser = 0xFFFF;

+    }

+    s->buffer_bytes = NULL;

+

+    /* compute desired wave input format */

+    format.wFormatTag      = WAVE_FORMAT_PCM;

+    format.nChannels       = as->nchannels;

+    format.nSamplesPerSec  = as->freq;

+    format.nAvgBytesPerSec = as->freq*as->nchannels;

+

+    switch (as->fmt) {

+        case AUD_FMT_S8:   shift = 0; break;

+        case AUD_FMT_U8:   shift = 0; break;

+        case AUD_FMT_S16:  shift = 1; break;

+        case AUD_FMT_U16:  shift = 1; break;

+        default:

+            fprintf(stderr, "qemu: winaudio: Bad input audio format: %d\n",

+                    as->fmt);

+                return -1;

+    }

+

+    format.nAvgBytesPerSec = (format.nSamplesPerSec * format.nChannels) << shift;

+    format.nBlockAlign     = format.nChannels << shift;

+    format.wBitsPerSample  = 8 << shift;

+    format.cbSize          = 0;

+

+    /* open the wave in device */

+    result = waveInOpen( &s->wavein, WAVE_MAPPER, &format,

+                         (DWORD_PTR)winaudio_in_buffer_done, (DWORD_PTR) hw,

+                         CALLBACK_FUNCTION);

+    if ( result != MMSYSERR_NOERROR ) {

+        dump_mmerror( "qemu: winaudio: waveInOpen()", result);

+            return -1;

+    }

+

+    samples_size    = format.nBlockAlign * conf.nb_samples;

+    s->buffer_bytes = qemu_malloc( NUM_IN_BUFFERS * samples_size );

+    if (s->buffer_bytes == NULL) {

+            waveInClose( s->wavein );

+            s->wavein = NULL;

+            fprintf(stderr, "not enough memory for Windows audio buffers\n");

+            return -1;

+    }

+

+    for (i = 0; i < NUM_IN_BUFFERS; i++) {

+        memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );

+        s->buffers[i].lpData         = (LPSTR)(s->buffer_bytes + i*samples_size);

+        s->buffers[i].dwBufferLength = samples_size;

+        s->buffers[i].dwFlags        = WHDR_DONE;

+

+        result = waveInPrepareHeader( s->wavein, &s->buffers[i],

+                               sizeof(s->buffers[i]) );

+        if ( result != MMSYSERR_NOERROR ) {

+                dump_mmerror("waveInPrepareHeader()", result);

+                return -1;

+        }

+

+        result = waveInAddBuffer( s->wavein, &s->buffers[i],

+                              sizeof(s->buffers[i]) );

+        if ( result != MMSYSERR_NOERROR ) {

+            dump_mmerror("waveInAddBuffer()", result);

+            return -1;

+        }

+    }

+

+#if DEBUG

+    /* Check the sound device we retrieved */

+    {

+        WAVEINCAPS caps;

+

+        result = waveInGetDevCaps((UINT) s->wavein, &caps, sizeof(caps));

+        if ( result != MMSYSERR_NOERROR ) {

+            dump_mmerror("waveInGetDevCaps()", result);

+        } else

+            printf("Audio in device: %s\n", caps.szPname);

+    }

+#endif

+

+    audio_pcm_init_info (&hw->info, as);

+    hw->samples = conf.nb_samples*2;

+

+    s->read_index = 0;

+    s->read_count = 0;

+    s->read_pos   = 0;

+    s->read_size  = samples_size;

+    return 0;

+}

+

+

+/* report the number of captured samples to the audio subsystem */

+static int

+winaudio_in_run (HWVoiceIn *hw)

+{

+    WinAudioIn*  s        = (WinAudioIn*) hw;

+    int          captured = 0;

+    int          has_buffer;

+    int          live = hw->samples - hw->total_samples_captured;

+

+    if (!live) {

+#if 0

+        static int  counter;

+        if (++counter == 100) {

+            D("0"); fflush(stdout);

+            counter = 0;

+        }

+#endif

+            return 0;

+    }

+

+    EnterCriticalSection( &s->lock );

+    has_buffer = (s->read_count > 0);

+    LeaveCriticalSection( &s->lock );

+

+    if (has_buffer > 0) {

+        while (live > 0) {

+            WAVEHDR*      wav_buffer  = s->buffers + s->read_index;

+            int           wav_bytes   = (s->read_size - s->read_pos);

+            int           wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);

+            int           hw_samples  = audio_MIN(hw->samples - hw->wpos, live);

+            st_sample_t*  dst         = hw->conv_buf + hw->wpos;

+            uint8_t*      src         = (uint8_t*)wav_buffer->lpData + s->read_pos;

+

+            if (wav_samples > hw_samples) {

+                wav_samples = hw_samples;

+            }

+

+            wav_bytes = wav_samples << hw->info.shift;

+

+            D("%s: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d wpos:%d hwsamples:%d\n",

+               __FUNCTION__, s->read_index, s->read_pos, s->read_size, wav_samples, wav_bytes, live,

+               hw->wpos, hw->samples);

+

+            hw->conv(dst, src, wav_samples, &nominal_volume);

+

+            hw->wpos += wav_samples;

+            if (hw->wpos >= hw->samples)

+                hw->wpos -= hw->samples;

+

+            live        -= wav_samples;

+            captured    += wav_samples;

+            s->read_pos += wav_bytes;

+            if (s->read_pos == s->read_size) {

+                s->read_pos    = 0;

+                s->read_index += 1;

+                if (s->read_index == NUM_IN_BUFFERS)

+                    s->read_index = 0;

+

+                waveInAddBuffer( s->wavein, wav_buffer, sizeof(*wav_buffer) );

+

+                EnterCriticalSection( &s->lock );

+                if (--s->read_count == 0) {

+                    live = 0;

+                }

+                LeaveCriticalSection( &s->lock );

+            }

+        }

+    }

+    return  captured;

+}

+

+

+static int

+winaudio_in_read (SWVoiceIn *sw, void *buf, int len)

+{

+    int  ret = audio_pcm_sw_read (sw, buf, len);

+    if (ret > 0)

+        D("%s: (%d) returned %d\n", __FUNCTION__, len, ret);

+    return ret;

+}

+

+

+static int

+winaudio_in_ctl (HWVoiceIn *hw, int cmd, ...)

+{

+	WinAudioIn*  s = (WinAudioIn*) hw;

+

+    switch (cmd) {

+    case VOICE_ENABLE:

+        D("%s: enable audio in\n", __FUNCTION__);

+        waveInStart( s->wavein );

+        break;

+

+    case VOICE_DISABLE:

+        D("%s: disable audio in\n", __FUNCTION__);

+        waveInStop( s->wavein );

+        break;

+    }

+    return 0;

+}

+

+/** AUDIO STATE

+ **/

+

+typedef struct WinAudioState {

+    int  dummy;

+} WinAudioState;

+

+static WinAudioState  g_winaudio;

+

+static void*

+winaudio_init(void)

+{

+    WinAudioState*  s = &g_winaudio;

+

+#if DEBUG

+    start_time = qemu_get_clock(vm_clock);

+    last_time  = 0;

+#endif

+

+    return s;

+}

+

+

+static void

+winaudio_fini (void *opaque)

+{

+}

+

+static struct audio_option winaudio_options[] = {

+    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,

+     "Size of Windows audio buffer in samples", NULL, 0},

+    {NULL, 0, NULL, NULL, NULL, 0}

+};

+

+static struct audio_pcm_ops winaudio_pcm_ops = {

+    winaudio_out_init,

+    winaudio_out_fini,

+    winaudio_out_run,

+    winaudio_out_write,

+    winaudio_out_ctl,

+

+    winaudio_in_init,

+    winaudio_in_fini,

+    winaudio_in_run,

+    winaudio_in_read,

+    winaudio_in_ctl

+};

+

+struct audio_driver win_audio_driver = {

+    INIT_FIELD (name           = ) "winaudio",

+    INIT_FIELD (descr          = ) "Windows wave audio",

+    INIT_FIELD (options        = ) winaudio_options,

+    INIT_FIELD (init           = ) winaudio_init,

+    INIT_FIELD (fini           = ) winaudio_fini,

+    INIT_FIELD (pcm_ops        = ) &winaudio_pcm_ops,

+    INIT_FIELD (can_be_default = ) 1,

+    INIT_FIELD (max_voices_out = ) 1,

+    INIT_FIELD (max_voices_in  = ) 1,

+    INIT_FIELD (voice_size_out = ) sizeof (WinAudioOut),

+    INIT_FIELD (voice_size_in  = ) sizeof (WinAudioIn)

+};

diff --git a/block-bochs.c b/block-bochs.c
new file mode 100644
index 0000000..b167e0b
--- /dev/null
+++ b/block-bochs.c
@@ -0,0 +1,254 @@
+/*
+ * Block driver for the various disk image formats used by Bochs
+ * Currently only for "growing" type in read-only mode
+ *
+ * Copyright (c) 2005 Alex Beregszaszi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+
+/**************************************************************/
+
+#define HEADER_MAGIC "Bochs Virtual HD Image"
+#define HEADER_VERSION 0x00020000
+#define HEADER_V1 0x00010000
+#define HEADER_SIZE 512
+
+#define REDOLOG_TYPE "Redolog"
+#define GROWING_TYPE "Growing"
+
+// not allocated: 0xffffffff
+
+// always little-endian
+struct bochs_header_v1 {
+    char magic[32]; // "Bochs Virtual HD Image"
+    char type[16]; // "Redolog"
+    char subtype[16]; // "Undoable" / "Volatile" / "Growing"
+    uint32_t version;
+    uint32_t header; // size of header
+
+    union {
+	struct {
+	    uint32_t catalog; // num of entries
+	    uint32_t bitmap; // bitmap size
+	    uint32_t extent; // extent size
+	    uint64_t disk; // disk size
+	    char padding[HEADER_SIZE - 64 - 8 - 20];
+	} redolog;
+	char padding[HEADER_SIZE - 64 - 8];
+    } extra;
+};
+
+// always little-endian
+struct bochs_header {
+    char magic[32]; // "Bochs Virtual HD Image"
+    char type[16]; // "Redolog"
+    char subtype[16]; // "Undoable" / "Volatile" / "Growing"
+    uint32_t version;
+    uint32_t header; // size of header
+
+    union {
+	struct {
+	    uint32_t catalog; // num of entries
+	    uint32_t bitmap; // bitmap size
+	    uint32_t extent; // extent size
+	    uint32_t reserved; // for ???
+	    uint64_t disk; // disk size
+	    char padding[HEADER_SIZE - 64 - 8 - 24];
+	} redolog;
+	char padding[HEADER_SIZE - 64 - 8];
+    } extra;
+};
+
+typedef struct BDRVBochsState {
+    int fd;
+
+    uint32_t *catalog_bitmap;
+    int catalog_size;
+
+    int data_offset;
+
+    int bitmap_blocks;
+    int extent_blocks;
+    int extent_size;
+} BDRVBochsState;
+
+static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    const struct bochs_header *bochs = (const void *)buf;
+
+    if (buf_size < HEADER_SIZE)
+	return 0;
+
+    if (!strcmp(bochs->magic, HEADER_MAGIC) &&
+	!strcmp(bochs->type, REDOLOG_TYPE) &&
+	!strcmp(bochs->subtype, GROWING_TYPE) &&
+	((le32_to_cpu(bochs->version) == HEADER_VERSION) ||
+	(le32_to_cpu(bochs->version) == HEADER_V1)))
+	return 100;
+
+    return 0;
+}
+
+static int bochs_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVBochsState *s = bs->opaque;
+    int fd, i;
+    struct bochs_header bochs;
+    struct bochs_header_v1 header_v1;
+
+    fd = open(filename, O_RDWR | O_BINARY);
+    if (fd < 0) {
+        fd = open(filename, O_RDONLY | O_BINARY);
+        if (fd < 0)
+            return -1;
+    }
+
+    bs->read_only = 1; // no write support yet
+
+    s->fd = fd;
+
+    if (read(fd, &bochs, sizeof(bochs)) != sizeof(bochs)) {
+        goto fail;
+    }
+
+    if (strcmp(bochs.magic, HEADER_MAGIC) ||
+        strcmp(bochs.type, REDOLOG_TYPE) ||
+        strcmp(bochs.subtype, GROWING_TYPE) ||
+	((le32_to_cpu(bochs.version) != HEADER_VERSION) &&
+	(le32_to_cpu(bochs.version) != HEADER_V1))) {
+        goto fail;
+    }
+
+    if (le32_to_cpu(bochs.version) == HEADER_V1) {
+      memcpy(&header_v1, &bochs, sizeof(bochs));
+      bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
+    } else {
+      bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
+    }
+
+    lseek(s->fd, le32_to_cpu(bochs.header), SEEK_SET);
+
+    s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
+    s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
+    if (!s->catalog_bitmap)
+	goto fail;
+    if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
+	s->catalog_size * 4)
+	goto fail;
+    for (i = 0; i < s->catalog_size; i++)
+	le32_to_cpus(&s->catalog_bitmap[i]);
+
+    s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
+
+    s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
+    s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
+
+    s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
+
+    return 0;
+ fail:
+    close(fd);
+    return -1;
+}
+
+static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+    BDRVBochsState *s = bs->opaque;
+    int64_t offset = sector_num * 512;
+    int64_t extent_index, extent_offset, bitmap_offset, block_offset;
+    char bitmap_entry;
+
+    // seek to sector
+    extent_index = offset / s->extent_size;
+    extent_offset = (offset % s->extent_size) / 512;
+
+    if (s->catalog_bitmap[extent_index] == 0xffffffff)
+    {
+//	fprintf(stderr, "page not allocated [%x - %x:%x]\n",
+//	    sector_num, extent_index, extent_offset);
+	return -1; // not allocated
+    }
+
+    bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
+	(s->extent_blocks + s->bitmap_blocks));
+    block_offset = bitmap_offset + (512 * (s->bitmap_blocks + extent_offset));
+
+//    fprintf(stderr, "sect: %x [ext i: %x o: %x] -> %x bitmap: %x block: %x\n",
+//	sector_num, extent_index, extent_offset,
+//	le32_to_cpu(s->catalog_bitmap[extent_index]),
+//	bitmap_offset, block_offset);
+
+    // read in bitmap for current extent
+    lseek(s->fd, bitmap_offset + (extent_offset / 8), SEEK_SET);
+
+    read(s->fd, &bitmap_entry, 1);
+
+    if (!((bitmap_entry >> (extent_offset % 8)) & 1))
+    {
+//	fprintf(stderr, "sector (%x) in bitmap not allocated\n",
+//	    sector_num);
+	return -1; // not allocated
+    }
+
+    lseek(s->fd, block_offset, SEEK_SET);
+
+    return 0;
+}
+
+static int bochs_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVBochsState *s = bs->opaque;
+    int ret;
+
+    while (nb_sectors > 0) {
+	if (!seek_to_sector(bs, sector_num))
+	{
+	    ret = read(s->fd, buf, 512);
+	    if (ret != 512)
+		return -1;
+	}
+	else
+            memset(buf, 0, 512);
+        nb_sectors--;
+        sector_num++;
+        buf += 512;
+    }
+    return 0;
+}
+
+static void bochs_close(BlockDriverState *bs)
+{
+    BDRVBochsState *s = bs->opaque;
+    qemu_free(s->catalog_bitmap);
+    close(s->fd);
+}
+
+BlockDriver bdrv_bochs = {
+    "bochs",
+    sizeof(BDRVBochsState),
+    bochs_probe,
+    bochs_open,
+    bochs_read,
+    NULL,
+    bochs_close,
+};
diff --git a/block-cloop.c b/block-cloop.c
new file mode 100644
index 0000000..43d3801
--- /dev/null
+++ b/block-cloop.c
@@ -0,0 +1,169 @@
+/*
+ * QEMU Block driver for CLOOP images
+ *
+ * Copyright (c) 2004 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include <zlib.h>
+
+typedef struct BDRVCloopState {
+    int fd;
+    uint32_t block_size;
+    uint32_t n_blocks;
+    uint64_t* offsets;
+    uint32_t sectors_per_block;
+    uint32_t current_block;
+    uint8_t *compressed_block;
+    uint8_t *uncompressed_block;
+    z_stream zstream;
+} BDRVCloopState;
+
+static int cloop_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    const char* magic_version_2_0="#!/bin/sh\n"
+	"#V2.0 Format\n"
+	"modprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n";
+    int length=strlen(magic_version_2_0);
+    if(length>buf_size)
+	length=buf_size;
+    if(!memcmp(magic_version_2_0,buf,length))
+	return 2;
+    return 0;
+}
+
+static int cloop_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVCloopState *s = bs->opaque;
+    uint32_t offsets_size,max_compressed_block_size=1,i;
+
+    s->fd = open(filename, O_RDONLY | O_BINARY);
+    if (s->fd < 0)
+        return -errno;
+    bs->read_only = 1;
+
+    /* read header */
+    if(lseek(s->fd,128,SEEK_SET)<0) {
+cloop_close:
+	close(s->fd);
+	return -1;
+    }
+    if(read(s->fd,&s->block_size,4)<4)
+	goto cloop_close;
+    s->block_size=be32_to_cpu(s->block_size);
+    if(read(s->fd,&s->n_blocks,4)<4)
+	goto cloop_close;
+    s->n_blocks=be32_to_cpu(s->n_blocks);
+
+    /* read offsets */
+    offsets_size=s->n_blocks*sizeof(uint64_t);
+    if(!(s->offsets=(uint64_t*)malloc(offsets_size)))
+	goto cloop_close;
+    if(read(s->fd,s->offsets,offsets_size)<offsets_size)
+	goto cloop_close;
+    for(i=0;i<s->n_blocks;i++) {
+	s->offsets[i]=be64_to_cpu(s->offsets[i]);
+	if(i>0) {
+	    uint32_t size=s->offsets[i]-s->offsets[i-1];
+	    if(size>max_compressed_block_size)
+		max_compressed_block_size=size;
+	}
+    }
+
+    /* initialize zlib engine */
+    if(!(s->compressed_block = malloc(max_compressed_block_size+1)))
+	goto cloop_close;
+    if(!(s->uncompressed_block = malloc(s->block_size)))
+	goto cloop_close;
+    if(inflateInit(&s->zstream) != Z_OK)
+	goto cloop_close;
+    s->current_block=s->n_blocks;
+
+    s->sectors_per_block = s->block_size/512;
+    bs->total_sectors = s->n_blocks*s->sectors_per_block;
+    return 0;
+}
+
+static inline int cloop_read_block(BDRVCloopState *s,int block_num)
+{
+    if(s->current_block != block_num) {
+	int ret;
+        uint32_t bytes = s->offsets[block_num+1]-s->offsets[block_num];
+
+	lseek(s->fd, s->offsets[block_num], SEEK_SET);
+        ret = read(s->fd, s->compressed_block, bytes);
+        if (ret != bytes)
+            return -1;
+
+	s->zstream.next_in = s->compressed_block;
+	s->zstream.avail_in = bytes;
+	s->zstream.next_out = s->uncompressed_block;
+	s->zstream.avail_out = s->block_size;
+	ret = inflateReset(&s->zstream);
+	if(ret != Z_OK)
+	    return -1;
+	ret = inflate(&s->zstream, Z_FINISH);
+	if(ret != Z_STREAM_END || s->zstream.total_out != s->block_size)
+	    return -1;
+
+	s->current_block = block_num;
+    }
+    return 0;
+}
+
+static int cloop_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVCloopState *s = bs->opaque;
+    int i;
+
+    for(i=0;i<nb_sectors;i++) {
+	uint32_t sector_offset_in_block=((sector_num+i)%s->sectors_per_block),
+	    block_num=(sector_num+i)/s->sectors_per_block;
+	if(cloop_read_block(s, block_num) != 0)
+	    return -1;
+	memcpy(buf+i*512,s->uncompressed_block+sector_offset_in_block*512,512);
+    }
+    return 0;
+}
+
+static void cloop_close(BlockDriverState *bs)
+{
+    BDRVCloopState *s = bs->opaque;
+    close(s->fd);
+    if(s->n_blocks>0)
+	free(s->offsets);
+    free(s->compressed_block);
+    free(s->uncompressed_block);
+    inflateEnd(&s->zstream);
+}
+
+BlockDriver bdrv_cloop = {
+    "cloop",
+    sizeof(BDRVCloopState),
+    cloop_probe,
+    cloop_open,
+    cloop_read,
+    NULL,
+    cloop_close,
+};
+
+
diff --git a/block-cow.c b/block-cow.c
new file mode 100644
index 0000000..9e7b646
--- /dev/null
+++ b/block-cow.c
@@ -0,0 +1,267 @@
+/*
+ * Block driver for the COW format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef _WIN32
+#include "qemu-common.h"
+#include "block_int.h"
+#include <sys/mman.h>
+
+/**************************************************************/
+/* COW block driver using file system holes */
+
+/* user mode linux compatible COW file */
+#define COW_MAGIC 0x4f4f4f4d  /* MOOO */
+#define COW_VERSION 2
+
+struct cow_header_v2 {
+    uint32_t magic;
+    uint32_t version;
+    char backing_file[1024];
+    int32_t mtime;
+    uint64_t size;
+    uint32_t sectorsize;
+};
+
+typedef struct BDRVCowState {
+    int fd;
+    uint8_t *cow_bitmap; /* if non NULL, COW mappings are used first */
+    uint8_t *cow_bitmap_addr; /* mmap address of cow_bitmap */
+    int cow_bitmap_size;
+    int64_t cow_sectors_offset;
+} BDRVCowState;
+
+static int cow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    const struct cow_header_v2 *cow_header = (const void *)buf;
+
+    if (buf_size >= sizeof(struct cow_header_v2) &&
+        be32_to_cpu(cow_header->magic) == COW_MAGIC &&
+        be32_to_cpu(cow_header->version) == COW_VERSION)
+        return 100;
+    else
+        return 0;
+}
+
+static int cow_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVCowState *s = bs->opaque;
+    int fd;
+    struct cow_header_v2 cow_header;
+    int64_t size;
+
+    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+    if (fd < 0) {
+        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+        if (fd < 0)
+            return -1;
+    }
+    s->fd = fd;
+    /* see if it is a cow image */
+    if (read(fd, &cow_header, sizeof(cow_header)) != sizeof(cow_header)) {
+        goto fail;
+    }
+
+    if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
+        be32_to_cpu(cow_header.version) != COW_VERSION) {
+        goto fail;
+    }
+
+    /* cow image found */
+    size = be64_to_cpu(cow_header.size);
+    bs->total_sectors = size / 512;
+
+    pstrcpy(bs->backing_file, sizeof(bs->backing_file),
+            cow_header.backing_file);
+
+    /* mmap the bitmap */
+    s->cow_bitmap_size = ((bs->total_sectors + 7) >> 3) + sizeof(cow_header);
+    s->cow_bitmap_addr = mmap(get_mmap_addr(s->cow_bitmap_size),
+                              s->cow_bitmap_size,
+                              PROT_READ | PROT_WRITE,
+                              MAP_SHARED, s->fd, 0);
+    if (s->cow_bitmap_addr == MAP_FAILED)
+        goto fail;
+    s->cow_bitmap = s->cow_bitmap_addr + sizeof(cow_header);
+    s->cow_sectors_offset = (s->cow_bitmap_size + 511) & ~511;
+    return 0;
+ fail:
+    close(fd);
+    return -1;
+}
+
+static inline void cow_set_bit(uint8_t *bitmap, int64_t bitnum)
+{
+    bitmap[bitnum / 8] |= (1 << (bitnum%8));
+}
+
+static inline int is_bit_set(const uint8_t *bitmap, int64_t bitnum)
+{
+    return !!(bitmap[bitnum / 8] & (1 << (bitnum%8)));
+}
+
+
+/* Return true if first block has been changed (ie. current version is
+ * in COW file).  Set the number of continuous blocks for which that
+ * is true. */
+static inline int is_changed(uint8_t *bitmap,
+                             int64_t sector_num, int nb_sectors,
+                             int *num_same)
+{
+    int changed;
+
+    if (!bitmap || nb_sectors == 0) {
+	*num_same = nb_sectors;
+	return 0;
+    }
+
+    changed = is_bit_set(bitmap, sector_num);
+    for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
+	if (is_bit_set(bitmap, sector_num + *num_same) != changed)
+	    break;
+    }
+
+    return changed;
+}
+
+static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+                            int nb_sectors, int *pnum)
+{
+    BDRVCowState *s = bs->opaque;
+    return is_changed(s->cow_bitmap, sector_num, nb_sectors, pnum);
+}
+
+static int cow_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVCowState *s = bs->opaque;
+    int ret, n;
+
+    while (nb_sectors > 0) {
+        if (is_changed(s->cow_bitmap, sector_num, nb_sectors, &n)) {
+            lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
+            ret = read(s->fd, buf, n * 512);
+            if (ret != n * 512)
+                return -1;
+        } else {
+            if (bs->backing_hd) {
+                /* read from the base image */
+                ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
+                if (ret < 0)
+                    return -1;
+            } else {
+            memset(buf, 0, n * 512);
+        }
+        }
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+    }
+    return 0;
+}
+
+static int cow_write(BlockDriverState *bs, int64_t sector_num,
+                     const uint8_t *buf, int nb_sectors)
+{
+    BDRVCowState *s = bs->opaque;
+    int ret, i;
+
+    lseek(s->fd, s->cow_sectors_offset + sector_num * 512, SEEK_SET);
+    ret = write(s->fd, buf, nb_sectors * 512);
+    if (ret != nb_sectors * 512)
+        return -1;
+    for (i = 0; i < nb_sectors; i++)
+        cow_set_bit(s->cow_bitmap, sector_num + i);
+    return 0;
+}
+
+static void cow_close(BlockDriverState *bs)
+{
+    BDRVCowState *s = bs->opaque;
+    munmap(s->cow_bitmap_addr, s->cow_bitmap_size);
+    close(s->fd);
+}
+
+static int cow_create(const char *filename, int64_t image_sectors,
+                      const char *image_filename, int flags)
+{
+    int fd, cow_fd;
+    struct cow_header_v2 cow_header;
+    struct stat st;
+
+    if (flags)
+        return -ENOTSUP;
+
+    cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+              0644);
+    if (cow_fd < 0)
+        return -1;
+    memset(&cow_header, 0, sizeof(cow_header));
+    cow_header.magic = cpu_to_be32(COW_MAGIC);
+    cow_header.version = cpu_to_be32(COW_VERSION);
+    if (image_filename) {
+        /* Note: if no file, we put a dummy mtime */
+        cow_header.mtime = cpu_to_be32(0);
+
+        fd = open(image_filename, O_RDONLY | O_BINARY);
+        if (fd < 0) {
+            close(cow_fd);
+            goto mtime_fail;
+        }
+        if (fstat(fd, &st) != 0) {
+            close(fd);
+            goto mtime_fail;
+        }
+        close(fd);
+        cow_header.mtime = cpu_to_be32(st.st_mtime);
+    mtime_fail:
+        pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
+                image_filename);
+    }
+    cow_header.sectorsize = cpu_to_be32(512);
+    cow_header.size = cpu_to_be64(image_sectors * 512);
+    write(cow_fd, &cow_header, sizeof(cow_header));
+    /* resize to include at least all the bitmap */
+    ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
+    close(cow_fd);
+    return 0;
+}
+
+static void cow_flush(BlockDriverState *bs)
+{
+    BDRVCowState *s = bs->opaque;
+    fsync(s->fd);
+}
+
+BlockDriver bdrv_cow = {
+    "cow",
+    sizeof(BDRVCowState),
+    cow_probe,
+    cow_open,
+    cow_read,
+    cow_write,
+    cow_close,
+    cow_create,
+    cow_flush,
+    cow_is_allocated,
+};
+#endif
diff --git a/block-dmg.c b/block-dmg.c
new file mode 100644
index 0000000..8c9d0da
--- /dev/null
+++ b/block-dmg.c
@@ -0,0 +1,297 @@
+/*
+ * QEMU Block driver for DMG images
+ *
+ * Copyright (c) 2004 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include "bswap.h"
+#include <zlib.h>
+
+typedef struct BDRVDMGState {
+    int fd;
+
+    /* each chunk contains a certain number of sectors,
+     * offsets[i] is the offset in the .dmg file,
+     * lengths[i] is the length of the compressed chunk,
+     * sectors[i] is the sector beginning at offsets[i],
+     * sectorcounts[i] is the number of sectors in that chunk,
+     * the sectors array is ordered
+     * 0<=i<n_chunks */
+
+    uint32_t n_chunks;
+    uint32_t* types;
+    uint64_t* offsets;
+    uint64_t* lengths;
+    uint64_t* sectors;
+    uint64_t* sectorcounts;
+    uint32_t current_chunk;
+    uint8_t *compressed_chunk;
+    uint8_t *uncompressed_chunk;
+    z_stream zstream;
+} BDRVDMGState;
+
+static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    int len=strlen(filename);
+    if(len>4 && !strcmp(filename+len-4,".dmg"))
+	return 2;
+    return 0;
+}
+
+static off_t read_off(int fd)
+{
+	uint64_t buffer;
+	if(read(fd,&buffer,8)<8)
+		return 0;
+	return be64_to_cpu(buffer);
+}
+
+static off_t read_uint32(int fd)
+{
+	uint32_t buffer;
+	if(read(fd,&buffer,4)<4)
+		return 0;
+	return be32_to_cpu(buffer);
+}
+
+static int dmg_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVDMGState *s = bs->opaque;
+    off_t info_begin,info_end,last_in_offset,last_out_offset;
+    uint32_t count;
+    uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
+
+    s->fd = open(filename, O_RDONLY | O_BINARY);
+    if (s->fd < 0)
+        return -errno;
+    bs->read_only = 1;
+    s->n_chunks = 0;
+    s->offsets = s->lengths = s->sectors = s->sectorcounts = 0;
+
+    /* read offset of info blocks */
+    if(lseek(s->fd,-0x1d8,SEEK_END)<0) {
+dmg_close:
+	close(s->fd);
+	/* open raw instead */
+	bs->drv=&bdrv_raw;
+	return bs->drv->bdrv_open(bs, filename, flags);
+    }
+    info_begin=read_off(s->fd);
+    if(info_begin==0)
+	goto dmg_close;
+    if(lseek(s->fd,info_begin,SEEK_SET)<0)
+	goto dmg_close;
+    if(read_uint32(s->fd)!=0x100)
+	goto dmg_close;
+    if((count = read_uint32(s->fd))==0)
+	goto dmg_close;
+    info_end = info_begin+count;
+    if(lseek(s->fd,0xf8,SEEK_CUR)<0)
+	goto dmg_close;
+
+    /* read offsets */
+    last_in_offset = last_out_offset = 0;
+    while(lseek(s->fd,0,SEEK_CUR)<info_end) {
+        uint32_t type;
+
+	count = read_uint32(s->fd);
+	if(count==0)
+	    goto dmg_close;
+	type = read_uint32(s->fd);
+	if(type!=0x6d697368 || count<244)
+	    lseek(s->fd,count-4,SEEK_CUR);
+	else {
+	    int new_size, chunk_count;
+	    if(lseek(s->fd,200,SEEK_CUR)<0)
+	        goto dmg_close;
+	    chunk_count = (count-204)/40;
+	    new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
+	    s->types = qemu_realloc(s->types, new_size/2);
+	    s->offsets = qemu_realloc(s->offsets, new_size);
+	    s->lengths = qemu_realloc(s->lengths, new_size);
+	    s->sectors = qemu_realloc(s->sectors, new_size);
+	    s->sectorcounts = qemu_realloc(s->sectorcounts, new_size);
+
+	    for(i=s->n_chunks;i<s->n_chunks+chunk_count;i++) {
+		s->types[i] = read_uint32(s->fd);
+		if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
+		    if(s->types[i]==0xffffffff) {
+			last_in_offset = s->offsets[i-1]+s->lengths[i-1];
+			last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
+		    }
+		    chunk_count--;
+		    i--;
+		    if(lseek(s->fd,36,SEEK_CUR)<0)
+			goto dmg_close;
+		    continue;
+		}
+		read_uint32(s->fd);
+		s->sectors[i] = last_out_offset+read_off(s->fd);
+		s->sectorcounts[i] = read_off(s->fd);
+		s->offsets[i] = last_in_offset+read_off(s->fd);
+		s->lengths[i] = read_off(s->fd);
+		if(s->lengths[i]>max_compressed_size)
+		    max_compressed_size = s->lengths[i];
+		if(s->sectorcounts[i]>max_sectors_per_chunk)
+		    max_sectors_per_chunk = s->sectorcounts[i];
+	    }
+	    s->n_chunks+=chunk_count;
+	}
+    }
+
+    /* initialize zlib engine */
+    if(!(s->compressed_chunk = malloc(max_compressed_size+1)))
+	goto dmg_close;
+    if(!(s->uncompressed_chunk = malloc(512*max_sectors_per_chunk)))
+	goto dmg_close;
+    if(inflateInit(&s->zstream) != Z_OK)
+	goto dmg_close;
+
+    s->current_chunk = s->n_chunks;
+
+    return 0;
+}
+
+static inline int is_sector_in_chunk(BDRVDMGState* s,
+		uint32_t chunk_num,int sector_num)
+{
+    if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
+	    s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
+	return 0;
+    else
+	return -1;
+}
+
+static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
+{
+    /* binary search */
+    uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
+    while(chunk1!=chunk2) {
+	chunk3 = (chunk1+chunk2)/2;
+	if(s->sectors[chunk3]>sector_num)
+	    chunk2 = chunk3;
+	else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
+	    return chunk3;
+	else
+	    chunk1 = chunk3;
+    }
+    return s->n_chunks; /* error */
+}
+
+static inline int dmg_read_chunk(BDRVDMGState *s,int sector_num)
+{
+    if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
+	int ret;
+	uint32_t chunk = search_chunk(s,sector_num);
+
+	if(chunk>=s->n_chunks)
+	    return -1;
+
+	s->current_chunk = s->n_chunks;
+	switch(s->types[chunk]) {
+	case 0x80000005: { /* zlib compressed */
+	    int i;
+
+	    ret = lseek(s->fd, s->offsets[chunk], SEEK_SET);
+	    if(ret<0)
+		return -1;
+
+	    /* we need to buffer, because only the chunk as whole can be
+	     * inflated. */
+	    i=0;
+	    do {
+		ret = read(s->fd, s->compressed_chunk+i, s->lengths[chunk]-i);
+		if(ret<0 && errno==EINTR)
+		    ret=0;
+		i+=ret;
+	    } while(ret>=0 && ret+i<s->lengths[chunk]);
+
+	    if (ret != s->lengths[chunk])
+		return -1;
+
+	    s->zstream.next_in = s->compressed_chunk;
+	    s->zstream.avail_in = s->lengths[chunk];
+	    s->zstream.next_out = s->uncompressed_chunk;
+	    s->zstream.avail_out = 512*s->sectorcounts[chunk];
+	    ret = inflateReset(&s->zstream);
+	    if(ret != Z_OK)
+		return -1;
+	    ret = inflate(&s->zstream, Z_FINISH);
+	    if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
+		return -1;
+	    break; }
+	case 1: /* copy */
+	    ret = read(s->fd, s->uncompressed_chunk, s->lengths[chunk]);
+	    if (ret != s->lengths[chunk])
+		return -1;
+	    break;
+	case 2: /* zero */
+	    memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
+	    break;
+	}
+	s->current_chunk = chunk;
+    }
+    return 0;
+}
+
+static int dmg_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVDMGState *s = bs->opaque;
+    int i;
+
+    for(i=0;i<nb_sectors;i++) {
+	uint32_t sector_offset_in_chunk;
+	if(dmg_read_chunk(s, sector_num+i) != 0)
+	    return -1;
+	sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
+	memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
+    }
+    return 0;
+}
+
+static void dmg_close(BlockDriverState *bs)
+{
+    BDRVDMGState *s = bs->opaque;
+    close(s->fd);
+    if(s->n_chunks>0) {
+	free(s->types);
+	free(s->offsets);
+	free(s->lengths);
+	free(s->sectors);
+	free(s->sectorcounts);
+    }
+    free(s->compressed_chunk);
+    free(s->uncompressed_chunk);
+    inflateEnd(&s->zstream);
+}
+
+BlockDriver bdrv_dmg = {
+    "dmg",
+    sizeof(BDRVDMGState),
+    dmg_probe,
+    dmg_open,
+    dmg_read,
+    NULL,
+    dmg_close,
+};
+
diff --git a/block-nbd.c b/block-nbd.c
new file mode 100644
index 0000000..5b6cc4f
--- /dev/null
+++ b/block-nbd.c
@@ -0,0 +1,189 @@
+/*
+ * QEMU Block driver for  NBD
+ *
+ * Copyright (C) 2008 Bull S.A.S.
+ *     Author: Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ * Some parts:
+ *    Copyright (C) 2007 Anthony Liguori <anthony@codemonkey.ws>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "nbd.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+typedef struct BDRVNBDState {
+    int sock;
+    off_t size;
+    size_t blocksize;
+} BDRVNBDState;
+
+static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
+{
+    BDRVNBDState *s = bs->opaque;
+    const char *host;
+    const char *unixpath;
+    int sock;
+    off_t size;
+    size_t blocksize;
+    int ret;
+
+    if ((flags & BDRV_O_CREAT))
+        return -EINVAL;
+
+    if (!strstart(filename, "nbd:", &host))
+        return -EINVAL;
+
+    if (strstart(host, "unix:", &unixpath)) {
+
+        if (unixpath[0] != '/')
+            return -EINVAL;
+
+        sock = unix_socket_outgoing(unixpath);
+
+    } else {
+        uint16_t port;
+        char *p, *r;
+        char hostname[128];
+
+        pstrcpy(hostname, 128, host);
+
+        p = strchr(hostname, ':');
+        if (p == NULL)
+            return -EINVAL;
+
+        *p = '\0';
+        p++;
+
+        port = strtol(p, &r, 0);
+        if (r == p)
+            return -EINVAL;
+        sock = tcp_socket_outgoing(hostname, port);
+    }
+
+    if (sock == -1)
+        return -errno;
+
+    ret = nbd_receive_negotiate(sock, &size, &blocksize);
+    if (ret == -1)
+        return -errno;
+
+    s->sock = sock;
+    s->size = size;
+    s->blocksize = blocksize;
+
+    return 0;
+}
+
+static int nbd_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVNBDState *s = bs->opaque;
+    struct nbd_request request;
+    struct nbd_reply reply;
+
+    request.type = NBD_CMD_READ;
+    request.handle = (uint64_t)(intptr_t)bs;
+    request.from = sector_num * 512;;
+    request.len = nb_sectors * 512;
+
+    if (nbd_send_request(s->sock, &request) == -1)
+        return -errno;
+
+    if (nbd_receive_reply(s->sock, &reply) == -1)
+        return -errno;
+
+    if (reply.error !=0)
+        return -reply.error;
+
+    if (reply.handle != request.handle)
+        return -EIO;
+
+    if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
+        return -EIO;
+
+    return 0;
+}
+
+static int nbd_write(BlockDriverState *bs, int64_t sector_num,
+                     const uint8_t *buf, int nb_sectors)
+{
+    BDRVNBDState *s = bs->opaque;
+    struct nbd_request request;
+    struct nbd_reply reply;
+
+    request.type = NBD_CMD_WRITE;
+    request.handle = (uint64_t)(intptr_t)bs;
+    request.from = sector_num * 512;;
+    request.len = nb_sectors * 512;
+
+    if (nbd_send_request(s->sock, &request) == -1)
+        return -errno;
+
+    if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
+        return -EIO;
+
+    if (nbd_receive_reply(s->sock, &reply) == -1)
+        return -errno;
+
+    if (reply.error !=0)
+        return -reply.error;
+
+    if (reply.handle != request.handle)
+        return -EIO;
+
+    return 0;
+}
+
+static void nbd_close(BlockDriverState *bs)
+{
+    BDRVNBDState *s = bs->opaque;
+    struct nbd_request request;
+
+    request.type = NBD_CMD_DISC;
+    request.handle = (uint64_t)(intptr_t)bs;
+    request.from = 0;
+    request.len = 0;
+    nbd_send_request(s->sock, &request);
+
+    close(s->sock);
+}
+
+static int64_t nbd_getlength(BlockDriverState *bs)
+{
+    BDRVNBDState *s = bs->opaque;
+
+    return s->size;
+}
+
+BlockDriver bdrv_nbd = {
+    "nbd",
+    sizeof(BDRVNBDState),
+    NULL, /* no probe for protocols */
+    nbd_open,
+    nbd_read,
+    nbd_write,
+    nbd_close,
+    .bdrv_getlength = nbd_getlength,
+    .protocol_name = "nbd",
+};
diff --git a/block-parallels.c b/block-parallels.c
new file mode 100644
index 0000000..4654b07
--- /dev/null
+++ b/block-parallels.c
@@ -0,0 +1,176 @@
+/*
+ * Block driver for Parallels disk image format
+ *
+ * Copyright (c) 2007 Alex Beregszaszi
+ *
+ * This code is based on comparing different disk images created by Parallels.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+
+/**************************************************************/
+
+#define HEADER_MAGIC "WithoutFreeSpace"
+#define HEADER_VERSION 2
+#define HEADER_SIZE 64
+
+// always little-endian
+struct parallels_header {
+    char magic[16]; // "WithoutFreeSpace"
+    uint32_t version;
+    uint32_t heads;
+    uint32_t cylinders;
+    uint32_t tracks;
+    uint32_t catalog_entries;
+    uint32_t nb_sectors;
+    char padding[24];
+} __attribute__((packed));
+
+typedef struct BDRVParallelsState {
+    int fd;
+
+    uint32_t *catalog_bitmap;
+    int catalog_size;
+
+    int tracks;
+} BDRVParallelsState;
+
+static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    const struct parallels_header *ph = (const void *)buf;
+
+    if (buf_size < HEADER_SIZE)
+	return 0;
+
+    if (!memcmp(ph->magic, HEADER_MAGIC, 16) &&
+	(le32_to_cpu(ph->version) == HEADER_VERSION))
+	return 100;
+
+    return 0;
+}
+
+static int parallels_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVParallelsState *s = bs->opaque;
+    int fd, i;
+    struct parallels_header ph;
+
+    fd = open(filename, O_RDWR | O_BINARY | O_LARGEFILE);
+    if (fd < 0) {
+        fd = open(filename, O_RDONLY | O_BINARY | O_LARGEFILE);
+        if (fd < 0)
+            return -1;
+    }
+
+    bs->read_only = 1; // no write support yet
+
+    s->fd = fd;
+
+    if (read(fd, &ph, sizeof(ph)) != sizeof(ph))
+        goto fail;
+
+    if (memcmp(ph.magic, HEADER_MAGIC, 16) ||
+	(le32_to_cpu(ph.version) != HEADER_VERSION)) {
+        goto fail;
+    }
+
+    bs->total_sectors = le32_to_cpu(ph.nb_sectors);
+
+    if (lseek(s->fd, 64, SEEK_SET) != 64)
+	goto fail;
+
+    s->tracks = le32_to_cpu(ph.tracks);
+
+    s->catalog_size = le32_to_cpu(ph.catalog_entries);
+    s->catalog_bitmap = qemu_malloc(s->catalog_size * 4);
+    if (!s->catalog_bitmap)
+	goto fail;
+    if (read(s->fd, s->catalog_bitmap, s->catalog_size * 4) !=
+	s->catalog_size * 4)
+	goto fail;
+    for (i = 0; i < s->catalog_size; i++)
+	le32_to_cpus(&s->catalog_bitmap[i]);
+
+    return 0;
+fail:
+    if (s->catalog_bitmap)
+	qemu_free(s->catalog_bitmap);
+    close(fd);
+    return -1;
+}
+
+static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+    BDRVParallelsState *s = bs->opaque;
+    uint32_t index, offset, position;
+
+    index = sector_num / s->tracks;
+    offset = sector_num % s->tracks;
+
+    // not allocated
+    if ((index > s->catalog_size) || (s->catalog_bitmap[index] == 0))
+	return -1;
+
+    position = (s->catalog_bitmap[index] + offset) * 512;
+
+//    fprintf(stderr, "sector: %llx index=%x offset=%x pointer=%x position=%x\n",
+//	sector_num, index, offset, s->catalog_bitmap[index], position);
+
+    if (lseek(s->fd, position, SEEK_SET) != position)
+	return -1;
+
+    return 0;
+}
+
+static int parallels_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVParallelsState *s = bs->opaque;
+
+    while (nb_sectors > 0) {
+	if (!seek_to_sector(bs, sector_num)) {
+	    if (read(s->fd, buf, 512) != 512)
+		return -1;
+	} else
+            memset(buf, 0, 512);
+        nb_sectors--;
+        sector_num++;
+        buf += 512;
+    }
+    return 0;
+}
+
+static void parallels_close(BlockDriverState *bs)
+{
+    BDRVParallelsState *s = bs->opaque;
+    qemu_free(s->catalog_bitmap);
+    close(s->fd);
+}
+
+BlockDriver bdrv_parallels = {
+    "parallels",
+    sizeof(BDRVParallelsState),
+    parallels_probe,
+    parallels_open,
+    parallels_read,
+    NULL,
+    parallels_close,
+};
diff --git a/block-qcow.c b/block-qcow.c
new file mode 100644
index 0000000..1fecf30
--- /dev/null
+++ b/block-qcow.c
@@ -0,0 +1,904 @@
+/*
+ * Block driver for the QCOW format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include <zlib.h>
+#include "aes.h"
+
+/**************************************************************/
+/* QEMU COW block driver with compression and encryption support */
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW_VERSION 1
+
+#define QCOW_CRYPT_NONE 0
+#define QCOW_CRYPT_AES  1
+
+#define QCOW_OFLAG_COMPRESSED (1LL << 63)
+
+typedef struct QCowHeader {
+    uint32_t magic;
+    uint32_t version;
+    uint64_t backing_file_offset;
+    uint32_t backing_file_size;
+    uint32_t mtime;
+    uint64_t size; /* in bytes */
+    uint8_t cluster_bits;
+    uint8_t l2_bits;
+    uint32_t crypt_method;
+    uint64_t l1_table_offset;
+} QCowHeader;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct BDRVQcowState {
+    BlockDriverState *hd;
+    int cluster_bits;
+    int cluster_size;
+    int cluster_sectors;
+    int l2_bits;
+    int l2_size;
+    int l1_size;
+    uint64_t cluster_offset_mask;
+    uint64_t l1_table_offset;
+    uint64_t *l1_table;
+    uint64_t *l2_cache;
+    uint64_t l2_cache_offsets[L2_CACHE_SIZE];
+    uint32_t l2_cache_counts[L2_CACHE_SIZE];
+    uint8_t *cluster_cache;
+    uint8_t *cluster_data;
+    uint64_t cluster_cache_offset;
+    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+    uint32_t crypt_method_header;
+    AES_KEY aes_encrypt_key;
+    AES_KEY aes_decrypt_key;
+} BDRVQcowState;
+
+static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
+
+static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    const QCowHeader *cow_header = (const void *)buf;
+
+    if (buf_size >= sizeof(QCowHeader) &&
+        be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
+        be32_to_cpu(cow_header->version) == QCOW_VERSION)
+        return 100;
+    else
+        return 0;
+}
+
+static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVQcowState *s = bs->opaque;
+    int len, i, shift, ret;
+    QCowHeader header;
+
+    ret = bdrv_file_open(&s->hd, filename, flags);
+    if (ret < 0)
+        return ret;
+    if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header))
+        goto fail;
+    be32_to_cpus(&header.magic);
+    be32_to_cpus(&header.version);
+    be64_to_cpus(&header.backing_file_offset);
+    be32_to_cpus(&header.backing_file_size);
+    be32_to_cpus(&header.mtime);
+    be64_to_cpus(&header.size);
+    be32_to_cpus(&header.crypt_method);
+    be64_to_cpus(&header.l1_table_offset);
+
+    if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
+        goto fail;
+    if (header.size <= 1 || header.cluster_bits < 9)
+        goto fail;
+    if (header.crypt_method > QCOW_CRYPT_AES)
+        goto fail;
+    s->crypt_method_header = header.crypt_method;
+    if (s->crypt_method_header)
+        bs->encrypted = 1;
+    s->cluster_bits = header.cluster_bits;
+    s->cluster_size = 1 << s->cluster_bits;
+    s->cluster_sectors = 1 << (s->cluster_bits - 9);
+    s->l2_bits = header.l2_bits;
+    s->l2_size = 1 << s->l2_bits;
+    bs->total_sectors = header.size / 512;
+    s->cluster_offset_mask = (1LL << (63 - s->cluster_bits)) - 1;
+
+    /* read the level 1 table */
+    shift = s->cluster_bits + s->l2_bits;
+    s->l1_size = (header.size + (1LL << shift) - 1) >> shift;
+
+    s->l1_table_offset = header.l1_table_offset;
+    s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
+    if (!s->l1_table)
+        goto fail;
+    if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
+        s->l1_size * sizeof(uint64_t))
+        goto fail;
+    for(i = 0;i < s->l1_size; i++) {
+        be64_to_cpus(&s->l1_table[i]);
+    }
+    /* alloc L2 cache */
+    s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+    if (!s->l2_cache)
+        goto fail;
+    s->cluster_cache = qemu_malloc(s->cluster_size);
+    if (!s->cluster_cache)
+        goto fail;
+    s->cluster_data = qemu_malloc(s->cluster_size);
+    if (!s->cluster_data)
+        goto fail;
+    s->cluster_cache_offset = -1;
+
+    /* read the backing file name */
+    if (header.backing_file_offset != 0) {
+        len = header.backing_file_size;
+        if (len > 1023)
+            len = 1023;
+        if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len)
+            goto fail;
+        bs->backing_file[len] = '\0';
+    }
+    return 0;
+
+ fail:
+    qemu_free(s->l1_table);
+    qemu_free(s->l2_cache);
+    qemu_free(s->cluster_cache);
+    qemu_free(s->cluster_data);
+    bdrv_delete(s->hd);
+    return -1;
+}
+
+static int qcow_set_key(BlockDriverState *bs, const char *key)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint8_t keybuf[16];
+    int len, i;
+
+    memset(keybuf, 0, 16);
+    len = strlen(key);
+    if (len > 16)
+        len = 16;
+    /* XXX: we could compress the chars to 7 bits to increase
+       entropy */
+    for(i = 0;i < len;i++) {
+        keybuf[i] = key[i];
+    }
+    s->crypt_method = s->crypt_method_header;
+
+    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+        return -1;
+    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+        return -1;
+#if 0
+    /* test */
+    {
+        uint8_t in[16];
+        uint8_t out[16];
+        uint8_t tmp[16];
+        for(i=0;i<16;i++)
+            in[i] = i;
+        AES_encrypt(in, tmp, &s->aes_encrypt_key);
+        AES_decrypt(tmp, out, &s->aes_decrypt_key);
+        for(i = 0; i < 16; i++)
+            printf(" %02x", tmp[i]);
+        printf("\n");
+        for(i = 0; i < 16; i++)
+            printf(" %02x", out[i]);
+        printf("\n");
+    }
+#endif
+    return 0;
+}
+
+/* The crypt function is compatible with the linux cryptoloop
+   algorithm for < 4 GB images. NOTE: out_buf == in_buf is
+   supported */
+static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                            uint8_t *out_buf, const uint8_t *in_buf,
+                            int nb_sectors, int enc,
+                            const AES_KEY *key)
+{
+    union {
+        uint64_t ll[2];
+        uint8_t b[16];
+    } ivec;
+    int i;
+
+    for(i = 0; i < nb_sectors; i++) {
+        ivec.ll[0] = cpu_to_le64(sector_num);
+        ivec.ll[1] = 0;
+        AES_cbc_encrypt(in_buf, out_buf, 512, key,
+                        ivec.b, enc);
+        sector_num++;
+        in_buf += 512;
+        out_buf += 512;
+    }
+}
+
+/* 'allocate' is:
+ *
+ * 0 to not allocate.
+ *
+ * 1 to allocate a normal cluster (for sector indexes 'n_start' to
+ * 'n_end')
+ *
+ * 2 to allocate a compressed cluster of size
+ * 'compressed_size'. 'compressed_size' must be > 0 and <
+ * cluster_size
+ *
+ * return 0 if not allocated.
+ */
+static uint64_t get_cluster_offset(BlockDriverState *bs,
+                                   uint64_t offset, int allocate,
+                                   int compressed_size,
+                                   int n_start, int n_end)
+{
+    BDRVQcowState *s = bs->opaque;
+    int min_index, i, j, l1_index, l2_index;
+    uint64_t l2_offset, *l2_table, cluster_offset, tmp;
+    uint32_t min_count;
+    int new_l2_table;
+
+    l1_index = offset >> (s->l2_bits + s->cluster_bits);
+    l2_offset = s->l1_table[l1_index];
+    new_l2_table = 0;
+    if (!l2_offset) {
+        if (!allocate)
+            return 0;
+        /* allocate a new l2 entry */
+        l2_offset = bdrv_getlength(s->hd);
+        /* round to cluster size */
+        l2_offset = (l2_offset + s->cluster_size - 1) & ~(s->cluster_size - 1);
+        /* update the L1 entry */
+        s->l1_table[l1_index] = l2_offset;
+        tmp = cpu_to_be64(l2_offset);
+        if (bdrv_pwrite(s->hd, s->l1_table_offset + l1_index * sizeof(tmp),
+                        &tmp, sizeof(tmp)) != sizeof(tmp))
+            return 0;
+        new_l2_table = 1;
+    }
+    for(i = 0; i < L2_CACHE_SIZE; i++) {
+        if (l2_offset == s->l2_cache_offsets[i]) {
+            /* increment the hit count */
+            if (++s->l2_cache_counts[i] == 0xffffffff) {
+                for(j = 0; j < L2_CACHE_SIZE; j++) {
+                    s->l2_cache_counts[j] >>= 1;
+                }
+            }
+            l2_table = s->l2_cache + (i << s->l2_bits);
+            goto found;
+        }
+    }
+    /* not found: load a new entry in the least used one */
+    min_index = 0;
+    min_count = 0xffffffff;
+    for(i = 0; i < L2_CACHE_SIZE; i++) {
+        if (s->l2_cache_counts[i] < min_count) {
+            min_count = s->l2_cache_counts[i];
+            min_index = i;
+        }
+    }
+    l2_table = s->l2_cache + (min_index << s->l2_bits);
+    if (new_l2_table) {
+        memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
+        if (bdrv_pwrite(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
+            s->l2_size * sizeof(uint64_t))
+            return 0;
+    } else {
+        if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
+            s->l2_size * sizeof(uint64_t))
+            return 0;
+    }
+    s->l2_cache_offsets[min_index] = l2_offset;
+    s->l2_cache_counts[min_index] = 1;
+ found:
+    l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+    cluster_offset = be64_to_cpu(l2_table[l2_index]);
+    if (!cluster_offset ||
+        ((cluster_offset & QCOW_OFLAG_COMPRESSED) && allocate == 1)) {
+        if (!allocate)
+            return 0;
+        /* allocate a new cluster */
+        if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
+            (n_end - n_start) < s->cluster_sectors) {
+            /* if the cluster is already compressed, we must
+               decompress it in the case it is not completely
+               overwritten */
+            if (decompress_cluster(s, cluster_offset) < 0)
+                return 0;
+            cluster_offset = bdrv_getlength(s->hd);
+            cluster_offset = (cluster_offset + s->cluster_size - 1) &
+                ~(s->cluster_size - 1);
+            /* write the cluster content */
+            if (bdrv_pwrite(s->hd, cluster_offset, s->cluster_cache, s->cluster_size) !=
+                s->cluster_size)
+                return -1;
+        } else {
+            cluster_offset = bdrv_getlength(s->hd);
+            /* round to cluster size */
+            cluster_offset = (cluster_offset + s->cluster_size - 1) &
+                ~(s->cluster_size - 1);
+            bdrv_truncate(s->hd, cluster_offset + s->cluster_size);
+            /* if encrypted, we must initialize the cluster
+               content which won't be written */
+            if (s->crypt_method &&
+                (n_end - n_start) < s->cluster_sectors) {
+                uint64_t start_sect;
+                start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
+                memset(s->cluster_data + 512, 0x00, 512);
+                for(i = 0; i < s->cluster_sectors; i++) {
+                    if (i < n_start || i >= n_end) {
+                        encrypt_sectors(s, start_sect + i,
+                                        s->cluster_data,
+                                        s->cluster_data + 512, 1, 1,
+                                        &s->aes_encrypt_key);
+                        if (bdrv_pwrite(s->hd, cluster_offset + i * 512,
+                                        s->cluster_data, 512) != 512)
+                            return -1;
+                    }
+                }
+            }
+        }
+        /* update L2 table */
+        tmp = cpu_to_be64(cluster_offset);
+        l2_table[l2_index] = tmp;
+        if (bdrv_pwrite(s->hd,
+                        l2_offset + l2_index * sizeof(tmp), &tmp, sizeof(tmp)) != sizeof(tmp))
+            return 0;
+    }
+    return cluster_offset;
+}
+
+static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+                             int nb_sectors, int *pnum)
+{
+    BDRVQcowState *s = bs->opaque;
+    int index_in_cluster, n;
+    uint64_t cluster_offset;
+
+    cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
+    index_in_cluster = sector_num & (s->cluster_sectors - 1);
+    n = s->cluster_sectors - index_in_cluster;
+    if (n > nb_sectors)
+        n = nb_sectors;
+    *pnum = n;
+    return (cluster_offset != 0);
+}
+
+static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
+                             const uint8_t *buf, int buf_size)
+{
+    z_stream strm1, *strm = &strm1;
+    int ret, out_len;
+
+    memset(strm, 0, sizeof(*strm));
+
+    strm->next_in = (uint8_t *)buf;
+    strm->avail_in = buf_size;
+    strm->next_out = out_buf;
+    strm->avail_out = out_buf_size;
+
+    ret = inflateInit2(strm, -12);
+    if (ret != Z_OK)
+        return -1;
+    ret = inflate(strm, Z_FINISH);
+    out_len = strm->next_out - out_buf;
+    if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
+        out_len != out_buf_size) {
+        inflateEnd(strm);
+        return -1;
+    }
+    inflateEnd(strm);
+    return 0;
+}
+
+static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
+{
+    int ret, csize;
+    uint64_t coffset;
+
+    coffset = cluster_offset & s->cluster_offset_mask;
+    if (s->cluster_cache_offset != coffset) {
+        csize = cluster_offset >> (63 - s->cluster_bits);
+        csize &= (s->cluster_size - 1);
+        ret = bdrv_pread(s->hd, coffset, s->cluster_data, csize);
+        if (ret != csize)
+            return -1;
+        if (decompress_buffer(s->cluster_cache, s->cluster_size,
+                              s->cluster_data, csize) < 0) {
+            return -1;
+        }
+        s->cluster_cache_offset = coffset;
+    }
+    return 0;
+}
+
+#if 0
+
+static int qcow_read(BlockDriverState *bs, int64_t sector_num,
+                     uint8_t *buf, int nb_sectors)
+{
+    BDRVQcowState *s = bs->opaque;
+    int ret, index_in_cluster, n;
+    uint64_t cluster_offset;
+
+    while (nb_sectors > 0) {
+        cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
+        index_in_cluster = sector_num & (s->cluster_sectors - 1);
+        n = s->cluster_sectors - index_in_cluster;
+        if (n > nb_sectors)
+            n = nb_sectors;
+        if (!cluster_offset) {
+            if (bs->backing_hd) {
+                /* read from the base image */
+                ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
+                if (ret < 0)
+                    return -1;
+            } else {
+                memset(buf, 0, 512 * n);
+            }
+        } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+            if (decompress_cluster(s, cluster_offset) < 0)
+                return -1;
+            memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
+        } else {
+            ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
+            if (ret != n * 512)
+                return -1;
+            if (s->crypt_method) {
+                encrypt_sectors(s, sector_num, buf, buf, n, 0,
+                                &s->aes_decrypt_key);
+            }
+        }
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+    }
+    return 0;
+}
+#endif
+
+static int qcow_write(BlockDriverState *bs, int64_t sector_num,
+                     const uint8_t *buf, int nb_sectors)
+{
+    BDRVQcowState *s = bs->opaque;
+    int ret, index_in_cluster, n;
+    uint64_t cluster_offset;
+
+    while (nb_sectors > 0) {
+        index_in_cluster = sector_num & (s->cluster_sectors - 1);
+        n = s->cluster_sectors - index_in_cluster;
+        if (n > nb_sectors)
+            n = nb_sectors;
+        cluster_offset = get_cluster_offset(bs, sector_num << 9, 1, 0,
+                                            index_in_cluster,
+                                            index_in_cluster + n);
+        if (!cluster_offset)
+            return -1;
+        if (s->crypt_method) {
+            encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
+                            &s->aes_encrypt_key);
+            ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512,
+                              s->cluster_data, n * 512);
+        } else {
+            ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
+        }
+        if (ret != n * 512)
+            return -1;
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+    }
+    s->cluster_cache_offset = -1; /* disable compressed cache */
+    return 0;
+}
+
+typedef struct QCowAIOCB {
+    BlockDriverAIOCB common;
+    int64_t sector_num;
+    uint8_t *buf;
+    int nb_sectors;
+    int n;
+    uint64_t cluster_offset;
+    uint8_t *cluster_data;
+    BlockDriverAIOCB *hd_aiocb;
+} QCowAIOCB;
+
+static void qcow_aio_read_cb(void *opaque, int ret)
+{
+    QCowAIOCB *acb = opaque;
+    BlockDriverState *bs = acb->common.bs;
+    BDRVQcowState *s = bs->opaque;
+    int index_in_cluster;
+
+    acb->hd_aiocb = NULL;
+    if (ret < 0) {
+    fail:
+        acb->common.cb(acb->common.opaque, ret);
+        qemu_aio_release(acb);
+        return;
+    }
+
+ redo:
+    /* post process the read buffer */
+    if (!acb->cluster_offset) {
+        /* nothing to do */
+    } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+        /* nothing to do */
+    } else {
+        if (s->crypt_method) {
+            encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf,
+                            acb->n, 0,
+                            &s->aes_decrypt_key);
+        }
+    }
+
+    acb->nb_sectors -= acb->n;
+    acb->sector_num += acb->n;
+    acb->buf += acb->n * 512;
+
+    if (acb->nb_sectors == 0) {
+        /* request completed */
+        acb->common.cb(acb->common.opaque, 0);
+        qemu_aio_release(acb);
+        return;
+    }
+
+    /* prepare next AIO request */
+    acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9,
+                                             0, 0, 0, 0);
+    index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+    acb->n = s->cluster_sectors - index_in_cluster;
+    if (acb->n > acb->nb_sectors)
+        acb->n = acb->nb_sectors;
+
+    if (!acb->cluster_offset) {
+        if (bs->backing_hd) {
+            /* read from the base image */
+            acb->hd_aiocb = bdrv_aio_read(bs->backing_hd,
+                acb->sector_num, acb->buf, acb->n, qcow_aio_read_cb, acb);
+            if (acb->hd_aiocb == NULL)
+                goto fail;
+        } else {
+            /* Note: in this case, no need to wait */
+            memset(acb->buf, 0, 512 * acb->n);
+            goto redo;
+        }
+    } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+        /* add AIO support for compressed blocks ? */
+        if (decompress_cluster(s, acb->cluster_offset) < 0)
+            goto fail;
+        memcpy(acb->buf,
+               s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
+        goto redo;
+    } else {
+        if ((acb->cluster_offset & 511) != 0) {
+            ret = -EIO;
+            goto fail;
+        }
+        acb->hd_aiocb = bdrv_aio_read(s->hd,
+                            (acb->cluster_offset >> 9) + index_in_cluster,
+                            acb->buf, acb->n, qcow_aio_read_cb, acb);
+        if (acb->hd_aiocb == NULL)
+            goto fail;
+    }
+}
+
+static BlockDriverAIOCB *qcow_aio_read(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    QCowAIOCB *acb;
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (!acb)
+        return NULL;
+    acb->hd_aiocb = NULL;
+    acb->sector_num = sector_num;
+    acb->buf = buf;
+    acb->nb_sectors = nb_sectors;
+    acb->n = 0;
+    acb->cluster_offset = 0;
+
+    qcow_aio_read_cb(acb, 0);
+    return &acb->common;
+}
+
+static void qcow_aio_write_cb(void *opaque, int ret)
+{
+    QCowAIOCB *acb = opaque;
+    BlockDriverState *bs = acb->common.bs;
+    BDRVQcowState *s = bs->opaque;
+    int index_in_cluster;
+    uint64_t cluster_offset;
+    const uint8_t *src_buf;
+
+    acb->hd_aiocb = NULL;
+
+    if (ret < 0) {
+    fail:
+        acb->common.cb(acb->common.opaque, ret);
+        qemu_aio_release(acb);
+        return;
+    }
+
+    acb->nb_sectors -= acb->n;
+    acb->sector_num += acb->n;
+    acb->buf += acb->n * 512;
+
+    if (acb->nb_sectors == 0) {
+        /* request completed */
+        acb->common.cb(acb->common.opaque, 0);
+        qemu_aio_release(acb);
+        return;
+    }
+
+    index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+    acb->n = s->cluster_sectors - index_in_cluster;
+    if (acb->n > acb->nb_sectors)
+        acb->n = acb->nb_sectors;
+    cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, 1, 0,
+                                        index_in_cluster,
+                                        index_in_cluster + acb->n);
+    if (!cluster_offset || (cluster_offset & 511) != 0) {
+        ret = -EIO;
+        goto fail;
+    }
+    if (s->crypt_method) {
+        if (!acb->cluster_data) {
+            acb->cluster_data = qemu_mallocz(s->cluster_size);
+            if (!acb->cluster_data) {
+                ret = -ENOMEM;
+                goto fail;
+            }
+        }
+        encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
+                        acb->n, 1, &s->aes_encrypt_key);
+        src_buf = acb->cluster_data;
+    } else {
+        src_buf = acb->buf;
+    }
+    acb->hd_aiocb = bdrv_aio_write(s->hd,
+                                   (cluster_offset >> 9) + index_in_cluster,
+                                   src_buf, acb->n,
+                                   qcow_aio_write_cb, acb);
+    if (acb->hd_aiocb == NULL)
+        goto fail;
+}
+
+static BlockDriverAIOCB *qcow_aio_write(BlockDriverState *bs,
+        int64_t sector_num, const uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowAIOCB *acb;
+
+    s->cluster_cache_offset = -1; /* disable compressed cache */
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (!acb)
+        return NULL;
+    acb->hd_aiocb = NULL;
+    acb->sector_num = sector_num;
+    acb->buf = (uint8_t *)buf;
+    acb->nb_sectors = nb_sectors;
+    acb->n = 0;
+
+    qcow_aio_write_cb(acb, 0);
+    return &acb->common;
+}
+
+static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    QCowAIOCB *acb = (QCowAIOCB *)blockacb;
+    if (acb->hd_aiocb)
+        bdrv_aio_cancel(acb->hd_aiocb);
+    qemu_aio_release(acb);
+}
+
+static void qcow_close(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    qemu_free(s->l1_table);
+    qemu_free(s->l2_cache);
+    qemu_free(s->cluster_cache);
+    qemu_free(s->cluster_data);
+    bdrv_delete(s->hd);
+}
+
+static int qcow_create(const char *filename, int64_t total_size,
+                      const char *backing_file, int flags)
+{
+    int fd, header_size, backing_filename_len, l1_size, i, shift;
+    QCowHeader header;
+    uint64_t tmp;
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+    if (fd < 0)
+        return -1;
+    memset(&header, 0, sizeof(header));
+    header.magic = cpu_to_be32(QCOW_MAGIC);
+    header.version = cpu_to_be32(QCOW_VERSION);
+    header.size = cpu_to_be64(total_size * 512);
+    header_size = sizeof(header);
+    backing_filename_len = 0;
+    if (backing_file) {
+        if (strcmp(backing_file, "fat:")) {
+            header.backing_file_offset = cpu_to_be64(header_size);
+            backing_filename_len = strlen(backing_file);
+            header.backing_file_size = cpu_to_be32(backing_filename_len);
+            header_size += backing_filename_len;
+        } else {
+            /* special backing file for vvfat */
+            backing_file = NULL;
+        }
+        header.cluster_bits = 9; /* 512 byte cluster to avoid copying
+                                    unmodifyed sectors */
+        header.l2_bits = 12; /* 32 KB L2 tables */
+    } else {
+        header.cluster_bits = 12; /* 4 KB clusters */
+        header.l2_bits = 9; /* 4 KB L2 tables */
+    }
+    header_size = (header_size + 7) & ~7;
+    shift = header.cluster_bits + header.l2_bits;
+    l1_size = ((total_size * 512) + (1LL << shift) - 1) >> shift;
+
+    header.l1_table_offset = cpu_to_be64(header_size);
+    if (flags & BLOCK_FLAG_ENCRYPT) {
+        header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+    } else {
+        header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+    }
+
+    /* write all the data */
+    write(fd, &header, sizeof(header));
+    if (backing_file) {
+        write(fd, backing_file, backing_filename_len);
+    }
+    lseek(fd, header_size, SEEK_SET);
+    tmp = 0;
+    for(i = 0;i < l1_size; i++) {
+        write(fd, &tmp, sizeof(tmp));
+    }
+    close(fd);
+    return 0;
+}
+
+static int qcow_make_empty(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint32_t l1_length = s->l1_size * sizeof(uint64_t);
+    int ret;
+
+    memset(s->l1_table, 0, l1_length);
+    if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, l1_length) < 0)
+	return -1;
+    ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length);
+    if (ret < 0)
+        return ret;
+
+    memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+    memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
+    memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
+
+    return 0;
+}
+
+/* XXX: put compressed sectors first, then all the cluster aligned
+   tables to avoid losing bytes in alignment */
+static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
+                                 const uint8_t *buf, int nb_sectors)
+{
+    BDRVQcowState *s = bs->opaque;
+    z_stream strm;
+    int ret, out_len;
+    uint8_t *out_buf;
+    uint64_t cluster_offset;
+
+    if (nb_sectors != s->cluster_sectors)
+        return -EINVAL;
+
+    out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
+    if (!out_buf)
+        return -1;
+
+    /* best compression, small window, no zlib header */
+    memset(&strm, 0, sizeof(strm));
+    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
+                       Z_DEFLATED, -12,
+                       9, Z_DEFAULT_STRATEGY);
+    if (ret != 0) {
+        qemu_free(out_buf);
+        return -1;
+    }
+
+    strm.avail_in = s->cluster_size;
+    strm.next_in = (uint8_t *)buf;
+    strm.avail_out = s->cluster_size;
+    strm.next_out = out_buf;
+
+    ret = deflate(&strm, Z_FINISH);
+    if (ret != Z_STREAM_END && ret != Z_OK) {
+        qemu_free(out_buf);
+        deflateEnd(&strm);
+        return -1;
+    }
+    out_len = strm.next_out - out_buf;
+
+    deflateEnd(&strm);
+
+    if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+        /* could not compress: write normal cluster */
+        qcow_write(bs, sector_num, buf, s->cluster_sectors);
+    } else {
+        cluster_offset = get_cluster_offset(bs, sector_num << 9, 2,
+                                            out_len, 0, 0);
+        cluster_offset &= s->cluster_offset_mask;
+        if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) {
+            qemu_free(out_buf);
+            return -1;
+        }
+    }
+
+    qemu_free(out_buf);
+    return 0;
+}
+
+static void qcow_flush(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    bdrv_flush(s->hd);
+}
+
+static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+    BDRVQcowState *s = bs->opaque;
+    bdi->cluster_size = s->cluster_size;
+    return 0;
+}
+
+BlockDriver bdrv_qcow = {
+    "qcow",
+    sizeof(BDRVQcowState),
+    qcow_probe,
+    qcow_open,
+    NULL,
+    NULL,
+    qcow_close,
+    qcow_create,
+    qcow_flush,
+    qcow_is_allocated,
+    qcow_set_key,
+    qcow_make_empty,
+
+    .bdrv_aio_read = qcow_aio_read,
+    .bdrv_aio_write = qcow_aio_write,
+    .bdrv_aio_cancel = qcow_aio_cancel,
+    .aiocb_size = sizeof(QCowAIOCB),
+    .bdrv_write_compressed = qcow_write_compressed,
+    .bdrv_get_info = qcow_get_info,
+};
diff --git a/block-qcow2.c b/block-qcow2.c
new file mode 100644
index 0000000..5f0fbe8
--- /dev/null
+++ b/block-qcow2.c
@@ -0,0 +1,2620 @@
+/*
+ * Block driver for the QCOW version 2 format
+ *
+ * Copyright (c) 2004-2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+#include <zlib.h>
+#include "aes.h"
+#include <assert.h>
+
+/*
+  Differences with QCOW:
+
+  - Support for multiple incremental snapshots.
+  - Memory management by reference counts.
+  - Clusters which have a reference count of one have the bit
+    QCOW_OFLAG_COPIED to optimize write performance.
+  - Size of compressed clusters is stored in sectors to reduce bit usage
+    in the cluster offsets.
+  - Support for storing additional data (such as the VM state) in the
+    snapshots.
+  - If a backing store is used, the cluster size is not constrained
+    (could be backported to QCOW).
+  - L2 tables have always a size of one cluster.
+*/
+
+//#define DEBUG_ALLOC
+//#define DEBUG_ALLOC2
+
+#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
+#define QCOW_VERSION 2
+
+#define QCOW_CRYPT_NONE 0
+#define QCOW_CRYPT_AES  1
+
+#define QCOW_MAX_CRYPT_CLUSTERS 32
+
+/* indicate that the refcount of the referenced cluster is exactly one. */
+#define QCOW_OFLAG_COPIED     (1LL << 63)
+/* indicate that the cluster is compressed (they never have the copied flag) */
+#define QCOW_OFLAG_COMPRESSED (1LL << 62)
+
+#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
+
+typedef struct QCowHeader {
+    uint32_t magic;
+    uint32_t version;
+    uint64_t backing_file_offset;
+    uint32_t backing_file_size;
+    uint32_t cluster_bits;
+    uint64_t size; /* in bytes */
+    uint32_t crypt_method;
+    uint32_t l1_size; /* XXX: save number of clusters instead ? */
+    uint64_t l1_table_offset;
+    uint64_t refcount_table_offset;
+    uint32_t refcount_table_clusters;
+    uint32_t nb_snapshots;
+    uint64_t snapshots_offset;
+} QCowHeader;
+
+typedef struct __attribute__((packed)) QCowSnapshotHeader {
+    /* header is 8 byte aligned */
+    uint64_t l1_table_offset;
+
+    uint32_t l1_size;
+    uint16_t id_str_size;
+    uint16_t name_size;
+
+    uint32_t date_sec;
+    uint32_t date_nsec;
+
+    uint64_t vm_clock_nsec;
+
+    uint32_t vm_state_size;
+    uint32_t extra_data_size; /* for extension */
+    /* extra data follows */
+    /* id_str follows */
+    /* name follows  */
+} QCowSnapshotHeader;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct QCowSnapshot {
+    uint64_t l1_table_offset;
+    uint32_t l1_size;
+    char *id_str;
+    char *name;
+    uint32_t vm_state_size;
+    uint32_t date_sec;
+    uint32_t date_nsec;
+    uint64_t vm_clock_nsec;
+} QCowSnapshot;
+
+typedef struct BDRVQcowState {
+    BlockDriverState *hd;
+    int cluster_bits;
+    int cluster_size;
+    int cluster_sectors;
+    int l2_bits;
+    int l2_size;
+    int l1_size;
+    int l1_vm_state_index;
+    int csize_shift;
+    int csize_mask;
+    uint64_t cluster_offset_mask;
+    uint64_t l1_table_offset;
+    uint64_t *l1_table;
+    uint64_t *l2_cache;
+    uint64_t l2_cache_offsets[L2_CACHE_SIZE];
+    uint32_t l2_cache_counts[L2_CACHE_SIZE];
+    uint8_t *cluster_cache;
+    uint8_t *cluster_data;
+    uint64_t cluster_cache_offset;
+
+    uint64_t *refcount_table;
+    uint64_t refcount_table_offset;
+    uint32_t refcount_table_size;
+    uint64_t refcount_block_cache_offset;
+    uint16_t *refcount_block_cache;
+    int64_t free_cluster_index;
+    int64_t free_byte_offset;
+
+    uint32_t crypt_method; /* current crypt method, 0 if no key yet */
+    uint32_t crypt_method_header;
+    AES_KEY aes_encrypt_key;
+    AES_KEY aes_decrypt_key;
+    uint64_t snapshots_offset;
+    int snapshots_size;
+    int nb_snapshots;
+    QCowSnapshot *snapshots;
+} BDRVQcowState;
+
+static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset);
+static int qcow_read(BlockDriverState *bs, int64_t sector_num,
+                     uint8_t *buf, int nb_sectors);
+static int qcow_read_snapshots(BlockDriverState *bs);
+static void qcow_free_snapshots(BlockDriverState *bs);
+static int refcount_init(BlockDriverState *bs);
+static void refcount_close(BlockDriverState *bs);
+static int get_refcount(BlockDriverState *bs, int64_t cluster_index);
+static int update_cluster_refcount(BlockDriverState *bs,
+                                   int64_t cluster_index,
+                                   int addend);
+static void update_refcount(BlockDriverState *bs,
+                            int64_t offset, int64_t length,
+                            int addend);
+static int64_t alloc_clusters(BlockDriverState *bs, int64_t size);
+static int64_t alloc_bytes(BlockDriverState *bs, int size);
+static void free_clusters(BlockDriverState *bs,
+                          int64_t offset, int64_t size);
+#ifdef DEBUG_ALLOC
+static void check_refcounts(BlockDriverState *bs);
+#endif
+
+static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    const QCowHeader *cow_header = (const void *)buf;
+
+    if (buf_size >= sizeof(QCowHeader) &&
+        be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
+        be32_to_cpu(cow_header->version) == QCOW_VERSION)
+        return 100;
+    else
+        return 0;
+}
+
+static int qcow_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVQcowState *s = bs->opaque;
+    int len, i, shift, ret;
+    QCowHeader header;
+
+    ret = bdrv_file_open(&s->hd, filename, flags);
+    if (ret < 0)
+        return ret;
+    if (bdrv_pread(s->hd, 0, &header, sizeof(header)) != sizeof(header))
+        goto fail;
+    be32_to_cpus(&header.magic);
+    be32_to_cpus(&header.version);
+    be64_to_cpus(&header.backing_file_offset);
+    be32_to_cpus(&header.backing_file_size);
+    be64_to_cpus(&header.size);
+    be32_to_cpus(&header.cluster_bits);
+    be32_to_cpus(&header.crypt_method);
+    be64_to_cpus(&header.l1_table_offset);
+    be32_to_cpus(&header.l1_size);
+    be64_to_cpus(&header.refcount_table_offset);
+    be32_to_cpus(&header.refcount_table_clusters);
+    be64_to_cpus(&header.snapshots_offset);
+    be32_to_cpus(&header.nb_snapshots);
+
+    if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
+        goto fail;
+    if (header.size <= 1 ||
+        header.cluster_bits < 9 ||
+        header.cluster_bits > 16)
+        goto fail;
+    if (header.crypt_method > QCOW_CRYPT_AES)
+        goto fail;
+    s->crypt_method_header = header.crypt_method;
+    if (s->crypt_method_header)
+        bs->encrypted = 1;
+    s->cluster_bits = header.cluster_bits;
+    s->cluster_size = 1 << s->cluster_bits;
+    s->cluster_sectors = 1 << (s->cluster_bits - 9);
+    s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
+    s->l2_size = 1 << s->l2_bits;
+    bs->total_sectors = header.size / 512;
+    s->csize_shift = (62 - (s->cluster_bits - 8));
+    s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
+    s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
+    s->refcount_table_offset = header.refcount_table_offset;
+    s->refcount_table_size =
+        header.refcount_table_clusters << (s->cluster_bits - 3);
+
+    s->snapshots_offset = header.snapshots_offset;
+    s->nb_snapshots = header.nb_snapshots;
+
+    /* read the level 1 table */
+    s->l1_size = header.l1_size;
+    shift = s->cluster_bits + s->l2_bits;
+    s->l1_vm_state_index = (header.size + (1LL << shift) - 1) >> shift;
+    /* the L1 table must contain at least enough entries to put
+       header.size bytes */
+    if (s->l1_size < s->l1_vm_state_index)
+        goto fail;
+    s->l1_table_offset = header.l1_table_offset;
+    s->l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
+    if (!s->l1_table)
+        goto fail;
+    if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
+        s->l1_size * sizeof(uint64_t))
+        goto fail;
+    for(i = 0;i < s->l1_size; i++) {
+        be64_to_cpus(&s->l1_table[i]);
+    }
+    /* alloc L2 cache */
+    s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+    if (!s->l2_cache)
+        goto fail;
+    s->cluster_cache = qemu_malloc(s->cluster_size);
+    if (!s->cluster_cache)
+        goto fail;
+    /* one more sector for decompressed data alignment */
+    s->cluster_data = qemu_malloc(QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size
+                                  + 512);
+    if (!s->cluster_data)
+        goto fail;
+    s->cluster_cache_offset = -1;
+
+    if (refcount_init(bs) < 0)
+        goto fail;
+
+    /* read the backing file name */
+    if (header.backing_file_offset != 0) {
+        len = header.backing_file_size;
+        if (len > 1023)
+            len = 1023;
+        if (bdrv_pread(s->hd, header.backing_file_offset, bs->backing_file, len) != len)
+            goto fail;
+        bs->backing_file[len] = '\0';
+    }
+    if (qcow_read_snapshots(bs) < 0)
+        goto fail;
+
+#ifdef DEBUG_ALLOC
+    check_refcounts(bs);
+#endif
+    return 0;
+
+ fail:
+    qcow_free_snapshots(bs);
+    refcount_close(bs);
+    qemu_free(s->l1_table);
+    qemu_free(s->l2_cache);
+    qemu_free(s->cluster_cache);
+    qemu_free(s->cluster_data);
+    bdrv_delete(s->hd);
+    return -1;
+}
+
+static int qcow_set_key(BlockDriverState *bs, const char *key)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint8_t keybuf[16];
+    int len, i;
+
+    memset(keybuf, 0, 16);
+    len = strlen(key);
+    if (len > 16)
+        len = 16;
+    /* XXX: we could compress the chars to 7 bits to increase
+       entropy */
+    for(i = 0;i < len;i++) {
+        keybuf[i] = key[i];
+    }
+    s->crypt_method = s->crypt_method_header;
+
+    if (AES_set_encrypt_key(keybuf, 128, &s->aes_encrypt_key) != 0)
+        return -1;
+    if (AES_set_decrypt_key(keybuf, 128, &s->aes_decrypt_key) != 0)
+        return -1;
+#if 0
+    /* test */
+    {
+        uint8_t in[16];
+        uint8_t out[16];
+        uint8_t tmp[16];
+        for(i=0;i<16;i++)
+            in[i] = i;
+        AES_encrypt(in, tmp, &s->aes_encrypt_key);
+        AES_decrypt(tmp, out, &s->aes_decrypt_key);
+        for(i = 0; i < 16; i++)
+            printf(" %02x", tmp[i]);
+        printf("\n");
+        for(i = 0; i < 16; i++)
+            printf(" %02x", out[i]);
+        printf("\n");
+    }
+#endif
+    return 0;
+}
+
+/* The crypt function is compatible with the linux cryptoloop
+   algorithm for < 4 GB images. NOTE: out_buf == in_buf is
+   supported */
+static void encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
+                            uint8_t *out_buf, const uint8_t *in_buf,
+                            int nb_sectors, int enc,
+                            const AES_KEY *key)
+{
+    union {
+        uint64_t ll[2];
+        uint8_t b[16];
+    } ivec;
+    int i;
+
+    for(i = 0; i < nb_sectors; i++) {
+        ivec.ll[0] = cpu_to_le64(sector_num);
+        ivec.ll[1] = 0;
+        AES_cbc_encrypt(in_buf, out_buf, 512, key,
+                        ivec.b, enc);
+        sector_num++;
+        in_buf += 512;
+        out_buf += 512;
+    }
+}
+
+static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
+                        uint64_t cluster_offset, int n_start, int n_end)
+{
+    BDRVQcowState *s = bs->opaque;
+    int n, ret;
+
+    n = n_end - n_start;
+    if (n <= 0)
+        return 0;
+    ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n);
+    if (ret < 0)
+        return ret;
+    if (s->crypt_method) {
+        encrypt_sectors(s, start_sect + n_start,
+                        s->cluster_data,
+                        s->cluster_data, n, 1,
+                        &s->aes_encrypt_key);
+    }
+    ret = bdrv_write(s->hd, (cluster_offset >> 9) + n_start,
+                     s->cluster_data, n);
+    if (ret < 0)
+        return ret;
+    return 0;
+}
+
+static void l2_cache_reset(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+
+    memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
+    memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
+    memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
+}
+
+static inline int l2_cache_new_entry(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint32_t min_count;
+    int min_index, i;
+
+    /* find a new entry in the least used one */
+    min_index = 0;
+    min_count = 0xffffffff;
+    for(i = 0; i < L2_CACHE_SIZE; i++) {
+        if (s->l2_cache_counts[i] < min_count) {
+            min_count = s->l2_cache_counts[i];
+            min_index = i;
+        }
+    }
+    return min_index;
+}
+
+static int64_t align_offset(int64_t offset, int n)
+{
+    offset = (offset + n - 1) & ~(n - 1);
+    return offset;
+}
+
+static int grow_l1_table(BlockDriverState *bs, int min_size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int new_l1_size, new_l1_size2, ret, i;
+    uint64_t *new_l1_table;
+    uint64_t new_l1_table_offset;
+    uint64_t data64;
+    uint32_t data32;
+
+    new_l1_size = s->l1_size;
+    if (min_size <= new_l1_size)
+        return 0;
+    while (min_size > new_l1_size) {
+        new_l1_size = (new_l1_size * 3 + 1) / 2;
+    }
+#ifdef DEBUG_ALLOC2
+    printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
+#endif
+
+    new_l1_size2 = sizeof(uint64_t) * new_l1_size;
+    new_l1_table = qemu_mallocz(new_l1_size2);
+    if (!new_l1_table)
+        return -ENOMEM;
+    memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t));
+
+    /* write new table (align to cluster) */
+    new_l1_table_offset = alloc_clusters(bs, new_l1_size2);
+
+    for(i = 0; i < s->l1_size; i++)
+        new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
+    ret = bdrv_pwrite(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2);
+    if (ret != new_l1_size2)
+        goto fail;
+    for(i = 0; i < s->l1_size; i++)
+        new_l1_table[i] = be64_to_cpu(new_l1_table[i]);
+
+    /* set new table */
+    data64 = cpu_to_be64(new_l1_table_offset);
+    if (bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_table_offset),
+                    &data64, sizeof(data64)) != sizeof(data64))
+        goto fail;
+    data32 = cpu_to_be32(new_l1_size);
+    if (bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_size),
+                    &data32, sizeof(data32)) != sizeof(data32))
+        goto fail;
+    qemu_free(s->l1_table);
+    free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t));
+    s->l1_table_offset = new_l1_table_offset;
+    s->l1_table = new_l1_table;
+    s->l1_size = new_l1_size;
+    return 0;
+ fail:
+    qemu_free(s->l1_table);
+    return -EIO;
+}
+
+/*
+ * seek_l2_table
+ *
+ * seek l2_offset in the l2_cache table
+ * if not found, return NULL,
+ * if found,
+ *   increments the l2 cache hit count of the entry,
+ *   if counter overflow, divide by two all counters
+ *   return the pointer to the l2 cache entry
+ *
+ */
+
+static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset)
+{
+    int i, j;
+
+    for(i = 0; i < L2_CACHE_SIZE; i++) {
+        if (l2_offset == s->l2_cache_offsets[i]) {
+            /* increment the hit count */
+            if (++s->l2_cache_counts[i] == 0xffffffff) {
+                for(j = 0; j < L2_CACHE_SIZE; j++) {
+                    s->l2_cache_counts[j] >>= 1;
+                }
+            }
+            return s->l2_cache + (i << s->l2_bits);
+        }
+    }
+    return NULL;
+}
+
+/*
+ * l2_load
+ *
+ * Loads a L2 table into memory. If the table is in the cache, the cache
+ * is used; otherwise the L2 table is loaded from the image file.
+ *
+ * Returns a pointer to the L2 table on success, or NULL if the read from
+ * the image file failed.
+ */
+
+static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset)
+{
+    BDRVQcowState *s = bs->opaque;
+    int min_index;
+    uint64_t *l2_table;
+
+    /* seek if the table for the given offset is in the cache */
+
+    l2_table = seek_l2_table(s, l2_offset);
+    if (l2_table != NULL)
+        return l2_table;
+
+    /* not found: load a new entry in the least used one */
+
+    min_index = l2_cache_new_entry(bs);
+    l2_table = s->l2_cache + (min_index << s->l2_bits);
+    if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) !=
+        s->l2_size * sizeof(uint64_t))
+        return NULL;
+    s->l2_cache_offsets[min_index] = l2_offset;
+    s->l2_cache_counts[min_index] = 1;
+
+    return l2_table;
+}
+
+/*
+ * l2_allocate
+ *
+ * Allocate a new l2 entry in the file. If l1_index points to an already
+ * used entry in the L2 table (i.e. we are doing a copy on write for the L2
+ * table) copy the contents of the old L2 table into the newly allocated one.
+ * Otherwise the new table is initialized with zeros.
+ *
+ */
+
+static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index)
+{
+    BDRVQcowState *s = bs->opaque;
+    int min_index;
+    uint64_t old_l2_offset, tmp;
+    uint64_t *l2_table, l2_offset;
+
+    old_l2_offset = s->l1_table[l1_index];
+
+    /* allocate a new l2 entry */
+
+    l2_offset = alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
+
+    /* update the L1 entry */
+
+    s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
+
+    tmp = cpu_to_be64(l2_offset | QCOW_OFLAG_COPIED);
+    if (bdrv_pwrite(s->hd, s->l1_table_offset + l1_index * sizeof(tmp),
+                    &tmp, sizeof(tmp)) != sizeof(tmp))
+        return NULL;
+
+    /* allocate a new entry in the l2 cache */
+
+    min_index = l2_cache_new_entry(bs);
+    l2_table = s->l2_cache + (min_index << s->l2_bits);
+
+    if (old_l2_offset == 0) {
+        /* if there was no old l2 table, clear the new table */
+        memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
+    } else {
+        /* if there was an old l2 table, read it from the disk */
+        if (bdrv_pread(s->hd, old_l2_offset,
+                       l2_table, s->l2_size * sizeof(uint64_t)) !=
+            s->l2_size * sizeof(uint64_t))
+            return NULL;
+    }
+    /* write the l2 table to the file */
+    if (bdrv_pwrite(s->hd, l2_offset,
+                    l2_table, s->l2_size * sizeof(uint64_t)) !=
+        s->l2_size * sizeof(uint64_t))
+        return NULL;
+
+    /* update the l2 cache entry */
+
+    s->l2_cache_offsets[min_index] = l2_offset;
+    s->l2_cache_counts[min_index] = 1;
+
+    return l2_table;
+}
+
+/*
+ * get_cluster_offset
+ *
+ * For a given offset of the disk image, return cluster offset in
+ * qcow2 file.
+ *
+ * on entry, *num is the number of contiguous clusters we'd like to
+ * access following offset.
+ *
+ * on exit, *num is the number of contiguous clusters we can read.
+ *
+ * Return 1, if the offset is found
+ * Return 0, otherwise.
+ *
+ */
+
+static uint64_t get_cluster_offset(BlockDriverState *bs,
+                                   uint64_t offset, int *num)
+{
+    BDRVQcowState *s = bs->opaque;
+    int l1_index, l2_index;
+    uint64_t l2_offset, *l2_table, cluster_offset, next;
+    int l1_bits;
+    int index_in_cluster, nb_available, nb_needed;
+
+    index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1);
+    nb_needed = *num + index_in_cluster;
+
+    l1_bits = s->l2_bits + s->cluster_bits;
+
+    /* compute how many bytes there are between the offset and
+     * and the end of the l1 entry
+     */
+
+    nb_available = (1 << l1_bits) - (offset & ((1 << l1_bits) - 1));
+
+    /* compute the number of available sectors */
+
+    nb_available = (nb_available >> 9) + index_in_cluster;
+
+    cluster_offset = 0;
+
+    /* seek the the l2 offset in the l1 table */
+
+    l1_index = offset >> l1_bits;
+    if (l1_index >= s->l1_size)
+        goto out;
+
+    l2_offset = s->l1_table[l1_index];
+
+    /* seek the l2 table of the given l2 offset */
+
+    if (!l2_offset)
+        goto out;
+
+    /* load the l2 table in memory */
+
+    l2_offset &= ~QCOW_OFLAG_COPIED;
+    l2_table = l2_load(bs, l2_offset);
+    if (l2_table == NULL)
+        return 0;
+
+    /* find the cluster offset for the given disk offset */
+
+    l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+    cluster_offset = be64_to_cpu(l2_table[l2_index]);
+    nb_available = s->cluster_sectors;
+    l2_index++;
+
+    if (!cluster_offset) {
+
+       /* how many empty clusters ? */
+
+       while (nb_available < nb_needed && !l2_table[l2_index]) {
+           l2_index++;
+           nb_available += s->cluster_sectors;
+       }
+    } else {
+
+       /* how many allocated clusters ? */
+
+       cluster_offset &= ~QCOW_OFLAG_COPIED;
+       while (nb_available < nb_needed) {
+           next = be64_to_cpu(l2_table[l2_index]) & ~QCOW_OFLAG_COPIED;
+           if (next != cluster_offset + (nb_available << 9))
+               break;
+           l2_index++;
+           nb_available += s->cluster_sectors;
+       }
+   }
+
+out:
+    if (nb_available > nb_needed)
+        nb_available = nb_needed;
+
+    *num = nb_available - index_in_cluster;
+
+    return cluster_offset;
+}
+
+/*
+ * free_any_clusters
+ *
+ * free clusters according to its type: compressed or not
+ *
+ */
+
+static void free_any_clusters(BlockDriverState *bs,
+                              uint64_t cluster_offset, int nb_clusters)
+{
+    BDRVQcowState *s = bs->opaque;
+
+    /* free the cluster */
+
+    if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+        int nb_csectors;
+        nb_csectors = ((cluster_offset >> s->csize_shift) &
+                       s->csize_mask) + 1;
+        free_clusters(bs, (cluster_offset & s->cluster_offset_mask) & ~511,
+                      nb_csectors * 512);
+        return;
+    }
+
+    free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits);
+
+    return;
+}
+
+/*
+ * get_cluster_table
+ *
+ * for a given disk offset, load (and allocate if needed)
+ * the l2 table.
+ *
+ * the l2 table offset in the qcow2 file and the cluster index
+ * in the l2 table are given to the caller.
+ *
+ */
+
+static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
+                             uint64_t **new_l2_table,
+                             uint64_t *new_l2_offset,
+                             int *new_l2_index)
+{
+    BDRVQcowState *s = bs->opaque;
+    int l1_index, l2_index, ret;
+    uint64_t l2_offset, *l2_table;
+
+    /* seek the the l2 offset in the l1 table */
+
+    l1_index = offset >> (s->l2_bits + s->cluster_bits);
+    if (l1_index >= s->l1_size) {
+        ret = grow_l1_table(bs, l1_index + 1);
+        if (ret < 0)
+            return 0;
+    }
+    l2_offset = s->l1_table[l1_index];
+
+    /* seek the l2 table of the given l2 offset */
+
+    if (l2_offset & QCOW_OFLAG_COPIED) {
+        /* load the l2 table in memory */
+        l2_offset &= ~QCOW_OFLAG_COPIED;
+        l2_table = l2_load(bs, l2_offset);
+        if (l2_table == NULL)
+            return 0;
+    } else {
+        if (l2_offset)
+            free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
+        l2_table = l2_allocate(bs, l1_index);
+        if (l2_table == NULL)
+            return 0;
+        l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
+    }
+
+    /* find the cluster offset for the given disk offset */
+
+    l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
+
+    *new_l2_table = l2_table;
+    *new_l2_offset = l2_offset;
+    *new_l2_index = l2_index;
+
+    return 1;
+}
+
+/*
+ * alloc_compressed_cluster_offset
+ *
+ * For a given offset of the disk image, return cluster offset in
+ * qcow2 file.
+ *
+ * If the offset is not found, allocate a new compressed cluster.
+ *
+ * Return the cluster offset if successful,
+ * Return 0, otherwise.
+ *
+ */
+
+static uint64_t alloc_compressed_cluster_offset(BlockDriverState *bs,
+                                                uint64_t offset,
+                                                int compressed_size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int l2_index, ret;
+    uint64_t l2_offset, *l2_table, cluster_offset;
+    int nb_csectors;
+
+    ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
+    if (ret == 0)
+        return 0;
+
+    cluster_offset = be64_to_cpu(l2_table[l2_index]);
+    if (cluster_offset & QCOW_OFLAG_COPIED)
+        return cluster_offset & ~QCOW_OFLAG_COPIED;
+
+    if (cluster_offset)
+        free_any_clusters(bs, cluster_offset, 1);
+
+    cluster_offset = alloc_bytes(bs, compressed_size);
+    nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) -
+                  (cluster_offset >> 9);
+
+    cluster_offset |= QCOW_OFLAG_COMPRESSED |
+                      ((uint64_t)nb_csectors << s->csize_shift);
+
+    /* update L2 table */
+
+    /* compressed clusters never have the copied flag */
+
+    l2_table[l2_index] = cpu_to_be64(cluster_offset);
+    if (bdrv_pwrite(s->hd,
+                    l2_offset + l2_index * sizeof(uint64_t),
+                    l2_table + l2_index,
+                    sizeof(uint64_t)) != sizeof(uint64_t))
+        return 0;
+
+    return cluster_offset;
+}
+
+/*
+ * alloc_cluster_offset
+ *
+ * For a given offset of the disk image, return cluster offset in
+ * qcow2 file.
+ *
+ * If the offset is not found, allocate a new cluster.
+ *
+ * Return the cluster offset if successful,
+ * Return 0, otherwise.
+ *
+ */
+
+static uint64_t alloc_cluster_offset(BlockDriverState *bs,
+                                     uint64_t offset,
+                                     int n_start, int n_end,
+                                     int *num)
+{
+    BDRVQcowState *s = bs->opaque;
+    int l2_index, ret;
+    uint64_t l2_offset, *l2_table, cluster_offset;
+    int nb_available, nb_clusters, i, j;
+    uint64_t start_sect, current;
+
+    ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
+    if (ret == 0)
+        return 0;
+
+    nb_clusters = ((n_end << 9) + s->cluster_size - 1) >>
+                  s->cluster_bits;
+    if (nb_clusters > s->l2_size - l2_index)
+            nb_clusters = s->l2_size - l2_index;
+
+    cluster_offset = be64_to_cpu(l2_table[l2_index]);
+
+    /* We keep all QCOW_OFLAG_COPIED clusters */
+
+    if (cluster_offset & QCOW_OFLAG_COPIED) {
+
+        for (i = 1; i < nb_clusters; i++) {
+            current = be64_to_cpu(l2_table[l2_index + i]);
+            if (cluster_offset + (i << s->cluster_bits) != current)
+                break;
+        }
+        nb_clusters = i;
+
+        nb_available = nb_clusters << (s->cluster_bits - 9);
+        if (nb_available > n_end)
+            nb_available = n_end;
+
+        cluster_offset &= ~QCOW_OFLAG_COPIED;
+
+        goto out;
+    }
+
+    /* for the moment, multiple compressed clusters are not managed */
+
+    if (cluster_offset & QCOW_OFLAG_COMPRESSED)
+        nb_clusters = 1;
+
+    /* how many available clusters ? */
+
+    i = 0;
+    while (i < nb_clusters) {
+
+        i++;
+
+        if (!cluster_offset) {
+
+            /* how many free clusters ? */
+
+            while (i < nb_clusters) {
+                cluster_offset = l2_table[l2_index + i];
+                if (cluster_offset != 0)
+                    break;
+                i++;
+            }
+
+            if ((cluster_offset & QCOW_OFLAG_COPIED) ||
+                (cluster_offset & QCOW_OFLAG_COMPRESSED))
+                break;
+
+        } else {
+
+            /* how many contiguous clusters ? */
+
+            j = 1;
+            current = 0;
+            while (i < nb_clusters) {
+                current = be64_to_cpu(l2_table[l2_index + i]);
+                if (cluster_offset + (j << s->cluster_bits) != current)
+                    break;
+
+                i++;
+                j++;
+            }
+
+            free_any_clusters(bs, cluster_offset, j);
+            if (current)
+                break;
+            cluster_offset = current;
+        }
+    }
+    nb_clusters = i;
+
+    /* allocate a new cluster */
+
+    cluster_offset = alloc_clusters(bs, nb_clusters * s->cluster_size);
+
+    /* we must initialize the cluster content which won't be
+       written */
+
+    nb_available = nb_clusters << (s->cluster_bits - 9);
+    if (nb_available > n_end)
+        nb_available = n_end;
+
+    /* copy content of unmodified sectors */
+
+    start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
+    if (n_start) {
+        ret = copy_sectors(bs, start_sect, cluster_offset, 0, n_start);
+        if (ret < 0)
+            return 0;
+    }
+
+    if (nb_available & (s->cluster_sectors - 1)) {
+        uint64_t end = nb_available & ~(uint64_t)(s->cluster_sectors - 1);
+        ret = copy_sectors(bs, start_sect + end,
+                           cluster_offset + (end << 9),
+                           nb_available - end,
+                           s->cluster_sectors);
+        if (ret < 0)
+            return 0;
+    }
+
+    /* update L2 table */
+
+    for (i = 0; i < nb_clusters; i++)
+        l2_table[l2_index + i] = cpu_to_be64((cluster_offset +
+                                             (i << s->cluster_bits)) |
+                                             QCOW_OFLAG_COPIED);
+
+    if (bdrv_pwrite(s->hd,
+                    l2_offset + l2_index * sizeof(uint64_t),
+                    l2_table + l2_index,
+                    nb_clusters * sizeof(uint64_t)) !=
+                    nb_clusters * sizeof(uint64_t))
+        return 0;
+
+out:
+    *num = nb_available - n_start;
+
+    return cluster_offset;
+}
+
+static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
+                             int nb_sectors, int *pnum)
+{
+    uint64_t cluster_offset;
+
+    *pnum = nb_sectors;
+    cluster_offset = get_cluster_offset(bs, sector_num << 9, pnum);
+
+    return (cluster_offset != 0);
+}
+
+static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
+                             const uint8_t *buf, int buf_size)
+{
+    z_stream strm1, *strm = &strm1;
+    int ret, out_len;
+
+    memset(strm, 0, sizeof(*strm));
+
+    strm->next_in = (uint8_t *)buf;
+    strm->avail_in = buf_size;
+    strm->next_out = out_buf;
+    strm->avail_out = out_buf_size;
+
+    ret = inflateInit2(strm, -12);
+    if (ret != Z_OK)
+        return -1;
+    ret = inflate(strm, Z_FINISH);
+    out_len = strm->next_out - out_buf;
+    if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) ||
+        out_len != out_buf_size) {
+        inflateEnd(strm);
+        return -1;
+    }
+    inflateEnd(strm);
+    return 0;
+}
+
+static int decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset)
+{
+    int ret, csize, nb_csectors, sector_offset;
+    uint64_t coffset;
+
+    coffset = cluster_offset & s->cluster_offset_mask;
+    if (s->cluster_cache_offset != coffset) {
+        nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1;
+        sector_offset = coffset & 511;
+        csize = nb_csectors * 512 - sector_offset;
+        ret = bdrv_read(s->hd, coffset >> 9, s->cluster_data, nb_csectors);
+        if (ret < 0) {
+            return -1;
+        }
+        if (decompress_buffer(s->cluster_cache, s->cluster_size,
+                              s->cluster_data + sector_offset, csize) < 0) {
+            return -1;
+        }
+        s->cluster_cache_offset = coffset;
+    }
+    return 0;
+}
+
+/* handle reading after the end of the backing file */
+static int backing_read1(BlockDriverState *bs,
+                         int64_t sector_num, uint8_t *buf, int nb_sectors)
+{
+    int n1;
+    if ((sector_num + nb_sectors) <= bs->total_sectors)
+        return nb_sectors;
+    if (sector_num >= bs->total_sectors)
+        n1 = 0;
+    else
+        n1 = bs->total_sectors - sector_num;
+    memset(buf + n1 * 512, 0, 512 * (nb_sectors - n1));
+    return n1;
+}
+
+static int qcow_read(BlockDriverState *bs, int64_t sector_num,
+                     uint8_t *buf, int nb_sectors)
+{
+    BDRVQcowState *s = bs->opaque;
+    int ret, index_in_cluster, n, n1;
+    uint64_t cluster_offset;
+
+    while (nb_sectors > 0) {
+        n = nb_sectors;
+        cluster_offset = get_cluster_offset(bs, sector_num << 9, &n);
+        index_in_cluster = sector_num & (s->cluster_sectors - 1);
+        if (!cluster_offset) {
+            if (bs->backing_hd) {
+                /* read from the base image */
+                n1 = backing_read1(bs->backing_hd, sector_num, buf, n);
+                if (n1 > 0) {
+                    ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
+                    if (ret < 0)
+                        return -1;
+                }
+            } else {
+                memset(buf, 0, 512 * n);
+            }
+        } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
+            if (decompress_cluster(s, cluster_offset) < 0)
+                return -1;
+            memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
+        } else {
+            ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
+            if (ret != n * 512)
+                return -1;
+            if (s->crypt_method) {
+                encrypt_sectors(s, sector_num, buf, buf, n, 0,
+                                &s->aes_decrypt_key);
+            }
+        }
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+    }
+    return 0;
+}
+
+static int qcow_write(BlockDriverState *bs, int64_t sector_num,
+                     const uint8_t *buf, int nb_sectors)
+{
+    BDRVQcowState *s = bs->opaque;
+    int ret, index_in_cluster, n;
+    uint64_t cluster_offset;
+    int n_end;
+
+    while (nb_sectors > 0) {
+        index_in_cluster = sector_num & (s->cluster_sectors - 1);
+        n_end = index_in_cluster + nb_sectors;
+        if (s->crypt_method &&
+            n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors)
+            n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
+        cluster_offset = alloc_cluster_offset(bs, sector_num << 9,
+                                              index_in_cluster,
+                                              n_end, &n);
+        if (!cluster_offset)
+            return -1;
+        if (s->crypt_method) {
+            encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1,
+                            &s->aes_encrypt_key);
+            ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512,
+                              s->cluster_data, n * 512);
+        } else {
+            ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512);
+        }
+        if (ret != n * 512)
+            return -1;
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+    }
+    s->cluster_cache_offset = -1; /* disable compressed cache */
+    return 0;
+}
+
+typedef struct QCowAIOCB {
+    BlockDriverAIOCB common;
+    int64_t sector_num;
+    uint8_t *buf;
+    int nb_sectors;
+    int n;
+    uint64_t cluster_offset;
+    uint8_t *cluster_data;
+    BlockDriverAIOCB *hd_aiocb;
+} QCowAIOCB;
+
+static void qcow_aio_read_cb(void *opaque, int ret)
+{
+    QCowAIOCB *acb = opaque;
+    BlockDriverState *bs = acb->common.bs;
+    BDRVQcowState *s = bs->opaque;
+    int index_in_cluster, n1;
+
+    acb->hd_aiocb = NULL;
+    if (ret < 0) {
+    fail:
+        acb->common.cb(acb->common.opaque, ret);
+        qemu_aio_release(acb);
+        return;
+    }
+
+ redo:
+    /* post process the read buffer */
+    if (!acb->cluster_offset) {
+        /* nothing to do */
+    } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+        /* nothing to do */
+    } else {
+        if (s->crypt_method) {
+            encrypt_sectors(s, acb->sector_num, acb->buf, acb->buf,
+                            acb->n, 0,
+                            &s->aes_decrypt_key);
+        }
+    }
+
+    acb->nb_sectors -= acb->n;
+    acb->sector_num += acb->n;
+    acb->buf += acb->n * 512;
+
+    if (acb->nb_sectors == 0) {
+        /* request completed */
+        acb->common.cb(acb->common.opaque, 0);
+        qemu_aio_release(acb);
+        return;
+    }
+
+    /* prepare next AIO request */
+    acb->n = acb->nb_sectors;
+    acb->cluster_offset = get_cluster_offset(bs, acb->sector_num << 9, &acb->n);
+    index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+
+    if (!acb->cluster_offset) {
+        if (bs->backing_hd) {
+            /* read from the base image */
+            n1 = backing_read1(bs->backing_hd, acb->sector_num,
+                               acb->buf, acb->n);
+            if (n1 > 0) {
+                acb->hd_aiocb = bdrv_aio_read(bs->backing_hd, acb->sector_num,
+                                    acb->buf, acb->n, qcow_aio_read_cb, acb);
+                if (acb->hd_aiocb == NULL)
+                    goto fail;
+            } else {
+                goto redo;
+            }
+        } else {
+            /* Note: in this case, no need to wait */
+            memset(acb->buf, 0, 512 * acb->n);
+            goto redo;
+        }
+    } else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
+        /* add AIO support for compressed blocks ? */
+        if (decompress_cluster(s, acb->cluster_offset) < 0)
+            goto fail;
+        memcpy(acb->buf,
+               s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
+        goto redo;
+    } else {
+        if ((acb->cluster_offset & 511) != 0) {
+            ret = -EIO;
+            goto fail;
+        }
+        acb->hd_aiocb = bdrv_aio_read(s->hd,
+                            (acb->cluster_offset >> 9) + index_in_cluster,
+                            acb->buf, acb->n, qcow_aio_read_cb, acb);
+        if (acb->hd_aiocb == NULL)
+            goto fail;
+    }
+}
+
+static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    QCowAIOCB *acb;
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (!acb)
+        return NULL;
+    acb->hd_aiocb = NULL;
+    acb->sector_num = sector_num;
+    acb->buf = buf;
+    acb->nb_sectors = nb_sectors;
+    acb->n = 0;
+    acb->cluster_offset = 0;
+    return acb;
+}
+
+static BlockDriverAIOCB *qcow_aio_read(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    QCowAIOCB *acb;
+
+    acb = qcow_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
+    if (!acb)
+        return NULL;
+
+    qcow_aio_read_cb(acb, 0);
+    return &acb->common;
+}
+
+static void qcow_aio_write_cb(void *opaque, int ret)
+{
+    QCowAIOCB *acb = opaque;
+    BlockDriverState *bs = acb->common.bs;
+    BDRVQcowState *s = bs->opaque;
+    int index_in_cluster;
+    uint64_t cluster_offset;
+    const uint8_t *src_buf;
+    int n_end;
+
+    acb->hd_aiocb = NULL;
+
+    if (ret < 0) {
+    fail:
+        acb->common.cb(acb->common.opaque, ret);
+        qemu_aio_release(acb);
+        return;
+    }
+
+    acb->nb_sectors -= acb->n;
+    acb->sector_num += acb->n;
+    acb->buf += acb->n * 512;
+
+    if (acb->nb_sectors == 0) {
+        /* request completed */
+        acb->common.cb(acb->common.opaque, 0);
+        qemu_aio_release(acb);
+        return;
+    }
+
+    index_in_cluster = acb->sector_num & (s->cluster_sectors - 1);
+    n_end = index_in_cluster + acb->nb_sectors;
+    if (s->crypt_method &&
+        n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors)
+        n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors;
+
+    cluster_offset = alloc_cluster_offset(bs, acb->sector_num << 9,
+                                          index_in_cluster,
+                                          n_end, &acb->n);
+    if (!cluster_offset || (cluster_offset & 511) != 0) {
+        ret = -EIO;
+        goto fail;
+    }
+    if (s->crypt_method) {
+        if (!acb->cluster_data) {
+            acb->cluster_data = qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS *
+                                             s->cluster_size);
+            if (!acb->cluster_data) {
+                ret = -ENOMEM;
+                goto fail;
+            }
+        }
+        encrypt_sectors(s, acb->sector_num, acb->cluster_data, acb->buf,
+                        acb->n, 1, &s->aes_encrypt_key);
+        src_buf = acb->cluster_data;
+    } else {
+        src_buf = acb->buf;
+    }
+    acb->hd_aiocb = bdrv_aio_write(s->hd,
+                                   (cluster_offset >> 9) + index_in_cluster,
+                                   src_buf, acb->n,
+                                   qcow_aio_write_cb, acb);
+    if (acb->hd_aiocb == NULL)
+        goto fail;
+}
+
+static BlockDriverAIOCB *qcow_aio_write(BlockDriverState *bs,
+        int64_t sector_num, const uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowAIOCB *acb;
+
+    s->cluster_cache_offset = -1; /* disable compressed cache */
+
+    acb = qcow_aio_setup(bs, sector_num, (uint8_t*)buf, nb_sectors, cb, opaque);
+    if (!acb)
+        return NULL;
+
+    qcow_aio_write_cb(acb, 0);
+    return &acb->common;
+}
+
+static void qcow_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    QCowAIOCB *acb = (QCowAIOCB *)blockacb;
+    if (acb->hd_aiocb)
+        bdrv_aio_cancel(acb->hd_aiocb);
+    qemu_aio_release(acb);
+}
+
+static void qcow_close(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    qemu_free(s->l1_table);
+    qemu_free(s->l2_cache);
+    qemu_free(s->cluster_cache);
+    qemu_free(s->cluster_data);
+    refcount_close(bs);
+    bdrv_delete(s->hd);
+}
+
+/* XXX: use std qcow open function ? */
+typedef struct QCowCreateState {
+    int cluster_size;
+    int cluster_bits;
+    uint16_t *refcount_block;
+    uint64_t *refcount_table;
+    int64_t l1_table_offset;
+    int64_t refcount_table_offset;
+    int64_t refcount_block_offset;
+} QCowCreateState;
+
+static void create_refcount_update(QCowCreateState *s,
+                                   int64_t offset, int64_t size)
+{
+    int refcount;
+    int64_t start, last, cluster_offset;
+    uint16_t *p;
+
+    start = offset & ~(s->cluster_size - 1);
+    last = (offset + size - 1)  & ~(s->cluster_size - 1);
+    for(cluster_offset = start; cluster_offset <= last;
+        cluster_offset += s->cluster_size) {
+        p = &s->refcount_block[cluster_offset >> s->cluster_bits];
+        refcount = be16_to_cpu(*p);
+        refcount++;
+        *p = cpu_to_be16(refcount);
+    }
+}
+
+static int qcow_create(const char *filename, int64_t total_size,
+                      const char *backing_file, int flags)
+{
+    int fd, header_size, backing_filename_len, l1_size, i, shift, l2_bits;
+    QCowHeader header;
+    uint64_t tmp, offset;
+    QCowCreateState s1, *s = &s1;
+
+    memset(s, 0, sizeof(*s));
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
+    if (fd < 0)
+        return -1;
+    memset(&header, 0, sizeof(header));
+    header.magic = cpu_to_be32(QCOW_MAGIC);
+    header.version = cpu_to_be32(QCOW_VERSION);
+    header.size = cpu_to_be64(total_size * 512);
+    header_size = sizeof(header);
+    backing_filename_len = 0;
+    if (backing_file) {
+        header.backing_file_offset = cpu_to_be64(header_size);
+        backing_filename_len = strlen(backing_file);
+        header.backing_file_size = cpu_to_be32(backing_filename_len);
+        header_size += backing_filename_len;
+    }
+    s->cluster_bits = 12;  /* 4 KB clusters */
+    s->cluster_size = 1 << s->cluster_bits;
+    header.cluster_bits = cpu_to_be32(s->cluster_bits);
+    header_size = (header_size + 7) & ~7;
+    if (flags & BLOCK_FLAG_ENCRYPT) {
+        header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
+    } else {
+        header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE);
+    }
+    l2_bits = s->cluster_bits - 3;
+    shift = s->cluster_bits + l2_bits;
+    l1_size = (((total_size * 512) + (1LL << shift) - 1) >> shift);
+    offset = align_offset(header_size, s->cluster_size);
+    s->l1_table_offset = offset;
+    header.l1_table_offset = cpu_to_be64(s->l1_table_offset);
+    header.l1_size = cpu_to_be32(l1_size);
+    offset += align_offset(l1_size * sizeof(uint64_t), s->cluster_size);
+
+    s->refcount_table = qemu_mallocz(s->cluster_size);
+    if (!s->refcount_table)
+        goto fail;
+    s->refcount_block = qemu_mallocz(s->cluster_size);
+    if (!s->refcount_block)
+        goto fail;
+
+    s->refcount_table_offset = offset;
+    header.refcount_table_offset = cpu_to_be64(offset);
+    header.refcount_table_clusters = cpu_to_be32(1);
+    offset += s->cluster_size;
+
+    s->refcount_table[0] = cpu_to_be64(offset);
+    s->refcount_block_offset = offset;
+    offset += s->cluster_size;
+
+    /* update refcounts */
+    create_refcount_update(s, 0, header_size);
+    create_refcount_update(s, s->l1_table_offset, l1_size * sizeof(uint64_t));
+    create_refcount_update(s, s->refcount_table_offset, s->cluster_size);
+    create_refcount_update(s, s->refcount_block_offset, s->cluster_size);
+
+    /* write all the data */
+    write(fd, &header, sizeof(header));
+    if (backing_file) {
+        write(fd, backing_file, backing_filename_len);
+    }
+    lseek(fd, s->l1_table_offset, SEEK_SET);
+    tmp = 0;
+    for(i = 0;i < l1_size; i++) {
+        write(fd, &tmp, sizeof(tmp));
+    }
+    lseek(fd, s->refcount_table_offset, SEEK_SET);
+    write(fd, s->refcount_table, s->cluster_size);
+
+    lseek(fd, s->refcount_block_offset, SEEK_SET);
+    write(fd, s->refcount_block, s->cluster_size);
+
+    qemu_free(s->refcount_table);
+    qemu_free(s->refcount_block);
+    close(fd);
+    return 0;
+ fail:
+    qemu_free(s->refcount_table);
+    qemu_free(s->refcount_block);
+    close(fd);
+    return -ENOMEM;
+}
+
+static int qcow_make_empty(BlockDriverState *bs)
+{
+#if 0
+    /* XXX: not correct */
+    BDRVQcowState *s = bs->opaque;
+    uint32_t l1_length = s->l1_size * sizeof(uint64_t);
+    int ret;
+
+    memset(s->l1_table, 0, l1_length);
+    if (bdrv_pwrite(s->hd, s->l1_table_offset, s->l1_table, l1_length) < 0)
+	return -1;
+    ret = bdrv_truncate(s->hd, s->l1_table_offset + l1_length);
+    if (ret < 0)
+        return ret;
+
+    l2_cache_reset(bs);
+#endif
+    return 0;
+}
+
+/* XXX: put compressed sectors first, then all the cluster aligned
+   tables to avoid losing bytes in alignment */
+static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
+                                 const uint8_t *buf, int nb_sectors)
+{
+    BDRVQcowState *s = bs->opaque;
+    z_stream strm;
+    int ret, out_len;
+    uint8_t *out_buf;
+    uint64_t cluster_offset;
+
+    if (nb_sectors == 0) {
+        /* align end of file to a sector boundary to ease reading with
+           sector based I/Os */
+        cluster_offset = bdrv_getlength(s->hd);
+        cluster_offset = (cluster_offset + 511) & ~511;
+        bdrv_truncate(s->hd, cluster_offset);
+        return 0;
+    }
+
+    if (nb_sectors != s->cluster_sectors)
+        return -EINVAL;
+
+    out_buf = qemu_malloc(s->cluster_size + (s->cluster_size / 1000) + 128);
+    if (!out_buf)
+        return -ENOMEM;
+
+    /* best compression, small window, no zlib header */
+    memset(&strm, 0, sizeof(strm));
+    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION,
+                       Z_DEFLATED, -12,
+                       9, Z_DEFAULT_STRATEGY);
+    if (ret != 0) {
+        qemu_free(out_buf);
+        return -1;
+    }
+
+    strm.avail_in = s->cluster_size;
+    strm.next_in = (uint8_t *)buf;
+    strm.avail_out = s->cluster_size;
+    strm.next_out = out_buf;
+
+    ret = deflate(&strm, Z_FINISH);
+    if (ret != Z_STREAM_END && ret != Z_OK) {
+        qemu_free(out_buf);
+        deflateEnd(&strm);
+        return -1;
+    }
+    out_len = strm.next_out - out_buf;
+
+    deflateEnd(&strm);
+
+    if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
+        /* could not compress: write normal cluster */
+        qcow_write(bs, sector_num, buf, s->cluster_sectors);
+    } else {
+        cluster_offset = alloc_compressed_cluster_offset(bs, sector_num << 9,
+                                              out_len);
+        if (!cluster_offset)
+            return -1;
+        cluster_offset &= s->cluster_offset_mask;
+        if (bdrv_pwrite(s->hd, cluster_offset, out_buf, out_len) != out_len) {
+            qemu_free(out_buf);
+            return -1;
+        }
+    }
+
+    qemu_free(out_buf);
+    return 0;
+}
+
+static void qcow_flush(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    bdrv_flush(s->hd);
+}
+
+static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+    BDRVQcowState *s = bs->opaque;
+    bdi->cluster_size = s->cluster_size;
+    bdi->vm_state_offset = (int64_t)s->l1_vm_state_index <<
+        (s->cluster_bits + s->l2_bits);
+    return 0;
+}
+
+/*********************************************************/
+/* snapshot support */
+
+/* update the refcounts of snapshots and the copied flag */
+static int update_snapshot_refcount(BlockDriverState *bs,
+                                    int64_t l1_table_offset,
+                                    int l1_size,
+                                    int addend)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
+    int64_t old_offset, old_l2_offset;
+    int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount;
+
+    l2_cache_reset(bs);
+
+    l2_table = NULL;
+    l1_table = NULL;
+    l1_size2 = l1_size * sizeof(uint64_t);
+    l1_allocated = 0;
+    if (l1_table_offset != s->l1_table_offset) {
+        l1_table = qemu_malloc(l1_size2);
+        if (!l1_table)
+            goto fail;
+        l1_allocated = 1;
+        if (bdrv_pread(s->hd, l1_table_offset,
+                       l1_table, l1_size2) != l1_size2)
+            goto fail;
+        for(i = 0;i < l1_size; i++)
+            be64_to_cpus(&l1_table[i]);
+    } else {
+        assert(l1_size == s->l1_size);
+        l1_table = s->l1_table;
+        l1_allocated = 0;
+    }
+
+    l2_size = s->l2_size * sizeof(uint64_t);
+    l2_table = qemu_malloc(l2_size);
+    if (!l2_table)
+        goto fail;
+    l1_modified = 0;
+    for(i = 0; i < l1_size; i++) {
+        l2_offset = l1_table[i];
+        if (l2_offset) {
+            old_l2_offset = l2_offset;
+            l2_offset &= ~QCOW_OFLAG_COPIED;
+            l2_modified = 0;
+            if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size)
+                goto fail;
+            for(j = 0; j < s->l2_size; j++) {
+                offset = be64_to_cpu(l2_table[j]);
+                if (offset != 0) {
+                    old_offset = offset;
+                    offset &= ~QCOW_OFLAG_COPIED;
+                    if (offset & QCOW_OFLAG_COMPRESSED) {
+                        nb_csectors = ((offset >> s->csize_shift) &
+                                       s->csize_mask) + 1;
+                        if (addend != 0)
+                            update_refcount(bs, (offset & s->cluster_offset_mask) & ~511,
+                                            nb_csectors * 512, addend);
+                        /* compressed clusters are never modified */
+                        refcount = 2;
+                    } else {
+                        if (addend != 0) {
+                            refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend);
+                        } else {
+                            refcount = get_refcount(bs, offset >> s->cluster_bits);
+                        }
+                    }
+
+                    if (refcount == 1) {
+                        offset |= QCOW_OFLAG_COPIED;
+                    }
+                    if (offset != old_offset) {
+                        l2_table[j] = cpu_to_be64(offset);
+                        l2_modified = 1;
+                    }
+                }
+            }
+            if (l2_modified) {
+                if (bdrv_pwrite(s->hd,
+                                l2_offset, l2_table, l2_size) != l2_size)
+                    goto fail;
+            }
+
+            if (addend != 0) {
+                refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
+            } else {
+                refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
+            }
+            if (refcount == 1) {
+                l2_offset |= QCOW_OFLAG_COPIED;
+            }
+            if (l2_offset != old_l2_offset) {
+                l1_table[i] = l2_offset;
+                l1_modified = 1;
+            }
+        }
+    }
+    if (l1_modified) {
+        for(i = 0; i < l1_size; i++)
+            cpu_to_be64s(&l1_table[i]);
+        if (bdrv_pwrite(s->hd, l1_table_offset, l1_table,
+                        l1_size2) != l1_size2)
+            goto fail;
+        for(i = 0; i < l1_size; i++)
+            be64_to_cpus(&l1_table[i]);
+    }
+    if (l1_allocated)
+        qemu_free(l1_table);
+    qemu_free(l2_table);
+    return 0;
+ fail:
+    if (l1_allocated)
+        qemu_free(l1_table);
+    qemu_free(l2_table);
+    return -EIO;
+}
+
+static void qcow_free_snapshots(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    int i;
+
+    for(i = 0; i < s->nb_snapshots; i++) {
+        qemu_free(s->snapshots[i].name);
+        qemu_free(s->snapshots[i].id_str);
+    }
+    qemu_free(s->snapshots);
+    s->snapshots = NULL;
+    s->nb_snapshots = 0;
+}
+
+static int qcow_read_snapshots(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshotHeader h;
+    QCowSnapshot *sn;
+    int i, id_str_size, name_size;
+    int64_t offset;
+    uint32_t extra_data_size;
+
+    offset = s->snapshots_offset;
+    s->snapshots = qemu_mallocz(s->nb_snapshots * sizeof(QCowSnapshot));
+    if (!s->snapshots)
+        goto fail;
+    for(i = 0; i < s->nb_snapshots; i++) {
+        offset = align_offset(offset, 8);
+        if (bdrv_pread(s->hd, offset, &h, sizeof(h)) != sizeof(h))
+            goto fail;
+        offset += sizeof(h);
+        sn = s->snapshots + i;
+        sn->l1_table_offset = be64_to_cpu(h.l1_table_offset);
+        sn->l1_size = be32_to_cpu(h.l1_size);
+        sn->vm_state_size = be32_to_cpu(h.vm_state_size);
+        sn->date_sec = be32_to_cpu(h.date_sec);
+        sn->date_nsec = be32_to_cpu(h.date_nsec);
+        sn->vm_clock_nsec = be64_to_cpu(h.vm_clock_nsec);
+        extra_data_size = be32_to_cpu(h.extra_data_size);
+
+        id_str_size = be16_to_cpu(h.id_str_size);
+        name_size = be16_to_cpu(h.name_size);
+
+        offset += extra_data_size;
+
+        sn->id_str = qemu_malloc(id_str_size + 1);
+        if (!sn->id_str)
+            goto fail;
+        if (bdrv_pread(s->hd, offset, sn->id_str, id_str_size) != id_str_size)
+            goto fail;
+        offset += id_str_size;
+        sn->id_str[id_str_size] = '\0';
+
+        sn->name = qemu_malloc(name_size + 1);
+        if (!sn->name)
+            goto fail;
+        if (bdrv_pread(s->hd, offset, sn->name, name_size) != name_size)
+            goto fail;
+        offset += name_size;
+        sn->name[name_size] = '\0';
+    }
+    s->snapshots_size = offset - s->snapshots_offset;
+    return 0;
+ fail:
+    qcow_free_snapshots(bs);
+    return -1;
+}
+
+/* add at the end of the file a new list of snapshots */
+static int qcow_write_snapshots(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshot *sn;
+    QCowSnapshotHeader h;
+    int i, name_size, id_str_size, snapshots_size;
+    uint64_t data64;
+    uint32_t data32;
+    int64_t offset, snapshots_offset;
+
+    /* compute the size of the snapshots */
+    offset = 0;
+    for(i = 0; i < s->nb_snapshots; i++) {
+        sn = s->snapshots + i;
+        offset = align_offset(offset, 8);
+        offset += sizeof(h);
+        offset += strlen(sn->id_str);
+        offset += strlen(sn->name);
+    }
+    snapshots_size = offset;
+
+    snapshots_offset = alloc_clusters(bs, snapshots_size);
+    offset = snapshots_offset;
+
+    for(i = 0; i < s->nb_snapshots; i++) {
+        sn = s->snapshots + i;
+        memset(&h, 0, sizeof(h));
+        h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
+        h.l1_size = cpu_to_be32(sn->l1_size);
+        h.vm_state_size = cpu_to_be32(sn->vm_state_size);
+        h.date_sec = cpu_to_be32(sn->date_sec);
+        h.date_nsec = cpu_to_be32(sn->date_nsec);
+        h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
+
+        id_str_size = strlen(sn->id_str);
+        name_size = strlen(sn->name);
+        h.id_str_size = cpu_to_be16(id_str_size);
+        h.name_size = cpu_to_be16(name_size);
+        offset = align_offset(offset, 8);
+        if (bdrv_pwrite(s->hd, offset, &h, sizeof(h)) != sizeof(h))
+            goto fail;
+        offset += sizeof(h);
+        if (bdrv_pwrite(s->hd, offset, sn->id_str, id_str_size) != id_str_size)
+            goto fail;
+        offset += id_str_size;
+        if (bdrv_pwrite(s->hd, offset, sn->name, name_size) != name_size)
+            goto fail;
+        offset += name_size;
+    }
+
+    /* update the various header fields */
+    data64 = cpu_to_be64(snapshots_offset);
+    if (bdrv_pwrite(s->hd, offsetof(QCowHeader, snapshots_offset),
+                    &data64, sizeof(data64)) != sizeof(data64))
+        goto fail;
+    data32 = cpu_to_be32(s->nb_snapshots);
+    if (bdrv_pwrite(s->hd, offsetof(QCowHeader, nb_snapshots),
+                    &data32, sizeof(data32)) != sizeof(data32))
+        goto fail;
+
+    /* free the old snapshot table */
+    free_clusters(bs, s->snapshots_offset, s->snapshots_size);
+    s->snapshots_offset = snapshots_offset;
+    s->snapshots_size = snapshots_size;
+    return 0;
+ fail:
+    return -1;
+}
+
+static void find_new_snapshot_id(BlockDriverState *bs,
+                                 char *id_str, int id_str_size)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshot *sn;
+    int i, id, id_max = 0;
+
+    for(i = 0; i < s->nb_snapshots; i++) {
+        sn = s->snapshots + i;
+        id = strtoul(sn->id_str, NULL, 10);
+        if (id > id_max)
+            id_max = id;
+    }
+    snprintf(id_str, id_str_size, "%d", id_max + 1);
+}
+
+static int find_snapshot_by_id(BlockDriverState *bs, const char *id_str)
+{
+    BDRVQcowState *s = bs->opaque;
+    int i;
+
+    for(i = 0; i < s->nb_snapshots; i++) {
+        if (!strcmp(s->snapshots[i].id_str, id_str))
+            return i;
+    }
+    return -1;
+}
+
+static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
+{
+    BDRVQcowState *s = bs->opaque;
+    int i, ret;
+
+    ret = find_snapshot_by_id(bs, name);
+    if (ret >= 0)
+        return ret;
+    for(i = 0; i < s->nb_snapshots; i++) {
+        if (!strcmp(s->snapshots[i].name, name))
+            return i;
+    }
+    return -1;
+}
+
+/* if no id is provided, a new one is constructed */
+static int qcow_snapshot_create(BlockDriverState *bs,
+                                QEMUSnapshotInfo *sn_info)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshot *snapshots1, sn1, *sn = &sn1;
+    int i, ret;
+    uint64_t *l1_table = NULL;
+
+    memset(sn, 0, sizeof(*sn));
+
+    if (sn_info->id_str[0] == '\0') {
+        /* compute a new id */
+        find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
+    }
+
+    /* check that the ID is unique */
+    if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
+        return -ENOENT;
+
+    sn->id_str = qemu_strdup(sn_info->id_str);
+    if (!sn->id_str)
+        goto fail;
+    sn->name = qemu_strdup(sn_info->name);
+    if (!sn->name)
+        goto fail;
+    sn->vm_state_size = sn_info->vm_state_size;
+    sn->date_sec = sn_info->date_sec;
+    sn->date_nsec = sn_info->date_nsec;
+    sn->vm_clock_nsec = sn_info->vm_clock_nsec;
+
+    ret = update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
+    if (ret < 0)
+        goto fail;
+
+    /* create the L1 table of the snapshot */
+    sn->l1_table_offset = alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
+    sn->l1_size = s->l1_size;
+
+    l1_table = qemu_malloc(s->l1_size * sizeof(uint64_t));
+    if (!l1_table)
+        goto fail;
+    for(i = 0; i < s->l1_size; i++) {
+        l1_table[i] = cpu_to_be64(s->l1_table[i]);
+    }
+    if (bdrv_pwrite(s->hd, sn->l1_table_offset,
+                    l1_table, s->l1_size * sizeof(uint64_t)) !=
+        (s->l1_size * sizeof(uint64_t)))
+        goto fail;
+    qemu_free(l1_table);
+    l1_table = NULL;
+
+    snapshots1 = qemu_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
+    if (!snapshots1)
+        goto fail;
+    memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
+    s->snapshots = snapshots1;
+    s->snapshots[s->nb_snapshots++] = *sn;
+
+    if (qcow_write_snapshots(bs) < 0)
+        goto fail;
+#ifdef DEBUG_ALLOC
+    check_refcounts(bs);
+#endif
+    return 0;
+ fail:
+    qemu_free(sn->name);
+    qemu_free(l1_table);
+    return -1;
+}
+
+/* copy the snapshot 'snapshot_name' into the current disk image */
+static int qcow_snapshot_goto(BlockDriverState *bs,
+                              const char *snapshot_id)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshot *sn;
+    int i, snapshot_index, l1_size2;
+
+    snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
+    if (snapshot_index < 0)
+        return -ENOENT;
+    sn = &s->snapshots[snapshot_index];
+
+    if (update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
+        goto fail;
+
+    if (grow_l1_table(bs, sn->l1_size) < 0)
+        goto fail;
+
+    s->l1_size = sn->l1_size;
+    l1_size2 = s->l1_size * sizeof(uint64_t);
+    /* copy the snapshot l1 table to the current l1 table */
+    if (bdrv_pread(s->hd, sn->l1_table_offset,
+                   s->l1_table, l1_size2) != l1_size2)
+        goto fail;
+    if (bdrv_pwrite(s->hd, s->l1_table_offset,
+                    s->l1_table, l1_size2) != l1_size2)
+        goto fail;
+    for(i = 0;i < s->l1_size; i++) {
+        be64_to_cpus(&s->l1_table[i]);
+    }
+
+    if (update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0)
+        goto fail;
+
+#ifdef DEBUG_ALLOC
+    check_refcounts(bs);
+#endif
+    return 0;
+ fail:
+    return -EIO;
+}
+
+static int qcow_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+{
+    BDRVQcowState *s = bs->opaque;
+    QCowSnapshot *sn;
+    int snapshot_index, ret;
+
+    snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
+    if (snapshot_index < 0)
+        return -ENOENT;
+    sn = &s->snapshots[snapshot_index];
+
+    ret = update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1);
+    if (ret < 0)
+        return ret;
+    /* must update the copied flag on the current cluster offsets */
+    ret = update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
+    if (ret < 0)
+        return ret;
+    free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t));
+
+    qemu_free(sn->id_str);
+    qemu_free(sn->name);
+    memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
+    s->nb_snapshots--;
+    ret = qcow_write_snapshots(bs);
+    if (ret < 0) {
+        /* XXX: restore snapshot if error ? */
+        return ret;
+    }
+#ifdef DEBUG_ALLOC
+    check_refcounts(bs);
+#endif
+    return 0;
+}
+
+static int qcow_snapshot_list(BlockDriverState *bs,
+                              QEMUSnapshotInfo **psn_tab)
+{
+    BDRVQcowState *s = bs->opaque;
+    QEMUSnapshotInfo *sn_tab, *sn_info;
+    QCowSnapshot *sn;
+    int i;
+
+    sn_tab = qemu_mallocz(s->nb_snapshots * sizeof(QEMUSnapshotInfo));
+    if (!sn_tab)
+        goto fail;
+    for(i = 0; i < s->nb_snapshots; i++) {
+        sn_info = sn_tab + i;
+        sn = s->snapshots + i;
+        pstrcpy(sn_info->id_str, sizeof(sn_info->id_str),
+                sn->id_str);
+        pstrcpy(sn_info->name, sizeof(sn_info->name),
+                sn->name);
+        sn_info->vm_state_size = sn->vm_state_size;
+        sn_info->date_sec = sn->date_sec;
+        sn_info->date_nsec = sn->date_nsec;
+        sn_info->vm_clock_nsec = sn->vm_clock_nsec;
+    }
+    *psn_tab = sn_tab;
+    return s->nb_snapshots;
+ fail:
+    qemu_free(sn_tab);
+    *psn_tab = NULL;
+    return -ENOMEM;
+}
+
+/*********************************************************/
+/* refcount handling */
+
+static int refcount_init(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    int ret, refcount_table_size2, i;
+
+    s->refcount_block_cache = qemu_malloc(s->cluster_size);
+    if (!s->refcount_block_cache)
+        goto fail;
+    refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
+    s->refcount_table = qemu_malloc(refcount_table_size2);
+    if (!s->refcount_table)
+        goto fail;
+    if (s->refcount_table_size > 0) {
+        ret = bdrv_pread(s->hd, s->refcount_table_offset,
+                         s->refcount_table, refcount_table_size2);
+        if (ret != refcount_table_size2)
+            goto fail;
+        for(i = 0; i < s->refcount_table_size; i++)
+            be64_to_cpus(&s->refcount_table[i]);
+    }
+    return 0;
+ fail:
+    return -ENOMEM;
+}
+
+static void refcount_close(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    qemu_free(s->refcount_block_cache);
+    qemu_free(s->refcount_table);
+}
+
+
+static int load_refcount_block(BlockDriverState *bs,
+                               int64_t refcount_block_offset)
+{
+    BDRVQcowState *s = bs->opaque;
+    int ret;
+    ret = bdrv_pread(s->hd, refcount_block_offset, s->refcount_block_cache,
+                     s->cluster_size);
+    if (ret != s->cluster_size)
+        return -EIO;
+    s->refcount_block_cache_offset = refcount_block_offset;
+    return 0;
+}
+
+static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
+{
+    BDRVQcowState *s = bs->opaque;
+    int refcount_table_index, block_index;
+    int64_t refcount_block_offset;
+
+    refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+    if (refcount_table_index >= s->refcount_table_size)
+        return 0;
+    refcount_block_offset = s->refcount_table[refcount_table_index];
+    if (!refcount_block_offset)
+        return 0;
+    if (refcount_block_offset != s->refcount_block_cache_offset) {
+        /* better than nothing: return allocated if read error */
+        if (load_refcount_block(bs, refcount_block_offset) < 0)
+            return 1;
+    }
+    block_index = cluster_index &
+        ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+    return be16_to_cpu(s->refcount_block_cache[block_index]);
+}
+
+/* return < 0 if error */
+static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int i, nb_clusters;
+
+    nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits;
+    for(;;) {
+        if (get_refcount(bs, s->free_cluster_index) == 0) {
+            s->free_cluster_index++;
+            for(i = 1; i < nb_clusters; i++) {
+                if (get_refcount(bs, s->free_cluster_index) != 0)
+                    goto not_found;
+                s->free_cluster_index++;
+            }
+#ifdef DEBUG_ALLOC2
+            printf("alloc_clusters: size=%lld -> %lld\n",
+                   size,
+                   (s->free_cluster_index - nb_clusters) << s->cluster_bits);
+#endif
+            return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
+        } else {
+        not_found:
+            s->free_cluster_index++;
+        }
+    }
+}
+
+static int64_t alloc_clusters(BlockDriverState *bs, int64_t size)
+{
+    int64_t offset;
+
+    offset = alloc_clusters_noref(bs, size);
+    update_refcount(bs, offset, size, 1);
+    return offset;
+}
+
+/* only used to allocate compressed sectors. We try to allocate
+   contiguous sectors. size must be <= cluster_size */
+static int64_t alloc_bytes(BlockDriverState *bs, int size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t offset, cluster_offset;
+    int free_in_cluster;
+
+    assert(size > 0 && size <= s->cluster_size);
+    if (s->free_byte_offset == 0) {
+        s->free_byte_offset = alloc_clusters(bs, s->cluster_size);
+    }
+ redo:
+    free_in_cluster = s->cluster_size -
+        (s->free_byte_offset & (s->cluster_size - 1));
+    if (size <= free_in_cluster) {
+        /* enough space in current cluster */
+        offset = s->free_byte_offset;
+        s->free_byte_offset += size;
+        free_in_cluster -= size;
+        if (free_in_cluster == 0)
+            s->free_byte_offset = 0;
+        if ((offset & (s->cluster_size - 1)) != 0)
+            update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
+    } else {
+        offset = alloc_clusters(bs, s->cluster_size);
+        cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1);
+        if ((cluster_offset + s->cluster_size) == offset) {
+            /* we are lucky: contiguous data */
+            offset = s->free_byte_offset;
+            update_cluster_refcount(bs, offset >> s->cluster_bits, 1);
+            s->free_byte_offset += size;
+        } else {
+            s->free_byte_offset = offset;
+            goto redo;
+        }
+    }
+    return offset;
+}
+
+static void free_clusters(BlockDriverState *bs,
+                          int64_t offset, int64_t size)
+{
+    update_refcount(bs, offset, size, -1);
+}
+
+static int grow_refcount_table(BlockDriverState *bs, int min_size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int new_table_size, new_table_size2, refcount_table_clusters, i, ret;
+    uint64_t *new_table;
+    int64_t table_offset;
+    uint64_t data64;
+    uint32_t data32;
+    int old_table_size;
+    int64_t old_table_offset;
+
+    if (min_size <= s->refcount_table_size)
+        return 0;
+    /* compute new table size */
+    refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);
+    for(;;) {
+        if (refcount_table_clusters == 0) {
+            refcount_table_clusters = 1;
+        } else {
+            refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2;
+        }
+        new_table_size = refcount_table_clusters << (s->cluster_bits - 3);
+        if (min_size <= new_table_size)
+            break;
+    }
+#ifdef DEBUG_ALLOC2
+    printf("grow_refcount_table from %d to %d\n",
+           s->refcount_table_size,
+           new_table_size);
+#endif
+    new_table_size2 = new_table_size * sizeof(uint64_t);
+    new_table = qemu_mallocz(new_table_size2);
+    if (!new_table)
+        return -ENOMEM;
+    memcpy(new_table, s->refcount_table,
+           s->refcount_table_size * sizeof(uint64_t));
+    for(i = 0; i < s->refcount_table_size; i++)
+        cpu_to_be64s(&new_table[i]);
+    /* Note: we cannot update the refcount now to avoid recursion */
+    table_offset = alloc_clusters_noref(bs, new_table_size2);
+    ret = bdrv_pwrite(s->hd, table_offset, new_table, new_table_size2);
+    if (ret != new_table_size2)
+        goto fail;
+    for(i = 0; i < s->refcount_table_size; i++)
+        be64_to_cpus(&new_table[i]);
+
+    data64 = cpu_to_be64(table_offset);
+    if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset),
+                    &data64, sizeof(data64)) != sizeof(data64))
+        goto fail;
+    data32 = cpu_to_be32(refcount_table_clusters);
+    if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_clusters),
+                    &data32, sizeof(data32)) != sizeof(data32))
+        goto fail;
+    qemu_free(s->refcount_table);
+    old_table_offset = s->refcount_table_offset;
+    old_table_size = s->refcount_table_size;
+    s->refcount_table = new_table;
+    s->refcount_table_size = new_table_size;
+    s->refcount_table_offset = table_offset;
+
+    update_refcount(bs, table_offset, new_table_size2, 1);
+    free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
+    return 0;
+ fail:
+    free_clusters(bs, table_offset, new_table_size2);
+    qemu_free(new_table);
+    return -EIO;
+}
+
+/* addend must be 1 or -1 */
+/* XXX: cache several refcount block clusters ? */
+static int update_cluster_refcount(BlockDriverState *bs,
+                                   int64_t cluster_index,
+                                   int addend)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t offset, refcount_block_offset;
+    int ret, refcount_table_index, block_index, refcount;
+    uint64_t data64;
+
+    refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
+    if (refcount_table_index >= s->refcount_table_size) {
+        if (addend < 0)
+            return -EINVAL;
+        ret = grow_refcount_table(bs, refcount_table_index + 1);
+        if (ret < 0)
+            return ret;
+    }
+    refcount_block_offset = s->refcount_table[refcount_table_index];
+    if (!refcount_block_offset) {
+        if (addend < 0)
+            return -EINVAL;
+        /* create a new refcount block */
+        /* Note: we cannot update the refcount now to avoid recursion */
+        offset = alloc_clusters_noref(bs, s->cluster_size);
+        memset(s->refcount_block_cache, 0, s->cluster_size);
+        ret = bdrv_pwrite(s->hd, offset, s->refcount_block_cache, s->cluster_size);
+        if (ret != s->cluster_size)
+            return -EINVAL;
+        s->refcount_table[refcount_table_index] = offset;
+        data64 = cpu_to_be64(offset);
+        ret = bdrv_pwrite(s->hd, s->refcount_table_offset +
+                          refcount_table_index * sizeof(uint64_t),
+                          &data64, sizeof(data64));
+        if (ret != sizeof(data64))
+            return -EINVAL;
+
+        refcount_block_offset = offset;
+        s->refcount_block_cache_offset = offset;
+        update_refcount(bs, offset, s->cluster_size, 1);
+    } else {
+        if (refcount_block_offset != s->refcount_block_cache_offset) {
+            if (load_refcount_block(bs, refcount_block_offset) < 0)
+                return -EIO;
+        }
+    }
+    /* we can update the count and save it */
+    block_index = cluster_index &
+        ((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
+    refcount = be16_to_cpu(s->refcount_block_cache[block_index]);
+    refcount += addend;
+    if (refcount < 0 || refcount > 0xffff)
+        return -EINVAL;
+    if (refcount == 0 && cluster_index < s->free_cluster_index) {
+        s->free_cluster_index = cluster_index;
+    }
+    s->refcount_block_cache[block_index] = cpu_to_be16(refcount);
+    if (bdrv_pwrite(s->hd,
+                    refcount_block_offset + (block_index << REFCOUNT_SHIFT),
+                    &s->refcount_block_cache[block_index], 2) != 2)
+        return -EIO;
+    return refcount;
+}
+
+static void update_refcount(BlockDriverState *bs,
+                            int64_t offset, int64_t length,
+                            int addend)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t start, last, cluster_offset;
+
+#ifdef DEBUG_ALLOC2
+    printf("update_refcount: offset=%lld size=%lld addend=%d\n",
+           offset, length, addend);
+#endif
+    if (length <= 0)
+        return;
+    start = offset & ~(s->cluster_size - 1);
+    last = (offset + length - 1) & ~(s->cluster_size - 1);
+    for(cluster_offset = start; cluster_offset <= last;
+        cluster_offset += s->cluster_size) {
+        update_cluster_refcount(bs, cluster_offset >> s->cluster_bits, addend);
+    }
+}
+
+#ifdef DEBUG_ALLOC
+static void inc_refcounts(BlockDriverState *bs,
+                          uint16_t *refcount_table,
+                          int refcount_table_size,
+                          int64_t offset, int64_t size)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t start, last, cluster_offset;
+    int k;
+
+    if (size <= 0)
+        return;
+
+    start = offset & ~(s->cluster_size - 1);
+    last = (offset + size - 1) & ~(s->cluster_size - 1);
+    for(cluster_offset = start; cluster_offset <= last;
+        cluster_offset += s->cluster_size) {
+        k = cluster_offset >> s->cluster_bits;
+        if (k < 0 || k >= refcount_table_size) {
+            printf("ERROR: invalid cluster offset=0x%llx\n", cluster_offset);
+        } else {
+            if (++refcount_table[k] == 0) {
+                printf("ERROR: overflow cluster offset=0x%llx\n", cluster_offset);
+            }
+        }
+    }
+}
+
+static int check_refcounts_l1(BlockDriverState *bs,
+                              uint16_t *refcount_table,
+                              int refcount_table_size,
+                              int64_t l1_table_offset, int l1_size,
+                              int check_copied)
+{
+    BDRVQcowState *s = bs->opaque;
+    uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2;
+    int l2_size, i, j, nb_csectors, refcount;
+
+    l2_table = NULL;
+    l1_size2 = l1_size * sizeof(uint64_t);
+
+    inc_refcounts(bs, refcount_table, refcount_table_size,
+                  l1_table_offset, l1_size2);
+
+    l1_table = qemu_malloc(l1_size2);
+    if (!l1_table)
+        goto fail;
+    if (bdrv_pread(s->hd, l1_table_offset,
+                   l1_table, l1_size2) != l1_size2)
+        goto fail;
+    for(i = 0;i < l1_size; i++)
+        be64_to_cpus(&l1_table[i]);
+
+    l2_size = s->l2_size * sizeof(uint64_t);
+    l2_table = qemu_malloc(l2_size);
+    if (!l2_table)
+        goto fail;
+    for(i = 0; i < l1_size; i++) {
+        l2_offset = l1_table[i];
+        if (l2_offset) {
+            if (check_copied) {
+                refcount = get_refcount(bs, (l2_offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits);
+                if ((refcount == 1) != ((l2_offset & QCOW_OFLAG_COPIED) != 0)) {
+                    printf("ERROR OFLAG_COPIED: l2_offset=%llx refcount=%d\n",
+                           l2_offset, refcount);
+                }
+            }
+            l2_offset &= ~QCOW_OFLAG_COPIED;
+            if (bdrv_pread(s->hd, l2_offset, l2_table, l2_size) != l2_size)
+                goto fail;
+            for(j = 0; j < s->l2_size; j++) {
+                offset = be64_to_cpu(l2_table[j]);
+                if (offset != 0) {
+                    if (offset & QCOW_OFLAG_COMPRESSED) {
+                        if (offset & QCOW_OFLAG_COPIED) {
+                            printf("ERROR: cluster %lld: copied flag must never be set for compressed clusters\n",
+                                   offset >> s->cluster_bits);
+                            offset &= ~QCOW_OFLAG_COPIED;
+                        }
+                        nb_csectors = ((offset >> s->csize_shift) &
+                                       s->csize_mask) + 1;
+                        offset &= s->cluster_offset_mask;
+                        inc_refcounts(bs, refcount_table,
+                                      refcount_table_size,
+                                      offset & ~511, nb_csectors * 512);
+                    } else {
+                        if (check_copied) {
+                            refcount = get_refcount(bs, (offset & ~QCOW_OFLAG_COPIED) >> s->cluster_bits);
+                            if ((refcount == 1) != ((offset & QCOW_OFLAG_COPIED) != 0)) {
+                                printf("ERROR OFLAG_COPIED: offset=%llx refcount=%d\n",
+                                       offset, refcount);
+                            }
+                        }
+                        offset &= ~QCOW_OFLAG_COPIED;
+                        inc_refcounts(bs, refcount_table,
+                                      refcount_table_size,
+                                      offset, s->cluster_size);
+                    }
+                }
+            }
+            inc_refcounts(bs, refcount_table,
+                          refcount_table_size,
+                          l2_offset,
+                          s->cluster_size);
+        }
+    }
+    qemu_free(l1_table);
+    qemu_free(l2_table);
+    return 0;
+ fail:
+    printf("ERROR: I/O error in check_refcounts_l1\n");
+    qemu_free(l1_table);
+    qemu_free(l2_table);
+    return -EIO;
+}
+
+static void check_refcounts(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t size;
+    int nb_clusters, refcount1, refcount2, i;
+    QCowSnapshot *sn;
+    uint16_t *refcount_table;
+
+    size = bdrv_getlength(s->hd);
+    nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits;
+    refcount_table = qemu_mallocz(nb_clusters * sizeof(uint16_t));
+
+    /* header */
+    inc_refcounts(bs, refcount_table, nb_clusters,
+                  0, s->cluster_size);
+
+    check_refcounts_l1(bs, refcount_table, nb_clusters,
+                       s->l1_table_offset, s->l1_size, 1);
+
+    /* snapshots */
+    for(i = 0; i < s->nb_snapshots; i++) {
+        sn = s->snapshots + i;
+        check_refcounts_l1(bs, refcount_table, nb_clusters,
+                           sn->l1_table_offset, sn->l1_size, 0);
+    }
+    inc_refcounts(bs, refcount_table, nb_clusters,
+                  s->snapshots_offset, s->snapshots_size);
+
+    /* refcount data */
+    inc_refcounts(bs, refcount_table, nb_clusters,
+                  s->refcount_table_offset,
+                  s->refcount_table_size * sizeof(uint64_t));
+    for(i = 0; i < s->refcount_table_size; i++) {
+        int64_t offset;
+        offset = s->refcount_table[i];
+        if (offset != 0) {
+            inc_refcounts(bs, refcount_table, nb_clusters,
+                          offset, s->cluster_size);
+        }
+    }
+
+    /* compare ref counts */
+    for(i = 0; i < nb_clusters; i++) {
+        refcount1 = get_refcount(bs, i);
+        refcount2 = refcount_table[i];
+        if (refcount1 != refcount2)
+            printf("ERROR cluster %d refcount=%d reference=%d\n",
+                   i, refcount1, refcount2);
+    }
+
+    qemu_free(refcount_table);
+}
+
+#if 0
+static void dump_refcounts(BlockDriverState *bs)
+{
+    BDRVQcowState *s = bs->opaque;
+    int64_t nb_clusters, k, k1, size;
+    int refcount;
+
+    size = bdrv_getlength(s->hd);
+    nb_clusters = (size + s->cluster_size - 1) >> s->cluster_bits;
+    for(k = 0; k < nb_clusters;) {
+        k1 = k;
+        refcount = get_refcount(bs, k);
+        k++;
+        while (k < nb_clusters && get_refcount(bs, k) == refcount)
+            k++;
+        printf("%lld: refcount=%d nb=%lld\n", k, refcount, k - k1);
+    }
+}
+#endif
+#endif
+
+BlockDriver bdrv_qcow2 = {
+    "qcow2",
+    sizeof(BDRVQcowState),
+    qcow_probe,
+    qcow_open,
+    NULL,
+    NULL,
+    qcow_close,
+    qcow_create,
+    qcow_flush,
+    qcow_is_allocated,
+    qcow_set_key,
+    qcow_make_empty,
+
+    .bdrv_aio_read = qcow_aio_read,
+    .bdrv_aio_write = qcow_aio_write,
+    .bdrv_aio_cancel = qcow_aio_cancel,
+    .aiocb_size = sizeof(QCowAIOCB),
+    .bdrv_write_compressed = qcow_write_compressed,
+
+    .bdrv_snapshot_create = qcow_snapshot_create,
+    .bdrv_snapshot_goto = qcow_snapshot_goto,
+    .bdrv_snapshot_delete = qcow_snapshot_delete,
+    .bdrv_snapshot_list = qcow_snapshot_list,
+    .bdrv_get_info = qcow_get_info,
+};
diff --git a/block-raw-posix.c b/block-raw-posix.c
new file mode 100644
index 0000000..7c42c10
--- /dev/null
+++ b/block-raw-posix.c
@@ -0,0 +1,1210 @@
+/*
+ * Block driver for RAW files (posix)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "block_int.h"
+#include "compatfd.h"
+#include <assert.h>
+#ifdef CONFIG_AIO
+#include <aio.h>
+#endif
+
+#ifdef CONFIG_COCOA
+#include <paths.h>
+#include <sys/param.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOMediaBSDClient.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/IOCDMedia.h>
+//#include <IOKit/storage/IOCDTypes.h>
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#ifdef __sun__
+#define _POSIX_PTHREAD_SEMANTICS 1
+#include <signal.h>
+#include <sys/dkio.h>
+#endif
+#ifdef __linux__
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+#include <linux/fd.h>
+#endif
+#ifdef __FreeBSD__
+#include <signal.h>
+#include <sys/disk.h>
+#endif
+
+#ifdef __OpenBSD__
+#include <sys/ioctl.h>
+#include <sys/disklabel.h>
+#include <sys/dkio.h>
+#endif
+
+//#define DEBUG_FLOPPY
+
+//#define DEBUG_BLOCK
+#if defined(DEBUG_BLOCK)
+#define DEBUG_BLOCK_PRINT(formatCstr, args...) do { if (loglevel != 0)	\
+    { fprintf(logfile, formatCstr, ##args); fflush(logfile); } } while (0)
+#else
+#define DEBUG_BLOCK_PRINT(formatCstr, args...)
+#endif
+
+#define FTYPE_FILE   0
+#define FTYPE_CD     1
+#define FTYPE_FD     2
+
+#define ALIGNED_BUFFER_SIZE (32 * 512)
+
+/* if the FD is not accessed during that time (in ms), we try to
+   reopen it to see if the disk has been changed */
+#define FD_OPEN_TIMEOUT 1000
+
+typedef struct BDRVRawState {
+    int fd;
+    int type;
+    unsigned int lseek_err_cnt;
+#if defined(__linux__)
+    /* linux floppy specific */
+    int fd_open_flags;
+    int64_t fd_open_time;
+    int64_t fd_error_time;
+    int fd_got_error;
+    int fd_media_changed;
+#endif
+#if defined(O_DIRECT)
+    uint8_t* aligned_buf;
+#endif
+} BDRVRawState;
+
+static int fd_open(BlockDriverState *bs);
+
+static int raw_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVRawState *s = bs->opaque;
+    int fd, open_flags, ret;
+
+    s->lseek_err_cnt = 0;
+
+    open_flags = O_BINARY;
+    if ((flags & BDRV_O_ACCESS) == O_RDWR) {
+        open_flags |= O_RDWR;
+    } else {
+        open_flags |= O_RDONLY;
+        bs->read_only = 1;
+    }
+    if (flags & BDRV_O_CREAT)
+        open_flags |= O_CREAT | O_TRUNC;
+#ifdef O_DIRECT
+    if (flags & BDRV_O_DIRECT)
+        open_flags |= O_DIRECT;
+#endif
+
+    s->type = FTYPE_FILE;
+
+    fd = open(filename, open_flags, 0644);
+    if (fd < 0) {
+        ret = -errno;
+        if (ret == -EROFS)
+            ret = -EACCES;
+        return ret;
+    }
+    s->fd = fd;
+#if defined(O_DIRECT)
+    s->aligned_buf = NULL;
+    if (flags & BDRV_O_DIRECT) {
+        s->aligned_buf = qemu_memalign(512, ALIGNED_BUFFER_SIZE);
+        if (s->aligned_buf == NULL) {
+            ret = -errno;
+            close(fd);
+            return ret;
+        }
+    }
+#endif
+    return 0;
+}
+
+/* XXX: use host sector size if necessary with:
+#ifdef DIOCGSECTORSIZE
+        {
+            unsigned int sectorsize = 512;
+            if (!ioctl(fd, DIOCGSECTORSIZE, &sectorsize) &&
+                sectorsize > bufsize)
+                bufsize = sectorsize;
+        }
+#endif
+#ifdef CONFIG_COCOA
+        u_int32_t   blockSize = 512;
+        if ( !ioctl( fd, DKIOCGETBLOCKSIZE, &blockSize ) && blockSize > bufsize) {
+            bufsize = blockSize;
+        }
+#endif
+*/
+
+/*
+ * offset and count are in bytes, but must be multiples of 512 for files
+ * opened with O_DIRECT. buf must be aligned to 512 bytes then.
+ *
+ * This function may be called without alignment if the caller ensures
+ * that O_DIRECT is not in effect.
+ */
+static int raw_pread_aligned(BlockDriverState *bs, int64_t offset,
+                     uint8_t *buf, int count)
+{
+    BDRVRawState *s = bs->opaque;
+    int ret;
+
+    ret = fd_open(bs);
+    if (ret < 0)
+        return ret;
+
+    if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) {
+        ++(s->lseek_err_cnt);
+        if(s->lseek_err_cnt <= 10) {
+            DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+                              "] lseek failed : %d = %s\n",
+                              s->fd, bs->filename, offset, buf, count,
+                              bs->total_sectors, errno, strerror(errno));
+        }
+        return -1;
+    }
+    s->lseek_err_cnt=0;
+
+    ret = read(s->fd, buf, count);
+    if (ret == count)
+        goto label__raw_read__success;
+
+    DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+                      "] read failed %d : %d = %s\n",
+                      s->fd, bs->filename, offset, buf, count,
+                      bs->total_sectors, ret, errno, strerror(errno));
+
+    /* Try harder for CDrom. */
+    if (bs->type == BDRV_TYPE_CDROM) {
+        lseek(s->fd, offset, SEEK_SET);
+        ret = read(s->fd, buf, count);
+        if (ret == count)
+            goto label__raw_read__success;
+        lseek(s->fd, offset, SEEK_SET);
+        ret = read(s->fd, buf, count);
+        if (ret == count)
+            goto label__raw_read__success;
+
+        DEBUG_BLOCK_PRINT("raw_pread(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+                          "] retry read failed %d : %d = %s\n",
+                          s->fd, bs->filename, offset, buf, count,
+                          bs->total_sectors, ret, errno, strerror(errno));
+    }
+
+label__raw_read__success:
+
+    return ret;
+}
+
+/*
+ * offset and count are in bytes, but must be multiples of 512 for files
+ * opened with O_DIRECT. buf must be aligned to 512 bytes then.
+ *
+ * This function may be called without alignment if the caller ensures
+ * that O_DIRECT is not in effect.
+ */
+static int raw_pwrite_aligned(BlockDriverState *bs, int64_t offset,
+                      const uint8_t *buf, int count)
+{
+    BDRVRawState *s = bs->opaque;
+    int ret;
+
+    ret = fd_open(bs);
+    if (ret < 0)
+        return ret;
+
+    if (offset >= 0 && lseek(s->fd, offset, SEEK_SET) == (off_t)-1) {
+        ++(s->lseek_err_cnt);
+        if(s->lseek_err_cnt) {
+            DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%"
+                              PRId64 "] lseek failed : %d = %s\n",
+                              s->fd, bs->filename, offset, buf, count,
+                              bs->total_sectors, errno, strerror(errno));
+        }
+        return -1;
+    }
+    s->lseek_err_cnt = 0;
+
+    ret = write(s->fd, buf, count);
+    if (ret == count)
+        goto label__raw_write__success;
+
+    DEBUG_BLOCK_PRINT("raw_pwrite(%d:%s, %" PRId64 ", %p, %d) [%" PRId64
+                      "] write failed %d : %d = %s\n",
+                      s->fd, bs->filename, offset, buf, count,
+                      bs->total_sectors, ret, errno, strerror(errno));
+
+label__raw_write__success:
+
+    return ret;
+}
+
+
+#if defined(O_DIRECT)
+/*
+ * offset and count are in bytes and possibly not aligned. For files opened
+ * with O_DIRECT, necessary alignments are ensured before calling
+ * raw_pread_aligned to do the actual read.
+ */
+static int raw_pread(BlockDriverState *bs, int64_t offset,
+                     uint8_t *buf, int count)
+{
+    BDRVRawState *s = bs->opaque;
+    int size, ret, shift, sum;
+
+    sum = 0;
+
+    if (s->aligned_buf != NULL)  {
+
+        if (offset & 0x1ff) {
+            /* align offset on a 512 bytes boundary */
+
+            shift = offset & 0x1ff;
+            size = (shift + count + 0x1ff) & ~0x1ff;
+            if (size > ALIGNED_BUFFER_SIZE)
+                size = ALIGNED_BUFFER_SIZE;
+            ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size);
+            if (ret < 0)
+                return ret;
+
+            size = 512 - shift;
+            if (size > count)
+                size = count;
+            memcpy(buf, s->aligned_buf + shift, size);
+
+            buf += size;
+            offset += size;
+            count -= size;
+            sum += size;
+
+            if (count == 0)
+                return sum;
+        }
+        if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
+
+            /* read on aligned buffer */
+
+            while (count) {
+
+                size = (count + 0x1ff) & ~0x1ff;
+                if (size > ALIGNED_BUFFER_SIZE)
+                    size = ALIGNED_BUFFER_SIZE;
+
+                ret = raw_pread_aligned(bs, offset, s->aligned_buf, size);
+                if (ret < 0)
+                    return ret;
+
+                size = ret;
+                if (size > count)
+                    size = count;
+
+                memcpy(buf, s->aligned_buf, size);
+
+                buf += size;
+                offset += size;
+                count -= size;
+                sum += size;
+            }
+
+            return sum;
+        }
+    }
+
+    return raw_pread_aligned(bs, offset, buf, count) + sum;
+}
+
+/*
+ * offset and count are in bytes and possibly not aligned. For files opened
+ * with O_DIRECT, necessary alignments are ensured before calling
+ * raw_pwrite_aligned to do the actual write.
+ */
+static int raw_pwrite(BlockDriverState *bs, int64_t offset,
+                      const uint8_t *buf, int count)
+{
+    BDRVRawState *s = bs->opaque;
+    int size, ret, shift, sum;
+
+    sum = 0;
+
+    if (s->aligned_buf != NULL) {
+
+        if (offset & 0x1ff) {
+            /* align offset on a 512 bytes boundary */
+            shift = offset & 0x1ff;
+            ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512);
+            if (ret < 0)
+                return ret;
+
+            size = 512 - shift;
+            if (size > count)
+                size = count;
+            memcpy(s->aligned_buf + shift, buf, size);
+
+            ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512);
+            if (ret < 0)
+                return ret;
+
+            buf += size;
+            offset += size;
+            count -= size;
+            sum += size;
+
+            if (count == 0)
+                return sum;
+        }
+        if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
+
+            while ((size = (count & ~0x1ff)) != 0) {
+
+                if (size > ALIGNED_BUFFER_SIZE)
+                    size = ALIGNED_BUFFER_SIZE;
+
+                memcpy(s->aligned_buf, buf, size);
+
+                ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, size);
+                if (ret < 0)
+                    return ret;
+
+                buf += ret;
+                offset += ret;
+                count -= ret;
+                sum += ret;
+            }
+            /* here, count < 512 because (count & ~0x1ff) == 0 */
+            if (count) {
+                ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512);
+                if (ret < 0)
+                    return ret;
+                 memcpy(s->aligned_buf, buf, count);
+
+                 ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512);
+                 if (ret < 0)
+                     return ret;
+                 if (count < ret)
+                     ret = count;
+
+                 sum += ret;
+            }
+            return sum;
+        }
+    }
+    return raw_pwrite_aligned(bs, offset, buf, count) + sum;
+}
+
+#else
+#define raw_pread raw_pread_aligned
+#define raw_pwrite raw_pwrite_aligned
+#endif
+
+
+#ifdef CONFIG_AIO
+/***********************************************************/
+/* Unix AIO using POSIX AIO */
+
+typedef struct RawAIOCB {
+    BlockDriverAIOCB common;
+    struct aiocb aiocb;
+    struct RawAIOCB *next;
+    int ret;
+} RawAIOCB;
+
+static int aio_sig_fd = -1;
+static int aio_sig_num = SIGUSR2;
+static RawAIOCB *first_aio; /* AIO issued */
+static int aio_initialized = 0;
+
+static void qemu_aio_poll(void *opaque)
+{
+    RawAIOCB *acb, **pacb;
+    int ret;
+    size_t offset;
+    union {
+        struct qemu_signalfd_siginfo siginfo;
+        char buf[128];
+    } sig;
+
+    /* try to read from signalfd, don't freak out if we can't read anything */
+    offset = 0;
+    while (offset < 128) {
+        ssize_t len;
+
+        len = read(aio_sig_fd, sig.buf + offset, 128 - offset);
+        if (len == -1 && errno == EINTR)
+            continue;
+        if (len == -1 && errno == EAGAIN) {
+            /* there is no natural reason for this to happen,
+             * so we'll spin hard until we get everything just
+             * to be on the safe side. */
+            if (offset > 0)
+                continue;
+        }
+
+        offset += len;
+    }
+
+    for(;;) {
+        pacb = &first_aio;
+        for(;;) {
+            acb = *pacb;
+            if (!acb)
+                goto the_end;
+            ret = aio_error(&acb->aiocb);
+            if (ret == ECANCELED) {
+                /* remove the request */
+                *pacb = acb->next;
+                qemu_aio_release(acb);
+            } else if (ret != EINPROGRESS) {
+                /* end of aio */
+                if (ret == 0) {
+                    ret = aio_return(&acb->aiocb);
+                    if (ret == acb->aiocb.aio_nbytes)
+                        ret = 0;
+                    else
+                        ret = -EINVAL;
+                } else {
+                    ret = -ret;
+                }
+                /* remove the request */
+                *pacb = acb->next;
+                /* call the callback */
+                acb->common.cb(acb->common.opaque, ret);
+                qemu_aio_release(acb);
+                break;
+            } else {
+                pacb = &acb->next;
+            }
+        }
+    }
+ the_end: ;
+}
+
+void qemu_aio_init(void)
+{
+    sigset_t mask;
+
+    aio_initialized = 1;
+
+    /* Make sure to block AIO signal */
+    sigemptyset(&mask);
+    sigaddset(&mask, aio_sig_num);
+    sigprocmask(SIG_BLOCK, &mask, NULL);
+    
+    aio_sig_fd = qemu_signalfd(&mask);
+
+    fcntl(aio_sig_fd, F_SETFL, O_NONBLOCK);
+
+    qemu_set_fd_handler2(aio_sig_fd, NULL, qemu_aio_poll, NULL, NULL);
+
+#if defined(__GLIBC__) && defined(__linux__)
+    {
+        /* XXX: aio thread exit seems to hang on RedHat 9 and this init
+           seems to fix the problem. */
+        struct aioinit ai;
+        memset(&ai, 0, sizeof(ai));
+        ai.aio_threads = 1;
+        ai.aio_num = 1;
+        ai.aio_idle_time = 365 * 100000;
+        aio_init(&ai);
+    }
+#endif
+}
+
+/* Wait for all IO requests to complete.  */
+void qemu_aio_flush(void)
+{
+    qemu_aio_poll(NULL);
+    while (first_aio) {
+        qemu_aio_wait();
+    }
+}
+
+void qemu_aio_wait(void)
+{
+    int ret;
+
+    if (qemu_bh_poll())
+        return;
+
+    if (!first_aio)
+        return;
+
+    do {
+        fd_set rdfds;
+
+        FD_ZERO(&rdfds);
+        FD_SET(aio_sig_fd, &rdfds);
+
+        ret = select(aio_sig_fd + 1, &rdfds, NULL, NULL, NULL);
+        if (ret == -1 && errno == EINTR)
+            continue;
+    } while (ret == 0);
+
+    qemu_aio_poll(NULL);
+}
+
+static RawAIOCB *raw_aio_setup(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BDRVRawState *s = bs->opaque;
+    RawAIOCB *acb;
+
+    if (fd_open(bs) < 0)
+        return NULL;
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (!acb)
+        return NULL;
+    acb->aiocb.aio_fildes = s->fd;
+    acb->aiocb.aio_sigevent.sigev_signo = aio_sig_num;
+    acb->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
+    acb->aiocb.aio_buf = buf;
+    if (nb_sectors < 0)
+        acb->aiocb.aio_nbytes = -nb_sectors;
+    else
+        acb->aiocb.aio_nbytes = nb_sectors * 512;
+    acb->aiocb.aio_offset = sector_num * 512;
+    acb->next = first_aio;
+    first_aio = acb;
+    return acb;
+}
+
+static void raw_aio_em_cb(void* opaque)
+{
+    RawAIOCB *acb = opaque;
+    acb->common.cb(acb->common.opaque, acb->ret);
+    qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    RawAIOCB *acb;
+
+    /*
+     * If O_DIRECT is used and the buffer is not aligned fall back
+     * to synchronous IO.
+     */
+#if defined(O_DIRECT)
+    BDRVRawState *s = bs->opaque;
+
+    if (unlikely(s->aligned_buf != NULL && ((uintptr_t) buf % 512))) {
+        QEMUBH *bh;
+        acb = qemu_aio_get(bs, cb, opaque);
+        acb->ret = raw_pread(bs, 512 * sector_num, buf, 512 * nb_sectors);
+        bh = qemu_bh_new(raw_aio_em_cb, acb);
+        qemu_bh_schedule(bh);
+        return &acb->common;
+    }
+#endif
+
+    acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
+    if (!acb)
+        return NULL;
+    if (aio_read(&acb->aiocb) < 0) {
+        qemu_aio_release(acb);
+        return NULL;
+    }
+    return &acb->common;
+}
+
+static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs,
+        int64_t sector_num, const uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    RawAIOCB *acb;
+
+    /*
+     * If O_DIRECT is used and the buffer is not aligned fall back
+     * to synchronous IO.
+     */
+#if defined(O_DIRECT)
+    BDRVRawState *s = bs->opaque;
+
+    if (unlikely(s->aligned_buf != NULL && ((uintptr_t) buf % 512))) {
+        QEMUBH *bh;
+        acb = qemu_aio_get(bs, cb, opaque);
+        acb->ret = raw_pwrite(bs, 512 * sector_num, buf, 512 * nb_sectors);
+        bh = qemu_bh_new(raw_aio_em_cb, acb);
+        qemu_bh_schedule(bh);
+        return &acb->common;
+    }
+#endif
+
+    acb = raw_aio_setup(bs, sector_num, (uint8_t*)buf, nb_sectors, cb, opaque);
+    if (!acb)
+        return NULL;
+    if (aio_write(&acb->aiocb) < 0) {
+        qemu_aio_release(acb);
+        return NULL;
+    }
+    return &acb->common;
+}
+
+static void raw_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    int ret;
+    RawAIOCB *acb = (RawAIOCB *)blockacb;
+    RawAIOCB **pacb;
+
+    ret = aio_cancel(acb->aiocb.aio_fildes, &acb->aiocb);
+    if (ret == AIO_NOTCANCELED) {
+        /* fail safe: if the aio could not be canceled, we wait for
+           it */
+        while (aio_error(&acb->aiocb) == EINPROGRESS);
+    }
+
+    /* remove the callback from the queue */
+    pacb = &first_aio;
+    for(;;) {
+        if (*pacb == NULL) {
+            break;
+        } else if (*pacb == acb) {
+            *pacb = acb->next;
+            qemu_aio_release(acb);
+            break;
+        }
+        pacb = &acb->next;
+    }
+}
+
+# else /* CONFIG_AIO */
+
+void qemu_aio_init(void)
+{
+}
+
+void qemu_aio_flush(void)
+{
+}
+
+void qemu_aio_wait(void)
+{
+    qemu_bh_poll();
+}
+
+#endif /* CONFIG_AIO */
+
+static void raw_close(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    if (s->fd >= 0) {
+        close(s->fd);
+        s->fd = -1;
+#if defined(O_DIRECT)
+        if (s->aligned_buf != NULL)
+            qemu_free(s->aligned_buf);
+#endif
+    }
+}
+
+static int raw_truncate(BlockDriverState *bs, int64_t offset)
+{
+    BDRVRawState *s = bs->opaque;
+    if (s->type != FTYPE_FILE)
+        return -ENOTSUP;
+    if (ftruncate(s->fd, offset) < 0)
+        return -errno;
+    return 0;
+}
+
+#ifdef __OpenBSD__
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    int fd = s->fd;
+    struct stat st;
+
+    if (fstat(fd, &st))
+        return -1;
+    if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+        struct disklabel dl;
+
+        if (ioctl(fd, DIOCGDINFO, &dl))
+            return -1;
+        return (uint64_t)dl.d_secsize *
+            dl.d_partitions[DISKPART(st.st_rdev)].p_size;
+    } else
+        return st.st_size;
+}
+#else /* !__OpenBSD__ */
+static int64_t  raw_getlength(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    int fd = s->fd;
+    int64_t size;
+#ifdef _BSD
+    struct stat sb;
+#endif
+#ifdef __sun__
+    struct dk_minfo minfo;
+    int rv;
+#endif
+    int ret;
+
+    ret = fd_open(bs);
+    if (ret < 0)
+        return ret;
+
+#ifdef _BSD
+    if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) {
+#ifdef DIOCGMEDIASIZE
+	if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size))
+#endif
+#ifdef CONFIG_COCOA
+        size = LONG_LONG_MAX;
+#else
+        size = lseek(fd, 0LL, SEEK_END);
+#endif
+    } else
+#endif
+#ifdef __sun__
+    /*
+     * use the DKIOCGMEDIAINFO ioctl to read the size.
+     */
+    rv = ioctl ( fd, DKIOCGMEDIAINFO, &minfo );
+    if ( rv != -1 ) {
+        size = minfo.dki_lbsize * minfo.dki_capacity;
+    } else /* there are reports that lseek on some devices
+              fails, but irc discussion said that contingency
+              on contingency was overkill */
+#endif
+    {
+        size = lseek(fd, 0, SEEK_END);
+    }
+    return size;
+}
+#endif
+
+static int raw_create(const char *filename, int64_t total_size,
+                      const char *backing_file, int flags)
+{
+    int fd;
+
+    if (flags || backing_file)
+        return -ENOTSUP;
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+              0644);
+    if (fd < 0)
+        return -EIO;
+    ftruncate(fd, total_size * 512);
+    close(fd);
+    return 0;
+}
+
+static void raw_flush(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    fsync(s->fd);
+}
+
+BlockDriver bdrv_raw = {
+    "raw",
+    sizeof(BDRVRawState),
+    NULL, /* no probe for protocols */
+    raw_open,
+    NULL,
+    NULL,
+    raw_close,
+    raw_create,
+    raw_flush,
+
+#ifdef CONFIG_AIO
+    .bdrv_aio_read = raw_aio_read,
+    .bdrv_aio_write = raw_aio_write,
+    .bdrv_aio_cancel = raw_aio_cancel,
+    .aiocb_size = sizeof(RawAIOCB),
+#endif
+    .bdrv_pread = raw_pread,
+    .bdrv_pwrite = raw_pwrite,
+    .bdrv_truncate = raw_truncate,
+    .bdrv_getlength = raw_getlength,
+};
+
+/***********************************************/
+/* host device */
+
+#ifdef CONFIG_COCOA
+static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
+static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
+
+kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
+{
+    kern_return_t       kernResult;
+    mach_port_t     masterPort;
+    CFMutableDictionaryRef  classesToMatch;
+
+    kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
+    if ( KERN_SUCCESS != kernResult ) {
+        printf( "IOMasterPort returned %d\n", kernResult );
+    }
+
+    classesToMatch = IOServiceMatching( kIOCDMediaClass );
+    if ( classesToMatch == NULL ) {
+        printf( "IOServiceMatching returned a NULL dictionary.\n" );
+    } else {
+    CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
+    }
+    kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
+    if ( KERN_SUCCESS != kernResult )
+    {
+        printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
+    }
+
+    return kernResult;
+}
+
+kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
+{
+    io_object_t     nextMedia;
+    kern_return_t   kernResult = KERN_FAILURE;
+    *bsdPath = '\0';
+    nextMedia = IOIteratorNext( mediaIterator );
+    if ( nextMedia )
+    {
+        CFTypeRef   bsdPathAsCFString;
+    bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia, CFSTR( kIOBSDNameKey ), kCFAllocatorDefault, 0 );
+        if ( bsdPathAsCFString ) {
+            size_t devPathLength;
+            strcpy( bsdPath, _PATH_DEV );
+            strcat( bsdPath, "r" );
+            devPathLength = strlen( bsdPath );
+            if ( CFStringGetCString( bsdPathAsCFString, bsdPath + devPathLength, maxPathSize - devPathLength, kCFStringEncodingASCII ) ) {
+                kernResult = KERN_SUCCESS;
+            }
+            CFRelease( bsdPathAsCFString );
+        }
+        IOObjectRelease( nextMedia );
+    }
+
+    return kernResult;
+}
+
+#endif
+
+static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVRawState *s = bs->opaque;
+    int fd, open_flags, ret;
+
+#ifdef CONFIG_COCOA
+    if (strstart(filename, "/dev/cdrom", NULL)) {
+        kern_return_t kernResult;
+        io_iterator_t mediaIterator;
+        char bsdPath[ MAXPATHLEN ];
+        int fd;
+
+        kernResult = FindEjectableCDMedia( &mediaIterator );
+        kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
+
+        if ( bsdPath[ 0 ] != '\0' ) {
+            strcat(bsdPath,"s0");
+            /* some CDs don't have a partition 0 */
+            fd = open(bsdPath, O_RDONLY | O_BINARY | O_LARGEFILE);
+            if (fd < 0) {
+                bsdPath[strlen(bsdPath)-1] = '1';
+            } else {
+                close(fd);
+            }
+            filename = bsdPath;
+        }
+
+        if ( mediaIterator )
+            IOObjectRelease( mediaIterator );
+    }
+#endif
+    open_flags = O_BINARY;
+    if ((flags & BDRV_O_ACCESS) == O_RDWR) {
+        open_flags |= O_RDWR;
+    } else {
+        open_flags |= O_RDONLY;
+        bs->read_only = 1;
+    }
+#ifdef O_DIRECT
+    if (flags & BDRV_O_DIRECT)
+        open_flags |= O_DIRECT;
+#endif
+
+    s->type = FTYPE_FILE;
+#if defined(__linux__)
+    if (strstart(filename, "/dev/cd", NULL)) {
+        /* open will not fail even if no CD is inserted */
+        open_flags |= O_NONBLOCK;
+        s->type = FTYPE_CD;
+    } else if (strstart(filename, "/dev/fd", NULL)) {
+        s->type = FTYPE_FD;
+        s->fd_open_flags = open_flags;
+        /* open will not fail even if no floppy is inserted */
+        open_flags |= O_NONBLOCK;
+    } else if (strstart(filename, "/dev/sg", NULL)) {
+        bs->sg = 1;
+    }
+#endif
+    fd = open(filename, open_flags, 0644);
+    if (fd < 0) {
+        ret = -errno;
+        if (ret == -EROFS)
+            ret = -EACCES;
+        return ret;
+    }
+    s->fd = fd;
+#if defined(__linux__)
+    /* close fd so that we can reopen it as needed */
+    if (s->type == FTYPE_FD) {
+        close(s->fd);
+        s->fd = -1;
+        s->fd_media_changed = 1;
+    }
+#endif
+    return 0;
+}
+
+#if defined(__linux__)
+
+/* Note: we do not have a reliable method to detect if the floppy is
+   present. The current method is to try to open the floppy at every
+   I/O and to keep it opened during a few hundreds of ms. */
+static int fd_open(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    int last_media_present;
+
+    if (s->type != FTYPE_FD)
+        return 0;
+    last_media_present = (s->fd >= 0);
+    if (s->fd >= 0 &&
+        (qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
+        close(s->fd);
+        s->fd = -1;
+#ifdef DEBUG_FLOPPY
+        printf("Floppy closed\n");
+#endif
+    }
+    if (s->fd < 0) {
+        if (s->fd_got_error &&
+            (qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) {
+#ifdef DEBUG_FLOPPY
+            printf("No floppy (open delayed)\n");
+#endif
+            return -EIO;
+        }
+        s->fd = open(bs->filename, s->fd_open_flags);
+        if (s->fd < 0) {
+            s->fd_error_time = qemu_get_clock(rt_clock);
+            s->fd_got_error = 1;
+            if (last_media_present)
+                s->fd_media_changed = 1;
+#ifdef DEBUG_FLOPPY
+            printf("No floppy\n");
+#endif
+            return -EIO;
+        }
+#ifdef DEBUG_FLOPPY
+        printf("Floppy opened\n");
+#endif
+    }
+    if (!last_media_present)
+        s->fd_media_changed = 1;
+    s->fd_open_time = qemu_get_clock(rt_clock);
+    s->fd_got_error = 0;
+    return 0;
+}
+
+static int raw_is_inserted(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    int ret;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        ret = ioctl(s->fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
+        if (ret == CDS_DISC_OK)
+            return 1;
+        else
+            return 0;
+        break;
+    case FTYPE_FD:
+        ret = fd_open(bs);
+        return (ret >= 0);
+    default:
+        return 1;
+    }
+}
+
+/* currently only used by fdc.c, but a CD version would be good too */
+static int raw_media_changed(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+
+    switch(s->type) {
+    case FTYPE_FD:
+        {
+            int ret;
+            /* XXX: we do not have a true media changed indication. It
+               does not work if the floppy is changed without trying
+               to read it */
+            fd_open(bs);
+            ret = s->fd_media_changed;
+            s->fd_media_changed = 0;
+#ifdef DEBUG_FLOPPY
+            printf("Floppy changed=%d\n", ret);
+#endif
+            return ret;
+        }
+    default:
+        return -ENOTSUP;
+    }
+}
+
+static int raw_eject(BlockDriverState *bs, int eject_flag)
+{
+    BDRVRawState *s = bs->opaque;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        if (eject_flag) {
+            if (ioctl (s->fd, CDROMEJECT, NULL) < 0)
+                perror("CDROMEJECT");
+        } else {
+            if (ioctl (s->fd, CDROMCLOSETRAY, NULL) < 0)
+                perror("CDROMEJECT");
+        }
+        break;
+    case FTYPE_FD:
+        {
+            int fd;
+            if (s->fd >= 0) {
+                close(s->fd);
+                s->fd = -1;
+            }
+            fd = open(bs->filename, s->fd_open_flags | O_NONBLOCK);
+            if (fd >= 0) {
+                if (ioctl(fd, FDEJECT, 0) < 0)
+                    perror("FDEJECT");
+                close(fd);
+            }
+        }
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static int raw_set_locked(BlockDriverState *bs, int locked)
+{
+    BDRVRawState *s = bs->opaque;
+
+    switch(s->type) {
+    case FTYPE_CD:
+        if (ioctl (s->fd, CDROM_LOCKDOOR, locked) < 0) {
+            /* Note: an error can happen if the distribution automatically
+               mounts the CD-ROM */
+            //        perror("CDROM_LOCKDOOR");
+        }
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+    BDRVRawState *s = bs->opaque;
+
+    return ioctl(s->fd, req, buf);
+}
+#else
+
+static int fd_open(BlockDriverState *bs)
+{
+    return 0;
+}
+
+static int raw_is_inserted(BlockDriverState *bs)
+{
+    return 1;
+}
+
+static int raw_media_changed(BlockDriverState *bs)
+{
+    return -ENOTSUP;
+}
+
+static int raw_eject(BlockDriverState *bs, int eject_flag)
+{
+    return -ENOTSUP;
+}
+
+static int raw_set_locked(BlockDriverState *bs, int locked)
+{
+    return -ENOTSUP;
+}
+
+static int raw_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+    return -ENOTSUP;
+}
+#endif /* !linux */
+
+BlockDriver bdrv_host_device = {
+    "host_device",
+    sizeof(BDRVRawState),
+    NULL, /* no probe for protocols */
+    hdev_open,
+    NULL,
+    NULL,
+    raw_close,
+    NULL,
+    raw_flush,
+
+#ifdef CONFIG_AIO
+    .bdrv_aio_read = raw_aio_read,
+    .bdrv_aio_write = raw_aio_write,
+    .bdrv_aio_cancel = raw_aio_cancel,
+    .aiocb_size = sizeof(RawAIOCB),
+#endif
+    .bdrv_pread = raw_pread,
+    .bdrv_pwrite = raw_pwrite,
+    .bdrv_getlength = raw_getlength,
+
+    /* removable device support */
+    .bdrv_is_inserted = raw_is_inserted,
+    .bdrv_media_changed = raw_media_changed,
+    .bdrv_eject = raw_eject,
+    .bdrv_set_locked = raw_set_locked,
+    /* generic scsi device */
+    .bdrv_ioctl = raw_ioctl,
+};
diff --git a/block-raw-win32.c b/block-raw-win32.c
new file mode 100644
index 0000000..71404ac
--- /dev/null
+++ b/block-raw-win32.c
@@ -0,0 +1,526 @@
+/*
+ * Block driver for RAW files (win32)
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "block_int.h"
+#include <assert.h>
+#include <winioctl.h>
+
+//#define WIN32_AIO
+
+#define FTYPE_FILE 0
+#define FTYPE_CD     1
+#define FTYPE_HARDDISK 2
+
+typedef struct BDRVRawState {
+    HANDLE hfile;
+    int type;
+    char drive_path[16]; /* format: "d:\" */
+} BDRVRawState;
+
+typedef struct RawAIOCB {
+    BlockDriverAIOCB common;
+    HANDLE hEvent;
+    OVERLAPPED ov;
+    int count;
+} RawAIOCB;
+
+int qemu_ftruncate64(int fd, int64_t length)
+{
+    LARGE_INTEGER li;
+    LONG high;
+    HANDLE h;
+    BOOL res;
+
+    if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0)
+	return -1;
+
+    h = (HANDLE)_get_osfhandle(fd);
+
+    /* get current position, ftruncate do not change position */
+    li.HighPart = 0;
+    li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT);
+    if (li.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
+	return -1;
+
+    high = length >> 32;
+    if (!SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN))
+	return -1;
+    res = SetEndOfFile(h);
+
+    /* back to old position */
+    SetFilePointer(h, li.LowPart, &li.HighPart, FILE_BEGIN);
+    return res ? 0 : -1;
+}
+
+static int set_sparse(int fd)
+{
+    DWORD returned;
+    return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE,
+				 NULL, 0, NULL, 0, &returned, NULL);
+}
+
+static int raw_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVRawState *s = bs->opaque;
+    int access_flags, create_flags;
+    DWORD overlapped;
+
+    s->type = FTYPE_FILE;
+
+    if ((flags & BDRV_O_ACCESS) == O_RDWR) {
+        access_flags = GENERIC_READ | GENERIC_WRITE;
+    } else {
+        access_flags = GENERIC_READ;
+    }
+    if (flags & BDRV_O_CREAT) {
+        create_flags = CREATE_ALWAYS;
+    } else {
+        create_flags = OPEN_EXISTING;
+    }
+#ifdef WIN32_AIO
+    overlapped = FILE_FLAG_OVERLAPPED;
+#else
+    overlapped = FILE_ATTRIBUTE_NORMAL;
+#endif
+    if (flags & BDRV_O_DIRECT)
+        overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
+    s->hfile = CreateFile(filename, access_flags,
+                          FILE_SHARE_READ, NULL,
+                          create_flags, overlapped, NULL);
+    if (s->hfile == INVALID_HANDLE_VALUE) {
+        int err = GetLastError();
+
+        if (err == ERROR_ACCESS_DENIED)
+            return -EACCES;
+        return -1;
+    }
+    return 0;
+}
+
+static int raw_pread(BlockDriverState *bs, int64_t offset,
+                     uint8_t *buf, int count)
+{
+    BDRVRawState *s = bs->opaque;
+    OVERLAPPED ov;
+    DWORD ret_count;
+    int ret;
+
+    memset(&ov, 0, sizeof(ov));
+    ov.Offset = offset;
+    ov.OffsetHigh = offset >> 32;
+    ret = ReadFile(s->hfile, buf, count, &ret_count, &ov);
+    if (!ret) {
+#ifdef WIN32_AIO
+        ret = GetOverlappedResult(s->hfile, &ov, &ret_count, TRUE);
+        if (!ret)
+            return -EIO;
+        else
+#endif
+            return ret_count;
+    }
+    return ret_count;
+}
+
+static int raw_pwrite(BlockDriverState *bs, int64_t offset,
+                      const uint8_t *buf, int count)
+{
+    BDRVRawState *s = bs->opaque;
+    OVERLAPPED ov;
+    DWORD ret_count;
+    int ret;
+
+    memset(&ov, 0, sizeof(ov));
+    ov.Offset = offset;
+    ov.OffsetHigh = offset >> 32;
+    ret = WriteFile(s->hfile, buf, count, &ret_count, &ov);
+    if (!ret) {
+#ifdef WIN32_AIO
+        ret = GetOverlappedResult(s->hfile, &ov, &ret_count, TRUE);
+        if (!ret)
+            return -EIO;
+        else
+#endif
+            return ret_count;
+    }
+    return ret_count;
+}
+
+#ifdef WIN32_AIO
+static void raw_aio_cb(void *opaque)
+{
+    RawAIOCB *acb = opaque;
+    BlockDriverState *bs = acb->common.bs;
+    BDRVRawState *s = bs->opaque;
+    DWORD ret_count;
+    int ret;
+
+    ret = GetOverlappedResult(s->hfile, &acb->ov, &ret_count, TRUE);
+    if (!ret || ret_count != acb->count) {
+        acb->common.cb(acb->common.opaque, -EIO);
+    } else {
+        acb->common.cb(acb->common.opaque, 0);
+    }
+}
+
+static RawAIOCB *raw_aio_setup(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    RawAIOCB *acb;
+    int64_t offset;
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (acb->hEvent) {
+        acb->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+        if (!acb->hEvent) {
+            qemu_aio_release(acb);
+            return NULL;
+        }
+    }
+    memset(&acb->ov, 0, sizeof(acb->ov));
+    offset = sector_num * 512;
+    acb->ov.Offset = offset;
+    acb->ov.OffsetHigh = offset >> 32;
+    acb->ov.hEvent = acb->hEvent;
+    acb->count = nb_sectors * 512;
+    qemu_add_wait_object(acb->ov.hEvent, raw_aio_cb, acb);
+    return acb;
+}
+
+static BlockDriverAIOCB *raw_aio_read(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BDRVRawState *s = bs->opaque;
+    RawAIOCB *acb;
+    int ret;
+
+    acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
+    if (!acb)
+        return NULL;
+    ret = ReadFile(s->hfile, buf, acb->count, NULL, &acb->ov);
+    if (!ret) {
+        qemu_aio_release(acb);
+        return NULL;
+    }
+    qemu_aio_release(acb);
+    return (BlockDriverAIOCB *)acb;
+}
+
+static BlockDriverAIOCB *raw_aio_write(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BDRVRawState *s = bs->opaque;
+    RawAIOCB *acb;
+    int ret;
+
+    acb = raw_aio_setup(bs, sector_num, buf, nb_sectors, cb, opaque);
+    if (!acb)
+        return NULL;
+    ret = WriteFile(s->hfile, buf, acb->count, NULL, &acb->ov);
+    if (!ret) {
+        qemu_aio_release(acb);
+        return NULL;
+    }
+    qemu_aio_release(acb);
+    return (BlockDriverAIOCB *)acb;
+}
+
+static void raw_aio_cancel(BlockDriverAIOCB *blockacb)
+{
+    RawAIOCB *acb = (RawAIOCB *)blockacb;
+    BlockDriverState *bs = acb->common.bs;
+    BDRVRawState *s = bs->opaque;
+
+    qemu_del_wait_object(acb->ov.hEvent, raw_aio_cb, acb);
+    /* XXX: if more than one async I/O it is not correct */
+    CancelIo(s->hfile);
+    qemu_aio_release(acb);
+}
+#endif /* #if WIN32_AIO */
+
+static void raw_flush(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    FlushFileBuffers(s->hfile);
+}
+
+static void raw_close(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    CloseHandle(s->hfile);
+}
+
+static int raw_truncate(BlockDriverState *bs, int64_t offset)
+{
+    BDRVRawState *s = bs->opaque;
+    DWORD low, high;
+
+    low = offset;
+    high = offset >> 32;
+    if (!SetFilePointer(s->hfile, low, &high, FILE_BEGIN))
+	return -EIO;
+    if (!SetEndOfFile(s->hfile))
+        return -EIO;
+    return 0;
+}
+
+static int64_t raw_getlength(BlockDriverState *bs)
+{
+    BDRVRawState *s = bs->opaque;
+    LARGE_INTEGER l;
+    ULARGE_INTEGER available, total, total_free;
+    DISK_GEOMETRY_EX dg;
+    DWORD count;
+    BOOL status;
+
+    switch(s->type) {
+    case FTYPE_FILE:
+        l.LowPart = GetFileSize(s->hfile, &l.HighPart);
+        if (l.LowPart == 0xffffffffUL && GetLastError() != NO_ERROR)
+            return -EIO;
+        break;
+    case FTYPE_CD:
+        if (!GetDiskFreeSpaceEx(s->drive_path, &available, &total, &total_free))
+            return -EIO;
+        l.QuadPart = total.QuadPart;
+        break;
+    case FTYPE_HARDDISK:
+        status = DeviceIoControl(s->hfile, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX,
+                                 NULL, 0, &dg, sizeof(dg), &count, NULL);
+        if (status != 0) {
+            l = dg.DiskSize;
+        }
+        break;
+    default:
+        return -EIO;
+    }
+    return l.QuadPart;
+}
+
+static int raw_create(const char *filename, int64_t total_size,
+                      const char *backing_file, int flags)
+{
+    int fd;
+
+    if (flags || backing_file)
+        return -ENOTSUP;
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
+              0644);
+    if (fd < 0)
+        return -EIO;
+    set_sparse(fd);
+    ftruncate(fd, total_size * 512);
+    close(fd);
+    return 0;
+}
+
+void qemu_aio_init(void)
+{
+}
+
+void qemu_aio_flush(void)
+{
+}
+
+void qemu_aio_wait(void)
+{
+    qemu_bh_poll();
+}
+
+BlockDriver bdrv_raw = {
+    "raw",
+    sizeof(BDRVRawState),
+    NULL, /* no probe for protocols */
+    raw_open,
+    NULL,
+    NULL,
+    raw_close,
+    raw_create,
+    raw_flush,
+
+#ifdef WIN32_AIO
+    .bdrv_aio_read = raw_aio_read,
+    .bdrv_aio_write = raw_aio_write,
+    .bdrv_aio_cancel = raw_aio_cancel,
+    .aiocb_size = sizeof(RawAIOCB);
+#endif
+    .bdrv_pread = raw_pread,
+    .bdrv_pwrite = raw_pwrite,
+    .bdrv_truncate = raw_truncate,
+    .bdrv_getlength = raw_getlength,
+};
+
+/***********************************************/
+/* host device */
+
+static int find_cdrom(char *cdrom_name, int cdrom_name_size)
+{
+    char drives[256], *pdrv = drives;
+    UINT type;
+
+    memset(drives, 0, sizeof(drives));
+    GetLogicalDriveStrings(sizeof(drives), drives);
+    while(pdrv[0] != '\0') {
+        type = GetDriveType(pdrv);
+        switch(type) {
+        case DRIVE_CDROM:
+            snprintf(cdrom_name, cdrom_name_size, "\\\\.\\%c:", pdrv[0]);
+            return 0;
+            break;
+        }
+        pdrv += lstrlen(pdrv) + 1;
+    }
+    return -1;
+}
+
+static int find_device_type(BlockDriverState *bs, const char *filename)
+{
+    BDRVRawState *s = bs->opaque;
+    UINT type;
+    const char *p;
+
+    if (strstart(filename, "\\\\.\\", &p) ||
+        strstart(filename, "//./", &p)) {
+        if (stristart(p, "PhysicalDrive", NULL))
+            return FTYPE_HARDDISK;
+        snprintf(s->drive_path, sizeof(s->drive_path), "%c:\\", p[0]);
+        type = GetDriveType(s->drive_path);
+        if (type == DRIVE_CDROM)
+            return FTYPE_CD;
+        else
+            return FTYPE_FILE;
+    } else {
+        return FTYPE_FILE;
+    }
+}
+
+static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVRawState *s = bs->opaque;
+    int access_flags, create_flags;
+    DWORD overlapped;
+    char device_name[64];
+
+    if (strstart(filename, "/dev/cdrom", NULL)) {
+        if (find_cdrom(device_name, sizeof(device_name)) < 0)
+            return -ENOENT;
+        filename = device_name;
+    } else {
+        /* transform drive letters into device name */
+        if (((filename[0] >= 'a' && filename[0] <= 'z') ||
+             (filename[0] >= 'A' && filename[0] <= 'Z')) &&
+            filename[1] == ':' && filename[2] == '\0') {
+            snprintf(device_name, sizeof(device_name), "\\\\.\\%c:", filename[0]);
+            filename = device_name;
+        }
+    }
+    s->type = find_device_type(bs, filename);
+
+    if ((flags & BDRV_O_ACCESS) == O_RDWR) {
+        access_flags = GENERIC_READ | GENERIC_WRITE;
+    } else {
+        access_flags = GENERIC_READ;
+    }
+    create_flags = OPEN_EXISTING;
+
+#ifdef WIN32_AIO
+    overlapped = FILE_FLAG_OVERLAPPED;
+#else
+    overlapped = FILE_ATTRIBUTE_NORMAL;
+#endif
+    if (flags & BDRV_O_DIRECT)
+        overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
+    s->hfile = CreateFile(filename, access_flags,
+                          FILE_SHARE_READ, NULL,
+                          create_flags, overlapped, NULL);
+    if (s->hfile == INVALID_HANDLE_VALUE) {
+        int err = GetLastError();
+
+        if (err == ERROR_ACCESS_DENIED)
+            return -EACCES;
+        return -1;
+    }
+    return 0;
+}
+
+#if 0
+/***********************************************/
+/* removable device additional commands */
+
+static int raw_is_inserted(BlockDriverState *bs)
+{
+    return 1;
+}
+
+static int raw_media_changed(BlockDriverState *bs)
+{
+    return -ENOTSUP;
+}
+
+static int raw_eject(BlockDriverState *bs, int eject_flag)
+{
+    DWORD ret_count;
+
+    if (s->type == FTYPE_FILE)
+        return -ENOTSUP;
+    if (eject_flag) {
+        DeviceIoControl(s->hfile, IOCTL_STORAGE_EJECT_MEDIA,
+                        NULL, 0, NULL, 0, &lpBytesReturned, NULL);
+    } else {
+        DeviceIoControl(s->hfile, IOCTL_STORAGE_LOAD_MEDIA,
+                        NULL, 0, NULL, 0, &lpBytesReturned, NULL);
+    }
+}
+
+static int raw_set_locked(BlockDriverState *bs, int locked)
+{
+    return -ENOTSUP;
+}
+#endif
+
+BlockDriver bdrv_host_device = {
+    "host_device",
+    sizeof(BDRVRawState),
+    NULL, /* no probe for protocols */
+    hdev_open,
+    NULL,
+    NULL,
+    raw_close,
+    NULL,
+    raw_flush,
+
+#ifdef WIN32_AIO
+    .bdrv_aio_read = raw_aio_read,
+    .bdrv_aio_write = raw_aio_write,
+    .bdrv_aio_cancel = raw_aio_cancel,
+    .aiocb_size = sizeof(RawAIOCB);
+#endif
+    .bdrv_pread = raw_pread,
+    .bdrv_pwrite = raw_pwrite,
+    .bdrv_getlength = raw_getlength,
+};
diff --git a/block-vmdk.c b/block-vmdk.c
new file mode 100644
index 0000000..8d67c2f
--- /dev/null
+++ b/block-vmdk.c
@@ -0,0 +1,834 @@
+/*
+ * Block driver for the VMDK format
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ * Copyright (c) 2005 Filip Navara
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "block_int.h"
+
+#define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D')
+#define VMDK4_MAGIC (('K' << 24) | ('D' << 16) | ('M' << 8) | 'V')
+
+typedef struct {
+    uint32_t version;
+    uint32_t flags;
+    uint32_t disk_sectors;
+    uint32_t granularity;
+    uint32_t l1dir_offset;
+    uint32_t l1dir_size;
+    uint32_t file_sectors;
+    uint32_t cylinders;
+    uint32_t heads;
+    uint32_t sectors_per_track;
+} VMDK3Header;
+
+typedef struct {
+    uint32_t version;
+    uint32_t flags;
+    int64_t capacity;
+    int64_t granularity;
+    int64_t desc_offset;
+    int64_t desc_size;
+    int32_t num_gtes_per_gte;
+    int64_t rgd_offset;
+    int64_t gd_offset;
+    int64_t grain_offset;
+    char filler[1];
+    char check_bytes[4];
+} __attribute__((packed)) VMDK4Header;
+
+#define L2_CACHE_SIZE 16
+
+typedef struct BDRVVmdkState {
+    BlockDriverState *hd;
+    int64_t l1_table_offset;
+    int64_t l1_backup_table_offset;
+    uint32_t *l1_table;
+    uint32_t *l1_backup_table;
+    unsigned int l1_size;
+    uint32_t l1_entry_sectors;
+
+    unsigned int l2_size;
+    uint32_t *l2_cache;
+    uint32_t l2_cache_offsets[L2_CACHE_SIZE];
+    uint32_t l2_cache_counts[L2_CACHE_SIZE];
+
+    unsigned int cluster_sectors;
+    uint32_t parent_cid;
+    int is_parent;
+} BDRVVmdkState;
+
+typedef struct VmdkMetaData {
+    uint32_t offset;
+    unsigned int l1_index;
+    unsigned int l2_index;
+    unsigned int l2_offset;
+    int valid;
+} VmdkMetaData;
+
+typedef struct ActiveBDRVState{
+    BlockDriverState *hd;            // active image handler
+    uint64_t cluster_offset;         // current write offset
+}ActiveBDRVState;
+
+static ActiveBDRVState activeBDRV;
+
+
+static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    uint32_t magic;
+
+    if (buf_size < 4)
+        return 0;
+    magic = be32_to_cpu(*(uint32_t *)buf);
+    if (magic == VMDK3_MAGIC ||
+        magic == VMDK4_MAGIC)
+        return 100;
+    else
+        return 0;
+}
+
+#define CHECK_CID 1
+
+#define SECTOR_SIZE 512
+#define DESC_SIZE 20*SECTOR_SIZE	// 20 sectors of 512 bytes each
+#define HEADER_SIZE 512   			// first sector of 512 bytes
+
+static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
+{
+    BDRVVmdkState *s = bs->opaque;
+    char desc[DESC_SIZE];
+    uint32_t cid;
+    const char *p_name, *cid_str;
+    size_t cid_str_size;
+
+    /* the descriptor offset = 0x200 */
+    if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+        return 0;
+
+    if (parent) {
+        cid_str = "parentCID";
+        cid_str_size = sizeof("parentCID");
+    } else {
+        cid_str = "CID";
+        cid_str_size = sizeof("CID");
+    }
+
+    if ((p_name = strstr(desc,cid_str)) != 0) {
+        p_name += cid_str_size;
+        sscanf(p_name,"%x",&cid);
+    }
+
+    return cid;
+}
+
+static int vmdk_write_cid(BlockDriverState *bs, uint32_t cid)
+{
+    BDRVVmdkState *s = bs->opaque;
+    char desc[DESC_SIZE], tmp_desc[DESC_SIZE];
+    char *p_name, *tmp_str;
+
+    /* the descriptor offset = 0x200 */
+    if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+        return -1;
+
+    tmp_str = strstr(desc,"parentCID");
+    pstrcpy(tmp_desc, sizeof(tmp_desc), tmp_str);
+    if ((p_name = strstr(desc,"CID")) != 0) {
+        p_name += sizeof("CID");
+        snprintf(p_name, sizeof(desc) - (p_name - desc), "%x\n", cid);
+        pstrcat(desc, sizeof(desc), tmp_desc);
+    }
+
+    if (bdrv_pwrite(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+        return -1;
+    return 0;
+}
+
+static int vmdk_is_cid_valid(BlockDriverState *bs)
+{
+#ifdef CHECK_CID
+    BDRVVmdkState *s = bs->opaque;
+    BlockDriverState *p_bs = s->hd->backing_hd;
+    uint32_t cur_pcid;
+
+    if (p_bs) {
+        cur_pcid = vmdk_read_cid(p_bs,0);
+        if (s->parent_cid != cur_pcid)
+            // CID not valid
+            return 0;
+    }
+#endif
+    // CID valid
+    return 1;
+}
+
+static int vmdk_snapshot_create(const char *filename, const char *backing_file)
+{
+    int snp_fd, p_fd;
+    uint32_t p_cid;
+    char *p_name, *gd_buf, *rgd_buf;
+    const char *real_filename, *temp_str;
+    VMDK4Header header;
+    uint32_t gde_entries, gd_size;
+    int64_t gd_offset, rgd_offset, capacity, gt_size;
+    char p_desc[DESC_SIZE], s_desc[DESC_SIZE], hdr[HEADER_SIZE];
+    static const char desc_template[] =
+    "# Disk DescriptorFile\n"
+    "version=1\n"
+    "CID=%x\n"
+    "parentCID=%x\n"
+    "createType=\"monolithicSparse\"\n"
+    "parentFileNameHint=\"%s\"\n"
+    "\n"
+    "# Extent description\n"
+    "RW %u SPARSE \"%s\"\n"
+    "\n"
+    "# The Disk Data Base \n"
+    "#DDB\n"
+    "\n";
+
+    snp_fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644);
+    if (snp_fd < 0)
+        return -1;
+    p_fd = open(backing_file, O_RDONLY | O_BINARY | O_LARGEFILE);
+    if (p_fd < 0) {
+        close(snp_fd);
+        return -1;
+    }
+
+    /* read the header */
+    if (lseek(p_fd, 0x0, SEEK_SET) == -1)
+        goto fail;
+    if (read(p_fd, hdr, HEADER_SIZE) != HEADER_SIZE)
+        goto fail;
+
+    /* write the header */
+    if (lseek(snp_fd, 0x0, SEEK_SET) == -1)
+        goto fail;
+    if (write(snp_fd, hdr, HEADER_SIZE) == -1)
+        goto fail;
+
+    memset(&header, 0, sizeof(header));
+    memcpy(&header,&hdr[4], sizeof(header)); // skip the VMDK4_MAGIC
+
+    ftruncate(snp_fd, header.grain_offset << 9);
+    /* the descriptor offset = 0x200 */
+    if (lseek(p_fd, 0x200, SEEK_SET) == -1)
+        goto fail;
+    if (read(p_fd, p_desc, DESC_SIZE) != DESC_SIZE)
+        goto fail;
+
+    if ((p_name = strstr(p_desc,"CID")) != 0) {
+        p_name += sizeof("CID");
+        sscanf(p_name,"%x",&p_cid);
+    }
+
+    real_filename = filename;
+    if ((temp_str = strrchr(real_filename, '\\')) != NULL)
+        real_filename = temp_str + 1;
+    if ((temp_str = strrchr(real_filename, '/')) != NULL)
+        real_filename = temp_str + 1;
+    if ((temp_str = strrchr(real_filename, ':')) != NULL)
+        real_filename = temp_str + 1;
+
+    snprintf(s_desc, sizeof(s_desc), desc_template, p_cid, p_cid, backing_file,
+             (uint32_t)header.capacity, real_filename);
+
+    /* write the descriptor */
+    if (lseek(snp_fd, 0x200, SEEK_SET) == -1)
+        goto fail;
+    if (write(snp_fd, s_desc, strlen(s_desc)) == -1)
+        goto fail;
+
+    gd_offset = header.gd_offset * SECTOR_SIZE;     // offset of GD table
+    rgd_offset = header.rgd_offset * SECTOR_SIZE;   // offset of RGD table
+    capacity = header.capacity * SECTOR_SIZE;       // Extent size
+    /*
+     * Each GDE span 32M disk, means:
+     * 512 GTE per GT, each GTE points to grain
+     */
+    gt_size = (int64_t)header.num_gtes_per_gte * header.granularity * SECTOR_SIZE;
+    if (!gt_size)
+        goto fail;
+    gde_entries = (uint32_t)(capacity / gt_size);  // number of gde/rgde
+    gd_size = gde_entries * sizeof(uint32_t);
+
+    /* write RGD */
+    rgd_buf = qemu_malloc(gd_size);
+    if (!rgd_buf)
+        goto fail;
+    if (lseek(p_fd, rgd_offset, SEEK_SET) == -1)
+        goto fail_rgd;
+    if (read(p_fd, rgd_buf, gd_size) != gd_size)
+        goto fail_rgd;
+    if (lseek(snp_fd, rgd_offset, SEEK_SET) == -1)
+        goto fail_rgd;
+    if (write(snp_fd, rgd_buf, gd_size) == -1)
+        goto fail_rgd;
+    qemu_free(rgd_buf);
+
+    /* write GD */
+    gd_buf = qemu_malloc(gd_size);
+    if (!gd_buf)
+        goto fail_rgd;
+    if (lseek(p_fd, gd_offset, SEEK_SET) == -1)
+        goto fail_gd;
+    if (read(p_fd, gd_buf, gd_size) != gd_size)
+        goto fail_gd;
+    if (lseek(snp_fd, gd_offset, SEEK_SET) == -1)
+        goto fail_gd;
+    if (write(snp_fd, gd_buf, gd_size) == -1)
+        goto fail_gd;
+    qemu_free(gd_buf);
+
+    close(p_fd);
+    close(snp_fd);
+    return 0;
+
+    fail_gd:
+    qemu_free(gd_buf);
+    fail_rgd:
+    qemu_free(rgd_buf);
+    fail:
+    close(p_fd);
+    close(snp_fd);
+    return -1;
+}
+
+static void vmdk_parent_close(BlockDriverState *bs)
+{
+    if (bs->backing_hd)
+        bdrv_close(bs->backing_hd);
+}
+
+int parent_open = 0;
+static int vmdk_parent_open(BlockDriverState *bs, const char * filename)
+{
+    BDRVVmdkState *s = bs->opaque;
+    char *p_name;
+    char desc[DESC_SIZE];
+    char parent_img_name[1024];
+
+    /* the descriptor offset = 0x200 */
+    if (bdrv_pread(s->hd, 0x200, desc, DESC_SIZE) != DESC_SIZE)
+        return -1;
+
+    if ((p_name = strstr(desc,"parentFileNameHint")) != 0) {
+        char *end_name;
+        struct stat file_buf;
+
+        p_name += sizeof("parentFileNameHint") + 1;
+        if ((end_name = strchr(p_name,'\"')) == 0)
+            return -1;
+        if ((end_name - p_name) > sizeof (s->hd->backing_file) - 1)
+            return -1;
+
+        strncpy(s->hd->backing_file, p_name, end_name - p_name);
+        if (stat(s->hd->backing_file, &file_buf) != 0) {
+            path_combine(parent_img_name, sizeof(parent_img_name),
+                         filename, s->hd->backing_file);
+        } else {
+            pstrcpy(parent_img_name, sizeof(parent_img_name),
+                    s->hd->backing_file);
+        }
+
+        s->hd->backing_hd = bdrv_new("");
+        if (!s->hd->backing_hd) {
+            failure:
+            bdrv_close(s->hd);
+            return -1;
+        }
+        parent_open = 1;
+        if (bdrv_open(s->hd->backing_hd, parent_img_name, BDRV_O_RDONLY) < 0)
+            goto failure;
+        parent_open = 0;
+    }
+
+    return 0;
+}
+
+static int vmdk_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVVmdkState *s = bs->opaque;
+    uint32_t magic;
+    int l1_size, i, ret;
+
+    if (parent_open)
+        // Parent must be opened as RO.
+        flags = BDRV_O_RDONLY;
+
+    ret = bdrv_file_open(&s->hd, filename, flags);
+    if (ret < 0)
+        return ret;
+    if (bdrv_pread(s->hd, 0, &magic, sizeof(magic)) != sizeof(magic))
+        goto fail;
+
+    magic = be32_to_cpu(magic);
+    if (magic == VMDK3_MAGIC) {
+        VMDK3Header header;
+
+        if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header))
+            goto fail;
+        s->cluster_sectors = le32_to_cpu(header.granularity);
+        s->l2_size = 1 << 9;
+        s->l1_size = 1 << 6;
+        bs->total_sectors = le32_to_cpu(header.disk_sectors);
+        s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
+        s->l1_backup_table_offset = 0;
+        s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+    } else if (magic == VMDK4_MAGIC) {
+        VMDK4Header header;
+
+        if (bdrv_pread(s->hd, sizeof(magic), &header, sizeof(header)) != sizeof(header))
+            goto fail;
+        bs->total_sectors = le64_to_cpu(header.capacity);
+        s->cluster_sectors = le64_to_cpu(header.granularity);
+        s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
+        s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+        if (s->l1_entry_sectors <= 0)
+            goto fail;
+        s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
+            / s->l1_entry_sectors;
+        s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
+        s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
+
+        if (parent_open)
+            s->is_parent = 1;
+        else
+            s->is_parent = 0;
+
+        // try to open parent images, if exist
+        if (vmdk_parent_open(bs, filename) != 0)
+            goto fail;
+        // write the CID once after the image creation
+        s->parent_cid = vmdk_read_cid(bs,1);
+    } else {
+        goto fail;
+    }
+
+    /* read the L1 table */
+    l1_size = s->l1_size * sizeof(uint32_t);
+    s->l1_table = qemu_malloc(l1_size);
+    if (!s->l1_table)
+        goto fail;
+    if (bdrv_pread(s->hd, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
+        goto fail;
+    for(i = 0; i < s->l1_size; i++) {
+        le32_to_cpus(&s->l1_table[i]);
+    }
+
+    if (s->l1_backup_table_offset) {
+        s->l1_backup_table = qemu_malloc(l1_size);
+        if (!s->l1_backup_table)
+            goto fail;
+        if (bdrv_pread(s->hd, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
+            goto fail;
+        for(i = 0; i < s->l1_size; i++) {
+            le32_to_cpus(&s->l1_backup_table[i]);
+        }
+    }
+
+    s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
+    if (!s->l2_cache)
+        goto fail;
+    return 0;
+ fail:
+    qemu_free(s->l1_backup_table);
+    qemu_free(s->l1_table);
+    qemu_free(s->l2_cache);
+    bdrv_delete(s->hd);
+    return -1;
+}
+
+static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
+                                   uint64_t offset, int allocate);
+
+static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
+                             uint64_t offset, int allocate)
+{
+    uint64_t parent_cluster_offset;
+    BDRVVmdkState *s = bs->opaque;
+    uint8_t  whole_grain[s->cluster_sectors*512];        // 128 sectors * 512 bytes each = grain size 64KB
+
+    // we will be here if it's first write on non-exist grain(cluster).
+    // try to read from parent image, if exist
+    if (s->hd->backing_hd) {
+        BDRVVmdkState *ps = s->hd->backing_hd->opaque;
+
+        if (!vmdk_is_cid_valid(bs))
+            return -1;
+
+        parent_cluster_offset = get_cluster_offset(s->hd->backing_hd, NULL, offset, allocate);
+
+        if (parent_cluster_offset) {
+            BDRVVmdkState *act_s = activeBDRV.hd->opaque;
+
+            if (bdrv_pread(ps->hd, parent_cluster_offset, whole_grain, ps->cluster_sectors*512) != ps->cluster_sectors*512)
+                return -1;
+
+            //Write grain only into the active image
+            if (bdrv_pwrite(act_s->hd, activeBDRV.cluster_offset << 9, whole_grain, sizeof(whole_grain)) != sizeof(whole_grain))
+                return -1;
+        }
+    }
+    return 0;
+}
+
+static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
+{
+    BDRVVmdkState *s = bs->opaque;
+
+    /* update L2 table */
+    if (bdrv_pwrite(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
+                    &(m_data->offset), sizeof(m_data->offset)) != sizeof(m_data->offset))
+        return -1;
+    /* update backup L2 table */
+    if (s->l1_backup_table_offset != 0) {
+        m_data->l2_offset = s->l1_backup_table[m_data->l1_index];
+        if (bdrv_pwrite(s->hd, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
+                        &(m_data->offset), sizeof(m_data->offset)) != sizeof(m_data->offset))
+            return -1;
+    }
+
+    return 0;
+}
+
+static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
+                                   uint64_t offset, int allocate)
+{
+    BDRVVmdkState *s = bs->opaque;
+    unsigned int l1_index, l2_offset, l2_index;
+    int min_index, i, j;
+    uint32_t min_count, *l2_table, tmp = 0;
+    uint64_t cluster_offset;
+
+    if (m_data)
+        m_data->valid = 0;
+
+    l1_index = (offset >> 9) / s->l1_entry_sectors;
+    if (l1_index >= s->l1_size)
+        return 0;
+    l2_offset = s->l1_table[l1_index];
+    if (!l2_offset)
+        return 0;
+    for(i = 0; i < L2_CACHE_SIZE; i++) {
+        if (l2_offset == s->l2_cache_offsets[i]) {
+            /* increment the hit count */
+            if (++s->l2_cache_counts[i] == 0xffffffff) {
+                for(j = 0; j < L2_CACHE_SIZE; j++) {
+                    s->l2_cache_counts[j] >>= 1;
+                }
+            }
+            l2_table = s->l2_cache + (i * s->l2_size);
+            goto found;
+        }
+    }
+    /* not found: load a new entry in the least used one */
+    min_index = 0;
+    min_count = 0xffffffff;
+    for(i = 0; i < L2_CACHE_SIZE; i++) {
+        if (s->l2_cache_counts[i] < min_count) {
+            min_count = s->l2_cache_counts[i];
+            min_index = i;
+        }
+    }
+    l2_table = s->l2_cache + (min_index * s->l2_size);
+    if (bdrv_pread(s->hd, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
+                                                                        s->l2_size * sizeof(uint32_t))
+        return 0;
+
+    s->l2_cache_offsets[min_index] = l2_offset;
+    s->l2_cache_counts[min_index] = 1;
+ found:
+    l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
+    cluster_offset = le32_to_cpu(l2_table[l2_index]);
+
+    if (!cluster_offset) {
+        if (!allocate)
+            return 0;
+        // Avoid the L2 tables update for the images that have snapshots.
+        if (!s->is_parent) {
+            cluster_offset = bdrv_getlength(s->hd);
+            bdrv_truncate(s->hd, cluster_offset + (s->cluster_sectors << 9));
+
+            cluster_offset >>= 9;
+            tmp = cpu_to_le32(cluster_offset);
+            l2_table[l2_index] = tmp;
+            // Save the active image state
+            activeBDRV.cluster_offset = cluster_offset;
+            activeBDRV.hd = bs;
+        }
+        /* First of all we write grain itself, to avoid race condition
+         * that may to corrupt the image.
+         * This problem may occur because of insufficient space on host disk
+         * or inappropriate VM shutdown.
+         */
+        if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
+            return 0;
+
+        if (m_data) {
+            m_data->offset = tmp;
+            m_data->l1_index = l1_index;
+            m_data->l2_index = l2_index;
+            m_data->l2_offset = l2_offset;
+            m_data->valid = 1;
+        }
+    }
+    cluster_offset <<= 9;
+    return cluster_offset;
+}
+
+static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
+                             int nb_sectors, int *pnum)
+{
+    BDRVVmdkState *s = bs->opaque;
+    int index_in_cluster, n;
+    uint64_t cluster_offset;
+
+    cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
+    index_in_cluster = sector_num % s->cluster_sectors;
+    n = s->cluster_sectors - index_in_cluster;
+    if (n > nb_sectors)
+        n = nb_sectors;
+    *pnum = n;
+    return (cluster_offset != 0);
+}
+
+static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVVmdkState *s = bs->opaque;
+    int index_in_cluster, n, ret;
+    uint64_t cluster_offset;
+
+    while (nb_sectors > 0) {
+        cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
+        index_in_cluster = sector_num % s->cluster_sectors;
+        n = s->cluster_sectors - index_in_cluster;
+        if (n > nb_sectors)
+            n = nb_sectors;
+        if (!cluster_offset) {
+            // try to read from parent image, if exist
+            if (s->hd->backing_hd) {
+                if (!vmdk_is_cid_valid(bs))
+                    return -1;
+                ret = bdrv_read(s->hd->backing_hd, sector_num, buf, n);
+                if (ret < 0)
+                    return -1;
+            } else {
+                memset(buf, 0, 512 * n);
+            }
+        } else {
+            if(bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
+                return -1;
+        }
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+    }
+    return 0;
+}
+
+static int vmdk_write(BlockDriverState *bs, int64_t sector_num,
+                     const uint8_t *buf, int nb_sectors)
+{
+    BDRVVmdkState *s = bs->opaque;
+    VmdkMetaData m_data;
+    int index_in_cluster, n;
+    uint64_t cluster_offset;
+    static int cid_update = 0;
+
+    if (sector_num > bs->total_sectors) {
+        fprintf(stderr,
+                "(VMDK) Wrong offset: sector_num=0x%" PRIx64
+                " total_sectors=0x%" PRIx64 "\n",
+                sector_num, bs->total_sectors);
+        return -1;
+    }
+
+    while (nb_sectors > 0) {
+        index_in_cluster = sector_num & (s->cluster_sectors - 1);
+        n = s->cluster_sectors - index_in_cluster;
+        if (n > nb_sectors)
+            n = nb_sectors;
+        cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1);
+        if (!cluster_offset)
+            return -1;
+
+        if (bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
+            return -1;
+        if (m_data.valid) {
+            /* update L2 tables */
+            if (vmdk_L2update(bs, &m_data) == -1)
+                return -1;
+        }
+        nb_sectors -= n;
+        sector_num += n;
+        buf += n * 512;
+
+        // update CID on the first write every time the virtual disk is opened
+        if (!cid_update) {
+            vmdk_write_cid(bs, time(NULL));
+            cid_update++;
+        }
+    }
+    return 0;
+}
+
+static int vmdk_create(const char *filename, int64_t total_size,
+                       const char *backing_file, int flags)
+{
+    int fd, i;
+    VMDK4Header header;
+    uint32_t tmp, magic, grains, gd_size, gt_size, gt_count;
+    static const char desc_template[] =
+        "# Disk DescriptorFile\n"
+        "version=1\n"
+        "CID=%x\n"
+        "parentCID=ffffffff\n"
+        "createType=\"monolithicSparse\"\n"
+        "\n"
+        "# Extent description\n"
+        "RW %lu SPARSE \"%s\"\n"
+        "\n"
+        "# The Disk Data Base \n"
+        "#DDB\n"
+        "\n"
+        "ddb.virtualHWVersion = \"%d\"\n"
+        "ddb.geometry.cylinders = \"%lu\"\n"
+        "ddb.geometry.heads = \"16\"\n"
+        "ddb.geometry.sectors = \"63\"\n"
+        "ddb.adapterType = \"ide\"\n";
+    char desc[1024];
+    const char *real_filename, *temp_str;
+
+    /* XXX: add support for backing file */
+    if (backing_file) {
+        return vmdk_snapshot_create(filename, backing_file);
+    }
+
+    fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
+              0644);
+    if (fd < 0)
+        return -1;
+    magic = cpu_to_be32(VMDK4_MAGIC);
+    memset(&header, 0, sizeof(header));
+    header.version = cpu_to_le32(1);
+    header.flags = cpu_to_le32(3); /* ?? */
+    header.capacity = cpu_to_le64(total_size);
+    header.granularity = cpu_to_le64(128);
+    header.num_gtes_per_gte = cpu_to_le32(512);
+
+    grains = (total_size + header.granularity - 1) / header.granularity;
+    gt_size = ((header.num_gtes_per_gte * sizeof(uint32_t)) + 511) >> 9;
+    gt_count = (grains + header.num_gtes_per_gte - 1) / header.num_gtes_per_gte;
+    gd_size = (gt_count * sizeof(uint32_t) + 511) >> 9;
+
+    header.desc_offset = 1;
+    header.desc_size = 20;
+    header.rgd_offset = header.desc_offset + header.desc_size;
+    header.gd_offset = header.rgd_offset + gd_size + (gt_size * gt_count);
+    header.grain_offset =
+       ((header.gd_offset + gd_size + (gt_size * gt_count) +
+         header.granularity - 1) / header.granularity) *
+        header.granularity;
+
+    header.desc_offset = cpu_to_le64(header.desc_offset);
+    header.desc_size = cpu_to_le64(header.desc_size);
+    header.rgd_offset = cpu_to_le64(header.rgd_offset);
+    header.gd_offset = cpu_to_le64(header.gd_offset);
+    header.grain_offset = cpu_to_le64(header.grain_offset);
+
+    header.check_bytes[0] = 0xa;
+    header.check_bytes[1] = 0x20;
+    header.check_bytes[2] = 0xd;
+    header.check_bytes[3] = 0xa;
+
+    /* write all the data */
+    write(fd, &magic, sizeof(magic));
+    write(fd, &header, sizeof(header));
+
+    ftruncate(fd, header.grain_offset << 9);
+
+    /* write grain directory */
+    lseek(fd, le64_to_cpu(header.rgd_offset) << 9, SEEK_SET);
+    for (i = 0, tmp = header.rgd_offset + gd_size;
+         i < gt_count; i++, tmp += gt_size)
+        write(fd, &tmp, sizeof(tmp));
+
+    /* write backup grain directory */
+    lseek(fd, le64_to_cpu(header.gd_offset) << 9, SEEK_SET);
+    for (i = 0, tmp = header.gd_offset + gd_size;
+         i < gt_count; i++, tmp += gt_size)
+        write(fd, &tmp, sizeof(tmp));
+
+    /* compose the descriptor */
+    real_filename = filename;
+    if ((temp_str = strrchr(real_filename, '\\')) != NULL)
+        real_filename = temp_str + 1;
+    if ((temp_str = strrchr(real_filename, '/')) != NULL)
+        real_filename = temp_str + 1;
+    if ((temp_str = strrchr(real_filename, ':')) != NULL)
+        real_filename = temp_str + 1;
+    snprintf(desc, sizeof(desc), desc_template, (unsigned int)time(NULL),
+             (unsigned long)total_size, real_filename,
+             (flags & BLOCK_FLAG_COMPAT6 ? 6 : 4), total_size / (63 * 16));
+
+    /* write the descriptor */
+    lseek(fd, le64_to_cpu(header.desc_offset) << 9, SEEK_SET);
+    write(fd, desc, strlen(desc));
+
+    close(fd);
+    return 0;
+}
+
+static void vmdk_close(BlockDriverState *bs)
+{
+    BDRVVmdkState *s = bs->opaque;
+
+    qemu_free(s->l1_table);
+    qemu_free(s->l2_cache);
+    // try to close parent image, if exist
+    vmdk_parent_close(s->hd);
+    bdrv_delete(s->hd);
+}
+
+static void vmdk_flush(BlockDriverState *bs)
+{
+    BDRVVmdkState *s = bs->opaque;
+    bdrv_flush(s->hd);
+}
+
+BlockDriver bdrv_vmdk = {
+    "vmdk",
+    sizeof(BDRVVmdkState),
+    vmdk_probe,
+    vmdk_open,
+    vmdk_read,
+    vmdk_write,
+    vmdk_close,
+    vmdk_create,
+    vmdk_flush,
+    vmdk_is_allocated,
+};
diff --git a/block-vpc.c b/block-vpc.c
new file mode 100644
index 0000000..f76c451
--- /dev/null
+++ b/block-vpc.c
@@ -0,0 +1,239 @@
+/*
+ * Block driver for Conectix/Microsoft Virtual PC images
+ *
+ * Copyright (c) 2005 Alex Beregszaszi
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "block_int.h"
+
+/**************************************************************/
+
+#define HEADER_SIZE 512
+
+//#define CACHE
+
+// always big-endian
+struct vpc_subheader {
+    char magic[8]; // "conectix" / "cxsparse"
+    union {
+	struct {
+	    uint32_t unk1[2];
+	    uint32_t unk2; // always zero?
+	    uint32_t subheader_offset;
+	    uint32_t unk3; // some size?
+	    char creator[4]; // "vpc "
+	    uint16_t major;
+	    uint16_t minor;
+	    char guest[4]; // "Wi2k"
+	    uint32_t unk4[7];
+	    uint8_t vnet_id[16]; // virtual network id, purpose unknown
+	    // next 16 longs are used, but dunno the purpose
+	    // next 6 longs unknown, following 7 long maybe a serial
+	    char padding[HEADER_SIZE - 84];
+	} main;
+	struct {
+	    uint32_t unk1[2]; // all bits set
+	    uint32_t unk2; // always zero?
+	    uint32_t pagetable_offset;
+	    uint32_t unk3;
+	    uint32_t pagetable_entries; // 32bit/entry
+	    uint32_t pageentry_size; // 512*8*512
+	    uint32_t nb_sectors;
+	    char padding[HEADER_SIZE - 40];
+	} sparse;
+	char padding[HEADER_SIZE - 8];
+    } type;
+};
+
+typedef struct BDRVVPCState {
+    int fd;
+
+    int pagetable_entries;
+    uint32_t *pagetable;
+
+    uint32_t pageentry_size;
+#ifdef CACHE
+    uint8_t *pageentry_u8;
+    uint32_t *pageentry_u32;
+    uint16_t *pageentry_u16;
+
+    uint64_t last_bitmap;
+#endif
+} BDRVVPCState;
+
+static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename)
+{
+    if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8))
+	return 100;
+    return 0;
+}
+
+static int vpc_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    BDRVVPCState *s = bs->opaque;
+    int fd, i;
+    struct vpc_subheader header;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+
+    bs->read_only = 1; // no write support yet
+
+    s->fd = fd;
+
+    if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
+        goto fail;
+
+    if (strncmp(header.magic, "conectix", 8))
+        goto fail;
+    lseek(s->fd, be32_to_cpu(header.type.main.subheader_offset), SEEK_SET);
+
+    if (read(fd, &header, HEADER_SIZE) != HEADER_SIZE)
+        goto fail;
+
+    if (strncmp(header.magic, "cxsparse", 8))
+	goto fail;
+
+    bs->total_sectors = ((uint64_t)be32_to_cpu(header.type.sparse.pagetable_entries) *
+			be32_to_cpu(header.type.sparse.pageentry_size)) / 512;
+
+    lseek(s->fd, be32_to_cpu(header.type.sparse.pagetable_offset), SEEK_SET);
+
+    s->pagetable_entries = be32_to_cpu(header.type.sparse.pagetable_entries);
+    s->pagetable = qemu_malloc(s->pagetable_entries * 4);
+    if (!s->pagetable)
+	goto fail;
+    if (read(s->fd, s->pagetable, s->pagetable_entries * 4) !=
+	s->pagetable_entries * 4)
+	goto fail;
+    for (i = 0; i < s->pagetable_entries; i++)
+	be32_to_cpus(&s->pagetable[i]);
+
+    s->pageentry_size = be32_to_cpu(header.type.sparse.pageentry_size);
+#ifdef CACHE
+    s->pageentry_u8 = qemu_malloc(512);
+    if (!s->pageentry_u8)
+	goto fail;
+    s->pageentry_u32 = s->pageentry_u8;
+    s->pageentry_u16 = s->pageentry_u8;
+    s->last_pagetable = -1;
+#endif
+
+    return 0;
+ fail:
+    close(fd);
+    return -1;
+}
+
+static inline int seek_to_sector(BlockDriverState *bs, int64_t sector_num)
+{
+    BDRVVPCState *s = bs->opaque;
+    uint64_t offset = sector_num * 512;
+    uint64_t bitmap_offset, block_offset;
+    uint32_t pagetable_index, pageentry_index;
+
+    pagetable_index = offset / s->pageentry_size;
+    pageentry_index = (offset % s->pageentry_size) / 512;
+
+    if (pagetable_index > s->pagetable_entries || s->pagetable[pagetable_index] == 0xffffffff)
+	return -1; // not allocated
+
+    bitmap_offset = 512 * s->pagetable[pagetable_index];
+    block_offset = bitmap_offset + 512 + (512 * pageentry_index);
+
+//    printf("sector: %" PRIx64 ", index: %x, offset: %x, bioff: %" PRIx64 ", bloff: %" PRIx64 "\n",
+//	sector_num, pagetable_index, pageentry_index,
+//	bitmap_offset, block_offset);
+
+// disabled by reason
+#if 0
+#ifdef CACHE
+    if (bitmap_offset != s->last_bitmap)
+    {
+	lseek(s->fd, bitmap_offset, SEEK_SET);
+
+	s->last_bitmap = bitmap_offset;
+
+	// Scary! Bitmap is stored as big endian 32bit entries,
+	// while we used to look it up byte by byte
+	read(s->fd, s->pageentry_u8, 512);
+	for (i = 0; i < 128; i++)
+	    be32_to_cpus(&s->pageentry_u32[i]);
+    }
+
+    if ((s->pageentry_u8[pageentry_index / 8] >> (pageentry_index % 8)) & 1)
+	return -1;
+#else
+    lseek(s->fd, bitmap_offset + (pageentry_index / 8), SEEK_SET);
+
+    read(s->fd, &bitmap_entry, 1);
+
+    if ((bitmap_entry >> (pageentry_index % 8)) & 1)
+	return -1; // not allocated
+#endif
+#endif
+    lseek(s->fd, block_offset, SEEK_SET);
+
+    return 0;
+}
+
+static int vpc_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVVPCState *s = bs->opaque;
+    int ret;
+
+    while (nb_sectors > 0) {
+	if (!seek_to_sector(bs, sector_num))
+	{
+	    ret = read(s->fd, buf, 512);
+	    if (ret != 512)
+		return -1;
+	}
+	else
+            memset(buf, 0, 512);
+        nb_sectors--;
+        sector_num++;
+        buf += 512;
+    }
+    return 0;
+}
+
+static void vpc_close(BlockDriverState *bs)
+{
+    BDRVVPCState *s = bs->opaque;
+    qemu_free(s->pagetable);
+#ifdef CACHE
+    qemu_free(s->pageentry_u8);
+#endif
+    close(s->fd);
+}
+
+BlockDriver bdrv_vpc = {
+    "vpc",
+    sizeof(BDRVVPCState),
+    vpc_probe,
+    vpc_open,
+    vpc_read,
+    NULL,
+    vpc_close,
+};
diff --git a/block-vvfat.c b/block-vvfat.c
new file mode 100644
index 0000000..79804a7
--- /dev/null
+++ b/block-vvfat.c
@@ -0,0 +1,2847 @@
+/* vim:set shiftwidth=4 ts=8: */
+/*
+ * QEMU Block driver for virtual VFAT (shadows a local directory)
+ *
+ * Copyright (c) 2004,2005 Johannes E. Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <sys/stat.h>
+#include <dirent.h>
+#include <assert.h>
+#include "qemu-common.h"
+#include "block_int.h"
+
+#ifndef S_IWGRP
+#define S_IWGRP 0
+#endif
+#ifndef S_IWOTH
+#define S_IWOTH 0
+#endif
+
+/* TODO: add ":bootsector=blabla.img:" */
+/* LATER TODO: add automatic boot sector generation from
+    BOOTEASY.ASM and Ranish Partition Manager
+    Note that DOS assumes the system files to be the first files in the
+    file system (test if the boot sector still relies on that fact)! */
+/* MAYBE TODO: write block-visofs.c */
+/* TODO: call try_commit() only after a timeout */
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+
+#define DLOG(a) a
+
+#undef stderr
+#define stderr STDERR
+FILE* stderr = NULL;
+
+static void checkpoint(void);
+
+#ifdef __MINGW32__
+void nonono(const char* file, int line, const char* msg) {
+    fprintf(stderr, "Nonono! %s:%d %s\n", file, line, msg);
+    exit(-5);
+}
+#undef assert
+#define assert(a) do {if (!(a)) nonono(__FILE__, __LINE__, #a);}while(0)
+#endif
+
+#else
+
+#define DLOG(a)
+
+#endif
+
+/* dynamic array functions */
+typedef struct array_t {
+    char* pointer;
+    unsigned int size,next,item_size;
+} array_t;
+
+static inline void array_init(array_t* array,unsigned int item_size)
+{
+    array->pointer=0;
+    array->size=0;
+    array->next=0;
+    array->item_size=item_size;
+}
+
+static inline void array_free(array_t* array)
+{
+    if(array->pointer)
+        free(array->pointer);
+    array->size=array->next=0;
+}
+
+/* does not automatically grow */
+static inline void* array_get(array_t* array,unsigned int index) {
+    assert(index < array->next);
+    return array->pointer + index * array->item_size;
+}
+
+static inline int array_ensure_allocated(array_t* array, int index)
+{
+    if((index + 1) * array->item_size > array->size) {
+	int new_size = (index + 32) * array->item_size;
+	array->pointer = qemu_realloc(array->pointer, new_size);
+	if (!array->pointer)
+	    return -1;
+	array->size = new_size;
+	array->next = index + 1;
+    }
+
+    return 0;
+}
+
+static inline void* array_get_next(array_t* array) {
+    unsigned int next = array->next;
+    void* result;
+
+    if (array_ensure_allocated(array, next) < 0)
+	return NULL;
+
+    array->next = next + 1;
+    result = array_get(array, next);
+
+    return result;
+}
+
+static inline void* array_insert(array_t* array,unsigned int index,unsigned int count) {
+    if((array->next+count)*array->item_size>array->size) {
+	int increment=count*array->item_size;
+	array->pointer=qemu_realloc(array->pointer,array->size+increment);
+	if(!array->pointer)
+	    return 0;
+	array->size+=increment;
+    }
+    memmove(array->pointer+(index+count)*array->item_size,
+		array->pointer+index*array->item_size,
+		(array->next-index)*array->item_size);
+    array->next+=count;
+    return array->pointer+index*array->item_size;
+}
+
+/* this performs a "roll", so that the element which was at index_from becomes
+ * index_to, but the order of all other elements is preserved. */
+static inline int array_roll(array_t* array,int index_to,int index_from,int count)
+{
+    char* buf;
+    char* from;
+    char* to;
+    int is;
+
+    if(!array ||
+	    index_to<0 || index_to>=array->next ||
+	    index_from<0 || index_from>=array->next)
+	return -1;
+
+    if(index_to==index_from)
+	return 0;
+
+    is=array->item_size;
+    from=array->pointer+index_from*is;
+    to=array->pointer+index_to*is;
+    buf=malloc(is*count);
+    memcpy(buf,from,is*count);
+
+    if(index_to<index_from)
+	memmove(to+is*count,to,from-to);
+    else
+	memmove(from,from+is*count,to-from);
+
+    memcpy(to,buf,is*count);
+
+    free(buf);
+
+    return 0;
+}
+
+static inline int array_remove_slice(array_t* array,int index, int count)
+{
+    assert(index >=0);
+    assert(count > 0);
+    assert(index + count <= array->next);
+    if(array_roll(array,array->next-1,index,count))
+	return -1;
+    array->next -= count;
+    return 0;
+}
+
+static int array_remove(array_t* array,int index)
+{
+    return array_remove_slice(array, index, 1);
+}
+
+/* return the index for a given member */
+static int array_index(array_t* array, void* pointer)
+{
+    size_t offset = (char*)pointer - array->pointer;
+    assert((offset % array->item_size) == 0);
+    assert(offset/array->item_size < array->next);
+    return offset/array->item_size;
+}
+
+/* These structures are used to fake a disk and the VFAT filesystem.
+ * For this reason we need to use __attribute__((packed)). */
+
+typedef struct bootsector_t {
+    uint8_t jump[3];
+    uint8_t name[8];
+    uint16_t sector_size;
+    uint8_t sectors_per_cluster;
+    uint16_t reserved_sectors;
+    uint8_t number_of_fats;
+    uint16_t root_entries;
+    uint16_t total_sectors16;
+    uint8_t media_type;
+    uint16_t sectors_per_fat;
+    uint16_t sectors_per_track;
+    uint16_t number_of_heads;
+    uint32_t hidden_sectors;
+    uint32_t total_sectors;
+    union {
+        struct {
+	    uint8_t drive_number;
+	    uint8_t current_head;
+	    uint8_t signature;
+	    uint32_t id;
+	    uint8_t volume_label[11];
+	} __attribute__((packed)) fat16;
+	struct {
+	    uint32_t sectors_per_fat;
+	    uint16_t flags;
+	    uint8_t major,minor;
+	    uint32_t first_cluster_of_root_directory;
+	    uint16_t info_sector;
+	    uint16_t backup_boot_sector;
+	    uint16_t ignored;
+	} __attribute__((packed)) fat32;
+    } u;
+    uint8_t fat_type[8];
+    uint8_t ignored[0x1c0];
+    uint8_t magic[2];
+} __attribute__((packed)) bootsector_t;
+
+typedef struct {
+    uint8_t head;
+    uint8_t sector;
+    uint8_t cylinder;
+} mbr_chs_t;
+
+typedef struct partition_t {
+    uint8_t attributes; /* 0x80 = bootable */
+    mbr_chs_t start_CHS;
+    uint8_t   fs_type; /* 0x1 = FAT12, 0x6 = FAT16, 0xe = FAT16_LBA, 0xb = FAT32, 0xc = FAT32_LBA */
+    mbr_chs_t end_CHS;
+    uint32_t start_sector_long;
+    uint32_t length_sector_long;
+} __attribute__((packed)) partition_t;
+
+typedef struct mbr_t {
+    uint8_t ignored[0x1b8];
+    uint32_t nt_id;
+    uint8_t ignored2[2];
+    partition_t partition[4];
+    uint8_t magic[2];
+} __attribute__((packed)) mbr_t;
+
+typedef struct direntry_t {
+    uint8_t name[8];
+    uint8_t extension[3];
+    uint8_t attributes;
+    uint8_t reserved[2];
+    uint16_t ctime;
+    uint16_t cdate;
+    uint16_t adate;
+    uint16_t begin_hi;
+    uint16_t mtime;
+    uint16_t mdate;
+    uint16_t begin;
+    uint32_t size;
+} __attribute__((packed)) direntry_t;
+
+/* this structure are used to transparently access the files */
+
+typedef struct mapping_t {
+    /* begin is the first cluster, end is the last+1 */
+    uint32_t begin,end;
+    /* as s->directory is growable, no pointer may be used here */
+    unsigned int dir_index;
+    /* the clusters of a file may be in any order; this points to the first */
+    int first_mapping_index;
+    union {
+	/* offset is
+	 * - the offset in the file (in clusters) for a file, or
+	 * - the next cluster of the directory for a directory, and
+	 * - the address of the buffer for a faked entry
+	 */
+	struct {
+	    uint32_t offset;
+	} file;
+	struct {
+	    int parent_mapping_index;
+	    int first_dir_index;
+	} dir;
+    } info;
+    /* path contains the full path, i.e. it always starts with s->path */
+    char* path;
+
+    enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2,
+	MODE_DIRECTORY = 4, MODE_FAKED = 8,
+	MODE_DELETED = 16, MODE_RENAMED = 32 } mode;
+    int read_only;
+} mapping_t;
+
+#ifdef DEBUG
+static void print_direntry(const struct direntry_t*);
+static void print_mapping(const struct mapping_t* mapping);
+#endif
+
+/* here begins the real VVFAT driver */
+
+typedef struct BDRVVVFATState {
+    BlockDriverState* bs; /* pointer to parent */
+    unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
+    unsigned char first_sectors[0x40*0x200];
+
+    int fat_type; /* 16 or 32 */
+    array_t fat,directory,mapping;
+
+    unsigned int cluster_size;
+    unsigned int sectors_per_cluster;
+    unsigned int sectors_per_fat;
+    unsigned int sectors_of_root_directory;
+    uint32_t last_cluster_of_root_directory;
+    unsigned int faked_sectors; /* how many sectors are faked before file data */
+    uint32_t sector_count; /* total number of sectors of the partition */
+    uint32_t cluster_count; /* total number of clusters of this partition */
+    uint32_t max_fat_value;
+
+    int current_fd;
+    mapping_t* current_mapping;
+    unsigned char* cluster; /* points to current cluster */
+    unsigned char* cluster_buffer; /* points to a buffer to hold temp data */
+    unsigned int current_cluster;
+
+    /* write support */
+    BlockDriverState* write_target;
+    char* qcow_filename;
+    BlockDriverState* qcow;
+    void* fat2;
+    char* used_clusters;
+    array_t commits;
+    const char* path;
+    int downcase_short_names;
+} BDRVVVFATState;
+
+/* take the sector position spos and convert it to Cylinder/Head/Sector position
+ * if the position is outside the specified geometry, fill maximum value for CHS
+ * and return 1 to signal overflow.
+ */
+static int sector2CHS(BlockDriverState* bs, mbr_chs_t * chs, int spos){
+    int head,sector;
+    sector   = spos % (bs->secs);  spos/= bs->secs;
+    head     = spos % (bs->heads); spos/= bs->heads;
+    if(spos >= bs->cyls){
+        /* Overflow,
+        it happens if 32bit sector positions are used, while CHS is only 24bit.
+        Windows/Dos is said to take 1023/255/63 as nonrepresentable CHS */
+        chs->head     = 0xFF;
+        chs->sector   = 0xFF;
+        chs->cylinder = 0xFF;
+        return 1;
+    }
+    chs->head     = (uint8_t)head;
+    chs->sector   = (uint8_t)( (sector+1) | ((spos>>8)<<6) );
+    chs->cylinder = (uint8_t)spos;
+    return 0;
+}
+
+static void init_mbr(BDRVVVFATState* s)
+{
+    /* TODO: if the files mbr.img and bootsect.img exist, use them */
+    mbr_t* real_mbr=(mbr_t*)s->first_sectors;
+    partition_t* partition=&(real_mbr->partition[0]);
+    int lba;
+
+    memset(s->first_sectors,0,512);
+
+    /* Win NT Disk Signature */
+    real_mbr->nt_id= cpu_to_le32(0xbe1afdfa);
+
+    partition->attributes=0x80; /* bootable */
+
+    /* LBA is used when partition is outside the CHS geometry */
+    lba = sector2CHS(s->bs, &partition->start_CHS, s->first_sectors_number-1);
+    lba|= sector2CHS(s->bs, &partition->end_CHS,   s->sector_count);
+
+    /*LBA partitions are identified only by start/length_sector_long not by CHS*/
+    partition->start_sector_long =cpu_to_le32(s->first_sectors_number-1);
+    partition->length_sector_long=cpu_to_le32(s->sector_count - s->first_sectors_number+1);
+
+    /* FAT12/FAT16/FAT32 */
+    /* DOS uses different types when partition is LBA,
+       probably to prevent older versions from using CHS on them */
+    partition->fs_type= s->fat_type==12 ? 0x1:
+                        s->fat_type==16 ? (lba?0xe:0x06):
+                         /*fat_tyoe==32*/ (lba?0xc:0x0b);
+
+    real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
+}
+
+/* direntry functions */
+
+/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */
+static inline int short2long_name(char* dest,const char* src)
+{
+    int i;
+    int len;
+    for(i=0;i<129 && src[i];i++) {
+        dest[2*i]=src[i];
+	dest[2*i+1]=0;
+    }
+    len=2*i;
+    dest[2*i]=dest[2*i+1]=0;
+    for(i=2*i+2;(i%26);i++)
+	dest[i]=0xff;
+    return len;
+}
+
+static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename)
+{
+    char buffer[258];
+    int length=short2long_name(buffer,filename),
+        number_of_entries=(length+25)/26,i;
+    direntry_t* entry;
+
+    for(i=0;i<number_of_entries;i++) {
+	entry=array_get_next(&(s->directory));
+	entry->attributes=0xf;
+	entry->reserved[0]=0;
+	entry->begin=0;
+	entry->name[0]=(number_of_entries-i)|(i==0?0x40:0);
+    }
+    for(i=0;i<26*number_of_entries;i++) {
+	int offset=(i%26);
+	if(offset<10) offset=1+offset;
+	else if(offset<22) offset=14+offset-10;
+	else offset=28+offset-22;
+	entry=array_get(&(s->directory),s->directory.next-1-(i/26));
+	entry->name[offset]=buffer[i];
+    }
+    return array_get(&(s->directory),s->directory.next-number_of_entries);
+}
+
+static char is_free(const direntry_t* direntry)
+{
+    return direntry->name[0]==0xe5 || direntry->name[0]==0x00;
+}
+
+static char is_volume_label(const direntry_t* direntry)
+{
+    return direntry->attributes == 0x28;
+}
+
+static char is_long_name(const direntry_t* direntry)
+{
+    return direntry->attributes == 0xf;
+}
+
+static char is_short_name(const direntry_t* direntry)
+{
+    return !is_volume_label(direntry) && !is_long_name(direntry)
+	&& !is_free(direntry);
+}
+
+static char is_directory(const direntry_t* direntry)
+{
+    return direntry->attributes & 0x10 && direntry->name[0] != 0xe5;
+}
+
+static inline char is_dot(const direntry_t* direntry)
+{
+    return is_short_name(direntry) && direntry->name[0] == '.';
+}
+
+static char is_file(const direntry_t* direntry)
+{
+    return is_short_name(direntry) && !is_directory(direntry);
+}
+
+static inline uint32_t begin_of_direntry(const direntry_t* direntry)
+{
+    return le16_to_cpu(direntry->begin)|(le16_to_cpu(direntry->begin_hi)<<16);
+}
+
+static inline uint32_t filesize_of_direntry(const direntry_t* direntry)
+{
+    return le32_to_cpu(direntry->size);
+}
+
+static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
+{
+    direntry->begin = cpu_to_le16(begin & 0xffff);
+    direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
+}
+
+/* fat functions */
+
+static inline uint8_t fat_chksum(const direntry_t* entry)
+{
+    uint8_t chksum=0;
+    int i;
+
+    for(i=0;i<11;i++)
+	chksum=(((chksum&0xfe)>>1)|((chksum&0x01)?0x80:0))
+	    +(unsigned char)entry->name[i];
+
+    return chksum;
+}
+
+/* if return_time==0, this returns the fat_date, else the fat_time */
+static uint16_t fat_datetime(time_t time,int return_time) {
+    struct tm* t;
+#ifdef _WIN32
+    t=localtime(&time); /* this is not thread safe */
+#else
+    struct tm t1;
+    t=&t1;
+    localtime_r(&time,t);
+#endif
+    if(return_time)
+	return cpu_to_le16((t->tm_sec/2)|(t->tm_min<<5)|(t->tm_hour<<11));
+    return cpu_to_le16((t->tm_mday)|((t->tm_mon+1)<<5)|((t->tm_year-80)<<9));
+}
+
+static inline void fat_set(BDRVVVFATState* s,unsigned int cluster,uint32_t value)
+{
+    if(s->fat_type==32) {
+	uint32_t* entry=array_get(&(s->fat),cluster);
+	*entry=cpu_to_le32(value);
+    } else if(s->fat_type==16) {
+	uint16_t* entry=array_get(&(s->fat),cluster);
+	*entry=cpu_to_le16(value&0xffff);
+    } else {
+	int offset = (cluster*3/2);
+	unsigned char* p = array_get(&(s->fat), offset);
+        switch (cluster&1) {
+	case 0:
+		p[0] = value&0xff;
+		p[1] = (p[1]&0xf0) | ((value>>8)&0xf);
+		break;
+	case 1:
+		p[0] = (p[0]&0xf) | ((value&0xf)<<4);
+		p[1] = (value>>4);
+		break;
+	}
+    }
+}
+
+static inline uint32_t fat_get(BDRVVVFATState* s,unsigned int cluster)
+{
+    if(s->fat_type==32) {
+	uint32_t* entry=array_get(&(s->fat),cluster);
+	return le32_to_cpu(*entry);
+    } else if(s->fat_type==16) {
+	uint16_t* entry=array_get(&(s->fat),cluster);
+	return le16_to_cpu(*entry);
+    } else {
+	const uint8_t* x=(uint8_t*)(s->fat.pointer)+cluster*3/2;
+	return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
+    }
+}
+
+static inline int fat_eof(BDRVVVFATState* s,uint32_t fat_entry)
+{
+    if(fat_entry>s->max_fat_value-8)
+	return -1;
+    return 0;
+}
+
+static inline void init_fat(BDRVVVFATState* s)
+{
+    if (s->fat_type == 12) {
+	array_init(&(s->fat),1);
+	array_ensure_allocated(&(s->fat),
+		s->sectors_per_fat * 0x200 * 3 / 2 - 1);
+    } else {
+	array_init(&(s->fat),(s->fat_type==32?4:2));
+	array_ensure_allocated(&(s->fat),
+		s->sectors_per_fat * 0x200 / s->fat.item_size - 1);
+    }
+    memset(s->fat.pointer,0,s->fat.size);
+
+    switch(s->fat_type) {
+	case 12: s->max_fat_value=0xfff; break;
+	case 16: s->max_fat_value=0xffff; break;
+	case 32: s->max_fat_value=0x0fffffff; break;
+	default: s->max_fat_value=0; /* error... */
+    }
+
+}
+
+/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
+/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
+static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
+	unsigned int directory_start, const char* filename, int is_dot)
+{
+    int i,j,long_index=s->directory.next;
+    direntry_t* entry=0;
+    direntry_t* entry_long=0;
+
+    if(is_dot) {
+	entry=array_get_next(&(s->directory));
+	memset(entry->name,0x20,11);
+	memcpy(entry->name,filename,strlen(filename));
+	return entry;
+    }
+
+    entry_long=create_long_filename(s,filename);
+
+    i = strlen(filename);
+    for(j = i - 1; j>0  && filename[j]!='.';j--);
+    if (j > 0)
+	i = (j > 8 ? 8 : j);
+    else if (i > 8)
+	i = 8;
+
+    entry=array_get_next(&(s->directory));
+    memset(entry->name,0x20,11);
+    strncpy((char*)entry->name,filename,i);
+
+    if(j > 0)
+	for (i = 0; i < 3 && filename[j+1+i]; i++)
+	    entry->extension[i] = filename[j+1+i];
+
+    /* upcase & remove unwanted characters */
+    for(i=10;i>=0;i--) {
+	if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
+	if(entry->name[i]<=' ' || entry->name[i]>0x7f
+		|| strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
+	    entry->name[i]='_';
+        else if(entry->name[i]>='a' && entry->name[i]<='z')
+            entry->name[i]+='A'-'a';
+    }
+
+    /* mangle duplicates */
+    while(1) {
+	direntry_t* entry1=array_get(&(s->directory),directory_start);
+	int j;
+
+	for(;entry1<entry;entry1++)
+	    if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
+		break; /* found dupe */
+	if(entry1==entry) /* no dupe found */
+	    break;
+
+	/* use all 8 characters of name */
+	if(entry->name[7]==' ') {
+	    int j;
+	    for(j=6;j>0 && entry->name[j]==' ';j--)
+		entry->name[j]='~';
+	}
+
+	/* increment number */
+	for(j=7;j>0 && entry->name[j]=='9';j--)
+	    entry->name[j]='0';
+	if(j>0) {
+	    if(entry->name[j]<'0' || entry->name[j]>'9')
+	        entry->name[j]='0';
+	    else
+	        entry->name[j]++;
+	}
+    }
+
+    /* calculate checksum; propagate to long name */
+    if(entry_long) {
+        uint8_t chksum=fat_chksum(entry);
+
+	/* calculate anew, because realloc could have taken place */
+	entry_long=array_get(&(s->directory),long_index);
+	while(entry_long<entry && is_long_name(entry_long)) {
+	    entry_long->reserved[1]=chksum;
+	    entry_long++;
+	}
+    }
+
+    return entry;
+}
+
+/*
+ * Read a directory. (the index of the corresponding mapping must be passed).
+ */
+static int read_directory(BDRVVVFATState* s, int mapping_index)
+{
+    mapping_t* mapping = array_get(&(s->mapping), mapping_index);
+    direntry_t* direntry;
+    const char* dirname = mapping->path;
+    int first_cluster = mapping->begin;
+    int parent_index = mapping->info.dir.parent_mapping_index;
+    mapping_t* parent_mapping = (mapping_t*)
+	(parent_index >= 0 ? array_get(&(s->mapping), parent_index) : 0);
+    int first_cluster_of_parent = parent_mapping ? parent_mapping->begin : -1;
+
+    DIR* dir=opendir(dirname);
+    struct dirent* entry;
+    int i;
+
+    assert(mapping->mode & MODE_DIRECTORY);
+
+    if(!dir) {
+	mapping->end = mapping->begin;
+	return -1;
+    }
+
+    i = mapping->info.dir.first_dir_index =
+	    first_cluster == 0 ? 0 : s->directory.next;
+
+    /* actually read the directory, and allocate the mappings */
+    while((entry=readdir(dir))) {
+	unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
+        char* buffer;
+	direntry_t* direntry;
+        struct stat st;
+	int is_dot=!strcmp(entry->d_name,".");
+	int is_dotdot=!strcmp(entry->d_name,"..");
+
+	if(first_cluster == 0 && (is_dotdot || is_dot))
+	    continue;
+
+	buffer=(char*)malloc(length);
+	assert(buffer);
+	snprintf(buffer,length,"%s/%s",dirname,entry->d_name);
+
+	if(stat(buffer,&st)<0) {
+	    free(buffer);
+            continue;
+	}
+
+	/* create directory entry for this file */
+	direntry=create_short_and_long_name(s, i, entry->d_name,
+		is_dot || is_dotdot);
+	direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
+	direntry->reserved[0]=direntry->reserved[1]=0;
+	direntry->ctime=fat_datetime(st.st_ctime,1);
+	direntry->cdate=fat_datetime(st.st_ctime,0);
+	direntry->adate=fat_datetime(st.st_atime,0);
+	direntry->begin_hi=0;
+	direntry->mtime=fat_datetime(st.st_mtime,1);
+	direntry->mdate=fat_datetime(st.st_mtime,0);
+	if(is_dotdot)
+	    set_begin_of_direntry(direntry, first_cluster_of_parent);
+	else if(is_dot)
+	    set_begin_of_direntry(direntry, first_cluster);
+	else
+	    direntry->begin=0; /* do that later */
+        if (st.st_size > 0x7fffffff) {
+	    fprintf(stderr, "File %s is larger than 2GB\n", buffer);
+	    free(buffer);
+	    return -2;
+        }
+	direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
+
+	/* create mapping for this file */
+	if(!is_dot && !is_dotdot && (S_ISDIR(st.st_mode) || st.st_size)) {
+	    s->current_mapping=(mapping_t*)array_get_next(&(s->mapping));
+	    s->current_mapping->begin=0;
+	    s->current_mapping->end=st.st_size;
+	    /*
+	     * we get the direntry of the most recent direntry, which
+	     * contains the short name and all the relevant information.
+	     */
+	    s->current_mapping->dir_index=s->directory.next-1;
+	    s->current_mapping->first_mapping_index = -1;
+	    if (S_ISDIR(st.st_mode)) {
+		s->current_mapping->mode = MODE_DIRECTORY;
+		s->current_mapping->info.dir.parent_mapping_index =
+		    mapping_index;
+	    } else {
+		s->current_mapping->mode = MODE_UNDEFINED;
+		s->current_mapping->info.file.offset = 0;
+	    }
+	    s->current_mapping->path=buffer;
+	    s->current_mapping->read_only =
+		(st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0;
+	}
+    }
+    closedir(dir);
+
+    /* fill with zeroes up to the end of the cluster */
+    while(s->directory.next%(0x10*s->sectors_per_cluster)) {
+	direntry_t* direntry=array_get_next(&(s->directory));
+	memset(direntry,0,sizeof(direntry_t));
+    }
+
+/* TODO: if there are more entries, bootsector has to be adjusted! */
+#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster)
+    if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) {
+	/* root directory */
+	int cur = s->directory.next;
+	array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1);
+	memset(array_get(&(s->directory), cur), 0,
+		(ROOT_ENTRIES - cur) * sizeof(direntry_t));
+    }
+
+     /* reget the mapping, since s->mapping was possibly realloc()ed */
+    mapping = (mapping_t*)array_get(&(s->mapping), mapping_index);
+    first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
+	* 0x20 / s->cluster_size;
+    mapping->end = first_cluster;
+
+    direntry = (direntry_t*)array_get(&(s->directory), mapping->dir_index);
+    set_begin_of_direntry(direntry, mapping->begin);
+
+    return 0;
+}
+
+static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
+{
+    return (sector_num-s->faked_sectors)/s->sectors_per_cluster;
+}
+
+static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
+{
+    return s->faked_sectors + s->sectors_per_cluster * cluster_num;
+}
+
+static inline uint32_t sector_offset_in_cluster(BDRVVVFATState* s,off_t sector_num)
+{
+    return (sector_num-s->first_sectors_number-2*s->sectors_per_fat)%s->sectors_per_cluster;
+}
+
+#ifdef DBG
+static direntry_t* get_direntry_for_mapping(BDRVVVFATState* s,mapping_t* mapping)
+{
+    if(mapping->mode==MODE_UNDEFINED)
+	return 0;
+    return (direntry_t*)(s->directory.pointer+sizeof(direntry_t)*mapping->dir_index);
+}
+#endif
+
+static int init_directories(BDRVVVFATState* s,
+	const char* dirname)
+{
+    bootsector_t* bootsector;
+    mapping_t* mapping;
+    unsigned int i;
+    unsigned int cluster;
+
+    memset(&(s->first_sectors[0]),0,0x40*0x200);
+
+    s->cluster_size=s->sectors_per_cluster*0x200;
+    s->cluster_buffer=malloc(s->cluster_size);
+    assert(s->cluster_buffer);
+
+    /*
+     * The formula: sc = spf+1+spf*spc*(512*8/fat_type),
+     * where sc is sector_count,
+     * spf is sectors_per_fat,
+     * spc is sectors_per_clusters, and
+     * fat_type = 12, 16 or 32.
+     */
+    i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
+    s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
+
+    array_init(&(s->mapping),sizeof(mapping_t));
+    array_init(&(s->directory),sizeof(direntry_t));
+
+    /* add volume label */
+    {
+	direntry_t* entry=array_get_next(&(s->directory));
+	entry->attributes=0x28; /* archive | volume label */
+	snprintf((char*)entry->name,11,"QEMU VVFAT");
+    }
+
+    /* Now build FAT, and write back information into directory */
+    init_fat(s);
+
+    s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2;
+    s->cluster_count=sector2cluster(s, s->sector_count);
+
+    mapping = array_get_next(&(s->mapping));
+    mapping->begin = 0;
+    mapping->dir_index = 0;
+    mapping->info.dir.parent_mapping_index = -1;
+    mapping->first_mapping_index = -1;
+    mapping->path = strdup(dirname);
+    i = strlen(mapping->path);
+    if (i > 0 && mapping->path[i - 1] == '/')
+	mapping->path[i - 1] = '\0';
+    mapping->mode = MODE_DIRECTORY;
+    mapping->read_only = 0;
+    s->path = mapping->path;
+
+    for (i = 0, cluster = 0; i < s->mapping.next; i++) {
+	int j;
+	/* MS-DOS expects the FAT to be 0 for the root directory
+	 * (except for the media byte). */
+	/* LATER TODO: still true for FAT32? */
+	int fix_fat = (i != 0);
+	mapping = array_get(&(s->mapping), i);
+
+        if (mapping->mode & MODE_DIRECTORY) {
+	    mapping->begin = cluster;
+	    if(read_directory(s, i)) {
+		fprintf(stderr, "Could not read directory %s\n",
+			mapping->path);
+		return -1;
+	    }
+	    mapping = array_get(&(s->mapping), i);
+	} else {
+	    assert(mapping->mode == MODE_UNDEFINED);
+	    mapping->mode=MODE_NORMAL;
+	    mapping->begin = cluster;
+	    if (mapping->end > 0) {
+		direntry_t* direntry = array_get(&(s->directory),
+			mapping->dir_index);
+
+		mapping->end = cluster + 1 + (mapping->end-1)/s->cluster_size;
+		set_begin_of_direntry(direntry, mapping->begin);
+	    } else {
+		mapping->end = cluster + 1;
+		fix_fat = 0;
+	    }
+	}
+
+	assert(mapping->begin < mapping->end);
+
+	/* fix fat for entry */
+	if (fix_fat) {
+	    for(j = mapping->begin; j < mapping->end - 1; j++)
+		fat_set(s, j, j+1);
+	    fat_set(s, mapping->end - 1, s->max_fat_value);
+	}
+
+	/* next free cluster */
+	cluster = mapping->end;
+
+	if(cluster > s->cluster_count) {
+	    fprintf(stderr,"Directory does not fit in FAT%d\n",s->fat_type);
+	    return -1;
+	}
+    }
+
+    mapping = array_get(&(s->mapping), 0);
+    s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
+    s->last_cluster_of_root_directory = mapping->end;
+
+    /* the FAT signature */
+    fat_set(s,0,s->max_fat_value);
+    fat_set(s,1,s->max_fat_value);
+
+    s->current_mapping = NULL;
+
+    bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200);
+    bootsector->jump[0]=0xeb;
+    bootsector->jump[1]=0x3e;
+    bootsector->jump[2]=0x90;
+    memcpy(bootsector->name,"QEMU    ",8);
+    bootsector->sector_size=cpu_to_le16(0x200);
+    bootsector->sectors_per_cluster=s->sectors_per_cluster;
+    bootsector->reserved_sectors=cpu_to_le16(1);
+    bootsector->number_of_fats=0x2; /* number of FATs */
+    bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10);
+    bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
+    bootsector->media_type=(s->fat_type!=12?0xf8:s->sector_count==5760?0xf9:0xf8); /* media descriptor */
+    s->fat.pointer[0] = bootsector->media_type;
+    bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
+    bootsector->sectors_per_track=cpu_to_le16(s->bs->secs);
+    bootsector->number_of_heads=cpu_to_le16(s->bs->heads);
+    bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f);
+    bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
+
+    /* LATER TODO: if FAT32, this is wrong */
+    bootsector->u.fat16.drive_number=s->fat_type==12?0:0x80; /* assume this is hda (TODO) */
+    bootsector->u.fat16.current_head=0;
+    bootsector->u.fat16.signature=0x29;
+    bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
+
+    memcpy(bootsector->u.fat16.volume_label,"QEMU VVFAT ",11);
+    memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12   ":s->fat_type==16?"FAT16   ":"FAT32   "),8);
+    bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
+
+    return 0;
+}
+
+#ifdef DEBUG
+static BDRVVVFATState *vvv = NULL;
+#endif
+
+static int enable_write_target(BDRVVVFATState *s);
+static int is_consistent(BDRVVVFATState *s);
+
+static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
+{
+    BDRVVVFATState *s = bs->opaque;
+    int floppy = 0;
+    int i;
+
+#ifdef DEBUG
+    vvv = s;
+#endif
+
+DLOG(if (stderr == NULL) {
+    stderr = fopen("vvfat.log", "a");
+    setbuf(stderr, NULL);
+})
+
+    s->bs = bs;
+
+    s->fat_type=16;
+    /* LATER TODO: if FAT32, adjust */
+    s->sectors_per_cluster=0x10;
+    /* 504MB disk*/
+    bs->cyls=1024; bs->heads=16; bs->secs=63;
+
+    s->current_cluster=0xffffffff;
+
+    s->first_sectors_number=0x40;
+    /* read only is the default for safety */
+    bs->read_only = 1;
+    s->qcow = s->write_target = NULL;
+    s->qcow_filename = NULL;
+    s->fat2 = NULL;
+    s->downcase_short_names = 1;
+
+    if (!strstart(dirname, "fat:", NULL))
+	return -1;
+
+    if (strstr(dirname, ":floppy:")) {
+	floppy = 1;
+	s->fat_type = 12;
+	s->first_sectors_number = 1;
+	s->sectors_per_cluster=2;
+	bs->cyls = 80; bs->heads = 2; bs->secs = 36;
+    }
+
+    s->sector_count=bs->cyls*bs->heads*bs->secs;
+
+    if (strstr(dirname, ":32:")) {
+	fprintf(stderr, "Big fat greek warning: FAT32 has not been tested. You are welcome to do so!\n");
+	s->fat_type = 32;
+    } else if (strstr(dirname, ":16:")) {
+	s->fat_type = 16;
+    } else if (strstr(dirname, ":12:")) {
+	s->fat_type = 12;
+	s->sector_count=2880;
+    }
+
+    if (strstr(dirname, ":rw:")) {
+	if (enable_write_target(s))
+	    return -1;
+	bs->read_only = 0;
+    }
+
+    i = strrchr(dirname, ':') - dirname;
+    assert(i >= 3);
+    if (dirname[i-2] == ':' && isalpha(dirname[i-1]))
+	/* workaround for DOS drive names */
+	dirname += i-1;
+    else
+	dirname += i+1;
+
+    bs->total_sectors=bs->cyls*bs->heads*bs->secs;
+
+    if(init_directories(s, dirname))
+	return -1;
+
+    s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count;
+
+    if(s->first_sectors_number==0x40)
+	init_mbr(s);
+
+    /* for some reason or other, MS-DOS does not like to know about CHS... */
+    if (floppy)
+	bs->heads = bs->cyls = bs->secs = 0;
+
+    //    assert(is_consistent(s));
+    return 0;
+}
+
+static inline void vvfat_close_current_file(BDRVVVFATState *s)
+{
+    if(s->current_mapping) {
+	s->current_mapping = NULL;
+	if (s->current_fd) {
+		close(s->current_fd);
+		s->current_fd = 0;
+	}
+    }
+    s->current_cluster = -1;
+}
+
+/* mappings between index1 and index2-1 are supposed to be ordered
+ * return value is the index of the last mapping for which end>cluster_num
+ */
+static inline int find_mapping_for_cluster_aux(BDRVVVFATState* s,int cluster_num,int index1,int index2)
+{
+    int index3=index1+1;
+    while(1) {
+	mapping_t* mapping;
+	index3=(index1+index2)/2;
+	mapping=array_get(&(s->mapping),index3);
+	assert(mapping->begin < mapping->end);
+	if(mapping->begin>=cluster_num) {
+	    assert(index2!=index3 || index2==0);
+	    if(index2==index3)
+		return index1;
+	    index2=index3;
+	} else {
+	    if(index1==index3)
+		return mapping->end<=cluster_num ? index2 : index1;
+	    index1=index3;
+	}
+	assert(index1<=index2);
+	DLOG(mapping=array_get(&(s->mapping),index1);
+	assert(mapping->begin<=cluster_num);
+	assert(index2 >= s->mapping.next ||
+		((mapping = array_get(&(s->mapping),index2)) &&
+		mapping->end>cluster_num)));
+    }
+}
+
+static inline mapping_t* find_mapping_for_cluster(BDRVVVFATState* s,int cluster_num)
+{
+    int index=find_mapping_for_cluster_aux(s,cluster_num,0,s->mapping.next);
+    mapping_t* mapping;
+    if(index>=s->mapping.next)
+	return 0;
+    mapping=array_get(&(s->mapping),index);
+    if(mapping->begin>cluster_num)
+	return 0;
+    assert(mapping->begin<=cluster_num && mapping->end>cluster_num);
+    return mapping;
+}
+
+/*
+ * This function simply compares path == mapping->path. Since the mappings
+ * are sorted by cluster, this is expensive: O(n).
+ */
+static inline mapping_t* find_mapping_for_path(BDRVVVFATState* s,
+	const char* path)
+{
+    int i;
+
+    for (i = 0; i < s->mapping.next; i++) {
+	mapping_t* mapping = array_get(&(s->mapping), i);
+	if (mapping->first_mapping_index < 0 &&
+		!strcmp(path, mapping->path))
+	    return mapping;
+    }
+
+    return NULL;
+}
+
+static int open_file(BDRVVVFATState* s,mapping_t* mapping)
+{
+    if(!mapping)
+	return -1;
+    if(!s->current_mapping ||
+	    strcmp(s->current_mapping->path,mapping->path)) {
+	/* open file */
+	int fd = open(mapping->path, O_RDONLY | O_BINARY | O_LARGEFILE);
+	if(fd<0)
+	    return -1;
+	vvfat_close_current_file(s);
+	s->current_fd = fd;
+	s->current_mapping = mapping;
+    }
+    return 0;
+}
+
+static inline int read_cluster(BDRVVVFATState *s,int cluster_num)
+{
+    if(s->current_cluster != cluster_num) {
+	int result=0;
+	off_t offset;
+	assert(!s->current_mapping || s->current_fd || (s->current_mapping->mode & MODE_DIRECTORY));
+	if(!s->current_mapping
+		|| s->current_mapping->begin>cluster_num
+		|| s->current_mapping->end<=cluster_num) {
+	    /* binary search of mappings for file */
+	    mapping_t* mapping=find_mapping_for_cluster(s,cluster_num);
+
+	    assert(!mapping || (cluster_num>=mapping->begin && cluster_num<mapping->end));
+
+	    if (mapping && mapping->mode & MODE_DIRECTORY) {
+		vvfat_close_current_file(s);
+		s->current_mapping = mapping;
+read_cluster_directory:
+		offset = s->cluster_size*(cluster_num-s->current_mapping->begin);
+		s->cluster = (unsigned char*)s->directory.pointer+offset
+			+ 0x20*s->current_mapping->info.dir.first_dir_index;
+		assert(((s->cluster-(unsigned char*)s->directory.pointer)%s->cluster_size)==0);
+		assert((char*)s->cluster+s->cluster_size <= s->directory.pointer+s->directory.next*s->directory.item_size);
+		s->current_cluster = cluster_num;
+		return 0;
+	    }
+
+	    if(open_file(s,mapping))
+		return -2;
+	} else if (s->current_mapping->mode & MODE_DIRECTORY)
+	    goto read_cluster_directory;
+
+	assert(s->current_fd);
+
+	offset=s->cluster_size*(cluster_num-s->current_mapping->begin)+s->current_mapping->info.file.offset;
+	if(lseek(s->current_fd, offset, SEEK_SET)!=offset)
+	    return -3;
+	s->cluster=s->cluster_buffer;
+	result=read(s->current_fd,s->cluster,s->cluster_size);
+	if(result<0) {
+	    s->current_cluster = -1;
+	    return -1;
+	}
+	s->current_cluster = cluster_num;
+    }
+    return 0;
+}
+
+#ifdef DEBUG
+static void hexdump(const void* address, uint32_t len)
+{
+    const unsigned char* p = address;
+    int i, j;
+
+    for (i = 0; i < len; i += 16) {
+	for (j = 0; j < 16 && i + j < len; j++)
+	    fprintf(stderr, "%02x ", p[i + j]);
+	for (; j < 16; j++)
+	    fprintf(stderr, "   ");
+	fprintf(stderr, " ");
+	for (j = 0; j < 16 && i + j < len; j++)
+	    fprintf(stderr, "%c", (p[i + j] < ' ' || p[i + j] > 0x7f) ? '.' : p[i + j]);
+	fprintf(stderr, "\n");
+    }
+}
+
+static void print_direntry(const direntry_t* direntry)
+{
+    int j = 0;
+    char buffer[1024];
+
+    fprintf(stderr, "direntry 0x%x: ", (int)direntry);
+    if(!direntry)
+	return;
+    if(is_long_name(direntry)) {
+	unsigned char* c=(unsigned char*)direntry;
+	int i;
+	for(i=1;i<11 && c[i] && c[i]!=0xff;i+=2)
+#define ADD_CHAR(c) {buffer[j] = (c); if (buffer[j] < ' ') buffer[j] = '°'; j++;}
+	    ADD_CHAR(c[i]);
+	for(i=14;i<26 && c[i] && c[i]!=0xff;i+=2)
+	    ADD_CHAR(c[i]);
+	for(i=28;i<32 && c[i] && c[i]!=0xff;i+=2)
+	    ADD_CHAR(c[i]);
+	buffer[j] = 0;
+	fprintf(stderr, "%s\n", buffer);
+    } else {
+	int i;
+	for(i=0;i<11;i++)
+	    ADD_CHAR(direntry->name[i]);
+	buffer[j] = 0;
+	fprintf(stderr,"%s attributes=0x%02x begin=%d size=%d\n",
+		buffer,
+		direntry->attributes,
+		begin_of_direntry(direntry),le32_to_cpu(direntry->size));
+    }
+}
+
+static void print_mapping(const mapping_t* mapping)
+{
+    fprintf(stderr, "mapping (0x%x): begin, end = %d, %d, dir_index = %d, first_mapping_index = %d, name = %s, mode = 0x%x, " , (int)mapping, mapping->begin, mapping->end, mapping->dir_index, mapping->first_mapping_index, mapping->path, mapping->mode);
+    if (mapping->mode & MODE_DIRECTORY)
+	fprintf(stderr, "parent_mapping_index = %d, first_dir_index = %d\n", mapping->info.dir.parent_mapping_index, mapping->info.dir.first_dir_index);
+    else
+	fprintf(stderr, "offset = %d\n", mapping->info.file.offset);
+}
+#endif
+
+static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
+                    uint8_t *buf, int nb_sectors)
+{
+    BDRVVVFATState *s = bs->opaque;
+    int i;
+
+    for(i=0;i<nb_sectors;i++,sector_num++) {
+	if (sector_num >= s->sector_count)
+	   return -1;
+	if (s->qcow) {
+	    int n;
+	    if (s->qcow->drv->bdrv_is_allocated(s->qcow,
+			sector_num, nb_sectors-i, &n)) {
+DLOG(fprintf(stderr, "sectors %d+%d allocated\n", (int)sector_num, n));
+		if (s->qcow->drv->bdrv_read(s->qcow, sector_num, buf+i*0x200, n))
+		    return -1;
+		i += n - 1;
+		sector_num += n - 1;
+		continue;
+	    }
+DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num));
+	}
+	if(sector_num<s->faked_sectors) {
+	    if(sector_num<s->first_sectors_number)
+		memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
+	    else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
+		memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
+	    else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
+		memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
+	} else {
+	    uint32_t sector=sector_num-s->faked_sectors,
+	    sector_offset_in_cluster=(sector%s->sectors_per_cluster),
+	    cluster_num=sector/s->sectors_per_cluster;
+	    if(read_cluster(s, cluster_num) != 0) {
+		/* LATER TODO: strict: return -1; */
+		memset(buf+i*0x200,0,0x200);
+		continue;
+	    }
+	    memcpy(buf+i*0x200,s->cluster+sector_offset_in_cluster*0x200,0x200);
+	}
+    }
+    return 0;
+}
+
+/* LATER TODO: statify all functions */
+
+/*
+ * Idea of the write support (use snapshot):
+ *
+ * 1. check if all data is consistent, recording renames, modifications,
+ *    new files and directories (in s->commits).
+ *
+ * 2. if the data is not consistent, stop committing
+ *
+ * 3. handle renames, and create new files and directories (do not yet
+ *    write their contents)
+ *
+ * 4. walk the directories, fixing the mapping and direntries, and marking
+ *    the handled mappings as not deleted
+ *
+ * 5. commit the contents of the files
+ *
+ * 6. handle deleted files and directories
+ *
+ */
+
+typedef struct commit_t {
+    char* path;
+    union {
+	struct { uint32_t cluster; } rename;
+	struct { int dir_index; uint32_t modified_offset; } writeout;
+	struct { uint32_t first_cluster; } new_file;
+	struct { uint32_t cluster; } mkdir;
+    } param;
+    /* DELETEs and RMDIRs are handled differently: see handle_deletes() */
+    enum {
+	ACTION_RENAME, ACTION_WRITEOUT, ACTION_NEW_FILE, ACTION_MKDIR
+    } action;
+} commit_t;
+
+static void clear_commits(BDRVVVFATState* s)
+{
+    int i;
+DLOG(fprintf(stderr, "clear_commits (%d commits)\n", s->commits.next));
+    for (i = 0; i < s->commits.next; i++) {
+	commit_t* commit = array_get(&(s->commits), i);
+	assert(commit->path || commit->action == ACTION_WRITEOUT);
+	if (commit->action != ACTION_WRITEOUT) {
+	    assert(commit->path);
+	    free(commit->path);
+	} else
+	    assert(commit->path == NULL);
+    }
+    s->commits.next = 0;
+}
+
+static void schedule_rename(BDRVVVFATState* s,
+	uint32_t cluster, char* new_path)
+{
+    commit_t* commit = array_get_next(&(s->commits));
+    commit->path = new_path;
+    commit->param.rename.cluster = cluster;
+    commit->action = ACTION_RENAME;
+}
+
+static void schedule_writeout(BDRVVVFATState* s,
+	int dir_index, uint32_t modified_offset)
+{
+    commit_t* commit = array_get_next(&(s->commits));
+    commit->path = NULL;
+    commit->param.writeout.dir_index = dir_index;
+    commit->param.writeout.modified_offset = modified_offset;
+    commit->action = ACTION_WRITEOUT;
+}
+
+static void schedule_new_file(BDRVVVFATState* s,
+	char* path, uint32_t first_cluster)
+{
+    commit_t* commit = array_get_next(&(s->commits));
+    commit->path = path;
+    commit->param.new_file.first_cluster = first_cluster;
+    commit->action = ACTION_NEW_FILE;
+}
+
+static void schedule_mkdir(BDRVVVFATState* s, uint32_t cluster, char* path)
+{
+    commit_t* commit = array_get_next(&(s->commits));
+    commit->path = path;
+    commit->param.mkdir.cluster = cluster;
+    commit->action = ACTION_MKDIR;
+}
+
+typedef struct {
+    /*
+     * Since the sequence number is at most 0x3f, and the filename
+     * length is at most 13 times the sequence number, the maximal
+     * filename length is 0x3f * 13 bytes.
+     */
+    unsigned char name[0x3f * 13 + 1];
+    int checksum, len;
+    int sequence_number;
+} long_file_name;
+
+static void lfn_init(long_file_name* lfn)
+{
+   lfn->sequence_number = lfn->len = 0;
+   lfn->checksum = 0x100;
+}
+
+/* return 0 if parsed successfully, > 0 if no long name, < 0 if error */
+static int parse_long_name(long_file_name* lfn,
+	const direntry_t* direntry)
+{
+    int i, j, offset;
+    const unsigned char* pointer = (const unsigned char*)direntry;
+
+    if (!is_long_name(direntry))
+	return 1;
+
+    if (pointer[0] & 0x40) {
+	lfn->sequence_number = pointer[0] & 0x3f;
+	lfn->checksum = pointer[13];
+	lfn->name[0] = 0;
+	lfn->name[lfn->sequence_number * 13] = 0;
+    } else if ((pointer[0] & 0x3f) != --lfn->sequence_number)
+	return -1;
+    else if (pointer[13] != lfn->checksum)
+	return -2;
+    else if (pointer[12] || pointer[26] || pointer[27])
+	return -3;
+
+    offset = 13 * (lfn->sequence_number - 1);
+    for (i = 0, j = 1; i < 13; i++, j+=2) {
+	if (j == 11)
+	    j = 14;
+	else if (j == 26)
+	    j = 28;
+
+	if (pointer[j+1] == 0)
+	    lfn->name[offset + i] = pointer[j];
+	else if (pointer[j+1] != 0xff || (pointer[0] & 0x40) == 0)
+	    return -4;
+	else
+	    lfn->name[offset + i] = 0;
+    }
+
+    if (pointer[0] & 0x40)
+	lfn->len = offset + strlen((char*)lfn->name + offset);
+
+    return 0;
+}
+
+/* returns 0 if successful, >0 if no short_name, and <0 on error */
+static int parse_short_name(BDRVVVFATState* s,
+	long_file_name* lfn, direntry_t* direntry)
+{
+    int i, j;
+
+    if (!is_short_name(direntry))
+	return 1;
+
+    for (j = 7; j >= 0 && direntry->name[j] == ' '; j--);
+    for (i = 0; i <= j; i++) {
+	if (direntry->name[i] <= ' ' || direntry->name[i] > 0x7f)
+	    return -1;
+	else if (s->downcase_short_names)
+	    lfn->name[i] = tolower(direntry->name[i]);
+	else
+	    lfn->name[i] = direntry->name[i];
+    }
+
+    for (j = 2; j >= 0 && direntry->extension[j] == ' '; j--);
+    if (j >= 0) {
+	lfn->name[i++] = '.';
+	lfn->name[i + j + 1] = '\0';
+	for (;j >= 0; j--) {
+	    if (direntry->extension[j] <= ' ' || direntry->extension[j] > 0x7f)
+		return -2;
+	    else if (s->downcase_short_names)
+		lfn->name[i + j] = tolower(direntry->extension[j]);
+	    else
+		lfn->name[i + j] = direntry->extension[j];
+	}
+    } else
+	lfn->name[i + j + 1] = '\0';
+
+    lfn->len = strlen((char*)lfn->name);
+
+    return 0;
+}
+
+static inline uint32_t modified_fat_get(BDRVVVFATState* s,
+	unsigned int cluster)
+{
+    if (cluster < s->last_cluster_of_root_directory) {
+	if (cluster + 1 == s->last_cluster_of_root_directory)
+	    return s->max_fat_value;
+	else
+	    return cluster + 1;
+    }
+
+    if (s->fat_type==32) {
+        uint32_t* entry=((uint32_t*)s->fat2)+cluster;
+        return le32_to_cpu(*entry);
+    } else if (s->fat_type==16) {
+        uint16_t* entry=((uint16_t*)s->fat2)+cluster;
+        return le16_to_cpu(*entry);
+    } else {
+        const uint8_t* x=s->fat2+cluster*3/2;
+        return ((x[0]|(x[1]<<8))>>(cluster&1?4:0))&0x0fff;
+    }
+}
+
+static inline int cluster_was_modified(BDRVVVFATState* s, uint32_t cluster_num)
+{
+    int was_modified = 0;
+    int i, dummy;
+
+    if (s->qcow == NULL)
+	return 0;
+
+    for (i = 0; !was_modified && i < s->sectors_per_cluster; i++)
+	was_modified = s->qcow->drv->bdrv_is_allocated(s->qcow,
+		cluster2sector(s, cluster_num) + i, 1, &dummy);
+
+    return was_modified;
+}
+
+static const char* get_basename(const char* path)
+{
+    char* basename = strrchr(path, '/');
+    if (basename == NULL)
+	return path;
+    else
+	return basename + 1; /* strip '/' */
+}
+
+/*
+ * The array s->used_clusters holds the states of the clusters. If it is
+ * part of a file, it has bit 2 set, in case of a directory, bit 1. If it
+ * was modified, bit 3 is set.
+ * If any cluster is allocated, but not part of a file or directory, this
+ * driver refuses to commit.
+ */
+typedef enum {
+     USED_DIRECTORY = 1, USED_FILE = 2, USED_ANY = 3, USED_ALLOCATED = 4
+} used_t;
+
+/*
+ * get_cluster_count_for_direntry() not only determines how many clusters
+ * are occupied by direntry, but also if it was renamed or modified.
+ *
+ * A file is thought to be renamed *only* if there already was a file with
+ * exactly the same first cluster, but a different name.
+ *
+ * Further, the files/directories handled by this function are
+ * assumed to be *not* deleted (and *only* those).
+ */
+static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
+	direntry_t* direntry, const char* path)
+{
+    /*
+     * This is a little bit tricky:
+     * IF the guest OS just inserts a cluster into the file chain,
+     * and leaves the rest alone, (i.e. the original file had clusters
+     * 15 -> 16, but now has 15 -> 32 -> 16), then the following happens:
+     *
+     * - do_commit will write the cluster into the file at the given
+     *   offset, but
+     *
+     * - the cluster which is overwritten should be moved to a later
+     *   position in the file.
+     *
+     * I am not aware that any OS does something as braindead, but this
+     * situation could happen anyway when not committing for a long time.
+     * Just to be sure that this does not bite us, detect it, and copy the
+     * contents of the clusters to-be-overwritten into the qcow.
+     */
+    int copy_it = 0;
+    int was_modified = 0;
+    int32_t ret = 0;
+
+    uint32_t cluster_num = begin_of_direntry(direntry);
+    uint32_t offset = 0;
+    int first_mapping_index = -1;
+    mapping_t* mapping = NULL;
+    const char* basename2 = NULL;
+
+    vvfat_close_current_file(s);
+
+    /* the root directory */
+    if (cluster_num == 0)
+	return 0;
+
+    /* write support */
+    if (s->qcow) {
+	basename2 = get_basename(path);
+
+	mapping = find_mapping_for_cluster(s, cluster_num);
+
+	if (mapping) {
+	    const char* basename;
+
+	    assert(mapping->mode & MODE_DELETED);
+	    mapping->mode &= ~MODE_DELETED;
+
+	    basename = get_basename(mapping->path);
+
+	    assert(mapping->mode & MODE_NORMAL);
+
+	    /* rename */
+	    if (strcmp(basename, basename2))
+		schedule_rename(s, cluster_num, strdup(path));
+	} else if (is_file(direntry))
+	    /* new file */
+	    schedule_new_file(s, strdup(path), cluster_num);
+	else {
+	    assert(0);
+	    return 0;
+	}
+    }
+
+    while(1) {
+	if (s->qcow) {
+	    if (!copy_it && cluster_was_modified(s, cluster_num)) {
+		if (mapping == NULL ||
+			mapping->begin > cluster_num ||
+			mapping->end <= cluster_num)
+		mapping = find_mapping_for_cluster(s, cluster_num);
+
+
+		if (mapping &&
+			(mapping->mode & MODE_DIRECTORY) == 0) {
+
+		    /* was modified in qcow */
+		    if (offset != mapping->info.file.offset + s->cluster_size
+			    * (cluster_num - mapping->begin)) {
+			/* offset of this cluster in file chain has changed */
+			assert(0);
+			copy_it = 1;
+		    } else if (offset == 0) {
+			const char* basename = get_basename(mapping->path);
+
+			if (strcmp(basename, basename2))
+			    copy_it = 1;
+			first_mapping_index = array_index(&(s->mapping), mapping);
+		    }
+
+		    if (mapping->first_mapping_index != first_mapping_index
+			    && mapping->info.file.offset > 0) {
+			assert(0);
+			copy_it = 1;
+		    }
+
+		    /* need to write out? */
+		    if (!was_modified && is_file(direntry)) {
+			was_modified = 1;
+			schedule_writeout(s, mapping->dir_index, offset);
+		    }
+		}
+	    }
+
+	    if (copy_it) {
+		int i, dummy;
+		/*
+		 * This is horribly inefficient, but that is okay, since
+		 * it is rarely executed, if at all.
+		 */
+		int64_t offset = cluster2sector(s, cluster_num);
+
+		vvfat_close_current_file(s);
+		for (i = 0; i < s->sectors_per_cluster; i++)
+		    if (!s->qcow->drv->bdrv_is_allocated(s->qcow,
+				offset + i, 1, &dummy)) {
+			if (vvfat_read(s->bs,
+				    offset, s->cluster_buffer, 1))
+			    return -1;
+			if (s->qcow->drv->bdrv_write(s->qcow,
+				    offset, s->cluster_buffer, 1))
+			    return -2;
+		    }
+	    }
+	}
+
+	ret++;
+	if (s->used_clusters[cluster_num] & USED_ANY)
+	    return 0;
+	s->used_clusters[cluster_num] = USED_FILE;
+
+	cluster_num = modified_fat_get(s, cluster_num);
+
+	if (fat_eof(s, cluster_num))
+	    return ret;
+	else if (cluster_num < 2 || cluster_num > s->max_fat_value - 16)
+	    return -1;
+
+	offset += s->cluster_size;
+    }
+}
+
+/*
+ * This function looks at the modified data (qcow).
+ * It returns 0 upon inconsistency or error, and the number of clusters
+ * used by the directory, its subdirectories and their files.
+ */
+static int check_directory_consistency(BDRVVVFATState *s,
+	int cluster_num, const char* path)
+{
+    int ret = 0;
+    unsigned char* cluster = malloc(s->cluster_size);
+    direntry_t* direntries = (direntry_t*)cluster;
+    mapping_t* mapping = find_mapping_for_cluster(s, cluster_num);
+
+    long_file_name lfn;
+    int path_len = strlen(path);
+    char path2[PATH_MAX];
+
+    assert(path_len < PATH_MAX); /* len was tested before! */
+    pstrcpy(path2, sizeof(path2), path);
+    path2[path_len] = '/';
+    path2[path_len + 1] = '\0';
+
+    if (mapping) {
+	const char* basename = get_basename(mapping->path);
+	const char* basename2 = get_basename(path);
+
+	assert(mapping->mode & MODE_DIRECTORY);
+
+	assert(mapping->mode & MODE_DELETED);
+	mapping->mode &= ~MODE_DELETED;
+
+	if (strcmp(basename, basename2))
+	    schedule_rename(s, cluster_num, strdup(path));
+    } else
+	/* new directory */
+	schedule_mkdir(s, cluster_num, strdup(path));
+
+    lfn_init(&lfn);
+    do {
+	int i;
+	int subret = 0;
+
+	ret++;
+
+	if (s->used_clusters[cluster_num] & USED_ANY) {
+	    fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num);
+	    return 0;
+	}
+	s->used_clusters[cluster_num] = USED_DIRECTORY;
+
+DLOG(fprintf(stderr, "read cluster %d (sector %d)\n", (int)cluster_num, (int)cluster2sector(s, cluster_num)));
+	subret = vvfat_read(s->bs, cluster2sector(s, cluster_num), cluster,
+		s->sectors_per_cluster);
+	if (subret) {
+	    fprintf(stderr, "Error fetching direntries\n");
+	fail:
+	    free(cluster);
+	    return 0;
+	}
+
+	for (i = 0; i < 0x10 * s->sectors_per_cluster; i++) {
+	    int cluster_count;
+
+DLOG(fprintf(stderr, "check direntry %d: \n", i); print_direntry(direntries + i));
+	    if (is_volume_label(direntries + i) || is_dot(direntries + i) ||
+		    is_free(direntries + i))
+		continue;
+
+	    subret = parse_long_name(&lfn, direntries + i);
+	    if (subret < 0) {
+		fprintf(stderr, "Error in long name\n");
+		goto fail;
+	    }
+	    if (subret == 0 || is_free(direntries + i))
+		continue;
+
+	    if (fat_chksum(direntries+i) != lfn.checksum) {
+		subret = parse_short_name(s, &lfn, direntries + i);
+		if (subret < 0) {
+		    fprintf(stderr, "Error in short name (%d)\n", subret);
+		    goto fail;
+		}
+		if (subret > 0 || !strcmp((char*)lfn.name, ".")
+			|| !strcmp((char*)lfn.name, ".."))
+		    continue;
+	    }
+	    lfn.checksum = 0x100; /* cannot use long name twice */
+
+	    if (path_len + 1 + lfn.len >= PATH_MAX) {
+		fprintf(stderr, "Name too long: %s/%s\n", path, lfn.name);
+		goto fail;
+	    }
+            pstrcpy(path2 + path_len + 1, sizeof(path2) - path_len - 1,
+                    (char*)lfn.name);
+
+	    if (is_directory(direntries + i)) {
+		if (begin_of_direntry(direntries + i) == 0) {
+		    DLOG(fprintf(stderr, "invalid begin for directory: %s\n", path2); print_direntry(direntries + i));
+		    goto fail;
+		}
+		cluster_count = check_directory_consistency(s,
+			begin_of_direntry(direntries + i), path2);
+		if (cluster_count == 0) {
+		    DLOG(fprintf(stderr, "problem in directory %s:\n", path2); print_direntry(direntries + i));
+		    goto fail;
+		}
+	    } else if (is_file(direntries + i)) {
+		/* check file size with FAT */
+		cluster_count = get_cluster_count_for_direntry(s, direntries + i, path2);
+		if (cluster_count !=
+			(le32_to_cpu(direntries[i].size) + s->cluster_size
+			 - 1) / s->cluster_size) {
+		    DLOG(fprintf(stderr, "Cluster count mismatch\n"));
+		    goto fail;
+		}
+	    } else
+		assert(0); /* cluster_count = 0; */
+
+	    ret += cluster_count;
+	}
+
+	cluster_num = modified_fat_get(s, cluster_num);
+    } while(!fat_eof(s, cluster_num));
+
+    free(cluster);
+    return ret;
+}
+
+/* returns 1 on success */
+static int is_consistent(BDRVVVFATState* s)
+{
+    int i, check;
+    int used_clusters_count = 0;
+
+DLOG(checkpoint());
+    /*
+     * - get modified FAT
+     * - compare the two FATs (TODO)
+     * - get buffer for marking used clusters
+     * - recurse direntries from root (using bs->bdrv_read to make
+     *    sure to get the new data)
+     *   - check that the FAT agrees with the size
+     *   - count the number of clusters occupied by this directory and
+     *     its files
+     * - check that the cumulative used cluster count agrees with the
+     *   FAT
+     * - if all is fine, return number of used clusters
+     */
+    if (s->fat2 == NULL) {
+	int size = 0x200 * s->sectors_per_fat;
+	s->fat2 = malloc(size);
+	memcpy(s->fat2, s->fat.pointer, size);
+    }
+    check = vvfat_read(s->bs,
+	    s->first_sectors_number, s->fat2, s->sectors_per_fat);
+    if (check) {
+	fprintf(stderr, "Could not copy fat\n");
+	return 0;
+    }
+    assert (s->used_clusters);
+    for (i = 0; i < sector2cluster(s, s->sector_count); i++)
+	s->used_clusters[i] &= ~USED_ANY;
+
+    clear_commits(s);
+
+    /* mark every mapped file/directory as deleted.
+     * (check_directory_consistency() will unmark those still present). */
+    if (s->qcow)
+	for (i = 0; i < s->mapping.next; i++) {
+	    mapping_t* mapping = array_get(&(s->mapping), i);
+	    if (mapping->first_mapping_index < 0)
+		mapping->mode |= MODE_DELETED;
+	}
+
+    used_clusters_count = check_directory_consistency(s, 0, s->path);
+    if (used_clusters_count <= 0) {
+	DLOG(fprintf(stderr, "problem in directory\n"));
+	return 0;
+    }
+
+    check = s->last_cluster_of_root_directory;
+    for (i = check; i < sector2cluster(s, s->sector_count); i++) {
+	if (modified_fat_get(s, i)) {
+	    if(!s->used_clusters[i]) {
+		DLOG(fprintf(stderr, "FAT was modified (%d), but cluster is not used?\n", i));
+		return 0;
+	    }
+	    check++;
+	}
+
+	if (s->used_clusters[i] == USED_ALLOCATED) {
+	    /* allocated, but not used... */
+	    DLOG(fprintf(stderr, "unused, modified cluster: %d\n", i));
+	    return 0;
+	}
+    }
+
+    if (check != used_clusters_count)
+	return 0;
+
+    return used_clusters_count;
+}
+
+static inline void adjust_mapping_indices(BDRVVVFATState* s,
+	int offset, int adjust)
+{
+    int i;
+
+    for (i = 0; i < s->mapping.next; i++) {
+	mapping_t* mapping = array_get(&(s->mapping), i);
+
+#define ADJUST_MAPPING_INDEX(name) \
+	if (mapping->name >= offset) \
+	    mapping->name += adjust
+
+	ADJUST_MAPPING_INDEX(first_mapping_index);
+	if (mapping->mode & MODE_DIRECTORY)
+	    ADJUST_MAPPING_INDEX(info.dir.parent_mapping_index);
+    }
+}
+
+/* insert or update mapping */
+static mapping_t* insert_mapping(BDRVVVFATState* s,
+	uint32_t begin, uint32_t end)
+{
+    /*
+     * - find mapping where mapping->begin >= begin,
+     * - if mapping->begin > begin: insert
+     *   - adjust all references to mappings!
+     * - else: adjust
+     * - replace name
+     */
+    int index = find_mapping_for_cluster_aux(s, begin, 0, s->mapping.next);
+    mapping_t* mapping = NULL;
+    mapping_t* first_mapping = array_get(&(s->mapping), 0);
+
+    if (index < s->mapping.next && (mapping = array_get(&(s->mapping), index))
+	    && mapping->begin < begin) {
+	mapping->end = begin;
+	index++;
+	mapping = array_get(&(s->mapping), index);
+    }
+    if (index >= s->mapping.next || mapping->begin > begin) {
+	mapping = array_insert(&(s->mapping), index, 1);
+	mapping->path = NULL;
+	adjust_mapping_indices(s, index, +1);
+    }
+
+    mapping->begin = begin;
+    mapping->end = end;
+
+DLOG(mapping_t* next_mapping;
+assert(index + 1 >= s->mapping.next ||
+((next_mapping = array_get(&(s->mapping), index + 1)) &&
+ next_mapping->begin >= end)));
+
+    if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
+	s->current_mapping = array_get(&(s->mapping),
+		s->current_mapping - first_mapping);
+
+    return mapping;
+}
+
+static int remove_mapping(BDRVVVFATState* s, int mapping_index)
+{
+    mapping_t* mapping = array_get(&(s->mapping), mapping_index);
+    mapping_t* first_mapping = array_get(&(s->mapping), 0);
+
+    /* free mapping */
+    if (mapping->first_mapping_index < 0)
+	free(mapping->path);
+
+    /* remove from s->mapping */
+    array_remove(&(s->mapping), mapping_index);
+
+    /* adjust all references to mappings */
+    adjust_mapping_indices(s, mapping_index, -1);
+
+    if (s->current_mapping && first_mapping != (mapping_t*)s->mapping.pointer)
+	s->current_mapping = array_get(&(s->mapping),
+		s->current_mapping - first_mapping);
+
+    return 0;
+}
+
+static void adjust_dirindices(BDRVVVFATState* s, int offset, int adjust)
+{
+    int i;
+    for (i = 0; i < s->mapping.next; i++) {
+	mapping_t* mapping = array_get(&(s->mapping), i);
+	if (mapping->dir_index >= offset)
+	    mapping->dir_index += adjust;
+	if ((mapping->mode & MODE_DIRECTORY) &&
+		mapping->info.dir.first_dir_index >= offset)
+	    mapping->info.dir.first_dir_index += adjust;
+    }
+}
+
+static direntry_t* insert_direntries(BDRVVVFATState* s,
+	int dir_index, int count)
+{
+    /*
+     * make room in s->directory,
+     * adjust_dirindices
+     */
+    direntry_t* result = array_insert(&(s->directory), dir_index, count);
+    if (result == NULL)
+	return NULL;
+    adjust_dirindices(s, dir_index, count);
+    return result;
+}
+
+static int remove_direntries(BDRVVVFATState* s, int dir_index, int count)
+{
+    int ret = array_remove_slice(&(s->directory), dir_index, count);
+    if (ret)
+	return ret;
+    adjust_dirindices(s, dir_index, -count);
+    return 0;
+}
+
+/*
+ * Adapt the mappings of the cluster chain starting at first cluster
+ * (i.e. if a file starts at first_cluster, the chain is followed according
+ * to the modified fat, and the corresponding entries in s->mapping are
+ * adjusted)
+ */
+static int commit_mappings(BDRVVVFATState* s,
+	uint32_t first_cluster, int dir_index)
+{
+    mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
+    direntry_t* direntry = array_get(&(s->directory), dir_index);
+    uint32_t cluster = first_cluster;
+
+    vvfat_close_current_file(s);
+
+    assert(mapping);
+    assert(mapping->begin == first_cluster);
+    mapping->first_mapping_index = -1;
+    mapping->dir_index = dir_index;
+    mapping->mode = (dir_index <= 0 || is_directory(direntry)) ?
+	MODE_DIRECTORY : MODE_NORMAL;
+
+    while (!fat_eof(s, cluster)) {
+	uint32_t c, c1;
+
+	for (c = cluster, c1 = modified_fat_get(s, c); c + 1 == c1;
+		c = c1, c1 = modified_fat_get(s, c1));
+
+	c++;
+	if (c > mapping->end) {
+	    int index = array_index(&(s->mapping), mapping);
+	    int i, max_i = s->mapping.next - index;
+	    for (i = 1; i < max_i && mapping[i].begin < c; i++);
+	    while (--i > 0)
+		remove_mapping(s, index + 1);
+	}
+	assert(mapping == array_get(&(s->mapping), s->mapping.next - 1)
+		|| mapping[1].begin >= c);
+	mapping->end = c;
+
+	if (!fat_eof(s, c1)) {
+	    int i = find_mapping_for_cluster_aux(s, c1, 0, s->mapping.next);
+	    mapping_t* next_mapping = i >= s->mapping.next ? NULL :
+		array_get(&(s->mapping), i);
+
+	    if (next_mapping == NULL || next_mapping->begin > c1) {
+		int i1 = array_index(&(s->mapping), mapping);
+
+		next_mapping = insert_mapping(s, c1, c1+1);
+
+		if (c1 < c)
+		    i1++;
+		mapping = array_get(&(s->mapping), i1);
+	    }
+
+	    next_mapping->dir_index = mapping->dir_index;
+	    next_mapping->first_mapping_index =
+		mapping->first_mapping_index < 0 ?
+		array_index(&(s->mapping), mapping) :
+		mapping->first_mapping_index;
+	    next_mapping->path = mapping->path;
+	    next_mapping->mode = mapping->mode;
+	    next_mapping->read_only = mapping->read_only;
+	    if (mapping->mode & MODE_DIRECTORY) {
+		next_mapping->info.dir.parent_mapping_index =
+			mapping->info.dir.parent_mapping_index;
+		next_mapping->info.dir.first_dir_index =
+			mapping->info.dir.first_dir_index +
+			0x10 * s->sectors_per_cluster *
+			(mapping->end - mapping->begin);
+	    } else
+		next_mapping->info.file.offset = mapping->info.file.offset +
+			mapping->end - mapping->begin;
+
+	    mapping = next_mapping;
+	}
+
+	cluster = c1;
+    }
+
+    return 0;
+}
+
+static int commit_direntries(BDRVVVFATState* s,
+	int dir_index, int parent_mapping_index)
+{
+    direntry_t* direntry = array_get(&(s->directory), dir_index);
+    uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
+    mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
+
+    int factor = 0x10 * s->sectors_per_cluster;
+    int old_cluster_count, new_cluster_count;
+    int current_dir_index = mapping->info.dir.first_dir_index;
+    int first_dir_index = current_dir_index;
+    int ret, i;
+    uint32_t c;
+
+DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
+
+    assert(direntry);
+    assert(mapping);
+    assert(mapping->begin == first_cluster);
+    assert(mapping->info.dir.first_dir_index < s->directory.next);
+    assert(mapping->mode & MODE_DIRECTORY);
+    assert(dir_index == 0 || is_directory(direntry));
+
+    mapping->info.dir.parent_mapping_index = parent_mapping_index;
+
+    if (first_cluster == 0) {
+	old_cluster_count = new_cluster_count =
+	    s->last_cluster_of_root_directory;
+    } else {
+	for (old_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
+		c = fat_get(s, c))
+	    old_cluster_count++;
+
+	for (new_cluster_count = 0, c = first_cluster; !fat_eof(s, c);
+		c = modified_fat_get(s, c))
+	    new_cluster_count++;
+    }
+
+    if (new_cluster_count > old_cluster_count) {
+	if (insert_direntries(s,
+		current_dir_index + factor * old_cluster_count,
+		factor * (new_cluster_count - old_cluster_count)) == NULL)
+	    return -1;
+    } else if (new_cluster_count < old_cluster_count)
+	remove_direntries(s,
+		current_dir_index + factor * new_cluster_count,
+		factor * (old_cluster_count - new_cluster_count));
+
+    for (c = first_cluster; !fat_eof(s, c); c = modified_fat_get(s, c)) {
+	void* direntry = array_get(&(s->directory), current_dir_index);
+	int ret = vvfat_read(s->bs, cluster2sector(s, c), direntry,
+		s->sectors_per_cluster);
+	if (ret)
+	    return ret;
+	assert(!strncmp(s->directory.pointer, "QEMU", 4));
+	current_dir_index += factor;
+    }
+
+    ret = commit_mappings(s, first_cluster, dir_index);
+    if (ret)
+	return ret;
+
+    /* recurse */
+    for (i = 0; i < factor * new_cluster_count; i++) {
+	direntry = array_get(&(s->directory), first_dir_index + i);
+	if (is_directory(direntry) && !is_dot(direntry)) {
+	    mapping = find_mapping_for_cluster(s, first_cluster);
+	    assert(mapping->mode & MODE_DIRECTORY);
+	    ret = commit_direntries(s, first_dir_index + i,
+		array_index(&(s->mapping), mapping));
+	    if (ret)
+		return ret;
+	}
+    }
+
+    return 0;
+}
+
+/* commit one file (adjust contents, adjust mapping),
+   return first_mapping_index */
+static int commit_one_file(BDRVVVFATState* s,
+	int dir_index, uint32_t offset)
+{
+    direntry_t* direntry = array_get(&(s->directory), dir_index);
+    uint32_t c = begin_of_direntry(direntry);
+    uint32_t first_cluster = c;
+    mapping_t* mapping = find_mapping_for_cluster(s, c);
+    uint32_t size = filesize_of_direntry(direntry);
+    char* cluster = malloc(s->cluster_size);
+    uint32_t i;
+    int fd = 0;
+
+    assert(offset < size);
+    assert((offset % s->cluster_size) == 0);
+
+    for (i = s->cluster_size; i < offset; i += s->cluster_size)
+	c = modified_fat_get(s, c);
+
+    fd = open(mapping->path, O_RDWR | O_CREAT | O_BINARY, 0666);
+    if (fd < 0) {
+	fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
+		strerror(errno), errno);
+	return fd;
+    }
+    if (offset > 0)
+	if (lseek(fd, offset, SEEK_SET) != offset)
+	    return -3;
+
+    while (offset < size) {
+	uint32_t c1;
+	int rest_size = (size - offset > s->cluster_size ?
+		s->cluster_size : size - offset);
+	int ret;
+
+	c1 = modified_fat_get(s, c);
+
+	assert((size - offset == 0 && fat_eof(s, c)) ||
+		(size > offset && c >=2 && !fat_eof(s, c)));
+
+	ret = vvfat_read(s->bs, cluster2sector(s, c),
+	    (uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
+
+	if (ret < 0)
+	    return ret;
+
+	if (write(fd, cluster, rest_size) < 0)
+	    return -2;
+
+	offset += rest_size;
+	c = c1;
+    }
+
+    ftruncate(fd, size);
+    close(fd);
+
+    return commit_mappings(s, first_cluster, dir_index);
+}
+
+#ifdef DEBUG
+/* test, if all mappings point to valid direntries */
+static void check1(BDRVVVFATState* s)
+{
+    int i;
+    for (i = 0; i < s->mapping.next; i++) {
+	mapping_t* mapping = array_get(&(s->mapping), i);
+	if (mapping->mode & MODE_DELETED) {
+	    fprintf(stderr, "deleted\n");
+	    continue;
+	}
+	assert(mapping->dir_index >= 0);
+	assert(mapping->dir_index < s->directory.next);
+	direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
+	assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
+	if (mapping->mode & MODE_DIRECTORY) {
+	    assert(mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster * (mapping->end - mapping->begin) <= s->directory.next);
+	    assert((mapping->info.dir.first_dir_index % (0x10 * s->sectors_per_cluster)) == 0);
+	}
+    }
+}
+
+/* test, if all direntries have mappings */
+static void check2(BDRVVVFATState* s)
+{
+    int i;
+    int first_mapping = -1;
+
+    for (i = 0; i < s->directory.next; i++) {
+	direntry_t* direntry = array_get(&(s->directory), i);
+
+	if (is_short_name(direntry) && begin_of_direntry(direntry)) {
+	    mapping_t* mapping = find_mapping_for_cluster(s, begin_of_direntry(direntry));
+	    assert(mapping);
+	    assert(mapping->dir_index == i || is_dot(direntry));
+	    assert(mapping->begin == begin_of_direntry(direntry) || is_dot(direntry));
+	}
+
+	if ((i % (0x10 * s->sectors_per_cluster)) == 0) {
+	    /* cluster start */
+	    int j, count = 0;
+
+	    for (j = 0; j < s->mapping.next; j++) {
+		mapping_t* mapping = array_get(&(s->mapping), j);
+		if (mapping->mode & MODE_DELETED)
+		    continue;
+		if (mapping->mode & MODE_DIRECTORY) {
+		    if (mapping->info.dir.first_dir_index <= i && mapping->info.dir.first_dir_index + 0x10 * s->sectors_per_cluster > i) {
+			assert(++count == 1);
+			if (mapping->first_mapping_index == -1)
+			    first_mapping = array_index(&(s->mapping), mapping);
+			else
+			    assert(first_mapping == mapping->first_mapping_index);
+			if (mapping->info.dir.parent_mapping_index < 0)
+			    assert(j == 0);
+			else {
+			    mapping_t* parent = array_get(&(s->mapping), mapping->info.dir.parent_mapping_index);
+			    assert(parent->mode & MODE_DIRECTORY);
+			    assert(parent->info.dir.first_dir_index < mapping->info.dir.first_dir_index);
+			}
+		    }
+		}
+	    }
+	    if (count == 0)
+		first_mapping = -1;
+	}
+    }
+}
+#endif
+
+static int handle_renames_and_mkdirs(BDRVVVFATState* s)
+{
+    int i;
+
+#ifdef DEBUG
+    fprintf(stderr, "handle_renames\n");
+    for (i = 0; i < s->commits.next; i++) {
+	commit_t* commit = array_get(&(s->commits), i);
+	fprintf(stderr, "%d, %s (%d, %d)\n", i, commit->path ? commit->path : "(null)", commit->param.rename.cluster, commit->action);
+    }
+#endif
+
+    for (i = 0; i < s->commits.next;) {
+	commit_t* commit = array_get(&(s->commits), i);
+	if (commit->action == ACTION_RENAME) {
+	    mapping_t* mapping = find_mapping_for_cluster(s,
+		    commit->param.rename.cluster);
+	    char* old_path = mapping->path;
+
+	    assert(commit->path);
+	    mapping->path = commit->path;
+	    if (rename(old_path, mapping->path))
+		return -2;
+
+	    if (mapping->mode & MODE_DIRECTORY) {
+		int l1 = strlen(mapping->path);
+		int l2 = strlen(old_path);
+		int diff = l1 - l2;
+		direntry_t* direntry = array_get(&(s->directory),
+			mapping->info.dir.first_dir_index);
+		uint32_t c = mapping->begin;
+		int i = 0;
+
+		/* recurse */
+		while (!fat_eof(s, c)) {
+		    do {
+			direntry_t* d = direntry + i;
+
+			if (is_file(d) || (is_directory(d) && !is_dot(d))) {
+			    mapping_t* m = find_mapping_for_cluster(s,
+				    begin_of_direntry(d));
+			    int l = strlen(m->path);
+			    char* new_path = malloc(l + diff + 1);
+
+			    assert(!strncmp(m->path, mapping->path, l2));
+
+                            pstrcpy(new_path, l + diff + 1, mapping->path);
+                            pstrcpy(new_path + l1, l + diff + 1 - l1,
+                                    m->path + l2);
+
+			    schedule_rename(s, m->begin, new_path);
+			}
+			i++;
+		    } while((i % (0x10 * s->sectors_per_cluster)) != 0);
+		    c = fat_get(s, c);
+		}
+	    }
+
+	    free(old_path);
+	    array_remove(&(s->commits), i);
+	    continue;
+	} else if (commit->action == ACTION_MKDIR) {
+	    mapping_t* mapping;
+	    int j, parent_path_len;
+
+#ifdef __MINGW32__
+            if (mkdir(commit->path))
+                return -5;
+#else
+            if (mkdir(commit->path, 0755))
+                return -5;
+#endif
+
+	    mapping = insert_mapping(s, commit->param.mkdir.cluster,
+		    commit->param.mkdir.cluster + 1);
+	    if (mapping == NULL)
+		return -6;
+
+	    mapping->mode = MODE_DIRECTORY;
+	    mapping->read_only = 0;
+	    mapping->path = commit->path;
+	    j = s->directory.next;
+	    assert(j);
+	    insert_direntries(s, s->directory.next,
+		    0x10 * s->sectors_per_cluster);
+	    mapping->info.dir.first_dir_index = j;
+
+	    parent_path_len = strlen(commit->path)
+		- strlen(get_basename(commit->path)) - 1;
+	    for (j = 0; j < s->mapping.next; j++) {
+		mapping_t* m = array_get(&(s->mapping), j);
+		if (m->first_mapping_index < 0 && m != mapping &&
+			!strncmp(m->path, mapping->path, parent_path_len) &&
+			strlen(m->path) == parent_path_len)
+		    break;
+	    }
+	    assert(j < s->mapping.next);
+	    mapping->info.dir.parent_mapping_index = j;
+
+	    array_remove(&(s->commits), i);
+	    continue;
+	}
+
+	i++;
+    }
+    return 0;
+}
+
+/*
+ * TODO: make sure that the short name is not matching *another* file
+ */
+static int handle_commits(BDRVVVFATState* s)
+{
+    int i, fail = 0;
+
+    vvfat_close_current_file(s);
+
+    for (i = 0; !fail && i < s->commits.next; i++) {
+	commit_t* commit = array_get(&(s->commits), i);
+	switch(commit->action) {
+	case ACTION_RENAME: case ACTION_MKDIR:
+	    assert(0);
+	    fail = -2;
+	    break;
+	case ACTION_WRITEOUT: {
+	    direntry_t* entry = array_get(&(s->directory),
+		    commit->param.writeout.dir_index);
+	    uint32_t begin = begin_of_direntry(entry);
+	    mapping_t* mapping = find_mapping_for_cluster(s, begin);
+
+	    assert(mapping);
+	    assert(mapping->begin == begin);
+	    assert(commit->path == NULL);
+
+	    if (commit_one_file(s, commit->param.writeout.dir_index,
+			commit->param.writeout.modified_offset))
+		fail = -3;
+
+	    break;
+	}
+	case ACTION_NEW_FILE: {
+	    int begin = commit->param.new_file.first_cluster;
+	    mapping_t* mapping = find_mapping_for_cluster(s, begin);
+	    direntry_t* entry;
+	    int i;
+
+	    /* find direntry */
+	    for (i = 0; i < s->directory.next; i++) {
+		entry = array_get(&(s->directory), i);
+		if (is_file(entry) && begin_of_direntry(entry) == begin)
+		    break;
+	    }
+
+	    if (i >= s->directory.next) {
+		fail = -6;
+		continue;
+	    }
+
+	    /* make sure there exists an initial mapping */
+	    if (mapping && mapping->begin != begin) {
+		mapping->end = begin;
+		mapping = NULL;
+	    }
+	    if (mapping == NULL) {
+		mapping = insert_mapping(s, begin, begin+1);
+	    }
+	    /* most members will be fixed in commit_mappings() */
+	    assert(commit->path);
+	    mapping->path = commit->path;
+	    mapping->read_only = 0;
+	    mapping->mode = MODE_NORMAL;
+	    mapping->info.file.offset = 0;
+
+	    if (commit_one_file(s, i, 0))
+		fail = -7;
+
+	    break;
+	}
+	default:
+	    assert(0);
+	}
+    }
+    if (i > 0 && array_remove_slice(&(s->commits), 0, i))
+	return -1;
+    return fail;
+}
+
+static int handle_deletes(BDRVVVFATState* s)
+{
+    int i, deferred = 1, deleted = 1;
+
+    /* delete files corresponding to mappings marked as deleted */
+    /* handle DELETEs and unused mappings (modified_fat_get(s, mapping->begin) == 0) */
+    while (deferred && deleted) {
+	deferred = 0;
+	deleted = 0;
+
+	for (i = 1; i < s->mapping.next; i++) {
+	    mapping_t* mapping = array_get(&(s->mapping), i);
+	    if (mapping->mode & MODE_DELETED) {
+		direntry_t* entry = array_get(&(s->directory),
+			mapping->dir_index);
+
+		if (is_free(entry)) {
+		    /* remove file/directory */
+		    if (mapping->mode & MODE_DIRECTORY) {
+			int j, next_dir_index = s->directory.next,
+			first_dir_index = mapping->info.dir.first_dir_index;
+
+			if (rmdir(mapping->path) < 0) {
+			    if (errno == ENOTEMPTY) {
+				deferred++;
+				continue;
+			    } else
+				return -5;
+			}
+
+			for (j = 1; j < s->mapping.next; j++) {
+			    mapping_t* m = array_get(&(s->mapping), j);
+			    if (m->mode & MODE_DIRECTORY &&
+				    m->info.dir.first_dir_index >
+				    first_dir_index &&
+				    m->info.dir.first_dir_index <
+				    next_dir_index)
+				next_dir_index =
+				    m->info.dir.first_dir_index;
+			}
+			remove_direntries(s, first_dir_index,
+				next_dir_index - first_dir_index);
+
+			deleted++;
+		    }
+		} else {
+		    if (unlink(mapping->path))
+			return -4;
+		    deleted++;
+		}
+		DLOG(fprintf(stderr, "DELETE (%d)\n", i); print_mapping(mapping); print_direntry(entry));
+		remove_mapping(s, i);
+	    }
+	}
+    }
+
+    return 0;
+}
+
+/*
+ * synchronize mapping with new state:
+ *
+ * - copy FAT (with bdrv_read)
+ * - mark all filenames corresponding to mappings as deleted
+ * - recurse direntries from root (using bs->bdrv_read)
+ * - delete files corresponding to mappings marked as deleted
+ */
+static int do_commit(BDRVVVFATState* s)
+{
+    int ret = 0;
+
+    /* the real meat are the commits. Nothing to do? Move along! */
+    if (s->commits.next == 0)
+	return 0;
+
+    vvfat_close_current_file(s);
+
+    ret = handle_renames_and_mkdirs(s);
+    if (ret) {
+	fprintf(stderr, "Error handling renames (%d)\n", ret);
+	assert(0);
+	return ret;
+    }
+
+    /* copy FAT (with bdrv_read) */
+    memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
+
+    /* recurse direntries from root (using bs->bdrv_read) */
+    ret = commit_direntries(s, 0, -1);
+    if (ret) {
+	fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
+	assert(0);
+	return ret;
+    }
+
+    ret = handle_commits(s);
+    if (ret) {
+	fprintf(stderr, "Error handling commits (%d)\n", ret);
+	assert(0);
+	return ret;
+    }
+
+    ret = handle_deletes(s);
+    if (ret) {
+	fprintf(stderr, "Error deleting\n");
+        assert(0);
+	return ret;
+    }
+
+    s->qcow->drv->bdrv_make_empty(s->qcow);
+
+    memset(s->used_clusters, 0, sector2cluster(s, s->sector_count));
+
+DLOG(checkpoint());
+    return 0;
+}
+
+static int try_commit(BDRVVVFATState* s)
+{
+    vvfat_close_current_file(s);
+DLOG(checkpoint());
+    if(!is_consistent(s))
+	return -1;
+    return do_commit(s);
+}
+
+static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
+                    const uint8_t *buf, int nb_sectors)
+{
+    BDRVVVFATState *s = bs->opaque;
+    int i, ret;
+
+DLOG(checkpoint());
+
+    vvfat_close_current_file(s);
+
+    /*
+     * Some sanity checks:
+     * - do not allow writing to the boot sector
+     * - do not allow to write non-ASCII filenames
+     */
+
+    if (sector_num < s->first_sectors_number)
+	return -1;
+
+    for (i = sector2cluster(s, sector_num);
+	    i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
+	mapping_t* mapping = find_mapping_for_cluster(s, i);
+	if (mapping) {
+	    if (mapping->read_only) {
+		fprintf(stderr, "Tried to write to write-protected file %s\n",
+			mapping->path);
+		return -1;
+	    }
+
+	    if (mapping->mode & MODE_DIRECTORY) {
+		int begin = cluster2sector(s, i);
+		int end = begin + s->sectors_per_cluster, k;
+		int dir_index;
+		const direntry_t* direntries;
+		long_file_name lfn;
+
+		lfn_init(&lfn);
+
+		if (begin < sector_num)
+		    begin = sector_num;
+		if (end > sector_num + nb_sectors)
+		    end = sector_num + nb_sectors;
+		dir_index  = mapping->dir_index +
+		    0x10 * (begin - mapping->begin * s->sectors_per_cluster);
+		direntries = (direntry_t*)(buf + 0x200 * (begin - sector_num));
+
+		for (k = 0; k < (end - begin) * 0x10; k++) {
+		    /* do not allow non-ASCII filenames */
+		    if (parse_long_name(&lfn, direntries + k) < 0) {
+			fprintf(stderr, "Warning: non-ASCII filename\n");
+			return -1;
+		    }
+		    /* no access to the direntry of a read-only file */
+		    else if (is_short_name(direntries+k) &&
+			    (direntries[k].attributes & 1)) {
+			if (memcmp(direntries + k,
+				    array_get(&(s->directory), dir_index + k),
+				    sizeof(direntry_t))) {
+			    fprintf(stderr, "Warning: tried to write to write-protected file\n");
+			    return -1;
+			}
+		    }
+		}
+	    }
+	    i = mapping->end;
+	} else
+	    i++;
+    }
+
+    /*
+     * Use qcow backend. Commit later.
+     */
+DLOG(fprintf(stderr, "Write to qcow backend: %d + %d\n", (int)sector_num, nb_sectors));
+    ret = s->qcow->drv->bdrv_write(s->qcow, sector_num, buf, nb_sectors);
+    if (ret < 0) {
+	fprintf(stderr, "Error writing to qcow backend\n");
+	return ret;
+    }
+
+    for (i = sector2cluster(s, sector_num);
+	    i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
+	if (i >= 0)
+	    s->used_clusters[i] |= USED_ALLOCATED;
+
+DLOG(checkpoint());
+    /* TODO: add timeout */
+    try_commit(s);
+
+DLOG(checkpoint());
+    return 0;
+}
+
+static int vvfat_is_allocated(BlockDriverState *bs,
+	int64_t sector_num, int nb_sectors, int* n)
+{
+    BDRVVVFATState* s = bs->opaque;
+    *n = s->sector_count - sector_num;
+    if (*n > nb_sectors)
+	*n = nb_sectors;
+    else if (*n < 0)
+	return 0;
+    return 1;
+}
+
+static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
+	const uint8_t* buffer, int nb_sectors) {
+    BDRVVVFATState* s = bs->opaque;
+    return try_commit(s);
+}
+
+static void write_target_close(BlockDriverState *bs) {
+    BDRVVVFATState* s = bs->opaque;
+    bdrv_delete(s->qcow);
+    free(s->qcow_filename);
+}
+
+static BlockDriver vvfat_write_target = {
+    "vvfat_write_target", 0, NULL, NULL, NULL,
+    write_target_commit,
+    write_target_close,
+    NULL, NULL, NULL
+};
+
+static int enable_write_target(BDRVVVFATState *s)
+{
+    int size = sector2cluster(s, s->sector_count);
+    s->used_clusters = calloc(size, 1);
+
+    array_init(&(s->commits), sizeof(commit_t));
+
+    s->qcow_filename = malloc(1024);
+    get_tmp_filename(s->qcow_filename, 1024);
+    if (bdrv_create(&bdrv_qcow,
+		s->qcow_filename, s->sector_count, "fat:", 0) < 0)
+	return -1;
+    s->qcow = bdrv_new("");
+    if (s->qcow == NULL || bdrv_open(s->qcow, s->qcow_filename, 0) < 0)
+	return -1;
+
+#ifndef _WIN32
+    unlink(s->qcow_filename);
+#endif
+
+    s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
+    s->bs->backing_hd->drv = &vvfat_write_target;
+    s->bs->backing_hd->opaque = s;
+
+    return 0;
+}
+
+static void vvfat_close(BlockDriverState *bs)
+{
+    BDRVVVFATState *s = bs->opaque;
+
+    vvfat_close_current_file(s);
+    array_free(&(s->fat));
+    array_free(&(s->directory));
+    array_free(&(s->mapping));
+    if(s->cluster_buffer)
+        free(s->cluster_buffer);
+}
+
+BlockDriver bdrv_vvfat = {
+    "vvfat",
+    sizeof(BDRVVVFATState),
+    NULL, /* no probe for protocols */
+    vvfat_open,
+    vvfat_read,
+    vvfat_write,
+    vvfat_close,
+    NULL, /* ??? Not sure if we can do any meaningful flushing.  */
+    NULL,
+    vvfat_is_allocated,
+    .protocol_name = "fat",
+};
+
+#ifdef DEBUG
+static void checkpoint(void) {
+    assert(((mapping_t*)array_get(&(vvv->mapping), 0))->end == 2);
+    check1(vvv);
+    check2(vvv);
+    assert(!vvv->current_mapping || vvv->current_fd || (vvv->current_mapping->mode & MODE_DIRECTORY));
+#if 0
+    if (((direntry_t*)vvv->directory.pointer)[1].attributes != 0xf)
+	fprintf(stderr, "Nonono!\n");
+    mapping_t* mapping;
+    direntry_t* direntry;
+    assert(vvv->mapping.size >= vvv->mapping.item_size * vvv->mapping.next);
+    assert(vvv->directory.size >= vvv->directory.item_size * vvv->directory.next);
+    if (vvv->mapping.next<47)
+	return;
+    assert((mapping = array_get(&(vvv->mapping), 47)));
+    assert(mapping->dir_index < vvv->directory.next);
+    direntry = array_get(&(vvv->directory), mapping->dir_index);
+    assert(!memcmp(direntry->name, "USB     H  ", 11) || direntry->name[0]==0);
+#endif
+    return;
+    /* avoid compiler warnings: */
+    hexdump(NULL, 100);
+    remove_mapping(vvv, NULL);
+    print_mapping(NULL);
+    print_direntry(NULL);
+}
+#endif
+
diff --git a/block.c b/block.c
new file mode 100644
index 0000000..06bea78
--- /dev/null
+++ b/block.c
@@ -0,0 +1,1433 @@
+/*
+ * QEMU System Emulator block driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "console.h"
+#include "block_int.h"
+
+#ifdef _BSD
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+#include <sys/disk.h>
+#endif
+
+#define SECTOR_BITS 9
+#define SECTOR_SIZE (1 << SECTOR_BITS)
+
+typedef struct BlockDriverAIOCBSync {
+    BlockDriverAIOCB common;
+    QEMUBH *bh;
+    int ret;
+} BlockDriverAIOCBSync;
+
+static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque);
+static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs,
+        int64_t sector_num, const uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque);
+static void bdrv_aio_cancel_em(BlockDriverAIOCB *acb);
+static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
+                        uint8_t *buf, int nb_sectors);
+static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
+                         const uint8_t *buf, int nb_sectors);
+
+static BlockDriver *first_drv;
+
+static int path_is_absolute(const char *path)
+{
+    const char *p;
+#ifdef _WIN32
+    /* specific case for names like: "\\.\d:" */
+    if (*path == '/' || *path == '\\')
+        return 1;
+#endif
+    p = strchr(path, ':');
+    if (p)
+        p++;
+    else
+        p = path;
+#ifdef _WIN32
+    return (*p == '/' || *p == '\\');
+#else
+    return (*p == '/');
+#endif
+}
+
+/* if filename is absolute, just copy it to dest. Otherwise, build a
+   path to it by considering it is relative to base_path. URL are
+   supported. */
+void path_combine(char *dest, int dest_size,
+                  const char *base_path,
+                  const char *filename)
+{
+    const char *p, *p1;
+    int len;
+
+    if (dest_size <= 0)
+        return;
+    if (path_is_absolute(filename)) {
+        pstrcpy(dest, dest_size, filename);
+    } else {
+        p = strchr(base_path, ':');
+        if (p)
+            p++;
+        else
+            p = base_path;
+        p1 = strrchr(base_path, '/');
+#ifdef _WIN32
+        {
+            const char *p2;
+            p2 = strrchr(base_path, '\\');
+            if (!p1 || p2 > p1)
+                p1 = p2;
+        }
+#endif
+        if (p1)
+            p1++;
+        else
+            p1 = base_path;
+        if (p1 > p)
+            p = p1;
+        len = p - base_path;
+        if (len > dest_size - 1)
+            len = dest_size - 1;
+        memcpy(dest, base_path, len);
+        dest[len] = '\0';
+        pstrcat(dest, dest_size, filename);
+    }
+}
+
+
+static void bdrv_register(BlockDriver *bdrv)
+{
+    if (!bdrv->bdrv_aio_read) {
+        /* add AIO emulation layer */
+        bdrv->bdrv_aio_read = bdrv_aio_read_em;
+        bdrv->bdrv_aio_write = bdrv_aio_write_em;
+        bdrv->bdrv_aio_cancel = bdrv_aio_cancel_em;
+        bdrv->aiocb_size = sizeof(BlockDriverAIOCBSync);
+    } else if (!bdrv->bdrv_read && !bdrv->bdrv_pread) {
+        /* add synchronous IO emulation layer */
+        bdrv->bdrv_read = bdrv_read_em;
+        bdrv->bdrv_write = bdrv_write_em;
+    }
+    bdrv->next = first_drv;
+    first_drv = bdrv;
+}
+
+/* create a new block device (by default it is empty) */
+BlockDriverState *bdrv_new(const char *device_name)
+{
+    BlockDriverState **pbs, *bs;
+
+    bs = qemu_mallocz(sizeof(BlockDriverState));
+    if(!bs)
+        return NULL;
+    pstrcpy(bs->device_name, sizeof(bs->device_name), device_name);
+    if (device_name[0] != '\0') {
+        /* insert at the end */
+        pbs = &bdrv_first;
+        while (*pbs != NULL)
+            pbs = &(*pbs)->next;
+        *pbs = bs;
+    }
+    return bs;
+}
+
+BlockDriver *bdrv_find_format(const char *format_name)
+{
+    BlockDriver *drv1;
+    for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
+        if (!strcmp(drv1->format_name, format_name))
+            return drv1;
+    }
+    return NULL;
+}
+
+int bdrv_create(BlockDriver *drv,
+                const char *filename, int64_t size_in_sectors,
+                const char *backing_file, int flags)
+{
+    if (!drv->bdrv_create)
+        return -ENOTSUP;
+    return drv->bdrv_create(filename, size_in_sectors, backing_file, flags);
+}
+
+#ifdef _WIN32
+void get_tmp_filename(char *filename, int size)
+{
+    char temp_dir[MAX_PATH];
+
+    GetTempPath(MAX_PATH, temp_dir);
+    GetTempFileName(temp_dir, "qem", 0, filename);
+}
+#else
+void get_tmp_filename(char *filename, int size)
+{
+    int fd;
+    const char *tmpdir;
+    /* XXX: race condition possible */
+    tmpdir = getenv("TMPDIR");
+    if (!tmpdir)
+        tmpdir = "/tmp";
+    snprintf(filename, size, "%s/vl.XXXXXX", tmpdir);
+    fd = mkstemp(filename);
+    close(fd);
+}
+#endif
+
+#ifdef _WIN32
+static int is_windows_drive_prefix(const char *filename)
+{
+    return (((filename[0] >= 'a' && filename[0] <= 'z') ||
+             (filename[0] >= 'A' && filename[0] <= 'Z')) &&
+            filename[1] == ':');
+}
+
+static int is_windows_drive(const char *filename)
+{
+    if (is_windows_drive_prefix(filename) &&
+        filename[2] == '\0')
+        return 1;
+    if (strstart(filename, "\\\\.\\", NULL) ||
+        strstart(filename, "//./", NULL))
+        return 1;
+    return 0;
+}
+#endif
+
+static BlockDriver *find_protocol(const char *filename)
+{
+    BlockDriver *drv1;
+    char protocol[128];
+    int len;
+    const char *p;
+
+#ifdef _WIN32
+    if (is_windows_drive(filename) ||
+        is_windows_drive_prefix(filename))
+        return &bdrv_raw;
+#endif
+    p = strchr(filename, ':');
+    if (!p)
+        return &bdrv_raw;
+    len = p - filename;
+    if (len > sizeof(protocol) - 1)
+        len = sizeof(protocol) - 1;
+    memcpy(protocol, filename, len);
+    protocol[len] = '\0';
+    for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
+        if (drv1->protocol_name &&
+            !strcmp(drv1->protocol_name, protocol))
+            return drv1;
+    }
+    return NULL;
+}
+
+/* XXX: force raw format if block or character device ? It would
+   simplify the BSD case */
+static BlockDriver *find_image_format(const char *filename)
+{
+    int ret, score, score_max;
+    BlockDriver *drv1, *drv;
+    uint8_t buf[2048];
+    BlockDriverState *bs;
+
+    /* detect host devices. By convention, /dev/cdrom[N] is always
+       recognized as a host CDROM */
+    if (strstart(filename, "/dev/cdrom", NULL))
+        return &bdrv_host_device;
+#ifdef _WIN32
+    if (is_windows_drive(filename))
+        return &bdrv_host_device;
+#else
+    {
+        struct stat st;
+        if (stat(filename, &st) >= 0 &&
+            (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode))) {
+            return &bdrv_host_device;
+        }
+    }
+#endif
+
+    drv = find_protocol(filename);
+    /* no need to test disk image formats for vvfat */
+    if (drv == &bdrv_vvfat)
+        return drv;
+
+    ret = bdrv_file_open(&bs, filename, BDRV_O_RDONLY);
+    if (ret < 0)
+        return NULL;
+    ret = bdrv_pread(bs, 0, buf, sizeof(buf));
+    bdrv_delete(bs);
+    if (ret < 0) {
+        return NULL;
+    }
+
+    score_max = 0;
+    for(drv1 = first_drv; drv1 != NULL; drv1 = drv1->next) {
+        if (drv1->bdrv_probe) {
+            score = drv1->bdrv_probe(buf, ret, filename);
+            if (score > score_max) {
+                score_max = score;
+                drv = drv1;
+            }
+        }
+    }
+    return drv;
+}
+
+int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
+{
+    BlockDriverState *bs;
+    int ret;
+
+    bs = bdrv_new("");
+    if (!bs)
+        return -ENOMEM;
+    ret = bdrv_open2(bs, filename, flags | BDRV_O_FILE, NULL);
+    if (ret < 0) {
+        bdrv_delete(bs);
+        return ret;
+    }
+    *pbs = bs;
+    return 0;
+}
+
+int bdrv_open(BlockDriverState *bs, const char *filename, int flags)
+{
+    return bdrv_open2(bs, filename, flags, NULL);
+}
+
+int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
+               BlockDriver *drv)
+{
+    int ret, open_flags;
+    char tmp_filename[PATH_MAX];
+    char backing_filename[PATH_MAX];
+
+    bs->read_only = 0;
+    bs->is_temporary = 0;
+    bs->encrypted = 0;
+
+    if (flags & BDRV_O_SNAPSHOT) {
+        BlockDriverState *bs1;
+        int64_t total_size;
+        int is_protocol = 0;
+
+        /* if snapshot, we create a temporary backing file and open it
+           instead of opening 'filename' directly */
+
+        /* if there is a backing file, use it */
+        bs1 = bdrv_new("");
+        if (!bs1) {
+            return -ENOMEM;
+        }
+        if (bdrv_open(bs1, filename, 0) < 0) {
+            bdrv_delete(bs1);
+            return -1;
+        }
+        total_size = bdrv_getlength(bs1) >> SECTOR_BITS;
+
+        if (bs1->drv && bs1->drv->protocol_name)
+            is_protocol = 1;
+
+        bdrv_delete(bs1);
+
+        get_tmp_filename(tmp_filename, sizeof(tmp_filename));
+
+        /* Real path is meaningless for protocols */
+        if (is_protocol)
+            snprintf(backing_filename, sizeof(backing_filename),
+                     "%s", filename);
+        else
+            realpath(filename, backing_filename);
+
+        if (bdrv_create(&bdrv_qcow2, tmp_filename,
+                        total_size, backing_filename, 0) < 0) {
+            return -1;
+        }
+        filename = tmp_filename;
+        bs->is_temporary = 1;
+    }
+
+    pstrcpy(bs->filename, sizeof(bs->filename), filename);
+    if (flags & BDRV_O_FILE) {
+        drv = find_protocol(filename);
+        if (!drv)
+            return -ENOENT;
+    } else {
+        if (!drv) {
+            drv = find_image_format(filename);
+            if (!drv)
+                return -1;
+        }
+    }
+    bs->drv = drv;
+    bs->opaque = qemu_mallocz(drv->instance_size);
+    if (bs->opaque == NULL && drv->instance_size > 0)
+        return -1;
+    /* Note: for compatibility, we open disk image files as RDWR, and
+       RDONLY as fallback */
+    if (!(flags & BDRV_O_FILE))
+        open_flags = BDRV_O_RDWR | (flags & BDRV_O_DIRECT);
+    else
+        open_flags = flags & ~(BDRV_O_FILE | BDRV_O_SNAPSHOT);
+    ret = drv->bdrv_open(bs, filename, open_flags);
+    if (ret == -EACCES && !(flags & BDRV_O_FILE)) {
+        ret = drv->bdrv_open(bs, filename, BDRV_O_RDONLY);
+        bs->read_only = 1;
+    }
+    if (ret < 0) {
+        qemu_free(bs->opaque);
+        bs->opaque = NULL;
+        bs->drv = NULL;
+        return ret;
+    }
+    if (drv->bdrv_getlength) {
+        bs->total_sectors = bdrv_getlength(bs) >> SECTOR_BITS;
+    }
+#ifndef _WIN32
+    if (bs->is_temporary) {
+        unlink(filename);
+    }
+#endif
+    if (bs->backing_file[0] != '\0') {
+        /* if there is a backing file, use it */
+        bs->backing_hd = bdrv_new("");
+        if (!bs->backing_hd) {
+        fail:
+            bdrv_close(bs);
+            return -ENOMEM;
+        }
+        path_combine(backing_filename, sizeof(backing_filename),
+                     filename, bs->backing_file);
+        if (bdrv_open(bs->backing_hd, backing_filename, 0) < 0)
+            goto fail;
+    }
+
+    /* call the change callback */
+    bs->media_changed = 1;
+    if (bs->change_cb)
+        bs->change_cb(bs->change_opaque);
+
+    return 0;
+}
+
+void bdrv_close(BlockDriverState *bs)
+{
+    if (bs->drv) {
+        if (bs->backing_hd)
+            bdrv_delete(bs->backing_hd);
+        bs->drv->bdrv_close(bs);
+        qemu_free(bs->opaque);
+#ifdef _WIN32
+        if (bs->is_temporary) {
+            unlink(bs->filename);
+        }
+#endif
+        bs->opaque = NULL;
+        bs->drv = NULL;
+
+        /* call the change callback */
+        bs->media_changed = 1;
+        if (bs->change_cb)
+            bs->change_cb(bs->change_opaque);
+    }
+}
+
+void bdrv_delete(BlockDriverState *bs)
+{
+    BlockDriverState **pbs;
+
+    pbs = &bdrv_first;
+    while (*pbs != bs && *pbs != NULL)
+        pbs = &(*pbs)->next;
+    if (*pbs == bs)
+        *pbs = bs->next;
+
+    bdrv_close(bs);
+    qemu_free(bs);
+}
+
+/* commit COW file into the raw image */
+int bdrv_commit(BlockDriverState *bs)
+{
+    BlockDriver *drv = bs->drv;
+    int64_t i, total_sectors;
+    int n, j;
+    unsigned char sector[512];
+
+    if (!drv)
+        return -ENOMEDIUM;
+
+    if (bs->read_only) {
+	return -EACCES;
+    }
+
+    if (!bs->backing_hd) {
+	return -ENOTSUP;
+    }
+
+    total_sectors = bdrv_getlength(bs) >> SECTOR_BITS;
+    for (i = 0; i < total_sectors;) {
+        if (drv->bdrv_is_allocated(bs, i, 65536, &n)) {
+            for(j = 0; j < n; j++) {
+                if (bdrv_read(bs, i, sector, 1) != 0) {
+                    return -EIO;
+                }
+
+                if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
+                    return -EIO;
+                }
+                i++;
+	    }
+	} else {
+            i += n;
+        }
+    }
+
+    if (drv->bdrv_make_empty)
+	return drv->bdrv_make_empty(bs);
+
+    return 0;
+}
+
+/* return < 0 if error. See bdrv_write() for the return codes */
+int bdrv_read(BlockDriverState *bs, int64_t sector_num,
+              uint8_t *buf, int nb_sectors)
+{
+    BlockDriver *drv = bs->drv;
+
+    if (!drv)
+        return -ENOMEDIUM;
+
+    if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
+            memcpy(buf, bs->boot_sector_data, 512);
+        sector_num++;
+        nb_sectors--;
+        buf += 512;
+        if (nb_sectors == 0)
+            return 0;
+    }
+    if (drv->bdrv_pread) {
+        int ret, len;
+        len = nb_sectors * 512;
+        ret = drv->bdrv_pread(bs, sector_num * 512, buf, len);
+        if (ret < 0)
+            return ret;
+        else if (ret != len)
+            return -EINVAL;
+        else {
+	    bs->rd_bytes += (unsigned) len;
+	    bs->rd_ops ++;
+            return 0;
+	}
+    } else {
+        return drv->bdrv_read(bs, sector_num, buf, nb_sectors);
+    }
+}
+
+/* Return < 0 if error. Important errors are:
+  -EIO         generic I/O error (may happen for all errors)
+  -ENOMEDIUM   No media inserted.
+  -EINVAL      Invalid sector number or nb_sectors
+  -EACCES      Trying to write a read-only device
+*/
+int bdrv_write(BlockDriverState *bs, int64_t sector_num,
+               const uint8_t *buf, int nb_sectors)
+{
+    BlockDriver *drv = bs->drv;
+    if (!bs->drv)
+        return -ENOMEDIUM;
+    if (bs->read_only)
+        return -EACCES;
+    if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
+        memcpy(bs->boot_sector_data, buf, 512);
+    }
+    if (drv->bdrv_pwrite) {
+        int ret, len;
+        len = nb_sectors * 512;
+        ret = drv->bdrv_pwrite(bs, sector_num * 512, buf, len);
+        if (ret < 0)
+            return ret;
+        else if (ret != len)
+            return -EIO;
+        else {
+	    bs->wr_bytes += (unsigned) len;
+	    bs->wr_ops ++;
+            return 0;
+	}
+    } else {
+        return drv->bdrv_write(bs, sector_num, buf, nb_sectors);
+    }
+}
+
+static int bdrv_pread_em(BlockDriverState *bs, int64_t offset,
+                         uint8_t *buf, int count1)
+{
+    uint8_t tmp_buf[SECTOR_SIZE];
+    int len, nb_sectors, count;
+    int64_t sector_num;
+
+    count = count1;
+    /* first read to align to sector start */
+    len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1);
+    if (len > count)
+        len = count;
+    sector_num = offset >> SECTOR_BITS;
+    if (len > 0) {
+        if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
+            return -EIO;
+        memcpy(buf, tmp_buf + (offset & (SECTOR_SIZE - 1)), len);
+        count -= len;
+        if (count == 0)
+            return count1;
+        sector_num++;
+        buf += len;
+    }
+
+    /* read the sectors "in place" */
+    nb_sectors = count >> SECTOR_BITS;
+    if (nb_sectors > 0) {
+        if (bdrv_read(bs, sector_num, buf, nb_sectors) < 0)
+            return -EIO;
+        sector_num += nb_sectors;
+        len = nb_sectors << SECTOR_BITS;
+        buf += len;
+        count -= len;
+    }
+
+    /* add data from the last sector */
+    if (count > 0) {
+        if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
+            return -EIO;
+        memcpy(buf, tmp_buf, count);
+    }
+    return count1;
+}
+
+static int bdrv_pwrite_em(BlockDriverState *bs, int64_t offset,
+                          const uint8_t *buf, int count1)
+{
+    uint8_t tmp_buf[SECTOR_SIZE];
+    int len, nb_sectors, count;
+    int64_t sector_num;
+
+    count = count1;
+    /* first write to align to sector start */
+    len = (SECTOR_SIZE - offset) & (SECTOR_SIZE - 1);
+    if (len > count)
+        len = count;
+    sector_num = offset >> SECTOR_BITS;
+    if (len > 0) {
+        if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
+            return -EIO;
+        memcpy(tmp_buf + (offset & (SECTOR_SIZE - 1)), buf, len);
+        if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0)
+            return -EIO;
+        count -= len;
+        if (count == 0)
+            return count1;
+        sector_num++;
+        buf += len;
+    }
+
+    /* write the sectors "in place" */
+    nb_sectors = count >> SECTOR_BITS;
+    if (nb_sectors > 0) {
+        if (bdrv_write(bs, sector_num, buf, nb_sectors) < 0)
+            return -EIO;
+        sector_num += nb_sectors;
+        len = nb_sectors << SECTOR_BITS;
+        buf += len;
+        count -= len;
+    }
+
+    /* add data from the last sector */
+    if (count > 0) {
+        if (bdrv_read(bs, sector_num, tmp_buf, 1) < 0)
+            return -EIO;
+        memcpy(tmp_buf, buf, count);
+        if (bdrv_write(bs, sector_num, tmp_buf, 1) < 0)
+            return -EIO;
+    }
+    return count1;
+}
+
+/**
+ * Read with byte offsets (needed only for file protocols)
+ */
+int bdrv_pread(BlockDriverState *bs, int64_t offset,
+               void *buf1, int count1)
+{
+    BlockDriver *drv = bs->drv;
+
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_pread)
+        return bdrv_pread_em(bs, offset, buf1, count1);
+    return drv->bdrv_pread(bs, offset, buf1, count1);
+}
+
+/**
+ * Write with byte offsets (needed only for file protocols)
+ */
+int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+                const void *buf1, int count1)
+{
+    BlockDriver *drv = bs->drv;
+
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_pwrite)
+        return bdrv_pwrite_em(bs, offset, buf1, count1);
+    return drv->bdrv_pwrite(bs, offset, buf1, count1);
+}
+
+/**
+ * Truncate file to 'offset' bytes (needed only for file protocols)
+ */
+int bdrv_truncate(BlockDriverState *bs, int64_t offset)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_truncate)
+        return -ENOTSUP;
+    return drv->bdrv_truncate(bs, offset);
+}
+
+/**
+ * Length of a file in bytes. Return < 0 if error or unknown.
+ */
+int64_t bdrv_getlength(BlockDriverState *bs)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_getlength) {
+        /* legacy mode */
+        return bs->total_sectors * SECTOR_SIZE;
+    }
+    return drv->bdrv_getlength(bs);
+}
+
+/* return 0 as number of sectors if no device present or error */
+void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr)
+{
+    int64_t length;
+    length = bdrv_getlength(bs);
+    if (length < 0)
+        length = 0;
+    else
+        length = length >> SECTOR_BITS;
+    *nb_sectors_ptr = length;
+}
+
+/* force a given boot sector. */
+void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size)
+{
+    bs->boot_sector_enabled = 1;
+    if (size > 512)
+        size = 512;
+    memcpy(bs->boot_sector_data, data, size);
+    memset(bs->boot_sector_data + size, 0, 512 - size);
+}
+
+void bdrv_set_geometry_hint(BlockDriverState *bs,
+                            int cyls, int heads, int secs)
+{
+    bs->cyls = cyls;
+    bs->heads = heads;
+    bs->secs = secs;
+}
+
+void bdrv_set_type_hint(BlockDriverState *bs, int type)
+{
+    bs->type = type;
+    bs->removable = ((type == BDRV_TYPE_CDROM ||
+                      type == BDRV_TYPE_FLOPPY));
+}
+
+void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
+{
+    bs->translation = translation;
+}
+
+void bdrv_get_geometry_hint(BlockDriverState *bs,
+                            int *pcyls, int *pheads, int *psecs)
+{
+    *pcyls = bs->cyls;
+    *pheads = bs->heads;
+    *psecs = bs->secs;
+}
+
+int bdrv_get_type_hint(BlockDriverState *bs)
+{
+    return bs->type;
+}
+
+int bdrv_get_translation_hint(BlockDriverState *bs)
+{
+    return bs->translation;
+}
+
+int bdrv_is_removable(BlockDriverState *bs)
+{
+    return bs->removable;
+}
+
+int bdrv_is_read_only(BlockDriverState *bs)
+{
+    return bs->read_only;
+}
+
+int bdrv_is_sg(BlockDriverState *bs)
+{
+    return bs->sg;
+}
+
+/* XXX: no longer used */
+void bdrv_set_change_cb(BlockDriverState *bs,
+                        void (*change_cb)(void *opaque), void *opaque)
+{
+    bs->change_cb = change_cb;
+    bs->change_opaque = opaque;
+}
+
+int bdrv_is_encrypted(BlockDriverState *bs)
+{
+    if (bs->backing_hd && bs->backing_hd->encrypted)
+        return 1;
+    return bs->encrypted;
+}
+
+int bdrv_set_key(BlockDriverState *bs, const char *key)
+{
+    int ret;
+    if (bs->backing_hd && bs->backing_hd->encrypted) {
+        ret = bdrv_set_key(bs->backing_hd, key);
+        if (ret < 0)
+            return ret;
+        if (!bs->encrypted)
+            return 0;
+    }
+    if (!bs->encrypted || !bs->drv || !bs->drv->bdrv_set_key)
+        return -1;
+    return bs->drv->bdrv_set_key(bs, key);
+}
+
+void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size)
+{
+    if (!bs->drv) {
+        buf[0] = '\0';
+    } else {
+        pstrcpy(buf, buf_size, bs->drv->format_name);
+    }
+}
+
+void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
+                         void *opaque)
+{
+    BlockDriver *drv;
+
+    for (drv = first_drv; drv != NULL; drv = drv->next) {
+        it(opaque, drv->format_name);
+    }
+}
+
+BlockDriverState *bdrv_find(const char *name)
+{
+    BlockDriverState *bs;
+
+    for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+        if (!strcmp(name, bs->device_name))
+            return bs;
+    }
+    return NULL;
+}
+
+void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque)
+{
+    BlockDriverState *bs;
+
+    for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+        it(opaque, bs->device_name);
+    }
+}
+
+const char *bdrv_get_device_name(BlockDriverState *bs)
+{
+    return bs->device_name;
+}
+
+void bdrv_flush(BlockDriverState *bs)
+{
+    if (bs->drv->bdrv_flush)
+        bs->drv->bdrv_flush(bs);
+    if (bs->backing_hd)
+        bdrv_flush(bs->backing_hd);
+}
+
+/*
+ * Returns true iff the specified sector is present in the disk image. Drivers
+ * not implementing the functionality are assumed to not support backing files,
+ * hence all their sectors are reported as allocated.
+ *
+ * 'pnum' is set to the number of sectors (including and immediately following
+ * the specified sector) that are known to be in the same
+ * allocated/unallocated state.
+ *
+ * 'nb_sectors' is the max value 'pnum' should be set to.
+ */
+int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
+	int *pnum)
+{
+    int64_t n;
+    if (!bs->drv->bdrv_is_allocated) {
+        if (sector_num >= bs->total_sectors) {
+            *pnum = 0;
+            return 0;
+        }
+        n = bs->total_sectors - sector_num;
+        *pnum = (n < nb_sectors) ? (n) : (nb_sectors);
+        return 1;
+    }
+    return bs->drv->bdrv_is_allocated(bs, sector_num, nb_sectors, pnum);
+}
+
+void bdrv_info(void)
+{
+    BlockDriverState *bs;
+
+    for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+        term_printf("%s:", bs->device_name);
+        term_printf(" type=");
+        switch(bs->type) {
+        case BDRV_TYPE_HD:
+            term_printf("hd");
+            break;
+        case BDRV_TYPE_CDROM:
+            term_printf("cdrom");
+            break;
+        case BDRV_TYPE_FLOPPY:
+            term_printf("floppy");
+            break;
+        }
+        term_printf(" removable=%d", bs->removable);
+        if (bs->removable) {
+            term_printf(" locked=%d", bs->locked);
+        }
+        if (bs->drv) {
+            term_printf(" file=");
+	    term_print_filename(bs->filename);
+            if (bs->backing_file[0] != '\0') {
+                term_printf(" backing_file=");
+		term_print_filename(bs->backing_file);
+	    }
+            term_printf(" ro=%d", bs->read_only);
+            term_printf(" drv=%s", bs->drv->format_name);
+            if (bs->encrypted)
+                term_printf(" encrypted");
+        } else {
+            term_printf(" [not inserted]");
+        }
+        term_printf("\n");
+    }
+}
+
+/* The "info blockstats" command. */
+void bdrv_info_stats (void)
+{
+    BlockDriverState *bs;
+
+    for (bs = bdrv_first; bs != NULL; bs = bs->next) {
+	term_printf ("%s:"
+		     " rd_bytes=%" PRIu64
+		     " wr_bytes=%" PRIu64
+		     " rd_operations=%" PRIu64
+		     " wr_operations=%" PRIu64
+		     "\n",
+		     bs->device_name,
+		     bs->rd_bytes, bs->wr_bytes,
+		     bs->rd_ops, bs->wr_ops);
+    }
+}
+
+void bdrv_get_backing_filename(BlockDriverState *bs,
+                               char *filename, int filename_size)
+{
+    if (!bs->backing_hd) {
+        pstrcpy(filename, filename_size, "");
+    } else {
+        pstrcpy(filename, filename_size, bs->backing_file);
+    }
+}
+
+int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
+                          const uint8_t *buf, int nb_sectors)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_write_compressed)
+        return -ENOTSUP;
+    return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
+}
+
+int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_get_info)
+        return -ENOTSUP;
+    memset(bdi, 0, sizeof(*bdi));
+    return drv->bdrv_get_info(bs, bdi);
+}
+
+/**************************************************************/
+/* handling of snapshots */
+
+int bdrv_snapshot_create(BlockDriverState *bs,
+                         QEMUSnapshotInfo *sn_info)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_snapshot_create)
+        return -ENOTSUP;
+    return drv->bdrv_snapshot_create(bs, sn_info);
+}
+
+int bdrv_snapshot_goto(BlockDriverState *bs,
+                       const char *snapshot_id)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_snapshot_goto)
+        return -ENOTSUP;
+    return drv->bdrv_snapshot_goto(bs, snapshot_id);
+}
+
+int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_snapshot_delete)
+        return -ENOTSUP;
+    return drv->bdrv_snapshot_delete(bs, snapshot_id);
+}
+
+int bdrv_snapshot_list(BlockDriverState *bs,
+                       QEMUSnapshotInfo **psn_info)
+{
+    BlockDriver *drv = bs->drv;
+    if (!drv)
+        return -ENOMEDIUM;
+    if (!drv->bdrv_snapshot_list)
+        return -ENOTSUP;
+    return drv->bdrv_snapshot_list(bs, psn_info);
+}
+
+#define NB_SUFFIXES 4
+
+char *get_human_readable_size(char *buf, int buf_size, int64_t size)
+{
+    static const char suffixes[NB_SUFFIXES] = "KMGT";
+    int64_t base;
+    int i;
+
+    if (size <= 999) {
+        snprintf(buf, buf_size, "%" PRId64, size);
+    } else {
+        base = 1024;
+        for(i = 0; i < NB_SUFFIXES; i++) {
+            if (size < (10 * base)) {
+                snprintf(buf, buf_size, "%0.1f%c",
+                         (double)size / base,
+                         suffixes[i]);
+                break;
+            } else if (size < (1000 * base) || i == (NB_SUFFIXES - 1)) {
+                snprintf(buf, buf_size, "%" PRId64 "%c",
+                         ((size + (base >> 1)) / base),
+                         suffixes[i]);
+                break;
+            }
+            base = base * 1024;
+        }
+    }
+    return buf;
+}
+
+char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn)
+{
+    char buf1[128], date_buf[128], clock_buf[128];
+#ifdef _WIN32
+    struct tm *ptm;
+#else
+    struct tm tm;
+#endif
+    time_t ti;
+    int64_t secs;
+
+    if (!sn) {
+        snprintf(buf, buf_size,
+                 "%-10s%-20s%7s%20s%15s",
+                 "ID", "TAG", "VM SIZE", "DATE", "VM CLOCK");
+    } else {
+        ti = sn->date_sec;
+#ifdef _WIN32
+        ptm = localtime(&ti);
+        strftime(date_buf, sizeof(date_buf),
+                 "%Y-%m-%d %H:%M:%S", ptm);
+#else
+        localtime_r(&ti, &tm);
+        strftime(date_buf, sizeof(date_buf),
+                 "%Y-%m-%d %H:%M:%S", &tm);
+#endif
+        secs = sn->vm_clock_nsec / 1000000000;
+        snprintf(clock_buf, sizeof(clock_buf),
+                 "%02d:%02d:%02d.%03d",
+                 (int)(secs / 3600),
+                 (int)((secs / 60) % 60),
+                 (int)(secs % 60),
+                 (int)((sn->vm_clock_nsec / 1000000) % 1000));
+        snprintf(buf, buf_size,
+                 "%-10s%-20s%7s%20s%15s",
+                 sn->id_str, sn->name,
+                 get_human_readable_size(buf1, sizeof(buf1), sn->vm_state_size),
+                 date_buf,
+                 clock_buf);
+    }
+    return buf;
+}
+
+
+/**************************************************************/
+/* async I/Os */
+
+BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
+                                uint8_t *buf, int nb_sectors,
+                                BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BlockDriver *drv = bs->drv;
+    BlockDriverAIOCB *ret;
+
+    if (!drv)
+        return NULL;
+
+    /* XXX: we assume that nb_sectors == 0 is suppored by the async read */
+    if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
+        memcpy(buf, bs->boot_sector_data, 512);
+        sector_num++;
+        nb_sectors--;
+        buf += 512;
+    }
+
+    ret = drv->bdrv_aio_read(bs, sector_num, buf, nb_sectors, cb, opaque);
+
+    if (ret) {
+	/* Update stats even though technically transfer has not happened. */
+	bs->rd_bytes += (unsigned) nb_sectors * SECTOR_SIZE;
+	bs->rd_ops ++;
+    }
+
+    return ret;
+}
+
+BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
+                                 const uint8_t *buf, int nb_sectors,
+                                 BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BlockDriver *drv = bs->drv;
+    BlockDriverAIOCB *ret;
+
+    if (!drv)
+        return NULL;
+    if (bs->read_only)
+        return NULL;
+    if (sector_num == 0 && bs->boot_sector_enabled && nb_sectors > 0) {
+        memcpy(bs->boot_sector_data, buf, 512);
+    }
+
+    ret = drv->bdrv_aio_write(bs, sector_num, buf, nb_sectors, cb, opaque);
+
+    if (ret) {
+	/* Update stats even though technically transfer has not happened. */
+	bs->wr_bytes += (unsigned) nb_sectors * SECTOR_SIZE;
+	bs->wr_ops ++;
+    }
+
+    return ret;
+}
+
+void bdrv_aio_cancel(BlockDriverAIOCB *acb)
+{
+    BlockDriver *drv = acb->bs->drv;
+
+    drv->bdrv_aio_cancel(acb);
+}
+
+
+/**************************************************************/
+/* async block device emulation */
+
+static void bdrv_aio_bh_cb(void *opaque)
+{
+    BlockDriverAIOCBSync *acb = opaque;
+    acb->common.cb(acb->common.opaque, acb->ret);
+    qemu_aio_release(acb);
+}
+
+static BlockDriverAIOCB *bdrv_aio_read_em(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BlockDriverAIOCBSync *acb;
+    int ret;
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (!acb->bh)
+        acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+    ret = bdrv_read(bs, sector_num, buf, nb_sectors);
+    acb->ret = ret;
+    qemu_bh_schedule(acb->bh);
+    return &acb->common;
+}
+
+static BlockDriverAIOCB *bdrv_aio_write_em(BlockDriverState *bs,
+        int64_t sector_num, const uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque)
+{
+    BlockDriverAIOCBSync *acb;
+    int ret;
+
+    acb = qemu_aio_get(bs, cb, opaque);
+    if (!acb->bh)
+        acb->bh = qemu_bh_new(bdrv_aio_bh_cb, acb);
+    ret = bdrv_write(bs, sector_num, buf, nb_sectors);
+    acb->ret = ret;
+    qemu_bh_schedule(acb->bh);
+    return &acb->common;
+}
+
+static void bdrv_aio_cancel_em(BlockDriverAIOCB *blockacb)
+{
+    BlockDriverAIOCBSync *acb = (BlockDriverAIOCBSync *)blockacb;
+    qemu_bh_cancel(acb->bh);
+    qemu_aio_release(acb);
+}
+
+/**************************************************************/
+/* sync block device emulation */
+
+static void bdrv_rw_em_cb(void *opaque, int ret)
+{
+    *(int *)opaque = ret;
+}
+
+#define NOT_DONE 0x7fffffff
+
+static int bdrv_read_em(BlockDriverState *bs, int64_t sector_num,
+                        uint8_t *buf, int nb_sectors)
+{
+    int async_ret;
+    BlockDriverAIOCB *acb;
+
+    async_ret = NOT_DONE;
+    acb = bdrv_aio_read(bs, sector_num, buf, nb_sectors,
+                        bdrv_rw_em_cb, &async_ret);
+    if (acb == NULL)
+        return -1;
+
+    while (async_ret == NOT_DONE) {
+        qemu_aio_wait();
+    }
+
+    return async_ret;
+}
+
+static int bdrv_write_em(BlockDriverState *bs, int64_t sector_num,
+                         const uint8_t *buf, int nb_sectors)
+{
+    int async_ret;
+    BlockDriverAIOCB *acb;
+
+    async_ret = NOT_DONE;
+    acb = bdrv_aio_write(bs, sector_num, buf, nb_sectors,
+                         bdrv_rw_em_cb, &async_ret);
+    if (acb == NULL)
+        return -1;
+    while (async_ret == NOT_DONE) {
+        qemu_aio_wait();
+    }
+    return async_ret;
+}
+
+void bdrv_init(void)
+{
+    bdrv_register(&bdrv_raw);
+    bdrv_register(&bdrv_host_device);
+#ifndef _WIN32
+    bdrv_register(&bdrv_cow);
+#endif
+    bdrv_register(&bdrv_qcow);
+#if 0
+    bdrv_register(&bdrv_vmdk);
+#endif
+    bdrv_register(&bdrv_cloop);
+    bdrv_register(&bdrv_dmg);
+#if 0
+    bdrv_register(&bdrv_bochs);
+    bdrv_register(&bdrv_vpc);
+#endif
+    bdrv_register(&bdrv_vvfat);
+#if 0
+    bdrv_register(&bdrv_qcow2);
+    bdrv_register(&bdrv_parallels);
+    bdrv_register(&bdrv_nbd);
+#endif
+    qemu_aio_init();
+}
+
+void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb,
+                   void *opaque)
+{
+    BlockDriver *drv;
+    BlockDriverAIOCB *acb;
+
+    drv = bs->drv;
+    if (drv->free_aiocb) {
+        acb = drv->free_aiocb;
+        drv->free_aiocb = acb->next;
+    } else {
+        acb = qemu_mallocz(drv->aiocb_size);
+        if (!acb)
+            return NULL;
+    }
+    acb->bs = bs;
+    acb->cb = cb;
+    acb->opaque = opaque;
+    return acb;
+}
+
+void qemu_aio_release(void *p)
+{
+    BlockDriverAIOCB *acb = p;
+    BlockDriver *drv = acb->bs->drv;
+    acb->next = drv->free_aiocb;
+    drv->free_aiocb = acb;
+}
+
+/**************************************************************/
+/* removable device support */
+
+/**
+ * Return TRUE if the media is present
+ */
+int bdrv_is_inserted(BlockDriverState *bs)
+{
+    BlockDriver *drv = bs->drv;
+    int ret;
+    if (!drv)
+        return 0;
+    if (!drv->bdrv_is_inserted)
+        return 1;
+    ret = drv->bdrv_is_inserted(bs);
+    return ret;
+}
+
+/**
+ * Return TRUE if the media changed since the last call to this
+ * function. It is currently only used for floppy disks
+ */
+int bdrv_media_changed(BlockDriverState *bs)
+{
+    BlockDriver *drv = bs->drv;
+    int ret;
+
+    if (!drv || !drv->bdrv_media_changed)
+        ret = -ENOTSUP;
+    else
+        ret = drv->bdrv_media_changed(bs);
+    if (ret == -ENOTSUP)
+        ret = bs->media_changed;
+    bs->media_changed = 0;
+    return ret;
+}
+
+/**
+ * If eject_flag is TRUE, eject the media. Otherwise, close the tray
+ */
+void bdrv_eject(BlockDriverState *bs, int eject_flag)
+{
+    BlockDriver *drv = bs->drv;
+    int ret;
+
+    if (!drv || !drv->bdrv_eject) {
+        ret = -ENOTSUP;
+    } else {
+        ret = drv->bdrv_eject(bs, eject_flag);
+    }
+    if (ret == -ENOTSUP) {
+        if (eject_flag)
+            bdrv_close(bs);
+    }
+}
+
+int bdrv_is_locked(BlockDriverState *bs)
+{
+    return bs->locked;
+}
+
+/**
+ * Lock or unlock the media (if it is locked, the user won't be able
+ * to eject it manually).
+ */
+void bdrv_set_locked(BlockDriverState *bs, int locked)
+{
+    BlockDriver *drv = bs->drv;
+
+    bs->locked = locked;
+    if (drv && drv->bdrv_set_locked) {
+        drv->bdrv_set_locked(bs, locked);
+    }
+}
+
+/* needed for generic scsi interface */
+
+int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
+{
+    BlockDriver *drv = bs->drv;
+
+    if (drv && drv->bdrv_ioctl)
+        return drv->bdrv_ioctl(bs, req, buf);
+    return -ENOTSUP;
+}
diff --git a/block.h b/block.h
new file mode 100644
index 0000000..7917eb8
--- /dev/null
+++ b/block.h
@@ -0,0 +1,159 @@
+#ifndef BLOCK_H
+#define BLOCK_H
+
+/* block.c */
+typedef struct BlockDriver BlockDriver;
+
+extern BlockDriver bdrv_raw;
+extern BlockDriver bdrv_host_device;
+extern BlockDriver bdrv_cow;
+extern BlockDriver bdrv_qcow;
+extern BlockDriver bdrv_vmdk;
+extern BlockDriver bdrv_cloop;
+extern BlockDriver bdrv_dmg;
+extern BlockDriver bdrv_bochs;
+extern BlockDriver bdrv_vpc;
+extern BlockDriver bdrv_vvfat;
+extern BlockDriver bdrv_qcow2;
+extern BlockDriver bdrv_parallels;
+extern BlockDriver bdrv_nbd;
+
+typedef struct BlockDriverInfo {
+    /* in bytes, 0 if irrelevant */
+    int cluster_size;
+    /* offset at which the VM state can be saved (0 if not possible) */
+    int64_t vm_state_offset;
+} BlockDriverInfo;
+
+typedef struct QEMUSnapshotInfo {
+    char id_str[128]; /* unique snapshot id */
+    /* the following fields are informative. They are not needed for
+       the consistency of the snapshot */
+    char name[256]; /* user choosen name */
+    uint32_t vm_state_size; /* VM state info size */
+    uint32_t date_sec; /* UTC date of the snapshot */
+    uint32_t date_nsec;
+    uint64_t vm_clock_nsec; /* VM clock relative to boot */
+} QEMUSnapshotInfo;
+
+#define BDRV_O_RDONLY      0x0000
+#define BDRV_O_RDWR        0x0002
+#define BDRV_O_ACCESS      0x0003
+#define BDRV_O_CREAT       0x0004 /* create an empty file */
+#define BDRV_O_SNAPSHOT    0x0008 /* open the file read only and save writes in a snapshot */
+#define BDRV_O_FILE        0x0010 /* open as a raw file (do not try to
+                                     use a disk image format on top of
+                                     it (default for
+                                     bdrv_file_open()) */
+#define BDRV_O_DIRECT      0x0020
+
+void bdrv_info(void);
+void bdrv_info_stats(void);
+
+void bdrv_init(void);
+BlockDriver *bdrv_find_format(const char *format_name);
+int bdrv_create(BlockDriver *drv,
+                const char *filename, int64_t size_in_sectors,
+                const char *backing_file, int flags);
+BlockDriverState *bdrv_new(const char *device_name);
+void bdrv_delete(BlockDriverState *bs);
+int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
+int bdrv_open(BlockDriverState *bs, const char *filename, int flags);
+int bdrv_open2(BlockDriverState *bs, const char *filename, int flags,
+               BlockDriver *drv);
+void bdrv_close(BlockDriverState *bs);
+int bdrv_read(BlockDriverState *bs, int64_t sector_num,
+              uint8_t *buf, int nb_sectors);
+int bdrv_write(BlockDriverState *bs, int64_t sector_num,
+               const uint8_t *buf, int nb_sectors);
+int bdrv_pread(BlockDriverState *bs, int64_t offset,
+               void *buf, int count);
+int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
+                const void *buf, int count);
+int bdrv_truncate(BlockDriverState *bs, int64_t offset);
+int64_t bdrv_getlength(BlockDriverState *bs);
+void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
+int bdrv_commit(BlockDriverState *bs);
+void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size);
+/* async block I/O */
+typedef struct BlockDriverAIOCB BlockDriverAIOCB;
+typedef void BlockDriverCompletionFunc(void *opaque, int ret);
+
+BlockDriverAIOCB *bdrv_aio_read(BlockDriverState *bs, int64_t sector_num,
+                                uint8_t *buf, int nb_sectors,
+                                BlockDriverCompletionFunc *cb, void *opaque);
+BlockDriverAIOCB *bdrv_aio_write(BlockDriverState *bs, int64_t sector_num,
+                                 const uint8_t *buf, int nb_sectors,
+                                 BlockDriverCompletionFunc *cb, void *opaque);
+void bdrv_aio_cancel(BlockDriverAIOCB *acb);
+
+void qemu_aio_init(void);
+void qemu_aio_flush(void);
+void qemu_aio_wait(void);
+
+int qemu_key_check(BlockDriverState *bs, const char *name);
+
+/* Ensure contents are flushed to disk.  */
+void bdrv_flush(BlockDriverState *bs);
+int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
+	int *pnum);
+
+#define BDRV_TYPE_HD     0
+#define BDRV_TYPE_CDROM  1
+#define BDRV_TYPE_FLOPPY 2
+#define BIOS_ATA_TRANSLATION_AUTO   0
+#define BIOS_ATA_TRANSLATION_NONE   1
+#define BIOS_ATA_TRANSLATION_LBA    2
+#define BIOS_ATA_TRANSLATION_LARGE  3
+#define BIOS_ATA_TRANSLATION_RECHS  4
+
+void bdrv_set_geometry_hint(BlockDriverState *bs,
+                            int cyls, int heads, int secs);
+void bdrv_set_type_hint(BlockDriverState *bs, int type);
+void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
+void bdrv_get_geometry_hint(BlockDriverState *bs,
+                            int *pcyls, int *pheads, int *psecs);
+int bdrv_get_type_hint(BlockDriverState *bs);
+int bdrv_get_translation_hint(BlockDriverState *bs);
+int bdrv_is_removable(BlockDriverState *bs);
+int bdrv_is_read_only(BlockDriverState *bs);
+int bdrv_is_sg(BlockDriverState *bs);
+int bdrv_is_inserted(BlockDriverState *bs);
+int bdrv_media_changed(BlockDriverState *bs);
+int bdrv_is_locked(BlockDriverState *bs);
+void bdrv_set_locked(BlockDriverState *bs, int locked);
+void bdrv_eject(BlockDriverState *bs, int eject_flag);
+void bdrv_set_change_cb(BlockDriverState *bs,
+                        void (*change_cb)(void *opaque), void *opaque);
+void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
+BlockDriverState *bdrv_find(const char *name);
+void bdrv_iterate(void (*it)(void *opaque, const char *name), void *opaque);
+int bdrv_is_encrypted(BlockDriverState *bs);
+int bdrv_set_key(BlockDriverState *bs, const char *key);
+void bdrv_iterate_format(void (*it)(void *opaque, const char *name),
+                         void *opaque);
+const char *bdrv_get_device_name(BlockDriverState *bs);
+int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
+                          const uint8_t *buf, int nb_sectors);
+int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
+
+void bdrv_get_backing_filename(BlockDriverState *bs,
+                               char *filename, int filename_size);
+int bdrv_snapshot_create(BlockDriverState *bs,
+                         QEMUSnapshotInfo *sn_info);
+int bdrv_snapshot_goto(BlockDriverState *bs,
+                       const char *snapshot_id);
+int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
+int bdrv_snapshot_list(BlockDriverState *bs,
+                       QEMUSnapshotInfo **psn_info);
+char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
+int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf);
+
+char *get_human_readable_size(char *buf, int buf_size, int64_t size);
+// don't remove below comment, it makes integration with upstream sources easier
+//int path_is_absolute(const char *path);
+void path_combine(char *dest, int dest_size,
+                  const char *base_path,
+                  const char *filename);
+
+#endif
diff --git a/block_int.h b/block_int.h
new file mode 100644
index 0000000..137000e
--- /dev/null
+++ b/block_int.h
@@ -0,0 +1,150 @@
+/*
+ * QEMU System Emulator block driver
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef BLOCK_INT_H
+#define BLOCK_INT_H
+
+#include "block.h"
+
+#define BLOCK_FLAG_ENCRYPT	1
+#define BLOCK_FLAG_COMPRESS	2
+#define BLOCK_FLAG_COMPAT6	4
+
+struct BlockDriver {
+    const char *format_name;
+    int instance_size;
+    int (*bdrv_probe)(const uint8_t *buf, int buf_size, const char *filename);
+    int (*bdrv_open)(BlockDriverState *bs, const char *filename, int flags);
+    int (*bdrv_read)(BlockDriverState *bs, int64_t sector_num,
+                     uint8_t *buf, int nb_sectors);
+    int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
+                      const uint8_t *buf, int nb_sectors);
+    void (*bdrv_close)(BlockDriverState *bs);
+    int (*bdrv_create)(const char *filename, int64_t total_sectors,
+                       const char *backing_file, int flags);
+    void (*bdrv_flush)(BlockDriverState *bs);
+    int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
+                             int nb_sectors, int *pnum);
+    int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
+    int (*bdrv_make_empty)(BlockDriverState *bs);
+    /* aio */
+    BlockDriverAIOCB *(*bdrv_aio_read)(BlockDriverState *bs,
+        int64_t sector_num, uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque);
+    BlockDriverAIOCB *(*bdrv_aio_write)(BlockDriverState *bs,
+        int64_t sector_num, const uint8_t *buf, int nb_sectors,
+        BlockDriverCompletionFunc *cb, void *opaque);
+    void (*bdrv_aio_cancel)(BlockDriverAIOCB *acb);
+    int aiocb_size;
+
+    const char *protocol_name;
+    int (*bdrv_pread)(BlockDriverState *bs, int64_t offset,
+                      uint8_t *buf, int count);
+    int (*bdrv_pwrite)(BlockDriverState *bs, int64_t offset,
+                       const uint8_t *buf, int count);
+    int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
+    int64_t (*bdrv_getlength)(BlockDriverState *bs);
+    int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
+                                 const uint8_t *buf, int nb_sectors);
+
+    int (*bdrv_snapshot_create)(BlockDriverState *bs,
+                                QEMUSnapshotInfo *sn_info);
+    int (*bdrv_snapshot_goto)(BlockDriverState *bs,
+                              const char *snapshot_id);
+    int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
+    int (*bdrv_snapshot_list)(BlockDriverState *bs,
+                              QEMUSnapshotInfo **psn_info);
+    int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
+
+    /* removable device specific */
+    int (*bdrv_is_inserted)(BlockDriverState *bs);
+    int (*bdrv_media_changed)(BlockDriverState *bs);
+    int (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
+    int (*bdrv_set_locked)(BlockDriverState *bs, int locked);
+
+    /* to control generic scsi devices */
+    int (*bdrv_ioctl)(BlockDriverState *bs, unsigned long int req, void *buf);
+
+    BlockDriverAIOCB *free_aiocb;
+    struct BlockDriver *next;
+};
+
+struct BlockDriverState {
+    int64_t total_sectors; /* if we are reading a disk image, give its
+                              size in sectors */
+    int read_only; /* if true, the media is read only */
+    int removable; /* if true, the media can be removed */
+    int locked;    /* if true, the media cannot temporarily be ejected */
+    int encrypted; /* if true, the media is encrypted */
+    int sg;        /* if true, the device is a /dev/sg* */
+    /* event callback when inserting/removing */
+    void (*change_cb)(void *opaque);
+    void *change_opaque;
+
+    BlockDriver *drv; /* NULL means no media */
+    void *opaque;
+
+    int boot_sector_enabled;
+    uint8_t boot_sector_data[512];
+
+    char filename[1024];
+    char backing_file[1024]; /* if non zero, the image is a diff of
+                                this file image */
+    int is_temporary;
+    int media_changed;
+
+    BlockDriverState *backing_hd;
+    /* async read/write emulation */
+
+    void *sync_aiocb;
+
+    /* I/O stats (display with "info blockstats"). */
+    uint64_t rd_bytes;
+    uint64_t wr_bytes;
+    uint64_t rd_ops;
+    uint64_t wr_ops;
+
+    /* NOTE: the following infos are only hints for real hardware
+       drivers. They are not used by the block driver */
+    int cyls, heads, secs, translation;
+    int type;
+    char device_name[32];
+    BlockDriverState *next;
+};
+
+struct BlockDriverAIOCB {
+    BlockDriverState *bs;
+    BlockDriverCompletionFunc *cb;
+    void *opaque;
+    BlockDriverAIOCB *next;
+};
+
+void get_tmp_filename(char *filename, int size);
+
+void *qemu_aio_get(BlockDriverState *bs, BlockDriverCompletionFunc *cb,
+                   void *opaque);
+void qemu_aio_release(void *p);
+
+BlockDriverState *bdrv_first;
+
+#endif /* BLOCK_INT_H */
diff --git a/bswap.h b/bswap.h
new file mode 100644
index 0000000..523d805
--- /dev/null
+++ b/bswap.h
@@ -0,0 +1,209 @@
+#ifndef BSWAP_H
+#define BSWAP_H
+
+#include "config-host.h"
+
+#include <inttypes.h>
+
+#ifdef HAVE_BYTESWAP_H
+#include <byteswap.h>
+#else
+
+#define bswap_16(x) \
+({ \
+	uint16_t __x = (x); \
+	((uint16_t)( \
+		(((uint16_t)(__x) & (uint16_t)0x00ffU) << 8) | \
+		(((uint16_t)(__x) & (uint16_t)0xff00U) >> 8) )); \
+})
+
+#define bswap_32(x) \
+({ \
+	uint32_t __x = (x); \
+	((uint32_t)( \
+		(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+		(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
+		(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
+		(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \
+})
+
+#define bswap_64(x) \
+({ \
+	uint64_t __x = (x); \
+	((uint64_t)( \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) <<  8) | \
+	        (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >>  8) | \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \
+		(uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \
+})
+
+#endif /* !HAVE_BYTESWAP_H */
+
+static inline uint16_t bswap16(uint16_t x)
+{
+    return bswap_16(x);
+}
+
+static inline uint32_t bswap32(uint32_t x)
+{
+    return bswap_32(x);
+}
+
+static inline uint64_t bswap64(uint64_t x)
+{
+    return bswap_64(x);
+}
+
+static inline void bswap16s(uint16_t *s)
+{
+    *s = bswap16(*s);
+}
+
+static inline void bswap32s(uint32_t *s)
+{
+    *s = bswap32(*s);
+}
+
+static inline void bswap64s(uint64_t *s)
+{
+    *s = bswap64(*s);
+}
+
+#if defined(WORDS_BIGENDIAN)
+#define be_bswap(v, size) (v)
+#define le_bswap(v, size) bswap ## size(v)
+#define be_bswaps(v, size)
+#define le_bswaps(p, size) *p = bswap ## size(*p);
+#else
+#define le_bswap(v, size) (v)
+#define be_bswap(v, size) bswap ## size(v)
+#define le_bswaps(v, size)
+#define be_bswaps(p, size) *p = bswap ## size(*p);
+#endif
+
+#define CPU_CONVERT(endian, size, type)\
+static inline type endian ## size ## _to_cpu(type v)\
+{\
+    return endian ## _bswap(v, size);\
+}\
+\
+static inline type cpu_to_ ## endian ## size(type v)\
+{\
+    return endian ## _bswap(v, size);\
+}\
+\
+static inline void endian ## size ## _to_cpus(type *p)\
+{\
+    endian ## _bswaps(p, size)\
+}\
+\
+static inline void cpu_to_ ## endian ## size ## s(type *p)\
+{\
+    endian ## _bswaps(p, size)\
+}\
+\
+static inline type endian ## size ## _to_cpup(const type *p)\
+{\
+    return endian ## size ## _to_cpu(*p);\
+}\
+\
+static inline void cpu_to_ ## endian ## size ## w(type *p, type v)\
+{\
+     *p = cpu_to_ ## endian ## size(v);\
+}
+
+CPU_CONVERT(be, 16, uint16_t)
+CPU_CONVERT(be, 32, uint32_t)
+CPU_CONVERT(be, 64, uint64_t)
+
+CPU_CONVERT(le, 16, uint16_t)
+CPU_CONVERT(le, 32, uint32_t)
+CPU_CONVERT(le, 64, uint64_t)
+
+/* unaligned versions (optimized for frequent unaligned accesses)*/
+
+#if defined(__i386__) || defined(__powerpc__)
+
+#define cpu_to_le16wu(p, v) cpu_to_le16w(p, v)
+#define cpu_to_le32wu(p, v) cpu_to_le32w(p, v)
+#define le16_to_cpupu(p) le16_to_cpup(p)
+#define le32_to_cpupu(p) le32_to_cpup(p)
+#define be32_to_cpupu(p) be32_to_cpup(p)
+
+#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
+#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
+
+#else
+
+static inline void cpu_to_le16wu(uint16_t *p, uint16_t v)
+{
+    uint8_t *p1 = (uint8_t *)p;
+
+    p1[0] = v;
+    p1[1] = v >> 8;
+}
+
+static inline void cpu_to_le32wu(uint32_t *p, uint32_t v)
+{
+    uint8_t *p1 = (uint8_t *)p;
+
+    p1[0] = v;
+    p1[1] = v >> 8;
+    p1[2] = v >> 16;
+    p1[3] = v >> 24;
+}
+
+static inline uint16_t le16_to_cpupu(const uint16_t *p)
+{
+    const uint8_t *p1 = (const uint8_t *)p;
+    return p1[0] | (p1[1] << 8);
+}
+
+static inline uint32_t le32_to_cpupu(const uint32_t *p)
+{
+    const uint8_t *p1 = (const uint8_t *)p;
+    return p1[0] | (p1[1] << 8) | (p1[2] << 16) | (p1[3] << 24);
+}
+
+static inline uint32_t be32_to_cpupu(const uint32_t *p)
+{
+    const uint8_t *p1 = (const uint8_t *)p;
+    return p1[3] | (p1[2] << 8) | (p1[1] << 16) | (p1[0] << 24);
+}
+
+static inline void cpu_to_be16wu(uint16_t *p, uint16_t v)
+{
+    uint8_t *p1 = (uint8_t *)p;
+
+    p1[0] = v >> 8;
+    p1[1] = v;
+}
+
+static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
+{
+    uint8_t *p1 = (uint8_t *)p;
+
+    p1[0] = v >> 24;
+    p1[1] = v >> 16;
+    p1[2] = v >> 8;
+    p1[3] = v;
+}
+
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define cpu_to_32wu cpu_to_be32wu
+#else
+#define cpu_to_32wu cpu_to_le32wu
+#endif
+
+#undef le_bswap
+#undef be_bswap
+#undef le_bswaps
+#undef be_bswaps
+
+#endif /* BSWAP_H */
diff --git a/cbuffer.c b/cbuffer.c
new file mode 100644
index 0000000..0e99237
--- /dev/null
+++ b/cbuffer.c
@@ -0,0 +1,231 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "cbuffer.h"
+#include "android/utils/stralloc.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define  DEBUG  0
+
+#if DEBUG
+#  define  ASSERT(cond,fmt,...)  ({ if (!(cond)) { fprintf(stderr, fmt, __VA_ARGS__); assert(cond); } })
+#else
+#  define  ASSERT(cond,fmt,...)  ((void)0)
+#endif
+
+#if DEBUG
+void
+cbuffer_assert( CBuffer*  cb, const char*  file, long  lineno )
+{
+    const char*  reason = NULL;
+
+    if (cb->rpos < 0 || cb->rpos >= cb->size) {
+        reason = "rpos is out of bounds";
+    }
+    else if (cb->count < 0 || cb->count > cb->size) {
+        reason = "count is incorrect";
+    }
+    if (!reason)
+        return;
+
+    fprintf(stderr, "assert:%s:%ld: assertion failed: %s (pos=%d count=%d size=%d)\n",
+            file, lineno, reason, cb->rpos, cb->count, cb->size);
+    assert(0);
+}
+#  define  CBUFFER_ASSERT(cb)  cbuffer_assert(cb,__FUNCTION__,__LINE__)
+#else
+#  define  CBUFFER_ASSERT(cb)  ((void)0)
+#endif
+
+int
+cbuffer_write_peek( CBuffer*  cb, uint8_t*  *pbase )
+{
+    int  wpos  = cb->rpos + cb->count;
+    int  avail = cb->size - cb->count;
+
+    CBUFFER_ASSERT(cb);
+
+    if (wpos >= cb->size)
+        wpos -= cb->size;
+
+    if (wpos + avail > cb->size)
+        avail = cb->size - wpos;
+
+    *pbase = cb->buff + wpos;
+    return avail;
+}
+
+void
+cbuffer_write_step( CBuffer*  cb, int  len )
+{
+    CBUFFER_ASSERT(cb);
+
+    cb->count += len;
+    if (cb->count > cb->size)
+        cb->count = cb->size;
+}
+
+
+int
+cbuffer_write( CBuffer*  cb, const void*  from, int  len )
+{
+    int  len2 = len;
+
+    CBUFFER_ASSERT(cb);
+
+    while (len2 > 0) {
+        int  avail = cb->size - cb->count;
+        int  wpos  = cb->rpos + cb->count;
+
+        ASSERT(avail >= 0, "avail is negative: %d", avail);
+
+        if (avail == 0)
+            break;
+
+        if (wpos >= cb->size)
+            wpos -= cb->size;
+
+        ASSERT( wpos >= 0 && wpos < cb->size, "wpos is out-of-bounds: %d (rpos=%d)", wpos, cb->rpos);
+
+        if (wpos + avail > cb->size)
+            avail = cb->size - wpos;
+
+        if (avail > len2)
+            avail = len2;
+
+        memcpy( cb->buff + wpos, (const char*)from, avail );
+
+        from  = (char*)from + avail;
+        len2 -= avail;
+        cb->count += avail;
+    }
+    return len - len2;
+}
+
+int
+cbuffer_read( CBuffer*  cb, void*  to, int  len )
+{
+    int   len2 = len;
+
+    CBUFFER_ASSERT(cb);
+
+    while (len2 > 0) {
+        int  avail = cb->count;
+        int  rpos = cb->rpos;
+
+        ASSERT(avail >= 0, "avail is negative: %d", avail);
+
+        if (avail == 0)
+            break;
+
+        ASSERT((rpos >= 0 && rpos < cb->size), "rpos is out-of-bounds: %d", rpos);
+
+        if (rpos+avail > cb->size)
+            avail = cb->size - rpos;
+
+        if (avail > len2)
+            avail = len2;
+
+        memcpy( (char*)to, (const char*)cb->buff + rpos, avail );
+        to    = (char*)to + avail;
+        len2 -= avail;
+        cb->count -= avail;
+        cb->rpos  += avail;
+        if (cb->rpos >= cb->size)
+            cb->rpos -= cb->size;
+    }
+    return len - len2;
+}
+
+int
+cbuffer_read_peek( CBuffer*  cb, uint8_t*  *pbase )
+{
+    int   rpos  = cb->rpos;
+    int   avail = cb->count;
+
+    CBUFFER_ASSERT(cb);
+
+    if (rpos + avail > cb->size)
+        avail = cb->size - rpos;
+
+    *pbase = cb->buff + rpos;
+    return avail;
+}
+
+
+void
+cbuffer_read_step( CBuffer*  cb, int  len )
+{
+    CBUFFER_ASSERT(cb);
+
+    if (len > cb->count)
+        len = cb->count;
+
+    cb->rpos  += len;
+    if (cb->rpos >= cb->size)
+        cb->rpos -= cb->size;
+
+    cb->count -= len;
+}
+
+const char*
+cbuffer_quote( CBuffer*  cb )
+{
+    STRALLOC_DEFINE(s);
+    char* q;
+
+    stralloc_format( s, "cbuffer %p (pos=%d count=%d size=%d)",
+                     cb, cb->rpos, cb->count, cb->size );
+
+    q = stralloc_to_tempstr( s );
+    stralloc_reset(s);
+
+    return q;
+}
+
+const char*
+cbuffer_quote_data( CBuffer*  cb )
+{
+    STRALLOC_DEFINE(s);
+    int   len  = cb->count;
+    int   rpos = cb->rpos;
+    char* result;
+
+    while (len > 0) {
+        int  avail = len;
+
+        if (rpos >= cb->size)
+            rpos -= cb->size;
+
+        if (rpos + avail > cb->size)
+            avail = cb->size - rpos;
+
+        stralloc_add_quote_bytes( s, cb->buff + rpos, avail );
+        rpos += avail;
+        len  -= avail;
+    }
+
+    result = stralloc_to_tempstr(s);
+    stralloc_reset(s);
+
+    return result;
+}
+
+void
+cbuffer_print( CBuffer*  cb )
+{
+    /* print the content of a cbuffer */
+    printf( "%s: %s", cbuffer_quote(cb), cbuffer_quote_data(cb) );
+}
+
diff --git a/cbuffer.h b/cbuffer.h
new file mode 100644
index 0000000..d25d765
--- /dev/null
+++ b/cbuffer.h
@@ -0,0 +1,61 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _qemu_cbuffer_h
+#define _qemu_cbuffer_h
+
+#include <stdint.h>
+
+/* Basic circular buffer type and methods */
+
+typedef struct {
+    uint8_t*  buff;
+    int       size;
+    int       rpos;
+    int       count;
+} CBuffer;
+
+static __inline__ void
+cbuffer_reset( CBuffer*  cb, void*  buff, int  size )
+{
+    cb->buff  = buff;
+    cb->size  = size;
+    cb->rpos  = 0;
+    cb->count = 0;
+}
+
+static __inline__ int
+cbuffer_write_avail( CBuffer*  cb )
+{
+    return cb->size - cb->count;
+}
+
+extern int  cbuffer_write( CBuffer*  cb, const void*  from, int  len );
+extern int  cbuffer_write_peek( CBuffer*  cb, uint8_t*  *pbase );
+extern void cbuffer_write_step( CBuffer*  cb, int  len );
+
+static __inline__ int
+cbuffer_read_avail( CBuffer*  cb )
+{
+    return cb->count;
+}
+
+extern int  cbuffer_read( CBuffer*  cb, void*  to, int  len );
+extern int  cbuffer_read_peek( CBuffer*  cb, uint8_t*  *pbase );
+extern void cbuffer_read_step( CBuffer*  cb, int  len );
+
+extern const char*  cbuffer_quote( CBuffer*  cb );
+extern const char*  cbuffer_quote_data( CBuffer*  cb );
+extern void         cbuffer_print( CBuffer*  cb );
+
+#endif /* qemu_cbuffer_h */
+
+
diff --git a/charpipe.c b/charpipe.c
new file mode 100644
index 0000000..d6b9829
--- /dev/null
+++ b/charpipe.c
@@ -0,0 +1,273 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu-char.h"
+#include "cbuffer.h"
+#include "qemu_debug.h"
+
+#define  xxDEBUG
+
+#ifdef DEBUG
+#  include <stdio.h>
+#  define  D(...)   ( fprintf( stderr, __VA_ARGS__ ), fprintf(stderr, "\n") )
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+/* we want to implement a bi-directionnal communication channel
+ * between two QEMU character drivers that merge well into the
+ * QEMU event loop.
+ *
+ * each half of the channel has its own object and buffer, and
+ * we implement communication through charpipe_poll() which
+ * must be called by the main event loop after its call to select()
+ *
+ */
+
+#define  BIP_BUFFER_SIZE  512
+
+typedef struct BipBuffer {
+    struct BipBuffer*  next;
+    CBuffer            cb[1];
+    char               buff[ BIP_BUFFER_SIZE ];
+} BipBuffer;
+
+static BipBuffer*  _free_bip_buffers;
+
+static BipBuffer*
+bip_buffer_alloc( void )
+{
+    BipBuffer*  bip = _free_bip_buffers;
+    if (bip != NULL) {
+        _free_bip_buffers = bip->next;
+    } else {
+        bip = malloc( sizeof(*bip) );
+        if (bip == NULL) {
+            derror( "%s: not enough memory", __FUNCTION__ );
+            exit(1);
+        }
+    }
+    bip->next = NULL;
+    cbuffer_reset( bip->cb, bip->buff, sizeof(bip->buff) );
+    return bip;
+}
+
+static void
+bip_buffer_free( BipBuffer*  bip )
+{
+    bip->next         = _free_bip_buffers;
+    _free_bip_buffers = bip;
+}
+
+/* this models each half of the charpipe */
+typedef struct CharPipeHalf {
+    CharDriverState       cs[1];
+    BipBuffer*            bip_first;
+    BipBuffer*            bip_last;
+    struct CharPipeHalf*  peer;         /* NULL if closed */
+} CharPipeHalf;
+
+
+
+static void
+charpipehalf_close( CharDriverState*  cs )
+{
+    CharPipeHalf*  ph = cs->opaque;
+
+    while (ph->bip_first) {
+        BipBuffer*  bip = ph->bip_first;
+        ph->bip_first = bip->next;
+        bip_buffer_free(bip);
+    }
+    ph->bip_last    = NULL;
+    ph->peer        = NULL;
+}
+
+
+static int
+charpipehalf_write( CharDriverState*  cs, const uint8_t*  buf, int  len )
+{
+    CharPipeHalf*  ph   = cs->opaque;
+    CharPipeHalf*  peer = ph->peer;
+    BipBuffer*     bip  = ph->bip_last;
+    int            ret  = 0;
+
+    D("%s: writing %d bytes to %p: '%s'", __FUNCTION__,
+      len, ph, quote_bytes( buf, len ));
+
+    if (bip == NULL && peer != NULL && peer->cs->chr_read != NULL) {
+        /* no buffered data, try to write directly to the peer */
+        while (len > 0) {
+            int  size;
+
+            if (peer->cs->chr_can_read) {
+                size = qemu_chr_can_read( peer->cs );
+                if (size == 0)
+                    break;
+
+                if (size > len)
+                    size = len;
+            } else
+                size = len;
+
+            qemu_chr_read( peer->cs, (uint8_t*)buf, size );
+            buf += size;
+            len -= size;
+            ret += size;
+        }
+    }
+
+    if (len == 0)
+        return ret;
+
+    /* buffer the remaining data */
+    if (bip == NULL) {
+        bip = bip_buffer_alloc();
+        ph->bip_first = ph->bip_last = bip;
+    }
+
+    while (len > 0) {
+        int  len2 = cbuffer_write( bip->cb, buf, len );
+
+        buf += len2;
+        ret += len2;
+        len -= len2;
+        if (len == 0)
+            break;
+
+        /* ok, we need another buffer */
+        ph->bip_last = bip_buffer_alloc();
+        bip->next = ph->bip_last;
+        bip       = ph->bip_last;
+    }
+    return  ret;
+}
+
+
+static void
+charpipehalf_poll( CharPipeHalf*  ph )
+{
+    CharPipeHalf*   peer = ph->peer;
+    int             size;
+
+    if (peer == NULL || peer->cs->chr_read == NULL)
+        return;
+
+    while (1) {
+        BipBuffer*  bip = ph->bip_first;
+        uint8_t*    base;
+        int         avail;
+
+        if (bip == NULL)
+            break;
+
+        size = cbuffer_read_avail(bip->cb);
+        if (size == 0) {
+            ph->bip_first = bip->next;
+            if (ph->bip_first == NULL)
+                ph->bip_last = NULL;
+            bip_buffer_free(bip);
+            continue;
+        }
+
+        if (ph->cs->chr_can_read) {
+            int  size2 = qemu_chr_can_read(peer->cs);
+
+            if (size2 == 0)
+                break;
+
+            if (size > size2)
+                size = size2;
+        }
+
+        avail = cbuffer_read_peek( bip->cb, &base );
+        if (avail > size)
+            avail = size;
+        D("%s: sending %d bytes from %p: '%s'", __FUNCTION__,
+            avail, ph, quote_bytes( base, avail ));
+
+        qemu_chr_read( peer->cs, base, avail );
+        cbuffer_read_step( bip->cb, avail );
+    }
+}
+
+
+static void
+charpipehalf_init( CharPipeHalf*  ph, CharPipeHalf*  peer )
+{
+    CharDriverState*  cs = ph->cs;
+
+    ph->bip_first   = NULL;
+    ph->bip_last    = NULL;
+    ph->peer        = peer;
+
+    cs->chr_write            = charpipehalf_write;
+    cs->chr_ioctl            = NULL;
+    cs->chr_send_event       = NULL;
+    cs->chr_close            = charpipehalf_close;
+    cs->opaque               = ph;
+}
+
+
+typedef struct CharPipeState {
+    CharPipeHalf  a[1];
+    CharPipeHalf  b[1];
+} CharPipeState;
+
+
+
+#define   MAX_CHAR_PIPES   8
+
+static CharPipeState  _s_charpipes[ MAX_CHAR_PIPES ];
+
+int
+qemu_chr_open_charpipe( CharDriverState*  *pfirst, CharDriverState*  *psecond )
+{
+    CharPipeState*  cp     = _s_charpipes;
+    CharPipeState*  cp_end = cp + MAX_CHAR_PIPES;
+
+    for ( ; cp < cp_end; cp++ ) {
+        if ( cp->a->peer == NULL && cp->b->peer == NULL )
+            break;
+    }
+
+    if (cp == cp_end) {  /* can't allocate one */
+        *pfirst  = NULL;
+        *psecond = NULL;
+        return -1;
+    }
+
+    charpipehalf_init( cp->a, cp->b );
+    charpipehalf_init( cp->b, cp->a );
+
+    *pfirst  = cp->a->cs;
+    *psecond = cp->b->cs;
+    return 0;
+}
+
+void
+charpipe_poll( void )
+{
+    CharPipeState*  cp     = _s_charpipes;
+    CharPipeState*  cp_end = cp + MAX_CHAR_PIPES;
+
+    for ( ; cp < cp_end; cp++ ) {
+        CharPipeHalf*  half;
+
+        half = cp->a;
+        if (half->peer != NULL)
+            charpipehalf_poll(half);
+
+        half = cp->b;
+        if (half->peer != NULL)
+            charpipehalf_poll(half);
+    }
+}
diff --git a/charpipe.h b/charpipe.h
new file mode 100644
index 0000000..88dffde
--- /dev/null
+++ b/charpipe.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _CHARPIPE_H
+#define _CHARPIPE_H
+
+#include "qemu-common.h"
+
+/* open two connected character drivers that can be used to communicate by internal
+ * QEMU components. For Android, this is used to connect an emulated serial port
+ * with the android modem
+ */
+extern int  qemu_chr_open_charpipe( CharDriverState* *pfirst, CharDriverState* *psecond );
+
+/* must be called from the main event loop to poll all charpipes */
+extern void charpipe_poll( void );
+
+#endif /* _CHARPIPE_H */
diff --git a/compatfd.c b/compatfd.c
new file mode 100644
index 0000000..46b0ae7
--- /dev/null
+++ b/compatfd.c
@@ -0,0 +1,128 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu-common.h"
+#include "compatfd.h"
+
+#include <sys/syscall.h>
+#include <pthread.h>
+
+struct sigfd_compat_info
+{
+    sigset_t mask;
+    int fd;
+};
+
+static void *sigwait_compat(void *opaque)
+{
+    struct sigfd_compat_info *info = opaque;
+    int err;
+    sigset_t all;
+
+    sigfillset(&all);
+    sigprocmask(SIG_BLOCK, &all, NULL);
+
+    do {
+	siginfo_t siginfo;
+
+	err = sigwaitinfo(&info->mask, &siginfo);
+	if (err == -1 && errno == EINTR) {
+            err = 0;
+            continue;
+        }
+
+	if (err > 0) {
+	    char buffer[128];
+	    size_t offset = 0;
+
+	    memcpy(buffer, &err, sizeof(err));
+	    while (offset < sizeof(buffer)) {
+		ssize_t len;
+
+		len = write(info->fd, buffer + offset,
+			    sizeof(buffer) - offset);
+		if (len == -1 && errno == EINTR)
+		    continue;
+
+		if (len <= 0) {
+		    err = -1;
+		    break;
+		}
+
+		offset += len;
+	    }
+	}
+    } while (err >= 0);
+
+    return NULL;
+}
+
+static int qemu_signalfd_compat(const sigset_t *mask)
+{
+    pthread_attr_t attr;
+    pthread_t tid;
+    struct sigfd_compat_info *info;
+    int fds[2];
+
+    info = malloc(sizeof(*info));
+    if (info == NULL) {
+	errno = ENOMEM;
+	return -1;
+    }
+
+    if (pipe(fds) == -1) {
+	free(info);
+	return -1;
+    }
+
+    memcpy(&info->mask, mask, sizeof(*mask));
+    info->fd = fds[1];
+
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&tid, &attr, sigwait_compat, info);
+
+    pthread_attr_destroy(&attr);
+
+    return fds[0];
+}
+
+int qemu_signalfd(const sigset_t *mask)
+{
+#if defined(SYS_signalfd)
+    int ret;
+
+    ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
+    if (!(ret == -1 && errno == ENOSYS))
+	return ret;
+#endif
+
+    return qemu_signalfd_compat(mask);
+}
+
+int qemu_eventfd(int *fds)
+{
+#if defined(SYS_eventfd)
+    int ret;
+
+    ret = syscall(SYS_eventfd, 0);
+    if (ret >= 0) {
+	fds[0] = fds[1] = ret;
+	return 0;
+    } else if (!(ret == -1 && errno == ENOSYS))
+	return ret;
+#endif
+
+    return pipe(fds);
+}
diff --git a/compatfd.h b/compatfd.h
new file mode 100644
index 0000000..55a111a
--- /dev/null
+++ b/compatfd.h
@@ -0,0 +1,28 @@
+/*
+ * signalfd/eventfd compatibility
+ *
+ * Copyright IBM, Corp. 2008
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
+#ifndef QEMU_COMPATFD_H
+#define QEMU_COMPATFD_H
+
+#include <signal.h>
+
+struct qemu_signalfd_siginfo {
+    uint32_t ssi_signo;
+    uint8_t pad[124];
+};
+
+int qemu_signalfd(const sigset_t *mask);
+
+int qemu_eventfd(int *fds);
+
+#endif
diff --git a/console.c b/console.c
new file mode 100644
index 0000000..785710a
--- /dev/null
+++ b/console.c
@@ -0,0 +1,1345 @@
+/*
+ * QEMU graphical console
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "console.h"
+#include "qemu-timer.h"
+
+//#define DEBUG_CONSOLE
+#define DEFAULT_BACKSCROLL 512
+#define MAX_CONSOLES 12
+#define DEFAULT_MONITOR_SIZE "800x600"
+
+#define QEMU_RGBA(r, g, b, a) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+#define QEMU_RGB(r, g, b) QEMU_RGBA(r, g, b, 0xff)
+
+typedef struct TextAttributes {
+    uint8_t fgcol:4;
+    uint8_t bgcol:4;
+    uint8_t bold:1;
+    uint8_t uline:1;
+    uint8_t blink:1;
+    uint8_t invers:1;
+    uint8_t unvisible:1;
+} TextAttributes;
+
+typedef struct TextCell {
+    uint8_t ch;
+    TextAttributes t_attrib;
+} TextCell;
+
+#define MAX_ESC_PARAMS 3
+
+enum TTYState {
+    TTY_STATE_NORM,
+    TTY_STATE_ESC,
+    TTY_STATE_CSI,
+};
+
+typedef struct QEMUFIFO {
+    uint8_t *buf;
+    int buf_size;
+    int count, wptr, rptr;
+} QEMUFIFO;
+
+static int qemu_fifo_write(QEMUFIFO *f, const uint8_t *buf, int len1)
+{
+    int l, len;
+
+    l = f->buf_size - f->count;
+    if (len1 > l)
+        len1 = l;
+    len = len1;
+    while (len > 0) {
+        l = f->buf_size - f->wptr;
+        if (l > len)
+            l = len;
+        memcpy(f->buf + f->wptr, buf, l);
+        f->wptr += l;
+        if (f->wptr >= f->buf_size)
+            f->wptr = 0;
+        buf += l;
+        len -= l;
+    }
+    f->count += len1;
+    return len1;
+}
+
+static int qemu_fifo_read(QEMUFIFO *f, uint8_t *buf, int len1)
+{
+    int l, len;
+
+    if (len1 > f->count)
+        len1 = f->count;
+    len = len1;
+    while (len > 0) {
+        l = f->buf_size - f->rptr;
+        if (l > len)
+            l = len;
+        memcpy(buf, f->buf + f->rptr, l);
+        f->rptr += l;
+        if (f->rptr >= f->buf_size)
+            f->rptr = 0;
+        buf += l;
+        len -= l;
+    }
+    f->count -= len1;
+    return len1;
+}
+
+typedef enum {
+    GRAPHIC_CONSOLE,
+    TEXT_CONSOLE
+} console_type_t;
+
+/* ??? This is mis-named.
+   It is used for both text and graphical consoles.  */
+struct TextConsole {
+    console_type_t console_type;
+    DisplayState *ds;
+    /* Graphic console state.  */
+    vga_hw_update_ptr hw_update;
+    vga_hw_invalidate_ptr hw_invalidate;
+    vga_hw_screen_dump_ptr hw_screen_dump;
+    vga_hw_text_update_ptr hw_text_update;
+    void *hw;
+
+    int g_width, g_height;
+    int width;
+    int height;
+    int total_height;
+    int backscroll_height;
+    int x, y;
+    int x_saved, y_saved;
+    int y_displayed;
+    int y_base;
+    TextAttributes t_attrib_default; /* default text attributes */
+    TextAttributes t_attrib; /* currently active text attributes */
+    TextCell *cells;
+    int text_x[2], text_y[2], cursor_invalidate;
+
+    enum TTYState state;
+    int esc_params[MAX_ESC_PARAMS];
+    int nb_esc_params;
+
+    CharDriverState *chr;
+    /* fifo for key pressed */
+    QEMUFIFO out_fifo;
+    uint8_t out_fifo_buf[16];
+    QEMUTimer *kbd_timer;
+};
+
+static TextConsole *active_console;
+static TextConsole *consoles[MAX_CONSOLES];
+static int nb_consoles = 0;
+
+void vga_hw_update(void)
+{
+    if (active_console && active_console->hw_update)
+        active_console->hw_update(active_console->hw);
+}
+
+void vga_hw_invalidate(void)
+{
+    if (active_console->hw_invalidate)
+        active_console->hw_invalidate(active_console->hw);
+}
+
+void vga_hw_screen_dump(const char *filename)
+{
+    TextConsole *previous_active_console;
+
+    previous_active_console = active_console;
+    active_console = consoles[0];
+    /* There is currently no way of specifying which screen we want to dump,
+       so always dump the first one.  */
+    if (consoles[0]->hw_screen_dump)
+        consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
+    active_console = previous_active_console;
+}
+
+void vga_hw_text_update(console_ch_t *chardata)
+{
+    if (active_console && active_console->hw_text_update)
+        active_console->hw_text_update(active_console->hw, chardata);
+}
+
+/* convert a RGBA color to a color index usable in graphic primitives */
+static unsigned int vga_get_color(DisplayState *ds, unsigned int rgba)
+{
+    unsigned int r, g, b, color;
+
+    switch(ds->depth) {
+#if 0
+    case 8:
+        r = (rgba >> 16) & 0xff;
+        g = (rgba >> 8) & 0xff;
+        b = (rgba) & 0xff;
+        color = (rgb_to_index[r] * 6 * 6) +
+            (rgb_to_index[g] * 6) +
+            (rgb_to_index[b]);
+        break;
+#endif
+    case 15:
+        r = (rgba >> 16) & 0xff;
+        g = (rgba >> 8) & 0xff;
+        b = (rgba) & 0xff;
+        color = ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
+        break;
+    case 16:
+        r = (rgba >> 16) & 0xff;
+        g = (rgba >> 8) & 0xff;
+        b = (rgba) & 0xff;
+        color = ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
+        break;
+    case 32:
+    default:
+        color = rgba;
+        break;
+    }
+    return color;
+}
+
+static void vga_fill_rect (DisplayState *ds,
+                           int posx, int posy, int width, int height, uint32_t color)
+{
+    uint8_t *d, *d1;
+    int x, y, bpp;
+
+    bpp = (ds->depth + 7) >> 3;
+    d1 = ds->data +
+        ds->linesize * posy + bpp * posx;
+    for (y = 0; y < height; y++) {
+        d = d1;
+        switch(bpp) {
+        case 1:
+            for (x = 0; x < width; x++) {
+                *((uint8_t *)d) = color;
+                d++;
+            }
+            break;
+        case 2:
+            for (x = 0; x < width; x++) {
+                *((uint16_t *)d) = color;
+                d += 2;
+            }
+            break;
+        case 4:
+            for (x = 0; x < width; x++) {
+                *((uint32_t *)d) = color;
+                d += 4;
+            }
+            break;
+        }
+        d1 += ds->linesize;
+    }
+}
+
+/* copy from (xs, ys) to (xd, yd) a rectangle of size (w, h) */
+static void vga_bitblt(DisplayState *ds, int xs, int ys, int xd, int yd, int w, int h)
+{
+    const uint8_t *s;
+    uint8_t *d;
+    int wb, y, bpp;
+
+    bpp = (ds->depth + 7) >> 3;
+    wb = w * bpp;
+    if (yd <= ys) {
+        s = ds->data +
+            ds->linesize * ys + bpp * xs;
+        d = ds->data +
+            ds->linesize * yd + bpp * xd;
+        for (y = 0; y < h; y++) {
+            memmove(d, s, wb);
+            d += ds->linesize;
+            s += ds->linesize;
+        }
+    } else {
+        s = ds->data +
+            ds->linesize * (ys + h - 1) + bpp * xs;
+        d = ds->data +
+            ds->linesize * (yd + h - 1) + bpp * xd;
+       for (y = 0; y < h; y++) {
+            memmove(d, s, wb);
+            d -= ds->linesize;
+            s -= ds->linesize;
+        }
+    }
+}
+
+/***********************************************************/
+/* basic char display */
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+#include "vgafont.h"
+
+#define cbswap_32(__x) \
+((uint32_t)( \
+		(((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \
+		(((uint32_t)(__x) & (uint32_t)0x0000ff00UL) <<  8) | \
+		(((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >>  8) | \
+		(((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) ))
+
+#ifdef WORDS_BIGENDIAN
+#define PAT(x) x
+#else
+#define PAT(x) cbswap_32(x)
+#endif
+
+static const uint32_t dmask16[16] = {
+    PAT(0x00000000),
+    PAT(0x000000ff),
+    PAT(0x0000ff00),
+    PAT(0x0000ffff),
+    PAT(0x00ff0000),
+    PAT(0x00ff00ff),
+    PAT(0x00ffff00),
+    PAT(0x00ffffff),
+    PAT(0xff000000),
+    PAT(0xff0000ff),
+    PAT(0xff00ff00),
+    PAT(0xff00ffff),
+    PAT(0xffff0000),
+    PAT(0xffff00ff),
+    PAT(0xffffff00),
+    PAT(0xffffffff),
+};
+
+static const uint32_t dmask4[4] = {
+    PAT(0x00000000),
+    PAT(0x0000ffff),
+    PAT(0xffff0000),
+    PAT(0xffffffff),
+};
+
+static uint32_t color_table[2][8];
+
+enum color_names {
+    COLOR_BLACK   = 0,
+    COLOR_RED     = 1,
+    COLOR_GREEN   = 2,
+    COLOR_YELLOW  = 3,
+    COLOR_BLUE    = 4,
+    COLOR_MAGENTA = 5,
+    COLOR_CYAN    = 6,
+    COLOR_WHITE   = 7
+};
+
+static const uint32_t color_table_rgb[2][8] = {
+    {   /* dark */
+        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
+        QEMU_RGB(0xaa, 0x00, 0x00),  /* red */
+        QEMU_RGB(0x00, 0xaa, 0x00),  /* green */
+        QEMU_RGB(0xaa, 0xaa, 0x00),  /* yellow */
+        QEMU_RGB(0x00, 0x00, 0xaa),  /* blue */
+        QEMU_RGB(0xaa, 0x00, 0xaa),  /* magenta */
+        QEMU_RGB(0x00, 0xaa, 0xaa),  /* cyan */
+        QEMU_RGB(0xaa, 0xaa, 0xaa),  /* white */
+    },
+    {   /* bright */
+        QEMU_RGB(0x00, 0x00, 0x00),  /* black */
+        QEMU_RGB(0xff, 0x00, 0x00),  /* red */
+        QEMU_RGB(0x00, 0xff, 0x00),  /* green */
+        QEMU_RGB(0xff, 0xff, 0x00),  /* yellow */
+        QEMU_RGB(0x00, 0x00, 0xff),  /* blue */
+        QEMU_RGB(0xff, 0x00, 0xff),  /* magenta */
+        QEMU_RGB(0x00, 0xff, 0xff),  /* cyan */
+        QEMU_RGB(0xff, 0xff, 0xff),  /* white */
+    }
+};
+
+static inline unsigned int col_expand(DisplayState *ds, unsigned int col)
+{
+    switch(ds->depth) {
+    case 8:
+        col |= col << 8;
+        col |= col << 16;
+        break;
+    case 15:
+    case 16:
+        col |= col << 16;
+        break;
+    default:
+        break;
+    }
+
+    return col;
+}
+#ifdef DEBUG_CONSOLE
+static void console_print_text_attributes(TextAttributes *t_attrib, char ch)
+{
+    if (t_attrib->bold) {
+        printf("b");
+    } else {
+        printf(" ");
+    }
+    if (t_attrib->uline) {
+        printf("u");
+    } else {
+        printf(" ");
+    }
+    if (t_attrib->blink) {
+        printf("l");
+    } else {
+        printf(" ");
+    }
+    if (t_attrib->invers) {
+        printf("i");
+    } else {
+        printf(" ");
+    }
+    if (t_attrib->unvisible) {
+        printf("n");
+    } else {
+        printf(" ");
+    }
+
+    printf(" fg: %d bg: %d ch:'%2X' '%c'\n", t_attrib->fgcol, t_attrib->bgcol, ch, ch);
+}
+#endif
+
+static void vga_putcharxy(DisplayState *ds, int x, int y, int ch,
+                          TextAttributes *t_attrib)
+{
+    uint8_t *d;
+    const uint8_t *font_ptr;
+    unsigned int font_data, linesize, xorcol, bpp;
+    int i;
+    unsigned int fgcol, bgcol;
+
+#ifdef DEBUG_CONSOLE
+    printf("x: %2i y: %2i", x, y);
+    console_print_text_attributes(t_attrib, ch);
+#endif
+
+    if (t_attrib->invers) {
+        bgcol = color_table[t_attrib->bold][t_attrib->fgcol];
+        fgcol = color_table[t_attrib->bold][t_attrib->bgcol];
+    } else {
+        fgcol = color_table[t_attrib->bold][t_attrib->fgcol];
+        bgcol = color_table[t_attrib->bold][t_attrib->bgcol];
+    }
+
+    bpp = (ds->depth + 7) >> 3;
+    d = ds->data +
+        ds->linesize * y * FONT_HEIGHT + bpp * x * FONT_WIDTH;
+    linesize = ds->linesize;
+    font_ptr = vgafont16 + FONT_HEIGHT * ch;
+    xorcol = bgcol ^ fgcol;
+    switch(ds->depth) {
+    case 8:
+        for(i = 0; i < FONT_HEIGHT; i++) {
+            font_data = *font_ptr++;
+            if (t_attrib->uline
+                && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+                font_data = 0xFFFF;
+            }
+            ((uint32_t *)d)[0] = (dmask16[(font_data >> 4)] & xorcol) ^ bgcol;
+            ((uint32_t *)d)[1] = (dmask16[(font_data >> 0) & 0xf] & xorcol) ^ bgcol;
+            d += linesize;
+        }
+        break;
+    case 16:
+    case 15:
+        for(i = 0; i < FONT_HEIGHT; i++) {
+            font_data = *font_ptr++;
+            if (t_attrib->uline
+                && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+                font_data = 0xFFFF;
+            }
+            ((uint32_t *)d)[0] = (dmask4[(font_data >> 6)] & xorcol) ^ bgcol;
+            ((uint32_t *)d)[1] = (dmask4[(font_data >> 4) & 3] & xorcol) ^ bgcol;
+            ((uint32_t *)d)[2] = (dmask4[(font_data >> 2) & 3] & xorcol) ^ bgcol;
+            ((uint32_t *)d)[3] = (dmask4[(font_data >> 0) & 3] & xorcol) ^ bgcol;
+            d += linesize;
+        }
+        break;
+    case 32:
+        for(i = 0; i < FONT_HEIGHT; i++) {
+            font_data = *font_ptr++;
+            if (t_attrib->uline && ((i == FONT_HEIGHT - 2) || (i == FONT_HEIGHT - 3))) {
+                font_data = 0xFFFF;
+            }
+            ((uint32_t *)d)[0] = (-((font_data >> 7)) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[1] = (-((font_data >> 6) & 1) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[2] = (-((font_data >> 5) & 1) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[3] = (-((font_data >> 4) & 1) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[4] = (-((font_data >> 3) & 1) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[5] = (-((font_data >> 2) & 1) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[6] = (-((font_data >> 1) & 1) & xorcol) ^ bgcol;
+            ((uint32_t *)d)[7] = (-((font_data >> 0) & 1) & xorcol) ^ bgcol;
+            d += linesize;
+        }
+        break;
+    }
+}
+
+static void text_console_resize(TextConsole *s)
+{
+    TextCell *cells, *c, *c1;
+    int w1, x, y, last_width;
+
+    last_width = s->width;
+    s->width = s->g_width / FONT_WIDTH;
+    s->height = s->g_height / FONT_HEIGHT;
+
+    w1 = last_width;
+    if (s->width < w1)
+        w1 = s->width;
+
+    cells = qemu_malloc(s->width * s->total_height * sizeof(TextCell));
+    for(y = 0; y < s->total_height; y++) {
+        c = &cells[y * s->width];
+        if (w1 > 0) {
+            c1 = &s->cells[y * last_width];
+            for(x = 0; x < w1; x++) {
+                *c++ = *c1++;
+            }
+        }
+        for(x = w1; x < s->width; x++) {
+            c->ch = ' ';
+            c->t_attrib = s->t_attrib_default;
+            c++;
+        }
+    }
+    qemu_free(s->cells);
+    s->cells = cells;
+}
+
+static inline void text_update_xy(TextConsole *s, int x, int y)
+{
+    s->text_x[0] = MIN(s->text_x[0], x);
+    s->text_x[1] = MAX(s->text_x[1], x);
+    s->text_y[0] = MIN(s->text_y[0], y);
+    s->text_y[1] = MAX(s->text_y[1], y);
+}
+
+static void update_xy(TextConsole *s, int x, int y)
+{
+    TextCell *c;
+    int y1, y2;
+
+    if (s == active_console) {
+        if (!s->ds->depth) {
+            text_update_xy(s, x, y);
+            return;
+        }
+
+        y1 = (s->y_base + y) % s->total_height;
+        y2 = y1 - s->y_displayed;
+        if (y2 < 0)
+            y2 += s->total_height;
+        if (y2 < s->height) {
+            c = &s->cells[y1 * s->width + x];
+            vga_putcharxy(s->ds, x, y2, c->ch,
+                          &(c->t_attrib));
+            dpy_update(s->ds, x * FONT_WIDTH, y2 * FONT_HEIGHT,
+                       FONT_WIDTH, FONT_HEIGHT);
+        }
+    }
+}
+
+static void console_show_cursor(TextConsole *s, int show)
+{
+    TextCell *c;
+    int y, y1;
+
+    if (s == active_console) {
+        int x = s->x;
+
+        if (!s->ds->depth) {
+            s->cursor_invalidate = 1;
+            return;
+        }
+
+        if (x >= s->width) {
+            x = s->width - 1;
+        }
+        y1 = (s->y_base + s->y) % s->total_height;
+        y = y1 - s->y_displayed;
+        if (y < 0)
+            y += s->total_height;
+        if (y < s->height) {
+            c = &s->cells[y1 * s->width + x];
+            if (show) {
+                TextAttributes t_attrib = s->t_attrib_default;
+                t_attrib.invers = !(t_attrib.invers); /* invert fg and bg */
+                vga_putcharxy(s->ds, x, y, c->ch, &t_attrib);
+            } else {
+                vga_putcharxy(s->ds, x, y, c->ch, &(c->t_attrib));
+            }
+            dpy_update(s->ds, x * FONT_WIDTH, y * FONT_HEIGHT,
+                       FONT_WIDTH, FONT_HEIGHT);
+        }
+    }
+}
+
+static void console_refresh(TextConsole *s)
+{
+    TextCell *c;
+    int x, y, y1;
+
+    if (s != active_console)
+        return;
+    if (!s->ds->depth) {
+        s->text_x[0] = 0;
+        s->text_y[0] = 0;
+        s->text_x[1] = s->width - 1;
+        s->text_y[1] = s->height - 1;
+        s->cursor_invalidate = 1;
+        return;
+    }
+
+    vga_fill_rect(s->ds, 0, 0, s->ds->width, s->ds->height,
+                  color_table[0][COLOR_BLACK]);
+    y1 = s->y_displayed;
+    for(y = 0; y < s->height; y++) {
+        c = s->cells + y1 * s->width;
+        for(x = 0; x < s->width; x++) {
+            vga_putcharxy(s->ds, x, y, c->ch,
+                          &(c->t_attrib));
+            c++;
+        }
+        if (++y1 == s->total_height)
+            y1 = 0;
+    }
+    dpy_update(s->ds, 0, 0, s->ds->width, s->ds->height);
+    console_show_cursor(s, 1);
+}
+
+static void console_scroll(int ydelta)
+{
+    TextConsole *s;
+    int i, y1;
+
+    s = active_console;
+    if (!s || (s->console_type == GRAPHIC_CONSOLE))
+        return;
+
+    if (ydelta > 0) {
+        for(i = 0; i < ydelta; i++) {
+            if (s->y_displayed == s->y_base)
+                break;
+            if (++s->y_displayed == s->total_height)
+                s->y_displayed = 0;
+        }
+    } else {
+        ydelta = -ydelta;
+        i = s->backscroll_height;
+        if (i > s->total_height - s->height)
+            i = s->total_height - s->height;
+        y1 = s->y_base - i;
+        if (y1 < 0)
+            y1 += s->total_height;
+        for(i = 0; i < ydelta; i++) {
+            if (s->y_displayed == y1)
+                break;
+            if (--s->y_displayed < 0)
+                s->y_displayed = s->total_height - 1;
+        }
+    }
+    console_refresh(s);
+}
+
+static void console_put_lf(TextConsole *s)
+{
+    TextCell *c;
+    int x, y1;
+
+    s->y++;
+    if (s->y >= s->height) {
+        s->y = s->height - 1;
+
+        if (s->y_displayed == s->y_base) {
+            if (++s->y_displayed == s->total_height)
+                s->y_displayed = 0;
+        }
+        if (++s->y_base == s->total_height)
+            s->y_base = 0;
+        if (s->backscroll_height < s->total_height)
+            s->backscroll_height++;
+        y1 = (s->y_base + s->height - 1) % s->total_height;
+        c = &s->cells[y1 * s->width];
+        for(x = 0; x < s->width; x++) {
+            c->ch = ' ';
+            c->t_attrib = s->t_attrib_default;
+            c++;
+        }
+        if (s == active_console && s->y_displayed == s->y_base) {
+            if (!s->ds->depth) {
+                s->text_x[0] = 0;
+                s->text_y[0] = 0;
+                s->text_x[1] = s->width - 1;
+                s->text_y[1] = s->height - 1;
+                return;
+            }
+
+            vga_bitblt(s->ds, 0, FONT_HEIGHT, 0, 0,
+                       s->width * FONT_WIDTH,
+                       (s->height - 1) * FONT_HEIGHT);
+            vga_fill_rect(s->ds, 0, (s->height - 1) * FONT_HEIGHT,
+                          s->width * FONT_WIDTH, FONT_HEIGHT,
+                          color_table[0][s->t_attrib_default.bgcol]);
+            dpy_update(s->ds, 0, 0,
+                       s->width * FONT_WIDTH, s->height * FONT_HEIGHT);
+        }
+    }
+}
+
+/* Set console attributes depending on the current escape codes.
+ * NOTE: I know this code is not very efficient (checking every color for it
+ * self) but it is more readable and better maintainable.
+ */
+static void console_handle_escape(TextConsole *s)
+{
+    int i;
+
+    for (i=0; i<s->nb_esc_params; i++) {
+        switch (s->esc_params[i]) {
+            case 0: /* reset all console attributes to default */
+                s->t_attrib = s->t_attrib_default;
+                break;
+            case 1:
+                s->t_attrib.bold = 1;
+                break;
+            case 4:
+                s->t_attrib.uline = 1;
+                break;
+            case 5:
+                s->t_attrib.blink = 1;
+                break;
+            case 7:
+                s->t_attrib.invers = 1;
+                break;
+            case 8:
+                s->t_attrib.unvisible = 1;
+                break;
+            case 22:
+                s->t_attrib.bold = 0;
+                break;
+            case 24:
+                s->t_attrib.uline = 0;
+                break;
+            case 25:
+                s->t_attrib.blink = 0;
+                break;
+            case 27:
+                s->t_attrib.invers = 0;
+                break;
+            case 28:
+                s->t_attrib.unvisible = 0;
+                break;
+            /* set foreground color */
+            case 30:
+                s->t_attrib.fgcol=COLOR_BLACK;
+                break;
+            case 31:
+                s->t_attrib.fgcol=COLOR_RED;
+                break;
+            case 32:
+                s->t_attrib.fgcol=COLOR_GREEN;
+                break;
+            case 33:
+                s->t_attrib.fgcol=COLOR_YELLOW;
+                break;
+            case 34:
+                s->t_attrib.fgcol=COLOR_BLUE;
+                break;
+            case 35:
+                s->t_attrib.fgcol=COLOR_MAGENTA;
+                break;
+            case 36:
+                s->t_attrib.fgcol=COLOR_CYAN;
+                break;
+            case 37:
+                s->t_attrib.fgcol=COLOR_WHITE;
+                break;
+            /* set background color */
+            case 40:
+                s->t_attrib.bgcol=COLOR_BLACK;
+                break;
+            case 41:
+                s->t_attrib.bgcol=COLOR_RED;
+                break;
+            case 42:
+                s->t_attrib.bgcol=COLOR_GREEN;
+                break;
+            case 43:
+                s->t_attrib.bgcol=COLOR_YELLOW;
+                break;
+            case 44:
+                s->t_attrib.bgcol=COLOR_BLUE;
+                break;
+            case 45:
+                s->t_attrib.bgcol=COLOR_MAGENTA;
+                break;
+            case 46:
+                s->t_attrib.bgcol=COLOR_CYAN;
+                break;
+            case 47:
+                s->t_attrib.bgcol=COLOR_WHITE;
+                break;
+        }
+    }
+}
+
+static void console_clear_xy(TextConsole *s, int x, int y)
+{
+    int y1 = (s->y_base + y) % s->total_height;
+    TextCell *c = &s->cells[y1 * s->width + x];
+    c->ch = ' ';
+    c->t_attrib = s->t_attrib_default;
+    c++;
+    update_xy(s, x, y);
+}
+
+static void console_putchar(TextConsole *s, int ch)
+{
+    TextCell *c;
+    int y1, i;
+    int x, y;
+
+    switch(s->state) {
+    case TTY_STATE_NORM:
+        switch(ch) {
+        case '\r':  /* carriage return */
+            s->x = 0;
+            break;
+        case '\n':  /* newline */
+            console_put_lf(s);
+            break;
+        case '\b':  /* backspace */
+            if (s->x > 0)
+                s->x--;
+            break;
+        case '\t':  /* tabspace */
+            if (s->x + (8 - (s->x % 8)) > s->width) {
+                s->x = 0;
+                console_put_lf(s);
+            } else {
+                s->x = s->x + (8 - (s->x % 8));
+            }
+            break;
+        case '\a':  /* alert aka. bell */
+            /* TODO: has to be implemented */
+            break;
+        case 14:
+            /* SI (shift in), character set 0 (ignored) */
+            break;
+        case 15:
+            /* SO (shift out), character set 1 (ignored) */
+            break;
+        case 27:    /* esc (introducing an escape sequence) */
+            s->state = TTY_STATE_ESC;
+            break;
+        default:
+            if (s->x >= s->width) {
+                /* line wrap */
+                s->x = 0;
+                console_put_lf(s);
+            }
+            y1 = (s->y_base + s->y) % s->total_height;
+            c = &s->cells[y1 * s->width + s->x];
+            c->ch = ch;
+            c->t_attrib = s->t_attrib;
+            update_xy(s, s->x, s->y);
+            s->x++;
+            break;
+        }
+        break;
+    case TTY_STATE_ESC: /* check if it is a terminal escape sequence */
+        if (ch == '[') {
+            for(i=0;i<MAX_ESC_PARAMS;i++)
+                s->esc_params[i] = 0;
+            s->nb_esc_params = 0;
+            s->state = TTY_STATE_CSI;
+        } else {
+            s->state = TTY_STATE_NORM;
+        }
+        break;
+    case TTY_STATE_CSI: /* handle escape sequence parameters */
+        if (ch >= '0' && ch <= '9') {
+            if (s->nb_esc_params < MAX_ESC_PARAMS) {
+                s->esc_params[s->nb_esc_params] =
+                    s->esc_params[s->nb_esc_params] * 10 + ch - '0';
+            }
+        } else {
+            s->nb_esc_params++;
+            if (ch == ';')
+                break;
+#ifdef DEBUG_CONSOLE
+            fprintf(stderr, "escape sequence CSI%d;%d%c, %d parameters\n",
+                    s->esc_params[0], s->esc_params[1], ch, s->nb_esc_params);
+#endif
+            s->state = TTY_STATE_NORM;
+            switch(ch) {
+            case 'A':
+                /* move cursor up */
+                if (s->esc_params[0] == 0) {
+                    s->esc_params[0] = 1;
+                }
+                s->y -= s->esc_params[0];
+                if (s->y < 0) {
+                    s->y = 0;
+                }
+                break;
+            case 'B':
+                /* move cursor down */
+                if (s->esc_params[0] == 0) {
+                    s->esc_params[0] = 1;
+                }
+                s->y += s->esc_params[0];
+                if (s->y >= s->height) {
+                    s->y = s->height - 1;
+                }
+                break;
+            case 'C':
+                /* move cursor right */
+                if (s->esc_params[0] == 0) {
+                    s->esc_params[0] = 1;
+                }
+                s->x += s->esc_params[0];
+                if (s->x >= s->width) {
+                    s->x = s->width - 1;
+                }
+                break;
+            case 'D':
+                /* move cursor left */
+                if (s->esc_params[0] == 0) {
+                    s->esc_params[0] = 1;
+                }
+                s->x -= s->esc_params[0];
+                if (s->x < 0) {
+                    s->x = 0;
+                }
+                break;
+            case 'G':
+                /* move cursor to column */
+                s->x = s->esc_params[0] - 1;
+                if (s->x < 0) {
+                    s->x = 0;
+                }
+                break;
+            case 'f':
+            case 'H':
+                /* move cursor to row, column */
+                s->x = s->esc_params[1] - 1;
+                if (s->x < 0) {
+                    s->x = 0;
+                }
+                s->y = s->esc_params[0] - 1;
+                if (s->y < 0) {
+                    s->y = 0;
+                }
+                break;
+            case 'J':
+                switch (s->esc_params[0]) {
+                case 0:
+                    /* clear to end of screen */
+                    for (y = s->y; y < s->height; y++) {
+                        for (x = 0; x < s->width; x++) {
+                            if (y == s->y && x < s->x) {
+                                continue;
+                            }
+                            console_clear_xy(s, x, y);
+                        }
+                    }
+                    break;
+                case 1:
+                    /* clear from beginning of screen */
+                    for (y = 0; y <= s->y; y++) {
+                        for (x = 0; x < s->width; x++) {
+                            if (y == s->y && x > s->x) {
+                                break;
+                            }
+                            console_clear_xy(s, x, y);
+                        }
+                    }
+                    break;
+                case 2:
+                    /* clear entire screen */
+                    for (y = 0; y <= s->height; y++) {
+                        for (x = 0; x < s->width; x++) {
+                            console_clear_xy(s, x, y);
+                        }
+                    }
+                break;
+                }
+            case 'K':
+                switch (s->esc_params[0]) {
+                case 0:
+                /* clear to eol */
+                for(x = s->x; x < s->width; x++) {
+                        console_clear_xy(s, x, s->y);
+                }
+                break;
+                case 1:
+                    /* clear from beginning of line */
+                    for (x = 0; x <= s->x; x++) {
+                        console_clear_xy(s, x, s->y);
+                    }
+                    break;
+                case 2:
+                    /* clear entire line */
+                    for(x = 0; x < s->width; x++) {
+                        console_clear_xy(s, x, s->y);
+                    }
+                break;
+            }
+                break;
+            case 'm':
+            console_handle_escape(s);
+            break;
+            case 'n':
+                /* report cursor position */
+                /* TODO: send ESC[row;colR */
+                break;
+            case 's':
+                /* save cursor position */
+                s->x_saved = s->x;
+                s->y_saved = s->y;
+                break;
+            case 'u':
+                /* restore cursor position */
+                s->x = s->x_saved;
+                s->y = s->y_saved;
+                break;
+            default:
+#ifdef DEBUG_CONSOLE
+                fprintf(stderr, "unhandled escape character '%c'\n", ch);
+#endif
+                break;
+            }
+            break;
+        }
+    }
+}
+
+void console_select(unsigned int index)
+{
+    TextConsole *s;
+
+    if (index >= MAX_CONSOLES)
+        return;
+    s = consoles[index];
+    if (s) {
+        active_console = s;
+        if (s->g_width && s->g_height
+            && (s->g_width != s->ds->width || s->g_height != s->ds->height))
+            dpy_resize(s->ds, s->g_width, s->g_height);
+        vga_hw_invalidate();
+    }
+}
+
+static int console_puts(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    TextConsole *s = chr->opaque;
+    int i;
+
+    console_show_cursor(s, 0);
+    for(i = 0; i < len; i++) {
+        console_putchar(s, buf[i]);
+    }
+    console_show_cursor(s, 1);
+    return len;
+}
+
+static void console_send_event(CharDriverState *chr, int event)
+{
+    TextConsole *s = chr->opaque;
+    int i;
+
+    if (event == CHR_EVENT_FOCUS) {
+        for(i = 0; i < nb_consoles; i++) {
+            if (consoles[i] == s) {
+                console_select(i);
+                break;
+            }
+        }
+    }
+}
+
+static void kbd_send_chars(void *opaque)
+{
+    TextConsole *s = opaque;
+    int len;
+    uint8_t buf[16];
+
+    len = qemu_chr_can_read(s->chr);
+    if (len > s->out_fifo.count)
+        len = s->out_fifo.count;
+    if (len > 0) {
+        if (len > sizeof(buf))
+            len = sizeof(buf);
+        qemu_fifo_read(&s->out_fifo, buf, len);
+        qemu_chr_read(s->chr, buf, len);
+    }
+    /* characters are pending: we send them a bit later (XXX:
+       horrible, should change char device API) */
+    if (s->out_fifo.count > 0) {
+        qemu_mod_timer(s->kbd_timer, qemu_get_clock(rt_clock) + 1);
+    }
+}
+
+/* called when an ascii key is pressed */
+void kbd_put_keysym(int keysym)
+{
+    TextConsole *s;
+    uint8_t buf[16], *q;
+    int c;
+
+    s = active_console;
+    if (!s || (s->console_type == GRAPHIC_CONSOLE))
+        return;
+
+    switch(keysym) {
+    case QEMU_KEY_CTRL_UP:
+        console_scroll(-1);
+        break;
+    case QEMU_KEY_CTRL_DOWN:
+        console_scroll(1);
+        break;
+    case QEMU_KEY_CTRL_PAGEUP:
+        console_scroll(-10);
+        break;
+    case QEMU_KEY_CTRL_PAGEDOWN:
+        console_scroll(10);
+        break;
+    default:
+        /* convert the QEMU keysym to VT100 key string */
+        q = buf;
+        if (keysym >= 0xe100 && keysym <= 0xe11f) {
+            *q++ = '\033';
+            *q++ = '[';
+            c = keysym - 0xe100;
+            if (c >= 10)
+                *q++ = '0' + (c / 10);
+            *q++ = '0' + (c % 10);
+            *q++ = '~';
+        } else if (keysym >= 0xe120 && keysym <= 0xe17f) {
+            *q++ = '\033';
+            *q++ = '[';
+            *q++ = keysym & 0xff;
+        } else {
+                *q++ = keysym;
+        }
+        if (s->chr->chr_read) {
+            qemu_fifo_write(&s->out_fifo, buf, q - buf);
+            kbd_send_chars(s);
+        }
+        break;
+    }
+}
+
+static void text_console_invalidate(void *opaque)
+{
+    TextConsole *s = (TextConsole *) opaque;
+
+    console_refresh(s);
+}
+
+static void text_console_update(void *opaque, console_ch_t *chardata)
+{
+    TextConsole *s = (TextConsole *) opaque;
+    int i, j, src;
+
+    if (s->text_x[0] <= s->text_x[1]) {
+        src = (s->y_base + s->text_y[0]) * s->width;
+        chardata += s->text_y[0] * s->width;
+        for (i = s->text_y[0]; i <= s->text_y[1]; i ++)
+            for (j = 0; j < s->width; j ++, src ++)
+                console_write_ch(chardata ++, s->cells[src].ch |
+                                (s->cells[src].t_attrib.fgcol << 12) |
+                                (s->cells[src].t_attrib.bgcol << 8) |
+                                (s->cells[src].t_attrib.bold << 21));
+        dpy_update(s->ds, s->text_x[0], s->text_y[0],
+                   s->text_x[1] - s->text_x[0], i - s->text_y[0]);
+        s->text_x[0] = s->width;
+        s->text_y[0] = s->height;
+        s->text_x[1] = 0;
+        s->text_y[1] = 0;
+    }
+    if (s->cursor_invalidate) {
+        dpy_cursor(s->ds, s->x, s->y);
+        s->cursor_invalidate = 0;
+    }
+}
+
+static TextConsole *new_console(DisplayState *ds, console_type_t console_type)
+{
+    TextConsole *s;
+    int i;
+
+    if (nb_consoles >= MAX_CONSOLES)
+        return NULL;
+    s = qemu_mallocz(sizeof(TextConsole));
+    if (!s) {
+        return NULL;
+    }
+    if (!active_console || ((active_console->console_type != GRAPHIC_CONSOLE) &&
+        (console_type == GRAPHIC_CONSOLE))) {
+        active_console = s;
+    }
+    s->ds = ds;
+    s->console_type = console_type;
+    if (console_type != GRAPHIC_CONSOLE) {
+        consoles[nb_consoles++] = s;
+    } else {
+        /* HACK: Put graphical consoles before text consoles.  */
+        for (i = nb_consoles; i > 0; i--) {
+            if (consoles[i - 1]->console_type == GRAPHIC_CONSOLE)
+                break;
+            consoles[i] = consoles[i - 1];
+        }
+        consoles[i] = s;
+    }
+    return s;
+}
+
+TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
+                                  vga_hw_invalidate_ptr invalidate,
+                                  vga_hw_screen_dump_ptr screen_dump,
+                                  vga_hw_text_update_ptr text_update,
+                                  void *opaque)
+{
+    TextConsole *s;
+
+    s = new_console(ds, GRAPHIC_CONSOLE);
+    if (!s)
+      return NULL;
+    s->hw_update = update;
+    s->hw_invalidate = invalidate;
+    s->hw_screen_dump = screen_dump;
+    s->hw_text_update = text_update;
+    s->hw = opaque;
+    return s;
+}
+
+int is_graphic_console(void)
+{
+    return active_console && active_console->console_type == GRAPHIC_CONSOLE;
+}
+
+void console_color_init(DisplayState *ds)
+{
+    int i, j;
+    for (j = 0; j < 2; j++) {
+        for (i = 0; i < 8; i++) {
+            color_table[j][i] = col_expand(ds, 
+                   vga_get_color(ds, color_table_rgb[j][i]));
+        }
+    }
+}
+
+CharDriverState *text_console_init(DisplayState *ds, const char *p)
+{
+    CharDriverState *chr;
+    TextConsole *s;
+    unsigned width;
+    unsigned height;
+    static int color_inited;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = new_console(ds, TEXT_CONSOLE);
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    if (!p)
+        p = DEFAULT_MONITOR_SIZE;
+
+    chr->opaque = s;
+    chr->chr_write = console_puts;
+    chr->chr_send_event = console_send_event;
+
+    s->chr = chr;
+    s->out_fifo.buf = s->out_fifo_buf;
+    s->out_fifo.buf_size = sizeof(s->out_fifo_buf);
+    s->kbd_timer = qemu_new_timer(rt_clock, kbd_send_chars, s);
+
+    if (!color_inited) {
+        color_inited = 1;
+        console_color_init(s->ds);
+    }
+    s->y_displayed = 0;
+    s->y_base = 0;
+    s->total_height = DEFAULT_BACKSCROLL;
+    s->x = 0;
+    s->y = 0;
+    width = s->ds->width;
+    height = s->ds->height;
+    if (p != 0) {
+        width = strtoul(p, (char **)&p, 10);
+        if (*p == 'C') {
+            p++;
+            width *= FONT_WIDTH;
+        }
+        if (*p == 'x') {
+            p++;
+            height = strtoul(p, (char **)&p, 10);
+            if (*p == 'C') {
+                p++;
+                height *= FONT_HEIGHT;
+            }
+        }
+    }
+    s->g_width = width;
+    s->g_height = height;
+
+    s->hw_invalidate = text_console_invalidate;
+    s->hw_text_update = text_console_update;
+    s->hw = s;
+
+    /* Set text attribute defaults */
+    s->t_attrib_default.bold = 0;
+    s->t_attrib_default.uline = 0;
+    s->t_attrib_default.blink = 0;
+    s->t_attrib_default.invers = 0;
+    s->t_attrib_default.unvisible = 0;
+    s->t_attrib_default.fgcol = COLOR_WHITE;
+    s->t_attrib_default.bgcol = COLOR_BLACK;
+
+    /* set current text attributes to default */
+    s->t_attrib = s->t_attrib_default;
+    text_console_resize(s);
+
+    qemu_chr_reset(chr);
+
+    return chr;
+}
+
+void qemu_console_resize(QEMUConsole *console, int width, int height)
+{
+    if (console->g_width != width || console->g_height != height
+        || !console->ds->data) {
+        console->g_width = width;
+        console->g_height = height;
+        if (active_console == console) {
+            dpy_resize(console->ds, width, height);
+        }
+    }
+}
diff --git a/console.h b/console.h
new file mode 100644
index 0000000..52dc4d8
--- /dev/null
+++ b/console.h
@@ -0,0 +1,195 @@
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include "qemu-char.h"
+
+/* keyboard/mouse support */
+
+#define MOUSE_EVENT_LBUTTON 0x01
+#define MOUSE_EVENT_RBUTTON 0x02
+#define MOUSE_EVENT_MBUTTON 0x04
+
+/* in ms */
+#if 1  /* ANDROID */
+#define GUI_REFRESH_INTERVAL (1000/60)  /* 60 frames/s is better */
+#else
+#define GUI_REFRESH_INTERVAL 30
+#endif
+
+typedef void QEMUPutKBDEvent(void *opaque, int keycode);
+typedef void QEMUPutKBDEventN(void *opaque, int*  keycodes, int  count);
+typedef void QEMUPutMouseEvent(void *opaque, int dx, int dy, int dz, int buttons_state);
+typedef void QEMUPutGenericEvent(void*  opaque, int  type, int  code, int  value);
+
+typedef struct QEMUPutMouseEntry {
+    QEMUPutMouseEvent *qemu_put_mouse_event;
+    void *qemu_put_mouse_event_opaque;
+    int qemu_put_mouse_event_absolute;
+    char *qemu_put_mouse_event_name;
+
+    /* used internally by qemu for handling mice */
+    struct QEMUPutMouseEntry *next;
+} QEMUPutMouseEntry;
+
+void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque);
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+                                                void *opaque, int absolute,
+                                                const char *name);
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry);
+
+void kbd_put_keycode(int keycode);
+void kbd_put_keycodes(int*  keycodes, int  count);
+void  kbd_generic_event(int  type, int code, int  value);
+void kbd_mouse_event(int dx, int dy, int dz, int buttons_state);
+int kbd_mouse_is_absolute(void);
+
+struct mouse_transform_info_s {
+    /* Touchscreen resolution */
+    int x;
+    int y;
+    /* Calibration values as used/generated by tslib */
+    int a[7];
+};
+
+void do_info_mice(void);
+void do_mouse_set(int index);
+
+/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
+   constants) */
+#define QEMU_KEY_ESC1(c) ((c) | 0xe100)
+#define QEMU_KEY_BACKSPACE  0x007f
+#define QEMU_KEY_UP         QEMU_KEY_ESC1('A')
+#define QEMU_KEY_DOWN       QEMU_KEY_ESC1('B')
+#define QEMU_KEY_RIGHT      QEMU_KEY_ESC1('C')
+#define QEMU_KEY_LEFT       QEMU_KEY_ESC1('D')
+#define QEMU_KEY_HOME       QEMU_KEY_ESC1(1)
+#define QEMU_KEY_END        QEMU_KEY_ESC1(4)
+#define QEMU_KEY_PAGEUP     QEMU_KEY_ESC1(5)
+#define QEMU_KEY_PAGEDOWN   QEMU_KEY_ESC1(6)
+#define QEMU_KEY_DELETE     QEMU_KEY_ESC1(3)
+
+#define QEMU_KEY_CTRL_UP         0xe400
+#define QEMU_KEY_CTRL_DOWN       0xe401
+#define QEMU_KEY_CTRL_LEFT       0xe402
+#define QEMU_KEY_CTRL_RIGHT      0xe403
+#define QEMU_KEY_CTRL_HOME       0xe404
+#define QEMU_KEY_CTRL_END        0xe405
+#define QEMU_KEY_CTRL_PAGEUP     0xe406
+#define QEMU_KEY_CTRL_PAGEDOWN   0xe407
+
+void kbd_put_keysym(int keysym);
+
+/* consoles */
+
+struct DisplayState {
+    uint8_t *data;
+    int linesize;
+    int depth;
+    int bgr; /* BGR color order instead of RGB. Only valid for depth == 32 */
+    int width;
+    int height;
+    void *opaque;
+    struct QEMUTimer *gui_timer;
+    uint64_t gui_timer_interval;
+    int idle; /* there is nothing to update (window invisible), set by vnc/sdl */
+
+    void (*dpy_update)(struct DisplayState *s, int x, int y, int w, int h);
+    void (*dpy_resize)(struct DisplayState *s, int w, int h);
+    void (*dpy_refresh)(struct DisplayState *s);
+    void (*dpy_copy)(struct DisplayState *s, int src_x, int src_y,
+                     int dst_x, int dst_y, int w, int h);
+    void (*dpy_fill)(struct DisplayState *s, int x, int y,
+                     int w, int h, uint32_t c);
+    void (*dpy_text_cursor)(struct DisplayState *s, int x, int y);
+    void (*mouse_set)(int x, int y, int on);
+    void (*cursor_define)(int width, int height, int bpp, int hot_x, int hot_y,
+                          uint8_t *image, uint8_t *mask);
+};
+
+static inline void dpy_update(DisplayState *s, int x, int y, int w, int h)
+{
+    s->dpy_update(s, x, y, w, h);
+}
+
+static inline void dpy_resize(DisplayState *s, int w, int h)
+{
+    s->dpy_resize(s, w, h);
+}
+
+static inline void dpy_cursor(DisplayState *s, int x, int y)
+{
+    if (s->dpy_text_cursor)
+        s->dpy_text_cursor(s, x, y);
+}
+
+typedef unsigned long console_ch_t;
+static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
+{
+    cpu_to_le32wu((uint32_t *) dest, ch);
+}
+
+typedef void (*vga_hw_update_ptr)(void *);
+typedef void (*vga_hw_invalidate_ptr)(void *);
+typedef void (*vga_hw_screen_dump_ptr)(void *, const char *);
+typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *);
+
+TextConsole *graphic_console_init(DisplayState *ds, vga_hw_update_ptr update,
+                                  vga_hw_invalidate_ptr invalidate,
+                                  vga_hw_screen_dump_ptr screen_dump,
+                                  vga_hw_text_update_ptr text_update,
+                                  void *opaque);
+void vga_hw_update(void);
+void vga_hw_invalidate(void);
+void vga_hw_screen_dump(const char *filename);
+void vga_hw_text_update(console_ch_t *chardata);
+
+int is_graphic_console(void);
+CharDriverState *text_console_init(DisplayState *ds, const char *p);
+void console_select(unsigned int index);
+void console_color_init(DisplayState *ds);
+void qemu_console_resize(QEMUConsole *console, int width, int height);
+
+/* sdl.c */
+void sdl_display_init(DisplayState *ds, int full_screen, int no_frame);
+
+/* cocoa.m */
+void cocoa_display_init(DisplayState *ds, int full_screen);
+
+/* vnc.c */
+void vnc_display_init(DisplayState *ds);
+void vnc_display_close(DisplayState *ds);
+int vnc_display_open(DisplayState *ds, const char *display);
+int vnc_display_password(DisplayState *ds, const char *password);
+void do_info_vnc(void);
+
+/* curses.c */
+void curses_display_init(DisplayState *ds, int full_screen);
+
+/* x_keymap.c */
+extern uint8_t _translate_keycode(const int key);
+
+/* FIXME: term_printf et al should probably go elsewhere so everything
+   does not need to include console.h  */
+/* monitor.c */
+void monitor_init(CharDriverState *hd, int show_banner);
+void term_puts(const char *str);
+void term_vprintf(const char *fmt, va_list ap);
+void term_printf(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+void term_print_filename(const char *filename);
+void term_flush(void);
+void term_print_help(void);
+void monitor_readline(const char *prompt, int is_password,
+                      char *buf, int buf_size);
+
+/* readline.c */
+typedef void ReadLineFunc(void *opaque, const char *str);
+
+extern int completion_index;
+void add_completion(const char *str);
+void readline_handle_byte(int ch);
+void readline_find_completion(const char *cmdline);
+const char *readline_get_history(unsigned int index);
+void readline_start(const char *prompt, int is_password,
+                    ReadLineFunc *readline_func, void *opaque);
+
+#endif
diff --git a/cpu-all.h b/cpu-all.h
new file mode 100644
index 0000000..8f4cb3c
--- /dev/null
+++ b/cpu-all.h
@@ -0,0 +1,1090 @@
+/*
+ * defines common to all virtual CPUs
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef CPU_ALL_H
+#define CPU_ALL_H
+
+#if defined(__arm__) || defined(__sparc__) || defined(__mips__) || defined(__hppa__)
+#define WORDS_ALIGNED
+#endif
+
+/* some important defines:
+ *
+ * WORDS_ALIGNED : if defined, the host cpu can only make word aligned
+ * memory accesses.
+ *
+ * WORDS_BIGENDIAN : if defined, the host cpu is big endian and
+ * otherwise little endian.
+ *
+ * (TARGET_WORDS_ALIGNED : same for target cpu (not supported yet))
+ *
+ * TARGET_WORDS_BIGENDIAN : same for target cpu
+ */
+
+#include "bswap.h"
+#include "softfloat.h"
+
+#if defined(WORDS_BIGENDIAN) != defined(TARGET_WORDS_BIGENDIAN)
+#define BSWAP_NEEDED
+#endif
+
+#ifdef BSWAP_NEEDED
+
+static inline uint16_t tswap16(uint16_t s)
+{
+    return bswap16(s);
+}
+
+static inline uint32_t tswap32(uint32_t s)
+{
+    return bswap32(s);
+}
+
+static inline uint64_t tswap64(uint64_t s)
+{
+    return bswap64(s);
+}
+
+static inline void tswap16s(uint16_t *s)
+{
+    *s = bswap16(*s);
+}
+
+static inline void tswap32s(uint32_t *s)
+{
+    *s = bswap32(*s);
+}
+
+static inline void tswap64s(uint64_t *s)
+{
+    *s = bswap64(*s);
+}
+
+#else
+
+static inline uint16_t tswap16(uint16_t s)
+{
+    return s;
+}
+
+static inline uint32_t tswap32(uint32_t s)
+{
+    return s;
+}
+
+static inline uint64_t tswap64(uint64_t s)
+{
+    return s;
+}
+
+static inline void tswap16s(uint16_t *s)
+{
+}
+
+static inline void tswap32s(uint32_t *s)
+{
+}
+
+static inline void tswap64s(uint64_t *s)
+{
+}
+
+#endif
+
+#if TARGET_LONG_SIZE == 4
+#define tswapl(s) tswap32(s)
+#define tswapls(s) tswap32s((uint32_t *)(s))
+#define bswaptls(s) bswap32s(s)
+#else
+#define tswapl(s) tswap64(s)
+#define tswapls(s) tswap64s((uint64_t *)(s))
+#define bswaptls(s) bswap64s(s)
+#endif
+
+typedef union {
+    float32 f;
+    uint32_t l;
+} CPU_FloatU;
+
+/* NOTE: arm FPA is horrible as double 32 bit words are stored in big
+   endian ! */
+typedef union {
+    float64 d;
+#if defined(WORDS_BIGENDIAN) \
+    || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
+    struct {
+        uint32_t upper;
+        uint32_t lower;
+    } l;
+#else
+    struct {
+        uint32_t lower;
+        uint32_t upper;
+    } l;
+#endif
+    uint64_t ll;
+} CPU_DoubleU;
+
+#ifdef TARGET_SPARC
+typedef union {
+    float128 q;
+#if defined(WORDS_BIGENDIAN) \
+    || (defined(__arm__) && !defined(__VFP_FP__) && !defined(CONFIG_SOFTFLOAT))
+    struct {
+        uint32_t upmost;
+        uint32_t upper;
+        uint32_t lower;
+        uint32_t lowest;
+    } l;
+    struct {
+        uint64_t upper;
+        uint64_t lower;
+    } ll;
+#else
+    struct {
+        uint32_t lowest;
+        uint32_t lower;
+        uint32_t upper;
+        uint32_t upmost;
+    } l;
+    struct {
+        uint64_t lower;
+        uint64_t upper;
+    } ll;
+#endif
+} CPU_QuadU;
+#endif
+
+/* CPU memory access without any memory or io remapping */
+
+/*
+ * the generic syntax for the memory accesses is:
+ *
+ * load: ld{type}{sign}{size}{endian}_{access_type}(ptr)
+ *
+ * store: st{type}{size}{endian}_{access_type}(ptr, val)
+ *
+ * type is:
+ * (empty): integer access
+ *   f    : float access
+ *
+ * sign is:
+ * (empty): for floats or 32 bit size
+ *   u    : unsigned
+ *   s    : signed
+ *
+ * size is:
+ *   b: 8 bits
+ *   w: 16 bits
+ *   l: 32 bits
+ *   q: 64 bits
+ *
+ * endian is:
+ * (empty): target cpu endianness or 8 bit access
+ *   r    : reversed target cpu endianness (not implemented yet)
+ *   be   : big endian (not implemented yet)
+ *   le   : little endian (not implemented yet)
+ *
+ * access_type is:
+ *   raw    : host memory access
+ *   user   : user mode access using soft MMU
+ *   kernel : kernel mode access using soft MMU
+ */
+static inline int ldub_p(void *ptr)
+{
+    return *(uint8_t *)ptr;
+}
+
+static inline int ldsb_p(void *ptr)
+{
+    return *(int8_t *)ptr;
+}
+
+static inline void stb_p(void *ptr, int v)
+{
+    *(uint8_t *)ptr = v;
+}
+
+/* NOTE: on arm, putting 2 in /proc/sys/debug/alignment so that the
+   kernel handles unaligned load/stores may give better results, but
+   it is a system wide setting : bad */
+#if defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+/* conservative code for little endian unaligned accesses */
+static inline int lduw_le_p(void *ptr)
+{
+#ifdef __powerpc__
+    int val;
+    __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+    return val;
+#else
+    uint8_t *p = ptr;
+    return p[0] | (p[1] << 8);
+#endif
+}
+
+static inline int ldsw_le_p(void *ptr)
+{
+#ifdef __powerpc__
+    int val;
+    __asm__ __volatile__ ("lhbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+    return (int16_t)val;
+#else
+    uint8_t *p = ptr;
+    return (int16_t)(p[0] | (p[1] << 8));
+#endif
+}
+
+static inline int ldl_le_p(void *ptr)
+{
+#ifdef __powerpc__
+    int val;
+    __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (val) : "r" (ptr));
+    return val;
+#else
+    uint8_t *p = ptr;
+    return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+#endif
+}
+
+static inline uint64_t ldq_le_p(void *ptr)
+{
+    uint8_t *p = ptr;
+    uint32_t v1, v2;
+    v1 = ldl_le_p(p);
+    v2 = ldl_le_p(p + 4);
+    return v1 | ((uint64_t)v2 << 32);
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+#ifdef __powerpc__
+    __asm__ __volatile__ ("sthbrx %1,0,%2" : "=m" (*(uint16_t *)ptr) : "r" (v), "r" (ptr));
+#else
+    uint8_t *p = ptr;
+    p[0] = v;
+    p[1] = v >> 8;
+#endif
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+#ifdef __powerpc__
+    __asm__ __volatile__ ("stwbrx %1,0,%2" : "=m" (*(uint32_t *)ptr) : "r" (v), "r" (ptr));
+#else
+    uint8_t *p = ptr;
+    p[0] = v;
+    p[1] = v >> 8;
+    p[2] = v >> 16;
+    p[3] = v >> 24;
+#endif
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+    uint8_t *p = ptr;
+    stl_le_p(p, (uint32_t)v);
+    stl_le_p(p + 4, v >> 32);
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(void *ptr)
+{
+    union {
+        float32 f;
+        uint32_t i;
+    } u;
+    u.i = ldl_le_p(ptr);
+    return u.f;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+    union {
+        float32 f;
+        uint32_t i;
+    } u;
+    u.f = v;
+    stl_le_p(ptr, u.i);
+}
+
+static inline float64 ldfq_le_p(void *ptr)
+{
+    CPU_DoubleU u;
+    u.l.lower = ldl_le_p(ptr);
+    u.l.upper = ldl_le_p(ptr + 4);
+    return u.d;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+    CPU_DoubleU u;
+    u.d = v;
+    stl_le_p(ptr, u.l.lower);
+    stl_le_p(ptr + 4, u.l.upper);
+}
+
+#else
+
+static inline int lduw_le_p(void *ptr)
+{
+    return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_le_p(void *ptr)
+{
+    return *(int16_t *)ptr;
+}
+
+static inline int ldl_le_p(void *ptr)
+{
+    return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_le_p(void *ptr)
+{
+    return *(uint64_t *)ptr;
+}
+
+static inline void stw_le_p(void *ptr, int v)
+{
+    *(uint16_t *)ptr = v;
+}
+
+static inline void stl_le_p(void *ptr, int v)
+{
+    *(uint32_t *)ptr = v;
+}
+
+static inline void stq_le_p(void *ptr, uint64_t v)
+{
+#if defined(__i386__) && __GNUC__ >= 4
+    const union { uint64_t v; uint32_t p[2]; } x = { .v = v };
+    ((uint32_t *)ptr)[0] = x.p[0];
+    ((uint32_t *)ptr)[1] = x.p[1];
+#else
+    *(uint64_t *)ptr = v;
+#endif
+}
+
+/* float access */
+
+static inline float32 ldfl_le_p(void *ptr)
+{
+    return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_le_p(void *ptr)
+{
+    return *(float64 *)ptr;
+}
+
+static inline void stfl_le_p(void *ptr, float32 v)
+{
+    *(float32 *)ptr = v;
+}
+
+static inline void stfq_le_p(void *ptr, float64 v)
+{
+    *(float64 *)ptr = v;
+}
+#endif
+
+#if !defined(WORDS_BIGENDIAN) || defined(WORDS_ALIGNED)
+
+static inline int lduw_be_p(void *ptr)
+{
+#if defined(__i386__)
+    int val;
+    asm volatile ("movzwl %1, %0\n"
+                  "xchgb %b0, %h0\n"
+                  : "=q" (val)
+                  : "m" (*(uint16_t *)ptr));
+    return val;
+#else
+    uint8_t *b = (uint8_t *) ptr;
+    return ((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldsw_be_p(void *ptr)
+{
+#if defined(__i386__)
+    int val;
+    asm volatile ("movzwl %1, %0\n"
+                  "xchgb %b0, %h0\n"
+                  : "=q" (val)
+                  : "m" (*(uint16_t *)ptr));
+    return (int16_t)val;
+#else
+    uint8_t *b = (uint8_t *) ptr;
+    return (int16_t)((b[0] << 8) | b[1]);
+#endif
+}
+
+static inline int ldl_be_p(void *ptr)
+{
+#if defined(__i386__) || defined(__x86_64__)
+    int val;
+    asm volatile ("movl %1, %0\n"
+                  "bswap %0\n"
+                  : "=r" (val)
+                  : "m" (*(uint32_t *)ptr));
+    return val;
+#else
+    uint8_t *b = (uint8_t *) ptr;
+    return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
+#endif
+}
+
+static inline uint64_t ldq_be_p(void *ptr)
+{
+    uint32_t a,b;
+    a = ldl_be_p(ptr);
+    b = ldl_be_p((uint8_t *)ptr + 4);
+    return (((uint64_t)a<<32)|b);
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+#if defined(__i386__)
+    asm volatile ("xchgb %b0, %h0\n"
+                  "movw %w0, %1\n"
+                  : "=q" (v)
+                  : "m" (*(uint16_t *)ptr), "0" (v));
+#else
+    uint8_t *d = (uint8_t *) ptr;
+    d[0] = v >> 8;
+    d[1] = v;
+#endif
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+#if defined(__i386__) || defined(__x86_64__)
+    asm volatile ("bswap %0\n"
+                  "movl %0, %1\n"
+                  : "=r" (v)
+                  : "m" (*(uint32_t *)ptr), "0" (v));
+#else
+    uint8_t *d = (uint8_t *) ptr;
+    d[0] = v >> 24;
+    d[1] = v >> 16;
+    d[2] = v >> 8;
+    d[3] = v;
+#endif
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+    stl_be_p(ptr, v >> 32);
+    stl_be_p((uint8_t *)ptr + 4, v);
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(void *ptr)
+{
+    union {
+        float32 f;
+        uint32_t i;
+    } u;
+    u.i = ldl_be_p(ptr);
+    return u.f;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+    union {
+        float32 f;
+        uint32_t i;
+    } u;
+    u.f = v;
+    stl_be_p(ptr, u.i);
+}
+
+static inline float64 ldfq_be_p(void *ptr)
+{
+    CPU_DoubleU u;
+    u.l.upper = ldl_be_p(ptr);
+    u.l.lower = ldl_be_p((uint8_t *)ptr + 4);
+    return u.d;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+    CPU_DoubleU u;
+    u.d = v;
+    stl_be_p(ptr, u.l.upper);
+    stl_be_p((uint8_t *)ptr + 4, u.l.lower);
+}
+
+#else
+
+static inline int lduw_be_p(void *ptr)
+{
+    return *(uint16_t *)ptr;
+}
+
+static inline int ldsw_be_p(void *ptr)
+{
+    return *(int16_t *)ptr;
+}
+
+static inline int ldl_be_p(void *ptr)
+{
+    return *(uint32_t *)ptr;
+}
+
+static inline uint64_t ldq_be_p(void *ptr)
+{
+    return *(uint64_t *)ptr;
+}
+
+static inline void stw_be_p(void *ptr, int v)
+{
+    *(uint16_t *)ptr = v;
+}
+
+static inline void stl_be_p(void *ptr, int v)
+{
+    *(uint32_t *)ptr = v;
+}
+
+static inline void stq_be_p(void *ptr, uint64_t v)
+{
+    *(uint64_t *)ptr = v;
+}
+
+/* float access */
+
+static inline float32 ldfl_be_p(void *ptr)
+{
+    return *(float32 *)ptr;
+}
+
+static inline float64 ldfq_be_p(void *ptr)
+{
+    return *(float64 *)ptr;
+}
+
+static inline void stfl_be_p(void *ptr, float32 v)
+{
+    *(float32 *)ptr = v;
+}
+
+static inline void stfq_be_p(void *ptr, float64 v)
+{
+    *(float64 *)ptr = v;
+}
+
+#endif
+
+/* target CPU memory access functions */
+#if defined(TARGET_WORDS_BIGENDIAN)
+#define lduw_p(p) lduw_be_p(p)
+#define ldsw_p(p) ldsw_be_p(p)
+#define ldl_p(p) ldl_be_p(p)
+#define ldq_p(p) ldq_be_p(p)
+#define ldfl_p(p) ldfl_be_p(p)
+#define ldfq_p(p) ldfq_be_p(p)
+#define stw_p(p, v) stw_be_p(p, v)
+#define stl_p(p, v) stl_be_p(p, v)
+#define stq_p(p, v) stq_be_p(p, v)
+#define stfl_p(p, v) stfl_be_p(p, v)
+#define stfq_p(p, v) stfq_be_p(p, v)
+#else
+#define lduw_p(p) lduw_le_p(p)
+#define ldsw_p(p) ldsw_le_p(p)
+#define ldl_p(p) ldl_le_p(p)
+#define ldq_p(p) ldq_le_p(p)
+#define ldfl_p(p) ldfl_le_p(p)
+#define ldfq_p(p) ldfq_le_p(p)
+#define stw_p(p, v) stw_le_p(p, v)
+#define stl_p(p, v) stl_le_p(p, v)
+#define stq_p(p, v) stq_le_p(p, v)
+#define stfl_p(p, v) stfl_le_p(p, v)
+#define stfq_p(p, v) stfq_le_p(p, v)
+#endif
+
+/* MMU memory access macros */
+
+#if defined(CONFIG_USER_ONLY)
+/* On some host systems the guest address space is reserved on the host.
+ * This allows the guest address space to be offset to a convenient location.
+ */
+//#define GUEST_BASE 0x20000000
+#define GUEST_BASE 0
+
+/* All direct uses of g2h and h2g need to go away for usermode softmmu.  */
+#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
+#define h2g(x) ((target_ulong)((unsigned long)(x) - GUEST_BASE))
+
+#define saddr(x) g2h(x)
+#define laddr(x) g2h(x)
+
+#else /* !CONFIG_USER_ONLY */
+/* NOTE: we use double casts if pointers and target_ulong have
+   different sizes */
+#define saddr(x) (uint8_t *)(long)(x)
+#define laddr(x) (uint8_t *)(long)(x)
+#endif
+
+#define ldub_raw(p) ldub_p(laddr((p)))
+#define ldsb_raw(p) ldsb_p(laddr((p)))
+#define lduw_raw(p) lduw_p(laddr((p)))
+#define ldsw_raw(p) ldsw_p(laddr((p)))
+#define ldl_raw(p) ldl_p(laddr((p)))
+#define ldq_raw(p) ldq_p(laddr((p)))
+#define ldfl_raw(p) ldfl_p(laddr((p)))
+#define ldfq_raw(p) ldfq_p(laddr((p)))
+#define stb_raw(p, v) stb_p(saddr((p)), v)
+#define stw_raw(p, v) stw_p(saddr((p)), v)
+#define stl_raw(p, v) stl_p(saddr((p)), v)
+#define stq_raw(p, v) stq_p(saddr((p)), v)
+#define stfl_raw(p, v) stfl_p(saddr((p)), v)
+#define stfq_raw(p, v) stfq_p(saddr((p)), v)
+
+
+#if defined(CONFIG_USER_ONLY)
+
+/* if user mode, no other memory access functions */
+#define ldub(p) ldub_raw(p)
+#define ldsb(p) ldsb_raw(p)
+#define lduw(p) lduw_raw(p)
+#define ldsw(p) ldsw_raw(p)
+#define ldl(p) ldl_raw(p)
+#define ldq(p) ldq_raw(p)
+#define ldfl(p) ldfl_raw(p)
+#define ldfq(p) ldfq_raw(p)
+#define stb(p, v) stb_raw(p, v)
+#define stw(p, v) stw_raw(p, v)
+#define stl(p, v) stl_raw(p, v)
+#define stq(p, v) stq_raw(p, v)
+#define stfl(p, v) stfl_raw(p, v)
+#define stfq(p, v) stfq_raw(p, v)
+
+#define ldub_code(p) ldub_raw(p)
+#define ldsb_code(p) ldsb_raw(p)
+#define lduw_code(p) lduw_raw(p)
+#define ldsw_code(p) ldsw_raw(p)
+#define ldl_code(p) ldl_raw(p)
+#define ldq_code(p) ldq_raw(p)
+
+#define ldub_kernel(p) ldub_raw(p)
+#define ldsb_kernel(p) ldsb_raw(p)
+#define lduw_kernel(p) lduw_raw(p)
+#define ldsw_kernel(p) ldsw_raw(p)
+#define ldl_kernel(p) ldl_raw(p)
+#define ldq_kernel(p) ldq_raw(p)
+#define ldfl_kernel(p) ldfl_raw(p)
+#define ldfq_kernel(p) ldfq_raw(p)
+#define stb_kernel(p, v) stb_raw(p, v)
+#define stw_kernel(p, v) stw_raw(p, v)
+#define stl_kernel(p, v) stl_raw(p, v)
+#define stq_kernel(p, v) stq_raw(p, v)
+#define stfl_kernel(p, v) stfl_raw(p, v)
+#define stfq_kernel(p, vt) stfq_raw(p, v)
+
+#endif /* defined(CONFIG_USER_ONLY) */
+
+/* page related stuff */
+
+#define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS)
+#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
+#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
+
+/* ??? These should be the larger of unsigned long and target_ulong.  */
+extern unsigned long qemu_real_host_page_size;
+extern unsigned long qemu_host_page_bits;
+extern unsigned long qemu_host_page_size;
+extern unsigned long qemu_host_page_mask;
+
+#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask)
+
+/* same as PROT_xxx */
+#define PAGE_READ      0x0001
+#define PAGE_WRITE     0x0002
+#define PAGE_EXEC      0x0004
+#define PAGE_BITS      (PAGE_READ | PAGE_WRITE | PAGE_EXEC)
+#define PAGE_VALID     0x0008
+/* original state of the write flag (used when tracking self-modifying
+   code */
+#define PAGE_WRITE_ORG 0x0010
+#define PAGE_RESERVED  0x0020
+
+void page_dump(FILE *f);
+int page_get_flags(target_ulong address);
+void page_set_flags(target_ulong start, target_ulong end, int flags);
+int page_check_range(target_ulong start, target_ulong len, int flags);
+
+void cpu_exec_init_all(unsigned long tb_size);
+CPUState *cpu_copy(CPUState *env);
+
+void cpu_dump_state(CPUState *env, FILE *f,
+                    int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+                    int flags);
+void cpu_dump_statistics (CPUState *env, FILE *f,
+                          int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+                          int flags);
+
+void cpu_abort(CPUState *env, const char *fmt, ...)
+    __attribute__ ((__format__ (__printf__, 2, 3)))
+    __attribute__ ((__noreturn__));
+extern CPUState *first_cpu;
+extern CPUState *cpu_single_env;
+extern int64_t qemu_icount;
+extern int use_icount;
+
+#define CPU_INTERRUPT_EXIT   0x01 /* wants exit from main loop */
+#define CPU_INTERRUPT_HARD   0x02 /* hardware interrupt pending */
+#define CPU_INTERRUPT_EXITTB 0x04 /* exit the current TB (use for x86 a20 case) */
+#define CPU_INTERRUPT_TIMER  0x08 /* internal timer exception pending */
+#define CPU_INTERRUPT_FIQ    0x10 /* Fast interrupt pending.  */
+#define CPU_INTERRUPT_HALT   0x20 /* CPU halt wanted */
+#define CPU_INTERRUPT_SMI    0x40 /* (x86 only) SMI interrupt pending */
+#define CPU_INTERRUPT_DEBUG  0x80 /* Debug event occured.  */
+#define CPU_INTERRUPT_VIRQ   0x100 /* virtual interrupt pending.  */
+#define CPU_INTERRUPT_NMI    0x200 /* NMI pending. */
+
+void cpu_interrupt(CPUState *s, int mask);
+void cpu_reset_interrupt(CPUState *env, int mask);
+
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, int type);
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr);
+void cpu_watchpoint_remove_all(CPUState *env);
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
+void cpu_breakpoint_remove_all(CPUState *env);
+
+#define SSTEP_ENABLE  0x1  /* Enable simulated HW single stepping */
+#define SSTEP_NOIRQ   0x2  /* Do not use IRQ while single stepping */
+#define SSTEP_NOTIMER 0x4  /* Do not Timers while single stepping */
+
+void cpu_single_step(CPUState *env, int enabled);
+void cpu_reset(CPUState *s);
+
+/* Return the physical page corresponding to a virtual one. Use it
+   only for debugging because no protection checks are done. Return -1
+   if no page found. */
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
+
+#define CPU_LOG_TB_OUT_ASM (1 << 0)
+#define CPU_LOG_TB_IN_ASM  (1 << 1)
+#define CPU_LOG_TB_OP      (1 << 2)
+#define CPU_LOG_TB_OP_OPT  (1 << 3)
+#define CPU_LOG_INT        (1 << 4)
+#define CPU_LOG_EXEC       (1 << 5)
+#define CPU_LOG_PCALL      (1 << 6)
+#define CPU_LOG_IOPORT     (1 << 7)
+#define CPU_LOG_TB_CPU     (1 << 8)
+
+/* define log items */
+typedef struct CPULogItem {
+    int mask;
+    const char *name;
+    const char *help;
+} CPULogItem;
+
+extern CPULogItem cpu_log_items[];
+
+void cpu_set_log(int log_flags);
+void cpu_set_log_filename(const char *filename);
+int cpu_str_to_log_mask(const char *str);
+
+/* IO ports API */
+
+/* NOTE: as these functions may be even used when there is an isa
+   brige on non x86 targets, we always defined them */
+#ifndef NO_CPU_IO_DEFS
+void cpu_outb(CPUState *env, int addr, int val);
+void cpu_outw(CPUState *env, int addr, int val);
+void cpu_outl(CPUState *env, int addr, int val);
+int cpu_inb(CPUState *env, int addr);
+int cpu_inw(CPUState *env, int addr);
+int cpu_inl(CPUState *env, int addr);
+#endif
+
+/* address in the RAM (different from a physical address) */
+#ifdef USE_KQEMU
+typedef uint32_t ram_addr_t;
+#else
+typedef unsigned long ram_addr_t;
+#endif
+
+/* memory API */
+
+extern ram_addr_t phys_ram_size;
+extern int phys_ram_fd;
+extern uint8_t *phys_ram_base;
+extern uint8_t *phys_ram_dirty;
+extern ram_addr_t ram_size;
+
+/* physical memory access */
+
+/* MMIO pages are identified by a combination of an IO device index and
+   3 flags.  The ROMD code stores the page ram offset in iotlb entry, 
+   so only a limited number of ids are avaiable.  */
+
+#define IO_MEM_SHIFT       3
+#define IO_MEM_NB_ENTRIES  (1 << (TARGET_PAGE_BITS  - IO_MEM_SHIFT))
+
+#define IO_MEM_RAM         (0 << IO_MEM_SHIFT) /* hardcoded offset */
+#define IO_MEM_ROM         (1 << IO_MEM_SHIFT) /* hardcoded offset */
+#define IO_MEM_UNASSIGNED  (2 << IO_MEM_SHIFT)
+#define IO_MEM_NOTDIRTY    (3 << IO_MEM_SHIFT)
+
+/* Acts like a ROM when read and like a device when written.  */
+#define IO_MEM_ROMD        (1)
+#define IO_MEM_SUBPAGE     (2)
+#define IO_MEM_SUBWIDTH    (4)
+
+/* Flags stored in the low bits of the TLB virtual address.  These are
+   defined so that fast path ram access is all zeros.  */
+/* Zero if TLB entry is valid.  */
+#define TLB_INVALID_MASK   (1 << 3)
+/* Set if TLB entry references a clean RAM page.  The iotlb entry will
+   contain the page physical address.  */
+#define TLB_NOTDIRTY    (1 << 4)
+/* Set if TLB entry is an IO callback.  */
+#define TLB_MMIO        (1 << 5)
+
+typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
+typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
+
+void cpu_register_physical_memory(target_phys_addr_t start_addr,
+                                  ram_addr_t size,
+                                  ram_addr_t phys_offset);
+ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
+ram_addr_t qemu_ram_alloc(ram_addr_t);
+void qemu_ram_free(ram_addr_t addr);
+int cpu_register_io_memory(int io_index,
+                           CPUReadMemoryFunc **mem_read,
+                           CPUWriteMemoryFunc **mem_write,
+                           void *opaque);
+CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index);
+CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index);
+
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+                            int len, int is_write);
+static inline void cpu_physical_memory_read(target_phys_addr_t addr,
+                                            uint8_t *buf, int len)
+{
+    cpu_physical_memory_rw(addr, buf, len, 0);
+}
+static inline void cpu_physical_memory_write(target_phys_addr_t addr,
+                                             const uint8_t *buf, int len)
+{
+    cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
+}
+uint32_t ldub_phys(target_phys_addr_t addr);
+uint32_t lduw_phys(target_phys_addr_t addr);
+uint32_t ldl_phys(target_phys_addr_t addr);
+uint64_t ldq_phys(target_phys_addr_t addr);
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val);
+void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val);
+void stb_phys(target_phys_addr_t addr, uint32_t val);
+void stw_phys(target_phys_addr_t addr, uint32_t val);
+void stl_phys(target_phys_addr_t addr, uint32_t val);
+void stq_phys(target_phys_addr_t addr, uint64_t val);
+
+void cpu_physical_memory_write_rom(target_phys_addr_t addr,
+                                   const uint8_t *buf, int len);
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+                        uint8_t *buf, int len, int is_write);
+
+#define VGA_DIRTY_FLAG  0x01
+#define CODE_DIRTY_FLAG 0x02
+
+/* read dirty bit (return 0 or 1) */
+static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
+{
+    return phys_ram_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
+}
+
+static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
+                                                int dirty_flags)
+{
+    return phys_ram_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
+}
+
+static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
+{
+    phys_ram_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
+}
+
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
+                                     int dirty_flags);
+void cpu_tlb_update_dirty(CPUState *env);
+
+void dump_exec_info(FILE *f,
+                    int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+/*******************************************/
+/* host CPU ticks (if available) */
+
+#if defined(__powerpc__)
+
+static inline uint32_t get_tbl(void)
+{
+    uint32_t tbl;
+    asm volatile("mftb %0" : "=r" (tbl));
+    return tbl;
+}
+
+static inline uint32_t get_tbu(void)
+{
+	uint32_t tbl;
+	asm volatile("mftbu %0" : "=r" (tbl));
+	return tbl;
+}
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+    uint32_t l, h, h1;
+    /* NOTE: we test if wrapping has occurred */
+    do {
+        h = get_tbu();
+        l = get_tbl();
+        h1 = get_tbu();
+    } while (h != h1);
+    return ((int64_t)h << 32) | l;
+}
+
+#elif defined(__i386__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+    int64_t val;
+    asm volatile ("rdtsc" : "=A" (val));
+    return val;
+}
+
+#elif defined(__x86_64__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+    uint32_t low,high;
+    int64_t val;
+    asm volatile("rdtsc" : "=a" (low), "=d" (high));
+    val = high;
+    val <<= 32;
+    val |= low;
+    return val;
+}
+
+#elif defined(__hppa__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+    int val;
+    asm volatile ("mfctl %%cr16, %0" : "=r"(val));
+    return val;
+}
+
+#elif defined(__ia64)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+	int64_t val;
+	asm volatile ("mov %0 = ar.itc" : "=r"(val) :: "memory");
+	return val;
+}
+
+#elif defined(__s390__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+    int64_t val;
+    asm volatile("stck 0(%1)" : "=m" (val) : "a" (&val) : "cc");
+    return val;
+}
+
+#elif defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || defined(__sparc_v9__)
+
+static inline int64_t cpu_get_real_ticks (void)
+{
+#if     defined(_LP64)
+        uint64_t        rval;
+        asm volatile("rd %%tick,%0" : "=r"(rval));
+        return rval;
+#else
+        union {
+                uint64_t i64;
+                struct {
+                        uint32_t high;
+                        uint32_t low;
+                }       i32;
+        } rval;
+        asm volatile("rd %%tick,%1; srlx %1,32,%0"
+                : "=r"(rval.i32.high), "=r"(rval.i32.low));
+        return rval.i64;
+#endif
+}
+
+#elif defined(__mips__)
+
+static inline int64_t cpu_get_real_ticks(void)
+{
+#if __mips_isa_rev >= 2
+    uint32_t count;
+    static uint32_t cyc_per_count = 0;
+
+    if (!cyc_per_count)
+        __asm__ __volatile__("rdhwr %0, $3" : "=r" (cyc_per_count));
+
+    __asm__ __volatile__("rdhwr %1, $2" : "=r" (count));
+    return (int64_t)(count * cyc_per_count);
+#else
+    /* FIXME */
+    static int64_t ticks = 0;
+    return ticks++;
+#endif
+}
+
+#else
+/* The host CPU doesn't have an easily accessible cycle counter.
+   Just return a monotonically increasing value.  This will be
+   totally wrong, but hopefully better than nothing.  */
+static inline int64_t cpu_get_real_ticks (void)
+{
+    static int64_t ticks = 0;
+    return ticks++;
+}
+#endif
+
+/* profiling */
+#ifdef CONFIG_PROFILER
+static inline int64_t profile_getclock(void)
+{
+    return cpu_get_real_ticks();
+}
+
+extern int64_t kqemu_time, kqemu_time_start;
+extern int64_t qemu_time, qemu_time_start;
+extern int64_t tlb_flush_time;
+extern int64_t kqemu_exec_count;
+extern int64_t dev_time;
+extern int64_t kqemu_ret_int_count;
+extern int64_t kqemu_ret_excp_count;
+extern int64_t kqemu_ret_intr_count;
+#endif
+
+#endif /* CPU_ALL_H */
diff --git a/cpu-defs.h b/cpu-defs.h
new file mode 100644
index 0000000..108d395
--- /dev/null
+++ b/cpu-defs.h
@@ -0,0 +1,198 @@
+/*
+ * common defines for all CPUs
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef CPU_DEFS_H
+#define CPU_DEFS_H
+
+#include "config.h"
+#include <setjmp.h>
+#include <inttypes.h>
+#include "osdep.h"
+
+#ifndef TARGET_LONG_BITS
+#error TARGET_LONG_BITS must be defined before including this header
+#endif
+
+#ifndef TARGET_PHYS_ADDR_BITS
+#if TARGET_LONG_BITS >= HOST_LONG_BITS
+#define TARGET_PHYS_ADDR_BITS TARGET_LONG_BITS
+#else
+#define TARGET_PHYS_ADDR_BITS HOST_LONG_BITS
+#endif
+#endif
+
+#define TARGET_LONG_SIZE (TARGET_LONG_BITS / 8)
+
+/* target_ulong is the type of a virtual address */
+#if TARGET_LONG_SIZE == 4
+typedef int32_t target_long;
+typedef uint32_t target_ulong;
+#define TARGET_FMT_lx "%08x"
+#define TARGET_FMT_ld "%d"
+#define TARGET_FMT_lu "%u"
+#elif TARGET_LONG_SIZE == 8
+typedef int64_t target_long;
+typedef uint64_t target_ulong;
+#define TARGET_FMT_lx "%016" PRIx64
+#define TARGET_FMT_ld "%" PRId64
+#define TARGET_FMT_lu "%" PRIu64
+#else
+#error TARGET_LONG_SIZE undefined
+#endif
+
+/* target_phys_addr_t is the type of a physical address (its size can
+   be different from 'target_ulong'). We have sizeof(target_phys_addr)
+   = max(sizeof(unsigned long),
+   sizeof(size_of_target_physical_address)) because we must pass a
+   host pointer to memory operations in some cases */
+
+#if TARGET_PHYS_ADDR_BITS == 32
+typedef uint32_t target_phys_addr_t;
+#define TARGET_FMT_plx "%08x"
+#elif TARGET_PHYS_ADDR_BITS == 64
+typedef uint64_t target_phys_addr_t;
+#define TARGET_FMT_plx "%016" PRIx64
+#else
+#error TARGET_PHYS_ADDR_BITS undefined
+#endif
+
+#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
+
+#define EXCP_INTERRUPT 	0x10000 /* async interruption */
+#define EXCP_HLT        0x10001 /* hlt instruction reached */
+#define EXCP_DEBUG      0x10002 /* cpu stopped after a breakpoint or singlestep */
+#define EXCP_HALTED     0x10003 /* cpu is halted (waiting for external event) */
+#define MAX_BREAKPOINTS 32
+#define MAX_WATCHPOINTS 32
+
+#define TB_JMP_CACHE_BITS 12
+#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
+
+/* Only the bottom TB_JMP_PAGE_BITS of the jump cache hash bits vary for
+   addresses on the same page.  The top bits are the same.  This allows
+   TLB invalidation to quickly clear a subset of the hash table.  */
+#define TB_JMP_PAGE_BITS (TB_JMP_CACHE_BITS / 2)
+#define TB_JMP_PAGE_SIZE (1 << TB_JMP_PAGE_BITS)
+#define TB_JMP_ADDR_MASK (TB_JMP_PAGE_SIZE - 1)
+#define TB_JMP_PAGE_MASK (TB_JMP_CACHE_SIZE - TB_JMP_PAGE_SIZE)
+
+#define CPU_TLB_BITS 8
+#define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
+
+#if TARGET_PHYS_ADDR_BITS == 32 && TARGET_LONG_BITS == 32
+#define CPU_TLB_ENTRY_BITS 4
+#else
+#define CPU_TLB_ENTRY_BITS 5
+#endif
+
+typedef struct CPUTLBEntry {
+    /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address
+       bit TARGET_PAGE_BITS-1..4  : Nonzero for accesses that should not
+                                    go directly to ram.
+       bit 3                      : indicates that the entry is invalid
+       bit 2..0                   : zero
+    */
+    target_ulong addr_read;
+    target_ulong addr_write;
+    target_ulong addr_code;
+    /* Addend to virtual address to get physical address.  IO accesses
+       use the correcponding iotlb value.  */
+#if TARGET_PHYS_ADDR_BITS == 64
+    /* on i386 Linux make sure it is aligned */
+    target_phys_addr_t addend __attribute__((aligned(8)));
+#else
+    target_phys_addr_t addend;
+#endif
+    /* padding to get a power of two size */
+    uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) - 
+                  (sizeof(target_ulong) * 3 + 
+                   ((-sizeof(target_ulong) * 3) & (sizeof(target_phys_addr_t) - 1)) + 
+                   sizeof(target_phys_addr_t))];
+} CPUTLBEntry;
+
+#ifdef WORDS_BIGENDIAN
+typedef struct icount_decr_u16 {
+    uint16_t high;
+    uint16_t low;
+} icount_decr_u16;
+#else
+typedef struct icount_decr_u16 {
+    uint16_t low;
+    uint16_t high;
+} icount_decr_u16;
+#endif
+
+#define CPU_TEMP_BUF_NLONGS 128
+#define CPU_COMMON                                                      \
+    struct TranslationBlock *current_tb; /* currently executing TB  */  \
+    /* soft mmu support */                                              \
+    /* in order to avoid passing too many arguments to the MMIO         \
+       helpers, we store some rarely used information in the CPU        \
+       context) */                                                      \
+    unsigned long mem_io_pc; /* host pc at which the memory was         \
+                                accessed */                             \
+    target_ulong mem_io_vaddr; /* target virtual addr at which the      \
+                                     memory was accessed */             \
+    uint32_t halted; /* Nonzero if the CPU is in suspend state */       \
+    uint32_t interrupt_request;                                         \
+    /* The meaning of the MMU modes is defined in the target code. */   \
+    CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE];                  \
+    target_phys_addr_t iotlb[NB_MMU_MODES][CPU_TLB_SIZE];               \
+    struct TranslationBlock *tb_jmp_cache[TB_JMP_CACHE_SIZE];           \
+    /* buffer for temporaries in the code generator */                  \
+    long temp_buf[CPU_TEMP_BUF_NLONGS];                                 \
+                                                                        \
+    int64_t icount_extra; /* Instructions until next timer event.  */   \
+    /* Number of cycles left, with interrupt flag in high bit.          \
+       This allows a single read-compare-cbranch-write sequence to test \
+       for both decrementer underflow and exceptions.  */               \
+    union {                                                             \
+        uint32_t u32;                                                   \
+        icount_decr_u16 u16;                                            \
+    } icount_decr;                                                      \
+    uint32_t can_do_io; /* nonzero if memory mapped IO is safe.  */     \
+                                                                        \
+    /* from this point: preserved by CPU reset */                       \
+    /* ice debug support */                                             \
+    target_ulong breakpoints[MAX_BREAKPOINTS];                          \
+    int nb_breakpoints;                                                 \
+    int singlestep_enabled;                                             \
+                                                                        \
+    struct {                                                            \
+        target_ulong vaddr;                                             \
+        int type; /* PAGE_READ/PAGE_WRITE */                            \
+    } watchpoint[MAX_WATCHPOINTS];                                      \
+    int nb_watchpoints;                                                 \
+    int watchpoint_hit;                                                 \
+                                                                        \
+    /* Core interrupt code */                                           \
+    jmp_buf jmp_env;                                                    \
+    int exception_index;                                                \
+                                                                        \
+    int user_mode_only;                                                 \
+                                                                        \
+    void *next_cpu; /* next CPU sharing TB cache */                     \
+    int cpu_index; /* CPU index (informative) */                        \
+    int running; /* Nonzero if cpu is currently running(usermode).  */  \
+    /* user data */                                                     \
+    void *opaque;                                                       \
+                                                                        \
+    const char *cpu_model_str;
+
+#endif
diff --git a/cpu-exec.c b/cpu-exec.c
new file mode 100644
index 0000000..8637e2a
--- /dev/null
+++ b/cpu-exec.c
@@ -0,0 +1,1487 @@
+/*
+ *  i386 emulator main execution loop
+ *
+ *  Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#define CPU_NO_GLOBAL_REGS
+#include "exec.h"
+#include "disas.h"
+#include "tcg.h"
+
+#if !defined(CONFIG_SOFTMMU)
+#undef EAX
+#undef ECX
+#undef EDX
+#undef EBX
+#undef ESP
+#undef EBP
+#undef ESI
+#undef EDI
+#undef EIP
+#include <signal.h>
+#include <sys/ucontext.h>
+#endif
+
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+// Work around ugly bugs in glibc that mangle global register contents
+#undef env
+#define env cpu_single_env
+#endif
+
+int tb_invalidated_flag;
+
+//#define DEBUG_EXEC
+//#define DEBUG_SIGNAL
+
+void cpu_loop_exit(void)
+{
+    /* NOTE: the register at this point must be saved by hand because
+       longjmp restore them */
+    regs_to_env();
+    longjmp(env->jmp_env, 1);
+}
+
+#if !(defined(TARGET_SPARC) || defined(TARGET_SH4) || defined(TARGET_M68K))
+#define reg_T2
+#endif
+
+/* exit the current TB from a signal handler. The host registers are
+   restored in a state compatible with the CPU emulator
+ */
+void cpu_resume_from_signal(CPUState *env1, void *puc)
+{
+#if !defined(CONFIG_SOFTMMU)
+    struct ucontext *uc = puc;
+#endif
+
+    env = env1;
+
+    /* XXX: restore cpu registers saved in host registers */
+
+#if !defined(CONFIG_SOFTMMU)
+    if (puc) {
+        /* XXX: use siglongjmp ? */
+        sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+    }
+#endif
+    longjmp(env->jmp_env, 1);
+}
+
+/* Execute the code without caching the generated code. An interpreter
+   could be used if available. */
+static void cpu_exec_nocache(int max_cycles, TranslationBlock *orig_tb)
+{
+    unsigned long next_tb;
+    TranslationBlock *tb;
+
+    /* Should never happen.
+       We only end up here when an existing TB is too long.  */
+    if (max_cycles > CF_COUNT_MASK)
+        max_cycles = CF_COUNT_MASK;
+
+    tb = tb_gen_code(env, orig_tb->pc, orig_tb->cs_base, orig_tb->flags,
+                     max_cycles);
+    env->current_tb = tb;
+    /* execute the generated code */
+    next_tb = tcg_qemu_tb_exec(tb->tc_ptr);
+
+    if ((next_tb & 3) == 2) {
+        /* Restore PC.  This may happen if async event occurs before
+           the TB starts executing.  */
+        CPU_PC_FROM_TB(env, tb);
+    }
+    tb_phys_invalidate(tb, -1);
+    tb_free(tb);
+}
+
+static TranslationBlock *tb_find_slow(target_ulong pc,
+                                      target_ulong cs_base,
+                                      uint64_t flags)
+{
+    TranslationBlock *tb, **ptb1;
+    unsigned int h;
+    target_ulong phys_pc, phys_page1, phys_page2, virt_page2;
+
+    tb_invalidated_flag = 0;
+
+    regs_to_env(); /* XXX: do it just before cpu_gen_code() */
+
+    /* find translated block using physical mappings */
+    phys_pc = get_phys_addr_code(env, pc);
+    phys_page1 = phys_pc & TARGET_PAGE_MASK;
+    phys_page2 = -1;
+    h = tb_phys_hash_func(phys_pc);
+    ptb1 = &tb_phys_hash[h];
+    for(;;) {
+        tb = *ptb1;
+        if (!tb)
+            goto not_found;
+        if (tb->pc == pc &&
+            tb->page_addr[0] == phys_page1 &&
+            tb->cs_base == cs_base &&
+            tb->flags == flags) {
+            /* check next page if needed */
+            if (tb->page_addr[1] != -1) {
+                virt_page2 = (pc & TARGET_PAGE_MASK) +
+                    TARGET_PAGE_SIZE;
+                phys_page2 = get_phys_addr_code(env, virt_page2);
+                if (tb->page_addr[1] == phys_page2)
+                    goto found;
+            } else {
+                goto found;
+            }
+        }
+        ptb1 = &tb->phys_hash_next;
+    }
+ not_found:
+   /* if no translated code available, then translate it now */
+    tb = tb_gen_code(env, pc, cs_base, flags, 0);
+
+ found:
+    /* we add the TB in the virtual pc hash table */
+    env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)] = tb;
+    return tb;
+}
+
+static inline TranslationBlock *tb_find_fast(void)
+{
+    TranslationBlock *tb;
+    target_ulong cs_base, pc;
+    uint64_t flags;
+
+    /* we record a subset of the CPU state. It will
+       always be the same before a given translated block
+       is executed. */
+#if defined(TARGET_I386)
+    flags = env->hflags;
+    flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
+    cs_base = env->segs[R_CS].base;
+    pc = cs_base + env->eip;
+#elif defined(TARGET_ARM)
+    flags = env->thumb | (env->vfp.vec_len << 1)
+            | (env->vfp.vec_stride << 4);
+    if ((env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR)
+        flags |= (1 << 6);
+    if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30))
+        flags |= (1 << 7);
+    flags |= (env->condexec_bits << 8);
+    cs_base = 0;
+    pc = env->regs[15];
+#elif defined(TARGET_SPARC)
+#ifdef TARGET_SPARC64
+    // AM . Combined FPU enable bits . PRIV . DMMU enabled . IMMU enabled
+    flags = ((env->pstate & PS_AM) << 2)
+        | (((env->pstate & PS_PEF) >> 1) | ((env->fprs & FPRS_FEF) << 2))
+        | (env->pstate & PS_PRIV) | ((env->lsu & (DMMU_E | IMMU_E)) >> 2);
+#else
+    // FPU enable . Supervisor
+    flags = (env->psref << 4) | env->psrs;
+#endif
+    cs_base = env->npc;
+    pc = env->pc;
+#elif defined(TARGET_PPC)
+    flags = env->hflags;
+    cs_base = 0;
+    pc = env->nip;
+#elif defined(TARGET_MIPS)
+    flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK);
+    cs_base = 0;
+    pc = env->active_tc.PC;
+#elif defined(TARGET_M68K)
+    flags = (env->fpcr & M68K_FPCR_PREC)  /* Bit  6 */
+            | (env->sr & SR_S)            /* Bit  13 */
+            | ((env->macsr >> 4) & 0xf);  /* Bits 0-3 */
+    cs_base = 0;
+    pc = env->pc;
+#elif defined(TARGET_SH4)
+    flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL
+                    | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME))   /* Bits  0- 3 */
+            | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR))  /* Bits 19-21 */
+            | (env->sr & (SR_MD | SR_RB));                     /* Bits 29-30 */
+    cs_base = 0;
+    pc = env->pc;
+#elif defined(TARGET_ALPHA)
+    flags = env->ps;
+    cs_base = 0;
+    pc = env->pc;
+#elif defined(TARGET_CRIS)
+    flags = env->pregs[PR_CCS] & (P_FLAG | U_FLAG | X_FLAG);
+    flags |= env->dslot;
+    cs_base = 0;
+    pc = env->pc;
+#else
+#error unsupported CPU
+#endif
+    tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
+    if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base ||
+                 tb->flags != flags)) {
+        tb = tb_find_slow(pc, cs_base, flags);
+    }
+    return tb;
+}
+
+/* main execution loop */
+
+int cpu_exec(CPUState *env1)
+{
+#define DECLARE_HOST_REGS 1
+#include "hostregs_helper.h"
+    int ret, interrupt_request;
+    TranslationBlock *tb;
+    uint8_t *tc_ptr;
+    unsigned long next_tb;
+
+    if (cpu_halted(env1) == EXCP_HALTED)
+        return EXCP_HALTED;
+
+    cpu_single_env = env1;
+
+    /* first we save global registers */
+#define SAVE_HOST_REGS 1
+#include "hostregs_helper.h"
+    env = env1;
+
+    env_to_regs();
+#if defined(TARGET_I386)
+    /* put eflags in CPU temporary format */
+    CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+    DF = 1 - (2 * ((env->eflags >> 10) & 1));
+    CC_OP = CC_OP_EFLAGS;
+    env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+#elif defined(TARGET_SPARC)
+#elif defined(TARGET_M68K)
+    env->cc_op = CC_OP_FLAGS;
+    env->cc_dest = env->sr & 0xf;
+    env->cc_x = (env->sr >> 4) & 1;
+#elif defined(TARGET_ALPHA)
+#elif defined(TARGET_ARM)
+#elif defined(TARGET_PPC)
+#elif defined(TARGET_MIPS)
+#elif defined(TARGET_SH4)
+#elif defined(TARGET_CRIS)
+    /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+    env->exception_index = -1;
+
+    /* prepare setjmp context for exception handling */
+    for(;;) {
+        if (setjmp(env->jmp_env) == 0) {
+            env->current_tb = NULL;
+            /* if an exception is pending, we execute it here */
+            if (env->exception_index >= 0) {
+                if (env->exception_index >= EXCP_INTERRUPT) {
+                    /* exit request from the cpu execution loop */
+                    ret = env->exception_index;
+                    break;
+                } else if (env->user_mode_only) {
+                    /* if user mode only, we simulate a fake exception
+                       which will be handled outside the cpu execution
+                       loop */
+#if defined(TARGET_I386)
+                    do_interrupt_user(env->exception_index,
+                                      env->exception_is_int,
+                                      env->error_code,
+                                      env->exception_next_eip);
+                    /* successfully delivered */
+                    env->old_exception = -1;
+#endif
+                    ret = env->exception_index;
+                    break;
+                } else {
+#if defined(TARGET_I386)
+                    /* simulate a real cpu exception. On i386, it can
+                       trigger new exceptions, but we do not handle
+                       double or triple faults yet. */
+                    do_interrupt(env->exception_index,
+                                 env->exception_is_int,
+                                 env->error_code,
+                                 env->exception_next_eip, 0);
+                    /* successfully delivered */
+                    env->old_exception = -1;
+#elif defined(TARGET_PPC)
+                    do_interrupt(env);
+#elif defined(TARGET_MIPS)
+                    do_interrupt(env);
+#elif defined(TARGET_SPARC)
+                    do_interrupt(env);
+#elif defined(TARGET_ARM)
+                    do_interrupt(env);
+#elif defined(TARGET_SH4)
+		    do_interrupt(env);
+#elif defined(TARGET_ALPHA)
+                    do_interrupt(env);
+#elif defined(TARGET_CRIS)
+                    do_interrupt(env);
+#elif defined(TARGET_M68K)
+                    do_interrupt(0);
+#endif
+                }
+                env->exception_index = -1;
+            }
+#ifdef USE_KQEMU
+            if (kqemu_is_ok(env) && env->interrupt_request == 0) {
+                int ret;
+                env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+                ret = kqemu_cpu_exec(env);
+                /* put eflags in CPU temporary format */
+                CC_SRC = env->eflags & (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+                DF = 1 - (2 * ((env->eflags >> 10) & 1));
+                CC_OP = CC_OP_EFLAGS;
+                env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+                if (ret == 1) {
+                    /* exception */
+                    longjmp(env->jmp_env, 1);
+                } else if (ret == 2) {
+                    /* softmmu execution needed */
+                } else {
+                    if (env->interrupt_request != 0) {
+                        /* hardware interrupt will be executed just after */
+                    } else {
+                        /* otherwise, we restart */
+                        longjmp(env->jmp_env, 1);
+                    }
+                }
+            }
+#endif
+
+            next_tb = 0; /* force lookup of first TB */
+            for(;;) {
+                interrupt_request = env->interrupt_request;
+                if (unlikely(interrupt_request) &&
+                    likely(!(env->singlestep_enabled & SSTEP_NOIRQ))) {
+                    if (interrupt_request & CPU_INTERRUPT_DEBUG) {
+                        env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
+                        env->exception_index = EXCP_DEBUG;
+                        cpu_loop_exit();
+                    }
+#if defined(TARGET_ARM) || defined(TARGET_SPARC) || defined(TARGET_MIPS) || \
+    defined(TARGET_PPC) || defined(TARGET_ALPHA) || defined(TARGET_CRIS)
+                    if (interrupt_request & CPU_INTERRUPT_HALT) {
+                        env->interrupt_request &= ~CPU_INTERRUPT_HALT;
+                        env->halted = 1;
+                        env->exception_index = EXCP_HLT;
+                        cpu_loop_exit();
+                    }
+#endif
+#if defined(TARGET_I386)
+                    if (env->hflags2 & HF2_GIF_MASK) {
+                        if ((interrupt_request & CPU_INTERRUPT_SMI) &&
+                            !(env->hflags & HF_SMM_MASK)) {
+                            svm_check_intercept(SVM_EXIT_SMI);
+                            env->interrupt_request &= ~CPU_INTERRUPT_SMI;
+                            do_smm_enter();
+                            next_tb = 0;
+                        } else if ((interrupt_request & CPU_INTERRUPT_NMI) &&
+                                   !(env->hflags2 & HF2_NMI_MASK)) {
+                            env->interrupt_request &= ~CPU_INTERRUPT_NMI;
+                            env->hflags2 |= HF2_NMI_MASK;
+                            do_interrupt(EXCP02_NMI, 0, 0, 0, 1);
+                            next_tb = 0;
+                        } else if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+                                   (((env->hflags2 & HF2_VINTR_MASK) && 
+                                     (env->hflags2 & HF2_HIF_MASK)) ||
+                                    (!(env->hflags2 & HF2_VINTR_MASK) && 
+                                     (env->eflags & IF_MASK && 
+                                      !(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
+                            int intno;
+                            svm_check_intercept(SVM_EXIT_INTR);
+                            env->interrupt_request &= ~(CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ);
+                            intno = cpu_get_pic_interrupt(env);
+                            if (loglevel & CPU_LOG_TB_IN_ASM) {
+                                fprintf(logfile, "Servicing hardware INT=0x%02x\n", intno);
+                            }
+                            do_interrupt(intno, 0, 0, 0, 1);
+                            /* ensure that no TB jump will be modified as
+                               the program flow was changed */
+                            next_tb = 0;
+#if !defined(CONFIG_USER_ONLY)
+                        } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) &&
+                                   (env->eflags & IF_MASK) && 
+                                   !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+                            int intno;
+                            /* FIXME: this should respect TPR */
+                            svm_check_intercept(SVM_EXIT_VINTR);
+                            env->interrupt_request &= ~CPU_INTERRUPT_VIRQ;
+                            intno = ldl_phys(env->vm_vmcb + offsetof(struct vmcb, control.int_vector));
+                            if (loglevel & CPU_LOG_TB_IN_ASM)
+                                fprintf(logfile, "Servicing virtual hardware INT=0x%02x\n", intno);
+                            do_interrupt(intno, 0, 0, 0, 1);
+                            next_tb = 0;
+#endif
+                        }
+                    }
+#elif defined(TARGET_PPC)
+#if 0
+                    if ((interrupt_request & CPU_INTERRUPT_RESET)) {
+                        cpu_ppc_reset(env);
+                    }
+#endif
+                    if (interrupt_request & CPU_INTERRUPT_HARD) {
+                        ppc_hw_interrupt(env);
+                        if (env->pending_interrupts == 0)
+                            env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+                        next_tb = 0;
+                    }
+#elif defined(TARGET_MIPS)
+                    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+                        (env->CP0_Status & env->CP0_Cause & CP0Ca_IP_mask) &&
+                        (env->CP0_Status & (1 << CP0St_IE)) &&
+                        !(env->CP0_Status & (1 << CP0St_EXL)) &&
+                        !(env->CP0_Status & (1 << CP0St_ERL)) &&
+                        !(env->hflags & MIPS_HFLAG_DM)) {
+                        /* Raise it */
+                        env->exception_index = EXCP_EXT_INTERRUPT;
+                        env->error_code = 0;
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+#elif defined(TARGET_SPARC)
+                    if ((interrupt_request & CPU_INTERRUPT_HARD) &&
+			(env->psret != 0)) {
+			int pil = env->interrupt_index & 15;
+			int type = env->interrupt_index & 0xf0;
+
+			if (((type == TT_EXTINT) &&
+			     (pil == 15 || pil > env->psrpil)) ||
+			    type != TT_EXTINT) {
+			    env->interrupt_request &= ~CPU_INTERRUPT_HARD;
+                            env->exception_index = env->interrupt_index;
+                            do_interrupt(env);
+			    env->interrupt_index = 0;
+#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
+                            cpu_check_irqs(env);
+#endif
+                        next_tb = 0;
+			}
+		    } else if (interrupt_request & CPU_INTERRUPT_TIMER) {
+			//do_interrupt(0, 0, 0, 0, 0);
+			env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
+		    }
+#elif defined(TARGET_ARM)
+                    if (interrupt_request & CPU_INTERRUPT_FIQ
+                        && !(env->uncached_cpsr & CPSR_F)) {
+                        env->exception_index = EXCP_FIQ;
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+                    /* ARMv7-M interrupt return works by loading a magic value
+                       into the PC.  On real hardware the load causes the
+                       return to occur.  The qemu implementation performs the
+                       jump normally, then does the exception return when the
+                       CPU tries to execute code at the magic address.
+                       This will cause the magic PC value to be pushed to
+                       the stack if an interrupt occured at the wrong time.
+                       We avoid this by disabling interrupts when
+                       pc contains a magic address.  */
+                    if (interrupt_request & CPU_INTERRUPT_HARD
+                        && ((IS_M(env) && env->regs[15] < 0xfffffff0)
+                            || !(env->uncached_cpsr & CPSR_I))) {
+                        env->exception_index = EXCP_IRQ;
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+#elif defined(TARGET_SH4)
+                    if (interrupt_request & CPU_INTERRUPT_HARD) {
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+#elif defined(TARGET_ALPHA)
+                    if (interrupt_request & CPU_INTERRUPT_HARD) {
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+#elif defined(TARGET_CRIS)
+                    if (interrupt_request & CPU_INTERRUPT_HARD
+                        && (env->pregs[PR_CCS] & I_FLAG)) {
+                        env->exception_index = EXCP_IRQ;
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+                    if (interrupt_request & CPU_INTERRUPT_NMI
+                        && (env->pregs[PR_CCS] & M_FLAG)) {
+                        env->exception_index = EXCP_NMI;
+                        do_interrupt(env);
+                        next_tb = 0;
+                    }
+#elif defined(TARGET_M68K)
+                    if (interrupt_request & CPU_INTERRUPT_HARD
+                        && ((env->sr & SR_I) >> SR_I_SHIFT)
+                            < env->pending_level) {
+                        /* Real hardware gets the interrupt vector via an
+                           IACK cycle at this point.  Current emulated
+                           hardware doesn't rely on this, so we
+                           provide/save the vector when the interrupt is
+                           first signalled.  */
+                        env->exception_index = env->pending_vector;
+                        do_interrupt(1);
+                        next_tb = 0;
+                    }
+#endif
+                   /* Don't use the cached interupt_request value,
+                      do_interrupt may have updated the EXITTB flag. */
+                    if (env->interrupt_request & CPU_INTERRUPT_EXITTB) {
+                        env->interrupt_request &= ~CPU_INTERRUPT_EXITTB;
+                        /* ensure that no TB jump will be modified as
+                           the program flow was changed */
+                        next_tb = 0;
+                    }
+                    if (interrupt_request & CPU_INTERRUPT_EXIT) {
+                        env->interrupt_request &= ~CPU_INTERRUPT_EXIT;
+                        env->exception_index = EXCP_INTERRUPT;
+                        cpu_loop_exit();
+                    }
+                }
+#ifdef DEBUG_EXEC
+                if ((loglevel & CPU_LOG_TB_CPU)) {
+                    /* restore flags in standard format */
+                    regs_to_env();
+#if defined(TARGET_I386)
+                    env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+                    cpu_dump_state(env, logfile, fprintf, X86_DUMP_CCOP);
+                    env->eflags &= ~(DF_MASK | CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C);
+#elif defined(TARGET_ARM)
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_SPARC)
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_PPC)
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_M68K)
+                    cpu_m68k_flush_flags(env, env->cc_op);
+                    env->cc_op = CC_OP_FLAGS;
+                    env->sr = (env->sr & 0xffe0)
+                              | env->cc_dest | (env->cc_x << 4);
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_MIPS)
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_SH4)
+		    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_ALPHA)
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#elif defined(TARGET_CRIS)
+                    cpu_dump_state(env, logfile, fprintf, 0);
+#else
+#error unsupported target CPU
+#endif
+                }
+#endif
+                spin_lock(&tb_lock);
+                tb = tb_find_fast();
+                /* Note: we do it here to avoid a gcc bug on Mac OS X when
+                   doing it in tb_find_slow */
+                if (tb_invalidated_flag) {
+                    /* as some TB could have been invalidated because
+                       of memory exceptions while generating the code, we
+                       must recompute the hash index here */
+                    next_tb = 0;
+                    tb_invalidated_flag = 0;
+                }
+#ifdef DEBUG_EXEC
+                if ((loglevel & CPU_LOG_EXEC)) {
+                    fprintf(logfile, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n",
+                            (long)tb->tc_ptr, tb->pc,
+                            lookup_symbol(tb->pc));
+                }
+#endif
+                /* see if we can patch the calling TB. When the TB
+                   spans two pages, we cannot safely do a direct
+                   jump. */
+                {
+                    if (next_tb != 0 &&
+#ifdef USE_KQEMU
+                        (env->kqemu_enabled != 2) &&
+#endif
+                        tb->page_addr[1] == -1) {
+                    tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb);
+                }
+                }
+                spin_unlock(&tb_lock);
+                env->current_tb = tb;
+                while (env->current_tb) {
+                    tc_ptr = tb->tc_ptr;
+                /* execute the generated code */
+#if defined(__sparc__) && !defined(HOST_SOLARIS)
+#undef env
+                    env = cpu_single_env;
+#define env cpu_single_env
+#endif
+                    next_tb = tcg_qemu_tb_exec(tc_ptr);
+                    env->current_tb = NULL;
+                    if ((next_tb & 3) == 2) {
+                        /* Instruction counter expired.  */
+                        int insns_left;
+                        tb = (TranslationBlock *)(long)(next_tb & ~3);
+                        /* Restore PC.  */
+                        CPU_PC_FROM_TB(env, tb);
+                        insns_left = env->icount_decr.u32;
+                        if (env->icount_extra && insns_left >= 0) {
+                            /* Refill decrementer and continue execution.  */
+                            env->icount_extra += insns_left;
+                            if (env->icount_extra > 0xffff) {
+                                insns_left = 0xffff;
+                            } else {
+                                insns_left = env->icount_extra;
+                            }
+                            env->icount_extra -= insns_left;
+                            env->icount_decr.u16.low = insns_left;
+                        } else {
+                            if (insns_left > 0) {
+                                /* Execute remaining instructions.  */
+                                cpu_exec_nocache(insns_left, tb);
+                            }
+                            env->exception_index = EXCP_INTERRUPT;
+                            next_tb = 0;
+                            cpu_loop_exit();
+                        }
+                    }
+                }
+                /* reset soft MMU for next block (it can currently
+                   only be set by a memory fault) */
+#if defined(USE_KQEMU)
+#define MIN_CYCLE_BEFORE_SWITCH (100 * 1000)
+                if (kqemu_is_ok(env) &&
+                    (cpu_get_time_fast() - env->last_io_time) >= MIN_CYCLE_BEFORE_SWITCH) {
+                    cpu_loop_exit();
+                }
+#endif
+            } /* for(;;) */
+        } else {
+            env_to_regs();
+        }
+    } /* for(;;) */
+
+
+#if defined(TARGET_I386)
+    /* restore flags in standard format */
+    env->eflags = env->eflags | cc_table[CC_OP].compute_all() | (DF & DF_MASK);
+#elif defined(TARGET_ARM)
+    /* XXX: Save/restore host fpu exception state?.  */
+#elif defined(TARGET_SPARC)
+#elif defined(TARGET_PPC)
+#elif defined(TARGET_M68K)
+    cpu_m68k_flush_flags(env, env->cc_op);
+    env->cc_op = CC_OP_FLAGS;
+    env->sr = (env->sr & 0xffe0)
+              | env->cc_dest | (env->cc_x << 4);
+#elif defined(TARGET_MIPS)
+#elif defined(TARGET_SH4)
+#elif defined(TARGET_ALPHA)
+#elif defined(TARGET_CRIS)
+    /* XXXXX */
+#else
+#error unsupported target CPU
+#endif
+
+    /* restore global registers */
+#include "hostregs_helper.h"
+
+    /* fail safe : never use cpu_single_env outside cpu_exec() */
+    cpu_single_env = NULL;
+    return ret;
+}
+
+/* must only be called from the generated code as an exception can be
+   generated */
+void tb_invalidate_page_range(target_ulong start, target_ulong end)
+{
+    /* XXX: cannot enable it yet because it yields to MMU exception
+       where NIP != read address on PowerPC */
+#if 0
+    target_ulong phys_addr;
+    phys_addr = get_phys_addr_code(env, start);
+    tb_invalidate_phys_page_range(phys_addr, phys_addr + end - start, 0);
+#endif
+}
+
+#if defined(TARGET_I386) && defined(CONFIG_USER_ONLY)
+
+void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
+{
+    CPUX86State *saved_env;
+
+    saved_env = env;
+    env = s;
+    if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
+        selector &= 0xffff;
+        cpu_x86_load_seg_cache(env, seg_reg, selector,
+                               (selector << 4), 0xffff, 0);
+    } else {
+        helper_load_seg(seg_reg, selector);
+    }
+    env = saved_env;
+}
+
+void cpu_x86_fsave(CPUX86State *s, target_ulong ptr, int data32)
+{
+    CPUX86State *saved_env;
+
+    saved_env = env;
+    env = s;
+
+    helper_fsave(ptr, data32);
+
+    env = saved_env;
+}
+
+void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32)
+{
+    CPUX86State *saved_env;
+
+    saved_env = env;
+    env = s;
+
+    helper_frstor(ptr, data32);
+
+    env = saved_env;
+}
+
+#endif /* TARGET_I386 */
+
+#if !defined(CONFIG_SOFTMMU)
+
+#if defined(TARGET_I386)
+
+/* 'pc' is the host PC at which the exception was raised. 'address' is
+   the effective address of the memory exception. 'is_write' is 1 if a
+   write caused the exception and otherwise 0'. 'old_set' is the
+   signal set which should be restored */
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    qemu_printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+                pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+
+    /* see if it is an MMU fault */
+    ret = cpu_x86_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    if (ret == 1) {
+#if 0
+        printf("PF exception: EIP=0x%08x CR2=0x%08x error=0x%x\n",
+               env->eip, env->cr[2], env->error_code);
+#endif
+        /* we restore the process signal mask as the sigreturn should
+           do it (XXX: use sigsetjmp) */
+        sigprocmask(SIG_SETMASK, old_set, NULL);
+        raise_exception_err(env->exception_index, env->error_code);
+    } else {
+        /* activate soft MMU for this block */
+        env->hflags |= HF_SOFTMMU_MASK;
+        cpu_resume_from_signal(env, puc);
+    }
+    /* never comes here */
+    return 1;
+}
+
+#elif defined(TARGET_ARM)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+    /* see if it is an MMU fault */
+    ret = cpu_arm_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+    sigprocmask(SIG_SETMASK, old_set, NULL);
+    cpu_loop_exit();
+    /* never comes here */
+    return 1;
+}
+#elif defined(TARGET_SPARC)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+    /* see if it is an MMU fault */
+    ret = cpu_sparc_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+    sigprocmask(SIG_SETMASK, old_set, NULL);
+    cpu_loop_exit();
+    /* never comes here */
+    return 1;
+}
+#elif defined (TARGET_PPC)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+
+    /* see if it is an MMU fault */
+    ret = cpu_ppc_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    if (ret == 1) {
+#if 0
+        printf("PF exception: NIP=0x%08x error=0x%x %p\n",
+               env->nip, env->error_code, tb);
+#endif
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+        sigprocmask(SIG_SETMASK, old_set, NULL);
+        do_raise_exception_err(env->exception_index, env->error_code);
+    } else {
+        /* activate soft MMU for this block */
+        cpu_resume_from_signal(env, puc);
+    }
+    /* never comes here */
+    return 1;
+}
+
+#elif defined(TARGET_M68K)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(address, pc, puc)) {
+        return 1;
+    }
+    /* see if it is an MMU fault */
+    ret = cpu_m68k_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+    sigprocmask(SIG_SETMASK, old_set, NULL);
+    cpu_loop_exit();
+    /* never comes here */
+    return 1;
+}
+
+#elif defined (TARGET_MIPS)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+
+    /* see if it is an MMU fault */
+    ret = cpu_mips_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    if (ret == 1) {
+#if 0
+        printf("PF exception: PC=0x" TARGET_FMT_lx " error=0x%x %p\n",
+               env->PC, env->error_code, tb);
+#endif
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+        sigprocmask(SIG_SETMASK, old_set, NULL);
+        do_raise_exception_err(env->exception_index, env->error_code);
+    } else {
+        /* activate soft MMU for this block */
+        cpu_resume_from_signal(env, puc);
+    }
+    /* never comes here */
+    return 1;
+}
+
+#elif defined (TARGET_SH4)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+
+    /* see if it is an MMU fault */
+    ret = cpu_sh4_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+#if 0
+        printf("PF exception: NIP=0x%08x error=0x%x %p\n",
+               env->nip, env->error_code, tb);
+#endif
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+    sigprocmask(SIG_SETMASK, old_set, NULL);
+    cpu_loop_exit();
+    /* never comes here */
+    return 1;
+}
+
+#elif defined (TARGET_ALPHA)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+
+    /* see if it is an MMU fault */
+    ret = cpu_alpha_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+#if 0
+        printf("PF exception: NIP=0x%08x error=0x%x %p\n",
+               env->nip, env->error_code, tb);
+#endif
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+    sigprocmask(SIG_SETMASK, old_set, NULL);
+    cpu_loop_exit();
+    /* never comes here */
+    return 1;
+}
+#elif defined (TARGET_CRIS)
+static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
+                                    int is_write, sigset_t *old_set,
+                                    void *puc)
+{
+    TranslationBlock *tb;
+    int ret;
+
+    if (cpu_single_env)
+        env = cpu_single_env; /* XXX: find a correct solution for multithread */
+#if defined(DEBUG_SIGNAL)
+    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
+           pc, address, is_write, *(unsigned long *)old_set);
+#endif
+    /* XXX: locking issue */
+    if (is_write && page_unprotect(h2g(address), pc, puc)) {
+        return 1;
+    }
+
+    /* see if it is an MMU fault */
+    ret = cpu_cris_handle_mmu_fault(env, address, is_write, MMU_USER_IDX, 0);
+    if (ret < 0)
+        return 0; /* not an MMU fault */
+    if (ret == 0)
+        return 1; /* the MMU fault was handled without causing real CPU fault */
+
+    /* now we have a real cpu fault */
+    tb = tb_find_pc(pc);
+    if (tb) {
+        /* the PC is inside the translated code. It means that we have
+           a virtual CPU fault */
+        cpu_restore_state(tb, env, pc, puc);
+    }
+    /* we restore the process signal mask as the sigreturn should
+       do it (XXX: use sigsetjmp) */
+    sigprocmask(SIG_SETMASK, old_set, NULL);
+    cpu_loop_exit();
+    /* never comes here */
+    return 1;
+}
+
+#else
+#error unsupported target CPU
+#endif
+
+#if defined(__i386__)
+
+#if defined(__APPLE__)
+# include <sys/ucontext.h>
+
+# define EIP_sig(context)  (*((unsigned long*)&(context)->uc_mcontext->ss.eip))
+# define TRAP_sig(context)    ((context)->uc_mcontext->es.trapno)
+# define ERROR_sig(context)   ((context)->uc_mcontext->es.err)
+#else
+# define EIP_sig(context)     ((context)->uc_mcontext.gregs[REG_EIP])
+# define TRAP_sig(context)    ((context)->uc_mcontext.gregs[REG_TRAPNO])
+# define ERROR_sig(context)   ((context)->uc_mcontext.gregs[REG_ERR])
+#endif
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+    int trapno;
+
+#ifndef REG_EIP
+/* for glibc 2.1 */
+#define REG_EIP    EIP
+#define REG_ERR    ERR
+#define REG_TRAPNO TRAPNO
+#endif
+    pc = EIP_sig(uc);
+    trapno = TRAP_sig(uc);
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             trapno == 0xe ?
+                             (ERROR_sig(uc) >> 1) & 1 : 0,
+                             &uc->uc_sigmask, puc);
+}
+
+#elif defined(__x86_64__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+
+    pc = uc->uc_mcontext.gregs[REG_RIP];
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe ?
+                             (uc->uc_mcontext.gregs[REG_ERR] >> 1) & 1 : 0,
+                             &uc->uc_sigmask, puc);
+}
+
+#elif defined(__powerpc__)
+
+/***********************************************************************
+ * signal context platform-specific definitions
+ * From Wine
+ */
+#ifdef linux
+/* All Registers access - only for local access */
+# define REG_sig(reg_name, context)		((context)->uc_mcontext.regs->reg_name)
+/* Gpr Registers access  */
+# define GPR_sig(reg_num, context)		REG_sig(gpr[reg_num], context)
+# define IAR_sig(context)			REG_sig(nip, context)	/* Program counter */
+# define MSR_sig(context)			REG_sig(msr, context)   /* Machine State Register (Supervisor) */
+# define CTR_sig(context)			REG_sig(ctr, context)   /* Count register */
+# define XER_sig(context)			REG_sig(xer, context) /* User's integer exception register */
+# define LR_sig(context)			REG_sig(link, context) /* Link register */
+# define CR_sig(context)			REG_sig(ccr, context) /* Condition register */
+/* Float Registers access  */
+# define FLOAT_sig(reg_num, context)		(((double*)((char*)((context)->uc_mcontext.regs+48*4)))[reg_num])
+# define FPSCR_sig(context)			(*(int*)((char*)((context)->uc_mcontext.regs+(48+32*2)*4)))
+/* Exception Registers access */
+# define DAR_sig(context)			REG_sig(dar, context)
+# define DSISR_sig(context)			REG_sig(dsisr, context)
+# define TRAP_sig(context)			REG_sig(trap, context)
+#endif /* linux */
+
+#ifdef __APPLE__
+# include <sys/ucontext.h>
+typedef struct ucontext SIGCONTEXT;
+/* All Registers access - only for local access */
+# define REG_sig(reg_name, context)		((context)->uc_mcontext->ss.reg_name)
+# define FLOATREG_sig(reg_name, context)	((context)->uc_mcontext->fs.reg_name)
+# define EXCEPREG_sig(reg_name, context)	((context)->uc_mcontext->es.reg_name)
+# define VECREG_sig(reg_name, context)		((context)->uc_mcontext->vs.reg_name)
+/* Gpr Registers access */
+# define GPR_sig(reg_num, context)		REG_sig(r##reg_num, context)
+# define IAR_sig(context)			REG_sig(srr0, context)	/* Program counter */
+# define MSR_sig(context)			REG_sig(srr1, context)  /* Machine State Register (Supervisor) */
+# define CTR_sig(context)			REG_sig(ctr, context)
+# define XER_sig(context)			REG_sig(xer, context) /* Link register */
+# define LR_sig(context)			REG_sig(lr, context)  /* User's integer exception register */
+# define CR_sig(context)			REG_sig(cr, context)  /* Condition register */
+/* Float Registers access */
+# define FLOAT_sig(reg_num, context)		FLOATREG_sig(fpregs[reg_num], context)
+# define FPSCR_sig(context)			((double)FLOATREG_sig(fpscr, context))
+/* Exception Registers access */
+# define DAR_sig(context)			EXCEPREG_sig(dar, context)     /* Fault registers for coredump */
+# define DSISR_sig(context)			EXCEPREG_sig(dsisr, context)
+# define TRAP_sig(context)			EXCEPREG_sig(exception, context) /* number of powerpc exception taken */
+#endif /* __APPLE__ */
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+    int is_write;
+
+    pc = IAR_sig(uc);
+    is_write = 0;
+#if 0
+    /* ppc 4xx case */
+    if (DSISR_sig(uc) & 0x00800000)
+        is_write = 1;
+#else
+    if (TRAP_sig(uc) != 0x400 && (DSISR_sig(uc) & 0x02000000))
+        is_write = 1;
+#endif
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__alpha__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                           void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    uint32_t *pc = uc->uc_mcontext.sc_pc;
+    uint32_t insn = *pc;
+    int is_write = 0;
+
+    /* XXX: need kernel patch to get write flag faster */
+    switch (insn >> 26) {
+    case 0x0d: // stw
+    case 0x0e: // stb
+    case 0x0f: // stq_u
+    case 0x24: // stf
+    case 0x25: // stg
+    case 0x26: // sts
+    case 0x27: // stt
+    case 0x2c: // stl
+    case 0x2d: // stq
+    case 0x2e: // stl_c
+    case 0x2f: // stq_c
+	is_write = 1;
+    }
+
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write, &uc->uc_sigmask, puc);
+}
+#elif defined(__sparc__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    int is_write;
+    uint32_t insn;
+#if !defined(__arch64__) || defined(HOST_SOLARIS)
+    uint32_t *regs = (uint32_t *)(info + 1);
+    void *sigmask = (regs + 20);
+    /* XXX: is there a standard glibc define ? */
+    unsigned long pc = regs[1];
+#else
+    struct sigcontext *sc = puc;
+    unsigned long pc = sc->sigc_regs.tpc;
+    void *sigmask = (void *)sc->sigc_mask;
+#endif
+
+    /* XXX: need kernel patch to get write flag faster */
+    is_write = 0;
+    insn = *(uint32_t *)pc;
+    if ((insn >> 30) == 3) {
+      switch((insn >> 19) & 0x3f) {
+      case 0x05: // stb
+      case 0x06: // sth
+      case 0x04: // st
+      case 0x07: // std
+      case 0x24: // stf
+      case 0x27: // stdf
+      case 0x25: // stfsr
+	is_write = 1;
+	break;
+      }
+    }
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write, sigmask, NULL);
+}
+
+#elif defined(__arm__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+    int is_write;
+
+#if (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3))
+    pc = uc->uc_mcontext.gregs[R15];
+#else
+    pc = uc->uc_mcontext.arm_pc;
+#endif
+    /* XXX: compute is_write */
+    is_write = 0;
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write,
+                             &uc->uc_sigmask, puc);
+}
+
+#elif defined(__mc68000)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+    int is_write;
+
+    pc = uc->uc_mcontext.gregs[16];
+    /* XXX: compute is_write */
+    is_write = 0;
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write,
+                             &uc->uc_sigmask, puc);
+}
+
+#elif defined(__ia64)
+
+#ifndef __ISR_VALID
+  /* This ought to be in <bits/siginfo.h>... */
+# define __ISR_VALID	1
+#endif
+
+int cpu_signal_handler(int host_signum, void *pinfo, void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long ip;
+    int is_write = 0;
+
+    ip = uc->uc_mcontext.sc_ip;
+    switch (host_signum) {
+      case SIGILL:
+      case SIGFPE:
+      case SIGSEGV:
+      case SIGBUS:
+      case SIGTRAP:
+	  if (info->si_code && (info->si_segvflags & __ISR_VALID))
+	      /* ISR.W (write-access) is bit 33:  */
+	      is_write = (info->si_isr >> 33) & 1;
+	  break;
+
+      default:
+	  break;
+    }
+    return handle_cpu_signal(ip, (unsigned long)info->si_addr,
+                             is_write,
+                             &uc->uc_sigmask, puc);
+}
+
+#elif defined(__s390__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+    int is_write;
+
+    pc = uc->uc_mcontext.psw.addr;
+    /* XXX: compute is_write */
+    is_write = 0;
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__mips__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    siginfo_t *info = pinfo;
+    struct ucontext *uc = puc;
+    greg_t pc = uc->uc_mcontext.pc;
+    int is_write;
+
+    /* XXX: compute is_write */
+    is_write = 0;
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr,
+                             is_write, &uc->uc_sigmask, puc);
+}
+
+#elif defined(__hppa__)
+
+int cpu_signal_handler(int host_signum, void *pinfo,
+                       void *puc)
+{
+    struct siginfo *info = pinfo;
+    struct ucontext *uc = puc;
+    unsigned long pc;
+    int is_write;
+
+    pc = uc->uc_mcontext.sc_iaoq[0];
+    /* FIXME: compute is_write */
+    is_write = 0;
+    return handle_cpu_signal(pc, (unsigned long)info->si_addr, 
+                             is_write,
+                             &uc->uc_sigmask, puc);
+}
+
+#else
+
+#error host CPU specific signal handler needed
+
+#endif
+
+#endif /* !defined(CONFIG_SOFTMMU) */
diff --git a/curses.c b/curses.c
new file mode 100644
index 0000000..96b6e49
--- /dev/null
+++ b/curses.c
@@ -0,0 +1,374 @@
+/*
+ * QEMU curses/ncurses display driver
+ * 
+ * Copyright (c) 2005 Andrzej Zaborowski  <balrog@zabor.org>
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+
+#include <curses.h>
+
+#ifndef _WIN32
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#endif
+
+#ifdef __OpenBSD__
+#define resize_term resizeterm
+#endif
+
+#define FONT_HEIGHT 16
+#define FONT_WIDTH 8
+
+static console_ch_t screen[160 * 100];
+static WINDOW *screenpad = NULL;
+static int width, height, gwidth, gheight, invalidate;
+static int px, py, sminx, sminy, smaxx, smaxy;
+
+static void curses_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    chtype *line;
+
+    line = ((chtype *) screen) + y * width;
+    for (h += y; y < h; y ++, line += width)
+        mvwaddchnstr(screenpad, y, 0, line, width);
+
+    pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1);
+    refresh();
+}
+
+static void curses_calc_pad(void)
+{
+    if (is_graphic_console()) {
+        width = gwidth;
+        height = gheight;
+    } else {
+        width = COLS;
+        height = LINES;
+    }
+
+    if (screenpad)
+        delwin(screenpad);
+
+    clear();
+    refresh();
+
+    screenpad = newpad(height, width);
+
+    if (width > COLS) {
+        px = (width - COLS) / 2;
+        sminx = 0;
+        smaxx = COLS;
+    } else {
+        px = 0;
+        sminx = (COLS - width) / 2;
+        smaxx = sminx + width;
+    }
+
+    if (height > LINES) {
+        py = (height - LINES) / 2;
+        sminy = 0;
+        smaxy = LINES;
+    } else {
+        py = 0;
+        sminy = (LINES - height) / 2;
+        smaxy = sminy + height;
+    }
+}
+
+static void curses_resize(DisplayState *ds, int w, int h)
+{
+    if (w == gwidth && h == gheight)
+        return;
+
+    gwidth = w;
+    gheight = h;
+
+    curses_calc_pad();
+}
+
+#ifndef _WIN32
+#if defined(SIGWINCH) && defined(KEY_RESIZE)
+static void curses_winch_handler(int signum)
+{
+    struct winsize {
+        unsigned short ws_row;
+        unsigned short ws_col;
+        unsigned short ws_xpixel;   /* unused */
+        unsigned short ws_ypixel;   /* unused */
+    } ws;
+
+    /* terminal size changed */
+    if (ioctl(1, TIOCGWINSZ, &ws) == -1)
+        return;
+
+    resize_term(ws.ws_row, ws.ws_col);
+    curses_calc_pad();
+    invalidate = 1;
+
+    /* some systems require this */
+    signal(SIGWINCH, curses_winch_handler);
+}
+#endif
+#endif
+
+static void curses_cursor_position(DisplayState *ds, int x, int y)
+{
+    if (x >= 0) {
+        x = sminx + x - px;
+        y = sminy + y - py;
+
+        if (x >= 0 && y >= 0 && x < COLS && y < LINES) {
+            move(y, x);
+            curs_set(1);
+            /* it seems that curs_set(1) must always be called before
+             * curs_set(2) for the latter to have effect */
+            if (!is_graphic_console())
+                curs_set(2);
+            return;
+        }
+    }
+
+    curs_set(0);
+}
+
+/* generic keyboard conversion */
+
+#include "curses_keys.h"
+#include "keymaps.c"
+
+static kbd_layout_t *kbd_layout = 0;
+static int keycode2keysym[CURSES_KEYS];
+
+static void curses_refresh(DisplayState *ds)
+{
+    int chr, nextchr, keysym, keycode;
+
+    if (invalidate) {
+        clear();
+        refresh();
+        curses_calc_pad();
+        ds->width = FONT_WIDTH * width;
+        ds->height = FONT_HEIGHT * height;
+        vga_hw_invalidate();
+        invalidate = 0;
+    }
+
+    vga_hw_text_update(screen);
+
+    nextchr = ERR;
+    while (1) {
+        /* while there are any pending key strokes to process */
+        if (nextchr == ERR)
+            chr = getch();
+        else {
+            chr = nextchr;
+            nextchr = ERR;
+        }
+
+        if (chr == ERR)
+            break;
+
+#ifdef KEY_RESIZE
+        /* this shouldn't occur when we use a custom SIGWINCH handler */
+        if (chr == KEY_RESIZE) {
+            clear();
+            refresh();
+            curses_calc_pad();
+            curses_update(ds, 0, 0, width, height);
+            ds->width = FONT_WIDTH * width;
+            ds->height = FONT_HEIGHT * height;
+            continue;
+        }
+#endif
+
+        keycode = curses2keycode[chr];
+        if (keycode == -1)
+            continue;
+
+        /* alt key */
+        if (keycode == 1) {
+            nextchr = getch();
+
+            if (nextchr != ERR) {
+                keycode = curses2keycode[nextchr];
+                nextchr = ERR;
+                if (keycode == -1)
+                    continue;
+
+                keycode |= ALT;
+
+                /* process keys reserved for qemu */
+                if (keycode >= QEMU_KEY_CONSOLE0 &&
+                        keycode < QEMU_KEY_CONSOLE0 + 9) {
+                    erase();
+                    wnoutrefresh(stdscr);
+                    console_select(keycode - QEMU_KEY_CONSOLE0);
+
+                    invalidate = 1;
+                    continue;
+                }
+            }
+        }
+
+        if (kbd_layout && !(keycode & GREY)) {
+            keysym = keycode2keysym[keycode & KEY_MASK];
+            if (keysym == -1)
+                keysym = chr;
+
+            keycode &= ~KEY_MASK;
+            keycode |= keysym2scancode(kbd_layout, keysym);
+        }
+
+        if (is_graphic_console()) {
+            /* since terminals don't know about key press and release
+             * events, we need to emit both for each key received */
+            if (keycode & SHIFT)
+                kbd_put_keycode(SHIFT_CODE);
+            if (keycode & CNTRL)
+                kbd_put_keycode(CNTRL_CODE);
+            if (keycode & ALT)
+                kbd_put_keycode(ALT_CODE);
+            if (keycode & GREY)
+                kbd_put_keycode(GREY_CODE);
+            kbd_put_keycode(keycode & KEY_MASK);
+            if (keycode & GREY)
+                kbd_put_keycode(GREY_CODE);
+            kbd_put_keycode((keycode & KEY_MASK) | KEY_RELEASE);
+            if (keycode & ALT)
+                kbd_put_keycode(ALT_CODE | KEY_RELEASE);
+            if (keycode & CNTRL)
+                kbd_put_keycode(CNTRL_CODE | KEY_RELEASE);
+            if (keycode & SHIFT)
+                kbd_put_keycode(SHIFT_CODE | KEY_RELEASE);
+        } else {
+            keysym = curses2keysym[chr];
+            if (keysym == -1)
+                keysym = chr;
+
+            kbd_put_keysym(keysym);
+        }
+    }
+}
+
+static void curses_cleanup(void *opaque) 
+{
+    endwin();
+}
+
+static void curses_atexit(void)
+{
+    curses_cleanup(NULL);
+}
+
+static void curses_setup(void)
+{
+    int i, colour_default[8] = {
+        COLOR_BLACK, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
+        COLOR_RED, COLOR_MAGENTA, COLOR_YELLOW, COLOR_WHITE,
+    };
+
+    /* input as raw as possible, let everything be interpreted
+     * by the guest system */
+    initscr(); noecho(); intrflush(stdscr, FALSE);
+    nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE);
+    start_color(); raw(); scrollok(stdscr, FALSE);
+
+    for (i = 0; i < 64; i ++)
+        init_pair(i, colour_default[i & 7], colour_default[i >> 3]);
+}
+
+static void curses_keyboard_setup(void)
+{
+    int i, keycode, keysym;
+
+#if defined(__APPLE__)
+    /* always use generic keymaps */
+    if (!keyboard_layout)
+        keyboard_layout = "en-us";
+#endif
+    if(keyboard_layout) {
+        kbd_layout = init_keyboard_layout(keyboard_layout);
+        if (!kbd_layout)
+            exit(1);
+    }
+
+    for (i = 0; i < CURSES_KEYS; i ++)
+        keycode2keysym[i] = -1;
+
+    for (i = 0; i < CURSES_KEYS; i ++) {
+        if (curses2keycode[i] == -1)
+            continue;
+
+        keycode = curses2keycode[i] & KEY_MASK;
+        if (keycode2keysym[keycode] >= 0)
+            continue;
+
+        for (keysym = 0; keysym < CURSES_KEYS; keysym ++)
+            if (curses2keycode[keysym] == keycode) {
+                keycode2keysym[keycode] = keysym;
+                break;
+            }
+
+        if (keysym >= CURSES_KEYS)
+            keycode2keysym[keycode] = i;
+    }
+}
+
+void curses_display_init(DisplayState *ds, int full_screen)
+{
+#ifndef _WIN32
+    if (!isatty(1)) {
+        fprintf(stderr, "We need a terminal output\n");
+        exit(1);
+    }
+#endif
+
+    curses_setup();
+    curses_keyboard_setup();
+    atexit(curses_atexit);
+
+#ifndef _WIN32
+#if defined(SIGWINCH) && defined(KEY_RESIZE)
+    /* some curses implementations provide a handler, but we
+     * want to be sure this is handled regardless of the library */
+    signal(SIGWINCH, curses_winch_handler);
+#endif
+#endif
+
+    ds->data = (void *) screen;
+    ds->linesize = 0;
+    ds->depth = 0;
+    ds->width = 640;
+    ds->height = 400;
+    ds->dpy_update = curses_update;
+    ds->dpy_resize = curses_resize;
+    ds->dpy_refresh = curses_refresh;
+    ds->dpy_text_cursor = curses_cursor_position;
+
+    invalidate = 1;
+
+    /* Standard VGA initial text mode dimensions */
+    curses_resize(ds, 80, 25);
+}
diff --git a/curses_keys.h b/curses_keys.h
new file mode 100644
index 0000000..27f9cd8
--- /dev/null
+++ b/curses_keys.h
@@ -0,0 +1,484 @@
+/*
+ * Keycode and keysyms conversion tables for curses
+ * 
+ * Copyright (c) 2005 Andrzej Zaborowski  <balrog@zabor.org>
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define KEY_RELEASE         0x80
+#define KEY_MASK            0x7f
+#define SHIFT_CODE          0x2a
+#define SHIFT               0x0080
+#define GREY_CODE           0xe0
+#define GREY                0x0100
+#define CNTRL_CODE          0x1d
+#define CNTRL               0x0200
+#define ALT_CODE            0x38
+#define ALT                 0x0400
+
+/* curses won't detect a Control + Alt + 1, so use Alt + 1 */
+#define QEMU_KEY_CONSOLE0   (2 | ALT)   /* (curses2keycode['1'] | ALT) */
+
+#define CURSES_KEYS         KEY_MAX     /* KEY_MAX defined in <curses.h> */
+
+int curses2keycode[CURSES_KEYS] = {
+    [0 ... (CURSES_KEYS - 1)] = -1,
+
+    [0x01b] = 1, /* Escape */
+    ['1'] = 2,
+    ['2'] = 3,
+    ['3'] = 4,
+    ['4'] = 5,
+    ['5'] = 6,
+    ['6'] = 7,
+    ['7'] = 8,
+    ['8'] = 9,
+    ['9'] = 10,
+    ['0'] = 11,
+    ['-'] = 12,
+    ['='] = 13,
+    [0x07f] = 14, /* Backspace */
+    [0x107] = 14, /* Backspace */
+
+    ['\t'] = 15, /* Tab */
+    ['q'] = 16,
+    ['w'] = 17,
+    ['e'] = 18,
+    ['r'] = 19,
+    ['t'] = 20,
+    ['y'] = 21,
+    ['u'] = 22,
+    ['i'] = 23,
+    ['o'] = 24,
+    ['p'] = 25,
+    ['['] = 26,
+    [']'] = 27,
+    ['\n'] = 28, /* Return */
+    ['\r'] = 28, /* Return */
+    [0x157] = 28, /* Return */
+
+    ['a'] = 30,
+    ['s'] = 31,
+    ['d'] = 32,
+    ['f'] = 33,
+    ['g'] = 34,
+    ['h'] = 35,
+    ['j'] = 36,
+    ['k'] = 37,
+    ['l'] = 38,
+    [';'] = 39,
+    ['\''] = 40, /* Single quote */
+    ['`'] = 41,
+    ['\\'] = 43, /* Backslash */
+
+    ['z'] = 44,
+    ['x'] = 45,
+    ['c'] = 46,
+    ['v'] = 47,
+    ['b'] = 48,
+    ['n'] = 49,
+    ['m'] = 50,
+    [','] = 51,
+    ['.'] = 52,
+    ['/'] = 53,
+
+    [' '] = 57,
+
+    [0x109] = 59, /* Function Key 1 */
+    [0x10a] = 60, /* Function Key 2 */
+    [0x10b] = 61, /* Function Key 3 */
+    [0x10c] = 62, /* Function Key 4 */
+    [0x10d] = 63, /* Function Key 5 */
+    [0x10e] = 64, /* Function Key 6 */
+    [0x10f] = 65, /* Function Key 7 */
+    [0x110] = 66, /* Function Key 8 */
+    [0x111] = 67, /* Function Key 9 */
+    [0x112] = 68, /* Function Key 10 */
+    [0x113] = 87, /* Function Key 11 */
+    [0x114] = 88, /* Function Key 12 */
+
+    [0x106] = 71 | GREY, /* Home */
+    [0x103] = 72 | GREY, /* Up Arrow */
+    [0x153] = 73 | GREY, /* Page Up */
+    [0x104] = 75 | GREY, /* Left Arrow */
+    [0x105] = 77 | GREY, /* Right Arrow */
+    [0x168] = 79 | GREY, /* End */
+    [0x102] = 80 | GREY, /* Down Arrow */
+    [0x152] = 81 | GREY, /* Page Down */
+    [0x14b] = 82 | GREY, /* Insert */
+    [0x14a] = 83 | GREY, /* Delete */
+
+    ['!'] = 2 | SHIFT,
+    ['@'] = 3 | SHIFT,
+    ['#'] = 4 | SHIFT,
+    ['$'] = 5 | SHIFT,
+    ['%'] = 6 | SHIFT,
+    ['^'] = 7 | SHIFT,
+    ['&'] = 8 | SHIFT,
+    ['*'] = 9 | SHIFT,
+    ['('] = 10 | SHIFT,
+    [')'] = 11 | SHIFT,
+    ['_'] = 12 | SHIFT,
+    ['+'] = 13 | SHIFT,
+
+    [0x161] = 15 | SHIFT, /* Shift + Tab */
+    ['Q'] = 16 | SHIFT,
+    ['W'] = 17 | SHIFT,
+    ['E'] = 18 | SHIFT,
+    ['R'] = 19 | SHIFT,
+    ['T'] = 20 | SHIFT,
+    ['Y'] = 21 | SHIFT,
+    ['U'] = 22 | SHIFT,
+    ['I'] = 23 | SHIFT,
+    ['O'] = 24 | SHIFT,
+    ['P'] = 25 | SHIFT,
+    ['{'] = 26 | SHIFT,
+    ['}'] = 27 | SHIFT,
+
+    ['A'] = 30 | SHIFT,
+    ['S'] = 31 | SHIFT,
+    ['D'] = 32 | SHIFT,
+    ['F'] = 33 | SHIFT,
+    ['G'] = 34 | SHIFT,
+    ['H'] = 35 | SHIFT,
+    ['J'] = 36 | SHIFT,
+    ['K'] = 37 | SHIFT,
+    ['L'] = 38 | SHIFT,
+    [':'] = 39 | SHIFT,
+    ['"'] = 40 | SHIFT,
+    ['~'] = 41 | SHIFT,
+    ['|'] = 43 | SHIFT,
+
+    ['Z'] = 44 | SHIFT,
+    ['X'] = 45 | SHIFT,
+    ['C'] = 46 | SHIFT,
+    ['V'] = 47 | SHIFT,
+    ['B'] = 48 | SHIFT,
+    ['N'] = 49 | SHIFT,
+    ['M'] = 50 | SHIFT,
+    ['<'] = 51 | SHIFT,
+    ['>'] = 52 | SHIFT,
+    ['?'] = 53 | SHIFT,
+
+    [0x115] = 59 | SHIFT, /* Shift + Function Key 1 */
+    [0x116] = 60 | SHIFT, /* Shift + Function Key 2 */
+    [0x117] = 61 | SHIFT, /* Shift + Function Key 3 */
+    [0x118] = 62 | SHIFT, /* Shift + Function Key 4 */
+    [0x119] = 63 | SHIFT, /* Shift + Function Key 5 */
+    [0x11a] = 64 | SHIFT, /* Shift + Function Key 6 */
+    [0x11b] = 65 | SHIFT, /* Shift + Function Key 7 */
+    [0x11c] = 66 | SHIFT, /* Shift + Function Key 8 */
+
+    [0x011] = 16 | CNTRL, /* Control + q */
+    [0x017] = 17 | CNTRL, /* Control + w */
+    [0x005] = 18 | CNTRL, /* Control + e */
+    [0x012] = 19 | CNTRL, /* Control + r */
+    [0x014] = 20 | CNTRL, /* Control + t */
+    [0x019] = 21 | CNTRL, /* Control + y */
+    [0x015] = 22 | CNTRL, /* Control + u */
+    [0x009] = 23 | CNTRL, /* Control + i */
+    [0x00f] = 24 | CNTRL, /* Control + o */
+    [0x010] = 25 | CNTRL, /* Control + p */
+
+    [0x001] = 30 | CNTRL, /* Control + a */
+    [0x013] = 31 | CNTRL, /* Control + s */
+    [0x004] = 32 | CNTRL, /* Control + d */
+    [0x006] = 33 | CNTRL, /* Control + f */
+    [0x007] = 34 | CNTRL, /* Control + g */
+    [0x008] = 35 | CNTRL, /* Control + h */
+    [0x00a] = 36 | CNTRL, /* Control + j */
+    [0x00b] = 37 | CNTRL, /* Control + k */
+    [0x00c] = 38 | CNTRL, /* Control + l */
+
+    [0x01a] = 44 | CNTRL, /* Control + z */
+    [0x018] = 45 | CNTRL, /* Control + x */
+    [0x003] = 46 | CNTRL, /* Control + c */
+    [0x016] = 47 | CNTRL, /* Control + v */
+    [0x002] = 48 | CNTRL, /* Control + b */
+    [0x00e] = 49 | CNTRL, /* Control + n */
+    /* Control + m collides with the keycode for Enter */
+
+};
+
+int curses2keysym[CURSES_KEYS] = {
+    [0 ... (CURSES_KEYS - 1)] = -1,
+
+    ['\n'] = '\n',
+    ['\r'] = '\n',
+
+    [0x07f] = QEMU_KEY_BACKSPACE,
+
+    [0x102] = QEMU_KEY_DOWN,
+    [0x103] = QEMU_KEY_UP,
+    [0x104] = QEMU_KEY_LEFT,
+    [0x105] = QEMU_KEY_RIGHT,
+    [0x106] = QEMU_KEY_HOME,
+    [0x107] = QEMU_KEY_BACKSPACE,
+
+    [0x14a] = QEMU_KEY_DELETE,
+    [0x152] = QEMU_KEY_PAGEDOWN,
+    [0x153] = QEMU_KEY_PAGEUP,
+    [0x157] = '\n',
+    [0x168] = QEMU_KEY_END,
+
+};
+
+typedef struct {
+	const char* name;
+	int keysym;
+} name2keysym_t;
+
+static name2keysym_t name2keysym[] = {
+    /* Plain ASCII */
+    { "space", 0x020 },
+    { "exclam", 0x021 },
+    { "quotedbl", 0x022 },
+    { "numbersign", 0x023 },
+    { "dollar", 0x024 },
+    { "percent", 0x025 },
+    { "ampersand", 0x026 },
+    { "apostrophe", 0x027 },
+    { "parenleft", 0x028 },
+    { "parenright", 0x029 },
+    { "asterisk", 0x02a },
+    { "plus", 0x02b },
+    { "comma", 0x02c },
+    { "minus", 0x02d },
+    { "period", 0x02e },
+    { "slash", 0x02f },
+    { "0", 0x030 },
+    { "1", 0x031 },
+    { "2", 0x032 },
+    { "3", 0x033 },
+    { "4", 0x034 },
+    { "5", 0x035 },
+    { "6", 0x036 },
+    { "7", 0x037 },
+    { "8", 0x038 },
+    { "9", 0x039 },
+    { "colon", 0x03a },
+    { "semicolon", 0x03b },
+    { "less", 0x03c },
+    { "equal", 0x03d },
+    { "greater", 0x03e },
+    { "question", 0x03f },
+    { "at", 0x040 },
+    { "A", 0x041 },
+    { "B", 0x042 },
+    { "C", 0x043 },
+    { "D", 0x044 },
+    { "E", 0x045 },
+    { "F", 0x046 },
+    { "G", 0x047 },
+    { "H", 0x048 },
+    { "I", 0x049 },
+    { "J", 0x04a },
+    { "K", 0x04b },
+    { "L", 0x04c },
+    { "M", 0x04d },
+    { "N", 0x04e },
+    { "O", 0x04f },
+    { "P", 0x050 },
+    { "Q", 0x051 },
+    { "R", 0x052 },
+    { "S", 0x053 },
+    { "T", 0x054 },
+    { "U", 0x055 },
+    { "V", 0x056 },
+    { "W", 0x057 },
+    { "X", 0x058 },
+    { "Y", 0x059 },
+    { "Z", 0x05a },
+    { "bracketleft", 0x05b },
+    { "backslash", 0x05c },
+    { "bracketright", 0x05d },
+    { "asciicircum", 0x05e },
+    { "underscore", 0x05f },
+    { "grave", 0x060 },
+    { "a", 0x061 },
+    { "b", 0x062 },
+    { "c", 0x063 },
+    { "d", 0x064 },
+    { "e", 0x065 },
+    { "f", 0x066 },
+    { "g", 0x067 },
+    { "h", 0x068 },
+    { "i", 0x069 },
+    { "j", 0x06a },
+    { "k", 0x06b },
+    { "l", 0x06c },
+    { "m", 0x06d },
+    { "n", 0x06e },
+    { "o", 0x06f },
+    { "p", 0x070 },
+    { "q", 0x071 },
+    { "r", 0x072 },
+    { "s", 0x073 },
+    { "t", 0x074 },
+    { "u", 0x075 },
+    { "v", 0x076 },
+    { "w", 0x077 },
+    { "x", 0x078 },
+    { "y", 0x079 },
+    { "z", 0x07a },
+    { "braceleft", 0x07b },
+    { "bar", 0x07c },
+    { "braceright", 0x07d },
+    { "asciitilde", 0x07e },
+
+    /* Latin-1 extensions */
+    { "nobreakspace", 0x0a0 },
+    { "exclamdown", 0x0a1 },
+    { "cent", 0x0a2 },
+    { "sterling", 0x0a3 },
+    { "currency", 0x0a4 },
+    { "yen", 0x0a5 },
+    { "brokenbar", 0x0a6 },
+    { "section", 0x0a7 },
+    { "diaeresis", 0x0a8 },
+    { "copyright", 0x0a9 },
+    { "ordfeminine", 0x0aa },
+    { "guillemotleft", 0x0ab },
+    { "notsign", 0x0ac },
+    { "hyphen", 0x0ad },
+    { "registered", 0x0ae },
+    { "macron", 0x0af },
+    { "degree", 0x0b0 },
+    { "plusminus", 0x0b1 },
+    { "twosuperior", 0x0b2 },
+    { "threesuperior", 0x0b3 },
+    { "acute", 0x0b4 },
+    { "mu", 0x0b5 },
+    { "paragraph", 0x0b6 },
+    { "periodcentered", 0x0b7 },
+    { "cedilla", 0x0b8 },
+    { "onesuperior", 0x0b9 },
+    { "masculine", 0x0ba },
+    { "guillemotright", 0x0bb },
+    { "onequarter", 0x0bc },
+    { "onehalf", 0x0bd },
+    { "threequarters", 0x0be },
+    { "questiondown", 0x0bf },
+    { "Agrave", 0x0c0 },
+    { "Aacute", 0x0c1 },
+    { "Acircumflex", 0x0c2 },
+    { "Atilde", 0x0c3 },
+    { "Adiaeresis", 0x0c4 },
+    { "Aring", 0x0c5 },
+    { "AE", 0x0c6 },
+    { "Ccedilla", 0x0c7 },
+    { "Egrave", 0x0c8 },
+    { "Eacute", 0x0c9 },
+    { "Ecircumflex", 0x0ca },
+    { "Ediaeresis", 0x0cb },
+    { "Igrave", 0x0cc },
+    { "Iacute", 0x0cd },
+    { "Icircumflex", 0x0ce },
+    { "Idiaeresis", 0x0cf },
+    { "ETH", 0x0d0 },
+    { "Eth", 0x0d0 },
+    { "Ntilde", 0x0d1 },
+    { "Ograve", 0x0d2 },
+    { "Oacute", 0x0d3 },
+    { "Ocircumflex", 0x0d4 },
+    { "Otilde", 0x0d5 },
+    { "Odiaeresis", 0x0d6 },
+    { "multiply", 0x0d7 },
+    { "Ooblique", 0x0d8 },
+    { "Oslash", 0x0d8 },
+    { "Ugrave", 0x0d9 },
+    { "Uacute", 0x0da },
+    { "Ucircumflex", 0x0db },
+    { "Udiaeresis", 0x0dc },
+    { "Yacute", 0x0dd },
+    { "THORN", 0x0de },
+    { "Thorn", 0x0de },
+    { "ssharp", 0x0df },
+    { "agrave", 0x0e0 },
+    { "aacute", 0x0e1 },
+    { "acircumflex", 0x0e2 },
+    { "atilde", 0x0e3 },
+    { "adiaeresis", 0x0e4 },
+    { "aring", 0x0e5 },
+    { "ae", 0x0e6 },
+    { "ccedilla", 0x0e7 },
+    { "egrave", 0x0e8 },
+    { "eacute", 0x0e9 },
+    { "ecircumflex", 0x0ea },
+    { "ediaeresis", 0x0eb },
+    { "igrave", 0x0ec },
+    { "iacute", 0x0ed },
+    { "icircumflex", 0x0ee },
+    { "idiaeresis", 0x0ef },
+    { "eth", 0x0f0 },
+    { "ntilde", 0x0f1 },
+    { "ograve", 0x0f2 },
+    { "oacute", 0x0f3 },
+    { "ocircumflex", 0x0f4 },
+    { "otilde", 0x0f5 },
+    { "odiaeresis", 0x0f6 },
+    { "division", 0x0f7 },
+    { "oslash", 0x0f8 },
+    { "ooblique", 0x0f8 },
+    { "ugrave", 0x0f9 },
+    { "uacute", 0x0fa },
+    { "ucircumflex", 0x0fb },
+    { "udiaeresis", 0x0fc },
+    { "yacute", 0x0fd },
+    { "thorn", 0x0fe },
+    { "ydiaeresis", 0x0ff },
+
+    /* Special keys */
+    { "BackSpace", 0x07f },
+    { "Tab", '\t' },
+    { "Return", '\r' },
+    { "Right", 0x105 },
+    { "Left", 0x104 },
+    { "Up", 0x103 },
+    { "Down", 0x102 },
+    { "Page_Down", 0x152 },
+    { "Page_Up", 0x153 },
+    { "Insert", 0x14b },
+    { "Delete", 0x14a },
+    { "Home", 0x106 },
+    { "End", 0x168 },
+    { "F1", 0x109 },
+    { "F2", 0x10a },
+    { "F3", 0x10b },
+    { "F4", 0x10c },
+    { "F5", 0x10d },
+    { "F6", 0x10e },
+    { "F7", 0x10f },
+    { "F8", 0x110 },
+    { "F9", 0x111 },
+    { "F10", 0x112 },
+    { "F11", 0x113 },
+    { "F12", 0x114 },
+    { "F13", 0x115 },
+    { "F14", 0x116 },
+    { "F15", 0x117 },
+    { "F16", 0x118 },
+    { "F17", 0x119 },
+    { "F18", 0x11a },
+    { "F19", 0x11b },
+    { "F20", 0x11c },
+    { "Escape", 27 },
+
+    { 0, 0 },
+};
diff --git a/cutils.c b/cutils.c
new file mode 100644
index 0000000..b256286
--- /dev/null
+++ b/cutils.c
@@ -0,0 +1,137 @@
+/*
+ * Simple C functions to supplement the C library
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+    int c;
+    char *q = buf;
+
+    if (buf_size <= 0)
+        return;
+
+    for(;;) {
+        c = *str++;
+        if (c == 0 || q >= buf + buf_size - 1)
+            break;
+        *q++ = c;
+    }
+    *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+    int len;
+    len = strlen(buf);
+    if (len < buf_size)
+        pstrcpy(buf + len, buf_size - len, s);
+    return buf;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+    const char *p, *q;
+    p = str;
+    q = val;
+    while (*q != '\0') {
+        if (*p != *q)
+            return 0;
+        p++;
+        q++;
+    }
+    if (ptr)
+        *ptr = p;
+    return 1;
+}
+
+int stristart(const char *str, const char *val, const char **ptr)
+{
+    const char *p, *q;
+    p = str;
+    q = val;
+    while (*q != '\0') {
+        if (toupper(*p) != toupper(*q))
+            return 0;
+        p++;
+        q++;
+    }
+    if (ptr)
+        *ptr = p;
+    return 1;
+}
+
+time_t mktimegm(struct tm *tm)
+{
+    time_t t;
+    int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
+    if (m < 3) {
+        m += 12;
+        y--;
+    }
+    t = 86400 * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 + 
+                 y / 400 - 719469);
+    t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
+    return t;
+}
+
+void *get_mmap_addr(unsigned long size)
+{
+    return NULL;
+}
+
+void qemu_free(void *ptr)
+{
+    free(ptr);
+}
+
+void *qemu_malloc(size_t size)
+{
+    return malloc(size);
+}
+
+void *qemu_mallocz(size_t size)
+{
+    void *ptr;
+    ptr = qemu_malloc(size);
+    if (!ptr)
+        return NULL;
+    memset(ptr, 0, size);
+    return ptr;
+}
+
+void *qemu_realloc(void*  ptr, size_t size)
+{
+    return realloc(ptr, size);
+}
+
+char *qemu_strdup(const char *str)
+{
+    char *ptr;
+    ptr = qemu_malloc(strlen(str) + 1);
+    if (!ptr)
+        return NULL;
+    strcpy(ptr, str);
+    return ptr;
+}
diff --git a/d3des.c b/d3des.c
new file mode 100644
index 0000000..6e8a02e
--- /dev/null
+++ b/d3des.c
@@ -0,0 +1,434 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.  Also the bytebit[] array
+ * has been reversed so that the most significant bit in each byte of the
+ * key is ignored, not the least significant.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* D3DES (V5.09) -
+ *
+ * A portable, public domain, version of the Data Encryption Standard.
+ *
+ * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+ * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+ * code;  Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+ * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+ * for humouring me on.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+ * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+ */
+
+#include "d3des.h"
+
+static void scrunch(unsigned char *, unsigned long *);
+static void unscrun(unsigned long *, unsigned char *);
+static void desfunc(unsigned long *, unsigned long *);
+static void cookey(unsigned long *);
+
+static unsigned long KnL[32] = { 0L };
+
+static const unsigned short bytebit[8]	= {
+	01, 02, 04, 010, 020, 040, 0100, 0200 };
+
+static const unsigned long bigbyte[24] = {
+	0x800000L,	0x400000L,	0x200000L,	0x100000L,
+	0x80000L,	0x40000L,	0x20000L,	0x10000L,
+	0x8000L,	0x4000L,	0x2000L,	0x1000L,
+	0x800L, 	0x400L, 	0x200L, 	0x100L,
+	0x80L,		0x40L,		0x20L,		0x10L,
+	0x8L,		0x4L,		0x2L,		0x1L	};
+
+/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
+
+static const unsigned char pc1[56] = {
+	56, 48, 40, 32, 24, 16,  8,	 0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26,	18, 10,  2, 59, 51, 43, 35,
+	62, 54, 46, 38, 30, 22, 14,	 6, 61, 53, 45, 37, 29, 21,
+	13,  5, 60, 52, 44, 36, 28,	20, 12,  4, 27, 19, 11,  3 };
+
+static const unsigned char totrot[16] = {
+	1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 };
+
+static const unsigned char pc2[48] = {
+	13, 16, 10, 23,  0,  4,  2, 27, 14,  5, 20,  9,
+	22, 18, 11,  3, 25,  7, 15,  6, 26, 19, 12,  1,
+	40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+	43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+void deskey(key, edf)	/* Thanks to James Gillogly & Phil Karn! */
+unsigned char *key;
+int edf;
+{
+	register int i, j, l, m, n;
+	unsigned char pc1m[56], pcr[56];
+	unsigned long kn[32];
+
+	for ( j = 0; j < 56; j++ ) {
+		l = pc1[j];
+		m = l & 07;
+		pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
+		}
+	for( i = 0; i < 16; i++ ) {
+		if( edf == DE1 ) m = (15 - i) << 1;
+		else m = i << 1;
+		n = m + 1;
+		kn[m] = kn[n] = 0L;
+		for( j = 0; j < 28; j++ ) {
+			l = j + totrot[i];
+			if( l < 28 ) pcr[j] = pc1m[l];
+			else pcr[j] = pc1m[l - 28];
+			}
+		for( j = 28; j < 56; j++ ) {
+		    l = j + totrot[i];
+		    if( l < 56 ) pcr[j] = pc1m[l];
+		    else pcr[j] = pc1m[l - 28];
+		    }
+		for( j = 0; j < 24; j++ ) {
+			if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
+			if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j];
+			}
+		}
+	cookey(kn);
+	return;
+	}
+
+static void cookey(raw1)
+register unsigned long *raw1;
+{
+	register unsigned long *cook, *raw0;
+	unsigned long dough[32];
+	register int i;
+
+	cook = dough;
+	for( i = 0; i < 16; i++, raw1++ ) {
+		raw0 = raw1++;
+		*cook	 = (*raw0 & 0x00fc0000L) << 6;
+		*cook	|= (*raw0 & 0x00000fc0L) << 10;
+		*cook	|= (*raw1 & 0x00fc0000L) >> 10;
+		*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+		*cook	 = (*raw0 & 0x0003f000L) << 12;
+		*cook	|= (*raw0 & 0x0000003fL) << 16;
+		*cook	|= (*raw1 & 0x0003f000L) >> 4;
+		*cook++ |= (*raw1 & 0x0000003fL);
+		}
+	usekey(dough);
+	return;
+	}
+
+void cpkey(into)
+register unsigned long *into;
+{
+	register unsigned long *from, *endp;
+
+	from = KnL, endp = &KnL[32];
+	while( from < endp ) *into++ = *from++;
+	return;
+	}
+
+void usekey(from)
+register unsigned long *from;
+{
+	register unsigned long *to, *endp;
+
+	to = KnL, endp = &KnL[32];
+	while( to < endp ) *to++ = *from++;
+	return;
+	}
+
+void des(inblock, outblock)
+unsigned char *inblock, *outblock;
+{
+	unsigned long work[2];
+
+	scrunch(inblock, work);
+	desfunc(work, KnL);
+	unscrun(work, outblock);
+	return;
+	}
+
+static void scrunch(outof, into)
+register unsigned char *outof;
+register unsigned long *into;
+{
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into++ |= (*outof++ & 0xffL);
+	*into	 = (*outof++ & 0xffL) << 24;
+	*into	|= (*outof++ & 0xffL) << 16;
+	*into	|= (*outof++ & 0xffL) << 8;
+	*into	|= (*outof   & 0xffL);
+	return;
+	}
+
+static void unscrun(outof, into)
+register unsigned long *outof;
+register unsigned char *into;
+{
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into++ = (unsigned char)(*outof++	 & 0xffL);
+	*into++ = (unsigned char)((*outof >> 24) & 0xffL);
+	*into++ = (unsigned char)((*outof >> 16) & 0xffL);
+	*into++ = (unsigned char)((*outof >>  8) & 0xffL);
+	*into	=  (unsigned char)(*outof	 & 0xffL);
+	return;
+	}
+
+static unsigned long SP1[64] = {
+	0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
+	0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
+	0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
+	0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
+	0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
+	0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
+	0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
+	0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
+	0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
+	0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
+	0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
+	0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
+	0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
+	0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
+	0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
+
+static unsigned long SP2[64] = {
+	0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
+	0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
+	0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
+	0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
+	0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
+	0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
+	0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
+	0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
+	0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
+	0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
+	0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
+	0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
+	0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
+	0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
+	0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
+	0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
+
+static unsigned long SP3[64] = {
+	0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
+	0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
+	0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
+	0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
+	0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
+	0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
+	0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
+	0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
+	0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
+	0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
+	0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
+	0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
+	0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
+	0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
+	0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
+	0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
+
+static unsigned long SP4[64] = {
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
+	0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
+	0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
+	0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
+	0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
+	0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
+	0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
+	0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
+	0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
+	0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
+	0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
+	0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
+	0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
+
+static unsigned long SP5[64] = {
+	0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
+	0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
+	0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
+	0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
+	0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
+	0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
+	0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
+	0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
+	0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
+	0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
+	0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
+	0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
+	0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
+	0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
+	0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
+	0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
+
+static unsigned long SP6[64] = {
+	0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
+	0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
+	0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
+	0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
+	0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
+	0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
+	0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
+	0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
+	0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
+	0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
+	0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
+	0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
+	0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
+	0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
+	0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
+
+static unsigned long SP7[64] = {
+	0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
+	0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
+	0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
+	0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
+	0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
+	0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
+	0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
+	0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
+	0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
+	0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
+	0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
+	0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
+	0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
+	0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
+	0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
+	0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
+
+static unsigned long SP8[64] = {
+	0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
+	0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
+	0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
+	0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
+	0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
+	0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
+	0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
+	0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
+	0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
+	0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
+	0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
+	0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
+	0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
+	0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
+	0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
+	0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
+
+static void desfunc(block, keys)
+register unsigned long *block, *keys;
+{
+	register unsigned long fval, work, right, leftt;
+	register int round;
+
+	leftt = block[0];
+	right = block[1];
+	work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
+	right ^= work;
+	leftt ^= (work << 4);
+	work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+	right ^= work;
+	leftt ^= (work << 16);
+	work = ((right >> 2) ^ leftt) & 0x33333333L;
+	leftt ^= work;
+	right ^= (work << 2);
+	work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
+	leftt ^= work;
+	right ^= (work << 8);
+	right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
+
+	for( round = 0; round < 8; round++ ) {
+		work  = (right << 28) | (right >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = right ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		leftt ^= fval;
+		work  = (leftt << 28) | (leftt >> 4);
+		work ^= *keys++;
+		fval  = SP7[ work		 & 0x3fL];
+		fval |= SP5[(work >>  8) & 0x3fL];
+		fval |= SP3[(work >> 16) & 0x3fL];
+		fval |= SP1[(work >> 24) & 0x3fL];
+		work  = leftt ^ *keys++;
+		fval |= SP8[ work		 & 0x3fL];
+		fval |= SP6[(work >>  8) & 0x3fL];
+		fval |= SP4[(work >> 16) & 0x3fL];
+		fval |= SP2[(work >> 24) & 0x3fL];
+		right ^= fval;
+		}
+
+	right = (right << 31) | (right >> 1);
+	work = (leftt ^ right) & 0xaaaaaaaaL;
+	leftt ^= work;
+	right ^= work;
+	leftt = (leftt << 31) | (leftt >> 1);
+	work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+	right ^= work;
+	leftt ^= (work << 8);
+	work = ((leftt >> 2) ^ right) & 0x33333333L;
+	right ^= work;
+	leftt ^= (work << 2);
+	work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+	leftt ^= work;
+	right ^= (work << 16);
+	work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+	leftt ^= work;
+	right ^= (work << 4);
+	*block++ = right;
+	*block = leftt;
+	return;
+	}
+
+/* Validation sets:
+ *
+ * Single-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : c957 4425 6a5e d31d
+ *
+ * Double-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : 7f1d 0a77 826b 8aff
+ *
+ * Double-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
+ *
+ * Triple-length key, single-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cde7
+ * Cipher : de0b 7c06 ae5e 0ed5
+ *
+ * Triple-length key, double-length plaintext -
+ * Key	  : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
+ * Plain  : 0123 4567 89ab cdef 0123 4567 89ab cdff
+ * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
+ *
+ * d3des V5.0a rwo 9208.07 18:44 Graven Imagery
+ **********************************************************************/
diff --git a/d3des.h b/d3des.h
new file mode 100644
index 0000000..ea3da44
--- /dev/null
+++ b/d3des.h
@@ -0,0 +1,51 @@
+/*
+ * This is D3DES (V5.09) by Richard Outerbridge with the double and
+ * triple-length support removed for use in VNC.
+ *
+ * These changes are:
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* d3des.h -
+ *
+ *	Headers and defines for d3des.c
+ *	Graven Imagery, 1992.
+ *
+ * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge
+ *	(GEnie : OUTER; CIS : [71755,204])
+ */
+
+#define EN0	0	/* MODE == encrypt */
+#define DE1	1	/* MODE == decrypt */
+
+extern void deskey(unsigned char *, int);
+/*		      hexkey[8]     MODE
+ * Sets the internal key register according to the hexadecimal
+ * key contained in the 8 bytes of hexkey, according to the DES,
+ * for encryption or decryption according to MODE.
+ */
+
+extern void usekey(unsigned long *);
+/*		    cookedkey[32]
+ * Loads the internal key register with the data in cookedkey.
+ */
+
+extern void cpkey(unsigned long *);
+/*		   cookedkey[32]
+ * Copies the contents of the internal key register into the storage
+ * located at &cookedkey[0].
+ */
+
+extern void des(unsigned char *, unsigned char *);
+/*		    from[8]	      to[8]
+ * Encrypts/Decrypts (according to the key currently loaded in the
+ * internal key register) one block of eight bytes at address 'from'
+ * into the block at address 'to'.  They can be the same.
+ */
+
+/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery
+ ********************************************************************/
diff --git a/dcache.c b/dcache.c
new file mode 100644
index 0000000..56426ee
--- /dev/null
+++ b/dcache.c
@@ -0,0 +1,349 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dcache.h"
+#include "cpu.h"
+#include "exec-all.h"
+#include "trace.h"
+#include "varint.h"
+
+extern FILE *ftrace_debug;
+
+int dcache_size = 16 * 1024;
+int dcache_ways = 4;
+int dcache_line_size = 32;
+int dcache_replace_policy = kPolicyRandom;
+int dcache_load_miss_penalty = 30;
+int dcache_store_miss_penalty = 5;
+
+typedef struct Dcache {
+  int		size;
+  int		ways;
+  int		line_size;
+  int		log_line_size;
+  int		rows;
+  uint32_t	addr_mask;
+  int		replace_policy;
+  int		next_way;
+  int		extra_increment_counter;
+  int		*replace;
+  uint32_t	**table;
+  int		load_miss_penalty;
+  int		store_miss_penalty;
+  uint64_t	load_hits;
+  uint64_t	load_misses;
+  uint64_t	store_hits;
+  uint64_t	store_misses;
+} Dcache;
+
+Dcache dcache;
+
+void dcache_cleanup();
+
+// Returns the log2 of "num" rounded up to the nearest integer.
+int log2_roundup(int num)
+{
+  int power2;
+  int exp;
+
+  for (exp = 0, power2 = 1; power2 < num; power2 <<= 1) {
+    exp += 1;
+  }
+  return exp;
+}
+
+void dcache_init(int size, int ways, int line_size, int replace_policy,
+                 int load_miss_penalty, int store_miss_penalty)
+{
+  int ii;
+
+  // Compute the logs of the params, rounded up
+  int log_size = log2_roundup(size);
+  int log_ways = log2_roundup(ways);
+  int log_line_size = log2_roundup(line_size);
+
+  // The number of rows in the table = size / (line_size * ways)
+  int log_rows = log_size - log_line_size - log_ways;
+
+  dcache.size = 1 << log_size;
+  dcache.ways = 1 << log_ways;
+  dcache.line_size = 1 << log_line_size;
+  dcache.log_line_size = log_line_size;
+  dcache.rows = 1 << log_rows;
+  dcache.addr_mask = (1 << log_rows) - 1;
+
+  // Allocate an array of pointers, one for each row
+  uint32_t **table = malloc(sizeof(uint32_t *) << log_rows);
+
+  // Allocate the data for the whole cache in one call to malloc()
+  int data_size = sizeof(uint32_t) << (log_rows + log_ways);
+  uint32_t *data = malloc(data_size);
+
+  // Fill the cache with invalid addresses
+  memset(data, ~0, data_size);
+
+  // Assign the pointers into the data array
+  int rows = dcache.rows;
+  for (ii = 0; ii < rows; ++ii) {
+    table[ii] = &data[ii << log_ways];
+  }
+  dcache.table = table;
+  dcache.replace_policy = replace_policy;
+  dcache.next_way = 0;
+  dcache.extra_increment_counter = 0;
+
+  dcache.replace = NULL;
+  if (replace_policy == kPolicyRoundRobin) {
+    dcache.replace = malloc(sizeof(int) << log_rows);
+    memset(dcache.replace, 0, sizeof(int) << log_rows);
+  }
+  dcache.load_miss_penalty = load_miss_penalty;
+  dcache.store_miss_penalty = store_miss_penalty;
+  dcache.load_hits = 0;
+  dcache.load_misses = 0;
+  dcache.store_hits = 0;
+  dcache.store_misses = 0;
+
+  atexit(dcache_cleanup);
+}
+
+void dcache_stats()
+{
+  uint64_t hits = dcache.load_hits + dcache.store_hits;
+  uint64_t misses = dcache.load_misses + dcache.store_misses;
+  uint64_t total = hits + misses;
+  double hit_per = 0;
+  double miss_per = 0;
+  if (total) {
+    hit_per = 100.0 * hits / total;
+    miss_per = 100.0 * misses / total;
+  }
+  printf("\n");
+  printf("Dcache hits   %10llu %6.2f%%\n", hits, hit_per);
+  printf("Dcache misses %10llu %6.2f%%\n", misses, miss_per);
+  printf("Dcache total  %10llu\n", hits + misses);
+}
+
+void dcache_free()
+{
+  free(dcache.table[0]);
+  free(dcache.table);
+  free(dcache.replace);
+  dcache.table = NULL;
+}
+
+void dcache_cleanup()
+{
+  dcache_stats();
+  dcache_free();
+}
+
+void compress_trace_addresses(TraceAddr *trace_addr)
+{
+  AddrRec *ptr;
+  char *comp_ptr = trace_addr->compressed_ptr;
+  uint32_t prev_addr = trace_addr->prev_addr;
+  uint64_t prev_time = trace_addr->prev_time;
+  AddrRec *last = &trace_addr->buffer[kMaxNumAddrs];
+  for (ptr = trace_addr->buffer; ptr != last; ++ptr) {
+    if (comp_ptr >= trace_addr->high_water_ptr) {
+      uint32_t size = comp_ptr - trace_addr->compressed;
+      fwrite(trace_addr->compressed, sizeof(char), size, trace_addr->fstream);
+      comp_ptr = trace_addr->compressed;
+    }
+
+    int addr_diff = ptr->addr - prev_addr;
+    uint64_t time_diff = ptr->time - prev_time;
+    prev_addr = ptr->addr;
+    prev_time = ptr->time;
+
+    comp_ptr = varint_encode_signed(addr_diff, comp_ptr);
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+  }
+  trace_addr->compressed_ptr = comp_ptr;
+  trace_addr->prev_addr = prev_addr;
+  trace_addr->prev_time = prev_time;
+}
+
+// This function is called by the generated code to simulate
+// a dcache load access.
+void dcache_load(uint32_t addr)
+{
+  int ii;
+  int ways = dcache.ways;
+  uint32_t cache_addr = addr >> dcache.log_line_size;
+  int row = cache_addr & dcache.addr_mask;
+  //printf("ld %lld 0x%x\n", sim_time, addr);
+  for (ii = 0; ii < ways; ++ii) {
+    if (cache_addr == dcache.table[row][ii]) {
+      dcache.load_hits += 1;
+#if 0
+      printf("dcache load hit  addr: 0x%x cache_addr: 0x%x row %d way %d\n",
+             addr, cache_addr, row, ii);
+#endif
+      // If we are tracing all addresses, then include this in the trace.
+      if (trace_all_addr) {
+        AddrRec *next = trace_load.next;
+        next->addr = addr;
+        next->time = sim_time;
+        next += 1;
+        if (next == &trace_load.buffer[kMaxNumAddrs]) {
+          // Compress the trace
+          compress_trace_addresses(&trace_load);
+          next = &trace_load.buffer[0];
+        }
+        trace_load.next = next;
+      }
+      return;
+    }
+  }
+  // This is a cache miss
+
+#if 0
+  if (ftrace_debug)
+    fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr);
+#endif
+  if (trace_load.fstream) {
+    AddrRec *next = trace_load.next;
+    next->addr = addr;
+    next->time = sim_time;
+    next += 1;
+    if (next == &trace_load.buffer[kMaxNumAddrs]) {
+      // Compress the trace
+      compress_trace_addresses(&trace_load);
+      next = &trace_load.buffer[0];
+    }
+    trace_load.next = next;
+  }
+
+  dcache.load_misses += 1;
+  sim_time += dcache.load_miss_penalty;
+
+  // Pick a way to replace
+  int way;
+  if (dcache.replace_policy == kPolicyRoundRobin) {
+    // Round robin replacement policy
+    way = dcache.replace[row];
+    int next_way = way + 1;
+    if (next_way == dcache.ways)
+      next_way = 0;
+    dcache.replace[row] = next_way;
+  } else {
+    // Random replacement policy
+    way = dcache.next_way;
+    dcache.next_way += 1;
+    if (dcache.next_way >= dcache.ways)
+      dcache.next_way = 0;
+
+    // Every 13 replacements, add an extra increment to the next way
+    dcache.extra_increment_counter += 1;
+    if (dcache.extra_increment_counter == 13) {
+      dcache.extra_increment_counter = 0;
+      dcache.next_way += 1;
+      if (dcache.next_way >= dcache.ways)
+        dcache.next_way = 0;
+    }
+  }
+#if 0
+  printf("dcache load miss addr: 0x%x cache_addr: 0x%x row %d replacing way %d\n",
+         addr, cache_addr, row, way);
+#endif
+  dcache.table[row][way] = cache_addr;
+}
+
+// This function is called by the generated code to simulate
+// a dcache store access.
+void dcache_store(uint32_t addr, uint32_t val)
+{
+  // Check for a write to a magic address (this is a virtual address)
+  //printf("st %lld 0x%08x val 0x%x\n", sim_time, addr, val);
+  if ((addr & kMagicBaseMask) == kMagicBaseAddr) {
+    uint32_t offset = addr & kMagicOffsetMask;
+    switch (offset) {
+    case kMethodTraceEnterOffset:
+        trace_interpreted_method(val, kMethodEnter);
+        break;
+    case kMethodTraceExitOffset:
+        trace_interpreted_method(val, kMethodExit);
+        break;
+    case kMethodTraceExceptionOffset:
+        trace_interpreted_method(val, kMethodException);
+        break;
+    }
+  }
+
+  int ii;
+  int ways = dcache.ways;
+  uint32_t cache_addr = addr >> dcache.log_line_size;
+  int row = cache_addr & dcache.addr_mask;
+  for (ii = 0; ii < ways; ++ii) {
+    if (cache_addr == dcache.table[row][ii]) {
+      dcache.store_hits += 1;
+#if 0
+      printf("dcache store hit  addr: 0x%x cache_addr: 0x%x row %d way %d\n",
+             addr, cache_addr, row, ii);
+#endif
+      // If we are tracing all addresses, then include this in the trace.
+      if (trace_all_addr) {
+        AddrRec *next = trace_store.next;
+        next->addr = addr;
+        next->time = sim_time;
+        next += 1;
+        if (next == &trace_store.buffer[kMaxNumAddrs]) {
+          // Compress the trace
+          compress_trace_addresses(&trace_store);
+          next = &trace_store.buffer[0];
+        }
+        trace_store.next = next;
+      }
+      return;
+    }
+  }
+  // This is a cache miss
+#if 0
+  printf("dcache store miss addr: 0x%x cache_addr: 0x%x row %d\n",
+         addr, cache_addr, row);
+#endif
+
+#if 0
+  if (ftrace_debug)
+    fprintf(ftrace_debug, "t%lld %08x\n", sim_time, addr);
+#endif
+
+  if (trace_store.fstream) {
+    AddrRec *next = trace_store.next;
+    next->addr = addr;
+    next->time = sim_time;
+    next += 1;
+    if (next == &trace_store.buffer[kMaxNumAddrs]) {
+      // Compress the trace
+      compress_trace_addresses(&trace_store);
+      next = &trace_store.buffer[0];
+    }
+    trace_store.next = next;
+  }
+
+  dcache.store_misses += 1;
+  sim_time += dcache.store_miss_penalty;
+
+  // Assume no write-allocate for now
+}
+
+// This function is called by the generated code to simulate
+// a dcache load and store (swp) access.
+void dcache_swp(uint32_t addr)
+{
+  dcache_load(addr);
+  dcache_store(addr, 0);
+}
diff --git a/dcache.h b/dcache.h
new file mode 100644
index 0000000..8857600
--- /dev/null
+++ b/dcache.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef DCACHE_H
+#define DCACHE_H
+
+#include <inttypes.h>
+
+// Define constants for the replacement policies
+#define kPolicyRoundRobin 1
+#define kPolicyRandom 2
+
+extern int dcache_size;
+extern int dcache_ways;
+extern int dcache_line_size;
+extern int dcache_replace_policy;
+extern int dcache_load_miss_penalty;
+extern int dcache_store_miss_penalty;
+
+extern void dcache_init(int size, int ways, int line_size, int replace_policy,
+                        int load_miss_penalty, int store_miss_penalty);
+
+#endif /* DCACHE_H */
diff --git a/dis-asm.h b/dis-asm.h
new file mode 100644
index 0000000..1ba86dd
--- /dev/null
+++ b/dis-asm.h
@@ -0,0 +1,478 @@
+/* Interface between the opcode library and its callers.
+   Written by Cygnus Support, 1993.
+
+   The opcode library (libopcodes.a) provides instruction decoders for
+   a large variety of instruction sets, callable with an identical
+   interface, for making instruction-processing programs more independent
+   of the instruction set being processed.  */
+
+#ifndef DIS_ASM_H
+#define DIS_ASM_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define PARAMS(x) x
+typedef void *PTR;
+typedef uint64_t bfd_vma;
+typedef int64_t bfd_signed_vma;
+typedef uint8_t bfd_byte;
+#define sprintf_vma(s,x) sprintf (s, "%0" PRIx64, x)
+#define snprintf_vma(s,ss,x) snprintf (s, ss, "%0" PRIx64, x)
+
+#define BFD64
+
+enum bfd_flavour {
+  bfd_target_unknown_flavour,
+  bfd_target_aout_flavour,
+  bfd_target_coff_flavour,
+  bfd_target_ecoff_flavour,
+  bfd_target_elf_flavour,
+  bfd_target_ieee_flavour,
+  bfd_target_nlm_flavour,
+  bfd_target_oasys_flavour,
+  bfd_target_tekhex_flavour,
+  bfd_target_srec_flavour,
+  bfd_target_ihex_flavour,
+  bfd_target_som_flavour,
+  bfd_target_os9k_flavour,
+  bfd_target_versados_flavour,
+  bfd_target_msdos_flavour,
+  bfd_target_evax_flavour
+};
+
+enum bfd_endian { BFD_ENDIAN_BIG, BFD_ENDIAN_LITTLE, BFD_ENDIAN_UNKNOWN };
+
+enum bfd_architecture
+{
+  bfd_arch_unknown,    /* File arch not known */
+  bfd_arch_obscure,    /* Arch known, not one of these */
+  bfd_arch_m68k,       /* Motorola 68xxx */
+#define bfd_mach_m68000 1
+#define bfd_mach_m68008 2
+#define bfd_mach_m68010 3
+#define bfd_mach_m68020 4
+#define bfd_mach_m68030 5
+#define bfd_mach_m68040 6
+#define bfd_mach_m68060 7
+#define bfd_mach_cpu32  8
+#define bfd_mach_mcf5200  9
+#define bfd_mach_mcf5206e 10
+#define bfd_mach_mcf5307  11
+#define bfd_mach_mcf5407  12
+#define bfd_mach_mcf528x  13
+#define bfd_mach_mcfv4e   14
+#define bfd_mach_mcf521x   15
+#define bfd_mach_mcf5249   16
+#define bfd_mach_mcf547x   17
+#define bfd_mach_mcf548x   18
+  bfd_arch_vax,        /* DEC Vax */
+  bfd_arch_i960,       /* Intel 960 */
+     /* The order of the following is important.
+       lower number indicates a machine type that
+       only accepts a subset of the instructions
+       available to machines with higher numbers.
+       The exception is the "ca", which is
+       incompatible with all other machines except
+       "core". */
+
+#define bfd_mach_i960_core      1
+#define bfd_mach_i960_ka_sa     2
+#define bfd_mach_i960_kb_sb     3
+#define bfd_mach_i960_mc        4
+#define bfd_mach_i960_xa        5
+#define bfd_mach_i960_ca        6
+#define bfd_mach_i960_jx        7
+#define bfd_mach_i960_hx        8
+
+  bfd_arch_a29k,       /* AMD 29000 */
+  bfd_arch_sparc,      /* SPARC */
+#define bfd_mach_sparc                 1
+/* The difference between v8plus and v9 is that v9 is a true 64 bit env.  */
+#define bfd_mach_sparc_sparclet        2
+#define bfd_mach_sparc_sparclite       3
+#define bfd_mach_sparc_v8plus          4
+#define bfd_mach_sparc_v8plusa         5 /* with ultrasparc add'ns.  */
+#define bfd_mach_sparc_sparclite_le    6
+#define bfd_mach_sparc_v9              7
+#define bfd_mach_sparc_v9a             8 /* with ultrasparc add'ns.  */
+#define bfd_mach_sparc_v8plusb         9 /* with cheetah add'ns.  */
+#define bfd_mach_sparc_v9b             10 /* with cheetah add'ns.  */
+/* Nonzero if MACH has the v9 instruction set.  */
+#define bfd_mach_sparc_v9_p(mach) \
+  ((mach) >= bfd_mach_sparc_v8plus && (mach) <= bfd_mach_sparc_v9b \
+   && (mach) != bfd_mach_sparc_sparclite_le)
+  bfd_arch_mips,       /* MIPS Rxxxx */
+#define bfd_mach_mips3000              3000
+#define bfd_mach_mips3900              3900
+#define bfd_mach_mips4000              4000
+#define bfd_mach_mips4010              4010
+#define bfd_mach_mips4100              4100
+#define bfd_mach_mips4300              4300
+#define bfd_mach_mips4400              4400
+#define bfd_mach_mips4600              4600
+#define bfd_mach_mips4650              4650
+#define bfd_mach_mips5000              5000
+#define bfd_mach_mips6000              6000
+#define bfd_mach_mips8000              8000
+#define bfd_mach_mips10000             10000
+#define bfd_mach_mips16                16
+  bfd_arch_i386,       /* Intel 386 */
+#define bfd_mach_i386_i386 0
+#define bfd_mach_i386_i8086 1
+#define bfd_mach_i386_i386_intel_syntax 2
+#define bfd_mach_x86_64 3
+#define bfd_mach_x86_64_intel_syntax 4
+  bfd_arch_we32k,      /* AT&T WE32xxx */
+  bfd_arch_tahoe,      /* CCI/Harris Tahoe */
+  bfd_arch_i860,       /* Intel 860 */
+  bfd_arch_romp,       /* IBM ROMP PC/RT */
+  bfd_arch_alliant,    /* Alliant */
+  bfd_arch_convex,     /* Convex */
+  bfd_arch_m88k,       /* Motorola 88xxx */
+  bfd_arch_pyramid,    /* Pyramid Technology */
+  bfd_arch_h8300,      /* Hitachi H8/300 */
+#define bfd_mach_h8300   1
+#define bfd_mach_h8300h  2
+#define bfd_mach_h8300s  3
+  bfd_arch_powerpc,    /* PowerPC */
+#define bfd_mach_ppc           0
+#define bfd_mach_ppc64         1
+#define bfd_mach_ppc_403       403
+#define bfd_mach_ppc_403gc     4030
+#define bfd_mach_ppc_505       505
+#define bfd_mach_ppc_601       601
+#define bfd_mach_ppc_602       602
+#define bfd_mach_ppc_603       603
+#define bfd_mach_ppc_ec603e    6031
+#define bfd_mach_ppc_604       604
+#define bfd_mach_ppc_620       620
+#define bfd_mach_ppc_630       630
+#define bfd_mach_ppc_750       750
+#define bfd_mach_ppc_860       860
+#define bfd_mach_ppc_a35       35
+#define bfd_mach_ppc_rs64ii    642
+#define bfd_mach_ppc_rs64iii   643
+#define bfd_mach_ppc_7400      7400
+  bfd_arch_rs6000,     /* IBM RS/6000 */
+  bfd_arch_hppa,       /* HP PA RISC */
+#define bfd_mach_hppa10        10
+#define bfd_mach_hppa11        11
+#define bfd_mach_hppa20        20
+#define bfd_mach_hppa20w       25
+  bfd_arch_d10v,       /* Mitsubishi D10V */
+  bfd_arch_z8k,        /* Zilog Z8000 */
+#define bfd_mach_z8001         1
+#define bfd_mach_z8002         2
+  bfd_arch_h8500,      /* Hitachi H8/500 */
+  bfd_arch_sh,         /* Hitachi SH */
+#define bfd_mach_sh            1
+#define bfd_mach_sh2        0x20
+#define bfd_mach_sh_dsp     0x2d
+#define bfd_mach_sh2a       0x2a
+#define bfd_mach_sh2a_nofpu 0x2b
+#define bfd_mach_sh2e       0x2e
+#define bfd_mach_sh3        0x30
+#define bfd_mach_sh3_nommu  0x31
+#define bfd_mach_sh3_dsp    0x3d
+#define bfd_mach_sh3e       0x3e
+#define bfd_mach_sh4        0x40
+#define bfd_mach_sh4_nofpu  0x41
+#define bfd_mach_sh4_nommu_nofpu  0x42
+#define bfd_mach_sh4a       0x4a
+#define bfd_mach_sh4a_nofpu 0x4b
+#define bfd_mach_sh4al_dsp  0x4d
+#define bfd_mach_sh5        0x50
+  bfd_arch_alpha,      /* Dec Alpha */
+#define bfd_mach_alpha 1
+  bfd_arch_arm,        /* Advanced Risc Machines ARM */
+#define bfd_mach_arm_unknown	0
+#define bfd_mach_arm_2		1
+#define bfd_mach_arm_2a		2
+#define bfd_mach_arm_3		3
+#define bfd_mach_arm_3M 	4
+#define bfd_mach_arm_4 		5
+#define bfd_mach_arm_4T 	6
+#define bfd_mach_arm_5 		7
+#define bfd_mach_arm_5T		8
+#define bfd_mach_arm_5TE	9
+#define bfd_mach_arm_XScale	10
+#define bfd_mach_arm_ep9312	11
+#define bfd_mach_arm_iWMMXt	12
+#define bfd_mach_arm_iWMMXt2	13
+  bfd_arch_ns32k,      /* National Semiconductors ns32000 */
+  bfd_arch_w65,        /* WDC 65816 */
+  bfd_arch_tic30,      /* Texas Instruments TMS320C30 */
+  bfd_arch_v850,       /* NEC V850 */
+#define bfd_mach_v850          0
+  bfd_arch_arc,        /* Argonaut RISC Core */
+#define bfd_mach_arc_base 0
+  bfd_arch_m32r,       /* Mitsubishi M32R/D */
+#define bfd_mach_m32r          0  /* backwards compatibility */
+  bfd_arch_mn10200,    /* Matsushita MN10200 */
+  bfd_arch_mn10300,    /* Matsushita MN10300 */
+  bfd_arch_cris,       /* Axis CRIS */
+#define bfd_mach_cris_v0_v10   255
+#define bfd_mach_cris_v32      32
+#define bfd_mach_cris_v10_v32  1032
+  bfd_arch_last
+  };
+#define bfd_mach_s390_31 31
+#define bfd_mach_s390_64 64
+
+typedef struct symbol_cache_entry
+{
+    const char *name;
+    union
+    {
+        PTR p;
+        bfd_vma i;
+    } udata;
+} asymbol;
+
+typedef int (*fprintf_ftype) PARAMS((FILE*, const char*, ...));
+
+enum dis_insn_type {
+  dis_noninsn,			/* Not a valid instruction */
+  dis_nonbranch,		/* Not a branch instruction */
+  dis_branch,			/* Unconditional branch */
+  dis_condbranch,		/* Conditional branch */
+  dis_jsr,			/* Jump to subroutine */
+  dis_condjsr,			/* Conditional jump to subroutine */
+  dis_dref,			/* Data reference instruction */
+  dis_dref2			/* Two data references in instruction */
+};
+
+/* This struct is passed into the instruction decoding routine,
+   and is passed back out into each callback.  The various fields are used
+   for conveying information from your main routine into your callbacks,
+   for passing information into the instruction decoders (such as the
+   addresses of the callback functions), or for passing information
+   back from the instruction decoders to their callers.
+
+   It must be initialized before it is first passed; this can be done
+   by hand, or using one of the initialization macros below.  */
+
+typedef struct disassemble_info {
+  fprintf_ftype fprintf_func;
+  FILE *stream;
+  PTR application_data;
+
+  /* Target description.  We could replace this with a pointer to the bfd,
+     but that would require one.  There currently isn't any such requirement
+     so to avoid introducing one we record these explicitly.  */
+  /* The bfd_flavour.  This can be bfd_target_unknown_flavour.  */
+  enum bfd_flavour flavour;
+  /* The bfd_arch value.  */
+  enum bfd_architecture arch;
+  /* The bfd_mach value.  */
+  unsigned long mach;
+  /* Endianness (for bi-endian cpus).  Mono-endian cpus can ignore this.  */
+  enum bfd_endian endian;
+
+  /* An array of pointers to symbols either at the location being disassembled
+     or at the start of the function being disassembled.  The array is sorted
+     so that the first symbol is intended to be the one used.  The others are
+     present for any misc. purposes.  This is not set reliably, but if it is
+     not NULL, it is correct.  */
+  asymbol **symbols;
+  /* Number of symbols in array.  */
+  int num_symbols;
+
+  /* For use by the disassembler.
+     The top 16 bits are reserved for public use (and are documented here).
+     The bottom 16 bits are for the internal use of the disassembler.  */
+  unsigned long flags;
+#define INSN_HAS_RELOC	0x80000000
+  PTR private_data;
+
+  /* Function used to get bytes to disassemble.  MEMADDR is the
+     address of the stuff to be disassembled, MYADDR is the address to
+     put the bytes in, and LENGTH is the number of bytes to read.
+     INFO is a pointer to this struct.
+     Returns an errno value or 0 for success.  */
+  int (*read_memory_func)
+    PARAMS ((bfd_vma memaddr, bfd_byte *myaddr, int length,
+	     struct disassemble_info *info));
+
+  /* Function which should be called if we get an error that we can't
+     recover from.  STATUS is the errno value from read_memory_func and
+     MEMADDR is the address that we were trying to read.  INFO is a
+     pointer to this struct.  */
+  void (*memory_error_func)
+    PARAMS ((int status, bfd_vma memaddr, struct disassemble_info *info));
+
+  /* Function called to print ADDR.  */
+  void (*print_address_func)
+    PARAMS ((bfd_vma addr, struct disassemble_info *info));
+
+  /* Function called to determine if there is a symbol at the given ADDR.
+     If there is, the function returns 1, otherwise it returns 0.
+     This is used by ports which support an overlay manager where
+     the overlay number is held in the top part of an address.  In
+     some circumstances we want to include the overlay number in the
+     address, (normally because there is a symbol associated with
+     that address), but sometimes we want to mask out the overlay bits.  */
+  int (* symbol_at_address_func)
+    PARAMS ((bfd_vma addr, struct disassemble_info * info));
+
+  /* These are for buffer_read_memory.  */
+  bfd_byte *buffer;
+  bfd_vma buffer_vma;
+  int buffer_length;
+
+  /* This variable may be set by the instruction decoder.  It suggests
+      the number of bytes objdump should display on a single line.  If
+      the instruction decoder sets this, it should always set it to
+      the same value in order to get reasonable looking output.  */
+  int bytes_per_line;
+
+  /* the next two variables control the way objdump displays the raw data */
+  /* For example, if bytes_per_line is 8 and bytes_per_chunk is 4, the */
+  /* output will look like this:
+     00:   00000000 00000000
+     with the chunks displayed according to "display_endian". */
+  int bytes_per_chunk;
+  enum bfd_endian display_endian;
+
+  /* Results from instruction decoders.  Not all decoders yet support
+     this information.  This info is set each time an instruction is
+     decoded, and is only valid for the last such instruction.
+
+     To determine whether this decoder supports this information, set
+     insn_info_valid to 0, decode an instruction, then check it.  */
+
+  char insn_info_valid;		/* Branch info has been set. */
+  char branch_delay_insns;	/* How many sequential insn's will run before
+				   a branch takes effect.  (0 = normal) */
+  char data_size;		/* Size of data reference in insn, in bytes */
+  enum dis_insn_type insn_type;	/* Type of instruction */
+  bfd_vma target;		/* Target address of branch or dref, if known;
+				   zero if unknown.  */
+  bfd_vma target2;		/* Second target address for dref2 */
+
+  /* Command line options specific to the target disassembler.  */
+  char * disassembler_options;
+
+} disassemble_info;
+
+
+/* Standard disassemblers.  Disassemble one instruction at the given
+   target address.  Return number of bytes processed.  */
+typedef int (*disassembler_ftype)
+     PARAMS((bfd_vma, disassemble_info *));
+
+extern int print_insn_big_mips		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_mips	PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_i386		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m68k		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z8001		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_z8002		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300h		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8300s		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_h8500		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_alpha		PARAMS ((bfd_vma, disassemble_info*));
+extern disassembler_ftype arc_get_disassembler PARAMS ((int, int));
+extern int print_insn_arm		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_sparc		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_big_a29k		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_a29k	PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_i960		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_sh		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_shl		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_hppa		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m32r		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_m88k		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_mn10200		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_mn10300		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_ns32k		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_big_powerpc	PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_little_powerpc	PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_rs6000		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_w65		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_d10v		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_v850		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_tic30		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_ppc		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_s390		PARAMS ((bfd_vma, disassemble_info*));
+extern int print_insn_crisv32           PARAMS ((bfd_vma, disassemble_info*));
+
+#if 0
+/* Fetch the disassembler for a given BFD, if that support is available.  */
+extern disassembler_ftype disassembler	PARAMS ((bfd *));
+#endif
+
+
+/* This block of definitions is for particular callers who read instructions
+   into a buffer before calling the instruction decoder.  */
+
+/* Here is a function which callers may wish to use for read_memory_func.
+   It gets bytes from a buffer.  */
+extern int buffer_read_memory
+  PARAMS ((bfd_vma, bfd_byte *, int, struct disassemble_info *));
+
+/* This function goes with buffer_read_memory.
+   It prints a message using info->fprintf_func and info->stream.  */
+extern void perror_memory PARAMS ((int, bfd_vma, struct disassemble_info *));
+
+
+/* Just print the address in hex.  This is included for completeness even
+   though both GDB and objdump provide their own (to print symbolic
+   addresses).  */
+extern void generic_print_address
+  PARAMS ((bfd_vma, struct disassemble_info *));
+
+/* Always true.  */
+extern int generic_symbol_at_address
+  PARAMS ((bfd_vma, struct disassemble_info *));
+
+/* Macro to initialize a disassemble_info struct.  This should be called
+   by all applications creating such a struct.  */
+#define INIT_DISASSEMBLE_INFO(INFO, STREAM, FPRINTF_FUNC) \
+  (INFO).flavour = bfd_target_unknown_flavour, \
+  (INFO).arch = bfd_arch_unknown, \
+  (INFO).mach = 0, \
+  (INFO).endian = BFD_ENDIAN_UNKNOWN, \
+  INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC)
+
+/* Call this macro to initialize only the internal variables for the
+   disassembler.  Architecture dependent things such as byte order, or machine
+   variant are not touched by this macro.  This makes things much easier for
+   GDB which must initialize these things separately.  */
+
+#define INIT_DISASSEMBLE_INFO_NO_ARCH(INFO, STREAM, FPRINTF_FUNC) \
+  (INFO).fprintf_func = (FPRINTF_FUNC), \
+  (INFO).stream = (STREAM), \
+  (INFO).symbols = NULL, \
+  (INFO).num_symbols = 0, \
+  (INFO).private_data = NULL, \
+  (INFO).buffer = NULL, \
+  (INFO).buffer_vma = 0, \
+  (INFO).buffer_length = 0, \
+  (INFO).read_memory_func = buffer_read_memory, \
+  (INFO).memory_error_func = perror_memory, \
+  (INFO).print_address_func = generic_print_address, \
+  (INFO).symbol_at_address_func = generic_symbol_at_address, \
+  (INFO).flags = 0, \
+  (INFO).bytes_per_line = 0, \
+  (INFO).bytes_per_chunk = 0, \
+  (INFO).display_endian = BFD_ENDIAN_UNKNOWN, \
+  (INFO).disassembler_options = NULL, \
+  (INFO).insn_info_valid = 0
+
+#define _(x) x
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+
+/* from libbfd */
+
+bfd_vma bfd_getl32 (const bfd_byte *addr);
+bfd_vma bfd_getb32 (const bfd_byte *addr);
+bfd_vma bfd_getl16 (const bfd_byte *addr);
+bfd_vma bfd_getb16 (const bfd_byte *addr);
+typedef enum bfd_boolean {false, true} boolean;
+typedef boolean bfd_boolean;
+
+#endif /* ! defined (DIS_ASM_H) */
diff --git a/disas.c b/disas.c
new file mode 100644
index 0000000..a8bc8ad
--- /dev/null
+++ b/disas.c
@@ -0,0 +1,431 @@
+/* General "disassemble this chunk" code.  Used for debugging. */
+#include "config.h"
+#include "dis-asm.h"
+#include "elf.h"
+#include <errno.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+/* Filled in by elfload.c.  Simplistic, but will do for now. */
+struct syminfo *syminfos = NULL;
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+   Transfer them to myaddr.  */
+int
+buffer_read_memory (memaddr, myaddr, length, info)
+     bfd_vma memaddr;
+     bfd_byte *myaddr;
+     int length;
+     struct disassemble_info *info;
+{
+    if (memaddr < info->buffer_vma
+        || memaddr + length > info->buffer_vma + info->buffer_length)
+        /* Out of bounds.  Use EIO because GDB uses it.  */
+        return EIO;
+    memcpy (myaddr, info->buffer + (memaddr - info->buffer_vma), length);
+    return 0;
+}
+
+/* Get LENGTH bytes from info's buffer, at target address memaddr.
+   Transfer them to myaddr.  */
+static int
+target_read_memory (bfd_vma memaddr,
+                    bfd_byte *myaddr,
+                    int length,
+                    struct disassemble_info *info)
+{
+    int i;
+    for(i = 0; i < length; i++) {
+        myaddr[i] = ldub_code(memaddr + i);
+    }
+    return 0;
+}
+
+/* Print an error message.  We can assume that this is in response to
+   an error return from buffer_read_memory.  */
+void
+perror_memory (status, memaddr, info)
+     int status;
+     bfd_vma memaddr;
+     struct disassemble_info *info;
+{
+  if (status != EIO)
+    /* Can't happen.  */
+    (*info->fprintf_func) (info->stream, "Unknown error %d\n", status);
+  else
+    /* Actually, address between memaddr and memaddr + len was
+       out of bounds.  */
+    (*info->fprintf_func) (info->stream,
+			   "Address 0x%" PRIx64 " is out of bounds.\n", memaddr);
+}
+
+/* This could be in a separate file, to save miniscule amounts of space
+   in statically linked executables.  */
+
+/* Just print the address is hex.  This is included for completeness even
+   though both GDB and objdump provide their own (to print symbolic
+   addresses).  */
+
+void
+generic_print_address (addr, info)
+     bfd_vma addr;
+     struct disassemble_info *info;
+{
+    (*info->fprintf_func) (info->stream, "0x%" PRIx64, addr);
+}
+
+/* Just return the given address.  */
+
+int
+generic_symbol_at_address (addr, info)
+     bfd_vma addr;
+     struct disassemble_info * info;
+{
+  return 1;
+}
+
+bfd_vma bfd_getl32 (const bfd_byte *addr)
+{
+  unsigned long v;
+
+  v = (unsigned long) addr[0];
+  v |= (unsigned long) addr[1] << 8;
+  v |= (unsigned long) addr[2] << 16;
+  v |= (unsigned long) addr[3] << 24;
+  return (bfd_vma) v;
+}
+
+bfd_vma bfd_getb32 (const bfd_byte *addr)
+{
+  unsigned long v;
+
+  v = (unsigned long) addr[0] << 24;
+  v |= (unsigned long) addr[1] << 16;
+  v |= (unsigned long) addr[2] << 8;
+  v |= (unsigned long) addr[3];
+  return (bfd_vma) v;
+}
+
+bfd_vma bfd_getl16 (const bfd_byte *addr)
+{
+  unsigned long v;
+
+  v = (unsigned long) addr[0];
+  v |= (unsigned long) addr[1] << 8;
+  return (bfd_vma) v;
+}
+
+bfd_vma bfd_getb16 (const bfd_byte *addr)
+{
+  unsigned long v;
+
+  v = (unsigned long) addr[0] << 24;
+  v |= (unsigned long) addr[1] << 16;
+  return (bfd_vma) v;
+}
+
+#ifdef TARGET_ARM
+static int
+print_insn_thumb1(bfd_vma pc, disassemble_info *info)
+{
+  return print_insn_arm(pc | 1, info);
+}
+#endif
+
+/* Disassemble this for me please... (debugging). 'flags' has the following
+   values:
+    i386 - nonzero means 16 bit code
+    arm  - nonzero means thumb code
+    ppc  - nonzero means little endian
+    other targets - unused
+ */
+void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
+{
+    target_ulong pc;
+    int count;
+    struct disassemble_info disasm_info;
+    int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+    INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
+
+    disasm_info.read_memory_func = target_read_memory;
+    disasm_info.buffer_vma = code;
+    disasm_info.buffer_length = size;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+    disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(TARGET_I386)
+    if (flags == 2)
+        disasm_info.mach = bfd_mach_x86_64;
+    else if (flags == 1)
+        disasm_info.mach = bfd_mach_i386_i8086;
+    else
+        disasm_info.mach = bfd_mach_i386_i386;
+    print_insn = print_insn_i386;
+#elif defined(TARGET_ARM)
+    if (flags)
+	print_insn = print_insn_thumb1;
+    else
+	print_insn = print_insn_arm;
+#elif defined(TARGET_SPARC)
+    print_insn = print_insn_sparc;
+#ifdef TARGET_SPARC64
+    disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(TARGET_PPC)
+    if (flags >> 16)
+        disasm_info.endian = BFD_ENDIAN_LITTLE;
+    if (flags & 0xFFFF) {
+        /* If we have a precise definitions of the instructions set, use it */
+        disasm_info.mach = flags & 0xFFFF;
+    } else {
+#ifdef TARGET_PPC64
+        disasm_info.mach = bfd_mach_ppc64;
+#else
+        disasm_info.mach = bfd_mach_ppc;
+#endif
+    }
+    print_insn = print_insn_ppc;
+#elif defined(TARGET_M68K)
+    print_insn = print_insn_m68k;
+#elif defined(TARGET_MIPS)
+#ifdef TARGET_WORDS_BIGENDIAN
+    print_insn = print_insn_big_mips;
+#else
+    print_insn = print_insn_little_mips;
+#endif
+#elif defined(TARGET_SH4)
+    disasm_info.mach = bfd_mach_sh4;
+    print_insn = print_insn_sh;
+#elif defined(TARGET_ALPHA)
+    disasm_info.mach = bfd_mach_alpha;
+    print_insn = print_insn_alpha;
+#elif defined(TARGET_CRIS)
+    disasm_info.mach = bfd_mach_cris_v32;
+    print_insn = print_insn_crisv32;
+#else
+    fprintf(out, "0x" TARGET_FMT_lx
+	    ": Asm output not supported on this arch\n", code);
+    return;
+#endif
+
+    for (pc = code; pc < code + size; pc += count) {
+	fprintf(out, "0x" TARGET_FMT_lx ":  ", pc);
+	count = print_insn(pc, &disasm_info);
+#if 0
+        {
+            int i;
+            uint8_t b;
+            fprintf(out, " {");
+            for(i = 0; i < count; i++) {
+                target_read_memory(pc + i, &b, 1, &disasm_info);
+                fprintf(out, " %02x", b);
+            }
+            fprintf(out, " }");
+        }
+#endif
+	fprintf(out, "\n");
+	if (count < 0)
+	    break;
+    }
+}
+
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, void *code, unsigned long size)
+{
+    unsigned long pc;
+    int count;
+    struct disassemble_info disasm_info;
+    int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+    INIT_DISASSEMBLE_INFO(disasm_info, out, fprintf);
+
+    disasm_info.buffer = code;
+    disasm_info.buffer_vma = (unsigned long)code;
+    disasm_info.buffer_length = size;
+
+#ifdef WORDS_BIGENDIAN
+    disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+    disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(__i386__)
+    disasm_info.mach = bfd_mach_i386_i386;
+    print_insn = print_insn_i386;
+#elif defined(__x86_64__)
+    disasm_info.mach = bfd_mach_x86_64;
+    print_insn = print_insn_i386;
+#elif defined(__powerpc__)
+    print_insn = print_insn_ppc;
+#elif defined(__alpha__)
+    print_insn = print_insn_alpha;
+#elif defined(__sparc__)
+    print_insn = print_insn_sparc;
+#if defined(__sparc_v8plus__) || defined(__sparc_v8plusa__) || defined(__sparc_v9__)
+    disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(__arm__)
+    print_insn = print_insn_arm;
+#elif defined(__MIPSEB__)
+    print_insn = print_insn_big_mips;
+#elif defined(__MIPSEL__)
+    print_insn = print_insn_little_mips;
+#elif defined(__m68k__)
+    print_insn = print_insn_m68k;
+#elif defined(__s390__)
+    print_insn = print_insn_s390;
+#elif defined(__hppa__)
+    print_insn = print_insn_hppa;
+#else
+    fprintf(out, "0x%lx: Asm output not supported on this arch\n",
+	    (long) code);
+    return;
+#endif
+    for (pc = (unsigned long)code; pc < (unsigned long)code + size; pc += count) {
+	fprintf(out, "0x%08lx:  ", pc);
+#ifdef __arm__
+        /* since data is included in the code, it is better to
+           display code data too */
+        fprintf(out, "%08x  ", (int)bfd_getl32((const bfd_byte *)pc));
+#endif
+	count = print_insn(pc, &disasm_info);
+	fprintf(out, "\n");
+	if (count < 0)
+	    break;
+    }
+}
+
+/* Look up symbol for debugging purpose.  Returns "" if unknown. */
+const char *lookup_symbol(target_ulong orig_addr)
+{
+    unsigned int i;
+    /* Hack, because we know this is x86. */
+    Elf32_Sym *sym;
+    struct syminfo *s;
+    target_ulong addr;
+
+    for (s = syminfos; s; s = s->next) {
+	sym = s->disas_symtab;
+	for (i = 0; i < s->disas_num_syms; i++) {
+	    if (sym[i].st_shndx == SHN_UNDEF
+		|| sym[i].st_shndx >= SHN_LORESERVE)
+		continue;
+
+	    if (ELF_ST_TYPE(sym[i].st_info) != STT_FUNC)
+		continue;
+
+	    addr = sym[i].st_value;
+#if defined(TARGET_ARM) || defined (TARGET_MIPS)
+            /* The bottom address bit marks a Thumb or MIPS16 symbol.  */
+            addr &= ~(target_ulong)1;
+#endif
+	    if (orig_addr >= addr
+		&& orig_addr < addr + sym[i].st_size)
+		return s->disas_strtab + sym[i].st_name;
+	}
+    }
+    return "";
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+void term_vprintf(const char *fmt, va_list ap);
+void term_printf(const char *fmt, ...);
+
+static int monitor_disas_is_physical;
+static CPUState *monitor_disas_env;
+
+static int
+monitor_read_memory (bfd_vma memaddr, bfd_byte *myaddr, int length,
+                     struct disassemble_info *info)
+{
+    if (monitor_disas_is_physical) {
+        cpu_physical_memory_rw(memaddr, myaddr, length, 0);
+    } else {
+        cpu_memory_rw_debug(monitor_disas_env, memaddr,myaddr, length, 0);
+    }
+    return 0;
+}
+
+static int monitor_fprintf(FILE *stream, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    term_vprintf(fmt, ap);
+    va_end(ap);
+    return 0;
+}
+
+void monitor_disas(CPUState *env,
+                   target_ulong pc, int nb_insn, int is_physical, int flags)
+{
+    int count, i;
+    struct disassemble_info disasm_info;
+    int (*print_insn)(bfd_vma pc, disassemble_info *info);
+
+    INIT_DISASSEMBLE_INFO(disasm_info, NULL, monitor_fprintf);
+
+    monitor_disas_env = env;
+    monitor_disas_is_physical = is_physical;
+    disasm_info.read_memory_func = monitor_read_memory;
+
+    disasm_info.buffer_vma = pc;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    disasm_info.endian = BFD_ENDIAN_BIG;
+#else
+    disasm_info.endian = BFD_ENDIAN_LITTLE;
+#endif
+#if defined(TARGET_I386)
+    if (flags == 2)
+        disasm_info.mach = bfd_mach_x86_64;
+    else if (flags == 1)
+        disasm_info.mach = bfd_mach_i386_i8086;
+    else
+        disasm_info.mach = bfd_mach_i386_i386;
+    print_insn = print_insn_i386;
+#elif defined(TARGET_ARM)
+    print_insn = print_insn_arm;
+#elif defined(TARGET_ALPHA)
+    print_insn = print_insn_alpha;
+#elif defined(TARGET_SPARC)
+    print_insn = print_insn_sparc;
+#ifdef TARGET_SPARC64
+    disasm_info.mach = bfd_mach_sparc_v9b;
+#endif
+#elif defined(TARGET_PPC)
+#ifdef TARGET_PPC64
+    disasm_info.mach = bfd_mach_ppc64;
+#else
+    disasm_info.mach = bfd_mach_ppc;
+#endif
+    print_insn = print_insn_ppc;
+#elif defined(TARGET_M68K)
+    print_insn = print_insn_m68k;
+#elif defined(TARGET_MIPS)
+#ifdef TARGET_WORDS_BIGENDIAN
+    print_insn = print_insn_big_mips;
+#else
+    print_insn = print_insn_little_mips;
+#endif
+#else
+    term_printf("0x" TARGET_FMT_lx
+		": Asm output not supported on this arch\n", pc);
+    return;
+#endif
+
+    for(i = 0; i < nb_insn; i++) {
+	term_printf("0x" TARGET_FMT_lx ":  ", pc);
+	count = print_insn(pc, &disasm_info);
+	term_printf("\n");
+	if (count < 0)
+	    break;
+        pc += count;
+    }
+}
+#endif
diff --git a/disas.h b/disas.h
new file mode 100644
index 0000000..ee0a79c
--- /dev/null
+++ b/disas.h
@@ -0,0 +1,21 @@
+#ifndef _QEMU_DISAS_H
+#define _QEMU_DISAS_H
+
+/* Disassemble this for me please... (debugging). */
+void disas(FILE *out, void *code, unsigned long size);
+void target_disas(FILE *out, target_ulong code, target_ulong size, int flags);
+void monitor_disas(CPUState *env,
+                   target_ulong pc, int nb_insn, int is_physical, int flags);
+
+/* Look up symbol for debugging purpose.  Returns "" if unknown. */
+const char *lookup_symbol(target_ulong orig_addr);
+
+/* Filled in by elfload.c.  Simplistic, but will do for now. */
+extern struct syminfo {
+    unsigned int disas_num_syms;
+    void *disas_symtab;
+    const char *disas_strtab;
+    struct syminfo *next;
+} *syminfos;
+
+#endif /* _QEMU_DISAS_H */
diff --git a/distrib/Makefile b/distrib/Makefile
new file mode 100644
index 0000000..677b0bb
--- /dev/null
+++ b/distrib/Makefile
@@ -0,0 +1,8 @@
+ZLIB_VERSION   := zlib-1.2.3
+LIBPNG_VERSION := libpng-1.2.19
+
+ZLIB_DIR    = $(SRC_PATH)/distrib/$(ZLIB_VERSION)
+LIBPNG_DIR := $(SRC_PATH)/distrib/$(LIBPNG_VERSION)
+
+include $(ZLIB_DIR)/Makefile
+include $(LIBPNG_DIR)/Makefile
diff --git a/distrib/README b/distrib/README
new file mode 100644
index 0000000..8a2cf52
--- /dev/null
+++ b/distrib/README
@@ -0,0 +1,50 @@
+This is source release of the Android emulator. simply run the "build-emulator.sh" script to
+generate a statically linked "emulator" binary in the current directory.
+
+you can also use the "--target=<path>" option to install the executable into a different location,
+
+At the moment, only Linux and Mac OS X are supported.
+
+This emulator is probably not usable without other support files provided by the Android project,
+like a specific kernel image, ramdisk, system and user disk images. Please go to the Android web
+site for more details.
+
+This emulator is licensed under the GNU General Public License (GPL) version 2, which can be
+found in the file "qemu/COPYING".
+
+it is based on QEMU 0.8.2 with many changes used to support the following features:
+
+  - additionnal hardware support for some Android reference boards.
+
+  - various OS-X related patches to make everything compile cleanly with GCC 4.1 and
+    beyond. this includes better support for the Mach-O binary format
+
+  - support for instruction-level profiling and data cache simulation. this allows the
+    emulator to generate "profile" files that can later be analyzed with external tools
+    to provide accurate information about what's happening in the system
+
+  - changes in the dynamic code generators, mainly to support concurrent generators in
+    a single binary (this allows us to use different generators for profiling and
+    non-profiling modes, and switch between them dynamically at runtime when needed)
+
+  - support for network throttling and latency simulation, used to better emulate the
+    network conditions of radio networks.
+
+  - a new graphical user interface capable of displaying and rotating "device skins"
+
+  - an optional (and disabled by default) "polling" runtime mode that doesn't use
+    SIGALRM signals to implement timers. this makes for much better timing accuracy
+    when using "old" emukated Linux kernels, at the cost of using 100% CPU, even when
+    the guest system is idle. This is now disabled since Linux 2.6.21 and beyond use
+    "dynamic ticks" that make this mode un-necessary for Android.
+
+
+it also uses a patched version of LibSDL-1.2.12 which implements the following:
+
+  - prevent a fatal bug in Quartz Extreme's QuickDraw emulation to crash the program
+    whenever SDL_WINDOW_POS is set in the environment before starting the program.
+    the patch implements a simple workaround to this system-level problem.
+
+  - new APIs: SDL_WM_GetPos() and SDL_WM_SetPos() are used to retrieve and set the emulator
+    window position. this allows us to implement a simple-yet-useful feature: the emulator remembers
+    its position among restarts.
diff --git a/distrib/build-emulator.sh b/distrib/build-emulator.sh
new file mode 100755
index 0000000..b2722cf
--- /dev/null
+++ b/distrib/build-emulator.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+#  this script is used to build a static version of the Android emulator
+#  from our distribution package.
+#
+cd $(dirname $0)
+CURDIR=$(pwd)
+
+show_help=
+TARGET=emulator
+for opt; do
+  optarg=`expr "x$opt" : 'x[^=]*=\(.*\)'`
+  case "$opt" in
+  --help|-h|-\?) show_help=yes
+  ;;
+  --target=*) TARGET=$optarg
+  ;;
+  esac
+done
+
+if [ -n "$show_help" ] ; then
+    echo "usage: build-emulator [--target=FILEPATH]"
+    exit 1
+fi
+
+# directory where we'll place the temporary SDL binaries
+LOCAL=$CURDIR/local
+
+cd $CURDIR/sdl
+if ! (./android-configure --prefix=$LOCAL && make && make install); then
+    echo "ERROR: could not build SDL library, please check their sources"
+fi
+
+cd $CURDIR/qemu
+if ! (./android-rebuild.sh --sdl-config=$LOCAL/bin/sdl-config); then
+    echo "ERROR: could not build the emulator, please check the sources"
+fi
+
+cp objs/emulator $CURDIR/emulator
diff --git a/distrib/build_gcc_qemu_darwin.sh b/distrib/build_gcc_qemu_darwin.sh
new file mode 100755
index 0000000..4c82af4
--- /dev/null
+++ b/distrib/build_gcc_qemu_darwin.sh
@@ -0,0 +1,482 @@
+#!/bin/sh
+# APPLE LOCAL file B&I
+
+set -x
+
+# set BUILD_CPLUSPLUS to 'true' if you want to compile support for C++
+BUILD_CPLUSPLUS=false
+
+# set BUILD_DOCS to 'true' to build and install the documentation
+BUILD_DOCS=false
+
+# set BUILD_SYM to 'true' to build and install symbolic binaries
+BUILD_SYM=false
+
+# -arch arguments are different than configure arguments. We need to
+# translate them.
+
+TRANSLATE_ARCH="sed -e s/ppc/powerpc/ -e s/i386/i686/"
+
+# Build GCC the "Apple way".
+# Parameters:
+
+# The first parameter is a space-separated list of the architectures
+# the compilers will run on.  For instance, "ppc i386".  If the
+# current machine isn't in the list, it will (effectively) be added.
+#HOSTS=`echo $1 | $TRANSLATE_ARCH `
+HOSTS=i686
+
+# The second parameter is a space-separated list of the architectures the
+# compilers will generate code for.  If the current machine isn't in
+# the list, a compiler for it will get built anyway, but won't be
+# installed.
+#TARGETS=`echo $2 | $TRANSLATE_ARCH`
+TARGETS=i686
+
+# The GNU makefile target ('bootstrap' by default).
+BOOTSTRAP=${BOOTSTRAP-bootstrap}
+
+# The B&I build srcript (~rc/bin/buildit) accepts an '-othercflags'
+# command-line flag, and captures the argument to that flag in
+# $RC_NONARCH_CFLAGS (and mysteriously prepends '-pipe' thereto).
+# We will allow this to override the default $CFLAGS and $CXXFLAGS.
+
+CFLAGS="-g -O2 ${RC_NONARCH_CFLAGS/-pipe/}"
+
+# This isn't a parameter; it is the architecture of the current machine.
+BUILD=`arch | $TRANSLATE_ARCH`
+
+# The third parameter is the path to the compiler sources.  There should
+# be a shell script named 'configure' in this directory.  This script
+# makes a copy...
+#ORIG_SRC_DIR="$3"
+ORIG_SRC_DIR=`dirname $0`
+ORIG_SRC_DIR=`pwd`/$ORIG_SRC_DIR
+
+# The fourth parameter is the location where the compiler will be installed,
+# normally "/usr".  You can move it once it's built, so this mostly controls
+# the layout of $DEST_DIR.
+#DEST_ROOT="$4"
+DEST_ROOT=/
+
+# The fifth parameter is the place where the compiler will be copied once
+# it's built.
+#DEST_DIR="$5"
+DEST_DIR=/Volumes/Android/device/prebuilt/darwin-x86/gcc-qemu
+
+# The sixth parameter is a directory in which to place information (like
+# unstripped executables and generated source files) helpful in debugging
+# the resulting compiler.
+#SYM_DIR="$6"
+SYM_DIR=`pwd`/sym
+
+# The current working directory is where the build will happen.
+# It may already contain a partial result of an interrupted build,
+# in which case this script will continue where it left off.
+DIR=`pwd`
+
+# This isn't a parameter; it's the version of the compiler that we're
+# about to build.  It's included in the names of various files and
+# directories in the installed image.
+VERS=`sed -n -e '/version_string/s/.*\"\([^ \"]*\)[ \"].*/\1/p' \
+  < $ORIG_SRC_DIR/gcc/version.c || exit 1`
+
+# This isn't a parameter either, it's the major version of the compiler
+# to be built.  It's VERS but only up to the second '.' (if there is one).
+MAJ_VERS=`echo $VERS | sed 's/\([0-9]*\.[0-9]*\)[.-].*/\1/'`
+
+# This is the default architecture for i386 configurations.
+I386_CPU="--with-arch=pentium-m --with-tune=prescott"
+
+# This is the libstdc++ version to use.
+LIBSTDCXX_VERSION=4.0.0
+
+# Sniff to see if we can do ppc64 building.
+DARWIN_VERS=8
+if [ x"`file /usr/lib/crt1.o | grep 'architecture ppc64'`" == x ]; then
+    DARWIN_VERS=7
+fi
+
+echo DARWIN_VERS = $DARWIN_VERS
+
+########################################
+# Run the build.
+
+# Create the source tree we'll actually use to build, deleting
+# tcl since it doesn't actually build properly in a cross environment
+# and we don't really need it.
+SRC_DIR=$DIR/src
+rm -rf $SRC_DIR || exit 1
+mkdir $SRC_DIR || exit 1
+ln -s $ORIG_SRC_DIR/* $SRC_DIR/ || exit 1
+rm -rf $SRC_DIR/tcl $SRC_DIR/expect $SRC_DIR/dejagnu || exit 1
+# Also remove libstdc++ since it is built from a separate project.
+rm -rf $SRC_DIR/libstdc++-v3 || exit 1
+# Clean out old specs files
+rm -f /usr/lib/gcc/*/4.0.0/specs
+
+ENABLE_LANGUAGES="--enable-languages=c,objc"
+if [ $BUILD_CPLUSPLUS = true ] ; then
+    ENABLE_LANGUAGES="$ENABLE_LANGUAGES,c++,obj-c++"
+fi
+
+# These are the configure and build flags that are used.
+CONFIGFLAGS="--disable-checking -enable-werror \
+  --prefix=$DEST_ROOT \
+  --mandir=\${prefix}/share/man \
+  $ENABLE_LANGUAGES \
+  --program-transform-name=/^[cg][^.-]*$/s/$/-$MAJ_VERS/ \
+  --with-gxx-include-dir=\${prefix}/include/c++/$LIBSTDCXX_VERSION \
+  --with-slibdir=/usr/lib \
+  --build=$BUILD-apple-darwin$DARWIN_VERS
+  --disable-nls"
+
+# Figure out how many make processes to run.
+SYSCTL=`sysctl -n hw.activecpu`
+
+# hw.activecpu only available in 10.2.6 and later
+if [ -z "$SYSCTL" ]; then
+  SYSCTL=`sysctl -n hw.ncpu`
+fi
+
+# sysctl -n hw.* does not work when invoked via B&I chroot /BuildRoot. 
+# Builders can default to 2, since even if they are single processor, 
+# nothing else is running on the machine.
+if [ -z "$SYSCTL" ]; then
+  SYSCTL=2
+fi
+
+# The $LOCAL_MAKEFLAGS variable can be used to override $MAKEFLAGS.
+MAKEFLAGS=${LOCAL_MAKEFLAGS-"-j $SYSCTL"}
+
+# Build the native GCC.  Do this even if the user didn't ask for it
+# because it'll be needed for the bootstrap.
+mkdir -p $DIR/obj-$BUILD-$BUILD $DIR/dst-$BUILD-$BUILD || exit 1
+cd $DIR/obj-$BUILD-$BUILD || exit 1
+if [ \! -f Makefile ]; then
+ $SRC_DIR/configure $CONFIGFLAGS \
+   `if [ $BUILD = i686 ] ; then echo $I386_CPU ; fi` \
+   --host=$BUILD-apple-darwin$DARWIN_VERS --target=$BUILD-apple-darwin$DARWIN_VERS || exit 1
+fi
+make $MAKEFLAGS $BOOTSTRAP CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+if [ $BUILD_DOCS = "true" ] ; then
+make $MAKEFLAGS html CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+fi
+make $MAKEFLAGS DESTDIR=$DIR/dst-$BUILD-$BUILD install-gcc install-target \
+  CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+
+# Add the compiler we just built to the path, giving it appropriate names.
+D=$DIR/dst-$BUILD-$BUILD/$DEST_ROOT/bin
+ln -f $D/gcc-$MAJ_VERS $D/gcc || exit 1
+ln -f $D/gcc $D/$BUILD-apple-darwin$DARWIN_VERS-gcc || exit 1
+PATH=$DIR/dst-$BUILD-$BUILD/$DEST_ROOT/bin:$PATH
+
+# The cross-tools' build process expects to find certain programs
+# under names like 'i386-apple-darwin$DARWIN_VERS-ar'; so make them.
+# Annoyingly, ranlib changes behaviour depending on what you call it,
+# so we have to use a shell script for indirection, grrr.
+rm -rf $DIR/bin || exit 1
+mkdir $DIR/bin || exit 1
+for prog in ar nm ranlib strip lipo ; do
+  for t in `echo $TARGETS $HOSTS | sort -u`; do
+    P=$DIR/bin/${t}-apple-darwin$DARWIN_VERS-${prog}
+    echo '#!/bin/sh' > $P || exit 1
+    echo 'exec /usr/bin/'${prog}' $*' >> $P || exit 1
+    chmod a+x $P || exit 1
+  done
+done
+for t in `echo $1 $2 | sort -u`; do
+  gt=`echo $t | $TRANSLATE_ARCH`
+  P=$DIR/bin/${gt}-apple-darwin$DARWIN_VERS-as
+  echo '#!/bin/sh' > $P || exit 1
+  echo 'exec /usr/bin/as -arch '${t}' $*' >> $P || exit 1
+  chmod a+x $P || exit 1
+done
+PATH=$DIR/bin:$PATH
+
+# Build the cross-compilers, using the compiler we just built.
+for t in $TARGETS ; do
+ if [ $t != $BUILD ] ; then
+  mkdir -p $DIR/obj-$BUILD-$t $DIR/dst-$BUILD-$t || exit 1
+   cd $DIR/obj-$BUILD-$t || exit 1
+   if [ \! -f Makefile ]; then
+    $SRC_DIR/configure $CONFIGFLAGS --enable-werror-always \
+     `if [ $t = i686 ] ; then echo $I386_CPU ; fi` \
+      --program-prefix=$t-apple-darwin$DARWIN_VERS- \
+      --host=$BUILD-apple-darwin$DARWIN_VERS --target=$t-apple-darwin$DARWIN_VERS || exit 1
+   fi
+   make $MAKEFLAGS all CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+   make $MAKEFLAGS DESTDIR=$DIR/dst-$BUILD-$t install-gcc install-target \
+     CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+
+    # Add the compiler we just built to the path.
+   PATH=$DIR/dst-$BUILD-$t/$DEST_ROOT/bin:$PATH
+ fi
+done
+
+# Rearrange various libraries, for no really good reason.
+for t in $TARGETS ; do
+  DT=$DIR/dst-$BUILD-$t
+  D=`echo $DT/$DEST_ROOT/lib/gcc/$t-apple-darwin$DARWIN_VERS/$VERS`
+  mv $D/static/libgcc.a $D/libgcc_static.a || exit 1
+  mv $D/kext/libgcc.a $D/libcc_kext.a || exit 1
+  rm -r $D/static $D/kext || exit 1
+done
+
+# Build the cross-hosted compilers.
+for h in $HOSTS ; do
+  if [ $h != $BUILD ] ; then
+    for t in $TARGETS ; do
+      mkdir -p $DIR/obj-$h-$t $DIR/dst-$h-$t || exit 1
+      cd $DIR/obj-$h-$t || exit 1
+      if [ $h = $t ] ; then
+	pp=
+      else
+	pp=$t-apple-darwin$DARWIN_VERS-
+      fi
+
+      if [ \! -f Makefile ]; then
+        $SRC_DIR/configure $CONFIGFLAGS \
+	  `if [ $t = i686 ] ; then echo $I386_CPU ; fi` \
+          --program-prefix=$pp \
+          --host=$h-apple-darwin$DARWIN_VERS --target=$t-apple-darwin$DARWIN_VERS || exit 1
+      fi
+      make $MAKEFLAGS all-gcc CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+      make $MAKEFLAGS DESTDIR=$DIR/dst-$h-$t install-gcc \
+        CFLAGS="$CFLAGS" CXXFLAGS="$CFLAGS" || exit 1
+    done
+  fi
+done
+
+########################################
+# Construct the actual destination root, by copying stuff from
+# $DIR/dst-* to $DEST_DIR, with occasional 'lipo' commands.
+
+cd $DEST_DIR || exit 1
+
+# Clean out DEST_DIR in case -noclean was passed to buildit.
+rm -rf * || exit 1
+
+if [ $BUILD_DOCS = "true" ] ; then
+# HTML documentation
+HTMLDIR="/Developer/ADC Reference Library/documentation/DeveloperTools"
+mkdir -p ".$HTMLDIR" || exit 1
+cp -Rp $DIR/obj-$BUILD-$BUILD/gcc/HTML/* ".$HTMLDIR/" || exit 1
+
+# Manual pages
+mkdir -p .$DEST_ROOT/share || exit 1
+cp -Rp $DIR/dst-$BUILD-$BUILD$DEST_ROOT/share/man .$DEST_ROOT/share/ \
+  || exit 1
+fi
+
+# libexec
+cd $DIR/dst-$BUILD-$BUILD$DEST_ROOT/libexec/gcc/$BUILD-apple-darwin$DARWIN_VERS/$VERS \
+  || exit 1
+LIBEXEC_FILES=`find . -type f -print || exit 1` 
+LIBEXEC_DIRS=`find . -type d -print || exit 1`
+cd $DEST_DIR || exit 1
+for t in $TARGETS ; do
+  DL=$DEST_ROOT/libexec/gcc/$t-apple-darwin$DARWIN_VERS/$VERS
+  for d in $LIBEXEC_DIRS ; do
+    mkdir -p .$DL/$d || exit 1
+  done
+  for f in $LIBEXEC_FILES ; do
+    if file $DIR/dst-*-$t$DL/$f | grep -q 'Mach-O executable' ; then
+      lipo -output .$DL/$f -create $DIR/dst-*-$t$DL/$f || exit 1
+    else
+      cp -p $DIR/dst-$BUILD-$t$DL/$f .$DL/$f || exit 1
+    fi
+  done
+done
+
+# bin
+# The native drivers ('native' is different in different architectures).
+BIN_FILES=`ls $DIR/dst-$BUILD-$BUILD$DEST_ROOT/bin | grep '^[^-]*-[0-9.]*$' \
+  | grep -v gccbug | grep -v gcov || exit 1`
+mkdir .$DEST_ROOT/bin
+for f in $BIN_FILES ; do 
+  lipo -output .$DEST_ROOT/bin/$f -create $DIR/dst-*$DEST_ROOT/bin/$f || exit 1
+done
+# gcov, which is special only because it gets built multiple times and lipo
+# will complain if we try to add two architectures into the same output.
+TARG0=`echo $TARGETS | cut -d ' ' -f 1`
+lipo -output .$DEST_ROOT/bin/gcov-$MAJ_VERS -create \
+  $DIR/dst-*-$TARG0$DEST_ROOT/bin/*gcov* || exit 1
+# The fully-named drivers, which have the same target on every host.
+for t in $TARGETS ; do
+  lipo -output .$DEST_ROOT/bin/$t-apple-darwin$DARWIN_VERS-gcc-$VERS -create \
+    $DIR/dst-*-$t$DEST_ROOT/bin/$t-apple-darwin$DARWIN_VERS-gcc-$VERS || exit 1
+  if [ $BUILD_CPLUSPLUS = "true" ] ; then
+      lipo -output .$DEST_ROOT/bin/$t-apple-darwin$DARWIN_VERS-g++-$VERS -create \
+        $DIR/dst-*-$t$DEST_ROOT/bin/$t-apple-darwin$DARWIN_VERS-g++* || exit 1
+  fi
+done
+
+# lib
+mkdir -p .$DEST_ROOT/lib/gcc || exit 1
+for t in $TARGETS ; do
+  cp -Rp $DIR/dst-$BUILD-$t$DEST_ROOT/lib/gcc/$t-apple-darwin$DARWIN_VERS \
+    .$DEST_ROOT/lib/gcc || exit 1
+done
+
+SHARED_LIBS="libgcc_s.1.dylib libgcc_s.10.4.dylib libgcc_s.10.5.dylib"
+if echo $HOSTS | grep -q powerpc ; then
+  SHARED_LIBS="${SHARED_LIBS} libgcc_s_ppc64.1.dylib"
+fi
+for l in $SHARED_LIBS ; do
+  CANDIDATES=()
+  for t in $TARGETS ; do
+    if [ -e $DIR/dst-$t-$t$DEST_ROOT/lib/$l ] ; then
+      CANDIDATES[${#CANDIDATES[*]}]=$DIR/dst-$t-$t$DEST_ROOT/lib/$l
+    fi
+  done
+  if [ -L ${CANDIDATES[0]} ] ; then
+    ln -s `readlink ${CANDIDATES[0]}` .$DEST_ROOT/lib/$l || exit 1
+  else
+    lipo -output .$DEST_ROOT/lib/$l -create "${CANDIDATES[@]}" || exit 1
+  fi
+done
+
+if [ $BUILD_CPLUSPLUS = "true" ] ; then
+for t in $TARGETS ; do
+  ln -s ../../../libstdc++.6.dylib \
+    .$DEST_ROOT/lib/gcc/$t-apple-darwin$DARWIN_VERS/$VERS/libstdc++.dylib \
+    || exit 1
+done
+fi
+
+# include
+HEADERPATH=$DEST_ROOT/include/gcc/darwin/$MAJ_VERS
+mkdir -p .$HEADERPATH || exit 1
+
+# Some headers are installed from more-hdrs/.  They all share
+# one common feature: they shouldn't be installed here.  Sometimes,
+# they should be part of FSF GCC and installed from there; sometimes,
+# they should be installed by some completely different package; sometimes,
+# they only exist for codewarrior compatibility and codewarrior should provide
+# its own.  We take care not to install the headers if Libc is already
+# providing them.
+cd $SRC_DIR/more-hdrs
+for h in `echo *.h` ; do
+  if [ ! -f /usr/include/$h -o -L /usr/include/$h ] ; then
+    cp -R $h $DEST_DIR$HEADERPATH/$h || exit 1
+    for t in $TARGETS ; do
+      THEADERPATH=$DEST_DIR$DEST_ROOT/lib/gcc/${t}-apple-darwin$DARWIN_VERS/$VERS/include
+      [ -f $THEADERPATH/$h ] || \
+        ln -s ../../../../../include/gcc/darwin/$MAJ_VERS/$h $THEADERPATH/$h || \
+        exit 1
+    done
+  fi
+done
+mkdir -p $DEST_DIR$HEADERPATH/machine
+for h in `echo */*.h` ; do
+  if [ ! -f /usr/include/$h -o -L /usr/include/$h ] ; then
+    cp -R $h $DEST_DIR$HEADERPATH/$h || exit 1
+    for t in $TARGETS ; do
+      THEADERPATH=$DEST_DIR$DEST_ROOT/lib/gcc/${t}-apple-darwin$DARWIN_VERS/$VERS/include
+      mkdir -p $THEADERPATH/machine
+      # In fixincludes/fixinc.in we created this file...  always link for now
+      [ -f /disable/$THEADERPATH/$h ] || \
+        ln -f -s ../../../../../../include/gcc/darwin/$MAJ_VERS/$h $THEADERPATH/$h || \
+        exit 1
+    done
+  fi
+done
+
+if [ $BUILD_DOCS = "true" ] ; then
+if [ $BUILD_CPLUSPLUS = "true" ] ; then
+# Add extra man page symlinks for 'c++' and for arch-specific names.
+MDIR=$DEST_DIR$DEST_ROOT/share/man/man1
+ln -f $MDIR/g++-$MAJ_VERS.1 $MDIR/c++-$MAJ_VERS.1 || exit 1
+for t in $TARGETS ; do
+  ln -f $MDIR/gcc-$MAJ_VERS.1 $MDIR/$t-apple-darwin$DARWIN_VERS-gcc-$VERS.1 \
+      || exit 1
+  ln -f $MDIR/g++-$MAJ_VERS.1 $MDIR/$t-apple-darwin$DARWIN_VERS-g++-$VERS.1 \
+      || exit 1
+done
+fi
+fi
+
+# Build driver-driver using fully-named drivers
+for h in $HOSTS ; do
+    $DEST_DIR$DEST_ROOT/bin/$h-apple-darwin$DARWIN_VERS-gcc-$VERS                          \
+	$ORIG_SRC_DIR/gcc/config/darwin-driver.c                               \
+	-DPDN="\"-apple-darwin$DARWIN_VERS-gcc-$VERS\""                                    \
+	-DIL="\"$DEST_ROOT/bin/\"" -I  $ORIG_SRC_DIR/include                   \
+	-I  $ORIG_SRC_DIR/gcc -I  $ORIG_SRC_DIR/gcc/config                     \
+	-liberty -L$DIR/dst-$BUILD-$h$DEST_ROOT/lib/                           \
+	-L$DIR/dst-$BUILD-$h$DEST_ROOT/$h-apple-darwin$DARWIN_VERS/lib/                    \
+        -L$DIR/obj-$h-$BUILD/libiberty/                                        \
+	-o $DEST_DIR/$DEST_ROOT/bin/tmp-$h-gcc-$MAJ_VERS || exit 1
+
+    if [ $BUILD_CPLUSPLUS = "true" ] ; then
+    $DEST_DIR$DEST_ROOT/bin/$h-apple-darwin$DARWIN_VERS-gcc-$VERS                          \
+	$ORIG_SRC_DIR/gcc/config/darwin-driver.c                               \
+	-DPDN="\"-apple-darwin$DARWIN_VERS-g++-$VERS\""                                    \
+	-DIL="\"$DEST_ROOT/bin/\"" -I  $ORIG_SRC_DIR/include                   \
+	-I  $ORIG_SRC_DIR/gcc -I  $ORIG_SRC_DIR/gcc/config                     \
+	-liberty -L$DIR/dst-$BUILD-$h$DEST_ROOT/lib/                           \
+	-L$DIR/dst-$BUILD-$h$DEST_ROOT/$h-apple-darwin$DARWIN_VERS/lib/                    \
+        -L$DIR/obj-$h-$BUILD/libiberty/                                        \
+	-o $DEST_DIR/$DEST_ROOT/bin/tmp-$h-g++-$MAJ_VERS || exit 1
+    fi
+done
+
+lipo -output $DEST_DIR/$DEST_ROOT/bin/gcc-$MAJ_VERS -create \
+  $DEST_DIR/$DEST_ROOT/bin/tmp-*-gcc-$MAJ_VERS || exit 1
+
+if [ $BUILD_CPLUSPLUS = "true" ] ; then
+lipo -output $DEST_DIR/$DEST_ROOT/bin/g++-$MAJ_VERS -create \
+  $DEST_DIR/$DEST_ROOT/bin/tmp-*-g++-$MAJ_VERS || exit 1
+
+ln -f $DEST_DIR/$DEST_ROOT/bin/g++-$MAJ_VERS $DEST_DIR/$DEST_ROOT/bin/c++-$MAJ_VERS || exit 1
+fi
+
+rm $DEST_DIR/$DEST_ROOT/bin/tmp-*-gcc-$MAJ_VERS || exit 1 
+if [ $BUILD_CPLUPLUS = "true" ] ; then
+rm $DEST_DIR/$DEST_ROOT/bin/tmp-*-g++-$MAJ_VERS || exit 1
+fi
+
+########################################
+# Save the source files and objects needed for debugging
+if [ $BUILD_SYM = "true" ] ; then
+cd $SYM_DIR || exit 1
+
+# Clean out SYM_DIR in case -noclean was passed to buildit.
+rm -rf * || exit 1
+
+# Save executables and libraries.
+cd $DEST_DIR || exit 1
+find . \( -perm -0111 -or -name \*.a -or -name \*.dylib \) -type f -print \
+  | cpio -pdml $SYM_DIR || exit 1
+# Save source files.
+mkdir $SYM_DIR/src || exit 1
+cd $DIR || exit 1
+find obj-* -name \*.\[chy\] -print | cpio -pdml $SYM_DIR/src || exit 1
+fi
+
+########################################
+# Strip the executables and libraries
+find $DEST_DIR -perm -0111 \! -name \*.dylib \! -name fixinc.sh \
+    \! -name mkheaders -type f -print \
+  | xargs strip || exit 1
+find $DEST_DIR \( -name \*.a -or -name \*.dylib \) \
+    \! -name libgcc_s.10.*.dylib -type f -print \
+  | xargs strip -SX || exit 1
+find $DEST_DIR -name \*.a -type f -print \
+  | xargs ranlib || exit 1
+chgrp -h -R wheel $DEST_DIR
+chgrp -R wheel $DEST_DIR
+
+#########################################3
+# Rename the executables
+FILES="gcc cpp"
+if [ $BUILD_CPLUSPLUS = "true" ] ; then
+    FILES="$FILES g++"
+fi
+for ff in $FILES; do
+    ln -f $DEST_DIR/bin/$ff-$MAJ_VERS $DEST_DIR/bin/$ff || exit 1
+done
+
+# Done!
+exit 0
diff --git a/distrib/libpng-1.2.19/Makefile b/distrib/libpng-1.2.19/Makefile
new file mode 100644
index 0000000..ba59f45
--- /dev/null
+++ b/distrib/libpng-1.2.19/Makefile
@@ -0,0 +1,25 @@
+# Makefile used to compile libpng statically
+# you need to define ZLIB_INCLUDE to the Zlib include path
+# and PREFIX to the installation path
+#
+LIBPNG_LIB    := $(SRC_PATH)/libpng.a
+LIBPNG_CFLAGS := -I$(LIBPNG_DIR)
+
+HOST_ARCH := $(shell uname -p)
+HOST_OS   := $(shell uname -s)
+ifeq ($(HOST_OS),Darwin)
+    HOST_OS := darwin
+endif
+
+include $(LIBPNG_DIR)/sources.make
+
+LIBPNG_OBJS := $(LIBPNG_SOURCES:%.c=%.o)
+
+$(LIBPNG_LIB): $(LIBPNG_OBJS)
+	ar ru $@ $(LIBPNG_OBJS)
+
+$(LIBPNG_OBJS): CFLAGS += $(ZLIB_CFLAGS) $(LIBPNG_CFLAGS)
+
+clean-libpng:
+	rm -f $(LIBPNG_OBJS) $(LIBPNG_LIB)
+
diff --git a/distrib/libpng-1.2.19/png.c b/distrib/libpng-1.2.19/png.c
new file mode 100644
index 0000000..8aa4131
--- /dev/null
+++ b/distrib/libpng-1.2.19/png.c
@@ -0,0 +1,895 @@
+
+/* png.c - location for general purpose libpng functions
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#define PNG_NO_EXTERN
+#include "png.h"
+
+/* Generate a compiler error if there is an old png.h in the search path. */
+typedef version_1_2_19 Your_png_h_is_not_version_1_2_19;
+
+/* Version information for C files.  This had better match the version
+ * string defined in png.h.  */
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+/* png_libpng_ver was changed to a function in version 1.0.5c */
+PNG_CONST char png_libpng_ver[18] = PNG_LIBPNG_VER_STRING;
+
+#ifdef PNG_READ_SUPPORTED
+
+/* png_sig was changed to a function in version 1.0.5c */
+/* Place to hold the signature string for a PNG file. */
+PNG_CONST png_byte FARDATA png_sig[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+#endif /* PNG_READ_SUPPORTED */
+
+/* Invoke global declarations for constant strings for known chunk types */
+PNG_IHDR;
+PNG_IDAT;
+PNG_IEND;
+PNG_PLTE;
+PNG_bKGD;
+PNG_cHRM;
+PNG_gAMA;
+PNG_hIST;
+PNG_iCCP;
+PNG_iTXt;
+PNG_oFFs;
+PNG_pCAL;
+PNG_sCAL;
+PNG_pHYs;
+PNG_sBIT;
+PNG_sPLT;
+PNG_sRGB;
+PNG_tEXt;
+PNG_tIME;
+PNG_tRNS;
+PNG_zTXt;
+
+#ifdef PNG_READ_SUPPORTED
+/* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+/* start of interlace block */
+PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+/* offset to next interlace block */
+PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+/* start of interlace block in the y direction */
+PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+/* offset to next interlace block in the y direction */
+PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+/* width of interlace block (used in assembler routines only) */
+#if defined(PNG_HAVE_MMX_COMBINE_ROW) || defined(PNG_OPTIMIZED_CODE_SUPPORTED)
+PNG_CONST int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1};
+#endif
+
+/* Height of interlace block.  This is not currently used - if you need
+ * it, uncomment it here and in png.h
+PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+*/
+
+/* Mask to determine which pixels are valid in a pass */
+PNG_CONST int FARDATA png_pass_mask[] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+
+/* Mask to determine which pixels to overwrite while displaying */
+PNG_CONST int FARDATA png_pass_dsp_mask[]
+   = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+
+#endif /* PNG_READ_SUPPORTED */
+#endif /* PNG_USE_GLOBAL_ARRAYS */
+
+/* Tells libpng that we have already handled the first "num_bytes" bytes
+ * of the PNG file signature.  If the PNG data is embedded into another
+ * stream we can set num_bytes = 8 so that libpng will not attempt to read
+ * or write any of the magic bytes before it starts on the IHDR.
+ */
+
+#ifdef PNG_READ_SUPPORTED
+void PNGAPI
+png_set_sig_bytes(png_structp png_ptr, int num_bytes)
+{
+   if(png_ptr == NULL) return;
+   png_debug(1, "in png_set_sig_bytes\n");
+   if (num_bytes > 8)
+      png_error(png_ptr, "Too many bytes for PNG signature.");
+
+   png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes);
+}
+
+/* Checks whether the supplied bytes match the PNG signature.  We allow
+ * checking less than the full 8-byte signature so that those apps that
+ * already read the first few bytes of a file to determine the file type
+ * can simply check the remaining bytes for extra assurance.  Returns
+ * an integer less than, equal to, or greater than zero if sig is found,
+ * respectively, to be less than, to match, or be greater than the correct
+ * PNG signature (this is the same behaviour as strcmp, memcmp, etc).
+ */
+int PNGAPI
+png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check)
+{
+   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+   if (num_to_check > 8)
+      num_to_check = 8;
+   else if (num_to_check < 1)
+      return (-1);
+
+   if (start > 7)
+      return (-1);
+
+   if (start + num_to_check > 8)
+      num_to_check = 8 - start;
+
+   return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check)));
+}
+
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+/* (Obsolete) function to check signature bytes.  It does not allow one
+ * to check a partial signature.  This function might be removed in the
+ * future - use png_sig_cmp().  Returns true (nonzero) if the file is PNG.
+ */
+int PNGAPI
+png_check_sig(png_bytep sig, int num)
+{
+  return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num));
+}
+#endif
+#endif /* PNG_READ_SUPPORTED */
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+/* Function to allocate memory for zlib and clear it to 0. */
+#ifdef PNG_1_0_X
+voidpf PNGAPI
+#else
+voidpf /* private */
+#endif
+png_zalloc(voidpf png_ptr, uInt items, uInt size)
+{
+   png_voidp ptr;
+   png_structp p=(png_structp)png_ptr;
+   png_uint_32 save_flags=p->flags;
+   png_uint_32 num_bytes;
+
+   if(png_ptr == NULL) return (NULL);
+   if (items > PNG_UINT_32_MAX/size)
+   {
+     png_warning (p, "Potential overflow in png_zalloc()");
+     return (NULL);
+   }
+   num_bytes = (png_uint_32)items * size;
+
+   p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
+   ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes);
+   p->flags=save_flags;
+
+#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO)
+   if (ptr == NULL)
+       return ((voidpf)ptr);
+
+   if (num_bytes > (png_uint_32)0x8000L)
+   {
+      png_memset(ptr, 0, (png_size_t)0x8000L);
+      png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0,
+         (png_size_t)(num_bytes - (png_uint_32)0x8000L));
+   }
+   else
+   {
+      png_memset(ptr, 0, (png_size_t)num_bytes);
+   }
+#endif
+   return ((voidpf)ptr);
+}
+
+/* function to free memory for zlib */
+#ifdef PNG_1_0_X
+void PNGAPI
+#else
+void /* private */
+#endif
+png_zfree(voidpf png_ptr, voidpf ptr)
+{
+   png_free((png_structp)png_ptr, (png_voidp)ptr);
+}
+
+/* Reset the CRC variable to 32 bits of 1's.  Care must be taken
+ * in case CRC is > 32 bits to leave the top bits 0.
+ */
+void /* PRIVATE */
+png_reset_crc(png_structp png_ptr)
+{
+   png_ptr->crc = crc32(0, Z_NULL, 0);
+}
+
+/* Calculate the CRC over a section of data.  We can only pass as
+ * much data to this routine as the largest single buffer size.  We
+ * also check that this data will actually be used before going to the
+ * trouble of calculating it.
+ */
+void /* PRIVATE */
+png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length)
+{
+   int need_crc = 1;
+
+   if (png_ptr->chunk_name[0] & 0x20)                     /* ancillary */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+   else                                                    /* critical */
+   {
+      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+         need_crc = 0;
+   }
+
+   if (need_crc)
+      png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length);
+}
+
+/* Allocate the memory for an info_struct for the application.  We don't
+ * really need the png_ptr, but it could potentially be useful in the
+ * future.  This should be used in favour of malloc(png_sizeof(png_info))
+ * and png_info_init() so that applications that want to use a shared
+ * libpng don't have to be recompiled if png_info changes size.
+ */
+png_infop PNGAPI
+png_create_info_struct(png_structp png_ptr)
+{
+   png_infop info_ptr;
+
+   png_debug(1, "in png_create_info_struct\n");
+   if(png_ptr == NULL) return (NULL);
+#ifdef PNG_USER_MEM_SUPPORTED
+   info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO,
+      png_ptr->malloc_fn, png_ptr->mem_ptr);
+#else
+   info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+#endif
+   if (info_ptr != NULL)
+      png_info_init_3(&info_ptr, png_sizeof(png_info));
+
+   return (info_ptr);
+}
+
+/* This function frees the memory associated with a single info struct.
+ * Normally, one would use either png_destroy_read_struct() or
+ * png_destroy_write_struct() to free an info struct, but this may be
+ * useful for some applications.
+ */
+void PNGAPI
+png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr)
+{
+   png_infop info_ptr = NULL;
+   if(png_ptr == NULL) return;
+
+   png_debug(1, "in png_destroy_info_struct\n");
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+      png_info_destroy(png_ptr, info_ptr);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn,
+          png_ptr->mem_ptr);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = NULL;
+   }
+}
+
+/* Initialize the info structure.  This is now an internal function (0.89)
+ * and applications using it are urged to use png_create_info_struct()
+ * instead.
+ */
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+#undef png_info_init
+void PNGAPI
+png_info_init(png_infop info_ptr)
+{
+   /* We only come here via pre-1.0.12-compiled applications */
+   png_info_init_3(&info_ptr, 0);
+}
+#endif
+
+void PNGAPI
+png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size)
+{
+   png_infop info_ptr = *ptr_ptr;
+
+   if(info_ptr == NULL) return;
+
+   png_debug(1, "in png_info_init_3\n");
+
+   if(png_sizeof(png_info) > png_info_struct_size)
+     {
+       png_destroy_struct(info_ptr);
+       info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO);
+       *ptr_ptr = info_ptr;
+     }
+
+   /* set everything to 0 */
+   png_memset(info_ptr, 0, png_sizeof (png_info));
+}
+
+#ifdef PNG_FREE_ME_SUPPORTED
+void PNGAPI
+png_data_freer(png_structp png_ptr, png_infop info_ptr,
+   int freer, png_uint_32 mask)
+{
+   png_debug(1, "in png_data_freer\n");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+   if(freer == PNG_DESTROY_WILL_FREE_DATA)
+      info_ptr->free_me |= mask;
+   else if(freer == PNG_USER_WILL_FREE_DATA)
+      info_ptr->free_me &= ~mask;
+   else
+      png_warning(png_ptr,
+         "Unknown freer parameter in png_data_freer.");
+}
+#endif
+
+void PNGAPI
+png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask,
+   int num)
+{
+   png_debug(1, "in png_free_data\n");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+#if defined(PNG_TEXT_SUPPORTED)
+/* free text item num or (if num == -1) all text items */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_TEXT) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_TEXT)
+#endif
+{
+   if (num != -1)
+   {
+     if (info_ptr->text && info_ptr->text[num].key)
+     {
+         png_free(png_ptr, info_ptr->text[num].key);
+         info_ptr->text[num].key = NULL;
+     }
+   }
+   else
+   {
+       int i;
+       for (i = 0; i < info_ptr->num_text; i++)
+           png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i);
+       png_free(png_ptr, info_ptr->text);
+       info_ptr->text = NULL;
+       info_ptr->num_text=0;
+   }
+}
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+/* free any tRNS entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_TRNS) & info_ptr->free_me)
+#else
+if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS))
+#endif
+{
+    png_free(png_ptr, info_ptr->trans);
+    info_ptr->valid &= ~PNG_INFO_tRNS;
+#ifndef PNG_FREE_ME_SUPPORTED
+    png_ptr->flags &= ~PNG_FLAG_FREE_TRNS;
+#endif
+    info_ptr->trans = NULL;
+}
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+/* free any sCAL entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_SCAL) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_SCAL)
+#endif
+{
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+    png_free(png_ptr, info_ptr->scal_s_width);
+    png_free(png_ptr, info_ptr->scal_s_height);
+    info_ptr->scal_s_width = NULL;
+    info_ptr->scal_s_height = NULL;
+#endif
+    info_ptr->valid &= ~PNG_INFO_sCAL;
+}
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+/* free any pCAL entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_PCAL) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_PCAL)
+#endif
+{
+    png_free(png_ptr, info_ptr->pcal_purpose);
+    png_free(png_ptr, info_ptr->pcal_units);
+    info_ptr->pcal_purpose = NULL;
+    info_ptr->pcal_units = NULL;
+    if (info_ptr->pcal_params != NULL)
+    {
+        int i;
+        for (i = 0; i < (int)info_ptr->pcal_nparams; i++)
+        {
+          png_free(png_ptr, info_ptr->pcal_params[i]);
+          info_ptr->pcal_params[i]=NULL;
+        }
+        png_free(png_ptr, info_ptr->pcal_params);
+        info_ptr->pcal_params = NULL;
+    }
+    info_ptr->valid &= ~PNG_INFO_pCAL;
+}
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+/* free any iCCP entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_ICCP) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_ICCP)
+#endif
+{
+    png_free(png_ptr, info_ptr->iccp_name);
+    png_free(png_ptr, info_ptr->iccp_profile);
+    info_ptr->iccp_name = NULL;
+    info_ptr->iccp_profile = NULL;
+    info_ptr->valid &= ~PNG_INFO_iCCP;
+}
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+/* free a given sPLT entry, or (if num == -1) all sPLT entries */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_SPLT) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_SPLT)
+#endif
+{
+   if (num != -1)
+   {
+      if(info_ptr->splt_palettes)
+      {
+          png_free(png_ptr, info_ptr->splt_palettes[num].name);
+          png_free(png_ptr, info_ptr->splt_palettes[num].entries);
+          info_ptr->splt_palettes[num].name = NULL;
+          info_ptr->splt_palettes[num].entries = NULL;
+      }
+   }
+   else
+   {
+       if(info_ptr->splt_palettes_num)
+       {
+         int i;
+         for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+            png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i);
+
+         png_free(png_ptr, info_ptr->splt_palettes);
+         info_ptr->splt_palettes = NULL;
+         info_ptr->splt_palettes_num = 0;
+       }
+       info_ptr->valid &= ~PNG_INFO_sPLT;
+   }
+}
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+  if(png_ptr->unknown_chunk.data)
+  {
+    png_free(png_ptr, png_ptr->unknown_chunk.data);
+    png_ptr->unknown_chunk.data = NULL;
+  }
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_UNKN) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_UNKN)
+#endif
+{
+   if (num != -1)
+   {
+       if(info_ptr->unknown_chunks)
+       {
+          png_free(png_ptr, info_ptr->unknown_chunks[num].data);
+          info_ptr->unknown_chunks[num].data = NULL;
+       }
+   }
+   else
+   {
+       int i;
+
+       if(info_ptr->unknown_chunks_num)
+       {
+         for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++)
+            png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i);
+
+         png_free(png_ptr, info_ptr->unknown_chunks);
+         info_ptr->unknown_chunks = NULL;
+         info_ptr->unknown_chunks_num = 0;
+       }
+   }
+}
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+/* free any hIST entry */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_HIST)  & info_ptr->free_me)
+#else
+if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST))
+#endif
+{
+    png_free(png_ptr, info_ptr->hist);
+    info_ptr->hist = NULL;
+    info_ptr->valid &= ~PNG_INFO_hIST;
+#ifndef PNG_FREE_ME_SUPPORTED
+    png_ptr->flags &= ~PNG_FLAG_FREE_HIST;
+#endif
+}
+#endif
+
+/* free any PLTE entry that was internally allocated */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_PLTE) & info_ptr->free_me)
+#else
+if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE))
+#endif
+{
+    png_zfree(png_ptr, info_ptr->palette);
+    info_ptr->palette = NULL;
+    info_ptr->valid &= ~PNG_INFO_PLTE;
+#ifndef PNG_FREE_ME_SUPPORTED
+    png_ptr->flags &= ~PNG_FLAG_FREE_PLTE;
+#endif
+    info_ptr->num_palette = 0;
+}
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+/* free any image bits attached to the info structure */
+#ifdef PNG_FREE_ME_SUPPORTED
+if ((mask & PNG_FREE_ROWS) & info_ptr->free_me)
+#else
+if (mask & PNG_FREE_ROWS)
+#endif
+{
+    if(info_ptr->row_pointers)
+    {
+       int row;
+       for (row = 0; row < (int)info_ptr->height; row++)
+       {
+          png_free(png_ptr, info_ptr->row_pointers[row]);
+          info_ptr->row_pointers[row]=NULL;
+       }
+       png_free(png_ptr, info_ptr->row_pointers);
+       info_ptr->row_pointers=NULL;
+    }
+    info_ptr->valid &= ~PNG_INFO_IDAT;
+}
+#endif
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   if(num == -1)
+     info_ptr->free_me &= ~mask;
+   else
+     info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL);
+#endif
+}
+
+/* This is an internal routine to free any memory that the info struct is
+ * pointing to before re-using it or freeing the struct itself.  Recall
+ * that png_free() checks for NULL pointers for us.
+ */
+void /* PRIVATE */
+png_info_destroy(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_info_destroy\n");
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   if (png_ptr->num_chunk_list)
+   {
+       png_free(png_ptr, png_ptr->chunk_list);
+       png_ptr->chunk_list=NULL;
+       png_ptr->num_chunk_list=0;
+   }
+#endif
+
+   png_info_init_3(&info_ptr, png_sizeof(png_info));
+}
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+
+/* This function returns a pointer to the io_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy() or png_read_destroy() are called.
+ */
+png_voidp PNGAPI
+png_get_io_ptr(png_structp png_ptr)
+{
+   if(png_ptr == NULL) return (NULL);
+   return (png_ptr->io_ptr);
+}
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+#if !defined(PNG_NO_STDIO)
+/* Initialize the default input/output functions for the PNG file.  If you
+ * use your own read or write routines, you can call either png_set_read_fn()
+ * or png_set_write_fn() instead of png_init_io().  If you have defined
+ * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't
+ * necessarily available.
+ */
+void PNGAPI
+png_init_io(png_structp png_ptr, png_FILE_p fp)
+{
+   png_debug(1, "in png_init_io\n");
+   if(png_ptr == NULL) return;
+   png_ptr->io_ptr = (png_voidp)fp;
+}
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+/* Convert the supplied time into an RFC 1123 string suitable for use in
+ * a "Creation Time" or other text-based time string.
+ */
+png_charp PNGAPI
+png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime)
+{
+   static PNG_CONST char short_months[12][4] =
+        {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+         "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+
+   if(png_ptr == NULL) return (NULL);
+   if (png_ptr->time_buffer == NULL)
+   {
+      png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29*
+         png_sizeof(char)));
+   }
+
+#if defined(_WIN32_WCE)
+   {
+      wchar_t time_buf[29];
+      wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"),
+          ptime->day % 32, short_months[(ptime->month - 1) % 12],
+        ptime->year, ptime->hour % 24, ptime->minute % 60,
+          ptime->second % 61);
+      WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29,
+          NULL, NULL);
+   }
+#else
+#ifdef USE_FAR_KEYWORD
+   {
+      char near_time_buf[29];
+      png_snprintf6(near_time_buf,29,"%d %s %d %02d:%02d:%02d +0000",
+          ptime->day % 32, short_months[(ptime->month - 1) % 12],
+          ptime->year, ptime->hour % 24, ptime->minute % 60,
+          ptime->second % 61);
+      png_memcpy(png_ptr->time_buffer, near_time_buf,
+          29*png_sizeof(char));
+   }
+#else
+   png_snprintf6(png_ptr->time_buffer,29,"%d %s %d %02d:%02d:%02d +0000",
+       ptime->day % 32, short_months[(ptime->month - 1) % 12],
+       ptime->year, ptime->hour % 24, ptime->minute % 60,
+       ptime->second % 61);
+#endif
+#endif /* _WIN32_WCE */
+   return ((png_charp)png_ptr->time_buffer);
+}
+#endif /* PNG_TIME_RFC1123_SUPPORTED */
+
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+
+png_charp PNGAPI
+png_get_copyright(png_structp png_ptr)
+{
+   png_ptr = png_ptr;  /* silence compiler warning about unused png_ptr */
+   return ((png_charp) "\n libpng version 1.2.19 - August 18, 2007\n\
+   Copyright (c) 1998-2007 Glenn Randers-Pehrson\n\
+   Copyright (c) 1996-1997 Andreas Dilger\n\
+   Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n");
+}
+
+/* The following return the library version as a short string in the
+ * format 1.0.0 through 99.99.99zz.  To get the version of *.h files
+ * used with your application, print out PNG_LIBPNG_VER_STRING, which
+ * is defined in png.h.
+ * Note: now there is no difference between png_get_libpng_ver() and
+ * png_get_header_ver().  Due to the version_nn_nn_nn typedef guard,
+ * it is guaranteed that png.c uses the correct version of png.h.
+ */
+png_charp PNGAPI
+png_get_libpng_ver(png_structp png_ptr)
+{
+   /* Version of *.c files used when building libpng */
+   png_ptr = png_ptr;  /* silence compiler warning about unused png_ptr */
+   return ((png_charp) PNG_LIBPNG_VER_STRING);
+}
+
+png_charp PNGAPI
+png_get_header_ver(png_structp png_ptr)
+{
+   /* Version of *.h files used when building libpng */
+   png_ptr = png_ptr;  /* silence compiler warning about unused png_ptr */
+   return ((png_charp) PNG_LIBPNG_VER_STRING);
+}
+
+png_charp PNGAPI
+png_get_header_version(png_structp png_ptr)
+{
+   /* Returns longer string containing both version and date */
+   png_ptr = png_ptr;  /* silence compiler warning about unused png_ptr */
+   return ((png_charp) PNG_HEADER_VERSION_STRING
+#ifdef PNG_READ_SUPPORTED
+#  ifdef PNG_USE_PNGGCCRD
+#    ifdef __x86_64__
+#      ifdef __PIC__
+   "     (PNGGCRD x86_64, PIC)\n"
+#      else
+#        ifdef PNG_THREAD_UNSAFE_OK
+   "     (PNGGCRD x86_64, Thread unsafe)\n"
+#        else
+   "     (PNGGCRD x86_64, Thread safe)\n"
+#        endif
+#      endif
+#    else
+#    ifdef PNG_THREAD_UNSAFE_OK
+   "     (PNGGCRD, Thread unsafe)\n"
+#      else
+   "     (PNGGCRD, Thread safe)\n"
+#      endif
+#    endif
+#  else
+#    ifdef PNG_USE_PNGVCRD
+#      ifdef __x86_64__
+   "     (x86_64 PNGVCRD)\n"
+#      else
+   "     (PNGVCRD)\n"
+#      endif
+#    else
+#      ifdef __x86_64__
+#        ifdef PNG_OPTIMIZED_CODE_SUPPORTED
+   "     (x86_64 OPTIMIZED)\n"
+#        else
+   "     (x86_64 NOT OPTIMIZED)\n"
+#        endif
+#      else
+#        ifdef PNG_OPTIMIZED_CODE_SUPPORTED
+   "     (OPTIMIZED)\n"
+#        else
+   "     (NOT OPTIMIZED)\n"
+#        endif
+#      endif
+#    endif
+#  endif
+#else
+   "     (NO READ SUPPORT)\n"
+#endif
+   );
+}
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+int PNGAPI
+png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name)
+{
+   /* check chunk_name and return "keep" value if it's on the list, else 0 */
+   int i;
+   png_bytep p;
+   if(png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0)
+      return 0;
+   p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5;
+   for (i = png_ptr->num_chunk_list; i; i--, p-=5)
+      if (!png_memcmp(chunk_name, p, 4))
+        return ((int)*(p+4));
+   return 0;
+}
+#endif
+
+/* This function, added to libpng-1.0.6g, is untested. */
+int PNGAPI
+png_reset_zstream(png_structp png_ptr)
+{
+   if (png_ptr == NULL) return Z_STREAM_ERROR;
+   return (inflateReset(&png_ptr->zstream));
+}
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
+
+/* This function was added to libpng-1.0.7 */
+png_uint_32 PNGAPI
+png_access_version_number(void)
+{
+   /* Version of *.c files used when building libpng */
+   return((png_uint_32) PNG_LIBPNG_VER);
+}
+
+
+#if defined(PNG_READ_SUPPORTED) && defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#if !defined(PNG_1_0_X)
+#if defined(PNG_MMX_CODE_SUPPORTED)
+/* this INTERNAL function was added to libpng 1.2.0 */
+void /* PRIVATE */
+png_init_mmx_flags (png_structp png_ptr)
+{
+    if(png_ptr == NULL) return;
+    png_ptr->mmx_rowbytes_threshold = 0;
+    png_ptr->mmx_bitdepth_threshold = 0;
+
+#  if (defined(PNG_USE_PNGVCRD) || defined(PNG_USE_PNGGCCRD))
+
+    png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_COMPILED;
+
+    if (png_mmx_support() > 0) {
+        png_ptr->asm_flags |= PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU
+#    ifdef PNG_HAVE_MMX_COMBINE_ROW
+                              | PNG_ASM_FLAG_MMX_READ_COMBINE_ROW
+#    endif
+#    ifdef PNG_HAVE_MMX_READ_INTERLACE
+                              | PNG_ASM_FLAG_MMX_READ_INTERLACE
+#    endif
+#    ifndef PNG_HAVE_MMX_READ_FILTER_ROW
+                              ;
+#    else
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_SUB
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_UP
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_AVG
+                              | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+
+        png_ptr->mmx_rowbytes_threshold = PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT;
+        png_ptr->mmx_bitdepth_threshold = PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT;
+#    endif
+    } else {
+        png_ptr->asm_flags &= ~( PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU
+                               | PNG_MMX_READ_FLAGS
+                               | PNG_MMX_WRITE_FLAGS );
+    }
+
+#  else /* !(PNGVCRD || PNGGCCRD) */
+
+    /* clear all MMX flags; no support is compiled in */
+    png_ptr->asm_flags &= ~( PNG_MMX_FLAGS );
+
+#  endif /* ?(PNGVCRD || PNGGCCRD) */
+}
+
+#endif /* !(PNG_MMX_CODE_SUPPORTED) */
+
+/* this function was added to libpng 1.2.0 */
+#if !defined(PNG_USE_PNGGCCRD) && \
+    !(defined(PNG_MMX_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD))
+int PNGAPI
+png_mmx_support(void)
+{
+    return -1;
+}
+#endif
+#endif /* PNG_1_0_X */
+#endif /* PNG_READ_SUPPORTED && PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+#ifdef PNG_SIZE_T
+/* Added at libpng version 1.2.6 */
+   PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size));
+png_size_t PNGAPI
+png_convert_size(size_t size)
+{
+  if (size > (png_size_t)-1)
+     PNG_ABORT();  /* We haven't got access to png_ptr, so no png_error() */
+  return ((png_size_t)size);
+}
+#endif /* PNG_SIZE_T */
+#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */
diff --git a/distrib/libpng-1.2.19/png.h b/distrib/libpng-1.2.19/png.h
new file mode 100644
index 0000000..04f786d
--- /dev/null
+++ b/distrib/libpng-1.2.19/png.h
@@ -0,0 +1,3525 @@
+
+/* png.h - header file for PNG reference library
+ *
+ * libpng version 1.2.19 - August 18, 2007
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * Authors and maintainers:
+ *  libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat
+ *  libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger
+ *  libpng versions 0.97, January 1998, through 1.2.19 - August 18, 2007: Glenn
+ *  See also "Contributing Authors", below.
+ *
+ * Note about libpng version numbers:
+ *
+ *    Due to various miscommunications, unforeseen code incompatibilities
+ *    and occasional factors outside the authors' control, version numbering
+ *    on the library has not always been consistent and straightforward.
+ *    The following table summarizes matters since version 0.89c, which was
+ *    the first widely used release:
+ *
+ *    source                 png.h  png.h  shared-lib
+ *    version                string   int  version
+ *    -------                ------ -----  ----------
+ *    0.89c "1.0 beta 3"     0.89      89  1.0.89
+ *    0.90  "1.0 beta 4"     0.90      90  0.90  [should have been 2.0.90]
+ *    0.95  "1.0 beta 5"     0.95      95  0.95  [should have been 2.0.95]
+ *    0.96  "1.0 beta 6"     0.96      96  0.96  [should have been 2.0.96]
+ *    0.97b "1.00.97 beta 7" 1.00.97   97  1.0.1 [should have been 2.0.97]
+ *    0.97c                  0.97      97  2.0.97
+ *    0.98                   0.98      98  2.0.98
+ *    0.99                   0.99      98  2.0.99
+ *    0.99a-m                0.99      99  2.0.99
+ *    1.00                   1.00     100  2.1.0 [100 should be 10000]
+ *    1.0.0      (from here on, the   100  2.1.0 [100 should be 10000]
+ *    1.0.1       png.h string is   10001  2.1.0
+ *    1.0.1a-e    identical to the  10002  from here on, the shared library
+ *    1.0.2       source version)   10002  is 2.V where V is the source code
+ *    1.0.2a-b                      10003  version, except as noted.
+ *    1.0.3                         10003
+ *    1.0.3a-d                      10004
+ *    1.0.4                         10004
+ *    1.0.4a-f                      10005
+ *    1.0.5 (+ 2 patches)           10005
+ *    1.0.5a-d                      10006
+ *    1.0.5e-r                      10100 (not source compatible)
+ *    1.0.5s-v                      10006 (not binary compatible)
+ *    1.0.6 (+ 3 patches)           10006 (still binary incompatible)
+ *    1.0.6d-f                      10007 (still binary incompatible)
+ *    1.0.6g                        10007
+ *    1.0.6h                        10007  10.6h (testing xy.z so-numbering)
+ *    1.0.6i                        10007  10.6i
+ *    1.0.6j                        10007  2.1.0.6j (incompatible with 1.0.0)
+ *    1.0.7beta11-14        DLLNUM  10007  2.1.0.7beta11-14 (binary compatible)
+ *    1.0.7beta15-18           1    10007  2.1.0.7beta15-18 (binary compatible)
+ *    1.0.7rc1-2               1    10007  2.1.0.7rc1-2 (binary compatible)
+ *    1.0.7                    1    10007  (still compatible)
+ *    1.0.8beta1-4             1    10008  2.1.0.8beta1-4
+ *    1.0.8rc1                 1    10008  2.1.0.8rc1
+ *    1.0.8                    1    10008  2.1.0.8
+ *    1.0.9beta1-6             1    10009  2.1.0.9beta1-6
+ *    1.0.9rc1                 1    10009  2.1.0.9rc1
+ *    1.0.9beta7-10            1    10009  2.1.0.9beta7-10
+ *    1.0.9rc2                 1    10009  2.1.0.9rc2
+ *    1.0.9                    1    10009  2.1.0.9
+ *    1.0.10beta1              1    10010  2.1.0.10beta1
+ *    1.0.10rc1                1    10010  2.1.0.10rc1
+ *    1.0.10                   1    10010  2.1.0.10
+ *    1.0.11beta1-3            1    10011  2.1.0.11beta1-3
+ *    1.0.11rc1                1    10011  2.1.0.11rc1
+ *    1.0.11                   1    10011  2.1.0.11
+ *    1.0.12beta1-2            2    10012  2.1.0.12beta1-2
+ *    1.0.12rc1                2    10012  2.1.0.12rc1
+ *    1.0.12                   2    10012  2.1.0.12
+ *    1.1.0a-f                 -    10100  2.1.1.0a-f (branch abandoned)
+ *    1.2.0beta1-2             2    10200  2.1.2.0beta1-2
+ *    1.2.0beta3-5             3    10200  3.1.2.0beta3-5
+ *    1.2.0rc1                 3    10200  3.1.2.0rc1
+ *    1.2.0                    3    10200  3.1.2.0
+ *    1.2.1beta1-4             3    10201  3.1.2.1beta1-4
+ *    1.2.1rc1-2               3    10201  3.1.2.1rc1-2
+ *    1.2.1                    3    10201  3.1.2.1
+ *    1.2.2beta1-6            12    10202  12.so.0.1.2.2beta1-6
+ *    1.0.13beta1             10    10013  10.so.0.1.0.13beta1
+ *    1.0.13rc1               10    10013  10.so.0.1.0.13rc1
+ *    1.2.2rc1                12    10202  12.so.0.1.2.2rc1
+ *    1.0.13                  10    10013  10.so.0.1.0.13
+ *    1.2.2                   12    10202  12.so.0.1.2.2
+ *    1.2.3rc1-6              12    10203  12.so.0.1.2.3rc1-6
+ *    1.2.3                   12    10203  12.so.0.1.2.3
+ *    1.2.4beta1-3            13    10204  12.so.0.1.2.4beta1-3
+ *    1.0.14rc1               13    10014  10.so.0.1.0.14rc1
+ *    1.2.4rc1                13    10204  12.so.0.1.2.4rc1
+ *    1.0.14                  10    10014  10.so.0.1.0.14
+ *    1.2.4                   13    10204  12.so.0.1.2.4
+ *    1.2.5beta1-2            13    10205  12.so.0.1.2.5beta1-2
+ *    1.0.15rc1-3             10    10015  10.so.0.1.0.15rc1-3
+ *    1.2.5rc1-3              13    10205  12.so.0.1.2.5rc1-3
+ *    1.0.15                  10    10015  10.so.0.1.0.15
+ *    1.2.5                   13    10205  12.so.0.1.2.5
+ *    1.2.6beta1-4            13    10206  12.so.0.1.2.6beta1-4
+ *    1.0.16                  10    10016  10.so.0.1.0.16
+ *    1.2.6                   13    10206  12.so.0.1.2.6
+ *    1.2.7beta1-2            13    10207  12.so.0.1.2.7beta1-2
+ *    1.0.17rc1               10    10017  10.so.0.1.0.17rc1
+ *    1.2.7rc1                13    10207  12.so.0.1.2.7rc1
+ *    1.0.17                  10    10017  10.so.0.1.0.17
+ *    1.2.7                   13    10207  12.so.0.1.2.7
+ *    1.2.8beta1-5            13    10208  12.so.0.1.2.8beta1-5
+ *    1.0.18rc1-5             10    10018  10.so.0.1.0.18rc1-5
+ *    1.2.8rc1-5              13    10208  12.so.0.1.2.8rc1-5
+ *    1.0.18                  10    10018  10.so.0.1.0.18
+ *    1.2.8                   13    10208  12.so.0.1.2.8
+ *    1.2.9beta1-3            13    10209  12.so.0.1.2.9beta1-3
+ *    1.2.9beta4-11           13    10209  12.so.0.9[.0]
+ *    1.2.9rc1                13    10209  12.so.0.9[.0]
+ *    1.2.9                   13    10209  12.so.0.9[.0]
+ *    1.2.10beta1-8           13    10210  12.so.0.10[.0]
+ *    1.2.10rc1-3             13    10210  12.so.0.10[.0]
+ *    1.2.10                  13    10210  12.so.0.10[.0]
+ *    1.2.11beta1-4           13    10211  12.so.0.11[.0]
+ *    1.0.19rc1-5             10    10019  10.so.0.19[.0]
+ *    1.2.11rc1-5             13    10211  12.so.0.11[.0]
+ *    1.0.19                  10    10019  10.so.0.19[.0]
+ *    1.2.11                  13    10211  12.so.0.11[.0]
+ *    1.0.20                  10    10020  10.so.0.20[.0]
+ *    1.2.12                  13    10212  12.so.0.12[.0]
+ *    1.2.13beta1             13    10213  12.so.0.13[.0]
+ *    1.0.21                  10    10021  10.so.0.21[.0]
+ *    1.2.13                  13    10213  12.so.0.13[.0]
+ *    1.2.14beta1-2           13    10214  12.so.0.14[.0]
+ *    1.0.22rc1               10    10022  10.so.0.22[.0]
+ *    1.2.14rc1               13    10214  12.so.0.14[.0]
+ *    1.0.22                  10    10022  10.so.0.22[.0]
+ *    1.2.14                  13    10214  12.so.0.14[.0]
+ *    1.2.15beta1-6           13    10215  12.so.0.15[.0]
+ *    1.0.23rc1-5             10    10023  10.so.0.23[.0]
+ *    1.2.15rc1-5             13    10215  12.so.0.15[.0]
+ *    1.0.23                  10    10023  10.so.0.23[.0]
+ *    1.2.15                  13    10215  12.so.0.15[.0]
+ *    1.2.16beta1-2           13    10216  12.so.0.16[.0]
+ *    1.2.16rc1               13    10216  12.so.0.16[.0]
+ *    1.0.24                  10    10024  10.so.0.24[.0]
+ *    1.2.16                  13    10216  12.so.0.16[.0]
+ *    1.2.17beta1-2           13    10217  12.so.0.17[.0]
+ *    1.0.25rc1               10    10025  10.so.0.25[.0]
+ *    1.2.17rc1-3             13    10217  12.so.0.17[.0]
+ *    1.0.25                  10    10025  10.so.0.25[.0]
+ *    1.2.17                  13    10217  12.so.0.17[.0]
+ *    1.0.26                  10    10026  10.so.0.26[.0]
+ *    1.2.18                  13    10218  12.so.0.18[.0]
+ *    1.2.19beta1-31          13    10219  12.so.0.19[.0]
+ *    1.0.27rc1-6             10    10027  10.so.0.27[.0]
+ *    1.2.19rc1-6             13    10219  12.so.0.19[.0]
+ *    1.0.27                  10    10027  10.so.0.27[.0]
+ *    1.2.19                  13    10219  12.so.0.19[.0]
+ *
+ *    Henceforth the source version will match the shared-library major
+ *    and minor numbers; the shared-library major version number will be
+ *    used for changes in backward compatibility, as it is intended.  The
+ *    PNG_LIBPNG_VER macro, which is not used within libpng but is available
+ *    for applications, is an unsigned integer of the form xyyzz corresponding
+ *    to the source version x.y.z (leading zeros in y and z).  Beta versions
+ *    were given the previous public release number plus a letter, until
+ *    version 1.0.6j; from then on they were given the upcoming public
+ *    release number plus "betaNN" or "rcN".
+ *
+ *    Binary incompatibility exists only when applications make direct access
+ *    to the info_ptr or png_ptr members through png.h, and the compiled
+ *    application is loaded with a different version of the library.
+ *
+ *    DLLNUM will change each time there are forward or backward changes
+ *    in binary compatibility (e.g., when a new feature is added).
+ *
+ * See libpng.txt or libpng.3 for more information.  The PNG specification
+ * is available as a W3C Recommendation and as an ISO Specification,
+ * <http://www.w3.org/TR/2003/REC-PNG-20031110/
+ */
+
+/*
+ * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+ *
+ * If you modify libpng you may insert additional notices immediately following
+ * this sentence.
+ *
+ * libpng versions 1.2.6, August 15, 2004, through 1.2.19, August 18, 2007, are
+ * Copyright (c) 2004, 2006-2007 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.2.5
+ * with the following individual added to the list of Contributing Authors:
+ *
+ *    Cosmin Truta
+ *
+ * libpng versions 1.0.7, July 1, 2000, through 1.2.5, October 3, 2002, are
+ * Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-1.0.6
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    Simon-Pierre Cadieux
+ *    Eric S. Raymond
+ *    Gilles Vollant
+ *
+ * and with the following additions to the disclaimer:
+ *
+ *    There is no warranty against interference with your enjoyment of the
+ *    library or against infringement.  There is no warranty that our
+ *    efforts or the library will fulfill any of your particular purposes
+ *    or needs.  This library is provided with all faults, and the entire
+ *    risk of satisfactory quality, performance, accuracy, and effort is with
+ *    the user.
+ *
+ * libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+ * Copyright (c) 1998, 1999, 2000 Glenn Randers-Pehrson, and are
+ * distributed according to the same disclaimer and license as libpng-0.96,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    Tom Lane
+ *    Glenn Randers-Pehrson
+ *    Willem van Schaik
+ *
+ * libpng versions 0.89, June 1996, through 0.96, May 1997, are
+ * Copyright (c) 1996, 1997 Andreas Dilger
+ * Distributed according to the same disclaimer and license as libpng-0.88,
+ * with the following individuals added to the list of Contributing Authors:
+ *
+ *    John Bowler
+ *    Kevin Bracey
+ *    Sam Bushell
+ *    Magnus Holmgren
+ *    Greg Roelofs
+ *    Tom Tanner
+ *
+ * libpng versions 0.5, May 1995, through 0.88, January 1996, are
+ * Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+ *
+ * For the purposes of this copyright and license, "Contributing Authors"
+ * is defined as the following set of individuals:
+ *
+ *    Andreas Dilger
+ *    Dave Martindale
+ *    Guy Eric Schalnat
+ *    Paul Schmidt
+ *    Tim Wegner
+ *
+ * The PNG Reference Library is supplied "AS IS".  The Contributing Authors
+ * and Group 42, Inc. disclaim all warranties, expressed or implied,
+ * including, without limitation, the warranties of merchantability and of
+ * fitness for any purpose.  The Contributing Authors and Group 42, Inc.
+ * assume no liability for direct, indirect, incidental, special, exemplary,
+ * or consequential damages, which may result from the use of the PNG
+ * Reference Library, even if advised of the possibility of such damage.
+ *
+ * Permission is hereby granted to use, copy, modify, and distribute this
+ * source code, or portions hereof, for any purpose, without fee, subject
+ * to the following restrictions:
+ *
+ * 1. The origin of this source code must not be misrepresented.
+ *
+ * 2. Altered versions must be plainly marked as such and
+ * must not be misrepresented as being the original source.
+ *
+ * 3. This Copyright notice may not be removed or altered from
+ *    any source or altered source distribution.
+ *
+ * The Contributing Authors and Group 42, Inc. specifically permit, without
+ * fee, and encourage the use of this source code as a component to
+ * supporting the PNG file format in commercial products.  If you use this
+ * source code in a product, acknowledgment is not required but would be
+ * appreciated.
+ */
+
+/*
+ * A "png_get_copyright" function is available, for convenient use in "about"
+ * boxes and the like:
+ *
+ * printf("%s",png_get_copyright(NULL));
+ *
+ * Also, the PNG logo (in PNG format, of course) is supplied in the
+ * files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31).
+ */
+
+/*
+ * Libpng is OSI Certified Open Source Software.  OSI Certified is a
+ * certification mark of the Open Source Initiative.
+ */
+
+/*
+ * The contributing authors would like to thank all those who helped
+ * with testing, bug fixes, and patience.  This wouldn't have been
+ * possible without all of you.
+ *
+ * Thanks to Frank J. T. Wojcik for helping with the documentation.
+ */
+
+/*
+ * Y2K compliance in libpng:
+ * =========================
+ *
+ *    August 18, 2007
+ *
+ *    Since the PNG Development group is an ad-hoc body, we can't make
+ *    an official declaration.
+ *
+ *    This is your unofficial assurance that libpng from version 0.71 and
+ *    upward through 1.2.19 are Y2K compliant.  It is my belief that earlier
+ *    versions were also Y2K compliant.
+ *
+ *    Libpng only has three year fields.  One is a 2-byte unsigned integer
+ *    that will hold years up to 65535.  The other two hold the date in text
+ *    format, and will hold years up to 9999.
+ *
+ *    The integer is
+ *        "png_uint_16 year" in png_time_struct.
+ *
+ *    The strings are
+ *        "png_charp time_buffer" in png_struct and
+ *        "near_time_buffer", which is a local character string in png.c.
+ *
+ *    There are seven time-related functions:
+ *        png.c: png_convert_to_rfc_1123() in png.c
+ *          (formerly png_convert_to_rfc_1152() in error)
+ *        png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c
+ *        png_convert_from_time_t() in pngwrite.c
+ *        png_get_tIME() in pngget.c
+ *        png_handle_tIME() in pngrutil.c, called in pngread.c
+ *        png_set_tIME() in pngset.c
+ *        png_write_tIME() in pngwutil.c, called in pngwrite.c
+ *
+ *    All handle dates properly in a Y2K environment.  The
+ *    png_convert_from_time_t() function calls gmtime() to convert from system
+ *    clock time, which returns (year - 1900), which we properly convert to
+ *    the full 4-digit year.  There is a possibility that applications using
+ *    libpng are not passing 4-digit years into the png_convert_to_rfc_1123()
+ *    function, or that they are incorrectly passing only a 2-digit year
+ *    instead of "year - 1900" into the png_convert_from_struct_tm() function,
+ *    but this is not under our control.  The libpng documentation has always
+ *    stated that it works with 4-digit years, and the APIs have been
+ *    documented as such.
+ *
+ *    The tIME chunk itself is also Y2K compliant.  It uses a 2-byte unsigned
+ *    integer to hold the year, and can hold years as large as 65535.
+ *
+ *    zlib, upon which libpng depends, is also Y2K compliant.  It contains
+ *    no date-related code.
+ *
+ *       Glenn Randers-Pehrson
+ *       libpng maintainer
+ *       PNG Development Group
+ */
+
+#ifndef PNG_H
+#define PNG_H
+
+/* This is not the place to learn how to use libpng.  The file libpng.txt
+ * describes how to use libpng, and the file example.c summarizes it
+ * with some code on which to build.  This file is useful for looking
+ * at the actual function definitions and structure components.
+ */
+
+/* Version information for png.h - this should match the version in png.c */
+#define PNG_LIBPNG_VER_STRING "1.2.19"
+#define PNG_HEADER_VERSION_STRING \
+   " libpng version 1.2.19 - August 18, 2007\n"
+
+#define PNG_LIBPNG_VER_SONUM   0
+#define PNG_LIBPNG_VER_DLLNUM  13
+
+/* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */
+#define PNG_LIBPNG_VER_MAJOR   1
+#define PNG_LIBPNG_VER_MINOR   2
+#define PNG_LIBPNG_VER_RELEASE 19
+/* This should match the numeric part of the final component of
+ * PNG_LIBPNG_VER_STRING, omitting any leading zero: */
+
+#define PNG_LIBPNG_VER_BUILD  0
+
+/* Release Status */
+#define PNG_LIBPNG_BUILD_ALPHA    1
+#define PNG_LIBPNG_BUILD_BETA     2
+#define PNG_LIBPNG_BUILD_RC       3
+#define PNG_LIBPNG_BUILD_STABLE   4
+#define PNG_LIBPNG_BUILD_RELEASE_STATUS_MASK 7
+  
+/* Release-Specific Flags */
+#define PNG_LIBPNG_BUILD_PATCH    8 /* Can be OR'ed with
+                                       PNG_LIBPNG_BUILD_STABLE only */
+#define PNG_LIBPNG_BUILD_PRIVATE 16 /* Cannot be OR'ed with
+                                       PNG_LIBPNG_BUILD_SPECIAL */
+#define PNG_LIBPNG_BUILD_SPECIAL 32 /* Cannot be OR'ed with
+                                       PNG_LIBPNG_BUILD_PRIVATE */
+
+#define PNG_LIBPNG_BUILD_BASE_TYPE PNG_LIBPNG_BUILD_STABLE
+
+/* Careful here.  At one time, Guy wanted to use 082, but that would be octal.
+ * We must not include leading zeros.
+ * Versions 0.7 through 1.0.0 were in the range 0 to 100 here (only
+ * version 1.0.0 was mis-numbered 100 instead of 10000).  From
+ * version 1.0.1 it's    xxyyzz, where x=major, y=minor, z=release */
+#define PNG_LIBPNG_VER 10219 /* 1.2.19 */
+
+#ifndef PNG_VERSION_INFO_ONLY
+/* include the compression library's header */
+#include "zlib.h"
+#endif
+
+/* include all user configurable info, including optional assembler routines */
+#include "pngconf.h"
+
+/*
+ * Added at libpng-1.2.8 */
+/* Ref MSDN: Private as priority over Special
+ * VS_FF_PRIVATEBUILD File *was not* built using standard release
+ * procedures. If this value is given, the StringFileInfo block must
+ * contain a PrivateBuild string. 
+ *
+ * VS_FF_SPECIALBUILD File *was* built by the original company using
+ * standard release procedures but is a variation of the standard
+ * file of the same version number. If this value is given, the
+ * StringFileInfo block must contain a SpecialBuild string. 
+ */
+
+#if defined(PNG_USER_PRIVATEBUILD)
+#  define PNG_LIBPNG_BUILD_TYPE \
+          (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_PRIVATE)
+#else
+#  if defined(PNG_LIBPNG_SPECIALBUILD)
+#    define PNG_LIBPNG_BUILD_TYPE \
+            (PNG_LIBPNG_BUILD_BASE_TYPE | PNG_LIBPNG_BUILD_SPECIAL)
+#  else
+#    define PNG_LIBPNG_BUILD_TYPE (PNG_LIBPNG_BUILD_BASE_TYPE)
+#  endif
+#endif
+
+#ifndef PNG_VERSION_INFO_ONLY
+
+/* Inhibit C++ name-mangling for libpng functions but not for system calls. */
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* This file is arranged in several sections.  The first section contains
+ * structure and type definitions.  The second section contains the external
+ * library functions, while the third has the internal library functions,
+ * which applications aren't expected to use directly.
+ */
+
+#ifndef PNG_NO_TYPECAST_NULL
+#define int_p_NULL                (int *)NULL
+#define png_bytep_NULL            (png_bytep)NULL
+#define png_bytepp_NULL           (png_bytepp)NULL
+#define png_doublep_NULL          (png_doublep)NULL
+#define png_error_ptr_NULL        (png_error_ptr)NULL
+#define png_flush_ptr_NULL        (png_flush_ptr)NULL
+#define png_free_ptr_NULL         (png_free_ptr)NULL
+#define png_infopp_NULL           (png_infopp)NULL
+#define png_malloc_ptr_NULL       (png_malloc_ptr)NULL
+#define png_read_status_ptr_NULL  (png_read_status_ptr)NULL
+#define png_rw_ptr_NULL           (png_rw_ptr)NULL
+#define png_structp_NULL          (png_structp)NULL
+#define png_uint_16p_NULL         (png_uint_16p)NULL
+#define png_voidp_NULL            (png_voidp)NULL
+#define png_write_status_ptr_NULL (png_write_status_ptr)NULL
+#else
+#define int_p_NULL                NULL
+#define png_bytep_NULL            NULL
+#define png_bytepp_NULL           NULL
+#define png_doublep_NULL          NULL
+#define png_error_ptr_NULL        NULL
+#define png_flush_ptr_NULL        NULL
+#define png_free_ptr_NULL         NULL
+#define png_infopp_NULL           NULL
+#define png_malloc_ptr_NULL       NULL
+#define png_read_status_ptr_NULL  NULL
+#define png_rw_ptr_NULL           NULL
+#define png_structp_NULL          NULL
+#define png_uint_16p_NULL         NULL
+#define png_voidp_NULL            NULL
+#define png_write_status_ptr_NULL NULL
+#endif
+
+/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */
+#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN)
+/* Version information for C files, stored in png.c.  This had better match
+ * the version above.
+ */
+#ifdef PNG_USE_GLOBAL_ARRAYS
+PNG_EXPORT_VAR (PNG_CONST char) png_libpng_ver[18];
+  /* need room for 99.99.99beta99z */
+#else
+#define png_libpng_ver png_get_header_ver(NULL)
+#endif
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+/* This was removed in version 1.0.5c */
+/* Structures to facilitate easy interlacing.  See png.c for more details */
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_start[7];
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_inc[7];
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_ystart[7];
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_yinc[7];
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_mask[7];
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_dsp_mask[7];
+#if defined(PNG_HAVE_MMX_COMBINE_ROW) || defined(PNG_OPTIMIZED_CODE_SUPPORTED)
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_width[7];
+#endif
+/* This isn't currently used.  If you need it, see png.c for more details.
+PNG_EXPORT_VAR (PNG_CONST int FARDATA) png_pass_height[7];
+*/
+#endif
+
+#endif /* PNG_NO_EXTERN */
+
+/* Three color definitions.  The order of the red, green, and blue, (and the
+ * exact size) is not important, although the size of the fields need to
+ * be png_byte or png_uint_16 (as defined below).
+ */
+typedef struct png_color_struct
+{
+   png_byte red;
+   png_byte green;
+   png_byte blue;
+} png_color;
+typedef png_color FAR * png_colorp;
+typedef png_color FAR * FAR * png_colorpp;
+
+typedef struct png_color_16_struct
+{
+   png_byte index;    /* used for palette files */
+   png_uint_16 red;   /* for use in red green blue files */
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 gray;  /* for use in grayscale files */
+} png_color_16;
+typedef png_color_16 FAR * png_color_16p;
+typedef png_color_16 FAR * FAR * png_color_16pp;
+
+typedef struct png_color_8_struct
+{
+   png_byte red;   /* for use in red green blue files */
+   png_byte green;
+   png_byte blue;
+   png_byte gray;  /* for use in grayscale files */
+   png_byte alpha; /* for alpha channel files */
+} png_color_8;
+typedef png_color_8 FAR * png_color_8p;
+typedef png_color_8 FAR * FAR * png_color_8pp;
+
+/*
+ * The following two structures are used for the in-core representation
+ * of sPLT chunks.
+ */
+typedef struct png_sPLT_entry_struct
+{
+   png_uint_16 red;
+   png_uint_16 green;
+   png_uint_16 blue;
+   png_uint_16 alpha;
+   png_uint_16 frequency;
+} png_sPLT_entry;
+typedef png_sPLT_entry FAR * png_sPLT_entryp;
+typedef png_sPLT_entry FAR * FAR * png_sPLT_entrypp;
+
+/*  When the depth of the sPLT palette is 8 bits, the color and alpha samples
+ *  occupy the LSB of their respective members, and the MSB of each member
+ *  is zero-filled.  The frequency member always occupies the full 16 bits.
+ */
+
+typedef struct png_sPLT_struct
+{
+   png_charp name;           /* palette name */
+   png_byte depth;           /* depth of palette samples */
+   png_sPLT_entryp entries;  /* palette entries */
+   png_int_32 nentries;      /* number of palette entries */
+} png_sPLT_t;
+typedef png_sPLT_t FAR * png_sPLT_tp;
+typedef png_sPLT_t FAR * FAR * png_sPLT_tpp;
+
+#ifdef PNG_TEXT_SUPPORTED
+/* png_text holds the contents of a text/ztxt/itxt chunk in a PNG file,
+ * and whether that contents is compressed or not.  The "key" field
+ * points to a regular zero-terminated C string.  The "text", "lang", and
+ * "lang_key" fields can be regular C strings, empty strings, or NULL pointers.
+ * However, the * structure returned by png_get_text() will always contain
+ * regular zero-terminated C strings (possibly empty), never NULL pointers,
+ * so they can be safely used in printf() and other string-handling functions.
+ */
+typedef struct png_text_struct
+{
+   int  compression;       /* compression value:
+                             -1: tEXt, none
+                              0: zTXt, deflate
+                              1: iTXt, none
+                              2: iTXt, deflate  */
+   png_charp key;          /* keyword, 1-79 character description of "text" */
+   png_charp text;         /* comment, may be an empty string (ie "")
+                              or a NULL pointer */
+   png_size_t text_length; /* length of the text string */
+#ifdef PNG_iTXt_SUPPORTED
+   png_size_t itxt_length; /* length of the itxt string */
+   png_charp lang;         /* language code, 0-79 characters
+                              or a NULL pointer */
+   png_charp lang_key;     /* keyword translated UTF-8 string, 0 or more
+                              chars or a NULL pointer */
+#endif
+} png_text;
+typedef png_text FAR * png_textp;
+typedef png_text FAR * FAR * png_textpp;
+#endif
+
+/* Supported compression types for text in PNG files (tEXt, and zTXt).
+ * The values of the PNG_TEXT_COMPRESSION_ defines should NOT be changed. */
+#define PNG_TEXT_COMPRESSION_NONE_WR -3
+#define PNG_TEXT_COMPRESSION_zTXt_WR -2
+#define PNG_TEXT_COMPRESSION_NONE    -1
+#define PNG_TEXT_COMPRESSION_zTXt     0
+#define PNG_ITXT_COMPRESSION_NONE     1
+#define PNG_ITXT_COMPRESSION_zTXt     2
+#define PNG_TEXT_COMPRESSION_LAST     3  /* Not a valid value */
+
+/* png_time is a way to hold the time in an machine independent way.
+ * Two conversions are provided, both from time_t and struct tm.  There
+ * is no portable way to convert to either of these structures, as far
+ * as I know.  If you know of a portable way, send it to me.  As a side
+ * note - PNG has always been Year 2000 compliant!
+ */
+typedef struct png_time_struct
+{
+   png_uint_16 year; /* full year, as in, 1995 */
+   png_byte month;   /* month of year, 1 - 12 */
+   png_byte day;     /* day of month, 1 - 31 */
+   png_byte hour;    /* hour of day, 0 - 23 */
+   png_byte minute;  /* minute of hour, 0 - 59 */
+   png_byte second;  /* second of minute, 0 - 60 (for leap seconds) */
+} png_time;
+typedef png_time FAR * png_timep;
+typedef png_time FAR * FAR * png_timepp;
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+/* png_unknown_chunk is a structure to hold queued chunks for which there is
+ * no specific support.  The idea is that we can use this to queue
+ * up private chunks for output even though the library doesn't actually
+ * know about their semantics.
+ */
+typedef struct png_unknown_chunk_t
+{
+    png_byte name[5];
+    png_byte *data;
+    png_size_t size;
+
+    /* libpng-using applications should NOT directly modify this byte. */
+    png_byte location; /* mode of operation at read time */
+}
+png_unknown_chunk;
+typedef png_unknown_chunk FAR * png_unknown_chunkp;
+typedef png_unknown_chunk FAR * FAR * png_unknown_chunkpp;
+#endif
+
+/* png_info is a structure that holds the information in a PNG file so
+ * that the application can find out the characteristics of the image.
+ * If you are reading the file, this structure will tell you what is
+ * in the PNG file.  If you are writing the file, fill in the information
+ * you want to put into the PNG file, then call png_write_info().
+ * The names chosen should be very close to the PNG specification, so
+ * consult that document for information about the meaning of each field.
+ *
+ * With libpng < 0.95, it was only possible to directly set and read the
+ * the values in the png_info_struct, which meant that the contents and
+ * order of the values had to remain fixed.  With libpng 0.95 and later,
+ * however, there are now functions that abstract the contents of
+ * png_info_struct from the application, so this makes it easier to use
+ * libpng with dynamic libraries, and even makes it possible to use
+ * libraries that don't have all of the libpng ancillary chunk-handing
+ * functionality.
+ *
+ * In any case, the order of the parameters in png_info_struct should NOT
+ * be changed for as long as possible to keep compatibility with applications
+ * that use the old direct-access method with png_info_struct.
+ *
+ * The following members may have allocated storage attached that should be
+ * cleaned up before the structure is discarded: palette, trans, text,
+ * pcal_purpose, pcal_units, pcal_params, hist, iccp_name, iccp_profile,
+ * splt_palettes, scal_unit, row_pointers, and unknowns.   By default, these
+ * are automatically freed when the info structure is deallocated, if they were
+ * allocated internally by libpng.  This behavior can be changed by means
+ * of the png_data_freer() function.
+ *
+ * More allocation details: all the chunk-reading functions that
+ * change these members go through the corresponding png_set_*
+ * functions.  A function to clear these members is available: see
+ * png_free_data().  The png_set_* functions do not depend on being
+ * able to point info structure members to any of the storage they are
+ * passed (they make their own copies), EXCEPT that the png_set_text
+ * functions use the same storage passed to them in the text_ptr or
+ * itxt_ptr structure argument, and the png_set_rows and png_set_unknowns
+ * functions do not make their own copies.
+ */
+typedef struct png_info_struct
+{
+   /* the following are necessary for every PNG file */
+   png_uint_32 width;       /* width of image in pixels (from IHDR) */
+   png_uint_32 height;      /* height of image in pixels (from IHDR) */
+   png_uint_32 valid;       /* valid chunk data (see PNG_INFO_ below) */
+   png_uint_32 rowbytes;    /* bytes needed to hold an untransformed row */
+   png_colorp palette;      /* array of color values (valid & PNG_INFO_PLTE) */
+   png_uint_16 num_palette; /* number of color entries in "palette" (PLTE) */
+   png_uint_16 num_trans;   /* number of transparent palette color (tRNS) */
+   png_byte bit_depth;      /* 1, 2, 4, 8, or 16 bits/channel (from IHDR) */
+   png_byte color_type;     /* see PNG_COLOR_TYPE_ below (from IHDR) */
+   /* The following three should have been named *_method not *_type */
+   png_byte compression_type; /* must be PNG_COMPRESSION_TYPE_BASE (IHDR) */
+   png_byte filter_type;    /* must be PNG_FILTER_TYPE_BASE (from IHDR) */
+   png_byte interlace_type; /* One of PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+
+   /* The following is informational only on read, and not used on writes. */
+   png_byte channels;       /* number of data channels per pixel (1, 2, 3, 4) */
+   png_byte pixel_depth;    /* number of bits per pixel */
+   png_byte spare_byte;     /* to align the data, and for future use */
+   png_byte signature[8];   /* magic bytes read by libpng from start of file */
+
+   /* The rest of the data is optional.  If you are reading, check the
+    * valid field to see if the information in these are valid.  If you
+    * are writing, set the valid field to those chunks you want written,
+    * and initialize the appropriate fields below.
+    */
+
+#if defined(PNG_gAMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+   /* The gAMA chunk describes the gamma characteristics of the system
+    * on which the image was created, normally in the range [1.0, 2.5].
+    * Data is valid if (valid & PNG_INFO_gAMA) is non-zero.
+    */
+   float gamma; /* gamma value of image, if (valid & PNG_INFO_gAMA) */
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+    /* GR-P, 0.96a */
+    /* Data valid if (valid & PNG_INFO_sRGB) non-zero. */
+   png_byte srgb_intent; /* sRGB rendering intent [0, 1, 2, or 3] */
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+   /* The tEXt, and zTXt chunks contain human-readable textual data in
+    * uncompressed, compressed, and optionally compressed forms, respectively.
+    * The data in "text" is an array of pointers to uncompressed,
+    * null-terminated C strings. Each chunk has a keyword that describes the
+    * textual data contained in that chunk.  Keywords are not required to be
+    * unique, and the text string may be empty.  Any number of text chunks may
+    * be in an image.
+    */
+   int num_text; /* number of comments read/to write */
+   int max_text; /* current size of text array */
+   png_textp text; /* array of comments read/to write */
+#endif /* PNG_TEXT_SUPPORTED */
+
+#if defined(PNG_tIME_SUPPORTED)
+   /* The tIME chunk holds the last time the displayed image data was
+    * modified.  See the png_time struct for the contents of this struct.
+    */
+   png_time mod_time;
+#endif
+
+#if defined(PNG_sBIT_SUPPORTED)
+   /* The sBIT chunk specifies the number of significant high-order bits
+    * in the pixel data.  Values are in the range [1, bit_depth], and are
+    * only specified for the channels in the pixel data.  The contents of
+    * the low-order bits is not specified.  Data is valid if
+    * (valid & PNG_INFO_sBIT) is non-zero.
+    */
+   png_color_8 sig_bit; /* significant bits in color channels */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_EXPAND_SUPPORTED) || \
+defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The tRNS chunk supplies transparency data for paletted images and
+    * other image types that don't need a full alpha channel.  There are
+    * "num_trans" transparency values for a paletted image, stored in the
+    * same order as the palette colors, starting from index 0.  Values
+    * for the data are in the range [0, 255], ranging from fully transparent
+    * to fully opaque, respectively.  For non-paletted images, there is a
+    * single color specified that should be treated as fully transparent.
+    * Data is valid if (valid & PNG_INFO_tRNS) is non-zero.
+    */
+   png_bytep trans; /* transparent values for paletted image */
+   png_color_16 trans_values; /* transparent color for non-palette image */
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* The bKGD chunk gives the suggested image background color if the
+    * display program does not have its own background color and the image
+    * is needs to composited onto a background before display.  The colors
+    * in "background" are normally in the same color space/depth as the
+    * pixel data.  Data is valid if (valid & PNG_INFO_bKGD) is non-zero.
+    */
+   png_color_16 background;
+#endif
+
+#if defined(PNG_oFFs_SUPPORTED)
+   /* The oFFs chunk gives the offset in "offset_unit_type" units rightwards
+    * and downwards from the top-left corner of the display, page, or other
+    * application-specific co-ordinate space.  See the PNG_OFFSET_ defines
+    * below for the unit types.  Valid if (valid & PNG_INFO_oFFs) non-zero.
+    */
+   png_int_32 x_offset; /* x offset on page */
+   png_int_32 y_offset; /* y offset on page */
+   png_byte offset_unit_type; /* offset units type */
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+   /* The pHYs chunk gives the physical pixel density of the image for
+    * display or printing in "phys_unit_type" units (see PNG_RESOLUTION_
+    * defines below).  Data is valid if (valid & PNG_INFO_pHYs) is non-zero.
+    */
+   png_uint_32 x_pixels_per_unit; /* horizontal pixel density */
+   png_uint_32 y_pixels_per_unit; /* vertical pixel density */
+   png_byte phys_unit_type; /* resolution type (see PNG_RESOLUTION_ below) */
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+   /* The hIST chunk contains the relative frequency or importance of the
+    * various palette entries, so that a viewer can intelligently select a
+    * reduced-color palette, if required.  Data is an array of "num_palette"
+    * values in the range [0,65535]. Data valid if (valid & PNG_INFO_hIST)
+    * is non-zero.
+    */
+   png_uint_16p hist;
+#endif
+
+#ifdef PNG_cHRM_SUPPORTED
+   /* The cHRM chunk describes the CIE color characteristics of the monitor
+    * on which the PNG was created.  This data allows the viewer to do gamut
+    * mapping of the input image to ensure that the viewer sees the same
+    * colors in the image as the creator.  Values are in the range
+    * [0.0, 0.8].  Data valid if (valid & PNG_INFO_cHRM) non-zero.
+    */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float x_white;
+   float y_white;
+   float x_red;
+   float y_red;
+   float x_green;
+   float y_green;
+   float x_blue;
+   float y_blue;
+#endif
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+   /* The pCAL chunk describes a transformation between the stored pixel
+    * values and original physical data values used to create the image.
+    * The integer range [0, 2^bit_depth - 1] maps to the floating-point
+    * range given by [pcal_X0, pcal_X1], and are further transformed by a
+    * (possibly non-linear) transformation function given by "pcal_type"
+    * and "pcal_params" into "pcal_units".  Please see the PNG_EQUATION_
+    * defines below, and the PNG-Group's PNG extensions document for a
+    * complete description of the transformations and how they should be
+    * implemented, and for a description of the ASCII parameter strings.
+    * Data values are valid if (valid & PNG_INFO_pCAL) non-zero.
+    */
+   png_charp pcal_purpose;  /* pCAL chunk description string */
+   png_int_32 pcal_X0;      /* minimum value */
+   png_int_32 pcal_X1;      /* maximum value */
+   png_charp pcal_units;    /* Latin-1 string giving physical units */
+   png_charpp pcal_params;  /* ASCII strings containing parameter values */
+   png_byte pcal_type;      /* equation type (see PNG_EQUATION_ below) */
+   png_byte pcal_nparams;   /* number of parameters given in pcal_params */
+#endif
+
+/* New members added in libpng-1.0.6 */
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_uint_32 free_me;     /* flags items libpng is responsible for freeing */
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   /* storage for unknown chunks that the library doesn't recognize. */
+   png_unknown_chunkp unknown_chunks;
+   png_size_t unknown_chunks_num;
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+   /* iCCP chunk data. */
+   png_charp iccp_name;     /* profile name */
+   png_charp iccp_profile;  /* International Color Consortium profile data */
+                            /* Note to maintainer: should be png_bytep */
+   png_uint_32 iccp_proflen;  /* ICC profile data length */
+   png_byte iccp_compression; /* Always zero */
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+   /* data on sPLT chunks (there may be more than one). */
+   png_sPLT_tp splt_palettes;
+   png_uint_32 splt_palettes_num;
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+   /* The sCAL chunk describes the actual physical dimensions of the
+    * subject matter of the graphic.  The chunk contains a unit specification
+    * a byte value, and two ASCII strings representing floating-point
+    * values.  The values are width and height corresponsing to one pixel
+    * in the image.  This external representation is converted to double
+    * here.  Data values are valid if (valid & PNG_INFO_sCAL) is non-zero.
+    */
+   png_byte scal_unit;         /* unit of physical scale */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   double scal_pixel_width;    /* width of one pixel */
+   double scal_pixel_height;   /* height of one pixel */
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_charp scal_s_width;     /* string containing height */
+   png_charp scal_s_height;    /* string containing width */
+#endif
+#endif
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+   /* Memory has been allocated if (valid & PNG_ALLOCATED_INFO_ROWS) non-zero */
+   /* Data valid if (valid & PNG_INFO_IDAT) non-zero */
+   png_bytepp row_pointers;        /* the image bits */
+#endif
+
+#if defined(PNG_FIXED_POINT_SUPPORTED) && defined(PNG_gAMA_SUPPORTED)
+   png_fixed_point int_gamma; /* gamma of image, if (valid & PNG_INFO_gAMA) */
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED) && defined(PNG_FIXED_POINT_SUPPORTED)
+   png_fixed_point int_x_white;
+   png_fixed_point int_y_white;
+   png_fixed_point int_x_red;
+   png_fixed_point int_y_red;
+   png_fixed_point int_x_green;
+   png_fixed_point int_y_green;
+   png_fixed_point int_x_blue;
+   png_fixed_point int_y_blue;
+#endif
+
+} png_info;
+
+typedef png_info FAR * png_infop;
+typedef png_info FAR * FAR * png_infopp;
+
+/* Maximum positive integer used in PNG is (2^31)-1 */
+#define PNG_UINT_31_MAX ((png_uint_32)0x7fffffffL)
+#define PNG_UINT_32_MAX ((png_uint_32)(-1))
+#define PNG_SIZE_MAX ((png_size_t)(-1))
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* PNG_MAX_UINT is deprecated; use PNG_UINT_31_MAX instead. */
+#define PNG_MAX_UINT PNG_UINT_31_MAX
+#endif
+
+/* These describe the color_type field in png_info. */
+/* color type masks */
+#define PNG_COLOR_MASK_PALETTE    1
+#define PNG_COLOR_MASK_COLOR      2
+#define PNG_COLOR_MASK_ALPHA      4
+
+/* color types.  Note that not all combinations are legal */
+#define PNG_COLOR_TYPE_GRAY 0
+#define PNG_COLOR_TYPE_PALETTE  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
+#define PNG_COLOR_TYPE_RGB        (PNG_COLOR_MASK_COLOR)
+#define PNG_COLOR_TYPE_RGB_ALPHA  (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
+#define PNG_COLOR_TYPE_GRAY_ALPHA (PNG_COLOR_MASK_ALPHA)
+/* aliases */
+#define PNG_COLOR_TYPE_RGBA  PNG_COLOR_TYPE_RGB_ALPHA
+#define PNG_COLOR_TYPE_GA  PNG_COLOR_TYPE_GRAY_ALPHA
+
+/* This is for compression type. PNG 1.0-1.2 only define the single type. */
+#define PNG_COMPRESSION_TYPE_BASE 0 /* Deflate method 8, 32K window */
+#define PNG_COMPRESSION_TYPE_DEFAULT PNG_COMPRESSION_TYPE_BASE
+
+/* This is for filter type. PNG 1.0-1.2 only define the single type. */
+#define PNG_FILTER_TYPE_BASE      0 /* Single row per-byte filtering */
+#define PNG_INTRAPIXEL_DIFFERENCING 64 /* Used only in MNG datastreams */
+#define PNG_FILTER_TYPE_DEFAULT   PNG_FILTER_TYPE_BASE
+
+/* These are for the interlacing type.  These values should NOT be changed. */
+#define PNG_INTERLACE_NONE        0 /* Non-interlaced image */
+#define PNG_INTERLACE_ADAM7       1 /* Adam7 interlacing */
+#define PNG_INTERLACE_LAST        2 /* Not a valid value */
+
+/* These are for the oFFs chunk.  These values should NOT be changed. */
+#define PNG_OFFSET_PIXEL          0 /* Offset in pixels */
+#define PNG_OFFSET_MICROMETER     1 /* Offset in micrometers (1/10^6 meter) */
+#define PNG_OFFSET_LAST           2 /* Not a valid value */
+
+/* These are for the pCAL chunk.  These values should NOT be changed. */
+#define PNG_EQUATION_LINEAR       0 /* Linear transformation */
+#define PNG_EQUATION_BASE_E       1 /* Exponential base e transform */
+#define PNG_EQUATION_ARBITRARY    2 /* Arbitrary base exponential transform */
+#define PNG_EQUATION_HYPERBOLIC   3 /* Hyperbolic sine transformation */
+#define PNG_EQUATION_LAST         4 /* Not a valid value */
+
+/* These are for the sCAL chunk.  These values should NOT be changed. */
+#define PNG_SCALE_UNKNOWN         0 /* unknown unit (image scale) */
+#define PNG_SCALE_METER           1 /* meters per pixel */
+#define PNG_SCALE_RADIAN          2 /* radians per pixel */
+#define PNG_SCALE_LAST            3 /* Not a valid value */
+
+/* These are for the pHYs chunk.  These values should NOT be changed. */
+#define PNG_RESOLUTION_UNKNOWN    0 /* pixels/unknown unit (aspect ratio) */
+#define PNG_RESOLUTION_METER      1 /* pixels/meter */
+#define PNG_RESOLUTION_LAST       2 /* Not a valid value */
+
+/* These are for the sRGB chunk.  These values should NOT be changed. */
+#define PNG_sRGB_INTENT_PERCEPTUAL 0
+#define PNG_sRGB_INTENT_RELATIVE   1
+#define PNG_sRGB_INTENT_SATURATION 2
+#define PNG_sRGB_INTENT_ABSOLUTE   3
+#define PNG_sRGB_INTENT_LAST       4 /* Not a valid value */
+
+/* This is for text chunks */
+#define PNG_KEYWORD_MAX_LENGTH     79
+
+/* Maximum number of entries in PLTE/sPLT/tRNS arrays */
+#define PNG_MAX_PALETTE_LENGTH    256
+
+/* These determine if an ancillary chunk's data has been successfully read
+ * from the PNG header, or if the application has filled in the corresponding
+ * data in the info_struct to be written into the output file.  The values
+ * of the PNG_INFO_<chunk> defines should NOT be changed.
+ */
+#define PNG_INFO_gAMA 0x0001
+#define PNG_INFO_sBIT 0x0002
+#define PNG_INFO_cHRM 0x0004
+#define PNG_INFO_PLTE 0x0008
+#define PNG_INFO_tRNS 0x0010
+#define PNG_INFO_bKGD 0x0020
+#define PNG_INFO_hIST 0x0040
+#define PNG_INFO_pHYs 0x0080
+#define PNG_INFO_oFFs 0x0100
+#define PNG_INFO_tIME 0x0200
+#define PNG_INFO_pCAL 0x0400
+#define PNG_INFO_sRGB 0x0800   /* GR-P, 0.96a */
+#define PNG_INFO_iCCP 0x1000   /* ESR, 1.0.6 */
+#define PNG_INFO_sPLT 0x2000   /* ESR, 1.0.6 */
+#define PNG_INFO_sCAL 0x4000   /* ESR, 1.0.6 */
+#define PNG_INFO_IDAT 0x8000L  /* ESR, 1.0.6 */
+
+/* This is used for the transformation routines, as some of them
+ * change these values for the row.  It also should enable using
+ * the routines for other purposes.
+ */
+typedef struct png_row_info_struct
+{
+   png_uint_32 width; /* width of row */
+   png_uint_32 rowbytes; /* number of bytes in row */
+   png_byte color_type; /* color type of row */
+   png_byte bit_depth; /* bit depth of row */
+   png_byte channels; /* number of channels (1, 2, 3, or 4) */
+   png_byte pixel_depth; /* bits per pixel (depth * channels) */
+} png_row_info;
+
+typedef png_row_info FAR * png_row_infop;
+typedef png_row_info FAR * FAR * png_row_infopp;
+
+/* These are the function types for the I/O functions and for the functions
+ * that allow the user to override the default I/O functions with his or her
+ * own.  The png_error_ptr type should match that of user-supplied warning
+ * and error functions, while the png_rw_ptr type should match that of the
+ * user read/write data functions.
+ */
+typedef struct png_struct_def png_struct;
+typedef png_struct FAR * png_structp;
+
+typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp));
+typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t));
+typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp));
+typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32,
+   int));
+typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32,
+   int));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop));
+typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop));
+typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep,
+   png_uint_32, int));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp,
+    png_row_infop, png_bytep));
+#endif
+
+#if defined(PNG_USER_CHUNKS_SUPPORTED)
+typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp));
+#endif
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp));
+#endif
+
+/* Transform masks for the high-level interface */
+#define PNG_TRANSFORM_IDENTITY       0x0000    /* read and write */
+#define PNG_TRANSFORM_STRIP_16       0x0001    /* read only */
+#define PNG_TRANSFORM_STRIP_ALPHA    0x0002    /* read only */
+#define PNG_TRANSFORM_PACKING        0x0004    /* read and write */
+#define PNG_TRANSFORM_PACKSWAP       0x0008    /* read and write */
+#define PNG_TRANSFORM_EXPAND         0x0010    /* read only */
+#define PNG_TRANSFORM_INVERT_MONO    0x0020    /* read and write */
+#define PNG_TRANSFORM_SHIFT          0x0040    /* read and write */
+#define PNG_TRANSFORM_BGR            0x0080    /* read and write */
+#define PNG_TRANSFORM_SWAP_ALPHA     0x0100    /* read and write */
+#define PNG_TRANSFORM_SWAP_ENDIAN    0x0200    /* read and write */
+#define PNG_TRANSFORM_INVERT_ALPHA   0x0400    /* read and write */
+#define PNG_TRANSFORM_STRIP_FILLER   0x0800    /* WRITE only */
+
+/* Flags for MNG supported features */
+#define PNG_FLAG_MNG_EMPTY_PLTE     0x01
+#define PNG_FLAG_MNG_FILTER_64      0x04
+#define PNG_ALL_MNG_FEATURES        0x05
+
+typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t));
+typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp));
+
+/* The structure that holds the information to read and write PNG files.
+ * The only people who need to care about what is inside of this are the
+ * people who will be modifying the library for their own special needs.
+ * It should NOT be accessed directly by an application, except to store
+ * the jmp_buf.
+ */
+
+struct png_struct_def
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf jmpbuf;            /* used in png_error */
+#endif
+   png_error_ptr error_fn;    /* function for printing errors and aborting */
+   png_error_ptr warning_fn;  /* function for printing warnings */
+   png_voidp error_ptr;       /* user supplied struct for error functions */
+   png_rw_ptr write_data_fn;  /* function for writing output data */
+   png_rw_ptr read_data_fn;   /* function for reading input data */
+   png_voidp io_ptr;          /* ptr to application struct for I/O functions */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   png_user_transform_ptr read_user_transform_fn; /* user read transform */
+#endif
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   png_user_transform_ptr write_user_transform_fn; /* user write transform */
+#endif
+
+/* These were added in libpng-1.0.2 */
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   png_voidp user_transform_ptr; /* user supplied struct for user transform */
+   png_byte user_transform_depth;    /* bit depth of user transformed pixels */
+   png_byte user_transform_channels; /* channels in user transformed pixels */
+#endif
+#endif
+
+   png_uint_32 mode;          /* tells us where we are in the PNG file */
+   png_uint_32 flags;         /* flags indicating various things to libpng */
+   png_uint_32 transformations; /* which transformations to perform */
+
+   z_stream zstream;          /* pointer to decompression structure (below) */
+   png_bytep zbuf;            /* buffer for zlib */
+   png_size_t zbuf_size;      /* size of zbuf */
+   int zlib_level;            /* holds zlib compression level */
+   int zlib_method;           /* holds zlib compression method */
+   int zlib_window_bits;      /* holds zlib compression window bits */
+   int zlib_mem_level;        /* holds zlib compression memory level */
+   int zlib_strategy;         /* holds zlib compression strategy */
+
+   png_uint_32 width;         /* width of image in pixels */
+   png_uint_32 height;        /* height of image in pixels */
+   png_uint_32 num_rows;      /* number of rows in current pass */
+   png_uint_32 usr_width;     /* width of row at start of write */
+   png_uint_32 rowbytes;      /* size of row in bytes */
+   png_uint_32 irowbytes;     /* size of current interlaced row in bytes */
+   png_uint_32 iwidth;        /* width of current interlaced row in pixels */
+   png_uint_32 row_number;    /* current row in interlace pass */
+   png_bytep prev_row;        /* buffer to save previous (unfiltered) row */
+   png_bytep row_buf;         /* buffer to save current (unfiltered) row */
+   png_bytep sub_row;         /* buffer to save "sub" row when filtering */
+   png_bytep up_row;          /* buffer to save "up" row when filtering */
+   png_bytep avg_row;         /* buffer to save "avg" row when filtering */
+   png_bytep paeth_row;       /* buffer to save "Paeth" row when filtering */
+   png_row_info row_info;     /* used for transformation routines */
+
+   png_uint_32 idat_size;     /* current IDAT size for read */
+   png_uint_32 crc;           /* current chunk CRC value */
+   png_colorp palette;        /* palette from the input file */
+   png_uint_16 num_palette;   /* number of color entries in palette */
+   png_uint_16 num_trans;     /* number of transparency values */
+   png_byte chunk_name[5];    /* null-terminated name of current chunk */
+   png_byte compression;      /* file compression type (always 0) */
+   png_byte filter;           /* file filter type (always 0) */
+   png_byte interlaced;       /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */
+   png_byte pass;             /* current interlace pass (0 - 6) */
+   png_byte do_filter;        /* row filter flags (see PNG_FILTER_ below ) */
+   png_byte color_type;       /* color type of file */
+   png_byte bit_depth;        /* bit depth of file */
+   png_byte usr_bit_depth;    /* bit depth of users row */
+   png_byte pixel_depth;      /* number of bits per pixel */
+   png_byte channels;         /* number of channels in file */
+   png_byte usr_channels;     /* channels at start of write */
+   png_byte sig_bytes;        /* magic bytes read/written from start of file */
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+#ifdef PNG_LEGACY_SUPPORTED
+   png_byte filler;           /* filler byte for pixel expansion */
+#else
+   png_uint_16 filler;           /* filler bytes for pixel expansion */
+#endif
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED)
+   png_byte background_gamma_type;
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+   float background_gamma;
+#  endif
+   png_color_16 background;   /* background color in screen gamma space */
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   png_color_16 background_1; /* background normalized to gamma 1.0 */
+#endif
+#endif /* PNG_bKGD_SUPPORTED */
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_flush_ptr output_flush_fn;/* Function for flushing output */
+   png_uint_32 flush_dist;    /* how many rows apart to flush, 0 - no flush */
+   png_uint_32 flush_rows;    /* number of rows written since last flush */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   int gamma_shift;      /* number of "insignificant" bits 16-bit gamma */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float gamma;          /* file gamma value */
+   float screen_gamma;   /* screen gamma value (display_exponent) */
+#endif
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep gamma_table;     /* gamma table for 8-bit depth files */
+   png_bytep gamma_from_1;    /* converts from 1.0 to screen */
+   png_bytep gamma_to_1;      /* converts from file to 1.0 */
+   png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */
+   png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */
+   png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED)
+   png_color_8 sig_bit;       /* significant bits in each available channel */
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+   png_color_8 shift;         /* shift for significant bit tranformation */
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \
+ || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_bytep trans;           /* transparency values for paletted files */
+   png_color_16 trans_values; /* transparency values for non-paletted files */
+#endif
+
+   png_read_status_ptr read_row_fn;   /* called after each row is decoded */
+   png_write_status_ptr write_row_fn; /* called after each row is encoded */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_progressive_info_ptr info_fn; /* called after header data fully read */
+   png_progressive_row_ptr row_fn;   /* called after each prog. row is decoded */
+   png_progressive_end_ptr end_fn;   /* called after image is complete */
+   png_bytep save_buffer_ptr;        /* current location in save_buffer */
+   png_bytep save_buffer;            /* buffer for previously read data */
+   png_bytep current_buffer_ptr;     /* current location in current_buffer */
+   png_bytep current_buffer;         /* buffer for recently used data */
+   png_uint_32 push_length;          /* size of current input chunk */
+   png_uint_32 skip_length;          /* bytes to skip in input data */
+   png_size_t save_buffer_size;      /* amount of data now in save_buffer */
+   png_size_t save_buffer_max;       /* total size of save_buffer */
+   png_size_t buffer_size;           /* total amount of available input data */
+   png_size_t current_buffer_size;   /* amount of data now in current_buffer */
+   int process_mode;                 /* what push library is currently doing */
+   int cur_palette;                  /* current push library palette index */
+
+#  if defined(PNG_TEXT_SUPPORTED)
+     png_size_t current_text_size;   /* current size of text input data */
+     png_size_t current_text_left;   /* how much text left to read in input */
+     png_charp current_text;         /* current text chunk buffer */
+     png_charp current_text_ptr;     /* current location in current_text */
+#  endif /* PNG_PROGRESSIVE_READ_SUPPORTED && PNG_TEXT_SUPPORTED */
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* for the Borland special 64K segment handler */
+   png_bytepp offset_table_ptr;
+   png_bytep offset_table;
+   png_uint_16 offset_table_number;
+   png_uint_16 offset_table_count;
+   png_uint_16 offset_table_count_free;
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   png_bytep palette_lookup;         /* lookup table for dithering */
+   png_bytep dither_index;           /* index translation for palette files */
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED)
+   png_uint_16p hist;                /* histogram */
+#endif
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_byte heuristic_method;        /* heuristic for row filter selection */
+   png_byte num_prev_filters;        /* number of weights for previous rows */
+   png_bytep prev_filters;           /* filter type(s) of previous row(s) */
+   png_uint_16p filter_weights;      /* weight(s) for previous line(s) */
+   png_uint_16p inv_filter_weights;  /* 1/weight(s) for previous line(s) */
+   png_uint_16p filter_costs;        /* relative filter calculation cost */
+   png_uint_16p inv_filter_costs;    /* 1/relative filter calculation cost */
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_charp time_buffer;            /* String to hold RFC 1123 time text */
+#endif
+
+/* New members added in libpng-1.0.6 */
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_uint_32 free_me;       /* flags items libpng is responsible for freeing */
+#endif
+
+#if defined(PNG_USER_CHUNKS_SUPPORTED)
+   png_voidp user_chunk_ptr;
+   png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   int num_chunk_list;
+   png_bytep chunk_list;
+#endif
+
+/* New members added in libpng-1.0.3 */
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   png_byte rgb_to_gray_status;
+   /* These were changed from png_byte in libpng-1.0.6 */
+   png_uint_16 rgb_to_gray_red_coeff;
+   png_uint_16 rgb_to_gray_green_coeff;
+   png_uint_16 rgb_to_gray_blue_coeff;
+#endif
+
+/* New member added in libpng-1.0.4 (renamed in 1.0.9) */
+#if defined(PNG_MNG_FEATURES_SUPPORTED) || \
+    defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+    defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+/* changed from png_byte to png_uint_32 at version 1.2.0 */
+#ifdef PNG_1_0_X
+   png_byte mng_features_permitted;
+#else
+   png_uint_32 mng_features_permitted;
+#endif /* PNG_1_0_X */
+#endif
+
+/* New member added in libpng-1.0.7 */
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_fixed_point int_gamma;
+#endif
+
+/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   png_byte filter_type;
+#endif
+
+#if defined(PNG_1_0_X)
+/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */
+   png_uint_32 row_buf_size;
+#endif
+
+/* New members added in libpng-1.2.0 */
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#  if !defined(PNG_1_0_X)
+#    if defined(PNG_MMX_CODE_SUPPORTED)
+   png_byte     mmx_bitdepth_threshold;
+   png_uint_32  mmx_rowbytes_threshold;
+#    endif
+   png_uint_32  asm_flags;
+#  endif
+#endif
+
+/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_voidp mem_ptr;                /* user supplied struct for mem functions */
+   png_malloc_ptr malloc_fn;         /* function for allocating memory */
+   png_free_ptr free_fn;             /* function for freeing memory */
+#endif
+
+/* New member added in libpng-1.0.13 and 1.2.0 */
+   png_bytep big_row_buf;         /* buffer to save current (unfiltered) row */
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* The following three members were added at version 1.0.14 and 1.2.4 */
+   png_bytep dither_sort;            /* working sort array */
+   png_bytep index_to_palette;       /* where the original index currently is */
+                                     /* in the palette */
+   png_bytep palette_to_index;       /* which original index points to this */
+                                     /* palette color */
+#endif
+
+/* New members added in libpng-1.0.16 and 1.2.6 */
+   png_byte compression_type;
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_uint_32 user_width_max;
+   png_uint_32 user_height_max;
+#endif
+
+/* New member added in libpng-1.0.25 and 1.2.17 */
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+   /* storage for unknown chunk that the library doesn't recognize. */
+   png_unknown_chunk unknown_chunk;
+#endif
+};
+
+
+/* This triggers a compiler error in png.c, if png.c and png.h
+ * do not agree upon the version number.
+ */
+typedef png_structp version_1_2_19;
+
+typedef png_struct FAR * FAR * png_structpp;
+
+/* Here are the function definitions most commonly used.  This is not
+ * the place to find out how to use libpng.  See libpng.txt for the
+ * full explanation, see example.c for the summary.  This just provides
+ * a simple one line description of the use of each function.
+ */
+
+/* Returns the version number of the library */
+extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void));
+
+/* Tell lib we have already handled the first <num_bytes> magic bytes.
+ * Handling more than 8 bytes from the beginning of the file is an error.
+ */
+extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr,
+   int num_bytes));
+
+/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a
+ * PNG file.  Returns zero if the supplied bytes match the 8-byte PNG
+ * signature, and non-zero otherwise.  Having num_to_check == 0 or
+ * start > 7 will always fail (ie return non-zero).
+ */
+extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start,
+   png_size_t num_to_check));
+
+/* Simple signature checking function.  This is the same as calling
+ * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n).
+ */
+extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num));
+
+/* Allocate and initialize png_ptr struct for reading, and any other memory. */
+extern PNG_EXPORT(png_structp,png_create_read_struct)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn));
+
+/* Allocate and initialize png_ptr struct for writing, and any other memory */
+extern PNG_EXPORT(png_structp,png_create_write_struct)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn));
+
+#ifdef PNG_WRITE_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size)
+   PNGARG((png_structp png_ptr));
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+extern PNG_EXPORT(void,png_set_compression_buffer_size)
+   PNGARG((png_structp png_ptr, png_uint_32 size));
+#endif
+
+/* Reset the compression stream */
+extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr));
+
+/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */
+#ifdef PNG_USER_MEM_SUPPORTED
+extern PNG_EXPORT(png_structp,png_create_read_struct_2)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+extern PNG_EXPORT(png_structp,png_create_write_struct_2)
+   PNGARG((png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+#endif
+
+/* Write a PNG chunk - size, type, (optional) data, CRC. */
+extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr,
+   png_bytep chunk_name, png_bytep data, png_size_t length));
+
+/* Write the start of a PNG chunk - length and chunk name. */
+extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr,
+   png_bytep chunk_name, png_uint_32 length));
+
+/* Write the data of a PNG chunk started with png_write_chunk_start(). */
+extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+/* Finish a chunk started with png_write_chunk_start() (includes CRC). */
+extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr));
+
+/* Allocate and initialize the info structure */
+extern PNG_EXPORT(png_infop,png_create_info_struct)
+   PNGARG((png_structp png_ptr));
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* Initialize the info structure (old interface - DEPRECATED) */
+extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr));
+#undef png_info_init
+#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\
+    png_sizeof(png_info));
+#endif
+
+extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr,
+    png_size_t png_info_struct_size));
+
+/* Writes all the PNG information before the image. */
+extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read the information before the actual image data. */
+extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+extern PNG_EXPORT(png_charp,png_convert_to_rfc1123)
+   PNGARG((png_structp png_ptr, png_timep ptime));
+#endif
+
+#if !defined(_WIN32_WCE)
+/* "time.h" functions are not supported on WindowsCE */
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+/* convert from a struct tm to png_time */
+extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime,
+   struct tm FAR * ttime));
+
+/* convert from time_t to png_time.  Uses gmtime() */
+extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime,
+   time_t ttime));
+#endif /* PNG_WRITE_tIME_SUPPORTED */
+#endif /* _WIN32_WCE */
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */
+extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr));
+#if !defined(PNG_1_0_X)
+extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp
+  png_ptr));
+#endif
+extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr));
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* Deprecated */
+extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr));
+#endif
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* Use blue, green, red order for pixels. */
+extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+/* Expand the grayscale to 24-bit RGB if necessary. */
+extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* Reduce RGB to grayscale. */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr,
+   int error_action, double red, double green ));
+#endif
+extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr,
+   int error_action, png_fixed_point red, png_fixed_point green ));
+extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp
+   png_ptr));
+#endif
+
+extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth,
+   png_colorp palette));
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */
+extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr,
+   png_uint_32 filler, int flags));
+/* The values of the PNG_FILLER_ defines should NOT be changed */
+#define PNG_FILLER_BEFORE 0
+#define PNG_FILLER_AFTER 1
+/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */
+#if !defined(PNG_1_0_X)
+extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr,
+   png_uint_32 filler, int flags));
+#endif
+#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* Swap bytes in 16-bit depth files. */
+extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */
+extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* Swap packing order of pixels in bytes. */
+extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Converts files to legal bit depths. */
+extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr,
+   png_color_8p true_bits));
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Have the code handle the interlacing.  Returns the number of passes. */
+extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+/* Invert monochrome files */
+extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* Handle alpha and tRNS by replacing with a background color. */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr,
+   png_color_16p background_color, int background_gamma_code,
+   int need_expand, double background_gamma));
+#endif
+#define PNG_BACKGROUND_GAMMA_UNKNOWN 0
+#define PNG_BACKGROUND_GAMMA_SCREEN  1
+#define PNG_BACKGROUND_GAMMA_FILE    2
+#define PNG_BACKGROUND_GAMMA_UNIQUE  3
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* strip the second byte of information from a 16-bit depth file. */
+extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* Turn on dithering, and reduce the palette to the number of colors available. */
+extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr,
+   png_colorp palette, int num_palette, int maximum_colors,
+   png_uint_16p histogram, int full_dither));
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Handle gamma correction. Screen_gamma=(display_exponent) */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr,
+   double screen_gamma, double default_file_gamma));
+#endif
+#endif
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+    defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */
+/* Deprecated and will be removed.  Use png_permit_mng_features() instead. */
+extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr,
+   int empty_plte_permitted));
+#endif
+#endif
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+/* Set how many lines between output flushes - 0 for no flushing */
+extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows));
+/* Flush the current PNG output buffer */
+extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr));
+#endif
+
+/* optional update palette with requested transformations */
+extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr));
+
+/* optional call to update the users info structure */
+extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read one or more rows of image data. */
+extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr,
+   png_bytepp row, png_bytepp display_row, png_uint_32 num_rows));
+#endif
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read a row of data. */
+extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr,
+   png_bytep row,
+   png_bytep display_row));
+#endif
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read the whole image into memory at once. */
+extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr,
+   png_bytepp image));
+#endif
+
+/* write a row of image data */
+extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr,
+   png_bytep row));
+
+/* write a few rows of image data */
+extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr,
+   png_bytepp row, png_uint_32 num_rows));
+
+/* write the image data */
+extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr,
+   png_bytepp image));
+
+/* writes the end of the PNG file. */
+extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* read the end of the PNG file. */
+extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+/* free any memory associated with the png_info_struct */
+extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr,
+   png_infopp info_ptr_ptr));
+
+/* free any memory associated with the png_struct and the png_info_structs */
+extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp
+   png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr));
+
+/* free all memory used by the read (old method - NOT DLL EXPORTED) */
+extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_infop end_info_ptr));
+
+/* free any memory associated with the png_struct and the png_info_structs */
+extern PNG_EXPORT(void,png_destroy_write_struct)
+   PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr));
+
+/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */
+extern void png_write_destroy PNGARG((png_structp png_ptr));
+
+/* set the libpng method of handling chunk CRC errors */
+extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr,
+   int crit_action, int ancil_action));
+
+/* Values for png_set_crc_action() to say how to handle CRC errors in
+ * ancillary and critical chunks, and whether to use the data contained
+ * therein.  Note that it is impossible to "discard" data in a critical
+ * chunk.  For versions prior to 0.90, the action was always error/quit,
+ * whereas in version 0.90 and later, the action for CRC errors in ancillary
+ * chunks is warn/discard.  These values should NOT be changed.
+ *
+ *      value                       action:critical     action:ancillary
+ */
+#define PNG_CRC_DEFAULT       0  /* error/quit          warn/discard data */
+#define PNG_CRC_ERROR_QUIT    1  /* error/quit          error/quit        */
+#define PNG_CRC_WARN_DISCARD  2  /* (INVALID)           warn/discard data */
+#define PNG_CRC_WARN_USE      3  /* warn/use data       warn/use data     */
+#define PNG_CRC_QUIET_USE     4  /* quiet/use data      quiet/use data    */
+#define PNG_CRC_NO_CHANGE     5  /* use current value   use current value */
+
+/* These functions give the user control over the scan-line filtering in
+ * libpng and the compression methods used by zlib.  These functions are
+ * mainly useful for testing, as the defaults should work with most users.
+ * Those users who are tight on memory or want faster performance at the
+ * expense of compression can modify them.  See the compression library
+ * header file (zlib.h) for an explination of the compression functions.
+ */
+
+/* set the filtering method(s) used by libpng.  Currently, the only valid
+ * value for "method" is 0.
+ */
+extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method,
+   int filters));
+
+/* Flags for png_set_filter() to say which filters to use.  The flags
+ * are chosen so that they don't conflict with real filter types
+ * below, in case they are supplied instead of the #defined constants.
+ * These values should NOT be changed.
+ */
+#define PNG_NO_FILTERS     0x00
+#define PNG_FILTER_NONE    0x08
+#define PNG_FILTER_SUB     0x10
+#define PNG_FILTER_UP      0x20
+#define PNG_FILTER_AVG     0x40
+#define PNG_FILTER_PAETH   0x80
+#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \
+                         PNG_FILTER_AVG | PNG_FILTER_PAETH)
+
+/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now.
+ * These defines should NOT be changed.
+ */
+#define PNG_FILTER_VALUE_NONE  0
+#define PNG_FILTER_VALUE_SUB   1
+#define PNG_FILTER_VALUE_UP    2
+#define PNG_FILTER_VALUE_AVG   3
+#define PNG_FILTER_VALUE_PAETH 4
+#define PNG_FILTER_VALUE_LAST  5
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */
+/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_
+ * defines, either the default (minimum-sum-of-absolute-differences), or
+ * the experimental method (weighted-minimum-sum-of-absolute-differences).
+ *
+ * Weights are factors >= 1.0, indicating how important it is to keep the
+ * filter type consistent between rows.  Larger numbers mean the current
+ * filter is that many times as likely to be the same as the "num_weights"
+ * previous filters.  This is cumulative for each previous row with a weight.
+ * There needs to be "num_weights" values in "filter_weights", or it can be
+ * NULL if the weights aren't being specified.  Weights have no influence on
+ * the selection of the first row filter.  Well chosen weights can (in theory)
+ * improve the compression for a given image.
+ *
+ * Costs are factors >= 1.0 indicating the relative decoding costs of a
+ * filter type.  Higher costs indicate more decoding expense, and are
+ * therefore less likely to be selected over a filter with lower computational
+ * costs.  There needs to be a value in "filter_costs" for each valid filter
+ * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't
+ * setting the costs.  Costs try to improve the speed of decompression without
+ * unduly increasing the compressed image size.
+ *
+ * A negative weight or cost indicates the default value is to be used, and
+ * values in the range [0.0, 1.0) indicate the value is to remain unchanged.
+ * The default values for both weights and costs are currently 1.0, but may
+ * change if good general weighting/cost heuristics can be found.  If both
+ * the weights and costs are set to 1.0, this degenerates the WEIGHTED method
+ * to the UNWEIGHTED method, but with added encoding time/computation.
+ */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr,
+   int heuristic_method, int num_weights, png_doublep filter_weights,
+   png_doublep filter_costs));
+#endif
+#endif /*  PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+/* Heuristic used for row filter selection.  These defines should NOT be
+ * changed.
+ */
+#define PNG_FILTER_HEURISTIC_DEFAULT    0  /* Currently "UNWEIGHTED" */
+#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1  /* Used by libpng < 0.95 */
+#define PNG_FILTER_HEURISTIC_WEIGHTED   2  /* Experimental feature */
+#define PNG_FILTER_HEURISTIC_LAST       3  /* Not a valid value */
+
+/* Set the library compression level.  Currently, valid values range from
+ * 0 - 9, corresponding directly to the zlib compression levels 0 - 9
+ * (0 - no compression, 9 - "maximal" compression).  Note that tests have
+ * shown that zlib compression levels 3-6 usually perform as well as level 9
+ * for PNG images, and do considerably fewer caclulations.  In the future,
+ * these values may not correspond directly to the zlib compression levels.
+ */
+extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr,
+   int level));
+
+extern PNG_EXPORT(void,png_set_compression_mem_level)
+   PNGARG((png_structp png_ptr, int mem_level));
+
+extern PNG_EXPORT(void,png_set_compression_strategy)
+   PNGARG((png_structp png_ptr, int strategy));
+
+extern PNG_EXPORT(void,png_set_compression_window_bits)
+   PNGARG((png_structp png_ptr, int window_bits));
+
+extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr,
+   int method));
+
+/* These next functions are called for input/output, memory, and error
+ * handling.  They are in the file pngrio.c, pngwio.c, and pngerror.c,
+ * and call standard C I/O routines such as fread(), fwrite(), and
+ * fprintf().  These functions can be made to use other I/O routines
+ * at run time for those applications that need to handle I/O in a
+ * different manner by calling png_set_???_fn().  See libpng.txt for
+ * more information.
+ */
+
+#if !defined(PNG_NO_STDIO)
+/* Initialize the input/output for the PNG file to the default functions. */
+extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp));
+#endif
+
+/* Replace the (error and abort), and warning functions with user
+ * supplied functions.  If no messages are to be printed you must still
+ * write and use replacement functions. The replacement error_fn should
+ * still do a longjmp to the last setjmp location if you are using this
+ * method of error handling.  If error_fn or warning_fn is NULL, the
+ * default function will be used.
+ */
+
+extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr,
+   png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn));
+
+/* Return the user pointer associated with the error functions */
+extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr));
+
+/* Replace the default data output functions with a user supplied one(s).
+ * If buffered output is not used, then output_flush_fn can be set to NULL.
+ * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time
+ * output_flush_fn will be ignored (and thus can be NULL).
+ */
+extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr,
+   png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn));
+
+/* Replace the default data input function with a user supplied one. */
+extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr,
+   png_voidp io_ptr, png_rw_ptr read_data_fn));
+
+/* Return the user pointer associated with the I/O functions */
+extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr));
+
+extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr,
+   png_read_status_ptr read_row_fn));
+
+extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr,
+   png_write_status_ptr write_row_fn));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* Replace the default memory allocation functions with user supplied one(s). */
+extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr,
+   png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn));
+/* Return the user pointer associated with the memory functions */
+extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp
+   png_ptr, png_user_transform_ptr read_user_transform_fn));
+#endif
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp
+   png_ptr, png_user_transform_ptr write_user_transform_fn));
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp
+   png_ptr, png_voidp user_transform_ptr, int user_transform_depth,
+   int user_transform_channels));
+/* Return the user pointer associated with the user transform functions */
+extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr)
+   PNGARG((png_structp png_ptr));
+#endif
+
+#ifdef PNG_USER_CHUNKS_SUPPORTED
+extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr,
+   png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn));
+extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp
+   png_ptr));
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+/* Sets the function callbacks for the push reader, and a pointer to a
+ * user-defined structure available to the callback functions.
+ */
+extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr,
+   png_voidp progressive_ptr,
+   png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+   png_progressive_end_ptr end_fn));
+
+/* returns the user pointer associated with the push read functions */
+extern PNG_EXPORT(png_voidp,png_get_progressive_ptr)
+   PNGARG((png_structp png_ptr));
+
+/* function to be called when data becomes available */
+extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep buffer, png_size_t buffer_size));
+
+/* function that combines rows.  Not very much different than the
+ * png_combine_row() call.  Is this even used?????
+ */
+extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr,
+   png_bytep old_row, png_bytep new_row));
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+
+#if defined(PNG_1_0_X)
+#  define png_malloc_warn png_malloc
+#else
+/* Added at libpng version 1.2.4 */
+extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+#endif
+
+/* frees a pointer allocated by png_malloc() */
+extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr));
+
+#if defined(PNG_1_0_X)
+/* Function to allocate memory for zlib. */
+extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items,
+   uInt size));
+
+/* Function to free memory for zlib */
+extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr));
+#endif
+
+/* Free data that was allocated internally */
+extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 free_me, int num));
+#ifdef PNG_FREE_ME_SUPPORTED
+/* Reassign responsibility for freeing existing data, whether allocated
+ * by libpng or by the application */
+extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int freer, png_uint_32 mask));
+#endif
+/* assignments for png_data_freer */
+#define PNG_DESTROY_WILL_FREE_DATA 1
+#define PNG_SET_WILL_FREE_DATA 1
+#define PNG_USER_WILL_FREE_DATA 2
+/* Flags for png_ptr->free_me and info_ptr->free_me */
+#define PNG_FREE_HIST 0x0008
+#define PNG_FREE_ICCP 0x0010
+#define PNG_FREE_SPLT 0x0020
+#define PNG_FREE_ROWS 0x0040
+#define PNG_FREE_PCAL 0x0080
+#define PNG_FREE_SCAL 0x0100
+#define PNG_FREE_UNKN 0x0200
+#define PNG_FREE_LIST 0x0400
+#define PNG_FREE_PLTE 0x1000
+#define PNG_FREE_TRNS 0x2000
+#define PNG_FREE_TEXT 0x4000
+#define PNG_FREE_ALL  0x7fff
+#define PNG_FREE_MUL  0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */
+
+#ifdef PNG_USER_MEM_SUPPORTED
+extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr,
+   png_uint_32 size));
+extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr,
+   png_voidp ptr));
+#endif
+
+extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr,
+   png_voidp s1, png_voidp s2, png_uint_32 size));
+
+extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr,
+   png_voidp s1, int value, png_uint_32 size));
+
+#if defined(USE_FAR_KEYWORD)  /* memory model conversion function */
+extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr,
+   int check));
+#endif /* USE_FAR_KEYWORD */
+
+/* Fatal error in PNG image of libpng - can't continue */
+extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr,
+   png_const_charp error_message));
+
+/* The same, but the chunk name is prepended to the error string. */
+extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr,
+   png_const_charp error_message));
+
+#ifndef PNG_NO_WARNINGS
+/* Non-fatal error in libpng.  Can continue, but may have a problem. */
+extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr,
+   png_const_charp warning_message));
+
+#ifdef PNG_READ_SUPPORTED
+/* Non-fatal error in libpng, chunk name is prepended to message. */
+extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr,
+   png_const_charp warning_message));
+#endif /* PNG_READ_SUPPORTED */
+#endif /* PNG_NO_WARNINGS */
+
+/* The png_set_<chunk> functions are for storing values in the png_info_struct.
+ * Similarly, the png_get_<chunk> calls are used to read values from the
+ * png_info_struct, either storing the parameters in the passed variables, or
+ * setting pointers into the png_info_struct where the data is stored.  The
+ * png_get_<chunk> functions return a non-zero value if the data was available
+ * in info_ptr, or return zero and do not change any of the parameters if the
+ * data was not available.
+ *
+ * These functions should be used instead of directly accessing png_info
+ * to avoid problems with future changes in the size and internal layout of
+ * png_info_struct.
+ */
+/* Returns "flag" if chunk data is valid in info_ptr. */
+extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr,
+png_infop info_ptr, png_uint_32 flag));
+
+/* Returns number of bytes needed to hold a transformed row. */
+extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+/* Returns row_pointers, which is an array of pointers to scanlines that was
+returned from png_read_png(). */
+extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+/* Set row_pointers, which is an array of pointers to scanlines for use
+by png_write_png(). */
+extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytepp row_pointers));
+#endif
+
+/* Returns number of color channels in image. */
+extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* Returns image width in pixels. */
+extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image height in pixels. */
+extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image bit_depth. */
+extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image color_type. */
+extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image filter_type. */
+extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image interlace_type. */
+extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image compression_type. */
+extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns image resolution in pixels per meter, from pHYs chunk data. */
+extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+/* Returns pixel aspect ratio, computed from pHYs chunk data.  */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+#endif
+
+/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */
+extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp
+png_ptr, png_infop info_ptr));
+
+#endif /* PNG_EASY_ACCESS_SUPPORTED */
+
+/* Returns pointer to signature string read from PNG header */
+extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#if defined(PNG_bKGD_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_16p *background));
+#endif
+
+#if defined(PNG_bKGD_SUPPORTED)
+extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_16p background));
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double *white_x, double *white_y, double *red_x,
+   double *red_y, double *green_x, double *green_y, double *blue_x,
+   double *blue_y));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point
+   *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y,
+   png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point
+   *int_blue_x, png_fixed_point *int_blue_y));
+#endif
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double white_x, double white_y, double red_x,
+   double red_y, double green_x, double green_y, double blue_x, double blue_y));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y,
+   png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point
+   int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x,
+   png_fixed_point int_blue_y));
+#endif
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double *file_gamma));
+#endif
+extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point *int_file_gamma));
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, double file_gamma));
+#endif
+extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_fixed_point int_file_gamma));
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_16p *hist));
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_16p hist));
+#endif
+
+extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *width, png_uint_32 *height,
+   int *bit_depth, int *color_type, int *interlace_method,
+   int *compression_method, int *filter_method));
+
+extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_method, int compression_method,
+   int filter_method));
+
+#if defined(PNG_oFFs_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y,
+   int *unit_type));
+#endif
+
+#if defined(PNG_oFFs_SUPPORTED)
+extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y,
+   int unit_type));
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1,
+   int *type, int *nparams, png_charp *units, png_charpp *params));
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1,
+   int type, int nparams, png_charp units, png_charpp params));
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type));
+#endif
+
+extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_colorp *palette, int *num_palette));
+
+extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_colorp palette, int num_palette));
+
+#if defined(PNG_sBIT_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_8p *sig_bit));
+#endif
+
+#if defined(PNG_sBIT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_color_8p sig_bit));
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *intent));
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int intent));
+extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int intent));
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charpp name, int *compression_type,
+   png_charpp profile, png_uint_32 *proflen));
+   /* Note to maintainer: profile should be png_bytepp */
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_charp name, int compression_type,
+   png_charp profile, png_uint_32 proflen));
+   /* Note to maintainer: profile should be png_bytep */
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_sPLT_tpp entries));
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_sPLT_tp entries, int nentries));
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+/* png_get_text also returns the number of text chunks in *num_text */
+extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp *text_ptr, int *num_text));
+#endif
+
+/*
+ *  Note while png_set_text() will accept a structure whose text,
+ *  language, and  translated keywords are NULL pointers, the structure
+ *  returned by png_get_text will always contain regular
+ *  zero-terminated C strings.  They might be empty strings but
+ *  they will never be NULL pointers.
+ */
+
+#if defined(PNG_TEXT_SUPPORTED)
+extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp text_ptr, int num_text));
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_timep *mod_time));
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_timep mod_time));
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep *trans, int *num_trans,
+   png_color_16p *trans_values));
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_bytep trans, int num_trans,
+   png_color_16p trans_values));
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *unit, double *width, double *height));
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight));
+#endif
+#endif
+#endif /* PNG_sCAL_SUPPORTED */
+
+#if defined(PNG_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int unit, double width, double height));
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int unit, png_charp swidth, png_charp sheight));
+#endif
+#endif
+#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+/* provide a list of chunks and how they are to be handled, if the built-in
+   handling or default unknown chunk handling is not desired.  Any chunks not
+   listed will be handled in the default manner.  The IHDR and IEND chunks
+   must not be listed.
+      keep = 0: follow default behaviour
+           = 1: do not keep
+           = 2: keep only if safe-to-copy
+           = 3: keep even if unsafe-to-copy
+*/
+extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp
+   png_ptr, int keep, png_bytep chunk_list, int num_chunks));
+extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns));
+extern PNG_EXPORT(void, png_set_unknown_chunk_location)
+   PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location));
+extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp
+   png_ptr, png_infop info_ptr, png_unknown_chunkpp entries));
+#endif
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep
+   chunk_name));
+#endif
+
+/* Png_free_data() will turn off the "valid" flag for anything it frees.
+   If you need to turn it off for a chunk that your application has freed,
+   you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */
+extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr,
+   png_infop info_ptr, int mask));
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+/* The "params" pointer is currently not used and is for future expansion. */
+extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr,
+                        png_infop info_ptr,
+                        int transforms,
+                        png_voidp params));
+extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr,
+                        png_infop info_ptr,
+                        int transforms,
+                        png_voidp params));
+#endif
+
+/* Define PNG_DEBUG at compile time for debugging information.  Higher
+ * numbers for PNG_DEBUG mean more debugging information.  This has
+ * only been added since version 0.95 so it is not implemented throughout
+ * libpng yet, but more support will be added as needed.
+ */
+#ifdef PNG_DEBUG
+#if (PNG_DEBUG > 0)
+#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER)
+#include <crtdbg.h>
+#if (PNG_DEBUG > 1)
+#define png_debug(l,m)  _RPT0(_CRT_WARN,m)
+#define png_debug1(l,m,p1)  _RPT1(_CRT_WARN,m,p1)
+#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2)
+#endif
+#else /* PNG_DEBUG_FILE || !_MSC_VER */
+#ifndef PNG_DEBUG_FILE
+#define PNG_DEBUG_FILE stderr
+#endif /* PNG_DEBUG_FILE */
+#if (PNG_DEBUG > 1)
+#define png_debug(l,m) \
+{ \
+     int num_tabs=l; \
+     fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \
+       (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \
+}
+#define png_debug1(l,m,p1) \
+{ \
+     int num_tabs=l; \
+     fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \
+       (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \
+}
+#define png_debug2(l,m,p1,p2) \
+{ \
+     int num_tabs=l; \
+     fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \
+       (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \
+}
+#endif /* (PNG_DEBUG > 1) */
+#endif /* _MSC_VER */
+#endif /* (PNG_DEBUG > 0) */
+#endif /* PNG_DEBUG */
+#ifndef png_debug
+#define png_debug(l, m)
+#endif
+#ifndef png_debug1
+#define png_debug1(l, m, p1)
+#endif
+#ifndef png_debug2
+#define png_debug2(l, m, p1, p2)
+#endif
+
+extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr));
+extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr));
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp
+   png_ptr, png_uint_32 mng_features_permitted));
+#endif
+
+/* For use in png_set_keep_unknown, added to version 1.2.6 */
+#define PNG_HANDLE_CHUNK_AS_DEFAULT   0
+#define PNG_HANDLE_CHUNK_NEVER        1
+#define PNG_HANDLE_CHUNK_IF_SAFE      2
+#define PNG_HANDLE_CHUNK_ALWAYS       3
+
+/* Added to version 1.2.0 */
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#if defined(PNG_MMX_CODE_SUPPORTED)
+#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED  0x01  /* not user-settable */
+#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU    0x02  /* not user-settable */
+#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  0x04
+#define PNG_ASM_FLAG_MMX_READ_INTERLACE    0x08
+#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB   0x10
+#define PNG_ASM_FLAG_MMX_READ_FILTER_UP    0x20
+#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG   0x40
+#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80
+#define PNG_ASM_FLAGS_INITIALIZED          0x80000000  /* not user-settable */
+
+#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  \
+                           | PNG_ASM_FLAG_MMX_READ_INTERLACE    \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_SUB   \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_UP    \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_AVG   \
+                           | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH )
+#define PNG_MMX_WRITE_FLAGS ( 0 )
+
+#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \
+                      | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU   \
+                      | PNG_MMX_READ_FLAGS                \
+                      | PNG_MMX_WRITE_FLAGS )
+
+#define PNG_SELECT_READ   1
+#define PNG_SELECT_WRITE  2
+#endif /* PNG_MMX_CODE_SUPPORTED */
+
+#if !defined(PNG_1_0_X)
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask)
+   PNGARG((int flag_select, int *compilerID));
+
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask)
+   PNGARG((int flag_select));
+
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_asm_flags)
+   PNGARG((png_structp png_ptr));
+
+/* pngget.c */
+extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold)
+   PNGARG((png_structp png_ptr));
+
+/* pngget.c */
+extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold)
+   PNGARG((png_structp png_ptr));
+
+/* pngset.c */
+extern PNG_EXPORT(void,png_set_asm_flags)
+   PNGARG((png_structp png_ptr, png_uint_32 asm_flags));
+
+/* pngset.c */
+extern PNG_EXPORT(void,png_set_mmx_thresholds)
+   PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold,
+   png_uint_32 mmx_rowbytes_threshold));
+
+#endif /* PNG_1_0_X */
+
+#if !defined(PNG_1_0_X)
+/* png.c, pnggccrd.c, or pngvcrd.c */
+extern PNG_EXPORT(int,png_mmx_support) PNGARG((void));
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */
+
+/* Strip the prepended error numbers ("#nnn ") from error and warning
+ * messages before passing them to the error or warning handler. */
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp
+   png_ptr, png_uint_32 strip_mode));
+#endif
+
+#endif /* PNG_1_0_X */
+
+/* Added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp
+   png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max));
+extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp
+   png_ptr));
+extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp
+   png_ptr));
+#endif
+
+/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */
+
+#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED
+/* With these routines we avoid an integer divide, which will be slower on
+ * most machines.  However, it does take more operations than the corresponding
+ * divide method, so it may be slower on a few RISC systems.  There are two
+ * shifts (by 8 or 16 bits) and an addition, versus a single integer divide.
+ *
+ * Note that the rounding factors are NOT supposed to be the same!  128 and
+ * 32768 are correct for the NODIV code; 127 and 32767 are correct for the
+ * standard method.
+ *
+ * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ]
+ */
+
+ /* fg and bg should be in `gamma 1.0' space; alpha is the opacity          */
+
+#  define png_composite(composite, fg, alpha, bg)                            \
+     { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \
+                        +        (png_uint_16)(bg)*(png_uint_16)(255 -       \
+                        (png_uint_16)(alpha)) + (png_uint_16)128);           \
+       (composite) = (png_byte)((temp + (temp >> 8)) >> 8); }
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \
+                        + (png_uint_32)(bg)*(png_uint_32)(65535L -           \
+                        (png_uint_32)(alpha)) + (png_uint_32)32768L);        \
+       (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); }
+
+#else  /* standard method using integer division */
+
+#  define png_composite(composite, fg, alpha, bg)                            \
+     (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) +    \
+       (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) +       \
+       (png_uint_16)127) / 255)
+
+#  define png_composite_16(composite, fg, alpha, bg)                         \
+     (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \
+       (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) +      \
+       (png_uint_32)32767) / (png_uint_32)65535L)
+
+#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */
+
+/* Inline macros to do direct reads of bytes from the input buffer.  These
+ * require that you are using an architecture that uses PNG byte ordering
+ * (MSB first) and supports unaligned data storage.  I think that PowerPC
+ * in big-endian mode and 680x0 are the only ones that will support this.
+ * The x86 line of processors definitely do not.  The png_get_int_32()
+ * routine also assumes we are using two's complement format for negative
+ * values, which is almost certainly true.
+ */
+#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED)
+#  define png_get_uint_32(buf) ( *((png_uint_32p) (buf)))
+#  define png_get_uint_16(buf) ( *((png_uint_16p) (buf)))
+#  define png_get_int_32(buf)  ( *((png_int_32p)  (buf)))
+#else
+extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf));
+extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf));
+extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf));
+#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */
+extern PNG_EXPORT(png_uint_32,png_get_uint_31)
+  PNGARG((png_structp png_ptr, png_bytep buf));
+/* No png_get_int_16 -- may be added if there's a real need for it. */
+
+/* Place a 32-bit number into a buffer in PNG byte order (big-endian).
+ */
+extern PNG_EXPORT(void,png_save_uint_32)
+   PNGARG((png_bytep buf, png_uint_32 i));
+extern PNG_EXPORT(void,png_save_int_32)
+   PNGARG((png_bytep buf, png_int_32 i));
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+extern PNG_EXPORT(void,png_save_uint_16)
+   PNGARG((png_bytep buf, unsigned int i));
+/* No png_save_int_16 -- may be added if there's a real need for it. */
+
+/* ************************************************************************* */
+
+/* These next functions are used internally in the code.  They generally
+ * shouldn't be used unless you are writing code to add or replace some
+ * functionality in libpng.  More information about most functions can
+ * be found in the files where the functions are located.
+ */
+
+
+/* Various modes of operation, that are visible to applications because
+ * they are used for unknown chunk location.
+ */
+#define PNG_HAVE_IHDR               0x01
+#define PNG_HAVE_PLTE               0x02
+#define PNG_HAVE_IDAT               0x04
+#define PNG_AFTER_IDAT              0x08 /* Have complete zlib datastream */
+#define PNG_HAVE_IEND               0x10
+
+#if defined(PNG_INTERNAL)
+
+/* More modes of operation.  Note that after an init, mode is set to
+ * zero automatically when the structure is created.
+ */
+#define PNG_HAVE_gAMA               0x20
+#define PNG_HAVE_cHRM               0x40
+#define PNG_HAVE_sRGB               0x80
+#define PNG_HAVE_CHUNK_HEADER      0x100
+#define PNG_WROTE_tIME             0x200
+#define PNG_WROTE_INFO_BEFORE_PLTE 0x400
+#define PNG_BACKGROUND_IS_GRAY     0x800
+#define PNG_HAVE_PNG_SIGNATURE    0x1000
+#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */
+
+/* flags for the transformations the PNG library does on the image data */
+#define PNG_BGR                0x0001
+#define PNG_INTERLACE          0x0002
+#define PNG_PACK               0x0004
+#define PNG_SHIFT              0x0008
+#define PNG_SWAP_BYTES         0x0010
+#define PNG_INVERT_MONO        0x0020
+#define PNG_DITHER             0x0040
+#define PNG_BACKGROUND         0x0080
+#define PNG_BACKGROUND_EXPAND  0x0100
+                          /*   0x0200 unused */
+#define PNG_16_TO_8            0x0400
+#define PNG_RGBA               0x0800
+#define PNG_EXPAND             0x1000
+#define PNG_GAMMA              0x2000
+#define PNG_GRAY_TO_RGB        0x4000
+#define PNG_FILLER             0x8000L
+#define PNG_PACKSWAP          0x10000L
+#define PNG_SWAP_ALPHA        0x20000L
+#define PNG_STRIP_ALPHA       0x40000L
+#define PNG_INVERT_ALPHA      0x80000L
+#define PNG_USER_TRANSFORM   0x100000L
+#define PNG_RGB_TO_GRAY_ERR  0x200000L
+#define PNG_RGB_TO_GRAY_WARN 0x400000L
+#define PNG_RGB_TO_GRAY      0x600000L  /* two bits, RGB_TO_GRAY_ERR|WARN */
+                       /*    0x800000L     Unused */
+#define PNG_ADD_ALPHA       0x1000000L  /* Added to libpng-1.2.7 */
+#define PNG_EXPAND_tRNS     0x2000000L  /* Added to libpng-1.2.9 */
+                       /*   0x4000000L  unused */
+                       /*   0x8000000L  unused */
+                       /*  0x10000000L  unused */
+                       /*  0x20000000L  unused */
+                       /*  0x40000000L  unused */
+
+/* flags for png_create_struct */
+#define PNG_STRUCT_PNG   0x0001
+#define PNG_STRUCT_INFO  0x0002
+
+/* Scaling factor for filter heuristic weighting calculations */
+#define PNG_WEIGHT_SHIFT 8
+#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT))
+#define PNG_COST_SHIFT 3
+#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT))
+
+/* flags for the png_ptr->flags rather than declaring a byte for each one */
+#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY     0x0001
+#define PNG_FLAG_ZLIB_CUSTOM_LEVEL        0x0002
+#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL    0x0004
+#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS  0x0008
+#define PNG_FLAG_ZLIB_CUSTOM_METHOD       0x0010
+#define PNG_FLAG_ZLIB_FINISHED            0x0020
+#define PNG_FLAG_ROW_INIT                 0x0040
+#define PNG_FLAG_FILLER_AFTER             0x0080
+#define PNG_FLAG_CRC_ANCILLARY_USE        0x0100
+#define PNG_FLAG_CRC_ANCILLARY_NOWARN     0x0200
+#define PNG_FLAG_CRC_CRITICAL_USE         0x0400
+#define PNG_FLAG_CRC_CRITICAL_IGNORE      0x0800
+#define PNG_FLAG_FREE_PLTE                0x1000
+#define PNG_FLAG_FREE_TRNS                0x2000
+#define PNG_FLAG_FREE_HIST                0x4000
+#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS      0x8000L
+#define PNG_FLAG_KEEP_UNSAFE_CHUNKS       0x10000L
+#define PNG_FLAG_LIBRARY_MISMATCH         0x20000L
+#define PNG_FLAG_STRIP_ERROR_NUMBERS      0x40000L
+#define PNG_FLAG_STRIP_ERROR_TEXT         0x80000L
+#define PNG_FLAG_MALLOC_NULL_MEM_OK       0x100000L
+#define PNG_FLAG_ADD_ALPHA                0x200000L  /* Added to libpng-1.2.8 */
+#define PNG_FLAG_STRIP_ALPHA              0x400000L  /* Added to libpng-1.2.8 */
+                                  /*      0x800000L  unused */
+                                  /*     0x1000000L  unused */
+                                  /*     0x2000000L  unused */
+                                  /*     0x4000000L  unused */
+                                  /*     0x8000000L  unused */
+                                  /*    0x10000000L  unused */
+                                  /*    0x20000000L  unused */
+                                  /*    0x40000000L  unused */
+
+#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \
+                                     PNG_FLAG_CRC_ANCILLARY_NOWARN)
+
+#define PNG_FLAG_CRC_CRITICAL_MASK  (PNG_FLAG_CRC_CRITICAL_USE | \
+                                     PNG_FLAG_CRC_CRITICAL_IGNORE)
+
+#define PNG_FLAG_CRC_MASK           (PNG_FLAG_CRC_ANCILLARY_MASK | \
+                                     PNG_FLAG_CRC_CRITICAL_MASK)
+
+/* save typing and make code easier to understand */
+
+#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \
+   abs((int)((c1).green) - (int)((c2).green)) + \
+   abs((int)((c1).blue) - (int)((c2).blue)))
+
+/* Added to libpng-1.2.6 JB */
+#define PNG_ROWBYTES(pixel_bits, width) \
+    ((pixel_bits) >= 8 ? \
+    ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \
+    (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) )
+
+/* PNG_OUT_OF_RANGE returns true if value is outside the range
+   ideal-delta..ideal+delta.  Each argument is evaluated twice.
+   "ideal" and "delta" should be constants, normally simple
+   integers, "value" a variable. Added to libpng-1.2.6 JB */
+#define PNG_OUT_OF_RANGE(value, ideal, delta) \
+        ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) )
+
+/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */
+#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN)
+/* place to hold the signature string for a PNG file. */
+#ifdef PNG_USE_GLOBAL_ARRAYS
+   PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8];
+#else
+#endif
+#endif /* PNG_NO_EXTERN */
+
+/* Constant strings for known chunk types.  If you need to add a chunk,
+ * define the name here, and add an invocation of the macro in png.c and
+ * wherever it's needed.
+ */
+#define PNG_IHDR png_byte png_IHDR[5] = { 73,  72,  68,  82, '\0'}
+#define PNG_IDAT png_byte png_IDAT[5] = { 73,  68,  65,  84, '\0'}
+#define PNG_IEND png_byte png_IEND[5] = { 73,  69,  78,  68, '\0'}
+#define PNG_PLTE png_byte png_PLTE[5] = { 80,  76,  84,  69, '\0'}
+#define PNG_bKGD png_byte png_bKGD[5] = { 98,  75,  71,  68, '\0'}
+#define PNG_cHRM png_byte png_cHRM[5] = { 99,  72,  82,  77, '\0'}
+#define PNG_gAMA png_byte png_gAMA[5] = {103,  65,  77,  65, '\0'}
+#define PNG_hIST png_byte png_hIST[5] = {104,  73,  83,  84, '\0'}
+#define PNG_iCCP png_byte png_iCCP[5] = {105,  67,  67,  80, '\0'}
+#define PNG_iTXt png_byte png_iTXt[5] = {105,  84,  88, 116, '\0'}
+#define PNG_oFFs png_byte png_oFFs[5] = {111,  70,  70, 115, '\0'}
+#define PNG_pCAL png_byte png_pCAL[5] = {112,  67,  65,  76, '\0'}
+#define PNG_sCAL png_byte png_sCAL[5] = {115,  67,  65,  76, '\0'}
+#define PNG_pHYs png_byte png_pHYs[5] = {112,  72,  89, 115, '\0'}
+#define PNG_sBIT png_byte png_sBIT[5] = {115,  66,  73,  84, '\0'}
+#define PNG_sPLT png_byte png_sPLT[5] = {115,  80,  76,  84, '\0'}
+#define PNG_sRGB png_byte png_sRGB[5] = {115,  82,  71,  66, '\0'}
+#define PNG_tEXt png_byte png_tEXt[5] = {116,  69,  88, 116, '\0'}
+#define PNG_tIME png_byte png_tIME[5] = {116,  73,  77,  69, '\0'}
+#define PNG_tRNS png_byte png_tRNS[5] = {116,  82,  78,  83, '\0'}
+#define PNG_zTXt png_byte png_zTXt[5] = {122,  84,  88, 116, '\0'}
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5];
+PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5];
+#endif /* PNG_USE_GLOBAL_ARRAYS */
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* Initialize png_ptr struct for reading, and allocate any other memory.
+ * (old interface - DEPRECATED - use png_create_read_struct instead).
+ */
+extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr));
+#undef png_read_init
+#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \
+    PNG_LIBPNG_VER_STRING,  png_sizeof(png_struct));
+#endif
+
+extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size));
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t
+    png_info_size));
+#endif
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* Initialize png_ptr struct for writing, and allocate any other memory.
+ * (old interface - DEPRECATED - use png_create_write_struct instead).
+ */
+extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr));
+#undef png_write_init
+#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \
+    PNG_LIBPNG_VER_STRING, png_sizeof(png_struct));
+#endif
+
+extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size));
+extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr,
+    png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t
+    png_info_size));
+
+/* Allocate memory for an internal libpng struct */
+PNG_EXTERN png_voidp png_create_struct PNGARG((int type));
+
+/* Free memory from internal libpng struct */
+PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr));
+
+PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr
+  malloc_fn, png_voidp mem_ptr));
+PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr,
+   png_free_ptr free_fn, png_voidp mem_ptr));
+
+/* Free any memory that info_ptr points to and reset struct. */
+PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+#ifndef PNG_1_0_X
+/* Function to allocate memory for zlib. */
+PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size));
+
+/* Function to free memory for zlib */
+PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr));
+
+#ifdef PNG_SIZE_T
+/* Function to convert a sizeof an item to png_sizeof item */
+   PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size));
+#endif
+
+/* Next four functions are used internally as callbacks.  PNGAPI is required
+ * but not PNG_EXPORT.  PNGAPI added at libpng version 1.2.3. */
+
+PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t length));
+#endif
+
+PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr,
+   png_bytep data, png_size_t length));
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+#if !defined(PNG_NO_STDIO)
+PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr));
+#endif
+#endif
+#else /* PNG_1_0_X */
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t length));
+#endif
+#endif /* PNG_1_0_X */
+
+/* Reset the CRC variable */
+PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr));
+
+/* Write the "data" buffer to whatever output you are using. */
+PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+/* Read data from whatever input you are using into the "data" buffer */
+PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+/* Read bytes into buf, and update png_ptr->crc */
+PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf,
+   png_size_t length));
+
+/* Decompress data in a chunk that uses compression */
+#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \
+    defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED)
+PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr,
+   int comp_type, png_charp chunkdata, png_size_t chunklength,
+   png_size_t prefix_length, png_size_t *data_length));
+#endif
+
+/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */
+PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip));
+
+/* Read the CRC from the file and compare it to the libpng calculated CRC */
+PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr));
+
+/* Calculate the CRC over a section of data.  Note that we are only
+ * passing a maximum of 64K on systems that have this as a memory limit,
+ * since this is the maximum buffer size we can specify.
+ */
+PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr,
+   png_size_t length));
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+PNG_EXTERN void png_flush PNGARG((png_structp png_ptr));
+#endif
+
+/* simple function to write the signature */
+PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr));
+
+/* write various chunks */
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.
+ */
+PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width,
+   png_uint_32 height,
+   int bit_depth, int color_type, int compression_method, int filter_method,
+   int interlace_method));
+
+PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette,
+   png_uint_32 num_pal));
+
+PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data,
+   png_size_t length));
+
+PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr));
+
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point
+    file_gamma));
+#endif
+#endif
+
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit,
+   int color_type));
+#endif
+
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr,
+   double white_x, double white_y,
+   double red_x, double red_y, double green_x, double green_y,
+   double blue_x, double blue_y));
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr,
+   png_fixed_point int_white_x, png_fixed_point int_white_y,
+   png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point
+   int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x,
+   png_fixed_point int_blue_y));
+#endif
+#endif
+
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr,
+   int intent));
+#endif
+
+#if defined(PNG_WRITE_iCCP_SUPPORTED)
+PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr,
+   png_charp name, int compression_type,
+   png_charp profile, int proflen));
+   /* Note to maintainer: profile should be png_bytep */
+#endif
+
+#if defined(PNG_WRITE_sPLT_SUPPORTED)
+PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr,
+   png_sPLT_tp palette));
+#endif
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans,
+   png_color_16p values, int number, int color_type));
+#endif
+
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr,
+   png_color_16p values, int color_type));
+#endif
+
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist,
+   int num_hist));
+#endif
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr,
+   png_charp key, png_charpp new_key));
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key,
+   png_charp text, png_size_t text_len));
+#endif
+
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key,
+   png_charp text, png_size_t text_len, int compression));
+#endif
+
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr,
+   int compression, png_charp key, png_charp lang, png_charp lang_key,
+   png_charp text));
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)  /* Added at version 1.0.14 and 1.2.4 */
+PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_textp text_ptr, int num_text));
+#endif
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr,
+   png_int_32 x_offset, png_int_32 y_offset, int unit_type));
+#endif
+
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose,
+   png_int_32 X0, png_int_32 X1, int type, int nparams,
+   png_charp units, png_charpp params));
+#endif
+
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr,
+   png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit,
+   int unit_type));
+#endif
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr,
+   png_timep mod_time));
+#endif
+
+#if defined(PNG_WRITE_sCAL_SUPPORTED)
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
+PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr,
+   int unit, double width, double height));
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr,
+   int unit, png_charp width, png_charp height));
+#endif
+#endif
+#endif
+
+/* Called when finished processing a row of data */
+PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr));
+
+/* Internal use only.   Called before first row of data */
+PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr));
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr));
+#endif
+
+/* combine a row of data, dealing with alpha, etc. if requested */
+PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row,
+   int mask));
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+/* expand an interlaced row */
+/* OLD pre-1.0.9 interface:
+PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info,
+   png_bytep row, int pass, png_uint_32 transformations));
+ */
+PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr));
+#endif
+
+/* GRR TO DO (2.0 or whenever):  simplify other internal calling interfaces */
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* grab pixels out of a row for an interlaced pass */
+PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info,
+   png_bytep row, int pass));
+#endif
+
+/* unfilter a row */
+PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr,
+   png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter));
+
+/* Choose the best filter to use and filter the row data */
+PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr,
+   png_row_infop row_info));
+
+/* Write out the filtered row. */
+PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr,
+   png_bytep filtered_row));
+/* finish a row while reading, dealing with interlacing passes, etc. */
+PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr));
+
+/* initialize the row buffers, etc. */
+PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr));
+/* optional call to update the users info structure */
+PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+
+/* these are the functions that do the transformations */
+#if defined(PNG_READ_FILLER_SUPPORTED)
+PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 filler, png_uint_32 flags));
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 flags));
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop
+   row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_8p sig_bits));
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info,
+   png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup));
+
+#  if defined(PNG_CORRECT_PALETTE_SUPPORTED)
+PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr,
+   png_colorp palette, int num_palette));
+#  endif
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row));
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info,
+   png_bytep row, png_uint_32 bit_depth));
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_8p bit_depth));
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background,
+   png_color_16p background_1,
+   png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1,
+   png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1,
+   png_uint_16pp gamma_16_to_1, int gamma_shift));
+#else
+PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background));
+#endif
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row,
+   png_bytep gamma_table, png_uint_16pp gamma_16_table,
+   int gamma_shift));
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info,
+   png_bytep row, png_colorp palette, png_bytep trans, int num_trans));
+PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info,
+   png_bytep row, png_color_16p trans_value));
+#endif
+
+/* The following decodes the appropriate chunks, and does error correction,
+ * then calls the appropriate callback for the chunk if it is valid.
+ */
+
+/* decode the IHDR chunk */
+PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_iCCP_SUPPORTED)
+extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif /* PNG_READ_iCCP_SUPPORTED */
+
+#if defined(PNG_READ_iTXt_SUPPORTED)
+PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sCAL_SUPPORTED)
+PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_sPLT_SUPPORTED)
+extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif /* PNG_READ_sPLT_SUPPORTED */
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 length));
+#endif
+
+PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+
+PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr,
+   png_bytep chunk_name));
+
+/* handle the transformations for reading and writing */
+PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr));
+
+PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr));
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr,
+   png_uint_32 length));
+PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr,
+   png_bytep buffer, png_size_t buffer_length));
+PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr));
+PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row));
+PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr));
+#if defined(PNG_READ_tEXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr, png_uint_32 length));
+PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr,
+   png_infop info_ptr));
+#endif
+
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+#ifdef PNG_MNG_FEATURES_SUPPORTED
+PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info,
+   png_bytep row));
+PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info,
+   png_bytep row));
+#endif
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED)
+#if defined(PNG_MMX_CODE_SUPPORTED)
+/* png.c */ /* PRIVATE */
+PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr));
+#endif
+#endif
+
+#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED)
+PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr,
+png_infop info_ptr));
+
+#if defined(PNG_pHYs_SUPPORTED)
+PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr,
+png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type));
+#endif /* PNG_pHYs_SUPPORTED */
+#endif  /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */
+
+/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */
+
+#endif /* PNG_INTERNAL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* PNG_VERSION_INFO_ONLY */
+/* do not put anything past this line */
+#endif /* PNG_H */
diff --git a/distrib/libpng-1.2.19/pngconf.h b/distrib/libpng-1.2.19/pngconf.h
new file mode 100644
index 0000000..43dfe04
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngconf.h
@@ -0,0 +1,1535 @@
+
+/* pngconf.h - machine configurable file for libpng
+ *
+ * libpng version 1.2.19 - August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+/* Any machine specific code is near the front of this file, so if you
+ * are configuring libpng for a machine, you may want to read the section
+ * starting here down to where it starts to typedef png_color, png_text,
+ * and png_info.
+ */
+
+#ifndef PNGCONF_H
+#define PNGCONF_H
+
+#define PNG_1_2_X
+
+/* 
+ * PNG_USER_CONFIG has to be defined on the compiler command line. This
+ * includes the resource compiler for Windows DLL configurations.
+ */
+#ifdef PNG_USER_CONFIG
+#  ifndef PNG_USER_PRIVATEBUILD
+#    define PNG_USER_PRIVATEBUILD
+#  endif
+#include "pngusr.h"
+#endif
+
+/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */
+#ifdef PNG_CONFIGURE_LIBPNG
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#endif
+
+/*
+ * Added at libpng-1.2.8
+ *  
+ * If you create a private DLL you need to define in "pngusr.h" the followings:
+ * #define PNG_USER_PRIVATEBUILD <Describes by whom and why this version of
+ *        the DLL was built>
+ *  e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons."
+ * #define PNG_USER_DLLFNAME_POSTFIX <two-letter postfix that serve to
+ *        distinguish your DLL from those of the official release. These
+ *        correspond to the trailing letters that come after the version
+ *        number and must match your private DLL name>
+ *  e.g. // private DLL "libpng13gx.dll"
+ *       #define PNG_USER_DLLFNAME_POSTFIX "gx"
+ * 
+ * The following macros are also at your disposal if you want to complete the 
+ * DLL VERSIONINFO structure.
+ * - PNG_USER_VERSIONINFO_COMMENTS
+ * - PNG_USER_VERSIONINFO_COMPANYNAME
+ * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS
+ */
+
+#ifdef __STDC__
+#ifdef SPECIALBUILD
+#  pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\
+ are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.")
+#endif
+
+#ifdef PRIVATEBUILD
+# pragma message("PRIVATEBUILD is deprecated.\
+ Use PNG_USER_PRIVATEBUILD instead.")
+# define PNG_USER_PRIVATEBUILD PRIVATEBUILD
+#endif
+#endif /* __STDC__ */
+
+#ifndef PNG_VERSION_INFO_ONLY
+
+/* End of material added to libpng-1.2.8 */
+
+/* Added at libpng-1.2.19 */
+#ifndef PNG_NO_WARN_UNINITIALIZED_ROW
+#  ifndef PNG_WARN_UNINITIALIZED_ROW
+#    define PNG_WARN_UNINITIALIZED_ROW 1 /* 0: warning; 1: error */
+#  endif
+#endif
+/* End of material added at libpng-1.2.19 */
+
+/* This is the size of the compression buffer, and thus the size of
+ * an IDAT chunk.  Make this whatever size you feel is best for your
+ * machine.  One of these will be allocated per png_struct.  When this
+ * is full, it writes the data to the disk, and does some other
+ * calculations.  Making this an extremely small size will slow
+ * the library down, but you may want to experiment to determine
+ * where it becomes significant, if you are concerned with memory
+ * usage.  Note that zlib allocates at least 32Kb also.  For readers,
+ * this describes the size of the buffer available to read the data in.
+ * Unless this gets smaller than the size of a row (compressed),
+ * it should not make much difference how big this is.
+ */
+
+#ifndef PNG_ZBUF_SIZE
+#  define PNG_ZBUF_SIZE 8192
+#endif
+
+/* Enable if you want a write-only libpng */
+
+#ifndef PNG_NO_READ_SUPPORTED
+#  define PNG_READ_SUPPORTED
+#endif
+
+/* Enable if you want a read-only libpng */
+
+#ifndef PNG_NO_WRITE_SUPPORTED
+#  define PNG_WRITE_SUPPORTED
+#endif
+
+/* Enabled by default in 1.2.0.  You can disable this if you don't need to
+   support PNGs that are embedded in MNG datastreams */
+#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES)
+#  ifndef PNG_MNG_FEATURES_SUPPORTED
+#    define PNG_MNG_FEATURES_SUPPORTED
+#  endif
+#endif
+
+#ifndef PNG_NO_FLOATING_POINT_SUPPORTED
+#  ifndef PNG_FLOATING_POINT_SUPPORTED
+#    define PNG_FLOATING_POINT_SUPPORTED
+#  endif
+#endif
+
+/* If you are running on a machine where you cannot allocate more
+ * than 64K of memory at once, uncomment this.  While libpng will not
+ * normally need that much memory in a chunk (unless you load up a very
+ * large file), zlib needs to know how big of a chunk it can use, and
+ * libpng thus makes sure to check any memory allocation to verify it
+ * will fit into memory.
+#define PNG_MAX_MALLOC_64K
+ */
+#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K)
+#  define PNG_MAX_MALLOC_64K
+#endif
+
+/* Special munging to support doing things the 'cygwin' way:
+ * 'Normal' png-on-win32 defines/defaults:
+ *   PNG_BUILD_DLL -- building dll
+ *   PNG_USE_DLL   -- building an application, linking to dll
+ *   (no define)   -- building static library, or building an
+ *                    application and linking to the static lib
+ * 'Cygwin' defines/defaults:
+ *   PNG_BUILD_DLL -- (ignored) building the dll
+ *   (no define)   -- (ignored) building an application, linking to the dll
+ *   PNG_STATIC    -- (ignored) building the static lib, or building an 
+ *                    application that links to the static lib.
+ *   ALL_STATIC    -- (ignored) building various static libs, or building an 
+ *                    application that links to the static libs.
+ * Thus,
+ * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and
+ * this bit of #ifdefs will define the 'correct' config variables based on
+ * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but
+ * unnecessary.
+ *
+ * Also, the precedence order is:
+ *   ALL_STATIC (since we can't #undef something outside our namespace)
+ *   PNG_BUILD_DLL
+ *   PNG_STATIC
+ *   (nothing) == PNG_USE_DLL
+ * 
+ * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent
+ *   of auto-import in binutils, we no longer need to worry about 
+ *   __declspec(dllexport) / __declspec(dllimport) and friends.  Therefore,
+ *   we don't need to worry about PNG_STATIC or ALL_STATIC when it comes
+ *   to __declspec() stuff.  However, we DO need to worry about 
+ *   PNG_BUILD_DLL and PNG_STATIC because those change some defaults
+ *   such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed.
+ */
+#if defined(__CYGWIN__)
+#  if defined(ALL_STATIC)
+#    if defined(PNG_BUILD_DLL)
+#      undef PNG_BUILD_DLL
+#    endif
+#    if defined(PNG_USE_DLL)
+#      undef PNG_USE_DLL
+#    endif
+#    if defined(PNG_DLL)
+#      undef PNG_DLL
+#    endif
+#    if !defined(PNG_STATIC)
+#      define PNG_STATIC
+#    endif
+#  else
+#    if defined (PNG_BUILD_DLL)
+#      if defined(PNG_STATIC)
+#        undef PNG_STATIC
+#      endif
+#      if defined(PNG_USE_DLL)
+#        undef PNG_USE_DLL
+#      endif
+#      if !defined(PNG_DLL)
+#        define PNG_DLL
+#      endif
+#    else
+#      if defined(PNG_STATIC)
+#        if defined(PNG_USE_DLL)
+#          undef PNG_USE_DLL
+#        endif
+#        if defined(PNG_DLL)
+#          undef PNG_DLL
+#        endif
+#      else
+#        if !defined(PNG_USE_DLL)
+#          define PNG_USE_DLL
+#        endif
+#        if !defined(PNG_DLL)
+#          define PNG_DLL
+#        endif
+#      endif  
+#    endif  
+#  endif
+#endif
+
+/* This protects us against compilers that run on a windowing system
+ * and thus don't have or would rather us not use the stdio types:
+ * stdin, stdout, and stderr.  The only one currently used is stderr
+ * in png_error() and png_warning().  #defining PNG_NO_CONSOLE_IO will
+ * prevent these from being compiled and used. #defining PNG_NO_STDIO
+ * will also prevent these, plus will prevent the entire set of stdio
+ * macros and functions (FILE *, printf, etc.) from being compiled and used,
+ * unless (PNG_DEBUG > 0) has been #defined.
+ *
+ * #define PNG_NO_CONSOLE_IO
+ * #define PNG_NO_STDIO
+ */
+
+#if defined(_WIN32_WCE)
+#  include <windows.h>
+   /* Console I/O functions are not supported on WindowsCE */
+#  define PNG_NO_CONSOLE_IO
+#  ifdef PNG_DEBUG
+#    undef PNG_DEBUG
+#  endif
+#endif
+
+#ifdef PNG_BUILD_DLL
+#  ifndef PNG_CONSOLE_IO_SUPPORTED
+#    ifndef PNG_NO_CONSOLE_IO
+#      define PNG_NO_CONSOLE_IO
+#    endif
+#  endif
+#endif
+
+#  ifdef PNG_NO_STDIO
+#    ifndef PNG_NO_CONSOLE_IO
+#      define PNG_NO_CONSOLE_IO
+#    endif
+#    ifdef PNG_DEBUG
+#      if (PNG_DEBUG > 0)
+#        include <stdio.h>
+#      endif
+#    endif
+#  else
+#    if !defined(_WIN32_WCE)
+/* "stdio.h" functions are not supported on WindowsCE */
+#      include <stdio.h>
+#    endif
+#  endif
+
+/* This macro protects us against machines that don't have function
+ * prototypes (ie K&R style headers).  If your compiler does not handle
+ * function prototypes, define this macro and use the included ansi2knr.
+ * I've always been able to use _NO_PROTO as the indicator, but you may
+ * need to drag the empty declaration out in front of here, or change the
+ * ifdef to suit your own needs.
+ */
+#ifndef PNGARG
+
+#ifdef OF /* zlib prototype munger */
+#  define PNGARG(arglist) OF(arglist)
+#else
+
+#ifdef _NO_PROTO
+#  define PNGARG(arglist) ()
+#  ifndef PNG_TYPECAST_NULL
+#     define PNG_TYPECAST_NULL
+#  endif
+#else
+#  define PNGARG(arglist) arglist
+#endif /* _NO_PROTO */
+
+
+#endif /* OF */
+
+#endif /* PNGARG */
+
+/* Try to determine if we are compiling on a Mac.  Note that testing for
+ * just __MWERKS__ is not good enough, because the Codewarrior is now used
+ * on non-Mac platforms.
+ */
+#ifndef MACOS
+#  if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \
+      defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC)
+#    define MACOS
+#  endif
+#endif
+
+/* enough people need this for various reasons to include it here */
+#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE)
+#  include <sys/types.h>
+#endif
+
+#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED)
+#  define PNG_SETJMP_SUPPORTED
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* This is an attempt to force a single setjmp behaviour on Linux.  If
+ * the X config stuff didn't define _BSD_SOURCE we wouldn't need this.
+ */
+
+#  ifdef __linux__
+#    ifdef _BSD_SOURCE
+#      define PNG_SAVE_BSD_SOURCE
+#      undef _BSD_SOURCE
+#    endif
+#    ifdef _SETJMP_H
+     /* If you encounter a compiler error here, see the explanation
+      * near the end of INSTALL.
+      */
+         __png.h__ already includes setjmp.h;
+         __dont__ include it again.;
+#    endif
+#  endif /* __linux__ */
+
+   /* include setjmp.h for error handling */
+#  include <setjmp.h>
+
+#  ifdef __linux__
+#    ifdef PNG_SAVE_BSD_SOURCE
+#      define _BSD_SOURCE
+#      undef PNG_SAVE_BSD_SOURCE
+#    endif
+#  endif /* __linux__ */
+#endif /* PNG_SETJMP_SUPPORTED */
+
+#ifdef BSD
+#  include <strings.h>
+#else
+#  include <string.h>
+#endif
+
+/* Other defines for things like memory and the like can go here.  */
+#ifdef PNG_INTERNAL
+
+#include <stdlib.h>
+
+/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which
+ * aren't usually used outside the library (as far as I know), so it is
+ * debatable if they should be exported at all.  In the future, when it is
+ * possible to have run-time registry of chunk-handling functions, some of
+ * these will be made available again.
+#define PNG_EXTERN extern
+ */
+#define PNG_EXTERN
+
+/* Other defines specific to compilers can go here.  Try to keep
+ * them inside an appropriate ifdef/endif pair for portability.
+ */
+
+#if defined(PNG_FLOATING_POINT_SUPPORTED)
+#  if defined(MACOS)
+     /* We need to check that <math.h> hasn't already been included earlier
+      * as it seems it doesn't agree with <fp.h>, yet we should really use
+      * <fp.h> if possible.
+      */
+#    if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__)
+#      include <fp.h>
+#    endif
+#  else
+#    include <math.h>
+#  endif
+#  if defined(_AMIGA) && defined(__SASC) && defined(_M68881)
+     /* Amiga SAS/C: We must include builtin FPU functions when compiling using
+      * MATH=68881
+      */
+#    include <m68881.h>
+#  endif
+#endif
+
+/* Codewarrior on NT has linking problems without this. */
+#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__)
+#  define PNG_ALWAYS_EXTERN
+#endif
+
+/* This provides the non-ANSI (far) memory allocation routines. */
+#if defined(__TURBOC__) && defined(__MSDOS__)
+#  include <mem.h>
+#  include <alloc.h>
+#endif
+
+/* I have no idea why is this necessary... */
+#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \
+    defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__))
+#  include <malloc.h>
+#endif
+
+/* This controls how fine the dithering gets.  As this allocates
+ * a largish chunk of memory (32K), those who are not as concerned
+ * with dithering quality can decrease some or all of these.
+ */
+#ifndef PNG_DITHER_RED_BITS
+#  define PNG_DITHER_RED_BITS 5
+#endif
+#ifndef PNG_DITHER_GREEN_BITS
+#  define PNG_DITHER_GREEN_BITS 5
+#endif
+#ifndef PNG_DITHER_BLUE_BITS
+#  define PNG_DITHER_BLUE_BITS 5
+#endif
+
+/* This controls how fine the gamma correction becomes when you
+ * are only interested in 8 bits anyway.  Increasing this value
+ * results in more memory being used, and more pow() functions
+ * being called to fill in the gamma tables.  Don't set this value
+ * less then 8, and even that may not work (I haven't tested it).
+ */
+
+#ifndef PNG_MAX_GAMMA_8
+#  define PNG_MAX_GAMMA_8 11
+#endif
+
+/* This controls how much a difference in gamma we can tolerate before
+ * we actually start doing gamma conversion.
+ */
+#ifndef PNG_GAMMA_THRESHOLD
+#  define PNG_GAMMA_THRESHOLD 0.05
+#endif
+
+#endif /* PNG_INTERNAL */
+
+/* The following uses const char * instead of char * for error
+ * and warning message functions, so some compilers won't complain.
+ * If you do not want to use const, define PNG_NO_CONST here.
+ */
+
+#ifndef PNG_NO_CONST
+#  define PNG_CONST const
+#else
+#  define PNG_CONST
+#endif
+
+/* The following defines give you the ability to remove code from the
+ * library that you will not be using.  I wish I could figure out how to
+ * automate this, but I can't do that without making it seriously hard
+ * on the users.  So if you are not using an ability, change the #define
+ * to and #undef, and that part of the library will not be compiled.  If
+ * your linker can't find a function, you may want to make sure the
+ * ability is defined here.  Some of these depend upon some others being
+ * defined.  I haven't figured out all the interactions here, so you may
+ * have to experiment awhile to get everything to compile.  If you are
+ * creating or using a shared library, you probably shouldn't touch this,
+ * as it will affect the size of the structures, and this will cause bad
+ * things to happen if the library and/or application ever change.
+ */
+
+/* Any features you will not be using can be undef'ed here */
+
+/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user
+ * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS
+ * on the compile line, then pick and choose which ones to define without
+ * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED
+ * if you only want to have a png-compliant reader/writer but don't need
+ * any of the extra transformations.  This saves about 80 kbytes in a
+ * typical installation of the library. (PNG_NO_* form added in version
+ * 1.0.1c, for consistency)
+ */
+
+/* The size of the png_text structure changed in libpng-1.0.6 when
+ * iTXt support was added.  iTXt support was turned off by default through
+ * libpng-1.2.x, to support old apps that malloc the png_text structure
+ * instead of calling png_set_text() and letting libpng malloc it.  It
+ * was turned on by default in libpng-1.3.0.
+ */
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+#  ifndef PNG_NO_iTXt_SUPPORTED
+#    define PNG_NO_iTXt_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_iTXt
+#    define PNG_NO_READ_iTXt
+#  endif
+#  ifndef PNG_NO_WRITE_iTXt
+#    define PNG_NO_WRITE_iTXt
+#  endif
+#endif
+
+#if !defined(PNG_NO_iTXt_SUPPORTED)
+#  if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt)
+#    define PNG_READ_iTXt
+#  endif
+#  if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt)
+#    define PNG_WRITE_iTXt
+#  endif
+#endif
+
+/* The following support, added after version 1.0.0, can be turned off here en
+ * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility
+ * with old applications that require the length of png_struct and png_info
+ * to remain unchanged.
+ */
+
+#ifdef PNG_LEGACY_SUPPORTED
+#  define PNG_NO_FREE_ME
+#  define PNG_NO_READ_UNKNOWN_CHUNKS
+#  define PNG_NO_WRITE_UNKNOWN_CHUNKS
+#  define PNG_NO_READ_USER_CHUNKS
+#  define PNG_NO_READ_iCCP
+#  define PNG_NO_WRITE_iCCP
+#  define PNG_NO_READ_iTXt
+#  define PNG_NO_WRITE_iTXt
+#  define PNG_NO_READ_sCAL
+#  define PNG_NO_WRITE_sCAL
+#  define PNG_NO_READ_sPLT
+#  define PNG_NO_WRITE_sPLT
+#  define PNG_NO_INFO_IMAGE
+#  define PNG_NO_READ_RGB_TO_GRAY
+#  define PNG_NO_READ_USER_TRANSFORM
+#  define PNG_NO_WRITE_USER_TRANSFORM
+#  define PNG_NO_USER_MEM
+#  define PNG_NO_READ_EMPTY_PLTE
+#  define PNG_NO_MNG_FEATURES
+#  define PNG_NO_FIXED_POINT_SUPPORTED
+#endif
+
+/* Ignore attempt to turn off both floating and fixed point support */
+#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \
+    !defined(PNG_NO_FIXED_POINT_SUPPORTED)
+#  define PNG_FIXED_POINT_SUPPORTED
+#endif
+
+#ifndef PNG_NO_FREE_ME
+#  define PNG_FREE_ME_SUPPORTED
+#endif
+
+#if defined(PNG_READ_SUPPORTED)
+
+#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \
+      !defined(PNG_NO_READ_TRANSFORMS)
+#  define PNG_READ_TRANSFORMS_SUPPORTED
+#endif
+
+#ifdef PNG_READ_TRANSFORMS_SUPPORTED
+#  ifndef PNG_NO_READ_EXPAND
+#    define PNG_READ_EXPAND_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_SHIFT
+#    define PNG_READ_SHIFT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_PACK
+#    define PNG_READ_PACK_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_BGR
+#    define PNG_READ_BGR_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_SWAP
+#    define PNG_READ_SWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_PACKSWAP
+#    define PNG_READ_PACKSWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_INVERT
+#    define PNG_READ_INVERT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_DITHER
+#    define PNG_READ_DITHER_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_BACKGROUND
+#    define PNG_READ_BACKGROUND_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_16_TO_8
+#    define PNG_READ_16_TO_8_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_FILLER
+#    define PNG_READ_FILLER_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_GAMMA
+#    define PNG_READ_GAMMA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_GRAY_TO_RGB
+#    define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_SWAP_ALPHA
+#    define PNG_READ_SWAP_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_INVERT_ALPHA
+#    define PNG_READ_INVERT_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_STRIP_ALPHA
+#    define PNG_READ_STRIP_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_USER_TRANSFORM
+#    define PNG_READ_USER_TRANSFORM_SUPPORTED
+#  endif
+#  ifndef PNG_NO_READ_RGB_TO_GRAY
+#    define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#  endif
+#endif /* PNG_READ_TRANSFORMS_SUPPORTED */
+
+#if !defined(PNG_NO_PROGRESSIVE_READ) && \
+ !defined(PNG_PROGRESSIVE_READ_NOT_SUPPORTED) /* if you don't do progressive */
+#  define PNG_PROGRESSIVE_READ_SUPPORTED    /* reading.  This is not talking */
+#endif                              /* about interlacing capability!  You'll */
+             /* still have interlacing unless you change the following line: */
+
+#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */
+
+#ifndef PNG_NO_READ_COMPOSITE_NODIV
+#  ifndef PNG_NO_READ_COMPOSITED_NODIV  /* libpng-1.0.x misspelling */
+#    define PNG_READ_COMPOSITE_NODIV_SUPPORTED  /* well tested on Intel, SGI */
+#  endif
+#endif
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* Deprecated, will be removed from version 2.0.0.
+   Use PNG_MNG_FEATURES_SUPPORTED instead. */
+#ifndef PNG_NO_READ_EMPTY_PLTE
+#  define PNG_READ_EMPTY_PLTE_SUPPORTED
+#endif
+#endif
+
+#endif /* PNG_READ_SUPPORTED */
+
+#if defined(PNG_WRITE_SUPPORTED)
+
+# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_WRITE_TRANSFORMS)
+#  define PNG_WRITE_TRANSFORMS_SUPPORTED
+#endif
+
+#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED
+#  ifndef PNG_NO_WRITE_SHIFT
+#    define PNG_WRITE_SHIFT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_PACK
+#    define PNG_WRITE_PACK_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_BGR
+#    define PNG_WRITE_BGR_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_SWAP
+#    define PNG_WRITE_SWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_PACKSWAP
+#    define PNG_WRITE_PACKSWAP_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_INVERT
+#    define PNG_WRITE_INVERT_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_FILLER
+#    define PNG_WRITE_FILLER_SUPPORTED   /* same as WRITE_STRIP_ALPHA */
+#  endif
+#  ifndef PNG_NO_WRITE_SWAP_ALPHA
+#    define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_INVERT_ALPHA
+#    define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#  endif
+#  ifndef PNG_NO_WRITE_USER_TRANSFORM
+#    define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#  endif
+#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */
+
+#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \
+    !defined(PNG_WRITE_INTERLACING_SUPPORTED)
+#define PNG_WRITE_INTERLACING_SUPPORTED  /* not required for PNG-compliant
+                                            encoders, but can cause trouble
+                                            if left undefined */
+#endif
+
+#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \
+    !defined(PNG_WRITE_WEIGHTED_FILTER) && \
+     defined(PNG_FLOATING_POINT_SUPPORTED)
+#  define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#endif
+
+#ifndef PNG_NO_WRITE_FLUSH
+#  define PNG_WRITE_FLUSH_SUPPORTED
+#endif
+
+#if defined(PNG_1_0_X) || defined (PNG_1_2_X)
+/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */
+#ifndef PNG_NO_WRITE_EMPTY_PLTE
+#  define PNG_WRITE_EMPTY_PLTE_SUPPORTED
+#endif
+#endif
+
+#endif /* PNG_WRITE_SUPPORTED */
+
+#ifndef PNG_1_0_X
+#  ifndef PNG_NO_ERROR_NUMBERS
+#    define PNG_ERROR_NUMBERS_SUPPORTED
+#  endif
+#endif /* PNG_1_0_X */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+#  ifndef PNG_NO_USER_TRANSFORM_PTR
+#    define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#  endif
+#endif
+
+#ifndef PNG_NO_STDIO
+#  define PNG_TIME_RFC1123_SUPPORTED
+#endif
+
+/* This adds extra functions in pngget.c for accessing data from the
+ * info pointer (added in version 0.99)
+ * png_get_image_width()
+ * png_get_image_height()
+ * png_get_bit_depth()
+ * png_get_color_type()
+ * png_get_compression_type()
+ * png_get_filter_type()
+ * png_get_interlace_type()
+ * png_get_pixel_aspect_ratio()
+ * png_get_pixels_per_meter()
+ * png_get_x_offset_pixels()
+ * png_get_y_offset_pixels()
+ * png_get_x_offset_microns()
+ * png_get_y_offset_microns()
+ */
+#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED)
+#  define PNG_EASY_ACCESS_SUPPORTED
+#endif
+
+/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 
+ * even when PNG_USE_PNGVCRD or PNG_USE_PNGGCCRD is not defined.
+ *
+ * PNG_NO_ASSEMBLER_CODE disables use of all assembler code,
+ * and removes several functions from the API.
+ *
+ * PNG_NO_MMX_CODE disables the use of MMX code without changing the API.
+ * When MMX code is off, then optimized C replacement functions are used,
+ * if PNG_NO_OPTIMIZED_CODE is not enabled.  This was added in version
+ * 1.2.19.
+*/
+
+#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE)
+#  ifndef PNG_OPTIMIZED_CODE_SUPPORTED
+#    define PNG_OPTIMIZED_CODE_SUPPORTED
+#  endif
+#endif
+
+#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE)
+#  ifndef PNG_ASSEMBLER_CODE_SUPPORTED
+#    define PNG_ASSEMBLER_CODE_SUPPORTED
+#  endif
+
+#  if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE)
+#    define PNG_MMX_CODE_SUPPORTED
+#  endif
+
+#  if !defined(PNG_USE_PNGVCRD) && defined(PNG_MMX_CODE_SUPPORTED) && \
+     defined(_MSC_VER)
+#    define PNG_USE_PNGVCRD
+#  endif
+
+#  if !defined(PNG_USE_PNGGCCRD) && defined(PNG_MMX_CODE_SUPPORTED) && \
+     !defined(PNG_USE_PNGVCRD)
+#    define PNG_USE_PNGGCCRD
+     /* If you are sure that you don't need thread safety and you are compiling
+        with PNG_USE_PNGCCRD for an MMX application, you can define this for
+        faster execution.  See pnggccrd.c.
+#    define PNG_THREAD_UNSAFE_OK
+     */
+#  endif
+
+#endif
+
+#if !defined(PNG_1_0_X)
+#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED)
+#  define PNG_USER_MEM_SUPPORTED
+#endif
+#endif /* PNG_1_0_X */
+
+/* Added at libpng-1.2.6 */
+#if !defined(PNG_1_0_X)
+#ifndef PNG_SET_USER_LIMITS_SUPPORTED
+#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED)
+#  define PNG_SET_USER_LIMITS_SUPPORTED
+#endif
+#endif
+#endif /* PNG_1_0_X */
+
+/* Added at libpng-1.0.16 and 1.2.6.  To accept all valid PNGS no matter
+ * how large, set these limits to 0x7fffffffL
+ */
+#ifndef PNG_USER_WIDTH_MAX
+#  define PNG_USER_WIDTH_MAX 1000000L
+#endif
+#ifndef PNG_USER_HEIGHT_MAX
+#  define PNG_USER_HEIGHT_MAX 1000000L
+#endif
+
+/* These are currently experimental features, define them if you want */
+
+/* very little testing */
+/*
+#ifdef PNG_READ_SUPPORTED
+#  ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+#    define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED
+#  endif
+#endif
+*/
+
+/* This is only for PowerPC big-endian and 680x0 systems */
+/* some testing */
+/*
+#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED
+#  define PNG_READ_BIG_ENDIAN_SUPPORTED
+#endif
+*/
+
+/* Buggy compilers (e.g., gcc 2.7.2.2) need this */
+/*
+#define PNG_NO_POINTER_INDEXING
+*/
+
+/* These functions are turned off by default, as they will be phased out. */
+/*
+#define  PNG_USELESS_TESTS_SUPPORTED
+#define  PNG_CORRECT_PALETTE_SUPPORTED
+*/
+
+/* Any chunks you are not interested in, you can undef here.  The
+ * ones that allocate memory may be expecially important (hIST,
+ * tEXt, zTXt, tRNS, pCAL).  Others will just save time and make png_info
+ * a bit smaller.
+ */
+
+#if defined(PNG_READ_SUPPORTED) && \
+    !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_READ_ANCILLARY_CHUNKS)
+#  define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#endif
+
+#if defined(PNG_WRITE_SUPPORTED) && \
+    !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \
+    !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS)
+#  define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#endif
+
+#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+
+#ifdef PNG_NO_READ_TEXT
+#  define PNG_NO_READ_iTXt
+#  define PNG_NO_READ_tEXt
+#  define PNG_NO_READ_zTXt
+#endif
+#ifndef PNG_NO_READ_bKGD
+#  define PNG_READ_bKGD_SUPPORTED
+#  define PNG_bKGD_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_cHRM
+#  define PNG_READ_cHRM_SUPPORTED
+#  define PNG_cHRM_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_gAMA
+#  define PNG_READ_gAMA_SUPPORTED
+#  define PNG_gAMA_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_hIST
+#  define PNG_READ_hIST_SUPPORTED
+#  define PNG_hIST_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_iCCP
+#  define PNG_READ_iCCP_SUPPORTED
+#  define PNG_iCCP_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_iTXt
+#  ifndef PNG_READ_iTXt_SUPPORTED
+#    define PNG_READ_iTXt_SUPPORTED
+#  endif
+#  ifndef PNG_iTXt_SUPPORTED
+#    define PNG_iTXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_READ_oFFs
+#  define PNG_READ_oFFs_SUPPORTED
+#  define PNG_oFFs_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_pCAL
+#  define PNG_READ_pCAL_SUPPORTED
+#  define PNG_pCAL_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sCAL
+#  define PNG_READ_sCAL_SUPPORTED
+#  define PNG_sCAL_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_pHYs
+#  define PNG_READ_pHYs_SUPPORTED
+#  define PNG_pHYs_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sBIT
+#  define PNG_READ_sBIT_SUPPORTED
+#  define PNG_sBIT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sPLT
+#  define PNG_READ_sPLT_SUPPORTED
+#  define PNG_sPLT_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_sRGB
+#  define PNG_READ_sRGB_SUPPORTED
+#  define PNG_sRGB_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tEXt
+#  define PNG_READ_tEXt_SUPPORTED
+#  define PNG_tEXt_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tIME
+#  define PNG_READ_tIME_SUPPORTED
+#  define PNG_tIME_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_tRNS
+#  define PNG_READ_tRNS_SUPPORTED
+#  define PNG_tRNS_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_zTXt
+#  define PNG_READ_zTXt_SUPPORTED
+#  define PNG_zTXt_SUPPORTED
+#endif
+#ifndef PNG_NO_READ_UNKNOWN_CHUNKS
+#  define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#  ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED
+#    define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#  endif
+#  ifndef PNG_NO_HANDLE_AS_UNKNOWN
+#    define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#  endif
+#endif
+#if !defined(PNG_NO_READ_USER_CHUNKS) && \
+     defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+#  define PNG_READ_USER_CHUNKS_SUPPORTED
+#  define PNG_USER_CHUNKS_SUPPORTED
+#  ifdef PNG_NO_READ_UNKNOWN_CHUNKS
+#    undef PNG_NO_READ_UNKNOWN_CHUNKS
+#  endif
+#  ifdef PNG_NO_HANDLE_AS_UNKNOWN
+#    undef PNG_NO_HANDLE_AS_UNKNOWN
+#  endif
+#endif
+#ifndef PNG_NO_READ_OPT_PLTE
+#  define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */
+#endif                      /* optional PLTE chunk in RGB and RGBA images */
+#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \
+    defined(PNG_READ_zTXt_SUPPORTED)
+#  define PNG_READ_TEXT_SUPPORTED
+#  define PNG_TEXT_SUPPORTED
+#endif
+
+#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */
+
+#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+
+#ifdef PNG_NO_WRITE_TEXT
+#  define PNG_NO_WRITE_iTXt
+#  define PNG_NO_WRITE_tEXt
+#  define PNG_NO_WRITE_zTXt
+#endif
+#ifndef PNG_NO_WRITE_bKGD
+#  define PNG_WRITE_bKGD_SUPPORTED
+#  ifndef PNG_bKGD_SUPPORTED
+#    define PNG_bKGD_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_cHRM
+#  define PNG_WRITE_cHRM_SUPPORTED
+#  ifndef PNG_cHRM_SUPPORTED
+#    define PNG_cHRM_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_gAMA
+#  define PNG_WRITE_gAMA_SUPPORTED
+#  ifndef PNG_gAMA_SUPPORTED
+#    define PNG_gAMA_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_hIST
+#  define PNG_WRITE_hIST_SUPPORTED
+#  ifndef PNG_hIST_SUPPORTED
+#    define PNG_hIST_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_iCCP
+#  define PNG_WRITE_iCCP_SUPPORTED
+#  ifndef PNG_iCCP_SUPPORTED
+#    define PNG_iCCP_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_iTXt
+#  ifndef PNG_WRITE_iTXt_SUPPORTED
+#    define PNG_WRITE_iTXt_SUPPORTED
+#  endif
+#  ifndef PNG_iTXt_SUPPORTED
+#    define PNG_iTXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_oFFs
+#  define PNG_WRITE_oFFs_SUPPORTED
+#  ifndef PNG_oFFs_SUPPORTED
+#    define PNG_oFFs_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_pCAL
+#  define PNG_WRITE_pCAL_SUPPORTED
+#  ifndef PNG_pCAL_SUPPORTED
+#    define PNG_pCAL_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sCAL
+#  define PNG_WRITE_sCAL_SUPPORTED
+#  ifndef PNG_sCAL_SUPPORTED
+#    define PNG_sCAL_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_pHYs
+#  define PNG_WRITE_pHYs_SUPPORTED
+#  ifndef PNG_pHYs_SUPPORTED
+#    define PNG_pHYs_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sBIT
+#  define PNG_WRITE_sBIT_SUPPORTED
+#  ifndef PNG_sBIT_SUPPORTED
+#    define PNG_sBIT_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sPLT
+#  define PNG_WRITE_sPLT_SUPPORTED
+#  ifndef PNG_sPLT_SUPPORTED
+#    define PNG_sPLT_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_sRGB
+#  define PNG_WRITE_sRGB_SUPPORTED
+#  ifndef PNG_sRGB_SUPPORTED
+#    define PNG_sRGB_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_tEXt
+#  define PNG_WRITE_tEXt_SUPPORTED
+#  ifndef PNG_tEXt_SUPPORTED
+#    define PNG_tEXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_tIME
+#  define PNG_WRITE_tIME_SUPPORTED
+#  ifndef PNG_tIME_SUPPORTED
+#    define PNG_tIME_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_tRNS
+#  define PNG_WRITE_tRNS_SUPPORTED
+#  ifndef PNG_tRNS_SUPPORTED
+#    define PNG_tRNS_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_zTXt
+#  define PNG_WRITE_zTXt_SUPPORTED
+#  ifndef PNG_zTXt_SUPPORTED
+#    define PNG_zTXt_SUPPORTED
+#  endif
+#endif
+#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS
+#  define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#  ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED
+#    define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#  endif
+#  ifndef PNG_NO_HANDLE_AS_UNKNOWN
+#     ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#       define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#     endif
+#  endif
+#endif
+#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \
+    defined(PNG_WRITE_zTXt_SUPPORTED)
+#  define PNG_WRITE_TEXT_SUPPORTED
+#  ifndef PNG_TEXT_SUPPORTED
+#    define PNG_TEXT_SUPPORTED
+#  endif
+#endif
+
+#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */
+
+/* Turn this off to disable png_read_png() and
+ * png_write_png() and leave the row_pointers member
+ * out of the info structure.
+ */
+#ifndef PNG_NO_INFO_IMAGE
+#  define PNG_INFO_IMAGE_SUPPORTED
+#endif
+
+/* need the time information for reading tIME chunks */
+#if defined(PNG_tIME_SUPPORTED)
+#  if !defined(_WIN32_WCE)
+     /* "time.h" functions are not supported on WindowsCE */
+#    include <time.h>
+#  endif
+#endif
+
+/* Some typedefs to get us started.  These should be safe on most of the
+ * common platforms.  The typedefs should be at least as large as the
+ * numbers suggest (a png_uint_32 must be at least 32 bits long), but they
+ * don't have to be exactly that size.  Some compilers dislike passing
+ * unsigned shorts as function parameters, so you may be better off using
+ * unsigned int for png_uint_16.  Likewise, for 64-bit systems, you may
+ * want to have unsigned int for png_uint_32 instead of unsigned long.
+ */
+
+typedef unsigned long png_uint_32;
+typedef long png_int_32;
+typedef unsigned short png_uint_16;
+typedef short png_int_16;
+typedef unsigned char png_byte;
+
+/* This is usually size_t.  It is typedef'ed just in case you need it to
+   change (I'm not sure if you will or not, so I thought I'd be safe) */
+#ifdef PNG_SIZE_T
+   typedef PNG_SIZE_T png_size_t;
+#  define png_sizeof(x) png_convert_size(sizeof (x))
+#else
+   typedef size_t png_size_t;
+#  define png_sizeof(x) sizeof (x)
+#endif
+
+/* The following is needed for medium model support.  It cannot be in the
+ * PNG_INTERNAL section.  Needs modification for other compilers besides
+ * MSC.  Model independent support declares all arrays and pointers to be
+ * large using the far keyword.  The zlib version used must also support
+ * model independent data.  As of version zlib 1.0.4, the necessary changes
+ * have been made in zlib.  The USE_FAR_KEYWORD define triggers other
+ * changes that are needed. (Tim Wegner)
+ */
+
+/* Separate compiler dependencies (problem here is that zlib.h always
+   defines FAR. (SJT) */
+#ifdef __BORLANDC__
+#  if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__)
+#    define LDATA 1
+#  else
+#    define LDATA 0
+#  endif
+   /* GRR:  why is Cygwin in here?  Cygwin is not Borland C... */
+#  if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__)
+#    define PNG_MAX_MALLOC_64K
+#    if (LDATA != 1)
+#      ifndef FAR
+#        define FAR __far
+#      endif
+#      define USE_FAR_KEYWORD
+#    endif   /* LDATA != 1 */
+     /* Possibly useful for moving data out of default segment.
+      * Uncomment it if you want. Could also define FARDATA as
+      * const if your compiler supports it. (SJT)
+#    define FARDATA FAR
+      */
+#  endif  /* __WIN32__, __FLAT__, __CYGWIN__ */
+#endif   /* __BORLANDC__ */
+
+
+/* Suggest testing for specific compiler first before testing for
+ * FAR.  The Watcom compiler defines both __MEDIUM__ and M_I86MM,
+ * making reliance oncertain keywords suspect. (SJT)
+ */
+
+/* MSC Medium model */
+#if defined(FAR)
+#  if defined(M_I86MM)
+#    define USE_FAR_KEYWORD
+#    define FARDATA FAR
+#    include <dos.h>
+#  endif
+#endif
+
+/* SJT: default case */
+#ifndef FAR
+#  define FAR
+#endif
+
+/* At this point FAR is always defined */
+#ifndef FARDATA
+#  define FARDATA
+#endif
+
+/* Typedef for floating-point numbers that are converted
+   to fixed-point with a multiple of 100,000, e.g., int_gamma */
+typedef png_int_32 png_fixed_point;
+
+/* Add typedefs for pointers */
+typedef void            FAR * png_voidp;
+typedef png_byte        FAR * png_bytep;
+typedef png_uint_32     FAR * png_uint_32p;
+typedef png_int_32      FAR * png_int_32p;
+typedef png_uint_16     FAR * png_uint_16p;
+typedef png_int_16      FAR * png_int_16p;
+typedef PNG_CONST char  FAR * png_const_charp;
+typedef char            FAR * png_charp;
+typedef png_fixed_point FAR * png_fixed_point_p;
+
+#ifndef PNG_NO_STDIO
+#if defined(_WIN32_WCE)
+typedef HANDLE                png_FILE_p;
+#else
+typedef FILE                * png_FILE_p;
+#endif
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double          FAR * png_doublep;
+#endif
+
+/* Pointers to pointers; i.e. arrays */
+typedef png_byte        FAR * FAR * png_bytepp;
+typedef png_uint_32     FAR * FAR * png_uint_32pp;
+typedef png_int_32      FAR * FAR * png_int_32pp;
+typedef png_uint_16     FAR * FAR * png_uint_16pp;
+typedef png_int_16      FAR * FAR * png_int_16pp;
+typedef PNG_CONST char  FAR * FAR * png_const_charpp;
+typedef char            FAR * FAR * png_charpp;
+typedef png_fixed_point FAR * FAR * png_fixed_point_pp;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+typedef double          FAR * FAR * png_doublepp;
+#endif
+
+/* Pointers to pointers to pointers; i.e., pointer to array */
+typedef char            FAR * FAR * FAR * png_charppp;
+
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+/* SPC -  Is this stuff deprecated? */
+/* It'll be removed as of libpng-1.3.0 - GR-P */
+/* libpng typedefs for types in zlib. If zlib changes
+ * or another compression library is used, then change these.
+ * Eliminates need to change all the source files.
+ */
+typedef charf *         png_zcharp;
+typedef charf * FAR *   png_zcharpp;
+typedef z_stream FAR *  png_zstreamp;
+#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */
+
+/*
+ * Define PNG_BUILD_DLL if the module being built is a Windows
+ * LIBPNG DLL.
+ *
+ * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL.
+ * It is equivalent to Microsoft predefined macro _DLL that is
+ * automatically defined when you compile using the share
+ * version of the CRT (C Run-Time library)
+ *
+ * The cygwin mods make this behavior a little different:
+ * Define PNG_BUILD_DLL if you are building a dll for use with cygwin
+ * Define PNG_STATIC if you are building a static library for use with cygwin,
+ *   -or- if you are building an application that you want to link to the
+ *   static library.
+ * PNG_USE_DLL is defined by default (no user action needed) unless one of
+ *   the other flags is defined.
+ */
+
+#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL))
+#  define PNG_DLL
+#endif
+/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib.
+ * When building a static lib, default to no GLOBAL ARRAYS, but allow
+ * command-line override
+ */
+#if defined(__CYGWIN__)
+#  if !defined(PNG_STATIC)
+#    if defined(PNG_USE_GLOBAL_ARRAYS)
+#      undef PNG_USE_GLOBAL_ARRAYS
+#    endif
+#    if !defined(PNG_USE_LOCAL_ARRAYS)
+#      define PNG_USE_LOCAL_ARRAYS
+#    endif
+#  else
+#    if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS)
+#      if defined(PNG_USE_GLOBAL_ARRAYS)
+#        undef PNG_USE_GLOBAL_ARRAYS
+#      endif
+#    endif
+#  endif
+#  if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS)
+#    define PNG_USE_LOCAL_ARRAYS
+#  endif
+#endif
+
+/* Do not use global arrays (helps with building DLL's)
+ * They are no longer used in libpng itself, since version 1.0.5c,
+ * but might be required for some pre-1.0.5c applications.
+ */
+#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS)
+#  if defined(PNG_NO_GLOBAL_ARRAYS) || \
+      (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER)
+#    define PNG_USE_LOCAL_ARRAYS
+#  else
+#    define PNG_USE_GLOBAL_ARRAYS
+#  endif
+#endif
+
+#if defined(__CYGWIN__)
+#  undef PNGAPI
+#  define PNGAPI __cdecl
+#  undef PNG_IMPEXP
+#  define PNG_IMPEXP
+#endif  
+
+/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall",
+ * you may get warnings regarding the linkage of png_zalloc and png_zfree.
+ * Don't ignore those warnings; you must also reset the default calling
+ * convention in your compiler to match your PNGAPI, and you must build
+ * zlib and your applications the same way you build libpng.
+ */
+
+#if defined(__MINGW32__) && !defined(PNG_MODULEDEF)
+#  ifndef PNG_NO_MODULEDEF
+#    define PNG_NO_MODULEDEF
+#  endif
+#endif
+
+#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF)
+#  define PNG_IMPEXP
+#endif
+
+#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \
+    (( defined(_Windows) || defined(_WINDOWS) || \
+       defined(WIN32) || defined(_WIN32) || defined(__WIN32__) ))
+
+#  ifndef PNGAPI
+#     if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800))
+#        define PNGAPI __cdecl
+#     else
+#        define PNGAPI _cdecl
+#     endif
+#  endif
+
+#  if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \
+       0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */)
+#     define PNG_IMPEXP
+#  endif
+
+#  if !defined(PNG_IMPEXP)
+
+#     define PNG_EXPORT_TYPE1(type,symbol)  PNG_IMPEXP type PNGAPI symbol
+#     define PNG_EXPORT_TYPE2(type,symbol)  type PNG_IMPEXP PNGAPI symbol
+
+      /* Borland/Microsoft */
+#     if defined(_MSC_VER) || defined(__BORLANDC__)
+#        if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500)
+#           define PNG_EXPORT PNG_EXPORT_TYPE1
+#        else
+#           define PNG_EXPORT PNG_EXPORT_TYPE2
+#           if defined(PNG_BUILD_DLL)
+#              define PNG_IMPEXP __export
+#           else
+#              define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in
+                                                 VC++ */
+#           endif                             /* Exists in Borland C++ for
+                                                 C++ classes (== huge) */
+#        endif
+#     endif
+
+#     if !defined(PNG_IMPEXP)
+#        if defined(PNG_BUILD_DLL)
+#           define PNG_IMPEXP __declspec(dllexport)
+#        else
+#           define PNG_IMPEXP __declspec(dllimport)
+#        endif
+#     endif
+#  endif  /* PNG_IMPEXP */
+#else /* !(DLL || non-cygwin WINDOWS) */
+#   if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__)
+#      ifndef PNGAPI
+#         define PNGAPI _System
+#      endif
+#   else
+#      if 0 /* ... other platforms, with other meanings */
+#      endif
+#   endif
+#endif
+
+#ifndef PNGAPI
+#  define PNGAPI
+#endif
+#ifndef PNG_IMPEXP
+#  define PNG_IMPEXP
+#endif
+
+#ifdef PNG_BUILDSYMS
+#  ifndef PNG_EXPORT
+#    define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END
+#  endif
+#  ifdef PNG_USE_GLOBAL_ARRAYS
+#    ifndef PNG_EXPORT_VAR
+#      define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT
+#    endif
+#  endif
+#endif
+
+#ifndef PNG_EXPORT
+#  define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol
+#endif
+
+#ifdef PNG_USE_GLOBAL_ARRAYS
+#  ifndef PNG_EXPORT_VAR
+#    define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type
+#  endif
+#endif
+
+/* User may want to use these so they are not in PNG_INTERNAL. Any library
+ * functions that are passed far data must be model independent.
+ */
+
+#ifndef PNG_ABORT
+#  define PNG_ABORT() abort()
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#  define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
+#else
+#  define png_jmpbuf(png_ptr) \
+   (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED)
+#endif
+
+#if defined(USE_FAR_KEYWORD)  /* memory model independent fns */
+/* use this to make far-to-near assignments */
+#  define CHECK   1
+#  define NOCHECK 0
+#  define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK))
+#  define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK))
+#  define png_snprintf _fsnprintf   /* Added to v 1.2.19 */
+#  define png_strcpy  _fstrcpy
+#  define png_strncpy _fstrncpy   /* Added to v 1.2.6 */
+#  define png_strlen  _fstrlen
+#  define png_memcmp  _fmemcmp    /* SJT: added */
+#  define png_memcpy  _fmemcpy
+#  define png_memset  _fmemset
+#else /* use the usual functions */
+#  define CVT_PTR(ptr)         (ptr)
+#  define CVT_PTR_NOCHECK(ptr) (ptr)
+#  ifndef PNG_NO_SNPRINTF
+#    ifdef _MSC_VER
+#      define png_snprintf _snprintf   /* Added to v 1.2.19 */
+#      define png_snprintf2 _snprintf
+#      define png_snprintf6 _snprintf
+#    else
+#      define png_snprintf snprintf   /* Added to v 1.2.19 */
+#      define png_snprintf2 snprintf
+#      define png_snprintf6 snprintf
+#    endif
+#  else
+     /* You don't have or don't want to use snprintf().  Caution: Using
+      * sprintf instead of snprintf exposes your application to accidental
+      * or malevolent buffer overflows.  If you don't have snprintf()
+      * as a general rule you should provide one (you can get one from
+      * Portable OpenSSH). */
+#    define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1)
+#    define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2)
+#    define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \
+        sprintf(s1,fmt,x1,x2,x3,x4,x5,x6)
+#  endif
+#  define png_strcpy  strcpy
+#  define png_strncpy strncpy     /* Added to v 1.2.6 */
+#  define png_strlen  strlen
+#  define png_memcmp  memcmp      /* SJT: added */
+#  define png_memcpy  memcpy
+#  define png_memset  memset
+#endif
+/* End of memory model independent support */
+
+/* Just a little check that someone hasn't tried to define something
+ * contradictory.
+ */
+#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K)
+#  undef PNG_ZBUF_SIZE
+#  define PNG_ZBUF_SIZE 65536L
+#endif
+
+#ifdef PNG_READ_SUPPORTED
+/* Prior to libpng-1.0.9, this block was in pngasmrd.h */
+#if defined(PNG_INTERNAL)
+
+#if defined(PNG_USE_PNGGCCRD) || defined(PNG_USE_PNGVCRD)
+  /* Platform must be Pentium.  Makefile must assemble and load
+   * pnggccrd.c or  pngvcrd.c. MMX will be detected at run time and
+   * used if present.
+   */
+#  ifndef PNG_NO_MMX_COMBINE_ROW
+#    define PNG_HAVE_MMX_COMBINE_ROW
+#  endif
+#  ifndef PNG_NO_MMX_READ_INTERLACE
+#    define PNG_HAVE_MMX_READ_INTERLACE
+#  endif
+#  ifndef PNG_NO_MMX_READ_FILTER_ROW
+#    define PNG_HAVE_MMX_READ_FILTER_ROW
+#    ifndef PNG_NO_MMX_FILTER_SUB
+#      define PNG_MMX_READ_FILTER_SUB_SUPPORTED
+#    endif
+#    if !(defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4))
+       /* work around 64-bit gcc compiler bugs in gcc-3.x */
+#      ifndef PNG_NO_MMX_FILTER_UP
+#        define PNG_MMX_READ_FILTER_UP_SUPPORTED
+#      endif
+#      ifndef PNG_NO_MMX_FILTER_AVG
+#        define PNG_MMX_READ_FILTER_AVG_SUPPORTED
+#      endif
+#      ifndef PNG_NO_MMX_FILTER_PAETH
+#        define PNG_MMX_READ_FILTER_PAETH_SUPPORTED
+#      endif
+#    endif /* !((__x86_64__) && (GNUC < 4)) */
+#  endif
+  /* These are the default thresholds before the MMX code kicks in; if either
+   * rowbytes or bitdepth is below the threshold, plain C code is used.  These
+   * can be overridden at runtime via the png_set_mmx_thresholds() call in
+   * libpng 1.2.0 and later.  The values below were chosen by Intel.
+   */
+#  ifndef PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT
+#    define PNG_MMX_ROWBYTES_THRESHOLD_DEFAULT  128  /*  >=  */
+#  endif
+#  ifndef PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT
+#    define PNG_MMX_BITDEPTH_THRESHOLD_DEFAULT  9    /*  >=  */   
+#  endif
+#endif /* PNG_USE_PNGGCCRD || PNG_USE_PNGVCRD */
+/* - see pngvcrd.c or pnggccrd.c for info about what is currently enabled */
+
+#endif /* PNG_INTERNAL */
+#endif /* PNG_READ_SUPPORTED */
+
+/* Added at libpng-1.2.8 */
+#endif /* PNG_VERSION_INFO_ONLY */
+
+#endif /* PNGCONF_H */
diff --git a/distrib/libpng-1.2.19/pngerror.c b/distrib/libpng-1.2.19/pngerror.c
new file mode 100644
index 0000000..b1d5fbe
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngerror.c
@@ -0,0 +1,326 @@
+
+/* pngerror.c - stub functions for i/o and memory allocation
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all error handling.  Users who
+ * need special error handling are expected to write replacement functions
+ * and use png_set_error_fn() to use those functions.  See the instructions
+ * at each function.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+static void /* PRIVATE */
+png_default_error PNGARG((png_structp png_ptr,
+  png_const_charp error_message));
+#ifndef PNG_NO_WARNINGS
+static void /* PRIVATE */
+png_default_warning PNGARG((png_structp png_ptr,
+  png_const_charp warning_message));
+#endif /* PNG_NO_WARNINGS */
+
+/* This function is called whenever there is a fatal error.  This function
+ * should not be changed.  If there is a need to handle errors differently,
+ * you should supply a replacement error function and use png_set_error_fn()
+ * to replace the error function at run-time.
+ */
+void PNGAPI
+png_error(png_structp png_ptr, png_const_charp error_message)
+{
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   char msg[16];
+   if (png_ptr != NULL)
+   {
+     if (png_ptr->flags&
+       (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
+     {
+       if (*error_message == '#')
+       {
+           int offset;
+           for (offset=1; offset<15; offset++)
+              if (*(error_message+offset) == ' ')
+                  break;
+           if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
+           {
+              int i;
+              for (i=0; i<offset-1; i++)
+                 msg[i]=error_message[i+1];
+              msg[i]='\0';
+              error_message=msg;
+           }
+           else
+              error_message+=offset;
+       }
+       else
+       {
+           if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT)
+           {
+              msg[0]='0';
+              msg[1]='\0';
+              error_message=msg;
+           }
+       }
+     }
+   }
+#endif
+   if (png_ptr != NULL && png_ptr->error_fn != NULL)
+      (*(png_ptr->error_fn))(png_ptr, error_message);
+
+   /* If the custom handler doesn't exist, or if it returns,
+      use the default handler, which will not return. */
+   png_default_error(png_ptr, error_message);
+}
+
+#ifndef PNG_NO_WARNINGS
+/* This function is called whenever there is a non-fatal error.  This function
+ * should not be changed.  If there is a need to handle warnings differently,
+ * you should supply a replacement warning function and use
+ * png_set_error_fn() to replace the warning function at run-time.
+ */
+void PNGAPI
+png_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+   int offset = 0;
+   if (png_ptr != NULL)
+   {
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (png_ptr->flags&
+     (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))
+#endif
+     {
+       if (*warning_message == '#')
+       {
+           for (offset=1; offset<15; offset++)
+              if (*(warning_message+offset) == ' ')
+                  break;
+       }
+     }
+     if (png_ptr != NULL && png_ptr->warning_fn != NULL)
+        (*(png_ptr->warning_fn))(png_ptr, warning_message+offset);
+   }
+   else
+      png_default_warning(png_ptr, warning_message+offset);
+}
+#endif /* PNG_NO_WARNINGS */
+
+
+/* These utilities are used internally to build an error message that relates
+ * to the current chunk.  The chunk name comes from png_ptr->chunk_name,
+ * this is used to prefix the message.  The message is limited in length
+ * to 63 bytes, the name characters are output as hex digits wrapped in []
+ * if the character is invalid.
+ */
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+static PNG_CONST char png_digit[16] = {
+   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+   'A', 'B', 'C', 'D', 'E', 'F'
+};
+
+static void /* PRIVATE */
+png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp
+   error_message)
+{
+   int iout = 0, iin = 0;
+
+   while (iin < 4)
+   {
+      int c = png_ptr->chunk_name[iin++];
+      if (isnonalpha(c))
+      {
+         buffer[iout++] = '[';
+         buffer[iout++] = png_digit[(c & 0xf0) >> 4];
+         buffer[iout++] = png_digit[c & 0x0f];
+         buffer[iout++] = ']';
+      }
+      else
+      {
+         buffer[iout++] = (png_byte)c;
+      }
+   }
+
+   if (error_message == NULL)
+      buffer[iout] = 0;
+   else
+   {
+      buffer[iout++] = ':';
+      buffer[iout++] = ' ';
+      png_strncpy(buffer+iout, error_message, 63);
+      buffer[iout+63] = 0;
+   }
+}
+
+#ifdef PNG_READ_SUPPORTED
+void PNGAPI
+png_chunk_error(png_structp png_ptr, png_const_charp error_message)
+{
+   char msg[18+64];
+   if (png_ptr == NULL)
+     png_error(png_ptr, error_message);
+   else
+   {
+     png_format_buffer(png_ptr, msg, error_message);
+     png_error(png_ptr, msg);
+   }
+}
+
+#ifndef PNG_NO_WARNINGS
+void PNGAPI
+png_chunk_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+   char msg[18+64];
+   if (png_ptr == NULL)
+     png_warning(png_ptr, warning_message);
+   else
+   {
+     png_format_buffer(png_ptr, msg, warning_message);
+     png_warning(png_ptr, msg);
+   }
+}
+#endif /* PNG_NO_WARNINGS */
+
+#endif /* PNG_READ_SUPPORTED */
+
+/* This is the default error handling function.  Note that replacements for
+ * this function MUST NOT RETURN, or the program will likely crash.  This
+ * function is used by default, or if the program supplies NULL for the
+ * error function pointer in png_set_error_fn().
+ */
+static void /* PRIVATE */
+png_default_error(png_structp png_ptr, png_const_charp error_message)
+{
+#ifndef PNG_NO_CONSOLE_IO
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (*error_message == '#')
+   {
+     int offset;
+     char error_number[16];
+     for (offset=0; offset<15; offset++)
+     {
+         error_number[offset] = *(error_message+offset+1);
+         if (*(error_message+offset) == ' ')
+             break;
+     }
+     if((offset > 1) && (offset < 15))
+     {
+       error_number[offset-1]='\0';
+       fprintf(stderr, "libpng error no. %s: %s\n", error_number,
+          error_message+offset);
+     }
+     else
+       fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset);
+   }
+   else
+#endif
+   fprintf(stderr, "libpng error: %s\n", error_message);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   if (png_ptr)
+   {
+#  ifdef USE_FAR_KEYWORD
+   {
+      jmp_buf jmpbuf;
+      png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf));
+      longjmp(jmpbuf, 1);
+   }
+#  else
+   longjmp(png_ptr->jmpbuf, 1);
+#  endif
+   }
+#else
+   PNG_ABORT();
+#endif
+#ifdef PNG_NO_CONSOLE_IO
+   error_message = error_message; /* make compiler happy */
+#endif
+}
+
+#ifndef PNG_NO_WARNINGS
+/* This function is called when there is a warning, but the library thinks
+ * it can continue anyway.  Replacement functions don't have to do anything
+ * here if you don't want them to.  In the default configuration, png_ptr is
+ * not used, but it is passed in case it may be useful.
+ */
+static void /* PRIVATE */
+png_default_warning(png_structp png_ptr, png_const_charp warning_message)
+{
+#ifndef PNG_NO_CONSOLE_IO
+#  ifdef PNG_ERROR_NUMBERS_SUPPORTED
+   if (*warning_message == '#')
+   {
+     int offset;
+     char warning_number[16];
+     for (offset=0; offset<15; offset++)
+     {
+        warning_number[offset]=*(warning_message+offset+1);
+        if (*(warning_message+offset) == ' ')
+            break;
+     }
+     if((offset > 1) && (offset < 15))
+     {
+       warning_number[offset-1]='\0';
+       fprintf(stderr, "libpng warning no. %s: %s\n", warning_number,
+          warning_message+offset);
+     }
+     else
+       fprintf(stderr, "libpng warning: %s\n", warning_message);
+   }
+   else
+#  endif
+     fprintf(stderr, "libpng warning: %s\n", warning_message);
+#else
+   warning_message = warning_message; /* make compiler happy */
+#endif
+   png_ptr = png_ptr; /* make compiler happy */
+}
+#endif /* PNG_NO_WARNINGS */
+
+/* This function is called when the application wants to use another method
+ * of handling errors and warnings.  Note that the error function MUST NOT
+ * return to the calling routine or serious problems will occur.  The return
+ * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1)
+ */
+void PNGAPI
+png_set_error_fn(png_structp png_ptr, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warning_fn)
+{
+   if (png_ptr == NULL)
+      return;
+   png_ptr->error_ptr = error_ptr;
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+}
+
+
+/* This function returns a pointer to the error_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_error_ptr(png_structp png_ptr)
+{
+   if (png_ptr == NULL)
+      return NULL;
+   return ((png_voidp)png_ptr->error_ptr);
+}
+
+
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+void PNGAPI
+png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode)
+{
+   if(png_ptr != NULL)
+   {
+     png_ptr->flags &=
+       ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode);
+   }
+}
+#endif
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pnggccrd.c b/distrib/libpng-1.2.19/pnggccrd.c
new file mode 100644
index 0000000..f63867c
--- /dev/null
+++ b/distrib/libpng-1.2.19/pnggccrd.c
@@ -0,0 +1,6041 @@
+
+/* pnggccrd.c - mixed C/assembler version of utilities to read a PNG file
+ *
+ * For Intel/AMD x86 or x86-64 CPU (Pentium-MMX or later) and GNU C compiler.
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998 Intel Corporation
+ * Copyright (c) 1999-2002,2007 Greg Roelofs
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ *
+ * Based on MSVC code contributed by Nirav Chhatrapati, Intel Corp., 1998.
+ * Interface to libpng contributed by Gilles Vollant, 1999.
+ * GNU C port by Greg Roelofs, 1999-2001.
+ *
+ * References:
+ *
+ *     http://www.intel.com/drg/pentiumII/appnotes/916/916.htm
+ *     http://www.intel.com/drg/pentiumII/appnotes/923/923.htm
+ *       [Intel's performance analysis of the MMX vs. non-MMX code;
+ *        moved/deleted as of 2006, but text and some graphs still
+ *        available via WayBack Machine at archive.org]
+ *
+ *     http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
+ *     http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inline-assembly-and-pic-mix-well
+ *     http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
+ *     http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html
+ *     http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
+ *     AMD64 Architecture Programmer's Manual, volumes 1 and 5
+ *       [http://www.amd.com/us-en/Processors/TechnicalResources/0,,30_182_739_7044,00.html]
+ *     Intel 64 and IA-32 Software Developer's Manuals
+ *       [http://developer.intel.com/products/processor/manuals/]
+ *
+ * png_read_filter_row_mmx_*() were converted in place with intel2gas 1.3.1:
+ *
+ *     intel2gas -mdI pnggccrd.c.partially-msvc -o pnggccrd.c
+ *
+ * and then cleaned up by hand.  See http://hermes.terminal.at/intel2gas/ .
+ *
+ * NOTE:  A sufficiently recent version of GNU as (or as.exe under DOS/Windows)
+ * is required to assemble the newer asm instructions such as movq.  (Version
+ * 2.5.2l.15 is definitely too old.)  See ftp://ftp.gnu.org/pub/gnu/binutils/ .
+ */
+
+/*
+ * PORTING NOTES AND CHANGELOG (mostly by Greg Roelofs)
+ * ===========================
+ *
+ * 19991006:
+ *  - fixed sign error in post-MMX cleanup code (16- & 32-bit cases)
+ *
+ * 19991007:
+ *  - additional optimizations (possible or definite):
+ *     x [DONE] write MMX code for 64-bit case (pixel_bytes == 8) [not tested]
+ *     - write MMX code for 48-bit case (pixel_bytes == 6)
+ *     - figure out what's up with 24-bit case (pixel_bytes == 3):
+ *        why subtract 8 from width_mmx in the pass 4/5 case?
+ *        (only width_mmx case) (near line 2335)
+ *     x [DONE] replace pixel_bytes within each block with the true
+ *        constant value (or are compilers smart enough to do that?)
+ *     - rewrite all MMX interlacing code so it's aligned with
+ *        the *beginning* of the row buffer, not the end.  This
+ *        would not only allow one to eliminate half of the memory
+ *        writes for odd passes (that is, pass == odd), it may also
+ *        eliminate some unaligned-data-access exceptions (assuming
+ *        there's a penalty for not aligning 64-bit accesses on
+ *        64-bit boundaries).  The only catch is that the "leftover"
+ *        pixel(s) at the end of the row would have to be saved,
+ *        but there are enough unused MMX registers in every case,
+ *        so this is not a problem.  A further benefit is that the
+ *        post-MMX cleanup code (C code) in at least some of the
+ *        cases could be done within the assembler block.
+ *  x [DONE] the "v3 v2 v1 v0 v7 v6 v5 v4" comments are confusing,
+ *     inconsistent, and don't match the MMX Programmer's Reference
+ *     Manual conventions anyway.  They should be changed to
+ *     "b7 b6 b5 b4 b3 b2 b1 b0," where b0 indicates the byte that
+ *     was lowest in memory (i.e., corresponding to a left pixel)
+ *     and b7 is the byte that was highest (i.e., a right pixel).
+ *
+ * 19991016:
+ *  - Brennan's Guide notwithstanding, gcc under Linux does *not*
+ *     want globals prefixed by underscores when referencing them--
+ *     i.e., if the variable is const4, then refer to it as const4,
+ *     not _const4.  This seems to be a djgpp-specific requirement.
+ *     Also, such variables apparently *must* be declared outside
+ *     of functions; neither static nor automatic variables work if
+ *     defined within the scope of a single function, but both
+ *     static and truly global (multi-module) variables work fine.
+ *
+ * 19991017:
+ *  - replaced pixel_bytes in each png_memcpy() call with constant value for
+ *     inlining (png_do_read_interlace() "non-MMX/modified C code" block)
+ *
+ * 19991023:
+ *  - fixed png_combine_row() non-MMX replication bug (odd passes only?)
+ *  - switched from string-concatenation-with-macros to cleaner method of
+ *     renaming global variables for djgpp--i.e., always use prefixes in
+ *     inlined assembler code (== strings) and conditionally rename the
+ *     variables, not the other way around.  Hence _const4, _mask8_0, etc.
+ *
+ * 19991024:
+ *  - fixed mmxsupport()/png_do_read_interlace() first-row bug
+ *     This one was severely weird:  even though mmxsupport() doesn't touch
+ *     ebx (where "row" pointer was stored), it nevertheless managed to zero
+ *     the register (even in static/non-fPIC code--see below), which in turn
+ *     caused png_do_read_interlace() to return prematurely on the first row of
+ *     interlaced images (i.e., without expanding the interlaced pixels).
+ *     Inspection of the generated assembly code didn't turn up any clues,
+ *     although it did point at a minor optimization (i.e., get rid of
+ *     mmx_supported_local variable and just use eax).  Possibly the CPUID
+ *     instruction is more destructive than it looks?  (Not yet checked.)
+ *  - "info gcc" was next to useless, so compared fPIC and non-fPIC assembly
+ *     listings...  Apparently register spillage has to do with ebx, since
+ *     it's used to index the global offset table.  Commenting it out of the
+ *     input-reg lists in png_combine_row() eliminated compiler barfage, so
+ *     ifdef'd with __PIC__ macro:  if defined, use a global for unmask
+ *
+ * 19991107:
+ *  - verified CPUID clobberage:  12-char string constant ("GenuineIntel",
+ *     "AuthenticAMD", etc.) placed in ebx:ecx:edx.  Still need to polish.
+ *
+ * 19991120:
+ *  - made "diff" variable (now "_dif") global to simplify conversion of
+ *     filtering routines (running out of regs, sigh).  "diff" is still used
+ *     in interlacing routines, however.
+ *  - fixed up both versions of mmxsupport() (ORIG_THAT_USED_TO_CLOBBER_EBX
+ *     macro determines which is used); original not yet tested.
+ *
+ * 20000213:
+ *  - when compiling with gcc, be sure to use  -fomit-frame-pointer
+ *
+ * 20000319:
+ *  - fixed a register-name typo in png_do_read_interlace(), default (MMX) case,
+ *     pass == 4 or 5, that caused visible corruption of interlaced images
+ *
+ * 20000623:
+ *  - Various problems were reported with gcc 2.95.2 in the Cygwin environment,
+ *     many of the form "forbidden register 0 (ax) was spilled for class AREG."
+ *     This is explained at http://gcc.gnu.org/fom_serv/cache/23.html, and
+ *     Chuck Wilson supplied a patch involving dummy output registers.  See
+ *     http://sourceforge.net/bugs/?func=detailbug&bug_id=108741&group_id=5624
+ *     for the original (anonymous) SourceForge bug report.
+ *
+ * 20000706:
+ *  - Chuck Wilson passed along these remaining gcc 2.95.2 errors:
+ *       pnggccrd.c: In function `png_combine_row':
+ *       pnggccrd.c:525: more than 10 operands in `asm'
+ *       pnggccrd.c:669: more than 10 operands in `asm'
+ *       pnggccrd.c:828: more than 10 operands in `asm'
+ *       pnggccrd.c:994: more than 10 operands in `asm'
+ *       pnggccrd.c:1177: more than 10 operands in `asm'
+ *     They are all the same problem and can be worked around by using the
+ *     global _unmask variable unconditionally, not just in the -fPIC case.
+ *     Reportedly earlier versions of gcc also have the problem with more than
+ *     10 operands; they just don't report it.  Much strangeness ensues, etc.
+ *
+ * 20000729:
+ *  - enabled png_read_filter_row_mmx_up() (shortest remaining unconverted
+ *     MMX routine); began converting png_read_filter_row_mmx_sub()
+ *  - to finish remaining sections:
+ *     - clean up indentation and comments
+ *     - preload local variables
+ *     - add output and input regs (order of former determines numerical
+ *        mapping of latter)
+ *     - avoid all usage of ebx (including bx, bh, bl) register [20000823]
+ *     - remove "$" from addressing of Shift and Mask variables [20000823]
+ *
+ * 20000731:
+ *  - global union vars causing segfaults in png_read_filter_row_mmx_sub()?
+ *
+ * 20000822:
+ *  - ARGH, stupid png_read_filter_row_mmx_sub() segfault only happens with
+ *     shared-library (-fPIC) version!  Code works just fine as part of static
+ *     library.  Should have tested that sooner.
+ *     ebx is getting clobbered again (explicitly this time); need to save it
+ *     on stack or rewrite asm code to avoid using it altogether.  Blargh!
+ *
+ * 20000823:
+ *  - first section was trickiest; all remaining sections have ebx -> edx now.
+ *     (-fPIC works again.)  Also added missing underscores to various Shift*
+ *     and *Mask* globals and got rid of leading "$" signs.
+ *
+ * 20000826:
+ *  - added visual separators to help navigate microscopic printed copies
+ *     (http://pobox.com/~newt/code/gpr-latest.zip, mode 10); started working
+ *     on png_read_filter_row_mmx_avg()
+ *
+ * 20000828:
+ *  - finished png_read_filter_row_mmx_avg():  only Paeth left! (930 lines...)
+ *     What the hell, did png_read_filter_row_mmx_paeth(), too.  Comments not
+ *     cleaned up/shortened in either routine, but functionality is complete
+ *     and seems to be working fine.
+ *
+ * 20000829:
+ *  - ahhh, figured out last(?) bit of gcc/gas asm-fu:  if register is listed
+ *     as an input reg (with dummy output variables, etc.), then it *cannot*
+ *     also appear in the clobber list or gcc 2.95.2 will barf.  The solution
+ *     is simple enough...
+ *
+ * 20000914:
+ *  - bug in png_read_filter_row_mmx_avg():  16-bit grayscale not handled
+ *     correctly (but 48-bit RGB just fine)
+ *
+ * 20000916:
+ *  - fixed bug in png_read_filter_row_mmx_avg(), bpp == 2 case; three errors:
+ *     - "_ShiftBpp.use = 24;"      should have been   "_ShiftBpp.use = 16;"
+ *     - "_ShiftRem.use = 40;"      should have been   "_ShiftRem.use = 48;"
+ *     - "psllq _ShiftRem, %%mm2"   should have been   "psrlq _ShiftRem, %%mm2"
+ *
+ * 20010101:
+ *  - added new png_init_mmx_flags() function (here only because it needs to
+ *     call mmxsupport(), which should probably become global png_mmxsupport());
+ *     modified other MMX routines to run conditionally (png_ptr->asm_flags)
+ *
+ * 20010103:
+ *  - renamed mmxsupport() to png_mmx_support(), with auto-set of mmx_supported,
+ *     and made it public; moved png_init_mmx_flags() to png.c as internal func
+ *
+ * 20010104:
+ *  - removed dependency on png_read_filter_row_c() (C code already duplicated
+ *     within MMX version of png_read_filter_row()) so no longer necessary to
+ *     compile it into pngrutil.o
+ *
+ * 20010310:
+ *  - fixed buffer-overrun bug in png_combine_row() C code (non-MMX)
+ *
+ * 20010808:
+ *  - added PNG_THREAD_UNSAFE_OK around code using global variables [GR-P]
+ *
+ * 20011124:
+ *  - fixed missing save of Eflag in png_mmx_support() [Maxim Sobolev]
+ *
+ * 20020304:
+ *  - eliminated incorrect use of width_mmx in pixel_bytes == 8 case
+ *
+ * 20020407:
+ *  - fixed insufficient preservation of ebx register [Sami Farin]
+ *
+ * 20040724:
+ *  - more tinkering with clobber list at lines 4529 and 5033 to get it to
+ *     compile with gcc 3.4 [GR-P]
+ *
+ * 20040809:
+ *  - added "rim" definitions for CONST4 and CONST6 [GR-P]
+ *
+ * 20060303:
+ *  - added "OS2" to list of systems that don't need leading underscores [GR-P]
+ *
+ * 20060320:
+ *  - made PIC-compliant [Christian Aichinger]
+ *
+ * 20070313:
+ *  - finally applied Giuseppe Ghibò's 64-bit patch of 20060803 (completely
+ *     overlooked Dylan Alex Simon's similar patch of 20060414, oops...)
+ *
+ * 20070524:
+ *  - fixed link failure caused by asm-only variables being optimized out
+ *     (identified by Dimitri of Trolltech) with __attribute__((used)), which
+ *     also gets rid of warnings => nuked ugly png_squelch_warnings() hack
+ *  - dropped redundant ifdef
+ *  - moved png_mmx_support() back up where originally intended (as in
+ *     pngvcrd.c), using __attribute__((noinline)) in extra prototype
+ *
+ * 20070527:
+ *  - revised png_combine_row() to reuse mask in lieu of external _unmask
+ *  - moved 32-bit (RGBA) case to top of png_combine_row():  most common
+ *  - just about ready to give up on x86-64 -fPIC mode; can't even access 16
+ *     _mask*_* constants without triggering link error on shared library:
+ *       /usr/bin/ld: pnggccrd.pic.o: relocation R_X86_64_32S against `a local
+ *         symbol' can not be used when making a shared object; recompile with
+ *         -fPIC
+ *       pnggccrd.pic.o: could not read symbols: Bad value
+ *       ("objdump -x pnggccrd.pic.o | grep rodata" to verify)
+ *     [might be able to work around by doing within assembly code whatever
+ *     -fPIC does, but given problems to date, seems like long shot...]
+ *     [relevant ifdefs:  __x86_64__ && __PIC__ => C code only]
+ *  - changed #if 0 to #ifdef PNG_CLOBBER_MMX_REGS_SUPPORTED in case gcc ever
+ *     supports MMX regs (%mm0, etc.) in clobber list (not supported by gcc
+ *     2.7.2.3, 2.91.66 (egcs 1.1.2), 3.x, or 4.1.2)
+ *
+ * 20070603:
+ *  - revised png_combine_row() to use @GOTPCREL(%%rip) addressing on _c64
+ *     struct of _mask*_* constants for x86-64 -fPIC; see sam.zoy.org link
+ *     above for details
+ *  - moved _const4 and _const6 into _c64 struct, renamed to _amask5_3_0 and
+ *     _amask7_1_0, respectively
+ *  - can't figure out how to use _c64._mask*_* vars within asm code, so still
+ *     need single variables for non-x86-64/-fPIC half :-(
+ *  - replaced various __PIC__ ifdefs with *_GOT_ebx macros
+ *  - moved _LBCarryMask and _HBClearMask into _c64 struct
+ *  - conditionally replaced _p*temp variables with %r11d-%r13d (via p*_TEMP
+ *     and CLOBBER_r1*d macros)
+ *
+ * 20070604:
+ *  - replaced all _ActiveMask and _ActiveMaskEnd with new _amask*_*_* consts
+ *     (_amask naming convention:  numbers of 00-bytes, ff-bytes, 00-bytes)
+ *    - _ActiveMask     // (10) // avg/paeth/sub; read-only; consts; movq/pand
+ *       0x0000000000ffffffLL (bpp 3, avg)      _amask5_3_0
+ *       0xffffffffffffffffLL (bpp 4, 6, avg)   _amask0_8_0
+ *       0x000000000000ffffLL (bpp 2, avg)      _amask6_2_0
+ *       0x0000000000ffffffLL (bpp 3, paeth)    _amask5_3_0
+ *       0x00000000ffffffffLL (bpp 6, paeth)    _amask4_4_0
+ *       0x00000000ffffffffLL (bpp 4, paeth)    _amask4_4_0
+ *       0x00000000ffffffffLL (bpp 8, paeth)    _amask4_4_0
+ *       0x0000ffffff000000LL (bpp 3, sub)      _amask2_3_3
+ *       0x00000000ffff0000LL (bpp 2, sub)      _amask4_2_2
+ *    - _ActiveMaskEnd  // (1)  // paeth only; read-only; const; pand
+ *       0xffff000000000000LL (bpp 3, paeth)    _amask0_2_6
+ *  - changed all "#if defined(__x86_64__) // later // && defined(__PIC__)"
+ *     lines to "#ifdef PNG_x86_64_USE_GOTPCREL" for easier/safer testing
+ *
+ * 20070605:
+ *  - merged PNG_x86_64_USE_GOTPCREL, non-PNG_x86_64_USE_GOTPCREL code via
+ *     *MASK* and LOAD/RESTORE macros
+ *
+ * 20070607:
+ *  - replaced all constant instances of _ShiftBpp, _ShiftRem with immediates
+ *     (still have two shared cases in avg, sub routines)
+ *
+ * 20070609:
+ *  - replaced remaining instances of _ShiftBpp, _ShiftRem with immediates
+ *     (split sub and avg 4/6-bpp cases into separate blocks)
+ *  - fixed paeth bug due to clobbered r11/r12/r13 regs
+ *
+ * 20070610:
+ *  - made global "_dif" variable (avg/paeth/sub routines) local again (now
+ *     "diff"--see 19991120 entry above), using register constraints
+ *  - note that %ebp in clobber list doesn't actually work, at least for 32-bit
+ *     version and gcc 4.1.2; must save and restore manually.  (Seems to work
+ *     OK for 64-bit version and gcc 3.4.3, but gcc may not be using ebp/rbp
+ *     in that case.)
+ *  - started replacing direct _MMXLength accesses with register constraints
+ *
+ * 20070612:
+ *  - continued replacing direct _MMXLength accesses with register constraints
+ *
+ * 20070613:
+ *  - finished replacing direct _MMXLength accesses with register constraints;
+ *     switched to local variable (and renamed back to MMXLength)
+ *
+ * 20070614:
+ *  - fixed sub bpp = 1 bug
+ *  - started replacing direct _FullLength accesses with register constraints
+ *
+ * 20070615:
+ *  - fixed 64-bit paeth bpp 3 crash bug (misplaced LOAD_GOT_rbp)
+ *  - fixed 64-bit paeth bpp 1/2 and cleanup-block crash bugs (misplaced
+ *     RESTORE_r11_r12_r13)
+ *  - slightly optimized avg/paeth cleanup blocks and paeth bpp 1/2 block
+ *     (save/restore ebx only if needed)
+ *  - continued replacing direct _FullLength accesses with register constraints
+ *
+ * 20070616:
+ *  - finished replacing direct _FullLength accesses with register constraints
+ *     (*ugly* conditional clobber-separator macros for avg and paeth, sigh)
+ *
+ * 20070618:
+ *  - fixed misplaced PNG_THREAD_UNSAFE_OK endif (was missing LOAD_GOT_rbp/
+ *     RESTORE_rbp in 32-bit thread-safe case)
+ *  - changed all "ifdef *" to "if defined(*)" [GR-P]
+ *
+ * 20070619:
+ *  - rearranged most bitdepth-related case statements to put most frequent
+ *     cases at top (24-bit, 32-bit, 8-bit, rest)
+ *
+ * 20070623:
+ *  - cleaned up png_debug() warnings/formatting
+ *  - removed PNG_MMX_CODE_SUPPORTED ifdefs and added outer __GNUC__ ifdef
+ *     (module no longer used by non-x86/non-GCC builds as of libpng 1.2.19)
+ *  - removed single libpng-1.2.x PNG_DEBUG dependency on 1.0.x png_struct
+ *     member (row_buf_size)
+ *  - rearranged pass-related if-blocks in png_do_read_interlace() to put most
+ *     frequent cases (4, 5) at top [GR-P suggestion]
+ *
+ * 20070624-29:
+ *  - fixed 64-bit crash bug:  pointers -> rsi/rdi, not esi/edi (switched to
+ *     %0/%1/%2/%3/%4 notation; eliminated size suffixes from relevant add/
+ *     inc/sub/mov instructions; changed dummy vars to pointers)
+ *     - png_combine_row()
+ *     - png_do_read_interlace()
+ *     - png_read_filter_row_mmx_avg()
+ *     - png_read_filter_row_mmx_paeth()
+ *     - png_read_filter_row_mmx_sub()
+ *     - png_read_filter_row_mmx_up()
+ *  - NOTE:  this fix makes use of the fact that modifying a 32-bit reg (e.g.,
+ *     %%ebx) clears the top half of its corresponding 64-bit reg (%%rbx), so
+ *     it's safe to mix 32-bit operations with 64-bit base/index addressing
+ *     (see new PSI/PAX/PBX/PDX/PBP/etc. "pointer-register" macros); applies
+ *     also to clobber lists
+ *
+ * 20070630:
+ *  - cleaned up formatting, macros, minor png_read_filter_row_mmx_sub() 8-bpp
+ *     register-usage inefficiency
+ *  - fixed 32-bit png_do_read_interlace() bug (was using pointer size for
+ *     64-bit dummy values)
+ *
+ * 20070703:
+ *  - added check for (manual) PIC macro to fix OpenBSD crash bug
+ *
+ * 20070717:
+ *  - fixed 48-bit png_combine_row() bug (was acting like 32-bit):  copy 6
+ *     bytes per pixel, not 4, and use stride of 6, not 4, in the second loop
+ *     of interlace processing of 48-bit pixels [GR-P]
+ *
+ * 20070722:
+ *  - fixed 64-bit png_uint_32 bug with MMXLength/FullLength temp vars
+ *
+ * [still broken:  tops of all row-filter blocks (input/output constraints);
+ *  shows up on 64-bit dynamic (-fPIC) version with -O2, especially if debug-
+ *  printfs enabled, but at right edge of odd-width images even if disabled]
+ *
+ *
+ * STILL TO DO:
+ *  - fix final thread-unsafe code using stack vars and pointer? (paeth top,
+ *     default, bottom only:  default, bottom already 5 reg constraints; could
+ *     replace bpp with pointer and group bpp/patemp/pbtemp/pctemp in array)
+ *  - fix ebp/no-reg-constraint inefficiency (avg/paeth/sub top)
+ *  - test png_do_read_interlace() 64-bit case (pixel_bytes == 8)
+ *  - write MMX code for 48-bit case (pixel_bytes == 6)
+ *  - figure out what's up with 24-bit case (pixel_bytes == 3):
+ *     why subtract 8 from width_mmx in the pass 4/5 case?  due to
+ *     odd number of bytes? (only width_mmx case) (near line 2335)
+ *  - rewrite all MMX interlacing code so it's aligned with beginning
+ *     of the row buffer, not the end (see 19991007 for details)
+ *  - add error messages to any remaining bogus default cases
+ *  - enable pixel_depth == 8 cases in png_read_filter_row()? (test speed)
+ *  - try =r, etc., as reg constraints?  (would gcc use 64-bit ones on x86-64?)
+ *  - need full, non-graphical, CRC-based test suite...  maybe autogenerate
+ *     random data of various height/width/depth, compute CRCs, write (C
+ *     funcs), read (asm/MMX), recompute CRCs, and compare?
+ *  - write true x86-64 version using 128-bit "media instructions", %xmm0-15,
+ *     and extra general-purpose registers
+ */
+
+#if defined(__GNUC__)
+
+#define PNG_INTERNAL
+#include "png.h"
+
+
+/* for some inexplicable reason, gcc 3.3.5 on OpenBSD (and elsewhere?) does
+ * *not* define __PIC__ when the -fPIC option is used, so we have to rely on
+ * makefiles and whatnot to define the PIC macro explicitly */
+#if defined(PIC) && !defined(__PIC__)   // (this can/should move to pngconf.h)
+#  define __PIC__
+#endif
+
+#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && defined(PNG_USE_PNGGCCRD)
+
+/* if you want/need full thread-safety on x86-64 even when linking statically,
+ * comment out the "&& defined(__PIC__)" part here: */
+#if defined(__x86_64__) && defined(__PIC__)
+#  define PNG_x86_64_USE_GOTPCREL            // GOTPCREL => full thread-safety
+#  define PNG_CLOBBER_x86_64_REGS_SUPPORTED  // works as of gcc 3.4.3 ...
+#endif
+
+int PNGAPI png_mmx_support(void);
+
+#if defined(PNG_USE_LOCAL_ARRAYS)
+static PNG_CONST int FARDATA png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+static PNG_CONST int FARDATA png_pass_inc[7]   = {8, 8, 4, 4, 2, 2, 1};
+static PNG_CONST int FARDATA png_pass_width[7] = {8, 4, 4, 2, 2, 1, 1};
+#endif
+
+/* djgpp, Win32, Cygwin, and OS2 add their own underscores to global variables,
+ * so define them without: */
+#if defined(__DJGPP__) || defined(WIN32) || defined(__CYGWIN__) || \
+    defined(__OS2__)
+#  define _mmx_supported  mmx_supported
+#  define _mask8_0        mask8_0
+#  define _mask16_1       mask16_1
+#  define _mask16_0       mask16_0
+#  define _mask24_2       mask24_2
+#  define _mask24_1       mask24_1
+#  define _mask24_0       mask24_0
+#  define _mask32_3       mask32_3
+#  define _mask32_2       mask32_2
+#  define _mask32_1       mask32_1
+#  define _mask32_0       mask32_0
+#  define _mask48_5       mask48_5
+#  define _mask48_4       mask48_4
+#  define _mask48_3       mask48_3
+#  define _mask48_2       mask48_2
+#  define _mask48_1       mask48_1
+#  define _mask48_0       mask48_0
+#  define _amask5_3_0     amask5_3_0
+#  define _amask7_1_0     amask7_1_0
+#  define _LBCarryMask    LBCarryMask
+#  define _HBClearMask    HBClearMask
+#  define _amask0_8_0     amask0_8_0
+#  define _amask6_2_0     amask6_2_0
+#  define _amask4_4_0     amask4_4_0
+#  define _amask0_2_6     amask0_2_6
+#  define _amask2_3_3     amask2_3_3
+#  define _amask4_2_2     amask4_2_2
+#  if defined(PNG_THREAD_UNSAFE_OK)
+#    define _patemp       patemp
+#    define _pbtemp       pbtemp
+#    define _pctemp       pctemp
+#  endif
+#endif // djgpp, Win32, Cygwin, OS2
+
+
+/* These constants are used in the inlined MMX assembly code. */
+
+typedef unsigned long long  ull;
+
+#if defined(PNG_x86_64_USE_GOTPCREL)
+static PNG_CONST struct {
+    //ull _mask_array[26];
+
+    // png_combine_row() constants:
+    ull _mask8_0;
+    ull _mask16_0, _mask16_1;
+    ull _mask24_0, _mask24_1, _mask24_2;
+    ull _mask32_0, _mask32_1, _mask32_2, _mask32_3;
+    ull _mask48_0, _mask48_1, _mask48_2, _mask48_3, _mask48_4, _mask48_5;
+
+    // png_do_read_interlace() constants:
+    ull _amask5_3_0, _amask7_1_0;  // was _const4 and _const6, respectively
+
+    // png_read_filter_row_mmx_avg() constants (also uses _amask5_3_0):
+    ull _LBCarryMask, _HBClearMask;
+    ull _amask0_8_0, _amask6_2_0;  // was ActiveMask for bpp 4/6 and 2 cases
+
+    // png_read_filter_row_mmx_paeth() constants (also uses _amask5_3_0):
+    ull _amask4_4_0, _amask0_2_6;  // was ActiveMask{,End} for bpp 6/4/8 and 3
+
+    // png_read_filter_row_mmx_sub() constants:
+    ull _amask2_3_3, _amask4_2_2;  // was ActiveMask for bpp 3 and 2 cases
+
+} _c64 __attribute__((used, aligned(8))) = {
+
+    // png_combine_row() constants:
+    0x0102040810204080LL, // _mask8_0      offset 0
+
+    0x1010202040408080LL, // _mask16_0     offset 8
+    0x0101020204040808LL, // _mask16_1     offset 16
+
+    0x2020404040808080LL, // _mask24_0     offset 24
+    0x0408080810101020LL, // _mask24_1     offset 32
+    0x0101010202020404LL, // _mask24_2     offset 40
+
+    0x4040404080808080LL, // _mask32_0     offset 48
+    0x1010101020202020LL, // _mask32_1     offset 56
+    0x0404040408080808LL, // _mask32_2     offset 64
+    0x0101010102020202LL, // _mask32_3     offset 72
+
+    0x4040808080808080LL, // _mask48_0     offset 80
+    0x2020202040404040LL, // _mask48_1     offset 88
+    0x1010101010102020LL, // _mask48_2     offset 96
+    0x0404080808080808LL, // _mask48_3     offset 104
+    0x0202020204040404LL, // _mask48_4     offset 112
+    0x0101010101010202LL, // _mask48_5     offset 120
+
+    // png_do_read_interlace() constants:
+    0x0000000000FFFFFFLL, // _amask5_3_0   offset 128  (bpp 3, avg/paeth) const4
+    0x00000000000000FFLL, // _amask7_1_0   offset 136                     const6
+
+    // png_read_filter_row_mmx_avg() constants:
+    0x0101010101010101LL, // _LBCarryMask  offset 144
+    0x7F7F7F7F7F7F7F7FLL, // _HBClearMask  offset 152
+    0xFFFFFFFFFFFFFFFFLL, // _amask0_8_0   offset 160  (bpp 4/6, avg)
+    0x000000000000FFFFLL, // _amask6_2_0   offset 168  (bpp 2,   avg)
+
+    // png_read_filter_row_mmx_paeth() constants:
+    0x00000000FFFFFFFFLL, // _amask4_4_0   offset 176  (bpp 6/4/8, paeth)
+    0xFFFF000000000000LL, // _amask0_2_6   offset 184  (bpp 3, paeth)   A.M.End
+
+    // png_read_filter_row_mmx_sub() constants:
+    0x0000FFFFFF000000LL, // _amask2_3_3   offset 192  (bpp 3, sub)
+    0x00000000FFFF0000LL, // _amask4_2_2   offset 200  (bpp 2, sub)
+
+};
+
+#define MASK8_0        "(%%rbp)"
+#define MASK16_0       "8(%%rbp)"
+#define MASK16_1       "16(%%rbp)"
+#define MASK24_0       "24(%%rbp)"
+#define MASK24_1       "32(%%rbp)"
+#define MASK24_2       "40(%%rbp)"
+#define MASK32_0       "48(%%rbp)"
+#define MASK32_1       "56(%%rbp)"
+#define MASK32_2       "64(%%rbp)"
+#define MASK32_3       "72(%%rbp)"
+#define MASK48_0       "80(%%rbp)"
+#define MASK48_1       "88(%%rbp)"
+#define MASK48_2       "96(%%rbp)"
+#define MASK48_3       "104(%%rbp)"
+#define MASK48_4       "112(%%rbp)"
+#define MASK48_5       "120(%%rbp)"
+#define AMASK5_3_0     "128(%%rbp)"
+#define AMASK7_1_0     "136(%%rbp)"
+#define LB_CARRY_MASK  "144(%%rbp)"
+#define HB_CLEAR_MASK  "152(%%rbp)"
+#define AMASK0_8_0     "160(%%rbp)"
+#define AMASK6_2_0     "168(%%rbp)"
+#define AMASK4_4_0     "176(%%rbp)"
+#define AMASK0_2_6     "184(%%rbp)"
+#define AMASK2_3_3     "192(%%rbp)"
+#define AMASK4_2_2     "200(%%rbp)"
+
+#else // !PNG_x86_64_USE_GOTPCREL
+
+static PNG_CONST ull _mask8_0  __attribute__((used, aligned(8))) = 0x0102040810204080LL;
+
+static PNG_CONST ull _mask16_1 __attribute__((used, aligned(8))) = 0x0101020204040808LL;
+static PNG_CONST ull _mask16_0 __attribute__((used, aligned(8))) = 0x1010202040408080LL;
+
+static PNG_CONST ull _mask24_2 __attribute__((used, aligned(8))) = 0x0101010202020404LL;
+static PNG_CONST ull _mask24_1 __attribute__((used, aligned(8))) = 0x0408080810101020LL;
+static PNG_CONST ull _mask24_0 __attribute__((used, aligned(8))) = 0x2020404040808080LL;
+
+static PNG_CONST ull _mask32_3 __attribute__((used, aligned(8))) = 0x0101010102020202LL;
+static PNG_CONST ull _mask32_2 __attribute__((used, aligned(8))) = 0x0404040408080808LL;
+static PNG_CONST ull _mask32_1 __attribute__((used, aligned(8))) = 0x1010101020202020LL;
+static PNG_CONST ull _mask32_0 __attribute__((used, aligned(8))) = 0x4040404080808080LL;
+
+static PNG_CONST ull _mask48_5 __attribute__((used, aligned(8))) = 0x0101010101010202LL;
+static PNG_CONST ull _mask48_4 __attribute__((used, aligned(8))) = 0x0202020204040404LL;
+static PNG_CONST ull _mask48_3 __attribute__((used, aligned(8))) = 0x0404080808080808LL;
+static PNG_CONST ull _mask48_2 __attribute__((used, aligned(8))) = 0x1010101010102020LL;
+static PNG_CONST ull _mask48_1 __attribute__((used, aligned(8))) = 0x2020202040404040LL;
+static PNG_CONST ull _mask48_0 __attribute__((used, aligned(8))) = 0x4040808080808080LL;
+
+// png_do_read_interlace() constants:
+static PNG_CONST ull _amask5_3_0  __attribute__((aligned(8))) = 0x0000000000FFFFFFLL;  // was _const4
+static PNG_CONST ull _amask7_1_0  __attribute__((aligned(8))) = 0x00000000000000FFLL;  // was _const6
+
+// png_read_filter_row_mmx_avg() constants:
+static PNG_CONST ull _LBCarryMask __attribute__((used, aligned(8))) = 0x0101010101010101LL;
+static PNG_CONST ull _HBClearMask __attribute__((used, aligned(8))) = 0x7f7f7f7f7f7f7f7fLL;
+static PNG_CONST ull _amask0_8_0  __attribute__((used, aligned(8))) = 0xFFFFFFFFFFFFFFFFLL;
+static PNG_CONST ull _amask6_2_0  __attribute__((used, aligned(8))) = 0x000000000000FFFFLL;
+
+// png_read_filter_row_mmx_paeth() constants:
+static PNG_CONST ull _amask4_4_0  __attribute__((used, aligned(8))) = 0x00000000FFFFFFFFLL;
+static PNG_CONST ull _amask0_2_6  __attribute__((used, aligned(8))) = 0xFFFF000000000000LL;
+
+// png_read_filter_row_mmx_sub() constants:
+static PNG_CONST ull _amask2_3_3  __attribute__((used, aligned(8))) = 0x0000FFFFFF000000LL;
+static PNG_CONST ull _amask4_2_2  __attribute__((used, aligned(8))) = 0x00000000FFFF0000LL;
+
+#define MASK8_0        "_mask8_0"
+#define MASK16_0       "_mask16_0"
+#define MASK16_1       "_mask16_1"
+#define MASK24_0       "_mask24_0"
+#define MASK24_1       "_mask24_1"
+#define MASK24_2       "_mask24_2"
+#define MASK32_0       "_mask32_0"
+#define MASK32_1       "_mask32_1"
+#define MASK32_2       "_mask32_2"
+#define MASK32_3       "_mask32_3"
+#define MASK48_0       "_mask48_0"
+#define MASK48_1       "_mask48_1"
+#define MASK48_2       "_mask48_2"
+#define MASK48_3       "_mask48_3"
+#define MASK48_4       "_mask48_4"
+#define MASK48_5       "_mask48_5"
+#define AMASK5_3_0     "_amask5_3_0"
+#define AMASK7_1_0     "_amask7_1_0"
+#define LB_CARRY_MASK  "_LBCarryMask"
+#define HB_CLEAR_MASK  "_HBClearMask"
+#define AMASK0_8_0     "_amask0_8_0"
+#define AMASK6_2_0     "_amask6_2_0"
+#define AMASK4_4_0     "_amask4_4_0"
+#define AMASK0_2_6     "_amask0_2_6"
+#define AMASK2_3_3     "_amask2_3_3"
+#define AMASK4_2_2     "_amask4_2_2"
+
+#endif // ?PNG_x86_64_USE_GOTPCREL
+
+
+#if defined(PNG_HAVE_MMX_READ_FILTER_ROW) || defined(PNG_HAVE_MMX_COMBINE_ROW)
+
+// this block is specific to png_read_filter_row_mmx_paeth() except for
+// LOAD_GOT_rbp and RESTORE_rbp, which are also used in png_combine_row()
+#if defined(PNG_x86_64_USE_GOTPCREL)
+#  define pa_TEMP                "%%r11d"
+#  define pb_TEMP                "%%r12d"
+#  define pc_TEMP                "%%r13d"
+#  if defined(PNG_CLOBBER_x86_64_REGS_SUPPORTED)  // works as of gcc 3.4.3 ...
+#    define SAVE_r11_r12_r13
+#    define RESTORE_r11_r12_r13
+#    define _CLOBBER_r11_r12_r13 ,"%r11", "%r12", "%r13"
+#    define CLOBBER_r11_r12_r13  "%r11", "%r12", "%r13"
+#  else // !PNG_CLOBBER_x86_64_REGS_SUPPORTED
+#    define SAVE_r11_r12_r13     "pushq %%r11  \n\t" \
+                                 "pushq %%r12  \n\t" \
+                                 "pushq %%r13  \n\t"  // "normally 0-extended"
+#    define RESTORE_r11_r12_r13  "popq  %%r13  \n\t" \
+                                 "popq  %%r12  \n\t" \
+                                 "popq  %%r11  \n\t"
+#    define _CLOBBER_r11_r12_r13
+#    define CLOBBER_r11_r12_r13
+#  endif
+#  define LOAD_GOT_rbp           "pushq %%rbp                        \n\t" \
+                                 "movq  _c64@GOTPCREL(%%rip), %%rbp  \n\t"
+#  define RESTORE_rbp            "popq  %%rbp                        \n\t"
+#else // 32-bit and/or non-PIC
+#  if defined(PNG_THREAD_UNSAFE_OK)
+     // These variables are used in png_read_filter_row_mmx_paeth() and would be
+     //   local variables if not for gcc-inline-assembly addressing limitations
+     //   (some apparently related to ELF format, others to CPU type).
+     //
+     // WARNING: Their presence defeats the thread-safety of libpng.
+     static int                     _patemp  __attribute__((used));
+     static int                     _pbtemp  __attribute__((used));
+     static int                     _pctemp  __attribute__((used));
+#    define pa_TEMP                "_patemp"
+#    define pb_TEMP                "_pbtemp"  // temp variables for
+#    define pc_TEMP                "_pctemp"  //  Paeth routine
+#    define SAVE_r11_r12_r13
+#    define RESTORE_r11_r12_r13
+#    define _CLOBBER_r11_r12_r13   // not using regs => not clobbering
+#    define CLOBBER_r11_r12_r13
+#  endif // PNG_THREAD_UNSAFE_OK
+#  define LOAD_GOT_rbp
+#  define RESTORE_rbp
+#endif
+
+#if defined(__x86_64__)
+#  define SAVE_ebp
+#  define RESTORE_ebp
+#  define _CLOBBER_ebp         ,"%ebp"
+#  define CLOBBER_ebp          "%ebp"
+#  define SAVE_FullLength      "movl %%eax, %%r15d  \n\t"
+#  define RESTORE_FullLength   "movl %%r15d, "     // may go into eax or ecx
+#  if defined(PNG_CLOBBER_x86_64_REGS_SUPPORTED)   // works as of gcc 3.4.3 ...
+#    define SAVE_r15
+#    define RESTORE_r15
+#    define _CLOBBER_r15       ,"%r15"
+#  else
+#    define SAVE_r15           "pushq %%r15  \n\t"
+#    define RESTORE_r15        "popq  %%r15  \n\t"
+#    define _CLOBBER_r15
+#  endif
+#  define PBP                  "%%rbp"             // regs used for 64-bit
+#  define PAX                  "%%rax"             //  pointers or in
+#  define PBX                  "%%rbx"             //  combination with
+#  define PCX                  "%%rcx"             //  64-bit pointer-regs
+#  define PDX                  "%%rdx"             //  (base/index pairs,
+#  define PSI                  "%%rsi"             //  add/sub/mov pairs)
+#  define CLEAR_BOTTOM_3_BITS  "and  $0xfffffffffffffff8, "
+#else
+#  define SAVE_ebp             "pushl %%ebp \n\t"  // clobber list doesn't work
+#  define RESTORE_ebp          "popl  %%ebp \n\t"  //  for %ebp on 32-bit; not
+#  define _CLOBBER_ebp                             //  clear why not
+#  define CLOBBER_ebp
+#  define SAVE_FullLength      "pushl %%eax \n\t"
+#  define RESTORE_FullLength   "popl "             // eax (avg) or ecx (paeth)
+#  define SAVE_r15
+#  define RESTORE_r15
+#  define _CLOBBER_r15
+#  define PBP                  "%%ebp"             // regs used for or in
+#  define PAX                  "%%eax"             //  combination with
+#  define PBX                  "%%ebx"             //  "normal," 32-bit
+#  define PCX                  "%%ecx"             //  pointers
+#  define PDX                  "%%edx"
+#  define PSI                  "%%esi"
+#  define CLEAR_BOTTOM_3_BITS  "and  $0xfffffff8, "
+#endif
+
+// CLOB_COMMA_ebx_ebp:  need comma ONLY if both CLOBBER_ebp and CLOBBER_GOT_ebx
+//                      have values, i.e., only if __x86_64__ AND !__PIC__
+#if defined(__x86_64__) && !defined(__PIC__)
+#  define CLOB_COMMA_ebx_ebp    , // clobbering both ebp and ebx => need comma
+#else
+#  define CLOB_COMMA_ebx_ebp
+#endif
+
+// CLOB_COMMA_ebX_r1X:  need comma UNLESS both CLOBBER_ebp and CLOBBER_GOT_ebx
+//                   are empty OR CLOBBER_r11_r12_r13 is empty--i.e., NO comma
+//                   if (!__x86_64__ AND __PIC__) OR !(PNG_x86_64_USE_GOTPCREL
+//                   AND PNG_CLOBBER_x86_64_REGS_SUPPORTED)   (double sigh...)
+#if (!defined(__x86_64__) && defined(__PIC__)) || \
+    !defined(PNG_x86_64_USE_GOTPCREL) || \
+    !defined(PNG_CLOBBER_x86_64_REGS_SUPPORTED)
+#  define CLOB_COMMA_ebX_r1X
+#else
+#  define CLOB_COMMA_ebX_r1X    , // clobbering (ebp OR ebx) AND r11_r12_r13
+#endif
+
+// CLOB_COLON_ebx_ebp:  need colon unless CLOBBER_ebp and CLOBBER_GOT_ebx are
+//                      BOTH empty--i.e., NO colon if (!__x86_64__ AND __PIC__)
+// CLOB_COLON_ebx_ebp_r1X:  if, in addition, CLOBBER_r11_r12_r13 is empty, then
+//                          no colon for Paeth blocks, either--i.e., NO colon
+//                          if !(PNG_x86_64_USE_GOTPCREL AND
+//                               PNG_CLOBBER_x86_64_REGS_SUPPORTED)
+#if (!defined(__x86_64__) && defined(__PIC__))
+#  define CLOB_COLON_ebx_ebp
+#  if !(defined(PNG_x86_64_USE_GOTPCREL) && \
+        defined(PNG_CLOBBER_x86_64_REGS_SUPPORTED))
+#    define CLOB_COLON_ebx_ebp_r1X
+#  else
+#    define CLOB_COLON_ebx_ebp_r1X  : // clobbering ebp OR ebx OR r11_r12_r13
+#  endif
+#else
+#  define CLOB_COLON_ebx_ebp        : // clobbering ebp OR ebx
+#  define CLOB_COLON_ebx_ebp_r1X    : // clobbering ebp OR ebx OR r11_r12_r13
+#endif
+
+#endif // PNG_HAVE_MMX_READ_FILTER_ROW
+
+#if defined(__PIC__)  // macros to save, restore index to Global Offset Table
+#  if defined(__x86_64__)
+#    define SAVE_GOT_ebx     "pushq %%rbx \n\t"
+#    define RESTORE_GOT_ebx  "popq  %%rbx \n\t"
+#  else
+#    define SAVE_GOT_ebx     "pushl %%ebx \n\t"
+#    define RESTORE_GOT_ebx  "popl  %%ebx \n\t"
+#  endif
+#  define _CLOBBER_GOT_ebx   // explicitly saved, restored => not clobbered
+#  define CLOBBER_GOT_ebx
+#else
+#  define SAVE_GOT_ebx
+#  define RESTORE_GOT_ebx
+#  define _CLOBBER_GOT_ebx   ,"%ebx"
+#  define CLOBBER_GOT_ebx    "%ebx"
+#endif
+
+#if defined(PNG_HAVE_MMX_COMBINE_ROW) || defined(PNG_HAVE_MMX_READ_INTERLACE)
+#  define BPP2  2
+#  define BPP3  3  // bytes per pixel (a.k.a. pixel_bytes)
+#  define BPP4  4  // (defined only to help avoid cut-and-paste errors)
+#  define BPP6  6
+#  define BPP8  8
+#endif
+
+
+
+static int _mmx_supported = 2; // 0: no MMX; 1: MMX supported; 2: not tested
+
+/*===========================================================================*/
+/*                                                                           */
+/*                      P N G _ M M X _ S U P P O R T                        */
+/*                                                                           */
+/*===========================================================================*/
+
+// GRR NOTES:  (1) the following code assumes 386 or better (pushfl/popfl)
+//             (2) all instructions compile with gcc 2.7.2.3 and later
+//           x (3) the function is moved down here to prevent gcc from
+//           x      inlining it in multiple places and then barfing be-
+//           x      cause the ".NOT_SUPPORTED" label is multiply defined
+//                  [need to retest with gcc 2.7.2.3]
+
+// GRR 20070524:  This declaration apparently is compatible with but supersedes
+//   the one in png.h; in any case, the generated object file is slightly
+//   smaller.  It is unnecessary with gcc 4.1.2, but gcc 2.x apparently
+//   replicated the ".NOT_SUPPORTED" label in each location the function was
+//   inlined, leading to compilation errors due to the "multiply defined"
+//   label.  Old workaround was to leave the function at the end of this
+//   file; new one (still testing) is to use a gcc-specific function attribute
+//   to prevent local inlining.
+int PNGAPI
+png_mmx_support(void) __attribute__((noinline));
+
+int PNGAPI
+png_mmx_support(void)
+{
+#if defined(PNG_MMX_CODE_SUPPORTED)  // superfluous, but what the heck
+    int result;
+    __asm__ __volatile__ (
+#if defined(__x86_64__)
+        "pushq %%rbx          \n\t"  // rbx gets clobbered by CPUID instruction
+        "pushq %%rcx          \n\t"  // so does rcx...
+        "pushq %%rdx          \n\t"  // ...and rdx (but rcx & rdx safe on Linux)
+        "pushfq               \n\t"  // save Eflag to stack
+        "popq %%rax           \n\t"  // get Eflag from stack into rax
+        "movq %%rax, %%rcx    \n\t"  // make another copy of Eflag in rcx
+        "xorl $0x200000, %%eax \n\t" // toggle ID bit in Eflag (i.e., bit 21)
+        "pushq %%rax          \n\t"  // save modified Eflag back to stack
+        "popfq                \n\t"  // restore modified value to Eflag reg
+        "pushfq               \n\t"  // save Eflag to stack
+        "popq %%rax           \n\t"  // get Eflag from stack
+        "pushq %%rcx          \n\t"  // save original Eflag to stack
+        "popfq                \n\t"  // restore original Eflag
+#else
+        "pushl %%ebx          \n\t"  // ebx gets clobbered by CPUID instruction
+        "pushl %%ecx          \n\t"  // so does ecx...
+        "pushl %%edx          \n\t"  // ...and edx (but ecx & edx safe on Linux)
+        "pushfl               \n\t"  // save Eflag to stack
+        "popl %%eax           \n\t"  // get Eflag from stack into eax
+        "movl %%eax, %%ecx    \n\t"  // make another copy of Eflag in ecx
+        "xorl $0x200000, %%eax \n\t" // toggle ID bit in Eflag (i.e., bit 21)
+        "pushl %%eax          \n\t"  // save modified Eflag back to stack
+        "popfl                \n\t"  // restore modified value to Eflag reg
+        "pushfl               \n\t"  // save Eflag to stack
+        "popl %%eax           \n\t"  // get Eflag from stack
+        "pushl %%ecx          \n\t"  // save original Eflag to stack
+        "popfl                \n\t"  // restore original Eflag
+#endif
+        "xorl %%ecx, %%eax    \n\t"  // compare new Eflag with original Eflag
+        "jz 0f                \n\t"  // if same, CPUID instr. is not supported
+
+        "xorl %%eax, %%eax    \n\t"  // set eax to zero
+//      ".byte  0x0f, 0xa2    \n\t"  // CPUID instruction (two-byte opcode)
+        "cpuid                \n\t"  // get the CPU identification info
+        "cmpl $1, %%eax       \n\t"  // make sure eax return non-zero value
+        "jl 0f                \n\t"  // if eax is zero, MMX is not supported
+
+        "xorl %%eax, %%eax    \n\t"  // set eax to zero and...
+        "incl %%eax           \n\t"  // ...increment eax to 1.  This pair is
+                                     // faster than the instruction "mov eax, 1"
+        "cpuid                \n\t"  // get the CPU identification info again
+        "andl $0x800000, %%edx \n\t" // mask out all bits but MMX bit (23)
+        "cmpl $0, %%edx       \n\t"  // 0 = MMX not supported
+        "jz 0f                \n\t"  // non-zero = yes, MMX IS supported
+
+        "movl $1, %%eax       \n\t"  // set return value to 1
+        "jmp  1f              \n\t"  // DONE:  have MMX support
+
+    "0:                       \n\t"  // .NOT_SUPPORTED: target label for jump instructions
+        "movl $0, %%eax       \n\t"  // set return value to 0
+    "1:                       \n\t"  // .RETURN: target label for jump instructions
+#if defined(__x86_64__)
+        "popq %%rdx           \n\t"  // restore rdx
+        "popq %%rcx           \n\t"  // restore rcx
+        "popq %%rbx           \n\t"  // restore rbx
+#else
+        "popl %%edx           \n\t"  // restore edx
+        "popl %%ecx           \n\t"  // restore ecx
+        "popl %%ebx           \n\t"  // restore ebx
+#endif
+
+//      "ret                  \n\t"  // DONE:  no MMX support
+                                     // (fall through to standard C "ret")
+
+        : "=a" (result)              // output list
+
+        :                            // any variables used on input (none)
+
+                                     // no clobber list
+//      , "%ebx", "%ecx", "%edx"     // GRR:  we handle these manually
+//      , "memory"   // if write to a variable gcc thought was in a reg
+//      , "cc"       // "condition codes" (flag bits)
+    );
+    _mmx_supported = result;
+#else
+    _mmx_supported = 0;
+#endif /* PNG_MMX_CODE_SUPPORTED */
+
+    return _mmx_supported;
+}
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                       P N G _ C O M B I N E _ R O W                       */
+/*                                                                           */
+/*===========================================================================*/
+
+#if defined(PNG_HAVE_MMX_COMBINE_ROW)
+
+/* Combines the row recently read in with the previous row.
+   This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined; a
+   zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.
+   If you want all pixels to be combined, pass 0xff (255) in mask. */
+
+/* Use this routine for the x86 platform - it uses a faster MMX routine
+   if the machine supports MMX. */
+
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+   int dummy_value_a;    // fix 'forbidden register spilled' error
+   int dummy_value_c;
+   int dummy_value_d;
+   png_bytep dummy_value_S;
+   png_bytep dummy_value_D;
+
+   png_debug(1, "in png_combine_row (pnggccrd.c)\n");
+
+   if (_mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+   if (mask == 0xff)
+   {
+      png_debug(2,"mask == 0xff:  doing single png_memcpy()\n");
+      png_memcpy(row, png_ptr->row_buf + 1,
+       (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,png_ptr->width));
+   }
+   else   /* (png_combine_row() is never called with mask == 0) */
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 24:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if !defined(PNG_1_0_X)
+            if (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width & ~7;          // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7);   // amount lost
+
+               __asm__ __volatile__ (
+                  "not       %%edx            \n\t" // mask => unmask
+                  "movd      %%edx, %%mm7     \n\t" // load bit pattern
+                  "not       %%edx            \n\t" // unmask => mask for later
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  LOAD_GOT_rbp
+                  "movq   " MASK24_0 ", %%mm0 \n\t" // _mask24_0 -> mm0
+                  "movq   " MASK24_1 ", %%mm1 \n\t" // _mask24_1 -> mm1
+                  "movq   " MASK24_2 ", %%mm2 \n\t" // _mask24_2 -> mm2
+                  RESTORE_rbp
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+                  "pand      %%mm7, %%mm2     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+                  "pcmpeqb   %%mm6, %%mm2     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %3       \n\t" // load source
+// preload        "movl      dstptr, %4       \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        mainloop24end    \n\t"
+
+                "mainloop24:                  \n\t"
+                  "movq      (%3), %%mm4      \n\t"
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "movq      (%4), %%mm7      \n\t"
+                  "pandn     %%mm7, %%mm6     \n\t"
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%4)      \n\t"
+
+                  "movq      8(%3), %%mm5     \n\t"
+                  "pand      %%mm1, %%mm5     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "movq      8(%4), %%mm6     \n\t"
+                  "pandn     %%mm6, %%mm7     \n\t"
+                  "por       %%mm7, %%mm5     \n\t"
+                  "movq      %%mm5, 8(%4)     \n\t"
+
+                  "movq      16(%3), %%mm6    \n\t"
+                  "pand      %%mm2, %%mm6     \n\t"
+                  "movq      %%mm2, %%mm4     \n\t"
+                  "movq      16(%4), %%mm7    \n\t"
+                  "pandn     %%mm7, %%mm4     \n\t"
+                  "por       %%mm4, %%mm6     \n\t"
+                  "movq      %%mm6, 16(%4)    \n\t"
+
+                  "add       $24, %3          \n\t" // inc by 24 bytes processed
+                  "add       $24, %4          \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+
+                  "ja        mainloop24       \n\t"
+
+                "mainloop24end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end24            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop24:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip24           \n\t" // if CF = 0
+                  "movw      (%3), %%ax       \n\t"
+                  "movw      %%ax, (%4)       \n\t"
+                  "xorl      %%eax, %%eax     \n\t"
+                  "movb      2(%3), %%al      \n\t"
+                  "movb      %%al, 2(%4)      \n\t"
+
+                "skip24:                      \n\t"
+                  "add       $3, %3           \n\t"
+                  "add       $3, %4           \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop24     \n\t"
+
+                "end24:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "0" (diff),        // eax       // input regs
+                    "1" (mask),        // edx
+                    "2" (len),         // ecx
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "3" (srcptr),      // esi/rsi
+                    "4" (dstptr)       // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                  : "%mm0", "%mm1", "%mm2"          // clobber list
+                  , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* not _mmx_supported - use modified C routine */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP3 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP3 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP3 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP3 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP3;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 24 bpp */
+
+         // formerly claimed to be most common case (combining 32-bit RGBA),
+         // but almost certainly less common than 24-bit RGB case
+         case 32:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if !defined(PNG_1_0_X)
+            if (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width & ~7;          // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7);   // amount lost
+
+               __asm__ __volatile__ (
+                  "not       %%edx            \n\t" // mask => unmask
+                  "movd      %%edx, %%mm7     \n\t" // load bit pattern
+                  "not       %%edx            \n\t" // unmask => mask for later
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  LOAD_GOT_rbp
+                  "movq   " MASK32_0 ", %%mm0 \n\t" // _mask32_0
+                  "movq   " MASK32_1 ", %%mm1 \n\t" // _mask32_1
+                  "movq   " MASK32_2 ", %%mm2 \n\t" // _mask32_2
+                  "movq   " MASK32_3 ", %%mm3 \n\t" // _mask32_3
+                  RESTORE_rbp
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+                  "pand      %%mm7, %%mm2     \n\t"
+                  "pand      %%mm7, %%mm3     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+                  "pcmpeqb   %%mm6, %%mm2     \n\t"
+                  "pcmpeqb   %%mm6, %%mm3     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %3       \n\t" // load source
+// preload        "movl      dstptr, %4       \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t" // lcr
+                  "jz        mainloop32end    \n\t"
+
+                "mainloop32:                  \n\t"
+                  "movq      (%3), %%mm4      \n\t"
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "movq      (%4), %%mm7      \n\t"
+                  "pandn     %%mm7, %%mm6     \n\t"
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%4)      \n\t"
+
+                  "movq      8(%3), %%mm5     \n\t"
+                  "pand      %%mm1, %%mm5     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "movq      8(%4), %%mm6     \n\t"
+                  "pandn     %%mm6, %%mm7     \n\t"
+                  "por       %%mm7, %%mm5     \n\t"
+                  "movq      %%mm5, 8(%4)     \n\t"
+
+                  "movq      16(%3), %%mm6    \n\t"
+                  "pand      %%mm2, %%mm6     \n\t"
+                  "movq      %%mm2, %%mm4     \n\t"
+                  "movq      16(%4), %%mm7    \n\t"
+                  "pandn     %%mm7, %%mm4     \n\t"
+                  "por       %%mm4, %%mm6     \n\t"
+                  "movq      %%mm6, 16(%4)    \n\t"
+
+                  "movq      24(%3), %%mm7    \n\t"
+                  "pand      %%mm3, %%mm7     \n\t"
+                  "movq      %%mm3, %%mm5     \n\t"
+                  "movq      24(%4), %%mm4    \n\t"
+                  "pandn     %%mm4, %%mm5     \n\t"
+                  "por       %%mm5, %%mm7     \n\t"
+                  "movq      %%mm7, 24(%4)    \n\t"
+
+                  "add       $32, %3          \n\t" // inc by 32 bytes processed
+                  "add       $32, %4          \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+                  "ja        mainloop32       \n\t"
+
+                "mainloop32end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end32            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // low byte => high byte
+
+                "secondloop32:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip32           \n\t" // if CF = 0
+                  "movl      (%3), %%eax      \n\t"
+                  "movl      %%eax, (%4)      \n\t"
+
+                "skip32:                      \n\t"
+                  "add       $4, %3           \n\t"
+                  "add       $4, %4           \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop32     \n\t"
+
+                "end32:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "0" (diff),        // eax       // input regs
+                    "1" (mask),        // edx
+                    "2" (len),         // ecx
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "3" (srcptr),      // esi/rsi
+                    "4" (dstptr)       // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                  : "%mm0", "%mm1", "%mm2", "%mm3"  // clobber list
+                  , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* not _mmx_supported - use modified C routine */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP4 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP4 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP4 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP4 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP4;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 32 bpp */
+
+         case 8:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if !defined(PNG_1_0_X)
+            if (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width & ~7;          // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7);   // amount lost
+
+               __asm__ __volatile__ (
+                  "not       %%edx            \n\t" // mask => unmask
+                  "movd      %%edx, %%mm7     \n\t" // load bit pattern
+                  "not       %%edx            \n\t" // unmask => mask for later
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  LOAD_GOT_rbp
+                  "movq   " MASK8_0 ", %%mm0  \n\t" // _mask8_0 -> mm0
+                  RESTORE_rbp
+
+                  "pand      %%mm7, %%mm0     \n\t" // nonzero if keep byte
+                  "pcmpeqb   %%mm6, %%mm0     \n\t" // zeros->1s, v versa
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %3       \n\t" // load source
+// preload        "movl      dstptr, %4       \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t" // len == 0 ?
+                  "je        mainloop8end     \n\t"
+
+                "mainloop8:                   \n\t"
+                  "movq      (%3), %%mm4      \n\t" // *srcptr
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "pandn     (%4), %%mm6      \n\t" // *dstptr
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%4)      \n\t"
+                  "add       $8, %3           \n\t" // inc by 8 bytes processed
+                  "add       $8, %4           \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+                  "ja        mainloop8        \n\t"
+
+                "mainloop8end:                \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end8             \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop8:                 \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip8            \n\t" // if CF = 0
+                  "movb      (%3), %%al       \n\t"
+                  "movb      %%al, (%4)       \n\t"
+
+                "skip8:                       \n\t"
+                  "inc       %3               \n\t"
+                  "inc       %4               \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop8      \n\t"
+
+                "end8:                        \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "0" (diff),        // eax       // input regs
+                    "1" (mask),        // edx
+                    "2" (len),         // ecx
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "3" (srcptr),      // esi/rsi
+                    "4" (dstptr)       // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                  : "%mm0", "%mm4", "%mm6", "%mm7"  // clobber list
+#endif
+               );
+            }
+            else /* not _mmx_supported - use modified C routine */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = len;  /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff /* *BPP1 */ ;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 8 bpp */
+
+         case 1:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_inc, s_start, s_end;
+            int m;
+            int shift;
+            png_uint_32 i;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 7;
+               s_inc = 1;
+            }
+            else
+#endif
+            {
+               s_start = 7;
+               s_end = 0;
+               s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }       /* end 1 bpp */
+
+         case 2:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }       /* end 2 bpp */
+
+         case 4:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }       /* end 4 bpp */
+
+         case 16:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if !defined(PNG_1_0_X)
+            if (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width & ~7;          // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7);   // amount lost
+
+               __asm__ __volatile__ (
+                  "not       %%edx            \n\t" // mask => unmask
+                  "movd      %%edx, %%mm7     \n\t" // load bit pattern
+                  "not       %%edx            \n\t" // unmask => mask for later
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  LOAD_GOT_rbp
+                  "movq   " MASK16_0 ", %%mm0 \n\t" // _mask16_0 -> mm0
+                  "movq   " MASK16_1 ", %%mm1 \n\t" // _mask16_1 -> mm1
+                  RESTORE_rbp
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %3       \n\t" // load source
+// preload        "movl      dstptr, %4       \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        mainloop16end    \n\t"
+
+                "mainloop16:                  \n\t"
+                  "movq      (%3), %%mm4      \n\t"
+                  "pand      %%mm0, %%mm4     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "movq      (%4), %%mm7      \n\t"
+                  "pandn     %%mm7, %%mm6     \n\t"
+                  "por       %%mm6, %%mm4     \n\t"
+                  "movq      %%mm4, (%4)      \n\t"
+
+                  "movq      8(%3), %%mm5     \n\t"
+                  "pand      %%mm1, %%mm5     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "movq      8(%4), %%mm6     \n\t"
+                  "pandn     %%mm6, %%mm7     \n\t"
+                  "por       %%mm7, %%mm5     \n\t"
+                  "movq      %%mm5, 8(%4)     \n\t"
+
+                  "add       $16, %3          \n\t" // inc by 16 bytes processed
+                  "add       $16, %4          \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+                  "ja        mainloop16       \n\t"
+
+                "mainloop16end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end16            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop16:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip16           \n\t" // if CF = 0
+                  "movw      (%3), %%ax       \n\t"
+                  "movw      %%ax, (%4)       \n\t"
+
+                "skip16:                      \n\t"
+                  "add       $2, %3           \n\t"
+                  "add       $2, %4           \n\t"
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop16     \n\t"
+
+                "end16:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "0" (diff),        // eax       // input regs
+                    "1" (mask),        // edx
+                    "2" (len),         // ecx
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "3" (srcptr),      // esi/rsi
+                    "4" (dstptr)       // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                  : "%mm0", "%mm1", "%mm4"          // clobber list
+                  , "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* not _mmx_supported - use modified C routine */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP2 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP2 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP2 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP2 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP2;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 16 bpp */
+
+         case 48:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+#if !defined(PNG_1_0_X)
+            if (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+#else
+            if (_mmx_supported)
+#endif
+            {
+               png_uint_32 len;
+               int diff;
+
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               len  = png_ptr->width & ~7;          // reduce to multiple of 8
+               diff = (int) (png_ptr->width & 7);   // amount lost
+
+               __asm__ __volatile__ (
+                  "not       %%edx            \n\t" // mask => unmask
+                  "movd      %%edx, %%mm7     \n\t" // load bit pattern
+                  "not       %%edx            \n\t" // unmask => mask for later
+                  "psubb     %%mm6, %%mm6     \n\t" // zero mm6
+                  "punpcklbw %%mm7, %%mm7     \n\t"
+                  "punpcklwd %%mm7, %%mm7     \n\t"
+                  "punpckldq %%mm7, %%mm7     \n\t" // fill reg with 8 masks
+
+                  LOAD_GOT_rbp
+                  "movq   " MASK48_0 ", %%mm0 \n\t" // _mask48_0 -> mm0
+                  "movq   " MASK48_1 ", %%mm1 \n\t" // _mask48_1 -> mm1
+                  "movq   " MASK48_2 ", %%mm2 \n\t" // _mask48_2 -> mm2
+                  "movq   " MASK48_3 ", %%mm3 \n\t" // _mask48_3 -> mm3
+                  "movq   " MASK48_4 ", %%mm4 \n\t" // _mask48_4 -> mm4
+                  "movq   " MASK48_5 ", %%mm5 \n\t" // _mask48_5 -> mm5
+                  RESTORE_rbp
+
+                  "pand      %%mm7, %%mm0     \n\t"
+                  "pand      %%mm7, %%mm1     \n\t"
+                  "pand      %%mm7, %%mm2     \n\t"
+                  "pand      %%mm7, %%mm3     \n\t"
+                  "pand      %%mm7, %%mm4     \n\t"
+                  "pand      %%mm7, %%mm5     \n\t"
+
+                  "pcmpeqb   %%mm6, %%mm0     \n\t"
+                  "pcmpeqb   %%mm6, %%mm1     \n\t"
+                  "pcmpeqb   %%mm6, %%mm2     \n\t"
+                  "pcmpeqb   %%mm6, %%mm3     \n\t"
+                  "pcmpeqb   %%mm6, %%mm4     \n\t"
+                  "pcmpeqb   %%mm6, %%mm5     \n\t"
+
+// preload        "movl      len, %%ecx       \n\t" // load length of line
+// preload        "movl      srcptr, %3       \n\t" // load source
+// preload        "movl      dstptr, %4       \n\t" // load dest
+
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        mainloop48end    \n\t"
+
+                "mainloop48:                  \n\t"
+                  "movq      (%3), %%mm7      \n\t"
+                  "pand      %%mm0, %%mm7     \n\t"
+                  "movq      %%mm0, %%mm6     \n\t"
+                  "pandn     (%4), %%mm6      \n\t"
+                  "por       %%mm6, %%mm7     \n\t"
+                  "movq      %%mm7, (%4)      \n\t"
+
+                  "movq      8(%3), %%mm6     \n\t"
+                  "pand      %%mm1, %%mm6     \n\t"
+                  "movq      %%mm1, %%mm7     \n\t"
+                  "pandn     8(%4), %%mm7     \n\t"
+                  "por       %%mm7, %%mm6     \n\t"
+                  "movq      %%mm6, 8(%4)     \n\t"
+
+                  "movq      16(%3), %%mm6    \n\t"
+                  "pand      %%mm2, %%mm6     \n\t"
+                  "movq      %%mm2, %%mm7     \n\t"
+                  "pandn     16(%4), %%mm7    \n\t"
+                  "por       %%mm7, %%mm6     \n\t"
+                  "movq      %%mm6, 16(%4)    \n\t"
+
+                  "movq      24(%3), %%mm7    \n\t"
+                  "pand      %%mm3, %%mm7     \n\t"
+                  "movq      %%mm3, %%mm6     \n\t"
+                  "pandn     24(%4), %%mm6    \n\t"
+                  "por       %%mm6, %%mm7     \n\t"
+                  "movq      %%mm7, 24(%4)    \n\t"
+
+                  "movq      32(%3), %%mm6    \n\t"
+                  "pand      %%mm4, %%mm6     \n\t"
+                  "movq      %%mm4, %%mm7     \n\t"
+                  "pandn     32(%4), %%mm7    \n\t"
+                  "por       %%mm7, %%mm6     \n\t"
+                  "movq      %%mm6, 32(%4)    \n\t"
+
+                  "movq      40(%3), %%mm7    \n\t"
+                  "pand      %%mm5, %%mm7     \n\t"
+                  "movq      %%mm5, %%mm6     \n\t"
+                  "pandn     40(%4), %%mm6    \n\t"
+                  "por       %%mm6, %%mm7     \n\t"
+                  "movq      %%mm7, 40(%4)    \n\t"
+
+                  "add       $48, %3          \n\t" // inc by 48 bytes processed
+                  "add       $48, %4          \n\t"
+                  "subl      $8, %%ecx        \n\t" // dec by 8 pixels processed
+
+                  "ja        mainloop48       \n\t"
+
+                "mainloop48end:               \n\t"
+// preload        "movl      diff, %%ecx      \n\t" // (diff is in eax)
+                  "movl      %%eax, %%ecx     \n\t"
+                  "cmpl      $0, %%ecx        \n\t"
+                  "jz        end48            \n\t"
+// preload        "movl      mask, %%edx      \n\t"
+                  "sall      $24, %%edx       \n\t" // make low byte, high byte
+
+                "secondloop48:                \n\t"
+                  "sall      %%edx            \n\t" // move high bit to CF
+                  "jnc       skip48           \n\t" // if CF = 0
+                  "movl      (%3), %%eax      \n\t"
+                  "movl      %%eax, (%4)      \n\t"
+                  "movw      4(%3), %%ax      \n\t" // GR-P bugfix 20070717
+                  "movw      %%ax, 4(%4)      \n\t" // GR-P bugfix 20070717
+
+                "skip48:                      \n\t"
+                  "add       $6, %3           \n\t" // GR-P bugfix 20070717
+                  "add       $6, %4           \n\t" // GR-P bugfix 20070717
+                  "decl      %%ecx            \n\t"
+                  "jnz       secondloop48     \n\t"
+
+                "end48:                       \n\t"
+                  "EMMS                       \n\t" // DONE
+
+                  : "=a" (dummy_value_a),           // output regs (dummy)
+                    "=d" (dummy_value_d),
+                    "=c" (dummy_value_c),
+                    "=S" (dummy_value_S),
+                    "=D" (dummy_value_D)
+
+                  : "0" (diff),        // eax       // input regs
+                    "1" (mask),        // edx
+                    "2" (len),         // ecx
+// was (unmask)     "b"    RESERVED    // ebx       // Global Offset Table idx
+                    "3" (srcptr),      // esi/rsi
+                    "4" (dstptr)       // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                  : "%mm0", "%mm1", "%mm2", "%mm3"  // clobber list
+                  , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+               );
+            }
+            else /* not _mmx_supported - use modified C routine */
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP6 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP6 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP6 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP6 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP6;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 48 bpp */
+
+         case 64:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            register png_uint_32 i;
+            png_uint_32 initial_val = BPP8 * png_pass_start[png_ptr->pass];
+              /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+            register int stride = BPP8 * png_pass_inc[png_ptr->pass];
+              /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+            register int rep_bytes = BPP8 * png_pass_width[png_ptr->pass];
+              /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+            png_uint_32 len = png_ptr->width &~7;  /* reduce to mult. of 8 */
+            int diff = (int) (png_ptr->width & 7); /* amount lost */
+            register png_uint_32 final_val = BPP8 * len;   /* GRR bugfix */
+
+            srcptr = png_ptr->row_buf + 1 + initial_val;
+            dstptr = row + initial_val;
+
+            for (i = initial_val; i < final_val; i += stride)
+            {
+               png_memcpy(dstptr, srcptr, rep_bytes);
+               srcptr += stride;
+               dstptr += stride;
+            }
+            if (diff)  /* number of leftover pixels:  3 for pngtest */
+            {
+               final_val += diff*BPP8;
+               for (; i < final_val; i += stride)
+               {
+                  if (rep_bytes > (int)(final_val-i))
+                     rep_bytes = (int)(final_val-i);
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+            }
+
+            break;
+         }       /* end 64 bpp */
+
+         default: /* png_ptr->row_info.pixel_depth != 1,2,4,8,16,24,32,48,64 */
+         {
+            // ERROR:  SHOULD NEVER BE REACHED
+#if defined(PNG_DEBUG)
+            png_debug(1, "Internal libpng logic error (GCC "
+              "png_combine_row() pixel_depth)\n");
+#endif
+            break;
+         }
+      } /* end switch (png_ptr->row_info.pixel_depth) */
+
+   } /* end if (non-trivial mask) */
+
+} /* end png_combine_row() */
+
+#endif /* PNG_HAVE_MMX_COMBINE_ROW */
+
+
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                 P N G _ D O _ R E A D _ I N T E R L A C E                 */
+/*                                                                           */
+/*===========================================================================*/
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+#if defined(PNG_HAVE_MMX_READ_INTERLACE)
+
+/* png_do_read_interlace() is called after any 16-bit to 8-bit conversion
+ * has taken place.  [GRR: what other steps come before and/or after?]
+ */
+
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   png_uint_32 transformations = png_ptr->transformations;
+#endif
+
+   png_debug(1, "in png_do_read_interlace (pnggccrd.c)\n");
+
+   if (_mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            dp = row + (png_size_t)((final_width - 1) >> 3);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)((row_info->width + 7) & 7);
+               dshift = (int)((final_width + 7) & 7);
+               s_start = 7;
+               s_end = 0;
+               s_inc = -1;
+            }
+            else
+#endif
+            {
+               sshift = 7 - (int)((row_info->width + 7) & 7);
+               dshift = 7 - (int)((final_width + 7) & 7);
+               s_start = 0;
+               s_end = 7;
+               s_inc = 1;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x1);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            dp = row + (png_size_t)((final_width - 1) >> 2);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 3) & 3) << 1);
+               dshift = (png_size_t)(((final_width + 3) & 3) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
+               dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x3);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            dp = row + (png_size_t)((final_width - 1) >> 1);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 1) & 1) << 2);
+               dshift = (png_size_t)(((final_width + 1) & 1) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
+               dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0xf);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+       /*====================================================================*/
+
+         default: /* 8-bit or larger (this is where the routine is modified) */
+         {
+            png_bytep sptr, dp;
+            png_uint_32 i;
+            png_size_t pixel_bytes;
+            int width = (int)row_info->width;
+
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            /* point sptr at the last pixel in the pre-expanded row: */
+            sptr = row + (width - 1) * pixel_bytes;
+
+            /* point dp at the last pixel position in the expanded row: */
+            dp = row + (final_width - 1) * pixel_bytes;
+
+            /* New code by Nirav Chhatrapati - Intel Corporation */
+
+#if !defined(PNG_1_0_X)
+            if (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)
+#else
+            if (_mmx_supported)
+#endif
+            {
+               int dummy_value_c;        // fix 'forbidden register spilled'
+               png_bytep dummy_value_S;
+               png_bytep dummy_value_D;
+               png_bytep dummy_value_a;
+               png_bytep dummy_value_d;
+
+               //--------------------------------------------------------------
+               if (pixel_bytes == BPP3)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) - 8;   // GRR:  huh?
+                     if (width_mmx < 0)
+                         width_mmx = 0;
+                     width -= width_mmx;        // 8 or 9 pix, 24 or 27 bytes
+                     if (width_mmx)
+                     {
+                        // png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+                        // sptr points at last pixel in pre-expanded row
+                        // dp points at last pixel position in expanded row
+                        __asm__ __volatile__ (
+                           "sub  $3, %1             \n\t"
+                           "sub  $9, %2             \n\t"
+                                        // (png_pass_inc[pass] + 1)*pixel_bytes
+
+                        ".loop3_pass4:              \n\t"
+                           "movq (%1), %%mm0        \n\t" // x x 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // x x 5 4 3 2 1 0
+                           "movq %%mm0, %%mm2       \n\t" // x x 5 4 3 2 1 0
+                           "psllq $24, %%mm0        \n\t" // 4 3 2 1 0 z z z
+                           "pand (%3), %%mm1        \n\t" // z z z z z 2 1 0
+                           "psrlq $24, %%mm2        \n\t" // z z z x x 5 4 3
+                           "por %%mm1, %%mm0        \n\t" // 4 3 2 1 0 2 1 0
+                           "movq %%mm2, %%mm3       \n\t" // z z z x x 5 4 3
+                           "psllq $8, %%mm2         \n\t" // z z x x 5 4 3 z
+                           "movq %%mm0, (%2)        \n\t"
+                           "psrlq $16, %%mm3        \n\t" // z z z z z x x 5
+                           "pand (%4), %%mm3        \n\t" // z z z z z z z 5
+                           "por %%mm3, %%mm2        \n\t" // z z x x 5 4 3 5
+                           "sub  $6, %1             \n\t"
+                           "movd %%mm2, 8(%2)       \n\t"
+                           "sub  $12, %2            \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop3_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D),
+                             "=a" (dummy_value_a),
+                             "=d" (dummy_value_d)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp),            // edi/rdi
+#if defined(PNG_x86_64_USE_GOTPCREL)     // formerly _const4 and _const6:
+                             "3" (&_c64._amask5_3_0), // (0x0000000000FFFFFFLL)
+                             "4" (&_c64._amask7_1_0)  // (0x00000000000000FFLL)
+#else
+                             "3" (&_amask5_3_0),  // eax (0x0000000000FFFFFFLL)
+                             "4" (&_amask7_1_0)   // edx (0x00000000000000FFLL)
+#endif
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+                           , "%mm2", "%mm3"
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx*BPP3;
+                     dp -= width_mmx*2*BPP3;
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+
+                        png_memcpy(v, sptr, BPP3);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           png_memcpy(dp, v, BPP3);
+                           dp -= BPP3;
+                        }
+                        sptr -= BPP3;
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     __asm__ __volatile__ (
+                        "sub  $9, %2             \n\t"
+                                     // (png_pass_inc[pass] - 1)*pixel_bytes
+
+                     ".loop3_pass2:              \n\t"
+                        "movd (%1), %%mm0        \n\t" // x x x x x 2 1 0
+                        "pand (%3), %%mm0        \n\t" // z z z z z 2 1 0
+                        "movq %%mm0, %%mm1       \n\t" // z z z z z 2 1 0
+                        "psllq $16, %%mm0        \n\t" // z z z 2 1 0 z z
+                        "movq %%mm0, %%mm2       \n\t" // z z z 2 1 0 z z
+                        "psllq $24, %%mm0        \n\t" // 2 1 0 z z z z z
+                        "psrlq $8, %%mm1         \n\t" // z z z z z z 2 1
+                        "por %%mm2, %%mm0        \n\t" // 2 1 0 2 1 0 z z
+                        "por %%mm1, %%mm0        \n\t" // 2 1 0 2 1 0 2 1
+                        "movq %%mm0, 4(%2)       \n\t"
+                        "psrlq $16, %%mm0        \n\t" // z z 2 1 0 2 1 0
+                        "sub  $3, %1             \n\t"
+                        "movd %%mm0, (%2)        \n\t"
+                        "sub  $12, %2            \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop3_pass2        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D),
+                          "=a" (dummy_value_a)
+
+                        : "0" (width),         // ecx  // input regs
+                          "1" (sptr),          // esi/rsi
+                          "2" (dp),            // edi/rdi
+#if defined(PNG_x86_64_USE_GOTPCREL)           // formerly _const4:
+                          "3" (&_c64._amask5_3_0)  // (0x0000000000FFFFFFLL)
+#else
+                          "3" (&_amask5_3_0)   // eax (0x0000000000FFFFFFLL)
+#endif
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+                        : "%mm0", "%mm1", "%mm2"       // clobber list
+#endif
+                     );
+                  }
+                  else if (width)  // && ((pass == 0) || (pass == 1))
+                  {
+                     __asm__ __volatile__ (
+                        "sub  $21, %2            \n\t"
+                                     // (png_pass_inc[pass] - 1)*pixel_bytes
+
+                     ".loop3_pass0:              \n\t"
+                        "movd (%1), %%mm0        \n\t" // x x x x x 2 1 0
+                        "pand (%3), %%mm0        \n\t" // z z z z z 2 1 0
+                        "movq %%mm0, %%mm1       \n\t" // z z z z z 2 1 0
+                        "psllq $16, %%mm0        \n\t" // z z z 2 1 0 z z
+                        "movq %%mm0, %%mm2       \n\t" // z z z 2 1 0 z z
+                        "psllq $24, %%mm0        \n\t" // 2 1 0 z z z z z
+                        "psrlq $8, %%mm1         \n\t" // z z z z z z 2 1
+                        "por %%mm2, %%mm0        \n\t" // 2 1 0 2 1 0 z z
+                        "por %%mm1, %%mm0        \n\t" // 2 1 0 2 1 0 2 1
+                        "movq %%mm0, %%mm3       \n\t" // 2 1 0 2 1 0 2 1
+                        "psllq $16, %%mm0        \n\t" // 0 2 1 0 2 1 z z
+                        "movq %%mm3, %%mm4       \n\t" // 2 1 0 2 1 0 2 1
+                        "punpckhdq %%mm0, %%mm3  \n\t" // 0 2 1 0 2 1 0 2
+                        "movq %%mm4, 16(%2)      \n\t"
+                        "psrlq $32, %%mm0        \n\t" // z z z z 0 2 1 0
+                        "movq %%mm3, 8(%2)       \n\t"
+                        "punpckldq %%mm4, %%mm0  \n\t" // 1 0 2 1 0 2 1 0
+                        "sub  $3, %1             \n\t"
+                        "movq %%mm0, (%2)        \n\t"
+                        "sub  $24, %2            \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop3_pass0        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D),
+                          "=a" (dummy_value_a)
+
+                        : "0" (width),         // ecx  // input regs
+                          "1" (sptr),          // esi/rsi
+                          "2" (dp),            // edi/rdi
+#if defined(PNG_x86_64_USE_GOTPCREL)           // formerly _const4:
+                          "3" (&_c64._amask5_3_0)  // (0x0000000000FFFFFFLL)
+#else
+                          "3" (&_amask5_3_0)   // eax (0x0000000000FFFFFFLL)
+#endif
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                        : "%mm0", "%mm1", "%mm2"       // clobber list
+                        , "%mm3", "%mm4"
+#endif
+                     );
+                  }
+               } /* end of pixel_bytes == 3 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == BPP4)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;        // 0,1 pixels => 0,4 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $4, %1             \n\t"
+                           "sub  $12, %2            \n\t"
+
+                        ".loop4_pass4:              \n\t"
+                           "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 3 2 1 0 3 2 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 7 6 5 4 7 6 5 4
+                           "movq %%mm0, (%2)        \n\t"
+                           "sub  $8, %1             \n\t"
+                           "movq %%mm1, 8(%2)       \n\t"
+                           "sub  $16, %2            \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop4_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*BPP4 - BPP4); // sign fixed
+                     dp -= (width_mmx*2*BPP4 - BPP4); // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= BPP4;
+                        png_memcpy(v, sptr, BPP4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= BPP4;
+                           png_memcpy(dp, v, BPP4);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;        // 0,1 pixels => 0,4 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $4, %1             \n\t"
+                           "sub  $28, %2            \n\t"
+
+                        ".loop4_pass2:              \n\t"
+                           "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 3 2 1 0 3 2 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 7 6 5 4 7 6 5 4
+                           "movq %%mm0, (%2)        \n\t"
+                           "movq %%mm0, 8(%2)       \n\t"
+                           "movq %%mm1, 16(%2)      \n\t"
+                           "movq %%mm1, 24(%2)      \n\t"
+                           "sub  $8, %1             \n\t"
+                           "sub  $32, %2            \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop4_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*4 - 4); // sign fixed
+                     dp -= (width_mmx*16 - 4);  // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (width)  // && ((pass == 0) || (pass == 1))
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;        // 0,1 pixels => 0,4 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $4, %1             \n\t"
+                           "sub  $60, %2            \n\t"
+
+                        ".loop4_pass0:              \n\t"
+                           "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 3 2 1 0 3 2 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 7 6 5 4 7 6 5 4
+                           "movq %%mm0, (%2)        \n\t"
+                           "movq %%mm0, 8(%2)       \n\t"
+                           "movq %%mm0, 16(%2)      \n\t"
+                           "movq %%mm0, 24(%2)      \n\t"
+                           "movq %%mm1, 32(%2)      \n\t"
+                           "movq %%mm1, 40(%2)      \n\t"
+                           "movq %%mm1, 48(%2)      \n\t"
+                           "sub  $8, %1             \n\t"
+                           "movq %%mm1, 56(%2)      \n\t"
+                           "sub  $64, %2            \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop4_pass0        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*4 - 4); // sign fixed
+                     dp -= (width_mmx*32 - 4);  // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+               } /* end of pixel_bytes == 4 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == 1)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 3) << 3);
+                     width -= width_mmx;        // 0-3 pixels => 0-3 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $7, %1             \n\t"
+                           "sub  $15, %2            \n\t"
+
+                        ".loop1_pass4:              \n\t"
+                           "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 7 6 5 4 3 2 1 0
+                           "punpcklbw %%mm0, %%mm0  \n\t" // 3 3 2 2 1 1 0 0
+                           "punpckhbw %%mm1, %%mm1  \n\t" // 7 7 6 6 5 5 4 4
+                           "movq %%mm1, 8(%2)       \n\t"
+                           "sub  $8, %1             \n\t"
+                           "movq %%mm0, (%2)        \n\t"
+                           "sub  $16, %2            \n\t"
+                           "subl $8, %%ecx          \n\t"
+                           "jnz .loop1_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*2;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        --sptr;
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;        // 0-3 pixels => 0-3 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $3, %1             \n\t"
+                           "sub  $15, %2            \n\t"
+
+                        ".loop1_pass2:              \n\t"
+                           "movd (%1), %%mm0        \n\t" // x x x x 3 2 1 0
+                           "punpcklbw %%mm0, %%mm0  \n\t" // 3 3 2 2 1 1 0 0
+                           "movq %%mm0, %%mm1       \n\t" // 3 3 2 2 1 1 0 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 1 1 1 1 0 0 0 0
+                           "punpckhwd %%mm1, %%mm1  \n\t" // 3 3 3 3 2 2 2 2
+                           "movq %%mm0, (%2)        \n\t"
+                           "sub  $4, %1             \n\t"
+                           "movq %%mm1, 8(%2)       \n\t"
+                           "sub  $16, %2            \n\t"
+                           "subl $4, %%ecx          \n\t"
+                           "jnz .loop1_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*4;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        --sptr;
+                     }
+                  }
+                  else if (width)  // && ((pass == 0) || (pass == 1))
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;        // 0-3 pixels => 0-3 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $3, %1             \n\t"
+                           "sub  $31, %2            \n\t"
+
+                        ".loop1_pass0:              \n\t"
+                           "movd (%1), %%mm0        \n\t" // x x x x 3 2 1 0
+                           "movq %%mm0, %%mm1       \n\t" // x x x x 3 2 1 0
+                           "punpcklbw %%mm0, %%mm0  \n\t" // 3 3 2 2 1 1 0 0
+                           "movq %%mm0, %%mm2       \n\t" // 3 3 2 2 1 1 0 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 1 1 1 1 0 0 0 0
+                           "movq %%mm0, %%mm3       \n\t" // 1 1 1 1 0 0 0 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 0 0 0 0 0 0 0 0
+                           "punpckhdq %%mm3, %%mm3  \n\t" // 1 1 1 1 1 1 1 1
+                           "movq %%mm0, (%2)        \n\t"
+                           "punpckhwd %%mm2, %%mm2  \n\t" // 3 3 3 3 2 2 2 2
+                           "movq %%mm3, 8(%2)       \n\t"
+                           "movq %%mm2, %%mm4       \n\t" // 3 3 3 3 2 2 2 2
+                           "punpckldq %%mm2, %%mm2  \n\t" // 2 2 2 2 2 2 2 2
+                           "punpckhdq %%mm4, %%mm4  \n\t" // 3 3 3 3 3 3 3 3
+                           "movq %%mm2, 16(%2)      \n\t"
+                           "sub  $4, %1             \n\t"
+                           "movq %%mm4, 24(%2)      \n\t"
+                           "sub  $32, %2            \n\t"
+                           "subl $4, %%ecx          \n\t"
+                           "jnz .loop1_pass0        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1", "%mm2"       // clobber list
+                           , "%mm3", "%mm4"
+#endif
+                        );
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*8;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                       /* I simplified this part in version 1.0.4e
+                        * here and in several other instances where
+                        * pixel_bytes == 1  -- GR-P
+                        *
+                        * Original code:
+                        *
+                        * png_byte v[8];
+                        * png_memcpy(v, sptr, pixel_bytes);
+                        * for (j = 0; j < png_pass_inc[pass]; j++)
+                        * {
+                        *    png_memcpy(dp, v, pixel_bytes);
+                        *    dp -= pixel_bytes;
+                        * }
+                        * sptr -= pixel_bytes;
+                        *
+                        * Replacement code is in the next three lines:
+                        */
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        --sptr;
+                     }
+                  }
+               } /* end of pixel_bytes == 1 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == BPP2)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;        // 0,1 pixels => 0,2 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $2, %1             \n\t"
+                           "sub  $6, %2             \n\t"
+
+                        ".loop2_pass4:              \n\t"
+                           "movd (%1), %%mm0        \n\t" // x x x x 3 2 1 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 3 2 3 2 1 0 1 0
+                           "sub  $4, %1             \n\t"
+                           "movq %%mm0, (%2)        \n\t"
+                           "sub  $8, %2             \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop2_pass4        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0"                       // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*BPP2 - BPP2); // sign fixed
+                     dp -= (width_mmx*2*BPP2 - BPP2); // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= BPP2;
+                        png_memcpy(v, sptr, BPP2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= BPP2;
+                           png_memcpy(dp, v, BPP2);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;        // 0,1 pixels => 0,2 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $2, %1             \n\t"
+                           "sub  $14, %2            \n\t"
+
+                        ".loop2_pass2:              \n\t"
+                           "movd (%1), %%mm0        \n\t" // x x x x 3 2 1 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 3 2 3 2 1 0 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 3 2 3 2 1 0 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 1 0 1 0 1 0 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 3 2 3 2 3 2 3 2
+                           "movq %%mm0, (%2)        \n\t"
+                           "sub  $4, %1             \n\t"
+                           "movq %%mm1, 8(%2)       \n\t"
+                           "sub  $16, %2            \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop2_pass2        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*2 - 2); // sign fixed
+                     dp -= (width_mmx*8 - 2);   // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (width)  // && ((pass == 0) || (pass == 1))
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;        // 0,1 pixels => 0,2 bytes
+                     if (width_mmx)
+                     {
+                        __asm__ __volatile__ (
+                           "sub  $2, %1             \n\t"
+                           "sub  $30, %2            \n\t"
+
+                        ".loop2_pass0:              \n\t"
+                           "movd (%1), %%mm0        \n\t" // x x x x 3 2 1 0
+                           "punpcklwd %%mm0, %%mm0  \n\t" // 3 2 3 2 1 0 1 0
+                           "movq %%mm0, %%mm1       \n\t" // 3 2 3 2 1 0 1 0
+                           "punpckldq %%mm0, %%mm0  \n\t" // 1 0 1 0 1 0 1 0
+                           "punpckhdq %%mm1, %%mm1  \n\t" // 3 2 3 2 3 2 3 2
+                           "movq %%mm0, (%2)        \n\t"
+                           "movq %%mm0, 8(%2)       \n\t"
+                           "movq %%mm1, 16(%2)      \n\t"
+                           "sub  $4, %1             \n\t"
+                           "movq %%mm1, 24(%2)      \n\t"
+                           "sub  $32, %2            \n\t"
+                           "subl $2, %%ecx          \n\t"
+                           "jnz .loop2_pass0        \n\t"
+                           "EMMS                    \n\t" // DONE
+
+                           : "=c" (dummy_value_c),        // output regs (dummy)
+                             "=S" (dummy_value_S),
+                             "=D" (dummy_value_D)
+
+                           : "0" (width_mmx),     // ecx  // input regs
+                             "1" (sptr),          // esi/rsi
+                             "2" (dp)             // edi/rdi
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+                           : "%mm0", "%mm1"               // clobber list
+#endif
+                        );
+                     }
+
+                     sptr -= (width_mmx*2 - 2); // sign fixed
+                     dp -= (width_mmx*16 - 2);  // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+               } /* end of pixel_bytes == 2 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == BPP8)
+               {
+// GRR TEST:  should work, but needs testing (special 64-bit version of rpng2?)
+                  // GRR NOTE:  no need to combine passes here!
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     // source is 8-byte RRGGBBAA
+                     // dest is 16-byte RRGGBBAA RRGGBBAA
+                     __asm__ __volatile__ (
+                        "sub  $8, %2             \n\t" // start of last block
+
+                     ".loop8_pass4:              \n\t"
+                        "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                        "movq %%mm0, (%2)        \n\t"
+                        "sub  $8, %1             \n\t"
+                        "movq %%mm0, 8(%2)       \n\t"
+                        "sub  $16, %2            \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop8_pass4        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D)
+
+                        : "0" (width),         // ecx  // input regs
+                          "1" (sptr),          // esi/rsi
+                          "2" (dp)             // edi/rdi
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+                        : "%mm0"                       // clobber list
+#endif
+                     );
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     // source is 8-byte RRGGBBAA
+                     // dest is 32-byte RRGGBBAA RRGGBBAA RRGGBBAA RRGGBBAA
+                     // (recall that expansion is _in place_:  sptr and dp
+                     //  both point at locations within same row buffer)
+                     __asm__ __volatile__ (
+                        "sub  $24, %2            \n\t" // start of last block
+
+                     ".loop8_pass2:              \n\t"
+                        "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                        "movq %%mm0, (%2)        \n\t"
+                        "movq %%mm0, 8(%2)       \n\t"
+                        "movq %%mm0, 16(%2)      \n\t"
+                        "sub  $8, %1             \n\t"
+                        "movq %%mm0, 24(%2)      \n\t"
+                        "sub  $32, %2            \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop8_pass2        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D)
+
+                        : "0" (width),         // ecx  // input regs
+                          "1" (sptr),          // esi/rsi
+                          "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                        : "%mm0"                       // clobber list
+#endif
+                     );
+                  }
+                  else if (width)  // && ((pass == 0) || (pass == 1))
+                  {
+                     // source is 8-byte RRGGBBAA
+                     // dest is 64-byte RRGGBBAA RRGGBBAA RRGGBBAA RRGGBBAA ...
+                     __asm__ __volatile__ (
+                        "sub  $56, %2            \n\t" // start of last block
+
+                     ".loop8_pass0:              \n\t"
+                        "movq (%1), %%mm0        \n\t" // 7 6 5 4 3 2 1 0
+                        "movq %%mm0, (%2)        \n\t"
+                        "movq %%mm0, 8(%2)       \n\t"
+                        "movq %%mm0, 16(%2)      \n\t"
+                        "movq %%mm0, 24(%2)      \n\t"
+                        "movq %%mm0, 32(%2)      \n\t"
+                        "movq %%mm0, 40(%2)      \n\t"
+                        "movq %%mm0, 48(%2)      \n\t"
+                        "sub  $8, %1             \n\t"
+                        "movq %%mm0, 56(%2)      \n\t"
+                        "sub  $64, %2            \n\t"
+                        "decl %%ecx              \n\t"
+                        "jnz .loop8_pass0        \n\t"
+                        "EMMS                    \n\t" // DONE
+
+                        : "=c" (dummy_value_c),        // output regs (dummy)
+                          "=S" (dummy_value_S),
+                          "=D" (dummy_value_D)
+
+                        : "0" (width),         // ecx  // input regs
+                          "1" (sptr),          // esi/rsi
+                          "2" (dp)             // edi/rdi
+
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+                        : "%mm0"                       // clobber list
+#endif
+                     );
+                  }
+               } /* end of pixel_bytes == 8 */
+
+               //--------------------------------------------------------------
+               else if (pixel_bytes == BPP6)   // why no MMX for this case?
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, BPP6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, BPP6);
+                        dp -= BPP6;
+                     }
+                     sptr -= BPP6;
+                  }
+               } /* end of pixel_bytes == 6 */
+
+               //--------------------------------------------------------------
+               else
+               {
+                  // ERROR:  SHOULD NEVER BE REACHED
+#if defined(PNG_DEBUG)
+                  png_debug(1, "Internal libpng logic error (GCC "
+                    "png_do_read_interlace() _mmx_supported)\n");
+#endif
+               }
+
+            } // end of _mmx_supported ========================================
+
+            else /* MMX not supported:  use modified C code - takes advantage
+                  *   of inlining of png_memcpy for a constant */
+            {
+               if (pixel_bytes == BPP3)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, BPP3);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, BPP3);
+                        dp -= BPP3;
+                     }
+                     sptr -= BPP3;
+                  }
+               }
+               else if (pixel_bytes == BPP4)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, BPP4);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+#if defined(PNG_DEBUG) && defined(PNG_1_0_X)  // row_buf_size gone in 1.2.x
+                        if (dp < row || dp+3 > row+png_ptr->row_buf_size)
+                        {
+                           printf("dp out of bounds: row=%10p, dp=%10p, "
+                             "rp=%10p\n", row, dp, row+png_ptr->row_buf_size);
+                           printf("row_buf_size=%lu\n", png_ptr->row_buf_size);
+                        }
+#endif
+                        png_memcpy(dp, v, BPP4);
+                        dp -= BPP4;
+                     }
+                     sptr -= BPP4;
+                  }
+               }
+               else if (pixel_bytes == 1)
+               {
+                  for (i = width; i; i--)
+                  {
+                     int j;
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        *dp-- = *sptr;
+                     }
+                     --sptr;
+                  }
+               }
+               else if (pixel_bytes == BPP2)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, BPP2);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, BPP2);
+                        dp -= BPP2;
+                     }
+                     sptr -= BPP2;
+                  }
+               }
+               else if (pixel_bytes == BPP6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, BPP6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, BPP6);
+                        dp -= BPP6;
+                     }
+                     sptr -= BPP6;
+                  }
+               }
+               else if (pixel_bytes == BPP8)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, BPP8);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, BPP8);
+                        dp -= BPP8;
+                     }
+                     sptr -= BPP8;
+                  }
+               }
+               else
+               {
+                  // ERROR:  SHOULD NEVER BE REACHED
+#if defined(PNG_DEBUG)
+                  png_debug(1, "Internal libpng logic error (GCC "
+                    "png_do_read_interlace() !_mmx_supported)\n");
+#endif
+               }
+
+            } /* end if (MMX not supported) */
+            break;
+         } /* end default (8-bit or larger) */
+      } /* end switch (row_info->pixel_depth) */
+
+      row_info->width = final_width;
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+
+} /* end png_do_read_interlace() */
+
+#endif /* PNG_HAVE_MMX_READ_INTERLACE */
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+
+
+#if defined(PNG_HAVE_MMX_READ_FILTER_ROW)
+#if defined(PNG_MMX_READ_FILTER_AVG_SUPPORTED)
+
+//===========================================================================//
+//                                                                           //
+//           P N G _ R E A D _ F I L T E R _ R O W _ M M X _ A V G           //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Average filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row,
+                            png_bytep prev_row)
+{
+   unsigned FullLength, MMXLength;  // png_uint_32 is actually 64-bit on x86-64
+   int bpp;
+   int dummy_value_a;
+   int dummy_value_c;   // fix 'forbidden register 2 (cx) was spilled' error
+   int dummy_value_d;
+   png_bytep dummy_value_S;
+   png_bytep dummy_value_D;
+   int diff; //     __attribute__((used));
+
+   bpp = (row_info->pixel_depth + 7) >> 3;  // calc number of bytes per pixel
+   FullLength = row_info->rowbytes;         // number of bytes to filter
+
+   __asm__ __volatile__ (
+   "avg_top:                       \n\t"
+      SAVE_GOT_ebx
+      SAVE_r15
+      SAVE_ebp
+      // initialize address pointers and offset
+//pre "movl row, %5                \n\t" // edi/rdi:  ptr to Avg(x)
+      "xorl %%ebx, %%ebx           \n\t" // ebx:  x
+//pre "movl prev_row, %4           \n\t" // esi/rsi:  ptr to Prior(x)
+      "mov  %5, " PDX "            \n\t" // copy of row ptr...
+//pre "subl bpp, " PDX "           \n\t" // (bpp is preloaded into ecx)
+      "sub  " PCX "," PDX "        \n\t" // edx/rdx:  ptr to Raw(x-bpp)
+//pre "movl FullLength, %%eax      \n\t" // bring in via eax...
+      SAVE_FullLength                    // ...but store for later use
+      "xorl %%eax, %%eax           \n\t"
+
+      // Compute the Raw value for the first bpp bytes
+      //    Raw(x) = Avg(x) + (Prior(x)/2)
+   "avg_rlp:                       \n\t"
+      "movb (%4," PBX ",), %%al    \n\t" // load al with Prior(x)
+      "incl %%ebx                  \n\t"
+      "shrb %%al                   \n\t" // divide by 2
+      "addb -1(%5," PBX ",), %%al  \n\t" // add Avg(x); -1 to offset inc ebx
+//pre "cmpl bpp, %%ebx             \n\t" // (bpp is preloaded into ecx)
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movb %%al, -1(%5," PBX ",)  \n\t" // write Raw(x); -1 to offset inc ebx
+      "jb avg_rlp                  \n\t" // mov does not affect flags
+
+      // get # of bytes to alignment (32-bit mask _would_ be good enough
+      // [computing delta], but 32-bit ops are zero-extended on 64-bit, argh)
+      // (if swapped edx and ebp, could do 8-bit or 16-bit mask...FIXME?)
+      "mov  %5, " PBP "            \n\t" // take start of row
+      "add  " PBX "," PBP "        \n\t" // add bpp
+      "add  $0xf, " PBP "          \n\t" // add 7+8 to incr past alignment bdry
+//    "andl $0xfffffff8, %%ebp     \n\t" // mask to alignment boundary (32-bit!)
+      CLEAR_BOTTOM_3_BITS  PBP    "\n\t" // mask to alignment boundary
+      "sub  %5, " PBP "            \n\t" // subtract row ptr again => ebp =
+      "jz avg_go                   \n\t" //  target value of ebx at alignment
+
+      "xorl %%ecx, %%ecx           \n\t"
+
+      // fix alignment
+      // Compute the Raw value for the bytes up to the alignment boundary
+      //    Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+   "avg_lp1:                       \n\t"
+      "xorl %%eax, %%eax           \n\t"
+      "movb (%4," PBX ",), %%cl    \n\t" // load cl with Prior(x)
+      "movb (" PDX "," PBX ",), %%al \n\t" // load al with Raw(x-bpp)
+      "addw %%cx, %%ax             \n\t"
+      "incl %%ebx                  \n\t"
+      "shrw %%ax                   \n\t" // divide by 2
+      "addb -1(%5," PBX ",), %%al  \n\t" // add Avg(x); -1 to offset inc ebx
+      "cmpl %%ebp, %%ebx           \n\t" // check if at alignment boundary
+      "movb %%al, -1(%5," PBX ",)  \n\t" // write Raw(x); -1 to offset inc ebx
+      "jb avg_lp1                  \n\t" // repeat until at alignment boundary
+
+   "avg_go:                        \n\t"
+      RESTORE_FullLength "%%eax    \n\t" // FullLength -> eax
+      "movl %%eax, %%ecx           \n\t" // copy -> ecx
+      "subl %%ebx, %%eax           \n\t" // subtract alignment fix
+      "andl $0x00000007, %%eax     \n\t" // calc bytes over mult of 8
+      "subl %%eax, %%ecx           \n\t" // sub over-bytes from original length
+//out "movl %%ecx, MMXLength       \n\t"
+      "movl %%ebp, %%eax           \n\t" // ebp = diff, but no reg constraint(?)
+      RESTORE_ebp                        //  (could swap ebp and edx functions)
+      RESTORE_r15
+      RESTORE_GOT_ebx
+
+// "There is no way for you to specify that an input operand is modified
+// without also specifying it as an output operand."  [makes sense]
+
+// "Unless an output operand has the `&' constraint modifier, GCC may
+// allocate it in the same register as an unrelated input operand, on the
+// assumption the inputs are consumed before the outputs are produced."
+// [trying to _force_ this]
+
+// "`='   Means that this operand is write-only for this instruction:
+//        the previous value is discarded and replaced by output data."
+//        [operand == variable name, presumably]
+
+      // output regs
+      // these are operands 0-1 (originally 0-3):
+      : "=c" (MMXLength),      // %0 -> %0
+        "=a" (diff)            // %3 -> %1
+//      "=S" (dummy_value_S),  // %1 -> GONE
+//      "=D" (dummy_value_D),  // %2 -> GONE
+
+      // input regs
+      // these are operands 2-5 (originally 4-7); two of their constraints say
+      // they must go in same places as operands 0-1 (originally 0-3) above:
+      : "0" (bpp),         // %4 -> %2 ecx
+        "1" (FullLength),  // %7 -> %3 eax
+        "S" (prev_row),    // %5 -> %4 esi/rsi
+        "D" (row)          // %6 -> %5 edi/rdi
+
+      : "%edx"                           // clobber list
+        _CLOBBER_r15
+        _CLOBBER_ebp
+        _CLOBBER_GOT_ebx
+   );
+
+   // now do the math for the rest of the row
+   switch (bpp)
+   {
+      case 3:
+      {
+//       _ShiftBpp = 24;    // == 3 * 8
+//       _ShiftRem = 40;    // == 64 - 24
+
+         __asm__ __volatile__ (
+            // re-init address pointers and offset
+            LOAD_GOT_rbp
+            "movq " AMASK5_3_0 ", %%mm7    \n\t" // _amask5_3_0 -> mm7
+// preload  "movl  diff, %%ecx             \n\t" // ecx:  x = offset to
+                                                 //  alignment boundary
+            "movq " LB_CARRY_MASK ", %%mm5 \n\t" // [interleave for parallel.?]
+// preload  "movl  row, %1                 \n\t" // edi:  Avg(x)
+            "movq " HB_CLEAR_MASK ", %%mm4 \n\t" // _HBClearMask -> mm4
+// preload  "movl  prev_row, %0            \n\t" // esi:  Prior(x)
+            RESTORE_rbp
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq  -8(%1," PCX ",), %%mm2 \n\t"// load previous aligned 8 bytes
+                                               // (correct pos. in loop below)
+         "avg_3lp:                        \n\t"
+            "movq  (%1," PCX ",), %%mm0   \n\t" // load mm0 with Avg(x)
+            "movq  %%mm5, %%mm3           \n\t"
+            "psrlq $40, %%mm2             \n\t" // correct position Raw(x-bpp)
+                                                // data
+            "movq  (%0," PCX ",), %%mm1   \n\t" // load mm1 with Prior(x)
+            "movq  %%mm7, %%mm6           \n\t"
+            "pand  %%mm1, %%mm3           \n\t" // get lsb for each prevrow byte
+            "psrlq $1, %%mm1              \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm0           \n\t" // add (Prev_row/2) to Avg for
+                                                // each byte
+            // add 1st active group (Raw(x-bpp)/2) to average with LBCarry
+            "movq  %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                                // LBCarrys
+            "pand  %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                                // where both lsb's were == 1
+                                                // (valid only for active group)
+            "psrlq $1, %%mm2              \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm2           \n\t" // add LBCarrys to Raw(x-bpp)/2
+                                                // for each byte
+            "pand  %%mm6, %%mm2           \n\t" // leave only Active Group 1
+                                                // bytes to add to Avg
+            "paddb %%mm2, %%mm0           \n\t" // add (Raw/2) + LBCarrys to
+                                                // Avg for each Active byte
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq $24, %%mm6             \n\t" // shift the mm6 mask to cover
+                                                // bytes 3-5
+            "movq  %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $24, %%mm2             \n\t" // shift data to pos. correctly
+            "movq  %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                                // LBCarrys
+            "pand  %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                                // where both lsb's were == 1
+                                                // (valid only for active group)
+            "psrlq $1, %%mm2              \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm2           \n\t" // add LBCarrys to Raw(x-bpp)/2
+                                                // for each byte
+            "pand  %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                                // bytes to add to Avg
+            "paddb %%mm2, %%mm0           \n\t" // add (Raw/2) + LBCarrys to
+                                                // Avg for each Active byte
+
+            // add 3rd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq $24, %%mm6             \n\t" // shift mm6 mask to cover last
+                                                // two bytes
+            "movq  %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $24, %%mm2             \n\t" // shift data to pos. correctly
+                              // Data need be shifted only once here to
+                              // get the correct x-bpp offset.
+            "movq  %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                                // LBCarrys
+            "pand  %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                                // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2              \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2           \n\t" // clear invalid bit 7 of each
+                                                // byte
+            "paddb %%mm1, %%mm2           \n\t" // add LBCarrys to Raw(x-bpp)/2
+                                                // for each byte
+            "pand  %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                                // bytes to add to Avg
+            "addl  $8, %%ecx              \n\t"
+            "paddb %%mm2, %%mm0           \n\t" // add (Raw/2) + LBCarrys to
+                                                // Avg for each Active byte
+            // now ready to write back to memory
+            "movq  %%mm0, -8(%1," PCX ",) \n\t"
+            // move updated Raw(x) to use as Raw(x-bpp) for next loop
+            "cmpl  %%eax, %%ecx           \n\t" // MMXLength
+            "movq  %%mm0, %%mm2           \n\t" // mov updated Raw(x) to mm2
+            "jb avg_3lp                   \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),    // esi/rsi    // input regs
+              "1" (row),         // edi/rdi
+              "2" (diff),        // ecx
+              "3" (MMXLength)    // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 3 bpp
+
+      case 4:   // formerly shared with 6 bpp case via _ShiftBpp and _ShiftRem,
+      {         // but loop uses all 8 MMX regs, and psrlq/psllq require 64-bit
+                // mem (PIC/.so problems), MMX reg (none left), or immediate
+//       _ShiftBpp = bpp << 3;        // 32 (psllq)
+//       _ShiftRem = 64 - _ShiftBpp;  // 32 (psrlq)
+
+         __asm__ __volatile__ (
+            LOAD_GOT_rbp
+            "movq " HB_CLEAR_MASK ", %%mm4 \n\t" // _HBClearMask -> mm4
+            "movq " LB_CARRY_MASK ", %%mm5 \n\t" // _LBCarryMask -> mm5
+            // re-init address pointers and offset
+// preload  "movl  diff, %%ecx             \n\t" // ecx:  x = offset to
+                                                 // alignment boundary
+            "movq " AMASK0_8_0 ", %%mm7    \n\t" // _amask0_8_0 -> mm7
+            RESTORE_rbp
+
+            // ... and clear all bytes except for 1st active group
+// preload  "movl  row, %1               \n\t" // edi:  Avg(x)
+            "psrlq $32, %%mm7            \n\t" // was _ShiftRem
+// preload  "movl  prev_row, %0          \n\t" // esi:  Prior(x)
+            "movq  %%mm7, %%mm6          \n\t"
+            "psllq $32, %%mm6            \n\t" // mask for 2nd active group
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm2 \n\t" // load previous aligned 8 bytes
+                                             // (we correct pos. in loop below)
+         "avg_4lp:                       \n\t"
+            "movq (%1," PCX ",), %%mm0   \n\t"
+            "psrlq $32, %%mm2            \n\t" // shift data to pos. correctly
+            "movq (%0," PCX ",), %%mm1   \n\t"
+            // add (Prev_row/2) to average
+            "movq %%mm5, %%mm3           \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg for
+                                               // each byte
+            // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm7, %%mm2           \n\t" // leave only Active Group 1
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to Avg
+                                               // for each Active
+                              // byte
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $32, %%mm2            \n\t" // shift data to pos. correctly
+            "addl $8, %%ecx              \n\t"
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            // now ready to write back to memory
+            "movq %%mm0, -8(%1," PCX ",) \n\t"
+            // prep Raw(x-bpp) for next loop
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "jb avg_4lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),    // esi/rsi    // input regs
+              "1" (row),         // edi/rdi
+              "2" (diff),        // ecx
+              "3" (MMXLength)    // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 4 bpp
+
+      case 1:
+      {
+         __asm__ __volatile__ (
+            // re-init address pointers and offset
+// preload  "movl diff, %%ecx            \n\t" // ecx: x = offset to align. bdry
+// preload  "movl row, %1                \n\t" // edi/rdi:  Avg(x)
+// preload  "movl FullLength, %%eax      \n\t"
+            "cmpl %%eax, %%ecx           \n\t" // test if offset at end of array
+            "jnb avg_1end                \n\t"
+
+            SAVE_ebp
+
+            // do Avg decode for remaining bytes
+// preload  "movl prev_row, %0           \n\t" // esi/rsi:  Prior(x)
+            "mov  %1, " PBP "            \n\t" // copy of row pointer...
+            "dec  " PBP "                \n\t" // ebp/rbp:  Raw(x-bpp)
+            "xorl %%edx, %%edx           \n\t" // zero edx before using dl & dx
+                                               //  in loop below
+            SAVE_GOT_ebx
+
+         "avg_1lp:                       \n\t"
+            // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+            "xorl %%ebx, %%ebx           \n\t"
+            "movb (%0," PCX ",), %%dl    \n\t" // load dl with Prior(x)
+            "movb (" PBP "," PCX ",), %%bl \n\t" // load bl with Raw(x-bpp)
+            "addw %%dx, %%bx             \n\t"
+            "incl %%ecx                  \n\t"
+            "shrw %%bx                   \n\t" // divide by 2
+            "addb -1(%1," PCX ",), %%bl  \n\t" // add Avg(x); -1 to offset
+                                               // inc ecx
+            "cmpl %%eax, %%ecx           \n\t" // check if at end of array
+            "movb %%bl, -1(%1," PCX ",)  \n\t" // write back Raw(x);
+                         // mov does not affect flags; -1 to offset inc ecx
+            "jb avg_1lp                  \n\t"
+
+            RESTORE_GOT_ebx
+            RESTORE_ebp
+
+         "avg_1end:                      \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),    // esi/rsi    // input regs
+              "1" (row),         // edi/rdi
+              "2" (diff),        // ecx
+              "3" (FullLength)   // eax
+
+            : "%edx"                           // clobber list
+              _CLOBBER_GOT_ebx
+              _CLOBBER_ebp
+         );
+      }
+      return;  // end 1 bpp
+
+      case 2:
+      {
+//       _ShiftBpp = 16;   // == 2 * 8
+//       _ShiftRem = 48;   // == 64 - _ShiftBpp
+
+         __asm__ __volatile__ (
+            LOAD_GOT_rbp
+            // load (former) _ActiveMask
+            "movq " AMASK6_2_0 ", %%mm7    \n\t" // _amask6_2_0 -> mm7
+            // re-init address pointers and offset
+// preload  "movl  diff, %%ecx             \n\t" // ecx:  x = offset to
+                                                 // alignment boundary
+            "movq " LB_CARRY_MASK ", %%mm5 \n\t" // _LBCarryMask -> mm5
+// preload  "movl  row, %1                 \n\t" // edi:  Avg(x)
+            "movq " HB_CLEAR_MASK ", %%mm4 \n\t" // _HBClearMask -> mm4
+// preload  "movl  prev_row, %0            \n\t" // esi:  Prior(x)
+            RESTORE_rbp
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm2 \n\t" // load previous aligned 8 bytes
+                                             // (we correct pos. in loop below)
+         "avg_2lp:                       \n\t"
+            "movq (%1," PCX ",), %%mm0   \n\t"
+            "psrlq $48, %%mm2            \n\t" // shift data to pos. correctly
+            "movq (%0," PCX ",), %%mm1   \n\t" //  (GRR BUGFIX:  was psllq)
+            // add (Prev_row/2) to average
+            "movq %%mm5, %%mm3           \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "movq %%mm7, %%mm6           \n\t"
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg for
+                                               // each byte
+
+            // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                                               // lsb's were == 1 (only valid
+                                               // for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 1
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to Avg
+                                               // for each Active byte
+
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq $16, %%mm6            \n\t" // shift the mm6 mask to cover
+                                               // bytes 2 & 3
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $16, %%mm2            \n\t" // shift data to pos. correctly
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                                               // lsb's were == 1 (only valid
+                                               // for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+
+            // add 3rd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq $16, %%mm6            \n\t" // shift the mm6 mask to cover
+                                               // bytes 4 & 5
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $16, %%mm2            \n\t" // shift data to pos. correctly
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both lsb's were == 1
+                                               // (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+
+            // add 4th active group (Raw(x-bpp)/2) to average with _LBCarry
+            "psllq $16, %%mm6            \n\t" // shift the mm6 mask to cover
+                                               // bytes 6 & 7
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $16, %%mm2            \n\t" // shift data to pos. correctly
+            "addl $8, %%ecx              \n\t"
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                                               // lsb's were == 1 (only valid
+                                               // for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            // now ready to write back to memory
+            "movq %%mm0, -8(%1," PCX ",) \n\t"
+            // prep Raw(x-bpp) for next loop
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "jb avg_2lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),    // esi/rsi    // input regs
+              "1" (row),         // edi/rdi
+              "2" (diff),        // ecx
+              "3" (MMXLength)    // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 2 bpp
+
+      case 6:   // formerly shared with 4 bpp case (see comments there)
+      {
+//       _ShiftBpp = bpp << 3;        // 48 (psllq)
+//       _ShiftRem = 64 - _ShiftBpp;  // 16 (psrlq)
+
+         __asm__ __volatile__ (
+            LOAD_GOT_rbp
+            "movq " HB_CLEAR_MASK ", %%mm4 \n\t" // _HBClearMask -> mm4
+            "movq " LB_CARRY_MASK ", %%mm5 \n\t" // _LBCarryMask -> mm5
+            // re-init address pointers and offset
+// preload  "movl  diff, %%ecx             \n\t" // ecx:  x = offset to
+                                                 // alignment boundary
+            "movq " AMASK0_8_0 ", %%mm7    \n\t" // _amask0_8_0 -> mm7
+            RESTORE_rbp
+
+            // ... and clear all bytes except for 1st active group
+// preload  "movl  row, %1               \n\t" // edi:  Avg(x)
+            "psrlq $16, %%mm7            \n\t"
+// preload  "movl  prev_row, %0          \n\t" // esi:  Prior(x)
+            "movq  %%mm7, %%mm6          \n\t"
+            "psllq $48, %%mm6            \n\t" // mask for 2nd active group
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm2 \n\t" // load previous aligned 8 bytes
+                                             // (we correct pos. in loop below)
+         "avg_6lp:                       \n\t"
+            "movq (%1," PCX ",), %%mm0   \n\t"
+            "psrlq $16, %%mm2            \n\t" // shift data to pos. correctly
+            "movq (%0," PCX ",), %%mm1   \n\t"
+            // add (Prev_row/2) to average
+            "movq %%mm5, %%mm3           \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg for
+                                               // each byte
+            // add 1st active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm7, %%mm2           \n\t" // leave only Active Group 1
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to Avg
+                                               // for each Active
+                              // byte
+            // add 2nd active group (Raw(x-bpp)/2) to average with _LBCarry
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "psllq $48, %%mm2            \n\t" // shift data to pos. correctly
+            "addl $8, %%ecx              \n\t"
+            "movq %%mm3, %%mm1           \n\t" // now use mm1 for getting
+                                               // LBCarrys
+            "pand %%mm2, %%mm1           \n\t" // get LBCarrys for each byte
+                                               // where both
+                              // lsb's were == 1 (only valid for active group)
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7 of each
+                                               // byte
+            "paddb %%mm1, %%mm2          \n\t" // add LBCarrys to (Raw(x-bpp)/2)
+                                               // for each byte
+            "pand %%mm6, %%mm2           \n\t" // leave only Active Group 2
+                                               // bytes to add to Avg
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) + LBCarrys to
+                                               // Avg for each Active byte
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            // now ready to write back to memory
+            "movq %%mm0, -8(%1," PCX ",) \n\t"
+            // prep Raw(x-bpp) for next loop
+            "movq %%mm0, %%mm2           \n\t" // mov updated Raws to mm2
+            "jb avg_6lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),    // esi/rsi    // input regs
+              "1" (row),         // edi/rdi
+              "2" (diff),        // ecx
+              "3" (MMXLength)    // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 6 bpp
+
+      case 8:
+      {
+         __asm__ __volatile__ (
+            // re-init address pointers and offset
+// preload  "movl  diff, %%ecx             \n\t" // ecx:  x = offset to
+                                                 // alignment boundary
+            LOAD_GOT_rbp
+            "movq " LB_CARRY_MASK ", %%mm5 \n\t" // [interleave for parallel.?]
+// preload  "movl  row, %1                 \n\t" // edi:  Avg(x)
+            "movq " HB_CLEAR_MASK ", %%mm4 \n\t" // _HBClearMask -> mm4
+// preload  "movl  prev_row, %0            \n\t" // esi:  Prior(x)
+            RESTORE_rbp
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm2 \n\t" // load previous aligned 8 bytes
+                                      // (NO NEED to correct pos. in loop below)
+
+         "avg_8lp:                       \n\t"
+            "movq (%1," PCX ",), %%mm0   \n\t"
+            "movq %%mm5, %%mm3           \n\t"
+            "movq (%0," PCX ",), %%mm1   \n\t"
+            "addl $8, %%ecx              \n\t"
+            "pand %%mm1, %%mm3           \n\t" // get lsb for each prev_row byte
+            "psrlq $1, %%mm1             \n\t" // divide prev_row bytes by 2
+            "pand %%mm2, %%mm3           \n\t" // get LBCarrys for each byte
+                                               //  where both lsb's were == 1
+            "psrlq $1, %%mm2             \n\t" // divide raw bytes by 2
+            "pand  %%mm4, %%mm1          \n\t" // clear invalid bit 7, each byte
+            "paddb %%mm3, %%mm0          \n\t" // add LBCarrys to Avg, each byte
+            "pand  %%mm4, %%mm2          \n\t" // clear invalid bit 7, each byte
+            "paddb %%mm1, %%mm0          \n\t" // add (Prev_row/2) to Avg, each
+            "paddb %%mm2, %%mm0          \n\t" // add (Raw/2) to Avg for each
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            "movq %%mm0, -8(%1," PCX ",) \n\t"
+            "movq %%mm0, %%mm2           \n\t" // reuse as Raw(x-bpp)
+            "jb avg_8lp                  \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),    // esi/rsi    // input regs
+              "1" (row),         // edi/rdi
+              "2" (diff),        // ecx
+              "3" (MMXLength)    // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2"           // clobber list
+            , "%mm3", "%mm4", "%mm5"
+#endif
+         );
+      }
+      break;  // end 8 bpp
+
+      default:                // bpp != 1,2,3,4,6,8:  doesn't exist
+      {
+         // ERROR:  SHOULD NEVER BE REACHED
+#if defined(PNG_DEBUG)
+         png_debug(1, "Internal libpng logic error (GCC "
+           "png_read_filter_row_mmx_avg())\n");
+#endif
+      }
+      break;
+
+   } // end switch (bpp)
+
+   __asm__ __volatile__ (
+      // MMX acceleration complete; now do clean-up
+      // check if any remaining bytes left to decode
+//pre "movl FullLength, %%edx      \n\t"
+//pre "movl MMXLength, %%eax       \n\t" // eax:  x == offset bytes after MMX
+//pre "movl row, %2                \n\t" // edi:  Avg(x)
+      "cmpl %%edx, %%eax           \n\t" // test if offset at end of array
+      "jnb avg_end                 \n\t"
+
+      SAVE_ebp
+
+      // do Avg decode for remaining bytes
+//pre "movl prev_row, %1           \n\t" // esi:  Prior(x)
+      "mov  %2, " PBP "            \n\t" // copy of row pointer...
+//pre "subl bpp, " PBP "           \n\t" // (bpp is preloaded into ecx)
+      "sub  " PCX "," PBP "        \n\t" // ebp:  Raw(x-bpp)
+      "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx below
+
+      SAVE_GOT_ebx
+
+   "avg_lp2:                       \n\t"
+      // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+      "xorl %%ebx, %%ebx           \n\t"
+      "movb (%1," PAX ",), %%cl    \n\t" // load cl with Prior(x)
+      "movb (" PBP "," PAX ",), %%bl \n\t" // load bl with Raw(x-bpp)
+      "addw %%cx, %%bx             \n\t"
+      "incl %%eax                  \n\t"
+      "shrw %%bx                   \n\t" // divide by 2
+      "addb -1(%2," PAX ",), %%bl  \n\t" // add Avg(x); -1 to offset inc eax
+      "cmpl %%edx, %%eax           \n\t" // check if at end of array
+      "movb %%bl, -1(%2," PAX ",)  \n\t" // write back Raw(x) [mov does not
+      "jb avg_lp2                  \n\t" //  affect flags; -1 to offset inc eax]
+
+      RESTORE_GOT_ebx
+      RESTORE_ebp
+
+   "avg_end:                       \n\t"
+      "EMMS                        \n\t" // end MMX; prep for poss. FP instrs.
+
+      : "=c" (dummy_value_c),            // output regs (dummy)
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D),
+        "=a" (dummy_value_a),
+        "=d" (dummy_value_d)
+
+      : "0" (bpp),         // ecx        // input regs
+        "1" (prev_row),    // esi/rsi
+        "2" (row),         // edi/rdi
+        "3" (MMXLength),   // eax
+        "4" (FullLength)   // edx
+
+      CLOB_COLON_ebx_ebp                 // clobber list
+        CLOBBER_GOT_ebx
+        CLOB_COMMA_ebx_ebp
+        CLOBBER_ebp
+   );
+
+} /* end png_read_filter_row_mmx_avg() */
+
+#endif /* PNG_MMX_READ_FILTER_AVG_SUPPORTED */
+
+
+
+#if defined(PNG_MMX_READ_FILTER_PAETH_SUPPORTED)
+#if defined(PNG_x86_64_USE_GOTPCREL) || defined(PNG_THREAD_UNSAFE_OK)
+
+//===========================================================================//
+//                                                                           //
+//         P N G _ R E A D _ F I L T E R _ R O W _ M M X _ P A E T H         //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Paeth filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row,
+                              png_bytep prev_row)
+{
+   unsigned FullLength, MMXLength;  // png_uint_32 is actually 64-bit on x86-64
+   int bpp;
+   int dummy_value_a;
+   int dummy_value_c;   // fix 'forbidden register 2 (cx) was spilled' error
+   int dummy_value_d;
+   png_charp dummy_value_S;
+   png_charp dummy_value_D;
+   int diff; //     __attribute__((used));
+
+   bpp = (row_info->pixel_depth + 7) >> 3;  // calc number of bytes per pixel
+   FullLength = row_info->rowbytes;         // number of bytes to filter
+
+   __asm__ __volatile__ (
+      SAVE_GOT_ebx
+      SAVE_r15
+      SAVE_ebp
+//pre "movl row, %2                \n\t" // edi/rdi
+      "xorl %%ebx, %%ebx           \n\t" // ebx:  x offset
+//pre "movl prev_row, %1           \n\t" // esi/rsi
+      "xorl %%edx, %%edx           \n\t" // edx:  x-bpp offset
+//pre "movl FullLength, %%eax      \n\t" // bring in via eax...
+      SAVE_FullLength                    // ...but store for later use
+      "xorl %%eax, %%eax           \n\t"
+
+      // Compute the Raw value for the first bpp bytes
+      // Note: the formula works out to be always
+      //   Paeth(x) = Raw(x) + Prior(x)      where x < bpp
+   "paeth_rlp:                     \n\t"
+      "movb (%2," PBX ",), %%al    \n\t"
+      "addb (%1," PBX ",), %%al    \n\t"
+      "incl %%ebx                  \n\t"
+//pre "cmpl bpp, %%ebx             \n\t" (bpp is preloaded into ecx)
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movb %%al, -1(%2," PBX ",)  \n\t"
+      "jb paeth_rlp                \n\t"
+
+      // get # of bytes to alignment (note:  computing _delta_ of two pointers,
+      // so hereafter %%ebp is sufficient even on 64-bit)
+      "mov  %2, " PBP "            \n\t" // take start of row
+      "add  " PBX "," PBP "        \n\t" // add bpp
+      "add  $0xf, " PBP "          \n\t" // add 7+8 to incr past alignment bdry
+//    "andl $0xfffffff8, %%ebp     \n\t" // mask to alignment boundary (32-bit!)
+      CLEAR_BOTTOM_3_BITS  PBP    "\n\t" // mask to alignment boundary
+      "sub  %2, " PBP "            \n\t" // subtract row ptr again => ebp =
+      "jz paeth_go                 \n\t" //  target value of ebx at alignment
+
+      "xorl %%ecx, %%ecx           \n\t"
+
+      SAVE_r11_r12_r13
+
+      // fix alignment
+   "paeth_lp1:                     \n\t"
+      "xorl %%eax, %%eax           \n\t"
+      // pav = p - a = (a + b - c) - a = b - c
+      "movb (%1," PBX ",), %%al    \n\t" // load Prior(x) into al
+      "movb (%1," PDX ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+      "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+      "movl %%eax, " pa_TEMP "     \n\t" // Save pav for later use
+      "xorl %%eax, %%eax           \n\t"
+      // pbv = p - b = (a + b - c) - b = a - c
+      "movb (%2," PDX ",), %%al    \n\t" // load Raw(x-bpp) into al
+      "subl %%ecx, %%eax           \n\t" // subtract Prior(x-bpp)
+      "movl %%eax, %%ecx           \n\t"
+      // pcv = p - c = (a + b - c) - c = (a - c) + (b - c) = pav + pbv
+      "addl " pa_TEMP ", %%eax     \n\t" // pcv = pav + pbv
+      // pc = abs(pcv)
+      "testl $0x80000000, %%eax    \n\t"
+      "jz paeth_pca                \n\t"
+      "negl %%eax                  \n\t" // reverse sign of neg values
+
+   "paeth_pca:                     \n\t"
+      "movl %%eax, " pc_TEMP "     \n\t" // save pc for later use
+      // pb = abs(pbv)
+      "testl $0x80000000, %%ecx    \n\t"
+      "jz paeth_pba                \n\t"
+      "negl %%ecx                  \n\t" // reverse sign of neg values
+
+   "paeth_pba:                     \n\t"
+      "movl %%ecx, " pb_TEMP "     \n\t" // save pb for later use
+      // pa = abs(pav)
+      "movl " pa_TEMP ", %%eax     \n\t"
+      "testl $0x80000000, %%eax    \n\t"
+      "jz paeth_paa                \n\t"
+      "negl %%eax                  \n\t" // reverse sign of neg values
+
+   "paeth_paa:                     \n\t"
+      "movl %%eax, " pa_TEMP "     \n\t" // save pa for later use
+      // test if pa <= pb
+      "cmpl %%ecx, %%eax           \n\t"
+      "jna paeth_abb               \n\t"
+      // pa > pb; now test if pb <= pc
+      "cmpl " pc_TEMP ", %%ecx     \n\t"
+      "jna paeth_bbc               \n\t"
+      // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%1," PDX ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth             \n\t"
+
+   "paeth_bbc:                     \n\t"
+      // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+      "movb (%1," PBX ",), %%cl    \n\t" // load Prior(x) into cl
+      "jmp paeth_paeth             \n\t"
+
+   "paeth_abb:                     \n\t"
+      // pa <= pb; now test if pa <= pc
+      "cmpl " pc_TEMP ", %%eax     \n\t"
+      "jna paeth_abc               \n\t"
+      // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%1," PDX ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth             \n\t"
+
+   "paeth_abc:                     \n\t"
+      // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+      "movb (%2," PDX ",), %%cl    \n\t" // load Raw(x-bpp) into cl
+
+   "paeth_paeth:                   \n\t"
+      "incl %%ebx                  \n\t"
+      "incl %%edx                  \n\t"
+      // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+      "addb %%cl, -1(%2," PBX ",)  \n\t"
+      "cmpl %%ebp, %%ebx           \n\t"
+      "jb paeth_lp1                \n\t"
+
+      RESTORE_r11_r12_r13
+
+   "paeth_go:                      \n\t"
+      RESTORE_FullLength "%%ecx    \n\t" // FullLength -> ecx
+      "movl %%ecx, %%eax           \n\t"
+      "subl %%ebx, %%eax           \n\t" // subtract alignment fix
+      "andl $0x00000007, %%eax     \n\t" // calc bytes over mult of 8
+      "subl %%eax, %%ecx           \n\t" // drop over bytes from original length
+//out "movl %%ecx, MMXLength       \n\t"
+      "movl %%ebp, %%eax           \n\t" // ebp = diff, but no reg constraint(?)
+      RESTORE_ebp                        //  (could swap ebp and edx functions)
+      RESTORE_r15
+      RESTORE_GOT_ebx
+
+      : "=c" (MMXLength),                // output regs
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D),
+        "=a" (diff)
+
+      : "0" (bpp),         // ecx        // input regs
+        "1" (prev_row),    // esi/rsi
+        "2" (row),         // edi/rdi
+        "3" (FullLength)   // eax
+
+      : "%edx"                           // clobber list
+        _CLOBBER_r11_r12_r13
+        _CLOBBER_r15
+        _CLOBBER_ebp
+        _CLOBBER_GOT_ebx
+   );
+
+   // now do the math for the rest of the row
+   switch (bpp)
+   {
+      case 3:
+      {
+//       _ShiftBpp = 24;    // == bpp * 8
+//       _ShiftRem = 40;    // == 64 - _ShiftBpp
+
+         __asm__ __volatile__ (
+            LOAD_GOT_rbp
+// preload  "movl diff, %%ecx            \n\t"
+// preload  "movl row, %1                \n\t" // edi/rdi
+// preload  "movl prev_row, %0           \n\t" // esi/rsi
+            "pxor %%mm0, %%mm0           \n\t"
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm1 \n\t"
+         "paeth_3lp:                     \n\t"
+            "psrlq $40, %%mm1            \n\t" // shift last 3 bytes to 1st
+                                               // 3 bytes
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            "movq -8(%0," PCX ",), %%mm3 \n\t" // prep c=Prior(x-bpp) bytes
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "psrlq $40, %%mm3            \n\t" // shift last 3 bytes to 1st
+                                               // 3 bytes
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq (%0," PCX ",), %%mm3   \n\t" // load c=Prior(x-bpp)
+            "pand " AMASK5_3_0 ", %%mm7  \n\t" // _amask5_3_0 (was _ActiveMask)
+            "movq %%mm3, %%mm2           \n\t" // load b=Prior(x) step 1
+            "paddb (%1," PCX ",), %%mm7  \n\t" // add Paeth predictor + Raw(x)
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "movq %%mm7, (%1," PCX ",)   \n\t" // write back updated value
+            "movq %%mm7, %%mm1           \n\t" // now mm1 will be used as
+                                               // Raw(x-bpp)
+            // now do Paeth for 2nd set of bytes (3-5)
+            "psrlq $24, %%mm2            \n\t" // load b=Prior(x) step 2
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            "pxor %%mm7, %%mm7           \n\t"
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) =
+            //       pav + pbv = pbv + pav
+            "movq %%mm5, %%mm6           \n\t"
+            "paddw %%mm4, %%mm6          \n\t"
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm5, %%mm0        \n\t" // create mask pbv bytes < 0
+            "pcmpgtw %%mm4, %%mm7        \n\t" // create mask pav bytes < 0
+            "pand %%mm5, %%mm0           \n\t" // only pbv bytes < 0 in mm0
+            "pand %%mm4, %%mm7           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm0, %%mm5          \n\t"
+            "psubw %%mm7, %%mm4          \n\t"
+            "psubw %%mm0, %%mm5          \n\t"
+            "psubw %%mm7, %%mm4          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x)
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq %%mm2, %%mm3           \n\t" // load c=Prior(x-bpp) step 1
+            "pand " AMASK5_3_0 ", %%mm7  \n\t" // _amask5_3_0 (was _ActiveMask)
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "psllq $24, %%mm7            \n\t" // shift bytes to 2nd group of
+                                               // 3 bytes
+             // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "paddb (%1," PCX ",), %%mm7  \n\t" // add Paeth predictor + Raw(x)
+            "psllq $24, %%mm3            \n\t" // load c=Prior(x-bpp) step 2
+            "movq %%mm7, (%1," PCX ",)   \n\t" // write back updated value
+            "movq %%mm7, %%mm1           \n\t"
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "psllq $24, %%mm1            \n\t" // shift bytes (was _ShiftBpp)
+                                    // now mm1 will be used as Raw(x-bpp)
+            // now do Paeth for 3rd, and final, set of bytes (6-7)
+            "pxor %%mm7, %%mm7           \n\t"
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            "psubw %%mm3, %%mm4          \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "paddw %%mm5, %%mm6          \n\t"
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "pand " AMASK0_2_6 ", %%mm1  \n\t" // _amask0_2_6 (_ActiveMaskEnd)
+            "paddb -8(%1," PCX ",), %%mm1 \n\t" // add Paeth predictor + Raw(x)
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            "pxor %%mm0, %%mm0           \n\t" // pxor does not affect flags
+            "movq %%mm1, -8(%1," PCX ",) \n\t" // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+                           // mm3 ready to be used as Prior(x-bpp) next loop
+            "jb paeth_3lp                \n\t"
+            RESTORE_rbp
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),  // esi/rsi      // input regs
+              "1" (row),       // edi/rdi
+              "2" (diff),      // ecx
+              "3" (MMXLength)  // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 3 bpp
+
+      case 4:
+      {
+         __asm__ __volatile__ (
+// preload  "movl diff, %%ecx            \n\t"
+// preload  "movl row, %1                \n\t" // edi/rdi
+// preload  "movl prev_row, %0           \n\t" // esi/rsi
+            "pxor %%mm0, %%mm0           \n\t"
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm1 \n\t" // only time should need to read
+                                               //  a=Raw(x-bpp) bytes
+         "paeth_4lp:                     \n\t"
+            // do first set of 4 bytes
+            "movq -8(%0," PCX ",), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq (%0," PCX ",), %%mm3   \n\t" // load c=Prior(x-bpp)
+            LOAD_GOT_rbp
+            "pand " AMASK4_4_0 ", %%mm7  \n\t" // _amask4_4_0 (was _ActiveMask)
+            RESTORE_rbp
+            "movq %%mm3, %%mm2           \n\t" // load b=Prior(x) step 1
+            "paddb (%1," PCX ",), %%mm7  \n\t" // add Paeth predictor + Raw(x)
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "movq %%mm7, (%1," PCX ",)   \n\t" // write back updated value
+            "movq %%mm7, %%mm1           \n\t" // now mm1 will be used as
+                                               // Raw(x-bpp)
+            // do second set of 4 bytes
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack Low bytes of b
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            "paddb -8(%1," PCX ",), %%mm1 \n\t" // add predictor with Raw(x)
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            "movq %%mm1, -8(%1," PCX ",) \n\t" // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+            "jb paeth_4lp                \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),  // esi/rsi      // input regs
+              "1" (row),       // edi/rdi
+              "2" (diff),      // ecx
+              "3" (MMXLength)  // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 4 bpp
+
+      case 1:
+      case 2:
+      {
+         __asm__ __volatile__ (
+// preload  "movl diff, %%eax            \n\t" // eax: x = offset to align. bdry
+// preload  "movl FullLength, %%edx      \n\t"
+            "cmpl %%edx, %%eax           \n\t"
+            "jnb paeth_dend              \n\t"
+
+            SAVE_ebp
+
+// preload  "movl row, %2                \n\t" // edi/rdi
+            // do Paeth decode for remaining bytes
+// preload  "movl prev_row, %1           \n\t" // esi/rsi
+            "movl %%eax, %%ebp           \n\t"
+// preload  "subl bpp, %%ebp             \n\t" // (bpp is preloaded into ecx)
+            "subl %%ecx, %%ebp           \n\t" // ebp = eax - bpp
+            "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx
+
+            SAVE_GOT_ebx
+            SAVE_r11_r12_r13
+
+         "paeth_dlp:                     \n\t"
+            "xorl %%ebx, %%ebx           \n\t"
+            // pav = p - a = (a + b - c) - a = b - c
+            "movb (%1," PAX ",), %%bl    \n\t" // load Prior(x) into bl
+            "movb (%1," PBP ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+            "subl %%ecx, %%ebx           \n\t" // subtract Prior(x-bpp)
+            "movl %%ebx, " pa_TEMP "     \n\t" // Save pav for later use
+            "xorl %%ebx, %%ebx           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movb (%2," PBP ",), %%bl    \n\t" // load Raw(x-bpp) into bl
+            "subl %%ecx, %%ebx           \n\t" // subtract Prior(x-bpp)
+            "movl %%ebx, %%ecx           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "addl " pa_TEMP ", %%ebx     \n\t" // pcv = pav + pbv
+            // pc = abs(pcv)
+            "testl $0x80000000, %%ebx    \n\t"
+            "jz paeth_dpca               \n\t"
+            "negl %%ebx                  \n\t" // reverse sign of neg values
+
+         "paeth_dpca:                    \n\t"
+            "movl %%ebx, " pc_TEMP "     \n\t" // save pc for later use
+            // pb = abs(pbv)
+            "testl $0x80000000, %%ecx    \n\t"
+            "jz paeth_dpba               \n\t"
+            "negl %%ecx                  \n\t" // reverse sign of neg values
+
+         "paeth_dpba:                    \n\t"
+            "movl %%ecx, " pb_TEMP "     \n\t" // save pb for later use
+            // pa = abs(pav)
+            "movl " pa_TEMP ", %%ebx     \n\t"
+            "testl $0x80000000, %%ebx    \n\t"
+            "jz paeth_dpaa               \n\t"
+            "negl %%ebx                  \n\t" // reverse sign of neg values
+
+         "paeth_dpaa:                    \n\t"
+            "movl %%ebx, " pa_TEMP "     \n\t" // save pa for later use
+            // test if pa <= pb
+            "cmpl %%ecx, %%ebx           \n\t"
+            "jna paeth_dabb              \n\t"
+            // pa > pb; now test if pb <= pc
+            "cmpl " pc_TEMP ", %%ecx     \n\t"
+            "jna paeth_dbbc              \n\t"
+            // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            "movb (%1," PBP ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+            "jmp paeth_dpaeth            \n\t"
+
+         "paeth_dbbc:                    \n\t"
+            // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+            "movb (%1," PAX ",), %%cl    \n\t" // load Prior(x) into cl
+            "jmp paeth_dpaeth            \n\t"
+
+         "paeth_dabb:                    \n\t"
+            // pa <= pb; now test if pa <= pc
+            "cmpl " pc_TEMP ", %%ebx     \n\t"
+            "jna paeth_dabc              \n\t"
+            // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            "movb (%1," PBP ",), %%cl   \n\t" // load Prior(x-bpp) into cl
+            "jmp paeth_dpaeth            \n\t"
+
+         "paeth_dabc:                    \n\t"
+            // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+            "movb (%2," PBP ",), %%cl    \n\t" // load Raw(x-bpp) into cl
+
+         "paeth_dpaeth:                  \n\t"
+            "incl %%eax                  \n\t"
+            "incl %%ebp                  \n\t"
+            // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+            "addb %%cl, -1(%2," PAX ",)  \n\t"
+            "cmpl %%edx, %%eax           \n\t" // check against FullLength
+            "jb paeth_dlp                \n\t"
+
+            RESTORE_r11_r12_r13
+            RESTORE_GOT_ebx
+            RESTORE_ebp
+
+         "paeth_dend:                    \n\t"
+
+            : "=c" (dummy_value_c),            // output regs (dummy)
+              "=S" (dummy_value_S),
+              "=D" (dummy_value_D),
+              "=a" (dummy_value_a),
+              "=d" (dummy_value_d)
+
+            : "0" (bpp),         // ecx        // input regs
+              "1" (prev_row),    // esi/rsi
+              "2" (row),         // edi/rdi
+              "3" (diff),        // eax
+              "4" (FullLength)   // edx
+
+            CLOB_COLON_ebx_ebp_r1X             // clobber list
+              CLOBBER_GOT_ebx
+              CLOB_COMMA_ebx_ebp
+              CLOBBER_ebp
+              CLOB_COMMA_ebX_r1X
+              CLOBBER_r11_r12_r13
+         );
+      }
+      return; // end 1 or 2 bpp (no need to go further with this one)
+
+      case 6:
+      {
+//       _ActiveMask2 = 0xffffffff00000000LL;  // NOT USED ("_amask_0_4_4")
+//       _ShiftBpp = 48;       // bpp << 3 == bpp * 8
+//       _ShiftRem = 16;       // 64 - _ShiftBpp
+
+         __asm__ __volatile__ (
+// preload  "movl diff, %%ecx            \n\t"
+// preload  "movl row, %1                \n\t" // edi/rdi
+// preload  "movl prev_row, %0           \n\t" // esi/rsi
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm1 \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+
+         "paeth_6lp:                     \n\t"
+            // must shift to position Raw(x-bpp) data
+            "psrlq $16, %%mm1            \n\t" // was _ShiftRem
+            // do first set of 4 bytes
+            "movq -8(%0," PCX ",), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack Low bytes of b
+            // must shift to position Prior(x-bpp) data
+            "psrlq $16, %%mm3            \n\t" // was _ShiftRem
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq -8(%0," PCX ",), %%mm3 \n\t" // load c=Prior(x-bpp)
+            LOAD_GOT_rbp
+            "pand " AMASK4_4_0 ", %%mm7  \n\t" // _amask4_4_0 (was _ActiveMask)
+            RESTORE_rbp
+            "psrlq $16, %%mm3            \n\t"
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x) step 1
+            "paddb (%1," PCX ",), %%mm7  \n\t" // add Paeth predictor + Raw(x)
+            "movq %%mm2, %%mm6           \n\t"
+            "movq %%mm7, (%1," PCX ",)   \n\t" // write back updated value
+            "movq -8(%1," PCX ",), %%mm1 \n\t"
+            "psllq $48, %%mm6            \n\t" // bpp * 8 = bits per pixel
+            "movq %%mm7, %%mm5           \n\t"
+            "psrlq $16, %%mm1            \n\t" // 64 - (bpp * 8) = remainder
+            "por %%mm6, %%mm3            \n\t"
+            "psllq $48, %%mm5            \n\t" // was _ShiftBpp
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "por %%mm5, %%mm1            \n\t"
+            // do second set of 4 bytes
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            "paddb -8(%1," PCX ",), %%mm1 \n\t" // add Paeth predictor + Raw(x)
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            "movq %%mm1, -8(%1," PCX ",) \n\t" // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+            "jb paeth_6lp                \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),  // esi/rsi      // input regs
+              "1" (row),       // edi/rdi
+              "2" (diff),      // ecx
+              "3" (MMXLength)  // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 6 bpp
+
+      case 8:                          // bpp == 8
+      {
+         __asm__ __volatile__ (
+// preload  "movl diff, %%ecx            \n\t"
+// preload  "movl row, %1                \n\t" // edi/rdi
+// preload  "movl prev_row, %0           \n\t" // esi/rsi
+            "pxor %%mm0, %%mm0           \n\t"
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PCX ",), %%mm1 \n\t" // only time should need to read
+                                               //  a=Raw(x-bpp) bytes
+         "paeth_8lp:                     \n\t"
+            // do first set of 4 bytes
+            "movq -8(%0," PCX ",), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            "punpcklbw %%mm0, %%mm1      \n\t" // unpack Low bytes of a
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x)
+            "punpcklbw %%mm0, %%mm2      \n\t" // unpack Low bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            "punpcklbw %%mm0, %%mm3      \n\t" // unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "packuswb %%mm1, %%mm7       \n\t"
+            "movq -8(%0," PCX ",), %%mm3 \n\t" // read c=Prior(x-bpp) bytes
+            LOAD_GOT_rbp
+            "pand " AMASK4_4_0 ", %%mm7  \n\t" // _amask4_4_0 (was _ActiveMask)
+            RESTORE_rbp
+            "movq (%0," PCX ",), %%mm2   \n\t" // load b=Prior(x)
+            "paddb (%1," PCX ",), %%mm7  \n\t" // add Paeth predictor + Raw(x)
+            "punpckhbw %%mm0, %%mm3      \n\t" // unpack High bytes of c
+            "movq %%mm7, (%1," PCX ",)   \n\t" // write back updated value
+            "movq -8(%1," PCX ",), %%mm1 \n\t" // read a=Raw(x-bpp) bytes
+
+            // do second set of 4 bytes
+            "punpckhbw %%mm0, %%mm2      \n\t" // unpack High bytes of b
+            "punpckhbw %%mm0, %%mm1      \n\t" // unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            "movq %%mm2, %%mm4           \n\t"
+            // pbv = p - b = (a + b - c) - b = a - c
+            "movq %%mm1, %%mm5           \n\t"
+            "psubw %%mm3, %%mm4          \n\t"
+            "pxor %%mm7, %%mm7           \n\t"
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            "movq %%mm4, %%mm6           \n\t"
+            "psubw %%mm3, %%mm5          \n\t"
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            "pcmpgtw %%mm4, %%mm0        \n\t" // create mask pav bytes < 0
+            "paddw %%mm5, %%mm6          \n\t"
+            "pand %%mm4, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "pcmpgtw %%mm5, %%mm7        \n\t" // create mask pbv bytes < 0
+            "psubw %%mm0, %%mm4          \n\t"
+            "pand %%mm5, %%mm7           \n\t" // only pbv bytes < 0 in mm0
+            "psubw %%mm0, %%mm4          \n\t"
+            "psubw %%mm7, %%mm5          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            "pcmpgtw %%mm6, %%mm0        \n\t" // create mask pcv bytes < 0
+            "pand %%mm6, %%mm0           \n\t" // only pav bytes < 0 in mm7
+            "psubw %%mm7, %%mm5          \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            //  test pa <= pb
+            "movq %%mm4, %%mm7           \n\t"
+            "psubw %%mm0, %%mm6          \n\t"
+            "pcmpgtw %%mm5, %%mm7        \n\t" // pa > pb?
+            "movq %%mm7, %%mm0           \n\t"
+            // use mm7 mask to merge pa & pb
+            "pand %%mm7, %%mm5           \n\t"
+            // use mm0 mask copy to merge a & b
+            "pand %%mm0, %%mm2           \n\t"
+            "pandn %%mm4, %%mm7          \n\t"
+            "pandn %%mm1, %%mm0          \n\t"
+            "paddw %%mm5, %%mm7          \n\t"
+            "paddw %%mm2, %%mm0          \n\t"
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            "pcmpgtw %%mm6, %%mm7        \n\t" // pab > pc?
+            "pxor %%mm1, %%mm1           \n\t"
+            "pand %%mm7, %%mm3           \n\t"
+            "pandn %%mm0, %%mm7          \n\t"
+            "pxor %%mm1, %%mm1           \n\t"
+            "paddw %%mm3, %%mm7          \n\t"
+            "pxor %%mm0, %%mm0           \n\t"
+            // step ecx to next set of 8 bytes and repeat loop til done
+            "addl $8, %%ecx              \n\t"
+            "packuswb %%mm7, %%mm1       \n\t"
+            "paddb -8(%1," PCX ",), %%mm1 \n\t" // add Paeth predictor + Raw(x)
+            "cmpl %%eax, %%ecx           \n\t" // MMXLength
+            "movq %%mm1, -8(%1," PCX ",) \n\t" // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+            "jb paeth_8lp                \n\t"
+
+            : "=S" (dummy_value_S),            // output regs (dummy)
+              "=D" (dummy_value_D),
+              "=c" (dummy_value_c),
+              "=a" (dummy_value_a)
+
+            : "0" (prev_row),  // esi/rsi      // input regs
+              "1" (row),       // edi/rdi
+              "2" (diff),      // ecx
+              "3" (MMXLength)  // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm2", "%mm3"   // clobber list
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 8 bpp
+
+      default:                // bpp != 1,2,3,4,6,8:  doesn't exist
+      {
+         // ERROR:  SHOULD NEVER BE REACHED
+#if defined(PNG_DEBUG)
+         png_debug(1, "Internal libpng logic error (GCC "
+           "png_read_filter_row_mmx_paeth())\n");
+#endif
+      }
+      break;
+
+   } // end switch (bpp)
+
+   __asm__ __volatile__ (
+      // MMX acceleration complete; now do clean-up
+      // check if any remaining bytes left to decode
+//pre "movl FullLength, %%edx      \n\t"
+//pre "movl MMXLength, %%eax       \n\t"
+      "cmpl %%edx, %%eax           \n\t"
+      "jnb paeth_end               \n\t"
+
+      SAVE_ebp
+
+//pre "movl row, %2                \n\t" // edi/rdi
+//pre "movl prev_row, %1           \n\t" // esi/rsi
+      // do Paeth decode for remaining bytes
+      "movl %%eax, %%ebp           \n\t"
+//pre "subl bpp, %%ebp             \n\t" // (bpp is preloaded into ecx)
+      "subl %%ecx, %%ebp           \n\t" // ebp = eax - bpp
+      "xorl %%ecx, %%ecx           \n\t" // zero ecx before using cl & cx below
+
+      SAVE_GOT_ebx
+      SAVE_r11_r12_r13
+
+   "paeth_lp2:                     \n\t"
+      "xorl %%ebx, %%ebx           \n\t"
+      // pav = p - a = (a + b - c) - a = b - c
+      "movb (%1," PAX ",), %%bl    \n\t" // load Prior(x) into bl
+      "movb (%1," PBP ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+      "subl %%ecx, %%ebx           \n\t" // subtract Prior(x-bpp)
+      "movl %%ebx, " pa_TEMP "     \n\t" // Save pav for later use
+      "xorl %%ebx, %%ebx           \n\t"
+      // pbv = p - b = (a + b - c) - b = a - c
+      "movb (%2," PBP ",), %%bl    \n\t" // load Raw(x-bpp) into bl
+      "subl %%ecx, %%ebx           \n\t" // subtract Prior(x-bpp)
+      "movl %%ebx, %%ecx           \n\t"
+      // pcv = p - c = (a + b - c) - c = (a - c) + (b - c) = pav + pbv
+      "addl " pa_TEMP ", %%ebx     \n\t" // pcv = pav + pbv
+      // pc = abs(pcv)
+      "testl $0x80000000, %%ebx    \n\t"
+      "jz paeth_pca2               \n\t"
+      "negl %%ebx                  \n\t" // reverse sign of neg values
+
+   "paeth_pca2:                    \n\t"
+      "movl %%ebx, " pc_TEMP "     \n\t" // save pc for later use
+      // pb = abs(pbv)
+      "testl $0x80000000, %%ecx    \n\t"
+      "jz paeth_pba2               \n\t"
+      "negl %%ecx                  \n\t" // reverse sign of neg values
+
+   "paeth_pba2:                    \n\t"
+      "movl %%ecx, " pb_TEMP "     \n\t" // save pb for later use
+      // pa = abs(pav)
+      "movl " pa_TEMP ", %%ebx     \n\t"
+      "testl $0x80000000, %%ebx    \n\t"
+      "jz paeth_paa2               \n\t"
+      "negl %%ebx                  \n\t" // reverse sign of neg values
+
+   "paeth_paa2:                    \n\t"
+      "movl %%ebx, " pa_TEMP "     \n\t" // save pa for later use
+      // test if pa <= pb
+      "cmpl %%ecx, %%ebx           \n\t"
+      "jna paeth_abb2              \n\t"
+      // pa > pb; now test if pb <= pc
+      "cmpl " pc_TEMP ", %%ecx     \n\t"
+      "jna paeth_bbc2              \n\t"
+      // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%1," PBP ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth2            \n\t"
+
+   "paeth_bbc2:                    \n\t"
+      // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+      "movb (%1," PAX ",), %%cl    \n\t" // load Prior(x) into cl
+      "jmp paeth_paeth2            \n\t"
+
+   "paeth_abb2:                    \n\t"
+      // pa <= pb; now test if pa <= pc
+      "cmpl " pc_TEMP ", %%ebx     \n\t"
+      "jna paeth_abc2              \n\t"
+      // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+      "movb (%1," PBP ",), %%cl    \n\t" // load Prior(x-bpp) into cl
+      "jmp paeth_paeth2            \n\t"
+
+   "paeth_abc2:                    \n\t"
+      // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+      "movb (%2," PBP ",), %%cl    \n\t" // load Raw(x-bpp) into cl
+
+   "paeth_paeth2:                  \n\t"
+      "incl %%eax                  \n\t"
+      "incl %%ebp                  \n\t"
+      // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+      "addb %%cl, -1(%2," PAX ",)  \n\t"
+      "cmpl %%edx, %%eax           \n\t" // check against FullLength
+      "jb paeth_lp2                \n\t"
+
+      RESTORE_r11_r12_r13
+      RESTORE_GOT_ebx
+      RESTORE_ebp
+
+   "paeth_end:                     \n\t"
+      "EMMS                        \n\t" // end MMX; prep for poss. FP instrs.
+
+      : "=c" (dummy_value_c),            // output regs (dummy)
+        "=S" (dummy_value_S),
+        "=D" (dummy_value_D),
+        "=a" (dummy_value_a),
+        "=d" (dummy_value_d)
+
+      : "0" (bpp),         // ecx        // input regs
+        "1" (prev_row),    // esi/rsi
+        "2" (row),         // edi/rdi
+        "3" (MMXLength),   // eax
+        "4" (FullLength)   // edx
+
+      CLOB_COLON_ebx_ebp_r1X             // clobber list
+        CLOBBER_GOT_ebx
+        CLOB_COMMA_ebx_ebp
+        CLOBBER_ebp
+        CLOB_COMMA_ebX_r1X
+        CLOBBER_r11_r12_r13
+   );
+
+} /* end png_read_filter_row_mmx_paeth() */
+
+#endif // PNG_x86_64_USE_GOTPCREL || PNG_THREAD_UNSAFE_OK
+#endif /* PNG_MMX_READ_FILTER_PAETH_SUPPORTED */
+
+
+
+
+#if defined(PNG_MMX_READ_FILTER_SUB_SUPPORTED)
+
+//===========================================================================//
+//                                                                           //
+//           P N G _ R E A D _ F I L T E R _ R O W _ M M X _ S U B           //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Sub filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row)
+{
+   unsigned FullLength, MMXLength;  // png_uint_32 is actually 64-bit on x86-64
+   int bpp;
+   int dummy_value_a;
+   int dummy_value_c;
+   int dummy_value_d;
+   png_bytep dummy_value_D;
+   int diff; //     __attribute__((used));
+
+   bpp = (row_info->pixel_depth + 7) >> 3;  // calc number of bytes per pixel
+   FullLength = row_info->rowbytes - bpp;   // number of bytes to filter
+     // (why do we subtract off bpp?  not so in avg or paeth...)
+
+   __asm__ __volatile__ (
+      SAVE_r15
+      SAVE_ebp
+//pre "movl row, %1                \n\t" // edi/rdi
+      "mov  %1, " PSI "            \n\t" // lp = row
+//pre "movl bpp, %%ecx             \n\t"
+      "add  " PCX ", %1            \n\t" // rp = row + bpp
+//pre "movl FullLength, %%eax      \n\t" // bring in via eax...
+      SAVE_FullLength                    // ...but store for later use
+
+      "xorl %%eax, %%eax           \n\t"
+
+      // get # of bytes to alignment (note:  computing _delta_ of two pointers,
+      // so hereafter %%ebp is sufficient even on 64-bit)
+      "mov  %1, " PBP "            \n\t" // take start of row
+      "add  $0xf, " PBP "          \n\t" // add 7+8 to incr past alignment bdry
+//    "andl $0xfffffff8, %%ebp     \n\t" // mask to alignment boundary (32-bit!)
+      CLEAR_BOTTOM_3_BITS  PBP    "\n\t" // mask to alignment boundary
+      "sub  %1, " PBP "            \n\t" // subtract row ptr again => ebp =
+      "jz sub_go                   \n\t" //  target value of eax at alignment
+
+   "sub_lp1:                       \n\t" // fix alignment
+      "movb (" PSI "," PAX ",), %%cl \n\t"
+      "addb %%cl, (%1," PAX ",)    \n\t"
+      "incl %%eax                  \n\t"
+      "cmpl %%ebp, %%eax           \n\t"
+      "jb sub_lp1                  \n\t"
+
+   "sub_go:                        \n\t"
+      RESTORE_FullLength "%%ecx    \n\t" // FullLength -> ecx
+      "movl %%ecx, %%edx           \n\t"
+      "subl %%eax, %%edx           \n\t" // subtract alignment fix
+      "andl $0x00000007, %%edx     \n\t" // calc bytes over mult of 8
+      "subl %%edx, %%ecx           \n\t" // drop over bytes from length
+//out "movl %%ecx, MMXLength       \n\t"
+      "movl %%ebp, %%eax           \n\t" // ebp = diff, but no reg constraint(?)
+      RESTORE_ebp                        //  (could swap ebp and ecx functions,
+      RESTORE_r15                        //  but %%cl issues...)
+
+      : "=c" (MMXLength),       // 0     // output regs
+        "=D" (dummy_value_D),   // 1
+        "=a" (diff)             // 2
+
+      : "0" (bpp),              // ecx   // input regs
+        "1" (row),              // edi
+        "2" (FullLength)        // eax
+
+      : "%esi", "%edx"                   // clobber list
+        _CLOBBER_r15
+        _CLOBBER_ebp
+   );
+
+   // now do the math for the rest of the row
+   switch (bpp)
+   {
+      case 3:
+      {
+//       _ShiftBpp = 24;       // == 3 * 8
+//       _ShiftRem  = 40;      // == 64 - 24
+
+         __asm__ __volatile__ (
+// preload  "mov  row, %1                 \n\t" // edi/rdi
+            LOAD_GOT_rbp
+            // load (former) _ActiveMask for 2nd active byte group
+            "movq " AMASK2_3_3 ", %%mm7   \n\t" // _amask2_3_3
+            RESTORE_rbp
+
+// notused  "mov  %1, " PSI "             \n\t" // lp = row
+// preload  "movl bpp, %%ecx              \n\t"
+            "add  " PCX ", %1             \n\t" // rp = row + bpp
+            "movq %%mm7, %%mm6            \n\t"
+// preload  "movl diff, %%edx             \n\t"
+            "psllq $24, %%mm6             \n\t" // move mask in mm6 to cover
+                                                //  3rd active byte group
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PDX ",), %%mm1  \n\t"
+
+         "sub_3lp:                        \n\t" // shift data for adding first
+            "psrlq $40, %%mm1             \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            // add 1st active group
+            "movq (%1," PDX ",), %%mm0    \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $24, %%mm1             \n\t" // shift data to pos. correctly
+            "pand %%mm7, %%mm1            \n\t" // mask to use 2nd active group
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 3rd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $24, %%mm1             \n\t" // shift data to pos. correctly
+            "pand %%mm6, %%mm1            \n\t" // mask to use 3rd active group
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            "cmpl %%eax, %%edx            \n\t" // MMXLength
+            "movq %%mm0, -8(%1," PDX ",)  \n\t" // write updated Raws to array
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_3lp                   \n\t"
+
+            : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D),   // 1
+              "=d" (dummy_value_d),   // 2
+              "=a" (dummy_value_a)    // 3
+
+            : "0" (bpp),              // ecx    // input regs
+              "1" (row),              // edi
+              "2" (diff),             // edx
+              "3" (MMXLength)         // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm6", "%mm7"    // clobber list
+#endif
+         );
+      }
+      break;  // end 3 bpp
+
+      case 4:   // formerly shared with 6 bpp case via _ShiftBpp and _ShiftRem,
+      {         // but 64-bit PIC/.so problems (could still share, moving vars
+                // into unused MMX regs via ecx/edx, but kludgy)
+//       _ShiftBpp = bpp << 3;        // 32 (psllq)
+//       _ShiftRem = 64 - _ShiftBpp;  // 32 (psrlq)
+
+         __asm__ __volatile__ (
+// preload  "mov  row, %1                 \n\t" // edi/rdi
+// preload  "movl diff, %%edx             \n\t"
+// notused  "mov  %1, " PSI "             \n\t" // lp = row
+// preload  "movl bpp, %%ecx              \n\t"
+            "add  " PCX ", %1             \n\t" // rp = row + bpp
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PDX ",), %%mm1  \n\t"
+
+         "sub_4lp:                        \n\t" // shift data for adding first
+            "psrlq $32, %%mm1             \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            "movq (%1," PDX ",), %%mm0    \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $32, %%mm1             \n\t" // shift data to pos. correctly
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            "cmpl %%eax, %%edx            \n\t" // MMXLength
+            "movq %%mm0, -8(%1," PDX ",)  \n\t" // write updated Raws to array
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_4lp                   \n\t"
+
+            : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D),   // 1
+              "=d" (dummy_value_d),   // 2
+              "=a" (dummy_value_a)    // 3
+
+            : "0" (bpp),              // ecx    // input regs
+              "1" (row),              // edi
+              "2" (diff),             // edx
+              "3" (MMXLength)         // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1"                    // clobber list
+#endif
+         );
+      }
+      break;  // end 4 bpp
+
+      case 1:
+      {
+         __asm__ __volatile__ (
+// preload  "movl diff, %%edx              \n\t"
+// preload  "mov  row, %1                  \n\t" // edi/rdi
+// preload  "cmpl FullLength, %%edx        \n\t"
+            "cmpl %%eax, %%edx             \n\t"
+            "jnb sub_1end                  \n\t"
+            "mov  %1, " PSI "              \n\t" // lp = row
+// irrel.   "xorl %%ecx, %%ecx             \n\t" // (actually bug with preload)
+// preload  "movl bpp, %%ecx               \n\t"
+            "add  " PCX ", %1              \n\t" // rp = row + bpp
+
+         "sub_1lp:                         \n\t"
+            "movb (" PSI "," PDX ",), %%cl \n\t"
+            "addb %%cl, (%1," PDX ",)      \n\t"
+            "incl %%edx                    \n\t"
+            "cmpl %%eax, %%edx             \n\t" // compare with FullLength
+            "jb sub_1lp                    \n\t"
+
+         "sub_1end:                        \n\t"
+
+            : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D),   // 1
+              "=d" (dummy_value_d),   // 2
+              "=a" (dummy_value_a)    // 3
+
+            : "0" (bpp),              // ecx    // input regs
+              "1" (row),              // edi
+              "2" (diff),             // edx
+              "3" (FullLength)        // eax
+
+            : "%esi"                            // clobber list
+         );
+      }
+      return;  // end 1 bpp (bypassing cleanup block!)
+
+      case 2:
+      {
+//       _ShiftBpp = 16;       // == 2 * 8
+//       _ShiftRem = 48;       // == 64 - 16
+
+         __asm__ __volatile__ (
+            LOAD_GOT_rbp
+            // load (former) _ActiveMask for 2nd active byte group
+            "movq " AMASK4_2_2 ", %%mm7   \n\t" // _amask4_2_2
+            RESTORE_rbp
+// preload  "movl diff, %%edx             \n\t"
+            "movq %%mm7, %%mm6            \n\t"
+// preload  "mov  row, %1                 \n\t" // edi/rdi
+            "psllq $16, %%mm6             \n\t" // move mask in mm6 to cover
+                                                //  3rd active byte group
+// notused  "mov  %1, " PSI "             \n\t" // lp = row
+            "movq %%mm6, %%mm5            \n\t"
+// preload  "movl bpp, %%ecx              \n\t"
+            "add  " PCX ", %1             \n\t" // rp = row + bpp
+            "psllq $16, %%mm5             \n\t" // move mask in mm5 to cover
+                                                //  4th active byte group
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PDX ",), %%mm1  \n\t"
+
+         "sub_2lp:                        \n\t" // shift data for adding first
+            "psrlq $48, %%mm1             \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            // add 1st active group
+            "movq (%1," PDX ",), %%mm0    \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $16, %%mm1             \n\t" // shift data to pos. correctly
+            "pand %%mm7, %%mm1            \n\t" // mask to use 2nd active group
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 3rd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $16, %%mm1             \n\t" // shift data to pos. correctly
+            "pand %%mm6, %%mm1            \n\t" // mask to use 3rd active group
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 4th active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $16, %%mm1             \n\t" // shift data to pos. correctly
+            "pand %%mm5, %%mm1            \n\t" // mask to use 4th active group
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+            "cmpl %%eax, %%edx            \n\t" // MMXLength
+            "movq %%mm0, -8(%1," PDX ",)  \n\t" // write updated Raws to array
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_2lp                   \n\t"
+
+            : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D),   // 1
+              "=d" (dummy_value_d),   // 2
+              "=a" (dummy_value_a)    // 3
+
+            : "0" (bpp),              // ecx    // input regs
+              "1" (row),              // edi
+              "2" (diff),             // edx
+              "3" (MMXLength)         // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1", "%mm5", "%mm6"    // clobber list
+            , "%mm7"
+#endif
+         );
+      }
+      break;  // end 2 bpp
+
+      case 6:   // formerly shared with 4 bpp case (see comments there)
+      {
+//       _ShiftBpp = bpp << 3;        // 48 (psllq)
+//       _ShiftRem = 64 - _ShiftBpp;  // 16 (psrlq)
+
+         __asm__ __volatile__ (
+// preload  "mov  row, %1                 \n\t" // edi/rdi
+// preload  "movl diff, %%edx             \n\t"
+// notused  "mov  %1, " PSI "             \n\t" // lp = row
+// preload  "movl bpp, %%ecx              \n\t"
+            "add  " PCX ", %1             \n\t" // rp = row + bpp
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PDX ",), %%mm1  \n\t"
+
+         "sub_6lp:                        \n\t" // shift data for adding first
+            "psrlq $16, %%mm1             \n\t" //  bpp bytes (no need for mask;
+                                                //  shift clears inactive bytes)
+            "movq (%1," PDX ",), %%mm0    \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            // add 2nd active group
+            "movq %%mm0, %%mm1            \n\t" // mov updated Raws to mm1
+            "psllq $48, %%mm1             \n\t" // shift data to pos. correctly
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm1, %%mm0           \n\t"
+
+            "cmpl %%eax, %%edx            \n\t" // MMXLength
+            "movq %%mm0, -8(%1," PDX ",)  \n\t" // write updated Raws to array
+            "movq %%mm0, %%mm1            \n\t" // prep 1st add at top of loop
+            "jb sub_6lp                   \n\t"
+
+            : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D),   // 1
+              "=d" (dummy_value_d),   // 2
+              "=a" (dummy_value_a)    // 3
+
+            : "0" (bpp),              // ecx    // input regs
+              "1" (row),              // edi
+              "2" (diff),             // edx
+              "3" (MMXLength)         // eax
+
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            : "%mm0", "%mm1"                    // clobber list
+#endif
+         );
+      }
+      break;  // end 6 bpp
+
+      case 8:
+      {
+         __asm__ __volatile__ (
+// preload  "mov  row, %1                 \n\t" // edi/rdi
+// preload  "movl diff, %%edx             \n\t"
+// notused  "mov  %1, " PSI "             \n\t" // lp = row
+// preload  "movl bpp, %%ecx              \n\t"
+            "add  " PCX ", %1             \n\t" // rp = row + bpp
+// preload  "movl MMXLength, %%eax        \n\t"
+
+            // prime the pump:  load the first Raw(x-bpp) data set
+            "movq -8(%1," PDX ",), %%mm7  \n\t"
+            "movl %%eax, %%esi            \n\t" // copy of MMXLength -> esi
+            "andl $0x0000003f, %%esi      \n\t" // calc bytes over mult of 64
+
+         "sub_8lp:                        \n\t"
+            "movq (%1," PDX ",), %%mm0    \n\t" // load Sub(x) for 1st 8 bytes
+            "paddb %%mm7, %%mm0           \n\t"
+            "movq 8(%1," PDX ",), %%mm1   \n\t" // load Sub(x) for 2nd 8 bytes
+            "movq %%mm0, (%1," PDX ",)    \n\t" // write Raw(x) for 1st 8 bytes
+
+            // Now mm0 will be used as Raw(x-bpp) for the 2nd group of 8 bytes.
+            // This will be repeated for each group of 8 bytes with the 8th
+            // group being used as the Raw(x-bpp) for the 1st group of the
+            // next loop.
+
+            "paddb %%mm0, %%mm1           \n\t"
+            "movq 16(%1," PDX ",), %%mm2  \n\t" // load Sub(x) for 3rd 8 bytes
+            "movq %%mm1, 8(%1," PDX ",)   \n\t" // write Raw(x) for 2nd 8 bytes
+            "paddb %%mm1, %%mm2           \n\t"
+            "movq 24(%1," PDX ",), %%mm3  \n\t" // load Sub(x) for 4th 8 bytes
+            "movq %%mm2, 16(%1," PDX ",)  \n\t" // write Raw(x) for 3rd 8 bytes
+            "paddb %%mm2, %%mm3           \n\t"
+            "movq 32(%1," PDX ",), %%mm4  \n\t" // load Sub(x) for 5th 8 bytes
+            "movq %%mm3, 24(%1," PDX ",)  \n\t" // write Raw(x) for 4th 8 bytes
+            "paddb %%mm3, %%mm4           \n\t"
+            "movq 40(%1," PDX ",), %%mm5  \n\t" // load Sub(x) for 6th 8 bytes
+            "movq %%mm4, 32(%1," PDX ",)  \n\t" // write Raw(x) for 5th 8 bytes
+            "paddb %%mm4, %%mm5           \n\t"
+            "movq 48(%1," PDX ",), %%mm6  \n\t" // load Sub(x) for 7th 8 bytes
+            "movq %%mm5, 40(%1," PDX ",)  \n\t" // write Raw(x) for 6th 8 bytes
+            "paddb %%mm5, %%mm6           \n\t"
+            "movq 56(%1," PDX ",), %%mm7  \n\t" // load Sub(x) for 8th 8 bytes
+            "movq %%mm6, 48(%1," PDX ",)  \n\t" // write Raw(x) for 7th 8 bytes
+            "addl $64, %%edx              \n\t"
+            "paddb %%mm6, %%mm7           \n\t"
+            "cmpl %%esi, %%edx            \n\t" // cmp to bytes over mult of 64
+            "movq %%mm7, -8(%1," PDX ",)  \n\t" // write Raw(x) for 8th 8 bytes
+            "jb sub_8lp                   \n\t"
+
+            "cmpl %%eax, %%edx            \n\t" // compare to MMXLength
+            "jnb sub_8lt8                 \n\t"
+
+         "sub_8lpA:                       \n\t"
+            "movq (%1," PDX ",), %%mm0    \n\t"
+            "addl $8, %%edx               \n\t"
+            "paddb %%mm7, %%mm0           \n\t"
+            "cmpl %%eax, %%edx            \n\t" // compare to MMXLength
+            "movq %%mm0, -8(%1," PDX ",)  \n\t" // -8 to offset early addl edx
+            "movq %%mm0, %%mm7            \n\t" // move calculated Raw(x) data
+            "jb sub_8lpA                  \n\t" //  to mm7 to be new Raw(x-bpp)
+                                                //  for next loop
+         "sub_8lt8:                       \n\t"
+
+            : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+              "=D" (dummy_value_D),   // 1
+              "=d" (dummy_value_d),   // 2
+              "=a" (dummy_value_a)    // 3
+
+            : "0" (bpp),              // ecx    // input regs
+              "1" (row),              // edi
+              "2" (diff),             // edx
+              "3" (MMXLength)         // eax
+
+            : "%esi"                            // clobber list
+#if defined(CLOBBER_MMX_REGS_SUPPORTED)
+            , "%mm0", "%mm1", "%mm2", "%mm3"
+            , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+         );
+      }
+      break;  // end 8 bpp
+
+      default:                // bpp != 1,2,3,4,6,8:  doesn't exist
+      {
+         // ERROR:  SHOULD NEVER BE REACHED
+#if defined(PNG_DEBUG)
+         png_debug(1, "Internal libpng logic error (GCC "
+           "png_read_filter_row_mmx_sub())\n");
+#endif
+      }
+      break;
+
+   } // end switch (bpp)
+
+   __asm__ __volatile__ (
+//pre "movl MMXLength, %%eax         \n\t"
+//pre "mov  row, %1                  \n\t" // edi/rdi
+//pre "cmpl FullLength, %%eax        \n\t"
+      "cmpl %%edx, %%eax             \n\t"
+      "jnb sub_end                   \n\t"
+
+      "mov  %1, " PSI "              \n\t" // lp = row
+//pre "movl bpp, %%ecx               \n\t"
+      "add  " PCX ", %1              \n\t" // rp = row + bpp
+      "xorl %%ecx, %%ecx             \n\t"
+
+   "sub_lp2:                         \n\t"
+      "movb (" PSI "," PAX ",), %%cl \n\t"
+      "addb %%cl, (%1," PAX ",)      \n\t"
+      "incl %%eax                    \n\t"
+      "cmpl %%edx, %%eax             \n\t" // FullLength
+      "jb sub_lp2                    \n\t"
+
+   "sub_end:                         \n\t"
+      "EMMS                          \n\t" // end MMX instructions
+
+      : "=c" (dummy_value_c),   // 0      // output regs (dummy)
+        "=D" (dummy_value_D),   // 1
+        "=a" (dummy_value_a),   // 2
+        "=d" (dummy_value_d)    // 3
+
+      : "0" (bpp),              // ecx    // input regs
+        "1" (row),              // edi
+        "2" (MMXLength),        // eax
+        "3" (FullLength)        // edx
+
+      : "%esi"                            // clobber list
+   );
+
+} // end of png_read_filter_row_mmx_sub()
+
+#endif /* PNG_MMX_READ_FILTER_SUB_SUPPORTED */
+
+
+
+
+#if defined(PNG_MMX_READ_FILTER_UP_SUPPORTED)
+
+//===========================================================================//
+//                                                                           //
+//            P N G _ R E A D _ F I L T E R _ R O W _ M M X _ U P            //
+//                                                                           //
+//===========================================================================//
+
+// Optimized code for PNG Up filter decoder
+
+static void /* PRIVATE */
+png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row,
+                           png_bytep prev_row)
+{
+   unsigned len;        // png_uint_32 is actually 64-bit on x86-64
+   int dummy_value_d;   // fix 'forbidden register 3 (dx) was spilled' error
+   png_bytep dummy_value_S;
+   png_bytep dummy_value_D;
+
+   len = row_info->rowbytes;              // number of bytes to filter
+
+   __asm__ __volatile__ (
+      SAVE_GOT_ebx
+//pre "mov  prev_row, %1           \n\t" // esi/rsi
+//pre "movl row, %2                \n\t" // edi/rdi
+
+      "xorl %%ebx, %%ebx           \n\t"
+      "xorl %%eax, %%eax           \n\t"
+
+      // get # of bytes to alignment (note:  computing _delta_ of two pointers,
+      // so hereafter %%ecx is sufficient even on 64-bit)
+      "mov  %2, " PCX "            \n\t" // take start of row
+      "add  $0x7, " PCX "          \n\t" // add 7 to incr past alignment bdry
+//    "andl $0xfffffff8, %%ecx     \n\t" // mask to alignment boundary (32-bit!)
+      CLEAR_BOTTOM_3_BITS  PCX    "\n\t" // mask to alignment boundary
+      "sub  %2, " PCX "            \n\t" // subtract row ptr again => ebp =
+      "jz up_go                    \n\t" //  target value of ecx at alignment
+
+   "up_lp1:                        \n\t" // fix alignment
+      "movb (%2," PBX ",), %%al    \n\t"
+      "addb (%1," PBX ",), %%al    \n\t"
+      "incl %%ebx                  \n\t"
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movb %%al, -1(%2," PBX ",)  \n\t" // mov does not affect flags; -1 to
+      "jb up_lp1                   \n\t" //  offset incl ebx
+
+   "up_go:                         \n\t"
+//pre "movl len, %%edx             \n\t"
+      "movl %%edx, %%ecx           \n\t"
+      "subl %%ebx, %%edx           \n\t" // subtract alignment fix
+      "andl $0x0000003f, %%edx     \n\t" // calc bytes over mult of 64
+      "subl %%edx, %%ecx           \n\t" // sub over-bytes from original length
+
+      // unrolled loop - use all MMX registers and interleave to reduce
+      // number of branch instructions (loops) and reduce partial stalls
+   "up_loop:                       \n\t"
+      "movq (%1," PBX ",), %%mm1   \n\t"
+      "movq (%2," PBX ",), %%mm0   \n\t"
+      "movq 8(%1," PBX ",), %%mm3  \n\t"
+      "paddb %%mm1, %%mm0          \n\t"
+      "movq 8(%2," PBX ",), %%mm2  \n\t"
+      "movq %%mm0, (%2," PBX ",)   \n\t"
+      "paddb %%mm3, %%mm2          \n\t"
+      "movq 16(%1," PBX ",), %%mm5 \n\t"
+      "movq %%mm2, 8(%2," PBX ",)  \n\t"
+      "movq 16(%2," PBX ",), %%mm4 \n\t"
+      "movq 24(%1," PBX ",), %%mm7 \n\t"
+      "paddb %%mm5, %%mm4          \n\t"
+      "movq 24(%2," PBX ",), %%mm6 \n\t"
+      "movq %%mm4, 16(%2," PBX ",) \n\t"
+      "paddb %%mm7, %%mm6          \n\t"
+      "movq 32(%1," PBX ",), %%mm1 \n\t"
+      "movq %%mm6, 24(%2," PBX ",) \n\t"
+      "movq 32(%2," PBX ",), %%mm0 \n\t"
+      "movq 40(%1," PBX ",), %%mm3 \n\t"
+      "paddb %%mm1, %%mm0          \n\t"
+      "movq 40(%2," PBX ",), %%mm2 \n\t"
+      "movq %%mm0, 32(%2," PBX ",) \n\t"
+      "paddb %%mm3, %%mm2          \n\t"
+      "movq 48(%1," PBX ",), %%mm5 \n\t"
+      "movq %%mm2, 40(%2," PBX ",) \n\t"
+      "movq 48(%2," PBX ",), %%mm4 \n\t"
+      "movq 56(%1," PBX ",), %%mm7 \n\t"
+      "paddb %%mm5, %%mm4          \n\t"
+      "movq 56(%2," PBX ",), %%mm6 \n\t"
+      "movq %%mm4, 48(%2," PBX ",) \n\t"
+      "addl $64, %%ebx             \n\t"
+      "paddb %%mm7, %%mm6          \n\t"
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movq %%mm6, -8(%2," PBX ",) \n\t" // (+56)movq does not affect flags;
+      "jb up_loop                  \n\t" //  -8 to offset addl ebx
+
+      "cmpl $0, %%edx              \n\t" // test for bytes over mult of 64
+      "jz up_end                   \n\t"
+
+      "cmpl $8, %%edx              \n\t" // test for less than 8 bytes
+      "jb up_lt8                   \n\t" //  [added by lcreeve at netins.net]
+
+      "addl %%edx, %%ecx           \n\t"
+      "andl $0x00000007, %%edx     \n\t" // calc bytes over mult of 8
+      "subl %%edx, %%ecx           \n\t" // drop over-bytes from length
+      "jz up_lt8                   \n\t"
+
+   "up_lpA:                        \n\t" // use MMX regs to update 8 bytes sim.
+      "movq (%1," PBX ",), %%mm1   \n\t"
+      "movq (%2," PBX ",), %%mm0   \n\t"
+      "addl $8, %%ebx              \n\t"
+      "paddb %%mm1, %%mm0          \n\t"
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movq %%mm0, -8(%2," PBX ",) \n\t" // movq does not affect flags; -8 to
+      "jb up_lpA                   \n\t" //  offset add ebx
+      "cmpl $0, %%edx              \n\t" // test for bytes over mult of 8
+      "jz up_end                   \n\t"
+
+   "up_lt8:                        \n\t"
+      "xorl %%eax, %%eax           \n\t"
+      "addl %%edx, %%ecx           \n\t" // move over byte count into counter
+
+   "up_lp2:                        \n\t" // use x86 regs for remaining bytes
+      "movb (%2," PBX ",), %%al    \n\t"
+      "addb (%1," PBX ",), %%al    \n\t"
+      "incl %%ebx                  \n\t"
+      "cmpl %%ecx, %%ebx           \n\t"
+      "movb %%al, -1(%2," PBX ",)  \n\t" // mov does not affect flags; -1 to
+      "jb up_lp2                   \n\t" //  offset inc ebx
+
+   "up_end:                        \n\t"
+      "EMMS                        \n\t" // conversion of filtered row complete
+      RESTORE_GOT_ebx
+
+      : "=d" (dummy_value_d),   // 0     // output regs (dummy)
+        "=S" (dummy_value_S),   // 1
+        "=D" (dummy_value_D)    // 2
+
+      : "0" (len),              // edx   // input regs
+        "1" (prev_row),         // esi
+        "2" (row)               // edi
+
+      : "%eax", "%ecx"                   // clobber list (no input regs!)
+        _CLOBBER_GOT_ebx
+#if defined(PNG_CLOBBER_MMX_REGS_SUPPORTED)
+      , "%mm0", "%mm1", "%mm2", "%mm3"
+      , "%mm4", "%mm5", "%mm6", "%mm7"
+#endif
+   );
+
+} // end of png_read_filter_row_mmx_up()
+
+#endif /* PNG_MMX_READ_FILTER_UP_SUPPORTED */
+
+
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                   P N G _ R E A D _ F I L T E R _ R O W                   */
+/*                                                                           */
+/*===========================================================================*/
+
+/* Optimized png_read_filter_row routines */
+
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep
+   row, png_bytep prev_row, int filter)
+{
+#if defined(PNG_DEBUG)
+   char filtname[10];
+#endif
+
+   if (_mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+#if defined(PNG_DEBUG)
+   png_debug(1, "in png_read_filter_row (pnggccrd.c)\n");
+   switch (filter)
+   {
+      case 0:
+         png_snprintf(filtname, 10, "none");
+         break;
+
+      case 1:
+         png_snprintf(filtname, 10, "sub-%s",
+#ifdef PNG_MMX_READ_FILTER_SUB_SUPPORTED
+#if !defined(PNG_1_0_X)
+           ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) &&
+            (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+            (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+           _mmx_supported
+#endif
+           ? "MMX" :
+#endif
+           "C");
+         break;
+
+      case 2:
+         png_snprintf(filtname, 10, "up-%s",
+#ifdef PNG_MMX_READ_FILTER_UP_SUPPORTED
+#if !defined(PNG_1_0_X)
+           ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) &&
+            (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+            (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+           _mmx_supported
+#endif
+           ? "MMX" :
+#endif
+           "C");
+         break;
+
+      case 3:
+         png_snprintf(filtname, 10, "avg-%s",
+#ifdef PNG_MMX_READ_FILTER_AVG_SUPPORTED
+#if !defined(PNG_1_0_X)
+           ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) &&
+            (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+            (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+           _mmx_supported
+#endif
+           ? "MMX" :
+#endif
+           "C");
+         break;
+
+      case 4:
+         png_snprintf(filtname, 10, "paeth-%s",
+#ifdef PNG_MMX_READ_FILTER_PAETH_SUPPORTED
+#if defined(PNG_x86_64_USE_GOTPCREL) || defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+           ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) &&
+            (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+            (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+           _mmx_supported
+#endif
+           ? "MMX" :
+#endif /* PNG_x86_64_USE_GOTPCREL || PNG_THREAD_UNSAFE_OK */
+#endif
+           "C");
+         break;
+
+      default:
+         png_snprintf(filtname, 10, "unknown");
+         break;
+   }
+   png_debug2(2, "row_number=%ld, %s, ", png_ptr->row_number, filtname);
+   //png_debug1(0, "png_ptr=%10p, ", png_ptr);
+   //png_debug1(0, "asm_flags=0x%08lx, ", png_ptr->asm_flags);
+   png_debug1(0, "row=%10p, ", row);
+   png_debug2(0, "pixdepth=%d, bytes=%d, ", (int)row_info->pixel_depth,
+      (int)((row_info->pixel_depth + 7) >> 3));
+   png_debug1(0, "rowbytes=%ld\n", row_info->rowbytes);
+#endif /* PNG_DEBUG */
+
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+
+      case PNG_FILTER_VALUE_SUB:
+#ifdef PNG_MMX_READ_FILTER_SUB_SUPPORTED
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_sub(row_info, row);
+         }
+         else
+#endif
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_bytep rp = row + bpp;
+            png_bytep lp = row;
+
+            for (i = bpp; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_sub */
+         break;
+
+      case PNG_FILTER_VALUE_UP:
+#ifdef PNG_MMX_READ_FILTER_UP_SUPPORTED
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_up(row_info, row, prev_row);
+         }
+          else
+#endif
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+
+            for (i = 0; i < istop; ++i)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_up */
+         break;
+
+      case PNG_FILTER_VALUE_AVG:
+#ifdef PNG_MMX_READ_FILTER_AVG_SUPPORTED
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_avg(row_info, row, prev_row);
+         }
+         else
+#endif
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++) >> 1)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++ + *lp++) >> 1)) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_avg */
+         break;
+
+      case PNG_FILTER_VALUE_PAETH:
+#ifdef PNG_MMX_READ_FILTER_PAETH_SUPPORTED
+#if defined(PNG_x86_64_USE_GOTPCREL) || defined(PNG_THREAD_UNSAFE_OK)
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (_mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_paeth(row_info, row, prev_row);
+         }
+         else
+#endif /* PNG_x86_64_USE_GOTPCREL || PNG_THREAD_UNSAFE_OK */
+#endif
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_bytep cp = prev_row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)   /* use leftover rp,pp */
+            {
+               int a, b, c, pa, pb, pc, p;
+
+               a = *lp++;
+               b = *pp++;
+               c = *cp++;
+
+               p = b - c;
+               pc = a - c;
+
+#if defined(PNG_USE_ABS)
+               pa = abs(p);
+               pb = abs(pc);
+               pc = abs(p + pc);
+#else
+               pa = p < 0 ? -p : p;
+               pb = pc < 0 ? -pc : pc;
+               pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+               /*
+                  if (pa <= pb && pa <= pc)
+                     p = a;
+                  else if (pb <= pc)
+                     p = b;
+                  else
+                     p = c;
+                */
+
+               p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
+
+               *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+               rp++;
+            }
+         }  /* end !UseMMX_paeth */
+         break;
+
+      default:
+         png_warning(png_ptr, "Ignoring bad row-filter type");
+         *row=0;
+         break;
+   }
+}
+
+#endif /* PNG_HAVE_MMX_READ_FILTER_ROW */
+
+
+#endif /* PNG_ASSEMBLER_CODE_SUPPORTED && PNG_USE_PNGGCCRD */
+#endif /* __GNUC__ */
diff --git a/distrib/libpng-1.2.19/pngget.c b/distrib/libpng-1.2.19/pngget.c
new file mode 100644
index 0000000..75d2ca0
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngget.c
@@ -0,0 +1,962 @@
+
+/* pngget.c - retrieval of values from info struct
+ *
+ * Last changed in libpng 1.2.15 January 5, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+png_uint_32 PNGAPI
+png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->valid & flag);
+   else
+      return(0);
+}
+
+png_uint_32 PNGAPI
+png_get_rowbytes(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->rowbytes);
+   else
+      return(0);
+}
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+png_bytepp PNGAPI
+png_get_rows(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->row_pointers);
+   else
+      return(0);
+}
+#endif
+
+#ifdef PNG_EASY_ACCESS_SUPPORTED
+/* easy access to info, added in libpng-0.99 */
+png_uint_32 PNGAPI
+png_get_image_width(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->width;
+   }
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_image_height(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->height;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_bit_depth(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->bit_depth;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_color_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->color_type;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_filter_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->filter_type;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_interlace_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->interlace_type;
+   }
+   return (0);
+}
+
+png_byte PNGAPI
+png_get_compression_type(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+   {
+      return info_ptr->compression_type;
+   }
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER)
+          return (0);
+      else return (info_ptr->x_pixels_per_unit);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER)
+          return (0);
+      else return (info_ptr->y_pixels_per_unit);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_uint_32 PNGAPI
+png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter");
+      if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER ||
+         info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit)
+          return (0);
+      else return (info_ptr->x_pixels_per_unit);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+float PNGAPI
+png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr)
+   {
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio");
+      if (info_ptr->x_pixels_per_unit == 0)
+         return ((float)0.0);
+      else
+         return ((float)((float)info_ptr->y_pixels_per_unit
+            /(float)info_ptr->x_pixels_per_unit));
+   }
+#else
+   return (0.0);
+#endif
+   return ((float)0.0);
+}
+#endif
+
+png_int_32 PNGAPI
+png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER)
+          return (0);
+      else return (info_ptr->x_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER)
+          return (0);
+      else return (info_ptr->y_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL)
+          return (0);
+      else return (info_ptr->x_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+png_int_32 PNGAPI
+png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+#if defined(PNG_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+   {
+      png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns");
+      if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL)
+          return (0);
+      else return (info_ptr->y_offset);
+   }
+#else
+   return (0);
+#endif
+   return (0);
+}
+
+#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr)
+     *.0254 +.5));
+}
+
+png_uint_32 PNGAPI
+png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr)
+     *.0254 +.5));
+}
+
+png_uint_32 PNGAPI
+png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr)
+     *.0254 +.5));
+}
+
+float PNGAPI
+png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((float)png_get_x_offset_microns(png_ptr, info_ptr)
+     *.00003937);
+}
+
+float PNGAPI
+png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr)
+{
+   return ((float)png_get_y_offset_microns(png_ptr, info_ptr)
+     *.00003937);
+}
+
+#if defined(PNG_pHYs_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "pHYs");
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+         if(*unit_type == 1)
+         {
+            if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50);
+            if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50);
+         }
+      }
+   }
+   return (retval);
+}
+#endif /* PNG_pHYs_SUPPORTED */
+#endif  /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */
+
+/* png_get_channels really belongs in here, too, but it's been around longer */
+
+#endif  /* PNG_EASY_ACCESS_SUPPORTED */
+
+png_byte PNGAPI
+png_get_channels(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->channels);
+   else
+      return (0);
+}
+
+png_bytep PNGAPI
+png_get_signature(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr != NULL && info_ptr != NULL)
+      return(info_ptr->signature);
+   else
+      return (NULL);
+}
+
+#if defined(PNG_bKGD_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_bKGD(png_structp png_ptr, png_infop info_ptr,
+   png_color_16p *background)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)
+      && background != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "bKGD");
+      *background = &(info_ptr->background);
+      return (PNG_INFO_bKGD);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM(png_structp png_ptr, png_infop info_ptr,
+   double *white_x, double *white_y, double *red_x, double *red_y,
+   double *green_x, double *green_y, double *blue_x, double *blue_y)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+   {
+      png_debug1(1, "in %s retrieval function\n", "cHRM");
+      if (white_x != NULL)
+         *white_x = (double)info_ptr->x_white;
+      if (white_y != NULL)
+         *white_y = (double)info_ptr->y_white;
+      if (red_x != NULL)
+         *red_x = (double)info_ptr->x_red;
+      if (red_y != NULL)
+         *red_y = (double)info_ptr->y_red;
+      if (green_x != NULL)
+         *green_x = (double)info_ptr->x_green;
+      if (green_y != NULL)
+         *green_y = (double)info_ptr->y_green;
+      if (blue_x != NULL)
+         *blue_x = (double)info_ptr->x_blue;
+      if (blue_y != NULL)
+         *blue_y = (double)info_ptr->y_blue;
+      return (PNG_INFO_cHRM);
+   }
+   return (0);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr,
+   png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x,
+   png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y,
+   png_fixed_point *blue_x, png_fixed_point *blue_y)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+   {
+      png_debug1(1, "in %s retrieval function\n", "cHRM");
+      if (white_x != NULL)
+         *white_x = info_ptr->int_x_white;
+      if (white_y != NULL)
+         *white_y = info_ptr->int_y_white;
+      if (red_x != NULL)
+         *red_x = info_ptr->int_x_red;
+      if (red_y != NULL)
+         *red_y = info_ptr->int_y_red;
+      if (green_x != NULL)
+         *green_x = info_ptr->int_x_green;
+      if (green_y != NULL)
+         *green_y = info_ptr->int_y_green;
+      if (blue_x != NULL)
+         *blue_x = info_ptr->int_x_blue;
+      if (blue_y != NULL)
+         *blue_y = info_ptr->int_y_blue;
+      return (PNG_INFO_cHRM);
+   }
+   return (0);
+}
+#endif
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+      && file_gamma != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "gAMA");
+      *file_gamma = (double)info_ptr->gamma;
+      return (PNG_INFO_gAMA);
+   }
+   return (0);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr,
+    png_fixed_point *int_file_gamma)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+      && int_file_gamma != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "gAMA");
+      *int_file_gamma = info_ptr->int_gamma;
+      return (PNG_INFO_gAMA);
+   }
+   return (0);
+}
+#endif
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)
+      && file_srgb_intent != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "sRGB");
+      *file_srgb_intent = (int)info_ptr->srgb_intent;
+      return (PNG_INFO_sRGB);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_iCCP_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_iCCP(png_structp png_ptr, png_infop info_ptr,
+             png_charpp name, int *compression_type,
+             png_charpp profile, png_uint_32 *proflen)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)
+      && name != NULL && profile != NULL && proflen != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "iCCP");
+      *name = info_ptr->iccp_name;
+      *profile = info_ptr->iccp_profile;
+      /* compression_type is a dummy so the API won't have to change
+         if we introduce multiple compression types later. */
+      *proflen = (int)info_ptr->iccp_proflen;
+      *compression_type = (int)info_ptr->iccp_compression;
+      return (PNG_INFO_iCCP);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sPLT(png_structp png_ptr, png_infop info_ptr,
+             png_sPLT_tpp spalettes)
+{
+   if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL)
+   {
+     *spalettes = info_ptr->splt_palettes;
+     return ((png_uint_32)info_ptr->splt_palettes_num);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)
+      && hist != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "hIST");
+      *hist = info_ptr->hist;
+      return (PNG_INFO_hIST);
+   }
+   return (0);
+}
+#endif
+
+png_uint_32 PNGAPI
+png_get_IHDR(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *width, png_uint_32 *height, int *bit_depth,
+   int *color_type, int *interlace_type, int *compression_type,
+   int *filter_type)
+
+{
+   if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL &&
+      bit_depth != NULL && color_type != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "IHDR");
+      *width = info_ptr->width;
+      *height = info_ptr->height;
+      *bit_depth = info_ptr->bit_depth;
+      if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16)
+        png_error(png_ptr, "Invalid bit depth");
+      *color_type = info_ptr->color_type;
+      if (info_ptr->color_type > 6)
+        png_error(png_ptr, "Invalid color type");
+      if (compression_type != NULL)
+         *compression_type = info_ptr->compression_type;
+      if (filter_type != NULL)
+         *filter_type = info_ptr->filter_type;
+      if (interlace_type != NULL)
+         *interlace_type = info_ptr->interlace_type;
+
+      /* check for potential overflow of rowbytes */
+      if (*width == 0 || *width > PNG_UINT_31_MAX)
+        png_error(png_ptr, "Invalid image width");
+      if (*height == 0 || *height > PNG_UINT_31_MAX)
+        png_error(png_ptr, "Invalid image height");
+      if (info_ptr->width > (PNG_UINT_32_MAX
+                 >> 3)      /* 8-byte RGBA pixels */
+                 - 64       /* bigrowbuf hack */
+                 - 1        /* filter byte */
+                 - 7*8      /* rounding of width to multiple of 8 pixels */
+                 - 8)       /* extra max_pixel_depth pad */
+      {
+         png_warning(png_ptr,
+            "Width too large for libpng to process image data.");
+      }
+      return (1);
+   }
+   return (0);
+}
+
+#if defined(PNG_oFFs_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_oFFs(png_structp png_ptr, png_infop info_ptr,
+   png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)
+      && offset_x != NULL && offset_y != NULL && unit_type != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "oFFs");
+      *offset_x = info_ptr->x_offset;
+      *offset_y = info_ptr->y_offset;
+      *unit_type = (int)info_ptr->offset_unit_type;
+      return (PNG_INFO_oFFs);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pCAL(png_structp png_ptr, png_infop info_ptr,
+   png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams,
+   png_charp *units, png_charpp *params)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)
+      && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL &&
+      nparams != NULL && units != NULL && params != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "pCAL");
+      *purpose = info_ptr->pcal_purpose;
+      *X0 = info_ptr->pcal_X0;
+      *X1 = info_ptr->pcal_X1;
+      *type = (int)info_ptr->pcal_type;
+      *nparams = (int)info_ptr->pcal_nparams;
+      *units = info_ptr->pcal_units;
+      *params = info_ptr->pcal_params;
+      return (PNG_INFO_pCAL);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL(png_structp png_ptr, png_infop info_ptr,
+             int *unit, double *width, double *height)
+{
+    if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL))
+    {
+        *unit = info_ptr->scal_unit;
+        *width = info_ptr->scal_pixel_width;
+        *height = info_ptr->scal_pixel_height;
+        return (PNG_INFO_sCAL);
+    }
+    return(0);
+}
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+png_uint_32 PNGAPI
+png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr,
+             int *unit, png_charpp width, png_charpp height)
+{
+    if (png_ptr != NULL && info_ptr != NULL &&
+       (info_ptr->valid & PNG_INFO_sCAL))
+    {
+        *unit = info_ptr->scal_unit;
+        *width = info_ptr->scal_s_width;
+        *height = info_ptr->scal_s_height;
+        return (PNG_INFO_sCAL);
+    }
+    return(0);
+}
+#endif
+#endif
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_pHYs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)
+{
+   png_uint_32 retval = 0;
+
+   if (png_ptr != NULL && info_ptr != NULL &&
+      (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_debug1(1, "in %s retrieval function\n", "pHYs");
+      if (res_x != NULL)
+      {
+         *res_x = info_ptr->x_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (res_y != NULL)
+      {
+         *res_y = info_ptr->y_pixels_per_unit;
+         retval |= PNG_INFO_pHYs;
+      }
+      if (unit_type != NULL)
+      {
+         *unit_type = (int)info_ptr->phys_unit_type;
+         retval |= PNG_INFO_pHYs;
+      }
+   }
+   return (retval);
+}
+#endif
+
+png_uint_32 PNGAPI
+png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette,
+   int *num_palette)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE)
+       && palette != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "PLTE");
+      *palette = info_ptr->palette;
+      *num_palette = info_ptr->num_palette;
+      png_debug1(3, "num_palette = %d\n", *num_palette);
+      return (PNG_INFO_PLTE);
+   }
+   return (0);
+}
+
+#if defined(PNG_sBIT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)
+      && sig_bit != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "sBIT");
+      *sig_bit = &(info_ptr->sig_bit);
+      return (PNG_INFO_sBIT);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr,
+   int *num_text)
+{
+   if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0)
+   {
+      png_debug1(1, "in %s retrieval function\n",
+         (png_ptr->chunk_name[0] == '\0' ? "text"
+             : (png_const_charp)png_ptr->chunk_name));
+      if (text_ptr != NULL)
+         *text_ptr = info_ptr->text;
+      if (num_text != NULL)
+         *num_text = info_ptr->num_text;
+      return ((png_uint_32)info_ptr->num_text);
+   }
+   if (num_text != NULL)
+     *num_text = 0;
+   return(0);
+}
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time)
+{
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)
+       && mod_time != NULL)
+   {
+      png_debug1(1, "in %s retrieval function\n", "tIME");
+      *mod_time = &(info_ptr->mod_time);
+      return (PNG_INFO_tIME);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_tRNS(png_structp png_ptr, png_infop info_ptr,
+   png_bytep *trans, int *num_trans, png_color_16p *trans_values)
+{
+   png_uint_32 retval = 0;
+   if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+   {
+      png_debug1(1, "in %s retrieval function\n", "tRNS");
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+          if (trans != NULL)
+          {
+             *trans = info_ptr->trans;
+             retval |= PNG_INFO_tRNS;
+          }
+          if (trans_values != NULL)
+             *trans_values = &(info_ptr->trans_values);
+      }
+      else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */
+      {
+          if (trans_values != NULL)
+          {
+             *trans_values = &(info_ptr->trans_values);
+             retval |= PNG_INFO_tRNS;
+          }
+          if(trans != NULL)
+             *trans = NULL;
+      }
+      if(num_trans != NULL)
+      {
+         *num_trans = info_ptr->num_trans;
+         retval |= PNG_INFO_tRNS;
+      }
+   }
+   return (retval);
+}
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+png_uint_32 PNGAPI
+png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr,
+             png_unknown_chunkpp unknowns)
+{
+   if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL)
+   {
+     *unknowns = info_ptr->unknown_chunks;
+     return ((png_uint_32)info_ptr->unknown_chunks_num);
+   }
+   return (0);
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+png_byte PNGAPI
+png_get_rgb_to_gray_status (png_structp png_ptr)
+{
+   return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0);
+}
+#endif
+
+#if defined(PNG_USER_CHUNKS_SUPPORTED)
+png_voidp PNGAPI
+png_get_user_chunk_ptr(png_structp png_ptr)
+{
+   return (png_ptr? png_ptr->user_chunk_ptr : NULL);
+}
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+png_uint_32 PNGAPI
+png_get_compression_buffer_size(png_structp png_ptr)
+{
+   return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L);
+}
+#endif
+
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+#ifndef PNG_1_0_X
+/* this function was added to libpng 1.2.0 and should exist by default */
+png_uint_32 PNGAPI
+png_get_asm_flags (png_structp png_ptr)
+{
+#ifdef PNG_MMX_CODE_SUPPORTED
+    return (png_uint_32)(png_ptr? png_ptr->asm_flags : 0L);
+#else
+    return (png_ptr? 0L: 0L);
+#endif
+}
+
+/* this function was added to libpng 1.2.0 and should exist by default */
+png_uint_32 PNGAPI
+png_get_asm_flagmask (int flag_select)
+{
+#ifdef PNG_MMX_CODE_SUPPORTED
+    png_uint_32 settable_asm_flags = 0;
+
+    if (flag_select & PNG_SELECT_READ)
+        settable_asm_flags |=
+          PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  |
+          PNG_ASM_FLAG_MMX_READ_INTERLACE    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_SUB   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_UP    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_AVG   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+          /* no non-MMX flags yet */
+
+#if 0
+    /* GRR:  no write-flags yet, either, but someday... */
+    if (flag_select & PNG_SELECT_WRITE)
+        settable_asm_flags |=
+          PNG_ASM_FLAG_MMX_WRITE_ [whatever] ;
+#endif /* 0 */
+
+    return settable_asm_flags;  /* _theoretically_ settable capabilities only */
+#else
+    return (0L);
+#endif /* PNG_MMX_CODE_SUPPORTED */
+}
+
+
+    /* GRR:  could add this:   && defined(PNG_MMX_CODE_SUPPORTED) */
+/* this function was added to libpng 1.2.0 */
+png_uint_32 PNGAPI
+png_get_mmx_flagmask (int flag_select, int *compilerID)
+{
+#if defined(PNG_MMX_CODE_SUPPORTED)
+    png_uint_32 settable_mmx_flags = 0;
+
+    if (flag_select & PNG_SELECT_READ)
+        settable_mmx_flags |=
+          PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  |
+          PNG_ASM_FLAG_MMX_READ_INTERLACE    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_SUB   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_UP    |
+          PNG_ASM_FLAG_MMX_READ_FILTER_AVG   |
+          PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ;
+#if 0
+    /* GRR:  no MMX write support yet, but someday... */
+    if (flag_select & PNG_SELECT_WRITE)
+        settable_mmx_flags |=
+          PNG_ASM_FLAG_MMX_WRITE_ [whatever] ;
+#endif /* 0 */
+
+    if (compilerID != NULL) {
+#ifdef PNG_USE_PNGVCRD
+        *compilerID = 1;    /* MSVC */
+#else
+#ifdef PNG_USE_PNGGCCRD
+        *compilerID = 2;    /* gcc/gas */
+#else
+        *compilerID = -1;   /* unknown (i.e., no asm/MMX code compiled) */
+#endif
+#endif
+    }
+
+    return settable_mmx_flags;  /* _theoretically_ settable capabilities only */
+#else
+    return (0L);
+#endif /* ?PNG_MMX_CODE_SUPPORTED */
+}
+
+/* this function was added to libpng 1.2.0 */
+png_byte PNGAPI
+png_get_mmx_bitdepth_threshold (png_structp png_ptr)
+{
+#if defined(PNG_MMX_CODE_SUPPORTED)
+    return (png_byte)(png_ptr? png_ptr->mmx_bitdepth_threshold : 0);
+#else
+    return (png_ptr? 0: 0);
+#endif /* ?PNG_MMX_CODE_SUPPORTED */
+}
+
+/* this function was added to libpng 1.2.0 */
+png_uint_32 PNGAPI
+png_get_mmx_rowbytes_threshold (png_structp png_ptr)
+{
+#if defined(PNG_MMX_CODE_SUPPORTED)
+    return (png_uint_32)(png_ptr? png_ptr->mmx_rowbytes_threshold : 0L);
+#else
+    return (png_ptr? 0L: 0L);
+#endif /* ?PNG_MMX_CODE_SUPPORTED */
+}
+#endif /* ?PNG_1_0_X */
+#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* these functions were added to libpng 1.2.6 */
+png_uint_32 PNGAPI
+png_get_user_width_max (png_structp png_ptr)
+{
+    return (png_ptr? png_ptr->user_width_max : 0);
+}
+png_uint_32 PNGAPI
+png_get_user_height_max (png_structp png_ptr)
+{
+    return (png_ptr? png_ptr->user_height_max : 0);
+}
+#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+ 
+
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngmem.c b/distrib/libpng-1.2.19/pngmem.c
new file mode 100644
index 0000000..248060f
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngmem.c
@@ -0,0 +1,608 @@
+
+/* pngmem.c - stub functions for memory allocation
+ *
+ * Last changed in libpng 1.2.13 November 13, 2006
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2006 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all memory allocation.  Users who
+ * need special memory handling are expected to supply replacement
+ * functions for png_malloc() and png_free(), and to use
+ * png_create_read_struct_2() and png_create_write_struct_2() to
+ * identify the replacement functions.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+/* Borland DOS special memory handler */
+#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__)
+/* if you change this, be sure to change the one in png.h also */
+
+/* Allocate memory for a png_struct.  The malloc and memset can be replaced
+   by a single call to calloc() if this is thought to improve performance. */
+png_voidp /* PRIVATE */
+png_create_struct(int type)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL));
+}
+
+/* Alternate version of png_create_struct, for use with user-defined malloc. */
+png_voidp /* PRIVATE */
+png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_size_t size;
+   png_voidp struct_ptr;
+
+   if (type == PNG_STRUCT_INFO)
+     size = png_sizeof(png_info);
+   else if (type == PNG_STRUCT_PNG)
+     size = png_sizeof(png_struct);
+   else
+     return (png_get_copyright(NULL));
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(malloc_fn != NULL)
+   {
+      png_struct dummy_struct;
+      png_structp png_ptr = &dummy_struct;
+      png_ptr->mem_ptr=mem_ptr;
+      struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size);
+   }
+   else
+#endif /* PNG_USER_MEM_SUPPORTED */
+      struct_ptr = (png_voidp)farmalloc(size);
+   if (struct_ptr != NULL)
+      png_memset(struct_ptr, 0, size);
+   return (struct_ptr);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct(png_voidp struct_ptr)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn,
+    png_voidp mem_ptr)
+{
+#endif
+   if (struct_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if(free_fn != NULL)
+      {
+         png_struct dummy_struct;
+         png_structp png_ptr = &dummy_struct;
+         png_ptr->mem_ptr=mem_ptr;
+         (*(free_fn))(png_ptr, struct_ptr);
+         return;
+      }
+#endif /* PNG_USER_MEM_SUPPORTED */
+      farfree (struct_ptr);
+   }
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+ * 64K.  However, zlib may allocate more then 64K if you don't tell
+ * it not to.  See zconf.h and png.h for more information. zlib does
+ * need to allocate exactly 64K, so whatever you call here must
+ * have the ability to do that.
+ *
+ * Borland seems to have a problem in DOS mode for exactly 64K.
+ * It gives you a segment with an offset of 8 (perhaps to store its
+ * memory stuff).  zlib doesn't like this at all, so we have to
+ * detect and deal with it.  This code should not be needed in
+ * Windows or OS/2 modes, and only in 16 bit mode.  This code has
+ * been updated by Alexander Lehmann for version 0.89 to waste less
+ * memory.
+ *
+ * Note that we can't use png_size_t for the "size" declaration,
+ * since on some systems a png_size_t is a 16-bit quantity, and as a
+ * result, we would be truncating potentially larger memory requests
+ * (which should cause a fatal error) and introducing major problems.
+ */
+
+png_voidp PNGAPI
+png_malloc(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(png_ptr->malloc_fn != NULL)
+       ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size));
+   else
+       ret = (png_malloc_default(png_ptr, size));
+   if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+       png_error(png_ptr, "Out of memory!");
+   return (ret);
+}
+
+png_voidp PNGAPI
+png_malloc_default(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+   {
+      png_warning(png_ptr, "Cannot Allocate > 64K");
+      ret = NULL;
+   }
+   else
+#endif
+
+   if (size != (size_t)size)
+     ret = NULL;
+   else if (size == (png_uint_32)65536L)
+   {
+      if (png_ptr->offset_table == NULL)
+      {
+         /* try to see if we need to do any of this fancy stuff */
+         ret = farmalloc(size);
+         if (ret == NULL || ((png_size_t)ret & 0xffff))
+         {
+            int num_blocks;
+            png_uint_32 total_size;
+            png_bytep table;
+            int i;
+            png_byte huge * hptr;
+
+            if (ret != NULL)
+            {
+               farfree(ret);
+               ret = NULL;
+            }
+
+            if(png_ptr->zlib_window_bits > 14)
+               num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14));
+            else
+               num_blocks = 1;
+            if (png_ptr->zlib_mem_level >= 7)
+               num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7));
+            else
+               num_blocks++;
+
+            total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16;
+
+            table = farmalloc(total_size);
+
+            if (table == NULL)
+            {
+#ifndef PNG_USER_MEM_SUPPORTED
+               if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+                  png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */
+               else
+                  png_warning(png_ptr, "Out Of Memory.");
+#endif
+               return (NULL);
+            }
+
+            if ((png_size_t)table & 0xfff0)
+            {
+#ifndef PNG_USER_MEM_SUPPORTED
+               if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+                  png_error(png_ptr,
+                    "Farmalloc didn't return normalized pointer");
+               else
+                  png_warning(png_ptr,
+                    "Farmalloc didn't return normalized pointer");
+#endif
+               return (NULL);
+            }
+
+            png_ptr->offset_table = table;
+            png_ptr->offset_table_ptr = farmalloc(num_blocks *
+               png_sizeof (png_bytep));
+
+            if (png_ptr->offset_table_ptr == NULL)
+            {
+#ifndef PNG_USER_MEM_SUPPORTED
+               if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+                  png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */
+               else
+                  png_warning(png_ptr, "Out Of memory.");
+#endif
+               return (NULL);
+            }
+
+            hptr = (png_byte huge *)table;
+            if ((png_size_t)hptr & 0xf)
+            {
+               hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L);
+               hptr = hptr + 16L;  /* "hptr += 16L" fails on Turbo C++ 3.0 */
+            }
+            for (i = 0; i < num_blocks; i++)
+            {
+               png_ptr->offset_table_ptr[i] = (png_bytep)hptr;
+               hptr = hptr + (png_uint_32)65536L;  /* "+=" fails on TC++3.0 */
+            }
+
+            png_ptr->offset_table_number = num_blocks;
+            png_ptr->offset_table_count = 0;
+            png_ptr->offset_table_count_free = 0;
+         }
+      }
+
+      if (png_ptr->offset_table_count >= png_ptr->offset_table_number)
+      {
+#ifndef PNG_USER_MEM_SUPPORTED
+         if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+            png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */
+         else
+            png_warning(png_ptr, "Out of Memory.");
+#endif
+         return (NULL);
+      }
+
+      ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++];
+   }
+   else
+      ret = farmalloc(size);
+
+#ifndef PNG_USER_MEM_SUPPORTED
+   if (ret == NULL)
+   {
+      if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+         png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */
+      else
+         png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */
+   }
+#endif
+
+   return (ret);
+}
+
+/* free a pointer allocated by png_malloc().  In the default
+   configuration, png_ptr is not used, but is passed in case it
+   is needed.  If ptr is NULL, return without taking any action. */
+void PNGAPI
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+   {
+      (*(png_ptr->free_fn))(png_ptr, ptr);
+      return;
+   }
+   else png_free_default(png_ptr, ptr);
+}
+
+void PNGAPI
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   if(png_ptr == NULL) return;
+
+   if (png_ptr->offset_table != NULL)
+   {
+      int i;
+
+      for (i = 0; i < png_ptr->offset_table_count; i++)
+      {
+         if (ptr == png_ptr->offset_table_ptr[i])
+         {
+            ptr = NULL;
+            png_ptr->offset_table_count_free++;
+            break;
+         }
+      }
+      if (png_ptr->offset_table_count_free == png_ptr->offset_table_count)
+      {
+         farfree(png_ptr->offset_table);
+         farfree(png_ptr->offset_table_ptr);
+         png_ptr->offset_table = NULL;
+         png_ptr->offset_table_ptr = NULL;
+      }
+   }
+
+   if (ptr != NULL)
+   {
+      farfree(ptr);
+   }
+}
+
+#else /* Not the Borland DOS special memory handler */
+
+/* Allocate memory for a png_struct or a png_info.  The malloc and
+   memset can be replaced by a single call to calloc() if this is thought
+   to improve performance noticably. */
+png_voidp /* PRIVATE */
+png_create_struct(int type)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL));
+}
+
+/* Allocate memory for a png_struct or a png_info.  The malloc and
+   memset can be replaced by a single call to calloc() if this is thought
+   to improve performance noticably. */
+png_voidp /* PRIVATE */
+png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_size_t size;
+   png_voidp struct_ptr;
+
+   if (type == PNG_STRUCT_INFO)
+      size = png_sizeof(png_info);
+   else if (type == PNG_STRUCT_PNG)
+      size = png_sizeof(png_struct);
+   else
+      return (NULL);
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if(malloc_fn != NULL)
+   {
+      png_struct dummy_struct;
+      png_structp png_ptr = &dummy_struct;
+      png_ptr->mem_ptr=mem_ptr;
+      struct_ptr = (*(malloc_fn))(png_ptr, size);
+      if (struct_ptr != NULL)
+         png_memset(struct_ptr, 0, size);
+      return (struct_ptr);
+   }
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   struct_ptr = (png_voidp)farmalloc(size);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   struct_ptr = (png_voidp)halloc(size,1);
+# else
+   struct_ptr = (png_voidp)malloc(size);
+# endif
+#endif
+   if (struct_ptr != NULL)
+      png_memset(struct_ptr, 0, size);
+
+   return (struct_ptr);
+}
+
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct(png_voidp struct_ptr)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL);
+}
+
+/* Free memory allocated by a png_create_struct() call */
+void /* PRIVATE */
+png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn,
+    png_voidp mem_ptr)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   if (struct_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      if(free_fn != NULL)
+      {
+         png_struct dummy_struct;
+         png_structp png_ptr = &dummy_struct;
+         png_ptr->mem_ptr=mem_ptr;
+         (*(free_fn))(png_ptr, struct_ptr);
+         return;
+      }
+#endif /* PNG_USER_MEM_SUPPORTED */
+#if defined(__TURBOC__) && !defined(__FLAT__)
+      farfree(struct_ptr);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+      hfree(struct_ptr);
+# else
+      free(struct_ptr);
+# endif
+#endif
+   }
+}
+
+/* Allocate memory.  For reasonable files, size should never exceed
+   64K.  However, zlib may allocate more then 64K if you don't tell
+   it not to.  See zconf.h and png.h for more information.  zlib does
+   need to allocate exactly 64K, so whatever you call here must
+   have the ability to do that. */
+
+png_voidp PNGAPI
+png_malloc(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+   if(png_ptr->malloc_fn != NULL)
+       ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size));
+   else
+       ret = (png_malloc_default(png_ptr, size));
+   if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+       png_error(png_ptr, "Out of Memory!");
+   return (ret);
+}
+
+png_voidp PNGAPI
+png_malloc_default(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ret;
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   if (png_ptr == NULL || size == 0)
+      return (NULL);
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (size > (png_uint_32)65536L)
+   {
+#ifndef PNG_USER_MEM_SUPPORTED
+      if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+         png_error(png_ptr, "Cannot Allocate > 64K");
+      else
+#endif
+         return NULL;
+   }
+#endif
+
+ /* Check for overflow */
+#if defined(__TURBOC__) && !defined(__FLAT__)
+ if (size != (unsigned long)size)
+   ret = NULL;
+ else
+   ret = farmalloc(size);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+ if (size != (unsigned long)size)
+   ret = NULL;
+ else
+   ret = halloc(size, 1);
+# else
+ if (size != (size_t)size)
+   ret = NULL;
+ else
+   ret = malloc((size_t)size);
+# endif
+#endif
+
+#ifndef PNG_USER_MEM_SUPPORTED
+   if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0)
+      png_error(png_ptr, "Out of Memory");
+#endif
+
+   return (ret);
+}
+
+/* Free a pointer allocated by png_malloc().  If ptr is NULL, return
+   without taking any action. */
+void PNGAPI
+png_free(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   if (png_ptr->free_fn != NULL)
+   {
+      (*(png_ptr->free_fn))(png_ptr, ptr);
+      return;
+   }
+   else png_free_default(png_ptr, ptr);
+}
+void PNGAPI
+png_free_default(png_structp png_ptr, png_voidp ptr)
+{
+   if (png_ptr == NULL || ptr == NULL)
+      return;
+
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+#if defined(__TURBOC__) && !defined(__FLAT__)
+   farfree(ptr);
+#else
+# if defined(_MSC_VER) && defined(MAXSEG_64K)
+   hfree(ptr);
+# else
+   free(ptr);
+# endif
+#endif
+}
+
+#endif /* Not Borland DOS special memory handler */
+
+#if defined(PNG_1_0_X)
+#  define png_malloc_warn png_malloc
+#else
+/* This function was added at libpng version 1.2.3.  The png_malloc_warn()
+ * function will set up png_malloc() to issue a png_warning and return NULL
+ * instead of issuing a png_error, if it fails to allocate the requested
+ * memory.
+ */
+png_voidp PNGAPI
+png_malloc_warn(png_structp png_ptr, png_uint_32 size)
+{
+   png_voidp ptr;
+   png_uint_32 save_flags;
+   if(png_ptr == NULL) return (NULL);
+
+   save_flags=png_ptr->flags;
+   png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK;
+   ptr = (png_voidp)png_malloc((png_structp)png_ptr, size);
+   png_ptr->flags=save_flags;
+   return(ptr);
+}
+#endif
+
+png_voidp PNGAPI
+png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2,
+   png_uint_32 length)
+{
+   png_size_t size;
+
+   size = (png_size_t)length;
+   if ((png_uint_32)size != length)
+      png_error(png_ptr,"Overflow in png_memcpy_check.");
+
+   return(png_memcpy (s1, s2, size));
+}
+
+png_voidp PNGAPI
+png_memset_check (png_structp png_ptr, png_voidp s1, int value,
+   png_uint_32 length)
+{
+   png_size_t size;
+
+   size = (png_size_t)length;
+   if ((png_uint_32)size != length)
+      png_error(png_ptr,"Overflow in png_memset_check.");
+
+   return (png_memset (s1, value, size));
+
+}
+
+#ifdef PNG_USER_MEM_SUPPORTED
+/* This function is called when the application wants to use another method
+ * of allocating and freeing memory.
+ */
+void PNGAPI
+png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr
+  malloc_fn, png_free_ptr free_fn)
+{
+   if(png_ptr != NULL) {
+   png_ptr->mem_ptr = mem_ptr;
+   png_ptr->malloc_fn = malloc_fn;
+   png_ptr->free_fn = free_fn;
+   }
+}
+
+/* This function returns a pointer to the mem_ptr associated with the user
+ * functions.  The application should free any memory associated with this
+ * pointer before png_write_destroy and png_read_destroy are called.
+ */
+png_voidp PNGAPI
+png_get_mem_ptr(png_structp png_ptr)
+{
+   if(png_ptr == NULL) return (NULL);
+   return ((png_voidp)png_ptr->mem_ptr);
+}
+#endif /* PNG_USER_MEM_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngpread.c b/distrib/libpng-1.2.19/pngpread.c
new file mode 100644
index 0000000..7d29e98
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngpread.c
@@ -0,0 +1,1585 @@
+
+/* pngpread.c - read a png file in push mode
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+
+/* push model modes */
+#define PNG_READ_SIG_MODE   0
+#define PNG_READ_CHUNK_MODE 1
+#define PNG_READ_IDAT_MODE  2
+#define PNG_SKIP_MODE       3
+#define PNG_READ_tEXt_MODE  4
+#define PNG_READ_zTXt_MODE  5
+#define PNG_READ_DONE_MODE  6
+#define PNG_READ_iTXt_MODE  7
+#define PNG_ERROR_MODE      8
+
+void PNGAPI
+png_process_data(png_structp png_ptr, png_infop info_ptr,
+   png_bytep buffer, png_size_t buffer_size)
+{
+   if(png_ptr == NULL) return;
+   png_push_restore_buffer(png_ptr, buffer, buffer_size);
+
+   while (png_ptr->buffer_size)
+   {
+      png_process_some_data(png_ptr, info_ptr);
+   }
+}
+
+/* What we do with the incoming data depends on what we were previously
+ * doing before we ran out of data...
+ */
+void /* PRIVATE */
+png_process_some_data(png_structp png_ptr, png_infop info_ptr)
+{
+   if(png_ptr == NULL) return;
+   switch (png_ptr->process_mode)
+   {
+      case PNG_READ_SIG_MODE:
+      {
+         png_push_read_sig(png_ptr, info_ptr);
+         break;
+      }
+      case PNG_READ_CHUNK_MODE:
+      {
+         png_push_read_chunk(png_ptr, info_ptr);
+         break;
+      }
+      case PNG_READ_IDAT_MODE:
+      {
+         png_push_read_IDAT(png_ptr);
+         break;
+      }
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      case PNG_READ_tEXt_MODE:
+      {
+         png_push_read_tEXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      case PNG_READ_zTXt_MODE:
+      {
+         png_push_read_zTXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      case PNG_READ_iTXt_MODE:
+      {
+         png_push_read_iTXt(png_ptr, info_ptr);
+         break;
+      }
+#endif
+      case PNG_SKIP_MODE:
+      {
+         png_push_crc_finish(png_ptr);
+         break;
+      }
+      default:
+      {
+         png_ptr->buffer_size = 0;
+         break;
+      }
+   }
+}
+
+/* Read any remaining signature bytes from the stream and compare them with
+ * the correct PNG signature.  It is possible that this routine is called
+ * with bytes already read from the signature, either because they have been
+ * checked by the calling application, or because of multiple calls to this
+ * routine.
+ */
+void /* PRIVATE */
+png_push_read_sig(png_structp png_ptr, png_infop info_ptr)
+{
+   png_size_t num_checked = png_ptr->sig_bytes,
+             num_to_check = 8 - num_checked;
+
+   if (png_ptr->buffer_size < num_to_check)
+   {
+      num_to_check = png_ptr->buffer_size;
+   }
+
+   png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]),
+      num_to_check);
+   png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check);
+
+   if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+   {
+      if (num_checked < 4 &&
+          png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+         png_error(png_ptr, "Not a PNG file");
+      else
+         png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+   }
+   else
+   {
+      if (png_ptr->sig_bytes >= 8)
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+      }
+   }
+}
+
+void /* PRIVATE */
+png_push_read_chunk(png_structp png_ptr, png_infop info_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_CONST PNG_IHDR;
+      PNG_CONST PNG_IDAT;
+      PNG_CONST PNG_IEND;
+      PNG_CONST PNG_PLTE;
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      PNG_CONST PNG_bKGD;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      PNG_CONST PNG_cHRM;
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      PNG_CONST PNG_gAMA;
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      PNG_CONST PNG_hIST;
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      PNG_CONST PNG_iCCP;
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      PNG_CONST PNG_iTXt;
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      PNG_CONST PNG_oFFs;
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      PNG_CONST PNG_pCAL;
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      PNG_CONST PNG_pHYs;
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      PNG_CONST PNG_sBIT;
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      PNG_CONST PNG_sCAL;
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      PNG_CONST PNG_sRGB;
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      PNG_CONST PNG_sPLT;
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      PNG_CONST PNG_tEXt;
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      PNG_CONST PNG_tIME;
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      PNG_CONST PNG_tRNS;
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      PNG_CONST PNG_zTXt;
+#endif
+#endif /* PNG_USE_LOCAL_ARRAYS */
+   /* First we make sure we have enough data for the 4 byte chunk name
+    * and the 4 byte chunk length before proceeding with decoding the
+    * chunk data.  To fully decode each of these chunks, we also make
+    * sure we have enough data in the buffer for the 4 byte CRC at the
+    * end of every chunk (except IDAT, which is handled separately).
+    */
+   if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+   {
+      png_byte chunk_length[4];
+
+      if (png_ptr->buffer_size < 8)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+   }
+
+   if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+     if(png_ptr->mode & PNG_AFTER_IDAT)
+        png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
+
+   if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length);
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length);
+
+      png_ptr->process_mode = PNG_READ_DONE_MODE;
+      png_push_have_end(png_ptr, info_ptr);
+   }
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+   else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+         png_ptr->mode |= PNG_HAVE_IDAT;
+      png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+      if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_ptr->mode |= PNG_HAVE_PLTE;
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         if (!(png_ptr->mode & PNG_HAVE_IHDR))
+            png_error(png_ptr, "Missing IHDR before IDAT");
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                  !(png_ptr->mode & PNG_HAVE_PLTE))
+            png_error(png_ptr, "Missing PLTE before IDAT");
+      }
+   }
+#endif
+   else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length);
+   }
+   else if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+   {
+      /* If we reach an IDAT chunk, this means we have read all of the
+       * header chunks, and we can start reading the image (or if this
+       * is called after the image has been read - we have an error).
+       */
+     if (!(png_ptr->mode & PNG_HAVE_IHDR))
+       png_error(png_ptr, "Missing IHDR before IDAT");
+     else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+         !(png_ptr->mode & PNG_HAVE_PLTE))
+       png_error(png_ptr, "Missing PLTE before IDAT");
+
+      if (png_ptr->mode & PNG_HAVE_IDAT)
+      {
+         if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
+           if (png_ptr->push_length == 0)
+              return;
+
+         if (png_ptr->mode & PNG_AFTER_IDAT)
+            png_error(png_ptr, "Too many IDAT's found");
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+      png_ptr->mode |= PNG_HAVE_IDAT;
+      png_ptr->process_mode = PNG_READ_IDAT_MODE;
+      png_push_have_info(png_ptr, info_ptr);
+      png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+      png_ptr->zstream.next_out = png_ptr->row_buf;
+      return;
+   }
+#if defined(PNG_READ_gAMA_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_bKGD_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+   else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length);
+   }
+#endif
+   else
+   {
+      if (png_ptr->push_length + 4 > png_ptr->buffer_size)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+      png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length);
+   }
+
+   png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+}
+
+void /* PRIVATE */
+png_push_crc_skip(png_structp png_ptr, png_uint_32 skip)
+{
+   png_ptr->process_mode = PNG_SKIP_MODE;
+   png_ptr->skip_length = skip;
+}
+
+void /* PRIVATE */
+png_push_crc_finish(png_structp png_ptr)
+{
+   if (png_ptr->skip_length && png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size)
+         save_size = (png_size_t)png_ptr->skip_length;
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+
+      png_ptr->skip_length -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (png_ptr->skip_length && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size)
+         save_size = (png_size_t)png_ptr->skip_length;
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->skip_length -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+   if (!png_ptr->skip_length)
+   {
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_crc_finish(png_ptr, 0);
+      png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+   }
+}
+
+void PNGAPI
+png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length)
+{
+   png_bytep ptr;
+
+   if(png_ptr == NULL) return;
+   ptr = buffer;
+   if (png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->save_buffer_size)
+         save_size = length;
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size);
+      length -= save_size;
+      ptr += save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (length && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (length < png_ptr->current_buffer_size)
+         save_size = length;
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size);
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+}
+
+void /* PRIVATE */
+png_push_save_buffer(png_structp png_ptr)
+{
+   if (png_ptr->save_buffer_size)
+   {
+      if (png_ptr->save_buffer_ptr != png_ptr->save_buffer)
+      {
+         png_size_t i,istop;
+         png_bytep sp;
+         png_bytep dp;
+
+         istop = png_ptr->save_buffer_size;
+         for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer;
+            i < istop; i++, sp++, dp++)
+         {
+            *dp = *sp;
+         }
+      }
+   }
+   if (png_ptr->save_buffer_size + png_ptr->current_buffer_size >
+      png_ptr->save_buffer_max)
+   {
+      png_size_t new_max;
+      png_bytep old_buffer;
+
+      if (png_ptr->save_buffer_size > PNG_SIZE_MAX -
+         (png_ptr->current_buffer_size + 256))
+      {
+        png_error(png_ptr, "Potential overflow of save_buffer");
+      }
+      new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256;
+      old_buffer = png_ptr->save_buffer;
+      png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)new_max);
+      png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size);
+      png_free(png_ptr, old_buffer);
+      png_ptr->save_buffer_max = new_max;
+   }
+   if (png_ptr->current_buffer_size)
+   {
+      png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size,
+         png_ptr->current_buffer_ptr, png_ptr->current_buffer_size);
+      png_ptr->save_buffer_size += png_ptr->current_buffer_size;
+      png_ptr->current_buffer_size = 0;
+   }
+   png_ptr->save_buffer_ptr = png_ptr->save_buffer;
+   png_ptr->buffer_size = 0;
+}
+
+void /* PRIVATE */
+png_push_restore_buffer(png_structp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   png_ptr->current_buffer = buffer;
+   png_ptr->current_buffer_size = buffer_length;
+   png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size;
+   png_ptr->current_buffer_ptr = png_ptr->current_buffer;
+}
+
+void /* PRIVATE */
+png_push_read_IDAT(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_CONST PNG_IDAT;
+#endif
+   if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER))
+   {
+      png_byte chunk_length[4];
+
+      if (png_ptr->buffer_size < 8)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_fill_buffer(png_ptr, chunk_length, 4);
+      png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length);
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+      png_ptr->mode |= PNG_HAVE_CHUNK_HEADER;
+
+      if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+      {
+         png_ptr->process_mode = PNG_READ_CHUNK_MODE;
+         if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+            png_error(png_ptr, "Not enough compressed data");
+         return;
+      }
+
+      png_ptr->idat_size = png_ptr->push_length;
+   }
+   if (png_ptr->idat_size && png_ptr->save_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size)
+      {
+         save_size = (png_size_t)png_ptr->idat_size;
+         /* check for overflow */
+         if((png_uint_32)save_size != png_ptr->idat_size)
+            png_error(png_ptr, "save_size overflowed in pngpread");
+      }
+      else
+         save_size = png_ptr->save_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size);
+      if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+         png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size);
+      png_ptr->idat_size -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->save_buffer_size -= save_size;
+      png_ptr->save_buffer_ptr += save_size;
+   }
+   if (png_ptr->idat_size && png_ptr->current_buffer_size)
+   {
+      png_size_t save_size;
+
+      if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size)
+      {
+         save_size = (png_size_t)png_ptr->idat_size;
+         /* check for overflow */
+         if((png_uint_32)save_size != png_ptr->idat_size)
+            png_error(png_ptr, "save_size overflowed in pngpread");
+      }
+      else
+         save_size = png_ptr->current_buffer_size;
+
+      png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size);
+      if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+        png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size);
+
+      png_ptr->idat_size -= save_size;
+      png_ptr->buffer_size -= save_size;
+      png_ptr->current_buffer_size -= save_size;
+      png_ptr->current_buffer_ptr += save_size;
+   }
+   if (!png_ptr->idat_size)
+   {
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_crc_finish(png_ptr, 0);
+      png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
+      png_ptr->mode |= PNG_AFTER_IDAT;
+   }
+}
+
+void /* PRIVATE */
+png_process_IDAT_data(png_structp png_ptr, png_bytep buffer,
+   png_size_t buffer_length)
+{
+   int ret;
+
+   if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length)
+      png_error(png_ptr, "Extra compression data");
+
+   png_ptr->zstream.next_in = buffer;
+   png_ptr->zstream.avail_in = (uInt)buffer_length;
+   for(;;)
+   {
+      ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret != Z_OK)
+      {
+         if (ret == Z_STREAM_END)
+         {
+            if (png_ptr->zstream.avail_in)
+               png_error(png_ptr, "Extra compressed data");
+            if (!(png_ptr->zstream.avail_out))
+            {
+               png_push_process_row(png_ptr);
+            }
+
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+         else if (ret == Z_BUF_ERROR)
+            break;
+         else
+            png_error(png_ptr, "Decompression Error");
+      }
+      if (!(png_ptr->zstream.avail_out))
+      {
+         if ((
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+             png_ptr->interlaced && png_ptr->pass > 6) ||
+             (!png_ptr->interlaced &&
+#endif
+             png_ptr->row_number == png_ptr->num_rows))
+         {
+           if (png_ptr->zstream.avail_in)
+             png_warning(png_ptr, "Too much data in IDAT chunks");
+           png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+           break;
+         }
+         png_push_process_row(png_ptr);
+         png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+         png_ptr->zstream.next_out = png_ptr->row_buf;
+      }
+      else
+         break;
+   }
+}
+
+void /* PRIVATE */
+png_push_process_row(png_structp png_ptr)
+{
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+
+   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+       png_ptr->row_info.width);
+
+   png_read_filter_row(png_ptr, &(png_ptr->row_info),
+      png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+      (int)(png_ptr->row_buf[0]));
+
+   png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf,
+      png_ptr->rowbytes + 1);
+
+   if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA))
+      png_do_read_transformations(png_ptr);
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+/*       old interface (pre-1.0.9):
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+ */
+         png_do_read_interlace(png_ptr);
+
+    switch (png_ptr->pass)
+    {
+         case 0:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 0; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */
+            }
+            if (png_ptr->pass == 2) /* pass 1 might be empty */
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            if (png_ptr->pass == 4 && png_ptr->height <= 4)
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            if (png_ptr->pass == 6 && png_ptr->height <= 4)
+            {
+                png_push_have_row(png_ptr, png_bytep_NULL);
+                png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 1:
+         {
+            int i;
+            for (i = 0; i < 8 && png_ptr->pass == 1; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 2) /* skip top 4 generated rows */
+            {
+               for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 2:
+         {
+            int i;
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            for (i = 0; i < 4 && png_ptr->pass == 2; i++)
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 4) /* pass 3 might be empty */
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 3:
+         {
+            int i;
+            for (i = 0; i < 4 && png_ptr->pass == 3; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 4) /* skip top two generated rows */
+            {
+               for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+               {
+                  png_push_have_row(png_ptr, png_bytep_NULL);
+                  png_read_push_finish_row(png_ptr);
+               }
+            }
+            break;
+         }
+         case 4:
+         {
+            int i;
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            for (i = 0; i < 2 && png_ptr->pass == 4; i++)
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 6) /* pass 5 might be empty */
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 5:
+         {
+            int i;
+            for (i = 0; i < 2 && png_ptr->pass == 5; i++)
+            {
+               png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+               png_read_push_finish_row(png_ptr);
+            }
+            if (png_ptr->pass == 6) /* skip top generated row */
+            {
+               png_push_have_row(png_ptr, png_bytep_NULL);
+               png_read_push_finish_row(png_ptr);
+            }
+            break;
+         }
+         case 6:
+         {
+            png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+            png_read_push_finish_row(png_ptr);
+            if (png_ptr->pass != 6)
+               break;
+            png_push_have_row(png_ptr, png_bytep_NULL);
+            png_read_push_finish_row(png_ptr);
+         }
+      }
+   }
+   else
+#endif
+   {
+      png_push_have_row(png_ptr, png_ptr->row_buf + 1);
+      png_read_push_finish_row(png_ptr);
+   }
+}
+
+void /* PRIVATE */
+png_read_push_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2};
+
+   /* Width of interlace block.  This is not currently used - if you need
+    * it, uncomment it here and in png.h
+   PNG_CONST int FARDATA png_pass_width[] = {8, 4, 4, 2, 2, 1, 1};
+   */
+
+   /* Height of interlace block.  This is not currently used - if you need
+    * it, uncomment it here and in png.h
+   PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1};
+   */
+#endif
+
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      png_memset_check(png_ptr, png_ptr->prev_row, 0,
+         png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if ((png_ptr->pass == 1 && png_ptr->width < 5) ||
+             (png_ptr->pass == 3 && png_ptr->width < 3) ||
+             (png_ptr->pass == 5 && png_ptr->width < 2))
+           png_ptr->pass++;
+
+         if (png_ptr->pass > 7)
+            png_ptr->pass--;
+         if (png_ptr->pass >= 7)
+            break;
+
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+
+         png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,
+            png_ptr->iwidth) + 1;
+
+         if (png_ptr->transformations & PNG_INTERLACE)
+            break;
+
+         png_ptr->num_rows = (png_ptr->height +
+            png_pass_yinc[png_ptr->pass] - 1 -
+            png_pass_ystart[png_ptr->pass]) /
+            png_pass_yinc[png_ptr->pass];
+
+      } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0);
+   }
+}
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+void /* PRIVATE */
+png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+      {
+         png_error(png_ptr, "Out of place tEXt");
+         info_ptr = info_ptr; /* to quiet some compiler warnings */
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   png_ptr->skip_length = 0;  /* This may not be necessary */
+
+   if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */
+   {
+      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+      png_ptr->skip_length = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+         (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_tEXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp text;
+      png_charp key;
+      int ret;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+#if defined(PNG_MAX_MALLOC_64K)
+      if (png_ptr->skip_length)
+         return;
+#endif
+
+      key = png_ptr->current_text;
+
+      for (text = key; *text; text++)
+         /* empty loop */ ;
+
+      if (text != key + png_ptr->current_text_size)
+         text++;
+
+      text_ptr = (png_textp)png_malloc(png_ptr,
+         (png_uint_32)png_sizeof(png_text));
+      text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+      text_ptr->key = key;
+#ifdef PNG_iTXt_SUPPORTED
+      text_ptr->lang = NULL;
+      text_ptr->lang_key = NULL;
+#endif
+      text_ptr->text = text;
+
+      ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+      png_free(png_ptr, key);
+      png_free(png_ptr, text_ptr);
+      png_ptr->current_text = NULL;
+
+      if (ret)
+        png_warning(png_ptr, "Insufficient memory to store text chunk.");
+   }
+}
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+void /* PRIVATE */
+png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+      {
+         png_error(png_ptr, "Out of place zTXt");
+         info_ptr = info_ptr; /* to quiet some compiler warnings */
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We can't handle zTXt chunks > 64K, since we don't have enough space
+    * to be able to store the uncompressed data.  Actually, the threshold
+    * is probably around 32K, but it isn't as definite as 64K is.
+    */
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "zTXt chunk too large to fit in memory");
+      png_push_crc_skip(png_ptr, length);
+      return;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+       (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_zTXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp text;
+      png_charp key;
+      int ret;
+      png_size_t text_size, key_size;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+      key = png_ptr->current_text;
+
+      for (text = key; *text; text++)
+         /* empty loop */ ;
+
+      /* zTXt can't have zero text */
+      if (text == key + png_ptr->current_text_size)
+      {
+         png_ptr->current_text = NULL;
+         png_free(png_ptr, key);
+         return;
+      }
+
+      text++;
+
+      if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */
+      {
+         png_ptr->current_text = NULL;
+         png_free(png_ptr, key);
+         return;
+      }
+
+      text++;
+
+      png_ptr->zstream.next_in = (png_bytep )text;
+      png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size -
+         (text - key));
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+      key_size = text - key;
+      text_size = 0;
+      text = NULL;
+      ret = Z_STREAM_END;
+
+      while (png_ptr->zstream.avail_in)
+      {
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            inflateReset(&png_ptr->zstream);
+            png_ptr->zstream.avail_in = 0;
+            png_ptr->current_text = NULL;
+            png_free(png_ptr, key);
+            png_free(png_ptr, text);
+            return;
+         }
+         if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END)
+         {
+            if (text == NULL)
+            {
+               text = (png_charp)png_malloc(png_ptr,
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out
+                     + key_size + 1));
+               png_memcpy(text + key_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               png_memcpy(text, key, key_size);
+               text_size = key_size + png_ptr->zbuf_size -
+                  png_ptr->zstream.avail_out;
+               *(text + text_size) = '\0';
+            }
+            else
+            {
+               png_charp tmp;
+
+               tmp = text;
+               text = (png_charp)png_malloc(png_ptr, text_size +
+                  (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out
+                   + 1));
+               png_memcpy(text, tmp, text_size);
+               png_free(png_ptr, tmp);
+               png_memcpy(text + text_size, png_ptr->zbuf,
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               *(text + text_size) = '\0';
+            }
+            if (ret != Z_STREAM_END)
+            {
+               png_ptr->zstream.next_out = png_ptr->zbuf;
+               png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            }
+         }
+         else
+         {
+            break;
+         }
+
+         if (ret == Z_STREAM_END)
+            break;
+      }
+
+      inflateReset(&png_ptr->zstream);
+      png_ptr->zstream.avail_in = 0;
+
+      if (ret != Z_STREAM_END)
+      {
+         png_ptr->current_text = NULL;
+         png_free(png_ptr, key);
+         png_free(png_ptr, text);
+         return;
+      }
+
+      png_ptr->current_text = NULL;
+      png_free(png_ptr, key);
+      key = text;
+      text += key_size;
+
+      text_ptr = (png_textp)png_malloc(png_ptr,
+          (png_uint_32)png_sizeof(png_text));
+      text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt;
+      text_ptr->key = key;
+#ifdef PNG_iTXt_SUPPORTED
+      text_ptr->lang = NULL;
+      text_ptr->lang_key = NULL;
+#endif
+      text_ptr->text = text;
+
+      ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+      png_free(png_ptr, key);
+      png_free(png_ptr, text_ptr);
+
+      if (ret)
+        png_warning(png_ptr, "Insufficient memory to store text chunk.");
+   }
+}
+#endif
+
+#if defined(PNG_READ_iTXt_SUPPORTED)
+void /* PRIVATE */
+png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND))
+      {
+         png_error(png_ptr, "Out of place iTXt");
+         info_ptr = info_ptr; /* to quiet some compiler warnings */
+      }
+
+#ifdef PNG_MAX_MALLOC_64K
+   png_ptr->skip_length = 0;  /* This may not be necessary */
+
+   if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */
+   {
+      png_warning(png_ptr, "iTXt chunk too large to fit in memory");
+      png_ptr->skip_length = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   png_ptr->current_text = (png_charp)png_malloc(png_ptr,
+         (png_uint_32)(length+1));
+   png_ptr->current_text[length] = '\0';
+   png_ptr->current_text_ptr = png_ptr->current_text;
+   png_ptr->current_text_size = (png_size_t)length;
+   png_ptr->current_text_left = (png_size_t)length;
+   png_ptr->process_mode = PNG_READ_iTXt_MODE;
+}
+
+void /* PRIVATE */
+png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr)
+{
+
+   if (png_ptr->buffer_size && png_ptr->current_text_left)
+   {
+      png_size_t text_size;
+
+      if (png_ptr->buffer_size < png_ptr->current_text_left)
+         text_size = png_ptr->buffer_size;
+      else
+         text_size = png_ptr->current_text_left;
+      png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size);
+      png_ptr->current_text_left -= text_size;
+      png_ptr->current_text_ptr += text_size;
+   }
+   if (!(png_ptr->current_text_left))
+   {
+      png_textp text_ptr;
+      png_charp key;
+      int comp_flag;
+      png_charp lang;
+      png_charp lang_key;
+      png_charp text;
+      int ret;
+
+      if (png_ptr->buffer_size < 4)
+      {
+         png_push_save_buffer(png_ptr);
+         return;
+      }
+
+      png_push_crc_finish(png_ptr);
+
+#if defined(PNG_MAX_MALLOC_64K)
+      if (png_ptr->skip_length)
+         return;
+#endif
+
+      key = png_ptr->current_text;
+
+      for (lang = key; *lang; lang++)
+         /* empty loop */ ;
+
+      if (lang != key + png_ptr->current_text_size)
+         lang++;
+
+      comp_flag = *lang++;
+      lang++;     /* skip comp_type, always zero */
+
+      for (lang_key = lang; *lang_key; lang_key++)
+         /* empty loop */ ;
+      lang_key++;        /* skip NUL separator */
+
+      for (text = lang_key; *text; text++)
+         /* empty loop */ ;
+
+      if (text != key + png_ptr->current_text_size)
+         text++;
+
+      text_ptr = (png_textp)png_malloc(png_ptr,
+         (png_uint_32)png_sizeof(png_text));
+      text_ptr->compression = comp_flag + 2;
+      text_ptr->key = key;
+      text_ptr->lang = lang;
+      text_ptr->lang_key = lang_key;
+      text_ptr->text = text;
+      text_ptr->text_length = 0;
+      text_ptr->itxt_length = png_strlen(text);
+
+      ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+      png_ptr->current_text = NULL;
+
+      png_free(png_ptr, text_ptr);
+      if (ret)
+        png_warning(png_ptr, "Insufficient memory to store iTXt chunk.");
+   }
+}
+#endif
+
+/* This function is called when we haven't found a handler for this
+ * chunk.  If there isn't a problem with the chunk itself (ie a bad chunk
+ * name or a critical chunk), the chunk is (currently) silently ignored.
+ */
+void /* PRIVATE */
+png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32
+   length)
+{
+   png_uint_32 skip=0;
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+   if (!(png_ptr->chunk_name[0] & 0x20))
+   {
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+      if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+           PNG_HANDLE_CHUNK_ALWAYS
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+           && png_ptr->read_user_chunk_fn == NULL
+#endif
+         )
+#endif
+         png_chunk_error(png_ptr, "unknown critical chunk");
+
+      info_ptr = info_ptr; /* to quiet some compiler warnings */
+   }
+
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS)
+   {
+#ifdef PNG_MAX_MALLOC_64K
+       if (length > (png_uint_32)65535L)
+       {
+           png_warning(png_ptr, "unknown chunk too large to fit in memory");
+           skip = length - (png_uint_32)65535L;
+           length = (png_uint_32)65535L;
+       }
+#endif
+       png_strncpy((png_charp)png_ptr->unknown_chunk.name,
+	 (png_charp)png_ptr->chunk_name,
+         png_sizeof((png_charp)png_ptr->chunk_name));
+       png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length);
+       png_ptr->unknown_chunk.size = (png_size_t)length;
+       png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length);
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+       if(png_ptr->read_user_chunk_fn != NULL)
+       {
+          /* callback to user unknown chunk handler */
+          int ret;
+          ret = (*(png_ptr->read_user_chunk_fn))
+            (png_ptr, &png_ptr->unknown_chunk);
+          if (ret < 0)
+             png_chunk_error(png_ptr, "error in user chunk");
+          if (ret == 0)
+          {
+             if (!(png_ptr->chunk_name[0] & 0x20))
+                if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+                     PNG_HANDLE_CHUNK_ALWAYS)
+                   png_chunk_error(png_ptr, "unknown critical chunk");
+                png_set_unknown_chunks(png_ptr, info_ptr,
+                   &png_ptr->unknown_chunk, 1);
+          }
+       }
+#else
+       png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1);
+#endif
+       png_free(png_ptr, png_ptr->unknown_chunk.data);
+       png_ptr->unknown_chunk.data = NULL;
+   }
+   else
+#endif
+      skip=length;
+   png_push_crc_skip(png_ptr, skip);
+}
+
+void /* PRIVATE */
+png_push_have_info(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->info_fn != NULL)
+      (*(png_ptr->info_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_end(png_structp png_ptr, png_infop info_ptr)
+{
+   if (png_ptr->end_fn != NULL)
+      (*(png_ptr->end_fn))(png_ptr, info_ptr);
+}
+
+void /* PRIVATE */
+png_push_have_row(png_structp png_ptr, png_bytep row)
+{
+   if (png_ptr->row_fn != NULL)
+      (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number,
+         (int)png_ptr->pass);
+}
+
+void PNGAPI
+png_progressive_combine_row (png_structp png_ptr,
+   png_bytep old_row, png_bytep new_row)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_CONST int FARDATA png_pass_dsp_mask[7] =
+      {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff};
+#endif
+   if(png_ptr == NULL) return;
+   if (new_row != NULL)    /* new_row must == png_ptr->row_buf here. */
+      png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]);
+}
+
+void PNGAPI
+png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr,
+   png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn,
+   png_progressive_end_ptr end_fn)
+{
+   if(png_ptr == NULL) return;
+   png_ptr->info_fn = info_fn;
+   png_ptr->row_fn = row_fn;
+   png_ptr->end_fn = end_fn;
+
+   png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer);
+}
+
+png_voidp PNGAPI
+png_get_progressive_ptr(png_structp png_ptr)
+{
+   if(png_ptr == NULL) return (NULL);
+   return png_ptr->io_ptr;
+}
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngread.c b/distrib/libpng-1.2.19/pngread.c
new file mode 100644
index 0000000..2e561e8
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngread.c
@@ -0,0 +1,1478 @@
+
+/* pngread.c - read a PNG file
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file contains routines that an application calls directly to
+ * read a PNG file or stream.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED)
+
+/* Create a PNG structure for reading, and allocate any memory needed. */
+png_structp PNGAPI
+png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn)
+{
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn,
+      warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL));
+}
+
+/* Alternate create PNG structure for reading, and allocate any memory needed. */
+png_structp PNGAPI
+png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+
+   png_structp png_ptr;
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif
+#endif
+
+   int i;
+
+   png_debug(1, "in png_create_read_struct\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+      (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
+#else
+   png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+#endif
+   if (png_ptr == NULL)
+      return (NULL);
+
+#if !defined(PNG_1_0_X)
+#ifdef PNG_MMX_CODE_SUPPORTED
+   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
+#endif
+#endif /* PNG_1_0_X */
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(png_ptr->jmpbuf))
+#endif
+   {
+      png_free(png_ptr, png_ptr->zbuf);
+      png_ptr->zbuf=NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr,
+         (png_free_ptr)free_fn, (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      return (NULL);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#endif
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif
+
+   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+   i=0;
+   do
+   {
+     if(user_png_ver[i] != png_libpng_ver[i])
+        png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+   } while (png_libpng_ver[i++]);
+
+   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+   {
+     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+      * we must recompile any applications that use any older library version.
+      * For versions after libpng 1.0, we will be compatible, so we need
+      * only check the first digit.
+      */
+     if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+         (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+         (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+     {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+        char msg[80];
+        if (user_png_ver)
+        {
+          png_snprintf(msg, 80,
+             "Application was compiled with png.h from libpng-%.20s",
+             user_png_ver);
+          png_warning(png_ptr, msg);
+        }
+        png_snprintf(msg, 80,
+             "Application  is  running with png.c from libpng-%.20s",
+           png_libpng_ver);
+        png_warning(png_ptr, msg);
+#endif
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+        png_ptr->flags=0;
+#endif
+        png_error(png_ptr,
+           "Incompatible libpng version in application and library");
+     }
+   }
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+     (png_uint_32)png_ptr->zbuf_size);
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+   switch (inflateInit(&png_ptr->zstream))
+   {
+     case Z_OK: /* Do nothing */ break;
+     case Z_MEM_ERROR:
+     case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break;
+     case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break;
+     default: png_error(png_ptr, "Unknown zlib error");
+   }
+
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL);
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* Applications that neglect to set up their own setjmp() and then encounter
+   a png_error() will longjmp here.  Since the jmpbuf is then meaningless we
+   abort instead of returning. */
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+      PNG_ABORT();
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#else
+   if (setjmp(png_ptr->jmpbuf))
+      PNG_ABORT();
+#endif
+#endif
+   return (png_ptr);
+}
+
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+/* Initialize PNG structure for reading, and allocate any memory needed.
+   This interface is deprecated in favour of the png_create_read_struct(),
+   and it will disappear as of libpng-1.3.0. */
+#undef png_read_init
+void PNGAPI
+png_read_init(png_structp png_ptr)
+{
+   /* We only come here via pre-1.0.7-compiled applications */
+   png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0);
+}
+
+void PNGAPI
+png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size, png_size_t png_info_size)
+{
+   /* We only come here via pre-1.0.12-compiled applications */
+   if(png_ptr == NULL) return;
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+   if(png_sizeof(png_struct) > png_struct_size ||
+      png_sizeof(png_info) > png_info_size)
+   {
+      char msg[80];
+      png_ptr->warning_fn=NULL;
+      if (user_png_ver)
+      {
+        png_snprintf(msg, 80,
+           "Application was compiled with png.h from libpng-%.20s",
+           user_png_ver);
+        png_warning(png_ptr, msg);
+      }
+      png_snprintf(msg, 80,
+         "Application  is  running with png.c from libpng-%.20s",
+         png_libpng_ver);
+      png_warning(png_ptr, msg);
+   }
+#endif
+   if(png_sizeof(png_struct) > png_struct_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+       "The png struct allocated by the application for reading is too small.");
+     }
+   if(png_sizeof(png_info) > png_info_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+         "The info struct allocated by application for reading is too small.");
+     }
+   png_read_init_3(&png_ptr, user_png_ver, png_struct_size);
+}
+#endif /* PNG_1_0_X || PNG_1_2_X */
+
+void PNGAPI
+png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp;  /* to save current jump buffer */
+#endif
+
+   int i=0;
+
+   png_structp png_ptr=*ptr_ptr;
+
+   if(png_ptr == NULL) return;
+
+   do
+   {
+     if(user_png_ver[i] != png_libpng_ver[i])
+     {
+#ifdef PNG_LEGACY_SUPPORTED
+       png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+#else
+       png_ptr->warning_fn=NULL;
+       png_warning(png_ptr,
+        "Application uses deprecated png_read_init() and should be recompiled.");
+       break;
+#endif
+     }
+   } while (png_libpng_ver[i++]);
+
+   png_debug(1, "in png_read_init_3\n");
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* save jump buffer and error functions */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   if(png_sizeof(png_struct) > png_struct_size)
+     {
+       png_destroy_struct(png_ptr);
+       *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+       png_ptr = *ptr_ptr;
+     }
+
+   /* reset all variables to 0 */
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* restore jump buffer */
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+     (png_uint_32)png_ptr->zbuf_size);
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+
+   switch (inflateInit(&png_ptr->zstream))
+   {
+     case Z_OK: /* Do nothing */ break;
+     case Z_MEM_ERROR:
+     case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break;
+     case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break;
+     default: png_error(png_ptr, "Unknown zlib error");
+   }
+
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+   png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL);
+}
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read the information before the actual image data.  This has been
+ * changed in v0.90 to allow reading a file that already has the magic
+ * bytes read from the stream.  You can tell libpng how many bytes have
+ * been read from the beginning of the stream (up to the maximum of 8)
+ * via png_set_sig_bytes(), and we will only check the remaining bytes
+ * here.  The application can then have access to the signature bytes we
+ * read if it is determined that this isn't a valid PNG file.
+ */
+void PNGAPI
+png_read_info(png_structp png_ptr, png_infop info_ptr)
+{
+   if(png_ptr == NULL) return;
+   png_debug(1, "in png_read_info\n");
+   /* If we haven't checked all of the PNG signature bytes, do so now. */
+   if (png_ptr->sig_bytes < 8)
+   {
+      png_size_t num_checked = png_ptr->sig_bytes,
+                 num_to_check = 8 - num_checked;
+
+      png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check);
+      png_ptr->sig_bytes = 8;
+
+      if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check))
+      {
+         if (num_checked < 4 &&
+             png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4))
+            png_error(png_ptr, "Not a PNG file");
+         else
+            png_error(png_ptr, "PNG file corrupted by ASCII conversion");
+      }
+      if (num_checked < 3)
+         png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+   }
+
+   for(;;)
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_CONST PNG_IHDR;
+      PNG_CONST PNG_IDAT;
+      PNG_CONST PNG_IEND;
+      PNG_CONST PNG_PLTE;
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      PNG_CONST PNG_bKGD;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      PNG_CONST PNG_cHRM;
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      PNG_CONST PNG_gAMA;
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      PNG_CONST PNG_hIST;
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      PNG_CONST PNG_iCCP;
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      PNG_CONST PNG_iTXt;
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      PNG_CONST PNG_oFFs;
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      PNG_CONST PNG_pCAL;
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      PNG_CONST PNG_pHYs;
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      PNG_CONST PNG_sBIT;
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      PNG_CONST PNG_sCAL;
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      PNG_CONST PNG_sPLT;
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      PNG_CONST PNG_sRGB;
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      PNG_CONST PNG_tEXt;
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      PNG_CONST PNG_tIME;
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      PNG_CONST PNG_tRNS;
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      PNG_CONST PNG_zTXt;
+#endif
+#endif /* PNG_USE_LOCAL_ARRAYS */
+      png_byte chunk_length[4];
+      png_uint_32 length;
+
+      png_read_data(png_ptr, chunk_length, 4);
+      length = png_get_uint_31(png_ptr,chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+
+      png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name,
+         length);
+
+      /* This should be a binary subdivision search or a hash for
+       * matching the chunk name rather than a linear search.
+       */
+      if (!png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+        if(png_ptr->mode & PNG_AFTER_IDAT)
+          png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT;
+
+      if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+         png_handle_IHDR(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+         png_handle_IEND(png_ptr, info_ptr, length);
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+      {
+         if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+            png_ptr->mode |= PNG_HAVE_IDAT;
+         png_handle_unknown(png_ptr, info_ptr, length);
+         if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+            png_ptr->mode |= PNG_HAVE_PLTE;
+         else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+         {
+            if (!(png_ptr->mode & PNG_HAVE_IHDR))
+               png_error(png_ptr, "Missing IHDR before IDAT");
+            else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                     !(png_ptr->mode & PNG_HAVE_PLTE))
+               png_error(png_ptr, "Missing PLTE before IDAT");
+            break;
+         }
+      }
+#endif
+      else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_handle_PLTE(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         if (!(png_ptr->mode & PNG_HAVE_IHDR))
+            png_error(png_ptr, "Missing IHDR before IDAT");
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+                  !(png_ptr->mode & PNG_HAVE_PLTE))
+            png_error(png_ptr, "Missing PLTE before IDAT");
+
+         png_ptr->idat_size = length;
+         png_ptr->mode |= PNG_HAVE_IDAT;
+         break;
+      }
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+         png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+         png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+         png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+         png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+      else
+         png_handle_unknown(png_ptr, info_ptr, length);
+   }
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+/* optional call to update the users info_ptr structure */
+void PNGAPI
+png_read_update_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_update_info\n");
+   if(png_ptr == NULL) return;
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+   else
+      png_warning(png_ptr,
+      "Ignoring extra png_read_update_info() call; row buffer not reallocated");
+   png_read_transform_info(png_ptr, info_ptr);
+}
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Initialize palette, background, etc, after transformations
+ * are set, but before any reading takes place.  This allows
+ * the user to obtain a gamma-corrected palette, for example.
+ * If the user doesn't call this, we will do it ourselves.
+ */
+void PNGAPI
+png_start_read_image(png_structp png_ptr)
+{
+   png_debug(1, "in png_start_read_image\n");
+   if(png_ptr == NULL) return;
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+void PNGAPI
+png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_CONST PNG_IDAT;
+   PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55,
+     0xff};
+   PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff};
+#endif
+   int ret;
+   if(png_ptr == NULL) return;
+   png_debug2(1, "in png_read_row (row %lu, pass %d)\n",
+      png_ptr->row_number, png_ptr->pass);
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      png_read_start_row(png_ptr);
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* check for transforms that have been set but were defined out */
+#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined.");
+#endif
+#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined.");
+#endif
+   }
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* if interlaced and we do not need a new row, combine row and return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 0x07)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 0x07) != 4)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 4))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 3) || png_ptr->width < 3)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 3) != 2)
+            {
+               if (dsp_row != NULL && (png_ptr->row_number & 2))
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 1) || png_ptr->width < 2)
+            {
+               if (dsp_row != NULL)
+                  png_combine_row(png_ptr, dsp_row,
+                     png_pass_dsp_mask[png_ptr->pass]);
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 1))
+            {
+               png_read_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   if (!(png_ptr->mode & PNG_HAVE_IDAT))
+      png_error(png_ptr, "Invalid attempt to read row data");
+
+   png_ptr->zstream.next_out = png_ptr->row_buf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes;
+   do
+   {
+      if (!(png_ptr->zstream.avail_in))
+      {
+         while (!png_ptr->idat_size)
+         {
+            png_byte chunk_length[4];
+
+            png_crc_finish(png_ptr, 0);
+
+            png_read_data(png_ptr, chunk_length, 4);
+            png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length);
+
+            png_reset_crc(png_ptr);
+            png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+            if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+               png_error(png_ptr, "Not enough image data");
+         }
+         png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_in = png_ptr->zbuf;
+         if (png_ptr->zbuf_size > png_ptr->idat_size)
+            png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+         png_crc_read(png_ptr, png_ptr->zbuf,
+            (png_size_t)png_ptr->zstream.avail_in);
+         png_ptr->idat_size -= png_ptr->zstream.avail_in;
+      }
+      ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+      if (ret == Z_STREAM_END)
+      {
+         if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in ||
+            png_ptr->idat_size)
+            png_error(png_ptr, "Extra compressed data");
+         png_ptr->mode |= PNG_AFTER_IDAT;
+         png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+         break;
+      }
+      if (ret != Z_OK)
+         png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+                   "Decompression error");
+
+   } while (png_ptr->zstream.avail_out);
+
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->iwidth;
+   png_ptr->row_info.channels = png_ptr->channels;
+   png_ptr->row_info.bit_depth = png_ptr->bit_depth;
+   png_ptr->row_info.pixel_depth = png_ptr->pixel_depth;
+   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+       png_ptr->row_info.width);
+
+   if(png_ptr->row_buf[0])
+   png_read_filter_row(png_ptr, &(png_ptr->row_info),
+      png_ptr->row_buf + 1, png_ptr->prev_row + 1,
+      (int)(png_ptr->row_buf[0]));
+
+   png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf,
+      png_ptr->rowbytes + 1);
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+   {
+      /* Intrapixel differencing */
+      png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   }
+#endif
+
+
+   if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA))
+      png_do_read_transformations(png_ptr);
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+   /* blow up interlaced rows to full size */
+   if (png_ptr->interlaced &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      if (png_ptr->pass < 6)
+/*       old interface (pre-1.0.9):
+         png_do_read_interlace(&(png_ptr->row_info),
+            png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations);
+ */
+         png_do_read_interlace(png_ptr);
+
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row,
+            png_pass_dsp_mask[png_ptr->pass]);
+      if (row != NULL)
+         png_combine_row(png_ptr, row,
+            png_pass_mask[png_ptr->pass]);
+   }
+   else
+#endif
+   {
+      if (row != NULL)
+         png_combine_row(png_ptr, row, 0xff);
+      if (dsp_row != NULL)
+         png_combine_row(png_ptr, dsp_row, 0xff);
+   }
+   png_read_finish_row(png_ptr);
+
+   if (png_ptr->read_row_fn != NULL)
+      (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read one or more rows of image data.  If the image is interlaced,
+ * and png_set_interlace_handling() has been called, the rows need to
+ * contain the contents of the rows from the previous pass.  If the
+ * image has alpha or transparency, and png_handle_alpha()[*] has been
+ * called, the rows contents must be initialized to the contents of the
+ * screen.
+ *
+ * "row" holds the actual image, and pixels are placed in it
+ * as they arrive.  If the image is displayed after each pass, it will
+ * appear to "sparkle" in.  "display_row" can be used to display a
+ * "chunky" progressive image, with finer detail added as it becomes
+ * available.  If you do not want this "chunky" display, you may pass
+ * NULL for display_row.  If you do not want the sparkle display, and
+ * you have not called png_handle_alpha(), you may pass NULL for rows.
+ * If you have called png_handle_alpha(), and the image has either an
+ * alpha channel or a transparency chunk, you must provide a buffer for
+ * rows.  In this case, you do not have to provide a display_row buffer
+ * also, but you may.  If the image is not interlaced, or if you have
+ * not called png_set_interlace_handling(), the display_row buffer will
+ * be ignored, so pass NULL to it.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of this version of libpng
+ */
+
+void PNGAPI
+png_read_rows(png_structp png_ptr, png_bytepp row,
+   png_bytepp display_row, png_uint_32 num_rows)
+{
+   png_uint_32 i;
+   png_bytepp rp;
+   png_bytepp dp;
+
+   png_debug(1, "in png_read_rows\n");
+   if(png_ptr == NULL) return;
+   rp = row;
+   dp = display_row;
+   if (rp != NULL && dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp++;
+         png_bytep dptr = *dp++;
+
+         png_read_row(png_ptr, rptr, dptr);
+      }
+   else if(rp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep rptr = *rp;
+         png_read_row(png_ptr, rptr, png_bytep_NULL);
+         rp++;
+      }
+   else if(dp != NULL)
+      for (i = 0; i < num_rows; i++)
+      {
+         png_bytep dptr = *dp;
+         png_read_row(png_ptr, png_bytep_NULL, dptr);
+         dp++;
+      }
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read the entire image.  If the image has an alpha channel or a tRNS
+ * chunk, and you have called png_handle_alpha()[*], you will need to
+ * initialize the image to the current image that PNG will be overlaying.
+ * We set the num_rows again here, in case it was incorrectly set in
+ * png_read_start_row() by a call to png_read_update_info() or
+ * png_start_read_image() if png_set_interlace_handling() wasn't called
+ * prior to either of these functions like it should have been.  You can
+ * only call this function once.  If you desire to have an image for
+ * each pass of a interlaced image, use png_read_rows() instead.
+ *
+ * [*] png_handle_alpha() does not exist yet, as of this version of libpng
+ */
+void PNGAPI
+png_read_image(png_structp png_ptr, png_bytepp image)
+{
+   png_uint_32 i,image_height;
+   int pass, j;
+   png_bytepp rp;
+
+   png_debug(1, "in png_read_image\n");
+   if(png_ptr == NULL) return;
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+   pass = png_set_interlace_handling(png_ptr);
+#else
+   if (png_ptr->interlaced)
+      png_error(png_ptr,
+        "Cannot read interlaced image -- interlace handler disabled.");
+   pass = 1;
+#endif
+
+
+   image_height=png_ptr->height;
+   png_ptr->num_rows = image_height; /* Make sure this is set correctly */
+
+   for (j = 0; j < pass; j++)
+   {
+      rp = image;
+      for (i = 0; i < image_height; i++)
+      {
+         png_read_row(png_ptr, *rp, png_bytep_NULL);
+         rp++;
+      }
+   }
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+/* Read the end of the PNG file.  Will not read past the end of the
+ * file, will verify the end is accurate, and will read any comments
+ * or time information at the end of the file, if info is not NULL.
+ */
+void PNGAPI
+png_read_end(png_structp png_ptr, png_infop info_ptr)
+{
+   png_byte chunk_length[4];
+   png_uint_32 length;
+
+   png_debug(1, "in png_read_end\n");
+   if(png_ptr == NULL) return;
+   png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */
+
+   do
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_CONST PNG_IHDR;
+      PNG_CONST PNG_IDAT;
+      PNG_CONST PNG_IEND;
+      PNG_CONST PNG_PLTE;
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      PNG_CONST PNG_bKGD;
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      PNG_CONST PNG_cHRM;
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      PNG_CONST PNG_gAMA;
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      PNG_CONST PNG_hIST;
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      PNG_CONST PNG_iCCP;
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      PNG_CONST PNG_iTXt;
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      PNG_CONST PNG_oFFs;
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      PNG_CONST PNG_pCAL;
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      PNG_CONST PNG_pHYs;
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      PNG_CONST PNG_sBIT;
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      PNG_CONST PNG_sCAL;
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      PNG_CONST PNG_sPLT;
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      PNG_CONST PNG_sRGB;
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      PNG_CONST PNG_tEXt;
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      PNG_CONST PNG_tIME;
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      PNG_CONST PNG_tRNS;
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      PNG_CONST PNG_zTXt;
+#endif
+#endif /* PNG_USE_LOCAL_ARRAYS */
+
+      png_read_data(png_ptr, chunk_length, 4);
+      length = png_get_uint_31(png_ptr,chunk_length);
+
+      png_reset_crc(png_ptr);
+      png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+
+      png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name);
+
+      if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4))
+         png_handle_IHDR(png_ptr, info_ptr, length);
+      else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4))
+         png_handle_IEND(png_ptr, info_ptr, length);
+#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+      else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name))
+      {
+         if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+         {
+            if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
+               png_error(png_ptr, "Too many IDAT's found");
+         }
+         png_handle_unknown(png_ptr, info_ptr, length);
+         if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+            png_ptr->mode |= PNG_HAVE_PLTE;
+      }
+#endif
+      else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4))
+      {
+         /* Zero length IDATs are legal after the last IDAT has been
+          * read, but not after other chunks have been read.
+          */
+         if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT))
+            png_error(png_ptr, "Too many IDAT's found");
+         png_crc_finish(png_ptr, length);
+      }
+      else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4))
+         png_handle_PLTE(png_ptr, info_ptr, length);
+#if defined(PNG_READ_bKGD_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4))
+         png_handle_bKGD(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_cHRM_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4))
+         png_handle_cHRM(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_gAMA_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4))
+         png_handle_gAMA(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4))
+         png_handle_hIST(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_oFFs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4))
+         png_handle_oFFs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4))
+         png_handle_pCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sCAL_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4))
+         png_handle_sCAL(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_pHYs_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4))
+         png_handle_pHYs(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sBIT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4))
+         png_handle_sBIT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4))
+         png_handle_sRGB(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iCCP_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4))
+         png_handle_iCCP(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_sPLT_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4))
+         png_handle_sPLT(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tEXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4))
+         png_handle_tEXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tIME_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4))
+         png_handle_tIME(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_tRNS_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4))
+         png_handle_tRNS(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_zTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4))
+         png_handle_zTXt(png_ptr, info_ptr, length);
+#endif
+#if defined(PNG_READ_iTXt_SUPPORTED)
+      else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4))
+         png_handle_iTXt(png_ptr, info_ptr, length);
+#endif
+      else
+         png_handle_unknown(png_ptr, info_ptr, length);
+   } while (!(png_ptr->mode & PNG_HAVE_IEND));
+}
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+
+/* free all memory used by the read */
+void PNGAPI
+png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr,
+   png_infopp end_info_ptr_ptr)
+{
+   png_structp png_ptr = NULL;
+   png_infop info_ptr = NULL, end_info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+   png_voidp mem_ptr;
+#endif
+
+   png_debug(1, "in png_destroy_read_struct\n");
+   if (png_ptr_ptr != NULL)
+      png_ptr = *png_ptr_ptr;
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (end_info_ptr_ptr != NULL)
+      end_info_ptr = *end_info_ptr_ptr;
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+   mem_ptr = png_ptr->mem_ptr;
+#endif
+
+   png_read_destroy(png_ptr, info_ptr, end_info_ptr);
+
+   if (info_ptr != NULL)
+   {
+#if defined(PNG_TEXT_SUPPORTED)
+      png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1);
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
+          (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = NULL;
+   }
+
+   if (end_info_ptr != NULL)
+   {
+#if defined(PNG_READ_TEXT_SUPPORTED)
+      png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1);
+#endif
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn,
+         (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)end_info_ptr);
+#endif
+      *end_info_ptr_ptr = NULL;
+   }
+
+   if (png_ptr != NULL)
+   {
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
+          (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      *png_ptr_ptr = NULL;
+   }
+}
+
+/* free all memory used by the read (old method) */
+void /* PRIVATE */
+png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp;
+#endif
+   png_error_ptr error_fn;
+   png_error_ptr warning_fn;
+   png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+#endif
+
+   png_debug(1, "in png_read_destroy\n");
+   if (info_ptr != NULL)
+      png_info_destroy(png_ptr, info_ptr);
+
+   if (end_info_ptr != NULL)
+      png_info_destroy(png_ptr, end_info_ptr);
+
+   png_free(png_ptr, png_ptr->zbuf);
+   png_free(png_ptr, png_ptr->big_row_buf);
+   png_free(png_ptr, png_ptr->prev_row);
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   png_free(png_ptr, png_ptr->palette_lookup);
+   png_free(png_ptr, png_ptr->dither_index);
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_table);
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   png_free(png_ptr, png_ptr->gamma_from_1);
+   png_free(png_ptr, png_ptr->gamma_to_1);
+#endif
+#ifdef PNG_FREE_ME_SUPPORTED
+   if (png_ptr->free_me & PNG_FREE_PLTE)
+      png_zfree(png_ptr, png_ptr->palette);
+   png_ptr->free_me &= ~PNG_FREE_PLTE;
+#else
+   if (png_ptr->flags & PNG_FLAG_FREE_PLTE)
+      png_zfree(png_ptr, png_ptr->palette);
+   png_ptr->flags &= ~PNG_FLAG_FREE_PLTE;
+#endif
+#if defined(PNG_tRNS_SUPPORTED) || \
+    defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+#ifdef PNG_FREE_ME_SUPPORTED
+   if (png_ptr->free_me & PNG_FREE_TRNS)
+      png_free(png_ptr, png_ptr->trans);
+   png_ptr->free_me &= ~PNG_FREE_TRNS;
+#else
+   if (png_ptr->flags & PNG_FLAG_FREE_TRNS)
+      png_free(png_ptr, png_ptr->trans);
+   png_ptr->flags &= ~PNG_FLAG_FREE_TRNS;
+#endif
+#endif
+#if defined(PNG_READ_hIST_SUPPORTED)
+#ifdef PNG_FREE_ME_SUPPORTED
+   if (png_ptr->free_me & PNG_FREE_HIST)
+      png_free(png_ptr, png_ptr->hist);
+   png_ptr->free_me &= ~PNG_FREE_HIST;
+#else
+   if (png_ptr->flags & PNG_FLAG_FREE_HIST)
+      png_free(png_ptr, png_ptr->hist);
+   png_ptr->flags &= ~PNG_FLAG_FREE_HIST;
+#endif
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->gamma_16_table != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_table[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_table);
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->gamma_16_from_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_from_1);
+   }
+   if (png_ptr->gamma_16_to_1 != NULL)
+   {
+      int i;
+      int istop = (1 << (8 - png_ptr->gamma_shift));
+      for (i = 0; i < istop; i++)
+      {
+         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
+      }
+   png_free(png_ptr, png_ptr->gamma_16_to_1);
+   }
+#endif
+#endif
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_free(png_ptr, png_ptr->time_buffer);
+#endif
+
+   inflateEnd(&png_ptr->zstream);
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+   png_free(png_ptr, png_ptr->save_buffer);
+#endif
+
+#ifdef PNG_PROGRESSIVE_READ_SUPPORTED
+#ifdef PNG_TEXT_SUPPORTED
+   png_free(png_ptr, png_ptr->current_text);
+#endif /* PNG_TEXT_SUPPORTED */
+#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */
+
+   /* Save the important info out of the png_struct, in case it is
+    * being used again.
+    */
+#ifdef PNG_SETJMP_SUPPORTED
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   error_fn = png_ptr->error_fn;
+   warning_fn = png_ptr->warning_fn;
+   error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+   png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr->free_fn = free_fn;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+
+}
+
+void PNGAPI
+png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn)
+{
+   if(png_ptr == NULL) return;
+   png_ptr->read_row_fn = read_row_fn;
+}
+
+
+#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+void PNGAPI
+png_read_png(png_structp png_ptr, png_infop info_ptr,
+                           int transforms,
+                           voidp params)
+{
+   int row;
+
+   if(png_ptr == NULL) return;
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+   /* invert the alpha channel from opacity to transparency
+    */
+   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+       png_set_invert_alpha(png_ptr);
+#endif
+
+   /* png_read_info() gives us all of the information from the
+    * PNG file before the first IDAT (image data chunk).
+    */
+   png_read_info(png_ptr, info_ptr);
+   if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep))
+      png_error(png_ptr,"Image is too high to process with png_read_png()");
+
+   /* -------------- image transformations start here ------------------- */
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   /* tell libpng to strip 16 bit/color files down to 8 bits per color
+    */
+   if (transforms & PNG_TRANSFORM_STRIP_16)
+       png_set_strip_16(png_ptr);
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   /* Strip alpha bytes from the input data without combining with
+    * the background (not recommended).
+    */
+   if (transforms & PNG_TRANSFORM_STRIP_ALPHA)
+       png_set_strip_alpha(png_ptr);
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED)
+   /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single
+    * byte into separate bytes (useful for paletted and grayscale images).
+    */
+   if (transforms & PNG_TRANSFORM_PACKING)
+       png_set_packing(png_ptr);
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   /* Change the order of packed pixels to least significant bit first
+    * (not useful if you are using png_set_packing).
+    */
+   if (transforms & PNG_TRANSFORM_PACKSWAP)
+       png_set_packswap(png_ptr);
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   /* Expand paletted colors into true RGB triplets
+    * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel
+    * Expand paletted or RGB images with transparency to full alpha
+    * channels so the data will be available as RGBA quartets.
+    */
+   if (transforms & PNG_TRANSFORM_EXPAND)
+       if ((png_ptr->bit_depth < 8) ||
+           (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) ||
+           (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)))
+         png_set_expand(png_ptr);
+#endif
+
+   /* We don't handle background color or gamma transformation or dithering.
+    */
+
+#if defined(PNG_READ_INVERT_SUPPORTED)
+   /* invert monochrome files to have 0 as white and 1 as black
+    */
+   if (transforms & PNG_TRANSFORM_INVERT_MONO)
+       png_set_invert_mono(png_ptr);
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   /* If you want to shift the pixel values from the range [0,255] or
+    * [0,65535] to the original [0,7] or [0,31], or whatever range the
+    * colors were originally in:
+    */
+   if ((transforms & PNG_TRANSFORM_SHIFT)
+       && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
+   {
+      png_color_8p sig_bit;
+
+      png_get_sBIT(png_ptr, info_ptr, &sig_bit);
+      png_set_shift(png_ptr, sig_bit);
+   }
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED)
+   /* flip the RGB pixels to BGR (or RGBA to BGRA)
+    */
+   if (transforms & PNG_TRANSFORM_BGR)
+       png_set_bgr(png_ptr);
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+   /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR)
+    */
+   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+       png_set_swap_alpha(png_ptr);
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED)
+   /* swap bytes of 16 bit files to least significant byte first
+    */
+   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+       png_set_swap(png_ptr);
+#endif
+
+   /* We don't handle adding filler bytes */
+
+   /* Optional call to gamma correct and add the background to the palette
+    * and update info structure.  REQUIRED if you are expecting libpng to
+    * update the palette for you (i.e., you selected such a transform above).
+    */
+   png_read_update_info(png_ptr, info_ptr);
+
+   /* -------------- image transformations end here ------------------- */
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+#endif
+   if(info_ptr->row_pointers == NULL)
+   {
+      info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr,
+         info_ptr->height * png_sizeof(png_bytep));
+#ifdef PNG_FREE_ME_SUPPORTED
+      info_ptr->free_me |= PNG_FREE_ROWS;
+#endif
+      for (row = 0; row < (int)info_ptr->height; row++)
+      {
+         info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr,
+            png_get_rowbytes(png_ptr, info_ptr));
+      }
+   }
+
+   png_read_image(png_ptr, info_ptr->row_pointers);
+   info_ptr->valid |= PNG_INFO_IDAT;
+
+   /* read rest of file, and get additional chunks in info_ptr - REQUIRED */
+   png_read_end(png_ptr, info_ptr);
+
+   transforms = transforms; /* quiet compiler warnings */
+   params = params;
+
+}
+#endif /* PNG_INFO_IMAGE_SUPPORTED */
+#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngrio.c b/distrib/libpng-1.2.19/pngrio.c
new file mode 100644
index 0000000..7d2522f
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngrio.c
@@ -0,0 +1,167 @@
+
+/* pngrio.c - functions for data input
+ *
+ * Last changed in libpng 1.2.13 November 13, 2006
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2006 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all input.  Users who need
+ * special handling are expected to write a function that has the same
+ * arguments as this and performs a similar function, but that possibly
+ * has a different input method.  Note that you shouldn't change this
+ * function, but rather write a replacement function and then make
+ * libpng use it at run time with png_set_read_fn(...).
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED)
+
+/* Read the data from whatever input you are using.  The default routine
+   reads from a file pointer.  Note that this routine sometimes gets called
+   with very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered reads.  This should never be asked
+   to read more then 64K on a 16 bit machine. */
+void /* PRIVATE */
+png_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_debug1(4,"reading %d bytes\n", (int)length);
+   if (png_ptr->read_data_fn != NULL)
+      (*(png_ptr->read_data_fn))(png_ptr, data, length);
+   else
+      png_error(png_ptr, "Call to NULL read function");
+}
+
+#if !defined(PNG_NO_STDIO)
+/* This is the function that does the actual reading of data.  If you are
+   not reading from a standard C stream, you should create a replacement
+   read_data function and use it at run time with png_set_read_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+void PNGAPI
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_size_t check;
+
+   if(png_ptr == NULL) return;
+   /* fread() returns 0 on error, so it is OK to store this in a png_size_t
+    * instead of an int, which is what fread() actually returns.
+    */
+#if defined(_WIN32_WCE)
+   if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
+      check = 0;
+#else
+   check = (png_size_t)fread(data, (png_size_t)1, length,
+      (png_FILE_p)png_ptr->io_ptr);
+#endif
+
+   if (check != length)
+      png_error(png_ptr, "Read Error");
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+static void PNGAPI
+png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   int check;
+   png_byte *n_data;
+   png_FILE_p io_ptr;
+
+   if(png_ptr == NULL) return;
+   /* Check if data really is near. If so, use usual code. */
+   n_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)n_data == data)
+   {
+#if defined(_WIN32_WCE)
+      if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
+         check = 0;
+#else
+      check = fread(n_data, 1, length, io_ptr);
+#endif
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t read, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         read = MIN(NEAR_BUF_SIZE, remaining);
+#if defined(_WIN32_WCE)
+         if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) )
+            err = 0;
+#else
+         err = fread(buf, (png_size_t)1, read, io_ptr);
+#endif
+         png_memcpy(data, buf, read); /* copy far buffer to near buffer */
+         if(err != read)
+            break;
+         else
+            check += err;
+         data += read;
+         remaining -= read;
+      }
+      while (remaining != 0);
+   }
+   if ((png_uint_32)check != (png_uint_32)length)
+      png_error(png_ptr, "read Error");
+}
+#endif
+#endif
+
+/* This function allows the application to supply a new input function
+   for libpng if standard C streams aren't being used.
+
+   This function takes as its arguments:
+   png_ptr      - pointer to a png input data structure
+   io_ptr       - pointer to user supplied structure containing info about
+                  the input functions.  May be NULL.
+   read_data_fn - pointer to a new input function that takes as its
+                  arguments a pointer to a png_struct, a pointer to
+                  a location where input data can be stored, and a 32-bit
+                  unsigned int that is the number of bytes to be read.
+                  To exit and output any fatal error messages the new write
+                  function should call png_error(png_ptr, "Error msg"). */
+void PNGAPI
+png_set_read_fn(png_structp png_ptr, png_voidp io_ptr,
+   png_rw_ptr read_data_fn)
+{
+   if(png_ptr == NULL) return;
+   png_ptr->io_ptr = io_ptr;
+
+#if !defined(PNG_NO_STDIO)
+   if (read_data_fn != NULL)
+      png_ptr->read_data_fn = read_data_fn;
+   else
+      png_ptr->read_data_fn = png_default_read_data;
+#else
+   png_ptr->read_data_fn = read_data_fn;
+#endif
+
+   /* It is an error to write to a read device */
+   if (png_ptr->write_data_fn != NULL)
+   {
+      png_ptr->write_data_fn = NULL;
+      png_warning(png_ptr,
+         "It's an error to set both read_data_fn and write_data_fn in the ");
+      png_warning(png_ptr,
+         "same structure.  Resetting write_data_fn to NULL.");
+   }
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_ptr->output_flush_fn = NULL;
+#endif
+}
+#endif /* PNG_READ_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngrtran.c b/distrib/libpng-1.2.19/pngrtran.c
new file mode 100644
index 0000000..3f04051
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngrtran.c
@@ -0,0 +1,4284 @@
+
+/* pngrtran.c - transforms the data in a row for PNG readers
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file contains functions optionally called by an application
+ * in order to tell libpng how to handle data when reading a PNG.
+ * Transformations that are used in both reading and writing are
+ * in pngtrans.c.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED)
+
+/* Set the action on getting a CRC error for an ancillary or critical chunk. */
+void PNGAPI
+png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action)
+{
+   png_debug(1, "in png_set_crc_action\n");
+   /* Tell libpng how we react to CRC errors in critical chunks */
+   if(png_ptr == NULL) return;
+   switch (crit_action)
+   {
+      case PNG_CRC_NO_CHANGE:                        /* leave setting as is */
+         break;
+      case PNG_CRC_WARN_USE:                               /* warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE;
+         break;
+      case PNG_CRC_QUIET_USE:                             /* quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE |
+                           PNG_FLAG_CRC_CRITICAL_IGNORE;
+         break;
+      case PNG_CRC_WARN_DISCARD:    /* not a valid action for critical data */
+         png_warning(png_ptr, "Can't discard critical data on CRC error.");
+      case PNG_CRC_ERROR_QUIT:                                /* error/quit */
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK;
+         break;
+   }
+
+   switch (ancil_action)
+   {
+      case PNG_CRC_NO_CHANGE:                       /* leave setting as is */
+         break;
+      case PNG_CRC_WARN_USE:                              /* warn/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE;
+         break;
+      case PNG_CRC_QUIET_USE:                            /* quiet/use data */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE |
+                           PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+      case PNG_CRC_ERROR_QUIT:                               /* error/quit */
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN;
+         break;
+      case PNG_CRC_WARN_DISCARD:                      /* warn/discard data */
+      case PNG_CRC_DEFAULT:
+      default:
+         png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK;
+         break;
+   }
+}
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+    defined(PNG_FLOATING_POINT_SUPPORTED)
+/* handle alpha and tRNS via a background color */
+void PNGAPI
+png_set_background(png_structp png_ptr,
+   png_color_16p background_color, int background_gamma_code,
+   int need_expand, double background_gamma)
+{
+   png_debug(1, "in png_set_background\n");
+   if(png_ptr == NULL) return;
+   if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN)
+   {
+      png_warning(png_ptr, "Application must supply a known background gamma");
+      return;
+   }
+
+   png_ptr->transformations |= PNG_BACKGROUND;
+   png_memcpy(&(png_ptr->background), background_color,
+      png_sizeof(png_color_16));
+   png_ptr->background_gamma = (float)background_gamma;
+   png_ptr->background_gamma_type = (png_byte)(background_gamma_code);
+   png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0);
+}
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* strip 16 bit depth files to 8 bit depth */
+void PNGAPI
+png_set_strip_16(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_strip_16\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_16_TO_8;
+}
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_strip_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_strip_alpha\n");
+   if(png_ptr == NULL) return;
+   png_ptr->flags |= PNG_FLAG_STRIP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+/* Dither file to 8 bit.  Supply a palette, the current number
+ * of elements in the palette, the maximum number of elements
+ * allowed, and a histogram if possible.  If the current number
+ * of colors is greater then the maximum number, the palette will be
+ * modified to fit in the maximum number.  "full_dither" indicates
+ * whether we need a dithering cube set up for RGB images, or if we
+ * simply are reducing the number of colors in a paletted image.
+ */
+
+typedef struct png_dsort_struct
+{
+   struct png_dsort_struct FAR * next;
+   png_byte left;
+   png_byte right;
+} png_dsort;
+typedef png_dsort FAR *       png_dsortp;
+typedef png_dsort FAR * FAR * png_dsortpp;
+
+void PNGAPI
+png_set_dither(png_structp png_ptr, png_colorp palette,
+   int num_palette, int maximum_colors, png_uint_16p histogram,
+   int full_dither)
+{
+   png_debug(1, "in png_set_dither\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_DITHER;
+
+   if (!full_dither)
+   {
+      int i;
+
+      png_ptr->dither_index = (png_bytep)png_malloc(png_ptr,
+         (png_uint_32)(num_palette * png_sizeof (png_byte)));
+      for (i = 0; i < num_palette; i++)
+         png_ptr->dither_index[i] = (png_byte)i;
+   }
+
+   if (num_palette > maximum_colors)
+   {
+      if (histogram != NULL)
+      {
+         /* This is easy enough, just throw out the least used colors.
+            Perhaps not the best solution, but good enough. */
+
+         int i;
+
+         /* initialize an array to sort colors */
+         png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * png_sizeof (png_byte)));
+
+         /* initialize the dither_sort array */
+         for (i = 0; i < num_palette; i++)
+            png_ptr->dither_sort[i] = (png_byte)i;
+
+         /* Find the least used palette entries by starting a
+            bubble sort, and running it until we have sorted
+            out enough colors.  Note that we don't care about
+            sorting all the colors, just finding which are
+            least used. */
+
+         for (i = num_palette - 1; i >= maximum_colors; i--)
+         {
+            int done; /* to stop early if the list is pre-sorted */
+            int j;
+
+            done = 1;
+            for (j = 0; j < i; j++)
+            {
+               if (histogram[png_ptr->dither_sort[j]]
+                   < histogram[png_ptr->dither_sort[j + 1]])
+               {
+                  png_byte t;
+
+                  t = png_ptr->dither_sort[j];
+                  png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1];
+                  png_ptr->dither_sort[j + 1] = t;
+                  done = 0;
+               }
+            }
+            if (done)
+               break;
+         }
+
+         /* swap the palette around, and set up a table, if necessary */
+         if (full_dither)
+         {
+            int j = num_palette;
+
+            /* put all the useful colors within the max, but don't
+               move the others */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               if ((int)png_ptr->dither_sort[i] >= maximum_colors)
+               {
+                  do
+                     j--;
+                  while ((int)png_ptr->dither_sort[j] >= maximum_colors);
+                  palette[i] = palette[j];
+               }
+            }
+         }
+         else
+         {
+            int j = num_palette;
+
+            /* move all the used colors inside the max limit, and
+               develop a translation table */
+            for (i = 0; i < maximum_colors; i++)
+            {
+               /* only move the colors we need to */
+               if ((int)png_ptr->dither_sort[i] >= maximum_colors)
+               {
+                  png_color tmp_color;
+
+                  do
+                     j--;
+                  while ((int)png_ptr->dither_sort[j] >= maximum_colors);
+
+                  tmp_color = palette[j];
+                  palette[j] = palette[i];
+                  palette[i] = tmp_color;
+                  /* indicate where the color went */
+                  png_ptr->dither_index[j] = (png_byte)i;
+                  png_ptr->dither_index[i] = (png_byte)j;
+               }
+            }
+
+            /* find closest color for those colors we are not using */
+            for (i = 0; i < num_palette; i++)
+            {
+               if ((int)png_ptr->dither_index[i] >= maximum_colors)
+               {
+                  int min_d, k, min_k, d_index;
+
+                  /* find the closest color to one we threw out */
+                  d_index = png_ptr->dither_index[i];
+                  min_d = PNG_COLOR_DIST(palette[d_index], palette[0]);
+                  for (k = 1, min_k = 0; k < maximum_colors; k++)
+                  {
+                     int d;
+
+                     d = PNG_COLOR_DIST(palette[d_index], palette[k]);
+
+                     if (d < min_d)
+                     {
+                        min_d = d;
+                        min_k = k;
+                     }
+                  }
+                  /* point to closest color */
+                  png_ptr->dither_index[i] = (png_byte)min_k;
+               }
+            }
+         }
+         png_free(png_ptr, png_ptr->dither_sort);
+         png_ptr->dither_sort=NULL;
+      }
+      else
+      {
+         /* This is much harder to do simply (and quickly).  Perhaps
+            we need to go through a median cut routine, but those
+            don't always behave themselves with only a few colors
+            as input.  So we will just find the closest two colors,
+            and throw out one of them (chosen somewhat randomly).
+            [We don't understand this at all, so if someone wants to
+             work on improving it, be our guest - AED, GRP]
+            */
+         int i;
+         int max_d;
+         int num_new_palette;
+         png_dsortp t;
+         png_dsortpp hash;
+
+         t=NULL;
+
+         /* initialize palette index arrays */
+         png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * png_sizeof (png_byte)));
+         png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(num_palette * png_sizeof (png_byte)));
+
+         /* initialize the sort array */
+         for (i = 0; i < num_palette; i++)
+         {
+            png_ptr->index_to_palette[i] = (png_byte)i;
+            png_ptr->palette_to_index[i] = (png_byte)i;
+         }
+
+         hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 *
+            png_sizeof (png_dsortp)));
+         for (i = 0; i < 769; i++)
+            hash[i] = NULL;
+/*         png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */
+
+         num_new_palette = num_palette;
+
+         /* initial wild guess at how far apart the farthest pixel
+            pair we will be eliminating will be.  Larger
+            numbers mean more areas will be allocated, Smaller
+            numbers run the risk of not saving enough data, and
+            having to do this all over again.
+
+            I have not done extensive checking on this number.
+            */
+         max_d = 96;
+
+         while (num_new_palette > maximum_colors)
+         {
+            for (i = 0; i < num_new_palette - 1; i++)
+            {
+               int j;
+
+               for (j = i + 1; j < num_new_palette; j++)
+               {
+                  int d;
+
+                  d = PNG_COLOR_DIST(palette[i], palette[j]);
+
+                  if (d <= max_d)
+                  {
+
+                     t = (png_dsortp)png_malloc_warn(png_ptr,
+                         (png_uint_32)(png_sizeof(png_dsort)));
+                     if (t == NULL)
+                         break;
+                     t->next = hash[d];
+                     t->left = (png_byte)i;
+                     t->right = (png_byte)j;
+                     hash[d] = t;
+                  }
+               }
+               if (t == NULL)
+                  break;
+            }
+
+            if (t != NULL)
+            for (i = 0; i <= max_d; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p;
+
+                  for (p = hash[i]; p; p = p->next)
+                  {
+                     if ((int)png_ptr->index_to_palette[p->left]
+                        < num_new_palette &&
+                        (int)png_ptr->index_to_palette[p->right]
+                        < num_new_palette)
+                     {
+                        int j, next_j;
+
+                        if (num_new_palette & 0x01)
+                        {
+                           j = p->left;
+                           next_j = p->right;
+                        }
+                        else
+                        {
+                           j = p->right;
+                           next_j = p->left;
+                        }
+
+                        num_new_palette--;
+                        palette[png_ptr->index_to_palette[j]]
+                          = palette[num_new_palette];
+                        if (!full_dither)
+                        {
+                           int k;
+
+                           for (k = 0; k < num_palette; k++)
+                           {
+                              if (png_ptr->dither_index[k] ==
+                                 png_ptr->index_to_palette[j])
+                                 png_ptr->dither_index[k] =
+                                    png_ptr->index_to_palette[next_j];
+                              if ((int)png_ptr->dither_index[k] ==
+                                 num_new_palette)
+                                 png_ptr->dither_index[k] =
+                                    png_ptr->index_to_palette[j];
+                           }
+                        }
+
+                        png_ptr->index_to_palette[png_ptr->palette_to_index
+                           [num_new_palette]] = png_ptr->index_to_palette[j];
+                        png_ptr->palette_to_index[png_ptr->index_to_palette[j]]
+                           = png_ptr->palette_to_index[num_new_palette];
+
+                        png_ptr->index_to_palette[j] = (png_byte)num_new_palette;
+                        png_ptr->palette_to_index[num_new_palette] = (png_byte)j;
+                     }
+                     if (num_new_palette <= maximum_colors)
+                        break;
+                  }
+                  if (num_new_palette <= maximum_colors)
+                     break;
+               }
+            }
+
+            for (i = 0; i < 769; i++)
+            {
+               if (hash[i] != NULL)
+               {
+                  png_dsortp p = hash[i];
+                  while (p)
+                  {
+                     t = p->next;
+                     png_free(png_ptr, p);
+                     p = t;
+                  }
+               }
+               hash[i] = 0;
+            }
+            max_d += 96;
+         }
+         png_free(png_ptr, hash);
+         png_free(png_ptr, png_ptr->palette_to_index);
+         png_free(png_ptr, png_ptr->index_to_palette);
+         png_ptr->palette_to_index=NULL;
+         png_ptr->index_to_palette=NULL;
+      }
+      num_palette = maximum_colors;
+   }
+   if (png_ptr->palette == NULL)
+   {
+      png_ptr->palette = palette;
+   }
+   png_ptr->num_palette = (png_uint_16)num_palette;
+
+   if (full_dither)
+   {
+      int i;
+      png_bytep distance;
+      int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS +
+         PNG_DITHER_BLUE_BITS;
+      int num_red = (1 << PNG_DITHER_RED_BITS);
+      int num_green = (1 << PNG_DITHER_GREEN_BITS);
+      int num_blue = (1 << PNG_DITHER_BLUE_BITS);
+      png_size_t num_entries = ((png_size_t)1 << total_bits);
+
+      png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr,
+         (png_uint_32)(num_entries * png_sizeof (png_byte)));
+
+      png_memset(png_ptr->palette_lookup, 0, num_entries *
+         png_sizeof (png_byte));
+
+      distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries *
+         png_sizeof(png_byte)));
+
+      png_memset(distance, 0xff, num_entries * png_sizeof(png_byte));
+
+      for (i = 0; i < num_palette; i++)
+      {
+         int ir, ig, ib;
+         int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS));
+         int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS));
+         int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS));
+
+         for (ir = 0; ir < num_red; ir++)
+         {
+            /* int dr = abs(ir - r); */
+            int dr = ((ir > r) ? ir - r : r - ir);
+            int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS));
+
+            for (ig = 0; ig < num_green; ig++)
+            {
+               /* int dg = abs(ig - g); */
+               int dg = ((ig > g) ? ig - g : g - ig);
+               int dt = dr + dg;
+               int dm = ((dr > dg) ? dr : dg);
+               int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS);
+
+               for (ib = 0; ib < num_blue; ib++)
+               {
+                  int d_index = index_g | ib;
+                  /* int db = abs(ib - b); */
+                  int db = ((ib > b) ? ib - b : b - ib);
+                  int dmax = ((dm > db) ? dm : db);
+                  int d = dmax + dt + db;
+
+                  if (d < (int)distance[d_index])
+                  {
+                     distance[d_index] = (png_byte)d;
+                     png_ptr->palette_lookup[d_index] = (png_byte)i;
+                  }
+               }
+            }
+         }
+      }
+
+      png_free(png_ptr, distance);
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+/* Transform the image from the file_gamma to the screen_gamma.  We
+ * only do transformations on images where the file_gamma and screen_gamma
+ * are not close reciprocals, otherwise it slows things down slightly, and
+ * also needlessly introduces small errors.
+ *
+ * We will turn off gamma transformation later if no semitransparent entries
+ * are present in the tRNS array for palette images.  We can't do it here
+ * because we don't necessarily have the tRNS chunk yet.
+ */
+void PNGAPI
+png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma)
+{
+   png_debug(1, "in png_set_gamma\n");
+   if(png_ptr == NULL) return;
+   if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) ||
+       (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) ||
+       (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE))
+     png_ptr->transformations |= PNG_GAMMA;
+   png_ptr->gamma = (float)file_gamma;
+   png_ptr->screen_gamma = (float)scrn_gamma;
+}
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expand paletted images to RGB, expand grayscale images of
+ * less than 8-bit depth to 8-bit depth, and expand tRNS chunks
+ * to alpha channels.
+ */
+void PNGAPI
+png_set_expand(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+   png_ptr->flags &= !(PNG_FLAG_ROW_INIT);
+#endif
+}
+
+/* GRR 19990627:  the following three functions currently are identical
+ *  to png_set_expand().  However, it is entirely reasonable that someone
+ *  might wish to expand an indexed image to RGB but *not* expand a single,
+ *  fully transparent palette entry to a full alpha channel--perhaps instead
+ *  convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace
+ *  the transparent color with a particular RGB value, or drop tRNS entirely.
+ *  IOW, a future version of the library may make the transformations flag
+ *  a bit more fine-grained, with separate bits for each of these three
+ *  functions.
+ *
+ *  More to the point, these functions make it obvious what libpng will be
+ *  doing, whereas "expand" can (and does) mean any number of things.
+ *
+ *  GRP 20060307: In libpng-1.4.0, png_set_gray_1_2_4_to_8() was modified
+ *  to expand only the sample depth but not to expand the tRNS to alpha.
+ */
+
+/* Expand paletted images to RGB. */
+void PNGAPI
+png_set_palette_to_rgb(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_palette_to_rgb\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+   png_ptr->flags &= !(PNG_FLAG_ROW_INIT);
+#endif
+}
+
+#if !defined(PNG_1_0_X)
+/* Expand grayscale images of less than 8-bit depth to 8 bits. */
+void PNGAPI
+png_set_expand_gray_1_2_4_to_8(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_expand_gray_1_2_4_to_8\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_EXPAND;
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+   png_ptr->flags &= !(PNG_FLAG_ROW_INIT);
+#endif
+}
+#endif
+
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+/* Expand grayscale images of less than 8-bit depth to 8 bits. */
+/* Deprecated as of libpng-1.2.9 */
+void PNGAPI
+png_set_gray_1_2_4_to_8(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_gray_1_2_4_to_8\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+}
+#endif
+
+
+/* Expand tRNS chunks to alpha channels. */
+void PNGAPI
+png_set_tRNS_to_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_tRNS_to_alpha\n");
+   png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS);
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+   png_ptr->flags &= !(PNG_FLAG_ROW_INIT);
+#endif
+}
+#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+void PNGAPI
+png_set_gray_to_rgb(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_gray_to_rgb\n");
+   png_ptr->transformations |= PNG_GRAY_TO_RGB;
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+   png_ptr->flags &= !(PNG_FLAG_ROW_INIT);
+#endif
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+#if defined(PNG_FLOATING_POINT_SUPPORTED)
+/* Convert a RGB image to a grayscale of the same width.  This allows us,
+ * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image.
+ */
+
+void PNGAPI
+png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red,
+   double green)
+{
+      int red_fixed = (int)((float)red*100000.0 + 0.5);
+      int green_fixed = (int)((float)green*100000.0 + 0.5);
+      if(png_ptr == NULL) return;
+      png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed);
+}
+#endif
+
+void PNGAPI
+png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action,
+   png_fixed_point red, png_fixed_point green)
+{
+   png_debug(1, "in png_set_rgb_to_gray\n");
+   if(png_ptr == NULL) return;
+   switch(error_action)
+   {
+      case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY;
+              break;
+      case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN;
+              break;
+      case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR;
+   }
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+      png_ptr->transformations |= PNG_EXPAND;
+#else
+   {
+      png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED.");
+      png_ptr->transformations &= ~PNG_RGB_TO_GRAY;
+   }
+#endif
+   {
+      png_uint_16 red_int, green_int;
+      if(red < 0 || green < 0)
+      {
+         red_int   =  6968; /* .212671 * 32768 + .5 */
+         green_int = 23434; /* .715160 * 32768 + .5 */
+      }
+      else if(red + green < 100000L)
+      {
+        red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L);
+        green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L);
+      }
+      else
+      {
+         png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients");
+         red_int   =  6968;
+         green_int = 23434;
+      }
+      png_ptr->rgb_to_gray_red_coeff   = red_int;
+      png_ptr->rgb_to_gray_green_coeff = green_int;
+      png_ptr->rgb_to_gray_blue_coeff  = (png_uint_16)(32768-red_int-green_int);
+   }
+}
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+void PNGAPI
+png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+   read_user_transform_fn)
+{
+   png_debug(1, "in png_set_read_user_transform_fn\n");
+   if(png_ptr == NULL) return;
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->read_user_transform_fn = read_user_transform_fn;
+#endif
+#ifdef PNG_LEGACY_SUPPORTED
+   if(read_user_transform_fn)
+      png_warning(png_ptr,
+        "This version of libpng does not support user transforms");
+#endif
+}
+#endif
+
+/* Initialize everything needed for the read.  This includes modifying
+ * the palette.
+ */
+void /* PRIVATE */
+png_init_read_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_init_read_transformations\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if(png_ptr != NULL)
+#endif
+  {
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \
+ || defined(PNG_READ_GAMMA_SUPPORTED)
+   int color_type = png_ptr->color_type;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED)
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* Detect gray background and attempt to enable optimization
+    * for gray --> RGB case */
+   /* Note:  if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or
+    * RGB_ALPHA (in which case need_expand is superfluous anyway), the
+    * background color might actually be gray yet not be flagged as such.
+    * This is not a problem for the current code, which uses
+    * PNG_BACKGROUND_IS_GRAY only to decide when to do the
+    * png_do_gray_to_rgb() transformation.
+    */
+   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+       !(color_type & PNG_COLOR_MASK_COLOR))
+   {
+          png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+   } else if ((png_ptr->transformations & PNG_BACKGROUND) &&
+              !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+              (png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+              png_ptr->background.red == png_ptr->background.green &&
+              png_ptr->background.red == png_ptr->background.blue)
+   {
+          png_ptr->mode |= PNG_BACKGROUND_IS_GRAY;
+          png_ptr->background.gray = png_ptr->background.red;
+   }
+#endif
+
+   if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) &&
+       (png_ptr->transformations & PNG_EXPAND))
+   {
+      if (!(color_type & PNG_COLOR_MASK_COLOR))  /* i.e., GRAY or GRAY_ALPHA */
+      {
+         /* expand background and tRNS chunks */
+         switch (png_ptr->bit_depth)
+         {
+            case 1:
+               png_ptr->background.gray *= (png_uint_16)0xff;
+               png_ptr->background.red = png_ptr->background.green
+                 =  png_ptr->background.blue = png_ptr->background.gray;
+               if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+               {
+                 png_ptr->trans_values.gray *= (png_uint_16)0xff;
+                 png_ptr->trans_values.red = png_ptr->trans_values.green
+                   = png_ptr->trans_values.blue = png_ptr->trans_values.gray;
+               }
+               break;
+            case 2:
+               png_ptr->background.gray *= (png_uint_16)0x55;
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+               if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+               {
+                 png_ptr->trans_values.gray *= (png_uint_16)0x55;
+                 png_ptr->trans_values.red = png_ptr->trans_values.green
+                   = png_ptr->trans_values.blue = png_ptr->trans_values.gray;
+               }
+               break;
+            case 4:
+               png_ptr->background.gray *= (png_uint_16)0x11;
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+               if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+               {
+                 png_ptr->trans_values.gray *= (png_uint_16)0x11;
+                 png_ptr->trans_values.red = png_ptr->trans_values.green
+                   = png_ptr->trans_values.blue = png_ptr->trans_values.gray;
+               }
+               break;
+            case 8:
+            case 16:
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+               break;
+         }
+      }
+      else if (color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_ptr->background.red   =
+            png_ptr->palette[png_ptr->background.index].red;
+         png_ptr->background.green =
+            png_ptr->palette[png_ptr->background.index].green;
+         png_ptr->background.blue  =
+            png_ptr->palette[png_ptr->background.index].blue;
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+        if (png_ptr->transformations & PNG_INVERT_ALPHA)
+        {
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+           if (!(png_ptr->transformations & PNG_EXPAND_tRNS))
+#endif
+           {
+           /* invert the alpha channel (in tRNS) unless the pixels are
+              going to be expanded, in which case leave it for later */
+              int i,istop;
+              istop=(int)png_ptr->num_trans;
+              for (i=0; i<istop; i++)
+                 png_ptr->trans[i] = (png_byte)(255 - png_ptr->trans[i]);
+           }
+        }
+#endif
+
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+   png_ptr->background_1 = png_ptr->background;
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+
+   if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0)
+       && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0)
+         < PNG_GAMMA_THRESHOLD))
+   {
+    int i,k;
+    k=0;
+    for (i=0; i<png_ptr->num_trans; i++)
+    {
+      if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff)
+        k=1; /* partial transparency is present */
+    }
+    if (k == 0)
+      png_ptr->transformations &= (~PNG_GAMMA);
+   }
+
+   if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) &&
+        png_ptr->gamma != 0.0)
+   {
+      png_build_gamma_table(png_ptr);
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+      if (png_ptr->transformations & PNG_BACKGROUND)
+      {
+         if (color_type == PNG_COLOR_TYPE_PALETTE)
+         {
+           /* could skip if no transparency and
+           */
+            png_color back, back_1;
+            png_colorp palette = png_ptr->palette;
+            int num_palette = png_ptr->num_palette;
+            int i;
+            if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+            {
+               back.red = png_ptr->gamma_table[png_ptr->background.red];
+               back.green = png_ptr->gamma_table[png_ptr->background.green];
+               back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+               back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+               back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+               back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+            }
+            else
+            {
+               double g, gs;
+
+               switch (png_ptr->background_gamma_type)
+               {
+                  case PNG_BACKGROUND_GAMMA_SCREEN:
+                     g = (png_ptr->screen_gamma);
+                     gs = 1.0;
+                     break;
+                  case PNG_BACKGROUND_GAMMA_FILE:
+                     g = 1.0 / (png_ptr->gamma);
+                     gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+                     break;
+                  case PNG_BACKGROUND_GAMMA_UNIQUE:
+                     g = 1.0 / (png_ptr->background_gamma);
+                     gs = 1.0 / (png_ptr->background_gamma *
+                                 png_ptr->screen_gamma);
+                     break;
+                  default:
+                     g = 1.0;    /* back_1 */
+                     gs = 1.0;   /* back */
+               }
+
+               if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD)
+               {
+                  back.red   = (png_byte)png_ptr->background.red;
+                  back.green = (png_byte)png_ptr->background.green;
+                  back.blue  = (png_byte)png_ptr->background.blue;
+               }
+               else
+               {
+                  back.red = (png_byte)(pow(
+                     (double)png_ptr->background.red/255, gs) * 255.0 + .5);
+                  back.green = (png_byte)(pow(
+                     (double)png_ptr->background.green/255, gs) * 255.0 + .5);
+                  back.blue = (png_byte)(pow(
+                     (double)png_ptr->background.blue/255, gs) * 255.0 + .5);
+               }
+
+               back_1.red = (png_byte)(pow(
+                  (double)png_ptr->background.red/255, g) * 255.0 + .5);
+               back_1.green = (png_byte)(pow(
+                  (double)png_ptr->background.green/255, g) * 255.0 + .5);
+               back_1.blue = (png_byte)(pow(
+                  (double)png_ptr->background.blue/255, g) * 255.0 + .5);
+            }
+            for (i = 0; i < num_palette; i++)
+            {
+               if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff)
+               {
+                  if (png_ptr->trans[i] == 0)
+                  {
+                     palette[i] = back;
+                  }
+                  else /* if (png_ptr->trans[i] != 0xff) */
+                  {
+                     png_byte v, w;
+
+                     v = png_ptr->gamma_to_1[palette[i].red];
+                     png_composite(w, v, png_ptr->trans[i], back_1.red);
+                     palette[i].red = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].green];
+                     png_composite(w, v, png_ptr->trans[i], back_1.green);
+                     palette[i].green = png_ptr->gamma_from_1[w];
+
+                     v = png_ptr->gamma_to_1[palette[i].blue];
+                     png_composite(w, v, png_ptr->trans[i], back_1.blue);
+                     palette[i].blue = png_ptr->gamma_from_1[w];
+                  }
+               }
+               else
+               {
+                  palette[i].red = png_ptr->gamma_table[palette[i].red];
+                  palette[i].green = png_ptr->gamma_table[palette[i].green];
+                  palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+               }
+            }
+         }
+         /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */
+         else
+         /* color_type != PNG_COLOR_TYPE_PALETTE */
+         {
+            double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1);
+            double g = 1.0;
+            double gs = 1.0;
+
+            switch (png_ptr->background_gamma_type)
+            {
+               case PNG_BACKGROUND_GAMMA_SCREEN:
+                  g = (png_ptr->screen_gamma);
+                  gs = 1.0;
+                  break;
+               case PNG_BACKGROUND_GAMMA_FILE:
+                  g = 1.0 / (png_ptr->gamma);
+                  gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+                  break;
+               case PNG_BACKGROUND_GAMMA_UNIQUE:
+                  g = 1.0 / (png_ptr->background_gamma);
+                  gs = 1.0 / (png_ptr->background_gamma *
+                     png_ptr->screen_gamma);
+                  break;
+            }
+
+            png_ptr->background_1.gray = (png_uint_16)(pow(
+               (double)png_ptr->background.gray / m, g) * m + .5);
+            png_ptr->background.gray = (png_uint_16)(pow(
+               (double)png_ptr->background.gray / m, gs) * m + .5);
+
+            if ((png_ptr->background.red != png_ptr->background.green) ||
+                (png_ptr->background.red != png_ptr->background.blue) ||
+                (png_ptr->background.red != png_ptr->background.gray))
+            {
+               /* RGB or RGBA with color background */
+               png_ptr->background_1.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, g) * m + .5);
+               png_ptr->background_1.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, g) * m + .5);
+               png_ptr->background_1.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, g) * m + .5);
+               png_ptr->background.red = (png_uint_16)(pow(
+                  (double)png_ptr->background.red / m, gs) * m + .5);
+               png_ptr->background.green = (png_uint_16)(pow(
+                  (double)png_ptr->background.green / m, gs) * m + .5);
+               png_ptr->background.blue = (png_uint_16)(pow(
+                  (double)png_ptr->background.blue / m, gs) * m + .5);
+            }
+            else
+            {
+               /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */
+               png_ptr->background_1.red = png_ptr->background_1.green
+                 = png_ptr->background_1.blue = png_ptr->background_1.gray;
+               png_ptr->background.red = png_ptr->background.green
+                 = png_ptr->background.blue = png_ptr->background.gray;
+            }
+         }
+      }
+      else
+      /* transformation does not include PNG_BACKGROUND */
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+      if (color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_colorp palette = png_ptr->palette;
+         int num_palette = png_ptr->num_palette;
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            palette[i].red = png_ptr->gamma_table[palette[i].red];
+            palette[i].green = png_ptr->gamma_table[palette[i].green];
+            palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+         }
+      }
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   else
+#endif
+#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   /* No GAMMA transformation */
+   if ((png_ptr->transformations & PNG_BACKGROUND) &&
+       (color_type == PNG_COLOR_TYPE_PALETTE))
+   {
+      int i;
+      int istop = (int)png_ptr->num_trans;
+      png_color back;
+      png_colorp palette = png_ptr->palette;
+
+      back.red   = (png_byte)png_ptr->background.red;
+      back.green = (png_byte)png_ptr->background.green;
+      back.blue  = (png_byte)png_ptr->background.blue;
+
+      for (i = 0; i < istop; i++)
+      {
+         if (png_ptr->trans[i] == 0)
+         {
+            palette[i] = back;
+         }
+         else if (png_ptr->trans[i] != 0xff)
+         {
+            /* The png_composite() macro is defined in png.h */
+            png_composite(palette[i].red, palette[i].red,
+               png_ptr->trans[i], back.red);
+            png_composite(palette[i].green, palette[i].green,
+               png_ptr->trans[i], back.green);
+            png_composite(palette[i].blue, palette[i].blue,
+               png_ptr->trans[i], back.blue);
+         }
+      }
+   }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED */
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   if ((png_ptr->transformations & PNG_SHIFT) &&
+      (color_type == PNG_COLOR_TYPE_PALETTE))
+   {
+      png_uint_16 i;
+      png_uint_16 istop = png_ptr->num_palette;
+      int sr = 8 - png_ptr->sig_bit.red;
+      int sg = 8 - png_ptr->sig_bit.green;
+      int sb = 8 - png_ptr->sig_bit.blue;
+
+      if (sr < 0 || sr > 8)
+         sr = 0;
+      if (sg < 0 || sg > 8)
+         sg = 0;
+      if (sb < 0 || sb > 8)
+         sb = 0;
+      for (i = 0; i < istop; i++)
+      {
+         png_ptr->palette[i].red >>= sr;
+         png_ptr->palette[i].green >>= sg;
+         png_ptr->palette[i].blue >>= sb;
+      }
+   }
+#endif  /* PNG_READ_SHIFT_SUPPORTED */
+ }
+#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \
+ && !defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if(png_ptr)
+      return;
+#endif
+}
+
+/* Modify the info structure to reflect the transformations.  The
+ * info should be updated so a PNG file could be written with it,
+ * assuming the transformations result in valid PNG data.
+ */
+void /* PRIVATE */
+png_read_transform_info(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_read_transform_info\n");
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS))
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         else
+            info_ptr->color_type = PNG_COLOR_TYPE_RGB;
+         info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+      }
+      else
+      {
+         if (png_ptr->num_trans)
+         {
+            if (png_ptr->transformations & PNG_EXPAND_tRNS)
+              info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+            else
+              info_ptr->color_type |= PNG_COLOR_MASK_COLOR;
+         }
+         if (info_ptr->bit_depth < 8)
+            info_ptr->bit_depth = 8;
+         info_ptr->num_trans = 0;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND)
+   {
+      info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+      info_ptr->num_trans = 0;
+      info_ptr->background = png_ptr->background;
+   }
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & PNG_GAMMA)
+   {
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+      info_ptr->gamma = png_ptr->gamma;
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      info_ptr->int_gamma = png_ptr->int_gamma;
+#endif
+   }
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16))
+      info_ptr->bit_depth = 8;
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+      info_ptr->color_type |= PNG_COLOR_MASK_COLOR;
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+      info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR;
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   if (png_ptr->transformations & PNG_DITHER)
+   {
+      if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+         (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) &&
+         png_ptr->palette_lookup && info_ptr->bit_depth == 8)
+      {
+         info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8))
+      info_ptr->bit_depth = 8;
+#endif
+
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      info_ptr->channels = 3;
+   else
+      info_ptr->channels = 1;
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)
+      info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA;
+#endif
+
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+      info_ptr->channels++;
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   /* STRIP_ALPHA and FILLER allowed:  MASK_ALPHA bit stripped above */
+   if ((png_ptr->transformations & PNG_FILLER) &&
+       ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) ||
+       (info_ptr->color_type == PNG_COLOR_TYPE_GRAY)))
+   {
+      info_ptr->channels++;
+      /* if adding a true alpha channel not just filler */
+#if !defined(PNG_1_0_X)
+      if (png_ptr->transformations & PNG_ADD_ALPHA)
+        info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
+#endif
+   }
+#endif
+
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \
+defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   if(png_ptr->transformations & PNG_USER_TRANSFORM)
+     {
+       if(info_ptr->bit_depth < png_ptr->user_transform_depth)
+         info_ptr->bit_depth = png_ptr->user_transform_depth;
+       if(info_ptr->channels < png_ptr->user_transform_channels)
+         info_ptr->channels = png_ptr->user_transform_channels;
+     }
+#endif
+
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels *
+      info_ptr->bit_depth);
+
+   info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width);
+
+#if !defined(PNG_READ_EXPAND_SUPPORTED)
+   if(png_ptr)
+      return;
+#endif
+}
+
+/* Transform the row.  The order of transformations is significant,
+ * and is very touchy.  If you add a transformation, take care to
+ * decide how it fits in with the other transformations here.
+ */
+void /* PRIVATE */
+png_do_read_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_do_read_transformations\n");
+   if (png_ptr->row_buf == NULL)
+   {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+      char msg[50];
+
+      png_snprintf2(msg, 50,
+         "NULL row buffer for row %ld, pass %d", png_ptr->row_number,
+         png_ptr->pass);
+      png_error(png_ptr, msg);
+#else
+      png_error(png_ptr, "NULL row buffer");
+#endif
+   }
+#ifdef PNG_WARN_UNINITIALIZED_ROW
+   if (!(png_ptr->flags & PNG_FLAG_ROW_INIT))
+      /* Application has failed to call either png_read_start_image()
+       * or png_read_update_info() after setting transforms that expand
+       * pixels.  This check added to libpng-1.2.19 */
+#if (PNG_WARN_UNINITIALIZED_ROW==1)
+      png_error(png_ptr, "Uninitialized row");
+#else
+      png_warning(png_ptr, "Uninitialized row");
+#endif
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1,
+            png_ptr->palette, png_ptr->trans, png_ptr->num_trans);
+      }
+      else
+      {
+         if (png_ptr->num_trans &&
+             (png_ptr->transformations & PNG_EXPAND_tRNS))
+            png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+               &(png_ptr->trans_values));
+         else
+            png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1,
+               NULL);
+      }
+   }
+#endif
+
+#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+   if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)
+      png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA));
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+   if (png_ptr->transformations & PNG_RGB_TO_GRAY)
+   {
+      int rgb_error =
+         png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1);
+      if(rgb_error)
+      {
+         png_ptr->rgb_to_gray_status=1;
+         if((png_ptr->transformations & PNG_RGB_TO_GRAY) == 
+             PNG_RGB_TO_GRAY_WARN)
+            png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+         if((png_ptr->transformations & PNG_RGB_TO_GRAY) ==
+             PNG_RGB_TO_GRAY_ERR)
+            png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel");
+      }
+   }
+#endif
+
+/*
+From Andreas Dilger e-mail to png-implement, 26 March 1998:
+
+  In most cases, the "simple transparency" should be done prior to doing
+  gray-to-RGB, or you will have to test 3x as many bytes to check if a
+  pixel is transparent.  You would also need to make sure that the
+  transparency information is upgraded to RGB.
+
+  To summarize, the current flow is:
+  - Gray + simple transparency -> compare 1 or 2 gray bytes and composite
+                                  with background "in place" if transparent,
+                                  convert to RGB if necessary
+  - Gray + alpha -> composite with gray background and remove alpha bytes,
+                                  convert to RGB if necessary
+
+  To support RGB backgrounds for gray images we need:
+  - Gray + simple transparency -> convert to RGB + simple transparency, compare
+                                  3 or 6 bytes and composite with background
+                                  "in place" if transparent (3x compare/pixel
+                                  compared to doing composite with gray bkgrnd)
+  - Gray + alpha -> convert to RGB + alpha, composite with background and
+                                  remove alpha bytes (3x float operations/pixel
+                                  compared with composite on gray background)
+
+  Greg's change will do this.  The reason it wasn't done before is for
+  performance, as this increases the per-pixel operations.  If we would check
+  in advance if the background was gray or RGB, and position the gray-to-RGB
+  transform appropriately, then it would save a lot of work/time.
+ */
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* if gray -> RGB, do so now only if background is non-gray; else do later
+    * for performance reasons */
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+       !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if ((png_ptr->transformations & PNG_BACKGROUND) &&
+      ((png_ptr->num_trans != 0 ) ||
+      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA)))
+      png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->trans_values), &(png_ptr->background)
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+         , &(png_ptr->background_1),
+         png_ptr->gamma_table, png_ptr->gamma_from_1,
+         png_ptr->gamma_to_1, png_ptr->gamma_16_table,
+         png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1,
+         png_ptr->gamma_shift
+#endif
+);
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if ((png_ptr->transformations & PNG_GAMMA) &&
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+      !((png_ptr->transformations & PNG_BACKGROUND) &&
+      ((png_ptr->num_trans != 0) ||
+      (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) &&
+#endif
+      (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE))
+      png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->gamma_table, png_ptr->gamma_16_table,
+         png_ptr->gamma_shift);
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+   if (png_ptr->transformations & PNG_16_TO_8)
+      png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+   if (png_ptr->transformations & PNG_DITHER)
+   {
+      png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->palette_lookup, png_ptr->dither_index);
+      if(png_ptr->row_info.rowbytes == (png_uint_32)0)
+         png_error(png_ptr, "png_do_dither returned rowbytes=0");
+   }
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   /* if gray -> RGB, do so now only if we did not do so above */
+   if ((png_ptr->transformations & PNG_GRAY_TO_RGB) &&
+       (png_ptr->mode & PNG_BACKGROUND_IS_GRAY))
+      png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         (png_uint_32)png_ptr->filler, png_ptr->flags);
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_ALPHA)
+      png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_ALPHA)
+      png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED)
+   if (png_ptr->transformations & PNG_USER_TRANSFORM)
+    {
+      if(png_ptr->read_user_transform_fn != NULL)
+        (*(png_ptr->read_user_transform_fn)) /* user read transform function */
+          (png_ptr,                    /* png_ptr */
+           &(png_ptr->row_info),       /* row_info:     */
+             /*  png_uint_32 width;          width of row */
+             /*  png_uint_32 rowbytes;       number of bytes in row */
+             /*  png_byte color_type;        color type of pixels */
+             /*  png_byte bit_depth;         bit depth of samples */
+             /*  png_byte channels;          number of channels (1-4) */
+             /*  png_byte pixel_depth;       bits per pixel (depth*channels) */
+           png_ptr->row_buf + 1);      /* start of pixel data for row */
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+      if(png_ptr->user_transform_depth)
+         png_ptr->row_info.bit_depth = png_ptr->user_transform_depth;
+      if(png_ptr->user_transform_channels)
+         png_ptr->row_info.channels = png_ptr->user_transform_channels;
+#endif
+      png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+         png_ptr->row_info.channels);
+      png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+         png_ptr->row_info.width);
+   }
+#endif
+
+}
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel,
+ * without changing the actual values.  Thus, if you had a row with
+ * a bit depth of 1, you would end up with bytes that only contained
+ * the numbers 0 or 1.  If you would rather they contain 0 and 255, use
+ * png_do_shift() after this.
+ */
+void /* PRIVATE */
+png_do_unpack(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_unpack\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && row_info->bit_depth < 8)
+#else
+   if (row_info->bit_depth < 8)
+#endif
+   {
+      png_uint_32 i;
+      png_uint_32 row_width=row_info->width;
+
+      switch (row_info->bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x01);
+               if (shift == 7)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift++;
+
+               dp--;
+            }
+            break;
+         }
+         case 2:
+         {
+
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 2);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x03);
+               if (shift == 6)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift += 2;
+
+               dp--;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)row_width - 1;
+            png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+            for (i = 0; i < row_width; i++)
+            {
+               *dp = (png_byte)((*sp >> shift) & 0x0f);
+               if (shift == 4)
+               {
+                  shift = 0;
+                  sp--;
+               }
+               else
+                  shift = 4;
+
+               dp--;
+            }
+            break;
+         }
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_width * row_info->channels;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED)
+/* Reverse the effects of png_do_shift.  This routine merely shifts the
+ * pixels back to their significant bits values.  Thus, if you have
+ * a row of bit depth 8, but only 5 are significant, this will shift
+ * the values back to 0 through 31.
+ */
+void /* PRIVATE */
+png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits)
+{
+   png_debug(1, "in png_do_unshift\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL && sig_bits != NULL &&
+#endif
+       row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift[4];
+      int channels = 0;
+      int c;
+      png_uint_16 value = 0;
+      png_uint_32 row_width = row_info->width;
+
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->red;
+         shift[channels++] = row_info->bit_depth - sig_bits->green;
+         shift[channels++] = row_info->bit_depth - sig_bits->blue;
+      }
+      else
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->gray;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift[channels++] = row_info->bit_depth - sig_bits->alpha;
+      }
+
+      for (c = 0; c < channels; c++)
+      {
+         if (shift[c] <= 0)
+            shift[c] = 0;
+         else
+            value = 1;
+      }
+
+      if (!value)
+         return;
+
+      switch (row_info->bit_depth)
+      {
+         case 2:
+         {
+            png_bytep bp;
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+
+            for (bp = row, i = 0; i < istop; i++)
+            {
+               *bp >>= 1;
+               *bp++ &= 0x55;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) |
+               (png_byte)((int)0xf >> shift[0]));
+
+            for (i = 0; i < istop; i++)
+            {
+               *bp >>= shift[0];
+               *bp++ &= mask;
+            }
+            break;
+         }
+         case 8:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = row_width * channels;
+
+            for (i = 0; i < istop; i++)
+            {
+               *bp++ >>= shift[i%channels];
+            }
+            break;
+         }
+         case 16:
+         {
+            png_bytep bp = row;
+            png_uint_32 i;
+            png_uint_32 istop = channels * row_width;
+
+            for (i = 0; i < istop; i++)
+            {
+               value = (png_uint_16)((*bp << 8) + *(bp + 1));
+               value >>= shift[i%channels];
+               *bp++ = (png_byte)(value >> 8);
+               *bp++ = (png_byte)(value & 0xff);
+            }
+            break;
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_16_TO_8_SUPPORTED)
+/* chop rows of bit depth 16 down to 8 */
+void /* PRIVATE */
+png_do_chop(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_chop\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && row_info->bit_depth == 16)
+#else
+   if (row_info->bit_depth == 16)
+#endif
+   {
+      png_bytep sp = row;
+      png_bytep dp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->width * row_info->channels;
+
+      for (i = 0; i<istop; i++, sp += 2, dp++)
+      {
+#if defined(PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED)
+      /* This does a more accurate scaling of the 16-bit color
+       * value, rather than a simple low-byte truncation.
+       *
+       * What the ideal calculation should be:
+       *   *dp = (((((png_uint_32)(*sp) << 8) |
+       *          (png_uint_32)(*(sp + 1))) * 255 + 127) / (png_uint_32)65535L;
+       *
+       * GRR: no, I think this is what it really should be:
+       *   *dp = (((((png_uint_32)(*sp) << 8) |
+       *           (png_uint_32)(*(sp + 1))) + 128L) / (png_uint_32)257L;
+       *
+       * GRR: here's the exact calculation with shifts:
+       *   temp = (((png_uint_32)(*sp) << 8) | (png_uint_32)(*(sp + 1))) + 128L;
+       *   *dp = (temp - (temp >> 8)) >> 8;
+       *
+       * Approximate calculation with shift/add instead of multiply/divide:
+       *   *dp = ((((png_uint_32)(*sp) << 8) |
+       *          (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8;
+       *
+       * What we actually do to avoid extra shifting and conversion:
+       */
+
+         *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0);
+#else
+       /* Simply discard the low order byte */
+         *dp = *sp;
+#endif
+      }
+      row_info->bit_depth = 8;
+      row_info->pixel_depth = (png_byte)(8 * row_info->channels);
+      row_info->rowbytes = row_info->width * row_info->channels;
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_read_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_swap_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from RGBA to ARGB */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+         /* This converts from RRGGBBAA to AARRGGBB */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from GA to AG */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save;
+            }
+         }
+         /* This converts from GGAA to AAGG */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_byte save[2];
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               save[0] = *(--sp);
+               save[1] = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = save[0];
+               *(--dp) = save[1];
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_read_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_invert_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This inverts the alpha channel in RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+
+/*             This does nothing:
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               We can replace it with:
+*/
+               sp-=3;
+               dp=sp;
+            }
+         }
+         /* This inverts the alpha channel in RRGGBBAA */
+         else
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = (png_byte)(255 - *(--sp));
+
+/*             This does nothing:
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               We can replace it with:
+*/
+               sp-=6;
+               dp=sp;
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This inverts the alpha channel in GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = *(--sp);
+            }
+         }
+         /* This inverts the alpha channel in GGAA */
+         else
+         {
+            png_bytep sp  = row + row_info->rowbytes;
+            png_bytep dp = sp;
+            png_uint_32 i;
+
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = (png_byte)(255 - *(--sp));
+               *(--dp) = (png_byte)(255 - *(--sp));
+/*
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+*/
+               sp-=2;
+               dp=sp;
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+/* Add filler channel if we have RGB color */
+void /* PRIVATE */
+png_do_read_filler(png_row_infop row_info, png_bytep row,
+   png_uint_32 filler, png_uint_32 flags)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_byte hi_filler = (png_byte)((filler>>8) & 0xff);
+   png_byte lo_filler = (png_byte)(filler & 0xff);
+
+   png_debug(1, "in png_do_read_filler\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL  && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      if(row_info->bit_depth == 8)
+      {
+         /* This changes the data from G to GX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp =  sp + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      /* This changes the data from G to XG */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+      }
+      else if(row_info->bit_depth == 16)
+      {
+         /* This changes the data from GG to GGXX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = hi_filler;
+            *(--dp) = lo_filler;
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+         /* This changes the data from GG to XXGG */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 2;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+   } /* COLOR_TYPE == GRAY */
+   else if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      if(row_info->bit_depth == 8)
+      {
+         /* This changes the data from RGB to RGBX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp  + (png_size_t)row_width;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      /* This changes the data from RGB to XRGB */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 3;
+            png_bytep dp = sp + (png_size_t)row_width;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 32;
+            row_info->rowbytes = row_width * 4;
+         }
+      }
+      else if(row_info->bit_depth == 16)
+      {
+         /* This changes the data from RRGGBB to RRGGBBXX */
+         if (flags & PNG_FLAG_FILLER_AFTER)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 6;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 1; i < row_width; i++)
+            {
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+            }
+            *(--dp) = hi_filler;
+            *(--dp) = lo_filler;
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 8;
+         }
+         /* This changes the data from RRGGBB to XXRRGGBB */
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 6;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = *(--sp);
+               *(--dp) = hi_filler;
+               *(--dp) = lo_filler;
+            }
+            row_info->channels = 4;
+            row_info->pixel_depth = 64;
+            row_info->rowbytes = row_width * 8;
+         }
+      }
+   } /* COLOR_TYPE == RGB */
+}
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+/* expand grayscale files to RGB, with or without alpha */
+void /* PRIVATE */
+png_do_gray_to_rgb(png_row_infop row_info, png_bytep row)
+{
+   png_uint_32 i;
+   png_uint_32 row_width = row_info->width;
+
+   png_debug(1, "in png_do_gray_to_rgb\n");
+   if (row_info->bit_depth >= 8 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      !(row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + (png_size_t)row_width - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *(sp--);
+            }
+         }
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp = row + (png_size_t)row_width * 2 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 2;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *sp;
+               *(dp--) = *(sp--);
+            }
+         }
+         else
+         {
+            png_bytep sp = row + (png_size_t)row_width * 4 - 1;
+            png_bytep dp = sp  + (png_size_t)row_width * 4;
+            for (i = 0; i < row_width; i++)
+            {
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *sp;
+               *(dp--) = *(sp - 1);
+               *(dp--) = *(sp--);
+               *(dp--) = *(sp--);
+            }
+         }
+      }
+      row_info->channels += (png_byte)2;
+      row_info->color_type |= PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+         row_info->bit_depth);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+   }
+}
+#endif
+
+#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+/* reduce RGB files to grayscale, with or without alpha
+ * using the equation given in Poynton's ColorFAQ at
+ * <http://www.inforamp.net/~poynton/>
+ * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net
+ *
+ *     Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
+ *
+ *  We approximate this with
+ *
+ *     Y = 0.21268 * R    + 0.7151 * G    + 0.07217 * B
+ *
+ *  which can be expressed with integers as
+ *
+ *     Y = (6969 * R + 23434 * G + 2365 * B)/32768
+ *
+ *  The calculation is to be done in a linear colorspace.
+ *
+ *  Other integer coefficents can be used via png_set_rgb_to_gray().
+ */
+int /* PRIVATE */
+png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row)
+
+{
+   png_uint_32 i;
+
+   png_uint_32 row_width = row_info->width;
+   int rgb_error = 0;
+
+   png_debug(1, "in png_do_rgb_to_gray\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff;
+      png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff;
+      png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff;
+
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (row_info->bit_depth == 8)
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte green = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte blue  = png_ptr->gamma_to_1[*(sp++)];
+                  if(red != green || red != blue)
+                  {
+                     rgb_error |= 1;
+                     *(dp++) = png_ptr->gamma_from_1[
+                       (rc*red+gc*green+bc*blue)>>15];
+                  }
+                  else
+                     *(dp++) = *(sp-1);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = *(sp++);
+                  png_byte green = *(sp++);
+                  png_byte blue  = *(sp++);
+                  if(red != green || red != blue)
+                  {
+                     rgb_error |= 1;
+                     *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15);
+                  }
+                  else
+                     *(dp++) = *(sp-1);
+               }
+            }
+         }
+
+         else /* RGB bit_depth == 16 */
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_16_to_1 != NULL &&
+                png_ptr->gamma_16_from_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, w;
+
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+
+                  if(red == green && red == blue)
+                     w = red;
+                  else
+                  {
+                     png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff) >>
+                                  png_ptr->gamma_shift][red>>8];
+                     png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >>
+                                  png_ptr->gamma_shift][green>>8];
+                     png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff) >>
+                                  png_ptr->gamma_shift][blue>>8];
+                     png_uint_16 gray16  = (png_uint_16)((rc*red_1 + gc*green_1
+                                  + bc*blue_1)>>15);
+                     w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+                         png_ptr->gamma_shift][gray16 >> 8];
+                     rgb_error |= 1;
+                  }
+
+                  *(dp++) = (png_byte)((w>>8) & 0xff);
+                  *(dp++) = (png_byte)(w & 0xff);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, gray16;
+
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  gray16  = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
+                  *(dp++) = (png_byte)((gray16>>8) & 0xff);
+                  *(dp++) = (png_byte)(gray16 & 0xff);
+               }
+            }
+         }
+      }
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         if (row_info->bit_depth == 8)
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte green = png_ptr->gamma_to_1[*(sp++)];
+                  png_byte blue  = png_ptr->gamma_to_1[*(sp++)];
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  *(dp++) =  png_ptr->gamma_from_1
+                             [(rc*red + gc*green + bc*blue)>>15];
+                  *(dp++) = *(sp++);  /* alpha */
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_byte red   = *(sp++);
+                  png_byte green = *(sp++);
+                  png_byte blue  = *(sp++);
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  *(dp++) =  (png_byte)((rc*red + gc*green + bc*blue)>>15);
+                  *(dp++) = *(sp++);  /* alpha */
+               }
+            }
+         }
+         else /* RGBA bit_depth == 16 */
+         {
+#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED)
+            if (png_ptr->gamma_16_to_1 != NULL &&
+                png_ptr->gamma_16_from_1 != NULL)
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, w;
+
+                  red   = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2;
+
+                  if(red == green && red == blue)
+                     w = red;
+                  else
+                  {
+                     png_uint_16 red_1   = png_ptr->gamma_16_to_1[(red&0xff) >>
+                                  png_ptr->gamma_shift][red>>8];
+                     png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >>
+                                  png_ptr->gamma_shift][green>>8];
+                     png_uint_16 blue_1  = png_ptr->gamma_16_to_1[(blue&0xff) >>
+                                  png_ptr->gamma_shift][blue>>8];
+                     png_uint_16 gray16  = (png_uint_16)((rc * red_1
+                                  + gc * green_1 + bc * blue_1)>>15);
+                     w = png_ptr->gamma_16_from_1[(gray16&0xff) >>
+                         png_ptr->gamma_shift][gray16 >> 8];
+                     rgb_error |= 1;
+                  }
+
+                  *(dp++) = (png_byte)((w>>8) & 0xff);
+                  *(dp++) = (png_byte)(w & 0xff);
+                  *(dp++) = *(sp++);  /* alpha */
+                  *(dp++) = *(sp++);
+               }
+            }
+            else
+#endif
+            {
+               png_bytep sp = row;
+               png_bytep dp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 red, green, blue, gray16;
+                  red   = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  blue  = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2;
+                  if(red != green || red != blue)
+                     rgb_error |= 1;
+                  gray16  = (png_uint_16)((rc*red + gc*green + bc*blue)>>15);
+                  *(dp++) = (png_byte)((gray16>>8) & 0xff);
+                  *(dp++) = (png_byte)(gray16 & 0xff);
+                  *(dp++) = *(sp++);  /* alpha */
+                  *(dp++) = *(sp++);
+               }
+            }
+         }
+      }
+   row_info->channels -= (png_byte)2;
+      row_info->color_type &= ~PNG_COLOR_MASK_COLOR;
+      row_info->pixel_depth = (png_byte)(row_info->channels *
+         row_info->bit_depth);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+   }
+   return rgb_error;
+}
+#endif
+
+/* Build a grayscale palette.  Palette is assumed to be 1 << bit_depth
+ * large of png_color.  This lets grayscale images be treated as
+ * paletted.  Most useful for gamma correction and simplification
+ * of code.
+ */
+void PNGAPI
+png_build_grayscale_palette(int bit_depth, png_colorp palette)
+{
+   int num_palette;
+   int color_inc;
+   int i;
+   int v;
+
+   png_debug(1, "in png_do_build_grayscale_palette\n");
+   if (palette == NULL)
+      return;
+
+   switch (bit_depth)
+   {
+      case 1:
+         num_palette = 2;
+         color_inc = 0xff;
+         break;
+      case 2:
+         num_palette = 4;
+         color_inc = 0x55;
+         break;
+      case 4:
+         num_palette = 16;
+         color_inc = 0x11;
+         break;
+      case 8:
+         num_palette = 256;
+         color_inc = 1;
+         break;
+      default:
+         num_palette = 0;
+         color_inc = 0;
+         break;
+   }
+
+   for (i = 0, v = 0; i < num_palette; i++, v += color_inc)
+   {
+      palette[i].red = (png_byte)v;
+      palette[i].green = (png_byte)v;
+      palette[i].blue = (png_byte)v;
+   }
+}
+
+/* This function is currently unused.  Do we really need it? */
+#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED)
+void /* PRIVATE */
+png_correct_palette(png_structp png_ptr, png_colorp palette,
+   int num_palette)
+{
+   png_debug(1, "in png_correct_palette\n");
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \
+    defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED)
+   if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND))
+   {
+      png_color back, back_1;
+
+      if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE)
+      {
+         back.red = png_ptr->gamma_table[png_ptr->background.red];
+         back.green = png_ptr->gamma_table[png_ptr->background.green];
+         back.blue = png_ptr->gamma_table[png_ptr->background.blue];
+
+         back_1.red = png_ptr->gamma_to_1[png_ptr->background.red];
+         back_1.green = png_ptr->gamma_to_1[png_ptr->background.green];
+         back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue];
+      }
+      else
+      {
+         double g;
+
+         g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma);
+
+         if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN ||
+             fabs(g - 1.0) < PNG_GAMMA_THRESHOLD)
+         {
+            back.red = png_ptr->background.red;
+            back.green = png_ptr->background.green;
+            back.blue = png_ptr->background.blue;
+         }
+         else
+         {
+            back.red =
+               (png_byte)(pow((double)png_ptr->background.red/255, g) *
+                255.0 + 0.5);
+            back.green =
+               (png_byte)(pow((double)png_ptr->background.green/255, g) *
+                255.0 + 0.5);
+            back.blue =
+               (png_byte)(pow((double)png_ptr->background.blue/255, g) *
+                255.0 + 0.5);
+         }
+
+         g = 1.0 / png_ptr->background_gamma;
+
+         back_1.red =
+            (png_byte)(pow((double)png_ptr->background.red/255, g) *
+             255.0 + 0.5);
+         back_1.green =
+            (png_byte)(pow((double)png_ptr->background.green/255, g) *
+             255.0 + 0.5);
+         back_1.blue =
+            (png_byte)(pow((double)png_ptr->background.blue/255, g) *
+             255.0 + 0.5);
+      }
+
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_uint_32 i;
+
+         for (i = 0; i < (png_uint_32)num_palette; i++)
+         {
+            if (i < png_ptr->num_trans && png_ptr->trans[i] == 0)
+            {
+               palette[i] = back;
+            }
+            else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff)
+            {
+               png_byte v, w;
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].red];
+               png_composite(w, v, png_ptr->trans[i], back_1.red);
+               palette[i].red = png_ptr->gamma_from_1[w];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].green];
+               png_composite(w, v, png_ptr->trans[i], back_1.green);
+               palette[i].green = png_ptr->gamma_from_1[w];
+
+               v = png_ptr->gamma_to_1[png_ptr->palette[i].blue];
+               png_composite(w, v, png_ptr->trans[i], back_1.blue);
+               palette[i].blue = png_ptr->gamma_from_1[w];
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+      else
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (palette[i].red == (png_byte)png_ptr->trans_values.gray)
+            {
+               palette[i] = back;
+            }
+            else
+            {
+               palette[i].red = png_ptr->gamma_table[palette[i].red];
+               palette[i].green = png_ptr->gamma_table[palette[i].green];
+               palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+            }
+         }
+      }
+   }
+   else
+#endif
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   if (png_ptr->transformations & PNG_GAMMA)
+   {
+      int i;
+
+      for (i = 0; i < num_palette; i++)
+      {
+         palette[i].red = png_ptr->gamma_table[palette[i].red];
+         palette[i].green = png_ptr->gamma_table[palette[i].green];
+         palette[i].blue = png_ptr->gamma_table[palette[i].blue];
+      }
+   }
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   else
+#endif
+#endif
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+   if (png_ptr->transformations & PNG_BACKGROUND)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         png_color back;
+
+         back.red   = (png_byte)png_ptr->background.red;
+         back.green = (png_byte)png_ptr->background.green;
+         back.blue  = (png_byte)png_ptr->background.blue;
+
+         for (i = 0; i < (int)png_ptr->num_trans; i++)
+         {
+            if (png_ptr->trans[i] == 0)
+            {
+               palette[i].red = back.red;
+               palette[i].green = back.green;
+               palette[i].blue = back.blue;
+            }
+            else if (png_ptr->trans[i] != 0xff)
+            {
+               png_composite(palette[i].red, png_ptr->palette[i].red,
+                  png_ptr->trans[i], back.red);
+               png_composite(palette[i].green, png_ptr->palette[i].green,
+                  png_ptr->trans[i], back.green);
+               png_composite(palette[i].blue, png_ptr->palette[i].blue,
+                  png_ptr->trans[i], back.blue);
+            }
+         }
+      }
+      else /* assume grayscale palette (what else could it be?) */
+      {
+         int i;
+
+         for (i = 0; i < num_palette; i++)
+         {
+            if (i == (png_byte)png_ptr->trans_values.gray)
+            {
+               palette[i].red = (png_byte)png_ptr->background.red;
+               palette[i].green = (png_byte)png_ptr->background.green;
+               palette[i].blue = (png_byte)png_ptr->background.blue;
+            }
+         }
+      }
+   }
+#endif
+}
+#endif
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED)
+/* Replace any alpha or transparency with the supplied background color.
+ * "background" is already in the screen gamma, while "background_1" is
+ * at a gamma of 1.0.  Paletted files have already been taken care of.
+ */
+void /* PRIVATE */
+png_do_background(png_row_infop row_info, png_bytep row,
+   png_color_16p trans_values, png_color_16p background
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+   , png_color_16p background_1,
+   png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1,
+   png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1,
+   png_uint_16pp gamma_16_to_1, int gamma_shift
+#endif
+   )
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+   int shift;
+
+   png_debug(1, "in png_do_background\n");
+   if (background != NULL &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) ||
+      (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  sp = row;
+                  shift = 7;
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((png_uint_16)((*sp >> shift) & 0x01)
+                        == trans_values->gray)
+                     {
+                        *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                        *sp |= (png_byte)(background->gray << shift);
+                     }
+                     if (!shift)
+                     {
+                        shift = 7;
+                        sp++;
+                     }
+                     else
+                        shift--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x03)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        else
+                        {
+                           png_byte p = (png_byte)((*sp >> shift) & 0x03);
+                           png_byte g = (png_byte)((gamma_table [p | (p << 2) |
+                               (p << 4) | (p << 6)] >> 6) & 0x03);
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(g << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 6;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x03)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 6;
+                           sp++;
+                        }
+                        else
+                           shift -= 2;
+                     }
+                  }
+                  break;
+               }
+               case 4:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x0f)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        else
+                        {
+                           png_byte p = (png_byte)((*sp >> shift) & 0x0f);
+                           png_byte g = (png_byte)((gamma_table[p |
+                             (p << 4)] >> 4) & 0x0f);
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(g << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     shift = 4;
+                     for (i = 0; i < row_width; i++)
+                     {
+                        if ((png_uint_16)((*sp >> shift) & 0x0f)
+                            == trans_values->gray)
+                        {
+                           *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                           *sp |= (png_byte)(background->gray << shift);
+                        }
+                        if (!shift)
+                        {
+                           shift = 4;
+                           sp++;
+                        }
+                        else
+                           shift -= 4;
+                     }
+                  }
+                  break;
+               }
+               case 8:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_table != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = (png_byte)background->gray;
+                        }
+                        else
+                        {
+                           *sp = gamma_table[*sp];
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp++)
+                     {
+                        if (*sp == trans_values->gray)
+                        {
+                           *sp = (png_byte)background->gray;
+                        }
+                     }
+                  }
+                  break;
+               }
+               case 16:
+               {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                  if (gamma_16 != NULL)
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        if (v == trans_values->gray)
+                        {
+                           /* background is already in screen gamma */
+                           *sp = (png_byte)((background->gray >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(background->gray & 0xff);
+                        }
+                        else
+                        {
+                           v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                           *sp = (png_byte)((v >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(v & 0xff);
+                        }
+                     }
+                  }
+                  else
+#endif
+                  {
+                     sp = row;
+                     for (i = 0; i < row_width; i++, sp += 2)
+                     {
+                        png_uint_16 v;
+
+                        v = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        if (v == trans_values->gray)
+                        {
+                           *sp = (png_byte)((background->gray >> 8) & 0xff);
+                           *(sp + 1) = (png_byte)(background->gray & 0xff);
+                        }
+                     }
+                  }
+                  break;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_table != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = (png_byte)background->red;
+                        *(sp + 1) = (png_byte)background->green;
+                        *(sp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        *sp = gamma_table[*sp];
+                        *(sp + 1) = gamma_table[*(sp + 1)];
+                        *(sp + 2) = gamma_table[*(sp + 2)];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 3)
+                  {
+                     if (*sp == trans_values->red &&
+                        *(sp + 1) == trans_values->green &&
+                        *(sp + 2) == trans_values->blue)
+                     {
+                        *sp = (png_byte)background->red;
+                        *(sp + 1) = (png_byte)background->green;
+                        *(sp + 2) = (png_byte)background->blue;
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL)
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                     png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+                     png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5));
+                     if (r == trans_values->red && g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        /* background is already in screen gamma */
+                        *sp = (png_byte)((background->red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(background->red & 0xff);
+                        *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(background->green & 0xff);
+                        *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *sp = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(sp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(sp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  for (i = 0; i < row_width; i++, sp += 6)
+                  {
+                     png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1));
+                     png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+                     png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5));
+
+                     if (r == trans_values->red && g == trans_values->green &&
+                        b == trans_values->blue)
+                     {
+                        *sp = (png_byte)((background->red >> 8) & 0xff);
+                        *(sp + 1) = (png_byte)(background->red & 0xff);
+                        *(sp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(sp + 3) = (png_byte)(background->green & 0xff);
+                        *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(sp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 2, dp++)
+                  {
+                     png_uint_16 a = *(sp + 1);
+
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)background->gray;
+                     }
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, background_1->gray);
+                        *dp = gamma_from_1[w];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 2, dp++)
+                  {
+                     png_byte a = *(sp + 1);
+
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)background->gray;
+                     }
+                     else
+                     {
+                        png_composite(*dp, *sp, a, background_1->gray);
+                     }
+#else
+                     *dp = (png_byte)background->gray;
+#endif
+                  }
+               }
+            }
+            else /* if (png_ptr->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+                  {
+                     png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else if (a == 0)
+#else
+                     else
+#endif
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)((background->gray >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->gray & 0xff);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else
+                     {
+                        png_uint_16 g, v, w;
+
+                        g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(v, g, a, background_1->gray);
+                        w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8];
+                        *dp = (png_byte)((w >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(w & 0xff);
+                     }
+#endif
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 2)
+                  {
+                     png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_memcpy(dp, sp, 2);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else if (a == 0)
+#else
+                     else
+#endif
+                     {
+                        *dp = (png_byte)((background->gray >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->gray & 0xff);
+                     }
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+                     else
+                     {
+                        png_uint_16 g, v;
+
+                        g = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        png_composite_16(v, g, a, background_1->gray);
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                     }
+#endif
+                  }
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_to_1 != NULL && gamma_from_1 != NULL &&
+                   gamma_table != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *dp = gamma_table[*sp];
+                        *(dp + 1) = gamma_table[*(sp + 1)];
+                        *(dp + 2) = gamma_table[*(sp + 2)];
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)background->red;
+                        *(dp + 1) = (png_byte)background->green;
+                        *(dp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        png_byte v, w;
+
+                        v = gamma_to_1[*sp];
+                        png_composite(w, v, a, background_1->red);
+                        *dp = gamma_from_1[w];
+                        v = gamma_to_1[*(sp + 1)];
+                        png_composite(w, v, a, background_1->green);
+                        *(dp + 1) = gamma_from_1[w];
+                        v = gamma_to_1[*(sp + 2)];
+                        png_composite(w, v, a, background_1->blue);
+                        *(dp + 2) = gamma_from_1[w];
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 4, dp += 3)
+                  {
+                     png_byte a = *(sp + 3);
+
+                     if (a == 0xff)
+                     {
+                        *dp = *sp;
+                        *(dp + 1) = *(sp + 1);
+                        *(dp + 2) = *(sp + 2);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)background->red;
+                        *(dp + 1) = (png_byte)background->green;
+                        *(dp + 2) = (png_byte)background->blue;
+                     }
+                     else
+                     {
+                        png_composite(*dp, *sp, a, background->red);
+                        png_composite(*(dp + 1), *(sp + 1), a,
+                           background->green);
+                        png_composite(*(dp + 2), *(sp + 2), a,
+                           background->blue);
+                     }
+                  }
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+               if (gamma_16 != NULL && gamma_16_from_1 != NULL &&
+                   gamma_16_to_1 != NULL)
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                         << 8) + (png_uint_16)(*(sp + 7)));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_uint_16 v;
+
+                        v = gamma_16[*(sp + 1) >> gamma_shift][*sp];
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                     else if (a == 0)
+                     {
+                        /* background is already in screen gamma */
+                        *dp = (png_byte)((background->red >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->red & 0xff);
+                        *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(background->green & 0xff);
+                        *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v, w, x;
+
+                        v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp];
+                        png_composite_16(w, v, a, background_1->red);
+                        x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+                        *dp = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(x & 0xff);
+                        v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)];
+                        png_composite_16(w, v, a, background_1->green);
+                        x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8];
+                        *(dp + 2) = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(x & 0xff);
+                        v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)];
+                        png_composite_16(w, v, a, background_1->blue);
+                        x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8];
+                        *(dp + 4) = (png_byte)((x >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(x & 0xff);
+                     }
+                  }
+               }
+               else
+#endif
+               {
+                  sp = row;
+                  dp = row;
+                  for (i = 0; i < row_width; i++, sp += 8, dp += 6)
+                  {
+                     png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6))
+                        << 8) + (png_uint_16)(*(sp + 7)));
+                     if (a == (png_uint_16)0xffff)
+                     {
+                        png_memcpy(dp, sp, 6);
+                     }
+                     else if (a == 0)
+                     {
+                        *dp = (png_byte)((background->red >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(background->red & 0xff);
+                        *(dp + 2) = (png_byte)((background->green >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(background->green & 0xff);
+                        *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(background->blue & 0xff);
+                     }
+                     else
+                     {
+                        png_uint_16 v;
+
+                        png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1));
+                        png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8)
+                            + *(sp + 3));
+                        png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8)
+                            + *(sp + 5));
+
+                        png_composite_16(v, r, a, background->red);
+                        *dp = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 1) = (png_byte)(v & 0xff);
+                        png_composite_16(v, g, a, background->green);
+                        *(dp + 2) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 3) = (png_byte)(v & 0xff);
+                        png_composite_16(v, b, a, background->blue);
+                        *(dp + 4) = (png_byte)((v >> 8) & 0xff);
+                        *(dp + 5) = (png_byte)(v & 0xff);
+                     }
+                  }
+               }
+            }
+            break;
+         }
+      }
+
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+         row_info->channels--;
+         row_info->pixel_depth = (png_byte)(row_info->channels *
+            row_info->bit_depth);
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+/* Gamma correct the image, avoiding the alpha channel.  Make sure
+ * you do this after you deal with the transparency issue on grayscale
+ * or RGB images. If your bit depth is 8, use gamma_table, if it
+ * is 16, use gamma_16_table and gamma_shift.  Build these with
+ * build_gamma_table().
+ */
+void /* PRIVATE */
+png_do_gamma(png_row_infop row_info, png_bytep row,
+   png_bytep gamma_table, png_uint_16pp gamma_16_table,
+   int gamma_shift)
+{
+   png_bytep sp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_gamma\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       ((row_info->bit_depth <= 8 && gamma_table != NULL) ||
+        (row_info->bit_depth == 16 && gamma_16_table != NULL)))
+   {
+      switch (row_info->color_type)
+      {
+         case PNG_COLOR_TYPE_RGB:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v;
+
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_RGB_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  *sp = gamma_table[*sp];
+                  sp++;
+                  sp++;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+                  v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY_ALPHA:
+         {
+            if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp += 2;
+               }
+            }
+            else /* if (row_info->bit_depth == 16) */
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 4;
+               }
+            }
+            break;
+         }
+         case PNG_COLOR_TYPE_GRAY:
+         {
+            if (row_info->bit_depth == 2)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 4)
+               {
+                  int a = *sp & 0xc0;
+                  int b = *sp & 0x30;
+                  int c = *sp & 0x0c;
+                  int d = *sp & 0x03;
+
+                  *sp = (png_byte)(
+                        ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)])   ) & 0xc0)|
+                        ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)|
+                        ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)|
+                        ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) ));
+                  sp++;
+               }
+            }
+            if (row_info->bit_depth == 4)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i += 2)
+               {
+                  int msb = *sp & 0xf0;
+                  int lsb = *sp & 0x0f;
+
+                  *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0)
+                          | (((int)gamma_table[(lsb << 4) | lsb]) >> 4));
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 8)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  *sp = gamma_table[*sp];
+                  sp++;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               sp = row;
+               for (i = 0; i < row_width; i++)
+               {
+                  png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp];
+                  *sp = (png_byte)((v >> 8) & 0xff);
+                  *(sp + 1) = (png_byte)(v & 0xff);
+                  sp += 2;
+               }
+            }
+            break;
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+/* Expands a palette row to an RGB or RGBA row depending
+ * upon whether you supply trans and num_trans.
+ */
+void /* PRIVATE */
+png_do_expand_palette(png_row_infop row_info, png_bytep row,
+   png_colorp palette, png_bytep trans, int num_trans)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand_palette\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (row_info->bit_depth < 8)
+      {
+         switch (row_info->bit_depth)
+         {
+            case 1:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 3);
+               dp = row + (png_size_t)row_width - 1;
+               shift = 7 - (int)((row_width + 7) & 0x07);
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((*sp >> shift) & 0x01)
+                     *dp = 1;
+                  else
+                     *dp = 0;
+                  if (shift == 7)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift++;
+
+                  dp--;
+               }
+               break;
+            }
+            case 2:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 2);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x03;
+                  *dp = (png_byte)value;
+                  if (shift == 6)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 2;
+
+                  dp--;
+               }
+               break;
+            }
+            case 4:
+            {
+               sp = row + (png_size_t)((row_width - 1) >> 1);
+               dp = row + (png_size_t)row_width - 1;
+               shift = (int)((row_width & 0x01) << 2);
+               for (i = 0; i < row_width; i++)
+               {
+                  value = (*sp >> shift) & 0x0f;
+                  *dp = (png_byte)value;
+                  if (shift == 4)
+                  {
+                     shift = 0;
+                     sp--;
+                  }
+                  else
+                     shift += 4;
+
+                  dp--;
+               }
+               break;
+            }
+         }
+         row_info->bit_depth = 8;
+         row_info->pixel_depth = 8;
+         row_info->rowbytes = row_width;
+      }
+      switch (row_info->bit_depth)
+      {
+         case 8:
+         {
+            if (trans != NULL)
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 2) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  if ((int)(*sp) >= num_trans)
+                     *dp-- = 0xff;
+                  else
+                     *dp-- = trans[*sp];
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 32;
+               row_info->rowbytes = row_width * 4;
+               row_info->color_type = 6;
+               row_info->channels = 4;
+            }
+            else
+            {
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width * 3) - 1;
+
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp-- = palette[*sp].blue;
+                  *dp-- = palette[*sp].green;
+                  *dp-- = palette[*sp].red;
+                  sp--;
+               }
+               row_info->bit_depth = 8;
+               row_info->pixel_depth = 24;
+               row_info->rowbytes = row_width * 3;
+               row_info->color_type = 2;
+               row_info->channels = 3;
+            }
+            break;
+         }
+      }
+   }
+}
+
+/* If the bit depth < 8, it is expanded to 8.  Also, if the already
+ * expanded transparency value is supplied, an alpha channel is built.
+ */
+void /* PRIVATE */
+png_do_expand(png_row_infop row_info, png_bytep row,
+   png_color_16p trans_value)
+{
+   int shift, value;
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_expand\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0);
+
+         if (row_info->bit_depth < 8)
+         {
+            switch (row_info->bit_depth)
+            {
+               case 1:
+               {
+                  gray = (png_uint_16)((gray&0x01)*0xff);
+                  sp = row + (png_size_t)((row_width - 1) >> 3);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = 7 - (int)((row_width + 7) & 0x07);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     if ((*sp >> shift) & 0x01)
+                        *dp = 0xff;
+                     else
+                        *dp = 0;
+                     if (shift == 7)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift++;
+
+                     dp--;
+                  }
+                  break;
+               }
+               case 2:
+               {
+                  gray = (png_uint_16)((gray&0x03)*0x55);
+                  sp = row + (png_size_t)((row_width - 1) >> 2);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((3 - ((row_width + 3) & 0x03)) << 1);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x03;
+                     *dp = (png_byte)(value | (value << 2) | (value << 4) |
+                        (value << 6));
+                     if (shift == 6)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift += 2;
+
+                     dp--;
+                  }
+                  break;
+               }
+               case 4:
+               {
+                  gray = (png_uint_16)((gray&0x0f)*0x11);
+                  sp = row + (png_size_t)((row_width - 1) >> 1);
+                  dp = row + (png_size_t)row_width - 1;
+                  shift = (int)((1 - ((row_width + 1) & 0x01)) << 2);
+                  for (i = 0; i < row_width; i++)
+                  {
+                     value = (*sp >> shift) & 0x0f;
+                     *dp = (png_byte)(value | (value << 4));
+                     if (shift == 4)
+                     {
+                        shift = 0;
+                        sp--;
+                     }
+                     else
+                        shift = 4;
+
+                     dp--;
+                  }
+                  break;
+               }
+            }
+            row_info->bit_depth = 8;
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+
+         if (trans_value != NULL)
+         {
+            if (row_info->bit_depth == 8)
+            {
+               gray = gray & 0xff;
+               sp = row + (png_size_t)row_width - 1;
+               dp = row + (png_size_t)(row_width << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if (*sp == gray)
+                     *dp-- = 0;
+                  else
+                     *dp-- = 0xff;
+                  *dp-- = *sp--;
+               }
+            }
+            else if (row_info->bit_depth == 16)
+            {
+               png_byte gray_high = (gray >> 8) & 0xff;
+               png_byte gray_low = gray & 0xff;
+               sp = row + row_info->rowbytes - 1;
+               dp = row + (row_info->rowbytes << 1) - 1;
+               for (i = 0; i < row_width; i++)
+               {
+                  if (*(sp-1) == gray_high && *(sp) == gray_low) 
+                  {
+                     *dp-- = 0;
+                     *dp-- = 0;
+                  }
+                  else
+                  {
+                     *dp-- = 0xff;
+                     *dp-- = 0xff;
+                  }
+                  *dp-- = *sp--;
+                  *dp-- = *sp--;
+               }
+            }
+            row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
+            row_info->channels = 2;
+            row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1);
+            row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+               row_width);
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            png_byte red = trans_value->red & 0xff;
+            png_byte green = trans_value->green & 0xff;
+            png_byte blue = trans_value->blue & 0xff;
+            sp = row + (png_size_t)row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 2) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue)
+                  *dp-- = 0;
+               else
+                  *dp-- = 0xff;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         else if (row_info->bit_depth == 16)
+         {
+            png_byte red_high = (trans_value->red > 8) & 0xff;
+            png_byte green_high = (trans_value->green > 8) & 0xff;
+            png_byte blue_high = (trans_value->blue > 8) & 0xff;
+            png_byte red_low = trans_value->red & 0xff;
+            png_byte green_low = trans_value->green & 0xff;
+            png_byte blue_low = trans_value->blue & 0xff;
+            sp = row + row_info->rowbytes - 1;
+            dp = row + (png_size_t)(row_width << 3) - 1;
+            for (i = 0; i < row_width; i++)
+            {
+               if (*(sp - 5) == red_high &&
+                  *(sp - 4) == red_low &&
+                  *(sp - 3) == green_high &&
+                  *(sp - 2) == green_low &&
+                  *(sp - 1) == blue_high &&
+                  *(sp    ) == blue_low)
+               {
+                  *dp-- = 0;
+                  *dp-- = 0;
+               }
+               else
+               {
+                  *dp-- = 0xff;
+                  *dp-- = 0xff;
+               }
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+               *dp-- = *sp--;
+            }
+         }
+         row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+         row_info->channels = 4;
+         row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2);
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_DITHER_SUPPORTED)
+void /* PRIVATE */
+png_do_dither(png_row_infop row_info, png_bytep row,
+    png_bytep palette_lookup, png_bytep dither_lookup)
+{
+   png_bytep sp, dp;
+   png_uint_32 i;
+   png_uint_32 row_width=row_info->width;
+
+   png_debug(1, "in png_do_dither\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB &&
+         palette_lookup && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+
+            /* this looks real messy, but the compiler will reduce
+               it down to a reasonable formula.  For example, with
+               5 bits per color, we get:
+               p = (((r >> 3) & 0x1f) << 10) |
+                  (((g >> 3) & 0x1f) << 5) |
+                  ((b >> 3) & 0x1f);
+               */
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         palette_lookup != NULL && row_info->bit_depth == 8)
+      {
+         int r, g, b, p;
+         sp = row;
+         dp = row;
+         for (i = 0; i < row_width; i++)
+         {
+            r = *sp++;
+            g = *sp++;
+            b = *sp++;
+            sp++;
+
+            p = (((r >> (8 - PNG_DITHER_RED_BITS)) &
+               ((1 << PNG_DITHER_RED_BITS) - 1)) <<
+               (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) |
+               (((g >> (8 - PNG_DITHER_GREEN_BITS)) &
+               ((1 << PNG_DITHER_GREEN_BITS) - 1)) <<
+               (PNG_DITHER_BLUE_BITS)) |
+               ((b >> (8 - PNG_DITHER_BLUE_BITS)) &
+               ((1 << PNG_DITHER_BLUE_BITS) - 1));
+
+            *dp++ = palette_lookup[p];
+         }
+         row_info->color_type = PNG_COLOR_TYPE_PALETTE;
+         row_info->channels = 1;
+         row_info->pixel_depth = row_info->bit_depth;
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width);
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE &&
+         dither_lookup && row_info->bit_depth == 8)
+      {
+         sp = row;
+         for (i = 0; i < row_width; i++, sp++)
+         {
+            *sp = dither_lookup[*sp];
+         }
+      }
+   }
+}
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+#if defined(PNG_READ_GAMMA_SUPPORTED)
+static PNG_CONST int png_gamma_shift[] =
+   {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00};
+
+/* We build the 8- or 16-bit gamma tables here.  Note that for 16-bit
+ * tables, we don't make a full table if we are reducing to 8-bit in
+ * the future.  Note also how the gamma_16 tables are segmented so that
+ * we don't need to allocate > 64K chunks for a full 16-bit table.
+ */
+void /* PRIVATE */
+png_build_gamma_table(png_structp png_ptr)
+{
+  png_debug(1, "in png_build_gamma_table\n");
+
+  if (png_ptr->bit_depth <= 8)
+  {
+     int i;
+     double g;
+
+     if (png_ptr->screen_gamma > .000001)
+        g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+     else
+        g = 1.0;
+
+     png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr,
+        (png_uint_32)256);
+
+     for (i = 0; i < 256; i++)
+     {
+        png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0,
+           g) * 255.0 + .5);
+     }
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+     if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY))
+     {
+
+        g = 1.0 / (png_ptr->gamma);
+
+        png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr,
+           (png_uint_32)256);
+
+        for (i = 0; i < 256; i++)
+        {
+           png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0,
+              g) * 255.0 + .5);
+        }
+
+
+        png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr,
+           (png_uint_32)256);
+
+        if(png_ptr->screen_gamma > 0.000001)
+           g = 1.0 / png_ptr->screen_gamma;
+        else
+           g = png_ptr->gamma;   /* probably doing rgb_to_gray */
+
+        for (i = 0; i < 256; i++)
+        {
+           png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0,
+              g) * 255.0 + .5);
+
+        }
+     }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+  }
+  else
+  {
+     double g;
+     int i, j, shift, num;
+     int sig_bit;
+     png_uint_32 ig;
+
+     if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+     {
+        sig_bit = (int)png_ptr->sig_bit.red;
+        if ((int)png_ptr->sig_bit.green > sig_bit)
+           sig_bit = png_ptr->sig_bit.green;
+        if ((int)png_ptr->sig_bit.blue > sig_bit)
+           sig_bit = png_ptr->sig_bit.blue;
+     }
+     else
+     {
+        sig_bit = (int)png_ptr->sig_bit.gray;
+     }
+
+     if (sig_bit > 0)
+        shift = 16 - sig_bit;
+     else
+        shift = 0;
+
+     if (png_ptr->transformations & PNG_16_TO_8)
+     {
+        if (shift < (16 - PNG_MAX_GAMMA_8))
+           shift = (16 - PNG_MAX_GAMMA_8);
+     }
+
+     if (shift > 8)
+        shift = 8;
+     if (shift < 0)
+        shift = 0;
+
+     png_ptr->gamma_shift = (png_byte)shift;
+
+     num = (1 << (8 - shift));
+
+     if (png_ptr->screen_gamma > .000001)
+        g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma);
+     else
+        g = 1.0;
+
+     png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr,
+        (png_uint_32)(num * png_sizeof (png_uint_16p)));
+
+     if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND))
+     {
+        double fin, fout;
+        png_uint_32 last, max;
+
+        for (i = 0; i < num; i++)
+        {
+           png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr,
+              (png_uint_32)(256 * png_sizeof (png_uint_16)));
+        }
+
+        g = 1.0 / g;
+        last = 0;
+        for (i = 0; i < 256; i++)
+        {
+           fout = ((double)i + 0.5) / 256.0;
+           fin = pow(fout, g);
+           max = (png_uint_32)(fin * (double)((png_uint_32)num << 8));
+           while (last <= max)
+           {
+              png_ptr->gamma_16_table[(int)(last & (0xff >> shift))]
+                 [(int)(last >> (8 - shift))] = (png_uint_16)(
+                 (png_uint_16)i | ((png_uint_16)i << 8));
+              last++;
+           }
+        }
+        while (last < ((png_uint_32)num << 8))
+        {
+           png_ptr->gamma_16_table[(int)(last & (0xff >> shift))]
+              [(int)(last >> (8 - shift))] = (png_uint_16)65535L;
+           last++;
+        }
+     }
+     else
+     {
+        for (i = 0; i < num; i++)
+        {
+           png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr,
+              (png_uint_32)(256 * png_sizeof (png_uint_16)));
+
+           ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4);
+           for (j = 0; j < 256; j++)
+           {
+              png_ptr->gamma_16_table[i][j] =
+                 (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                    65535.0, g) * 65535.0 + .5);
+           }
+        }
+     }
+
+#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
+   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
+     if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY))
+     {
+
+        g = 1.0 / (png_ptr->gamma);
+
+        png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr,
+           (png_uint_32)(num * png_sizeof (png_uint_16p )));
+
+        for (i = 0; i < num; i++)
+        {
+           png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr,
+              (png_uint_32)(256 * png_sizeof (png_uint_16)));
+
+           ig = (((png_uint_32)i *
+              (png_uint_32)png_gamma_shift[shift]) >> 4);
+           for (j = 0; j < 256; j++)
+           {
+              png_ptr->gamma_16_to_1[i][j] =
+                 (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                    65535.0, g) * 65535.0 + .5);
+           }
+        }
+
+        if(png_ptr->screen_gamma > 0.000001)
+           g = 1.0 / png_ptr->screen_gamma;
+        else
+           g = png_ptr->gamma;   /* probably doing rgb_to_gray */
+
+        png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr,
+           (png_uint_32)(num * png_sizeof (png_uint_16p)));
+
+        for (i = 0; i < num; i++)
+        {
+           png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr,
+              (png_uint_32)(256 * png_sizeof (png_uint_16)));
+
+           ig = (((png_uint_32)i *
+              (png_uint_32)png_gamma_shift[shift]) >> 4);
+           for (j = 0; j < 256; j++)
+           {
+              png_ptr->gamma_16_from_1[i][j] =
+                 (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) /
+                    65535.0, g) * 65535.0 + .5);
+           }
+        }
+     }
+#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */
+  }
+}
+#endif
+/* To do: install integer version of png_build_gamma_table here */
+#endif
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+/* undoes intrapixel differencing  */
+void /* PRIVATE */
+png_do_read_intrapixel(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_read_intrapixel\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      int bytes_per_pixel;
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 3;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 4;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff);
+            *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff);
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 6;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 8;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            png_uint_32 s0   = (*(rp  ) << 8) | *(rp+1);
+            png_uint_32 s1   = (*(rp+2) << 8) | *(rp+3);
+            png_uint_32 s2   = (*(rp+4) << 8) | *(rp+5);
+            png_uint_32 red  = (png_uint_32)((s0+s1+65536L) & 0xffffL);
+            png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL);
+            *(rp  ) = (png_byte)((red >> 8) & 0xff);
+            *(rp+1) = (png_byte)(red & 0xff);
+            *(rp+4) = (png_byte)((blue >> 8) & 0xff);
+            *(rp+5) = (png_byte)(blue & 0xff);
+         }
+      }
+   }
+}
+#endif /* PNG_MNG_FEATURES_SUPPORTED */
+#endif /* PNG_READ_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngrutil.c b/distrib/libpng-1.2.19/pngrutil.c
new file mode 100644
index 0000000..86e924c
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngrutil.c
@@ -0,0 +1,4189 @@
+
+/* pngrutil.c - utilities to read a PNG file
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file contains routines that are only called from within
+ * libpng itself during the course of reading an image.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED)
+
+#if defined(_WIN32_WCE) && (_WIN32_WCE<0x500)
+#  define WIN32_WCE_OLD
+#endif
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+#  if defined(WIN32_WCE_OLD)
+/* strtod() function is not supported on WindowsCE */
+__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, char **endptr)
+{
+   double result = 0;
+   int len;
+   wchar_t *str, *end;
+
+   len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0);
+   str = (wchar_t *)png_malloc(png_ptr, len * sizeof(wchar_t));
+   if ( NULL != str )
+   {
+      MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len);
+      result = wcstod(str, &end);
+      len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL);
+      *endptr = (char *)nptr + (png_strlen(nptr) - len + 1);
+      png_free(png_ptr, str);
+   }
+   return result;
+}
+#  else
+#    define png_strtod(p,a,b) strtod(a,b)
+#  endif
+#endif
+
+png_uint_32 PNGAPI
+png_get_uint_31(png_structp png_ptr, png_bytep buf)
+{
+   png_uint_32 i = png_get_uint_32(buf);
+   if (i > PNG_UINT_31_MAX)
+     png_error(png_ptr, "PNG unsigned integer out of range.");
+   return (i);
+}
+#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED
+/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */
+png_uint_32 PNGAPI
+png_get_uint_32(png_bytep buf)
+{
+   png_uint_32 i = ((png_uint_32)(*buf) << 24) +
+      ((png_uint_32)(*(buf + 1)) << 16) +
+      ((png_uint_32)(*(buf + 2)) << 8) +
+      (png_uint_32)(*(buf + 3));
+
+   return (i);
+}
+
+/* Grab a signed 32-bit integer from a buffer in big-endian format.  The
+ * data is stored in the PNG file in two's complement format, and it is
+ * assumed that the machine format for signed integers is the same. */
+png_int_32 PNGAPI
+png_get_int_32(png_bytep buf)
+{
+   png_int_32 i = ((png_int_32)(*buf) << 24) +
+      ((png_int_32)(*(buf + 1)) << 16) +
+      ((png_int_32)(*(buf + 2)) << 8) +
+      (png_int_32)(*(buf + 3));
+
+   return (i);
+}
+
+/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */
+png_uint_16 PNGAPI
+png_get_uint_16(png_bytep buf)
+{
+   png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) +
+      (png_uint_16)(*(buf + 1)));
+
+   return (i);
+}
+#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */
+
+/* Read data, and (optionally) run it through the CRC. */
+void /* PRIVATE */
+png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length)
+{
+   if(png_ptr == NULL) return;
+   png_read_data(png_ptr, buf, length);
+   png_calculate_crc(png_ptr, buf, length);
+}
+
+/* Optionally skip data and then check the CRC.  Depending on whether we
+   are reading a ancillary or critical chunk, and how the program has set
+   things up, we may calculate the CRC on the data and print a message.
+   Returns '1' if there was a CRC error, '0' otherwise. */
+int /* PRIVATE */
+png_crc_finish(png_structp png_ptr, png_uint_32 skip)
+{
+   png_size_t i;
+   png_size_t istop = png_ptr->zbuf_size;
+
+   for (i = (png_size_t)skip; i > istop; i -= istop)
+   {
+      png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+   }
+   if (i)
+   {
+      png_crc_read(png_ptr, png_ptr->zbuf, i);
+   }
+
+   if (png_crc_error(png_ptr))
+   {
+      if (((png_ptr->chunk_name[0] & 0x20) &&                /* Ancillary */
+           !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) ||
+          (!(png_ptr->chunk_name[0] & 0x20) &&             /* Critical  */
+          (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE)))
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+      else
+      {
+         png_chunk_error(png_ptr, "CRC error");
+      }
+      return (1);
+   }
+
+   return (0);
+}
+
+/* Compare the CRC stored in the PNG file with that calculated by libpng from
+   the data it has read thus far. */
+int /* PRIVATE */
+png_crc_error(png_structp png_ptr)
+{
+   png_byte crc_bytes[4];
+   png_uint_32 crc;
+   int need_crc = 1;
+
+   if (png_ptr->chunk_name[0] & 0x20)                     /* ancillary */
+   {
+      if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) ==
+          (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN))
+         need_crc = 0;
+   }
+   else                                                    /* critical */
+   {
+      if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE)
+         need_crc = 0;
+   }
+
+   png_read_data(png_ptr, crc_bytes, 4);
+
+   if (need_crc)
+   {
+      crc = png_get_uint_32(crc_bytes);
+      return ((int)(crc != png_ptr->crc));
+   }
+   else
+      return (0);
+}
+
+#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \
+    defined(PNG_READ_iCCP_SUPPORTED)
+/*
+ * Decompress trailing data in a chunk.  The assumption is that chunkdata
+ * points at an allocated area holding the contents of a chunk with a
+ * trailing compressed part.  What we get back is an allocated area
+ * holding the original prefix part and an uncompressed version of the
+ * trailing part (the malloc area passed in is freed).
+ */
+png_charp /* PRIVATE */
+png_decompress_chunk(png_structp png_ptr, int comp_type,
+                              png_charp chunkdata, png_size_t chunklength,
+                              png_size_t prefix_size, png_size_t *newlength)
+{
+   static PNG_CONST char msg[] = "Error decoding compressed text";
+   png_charp text;
+   png_size_t text_size;
+
+   if (comp_type == PNG_COMPRESSION_TYPE_BASE)
+   {
+      int ret = Z_OK;
+      png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size);
+      png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size);
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+
+      text_size = 0;
+      text = NULL;
+
+      while (png_ptr->zstream.avail_in)
+      {
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            if (png_ptr->zstream.msg != NULL)
+               png_warning(png_ptr, png_ptr->zstream.msg);
+            else
+               png_warning(png_ptr, msg);
+            inflateReset(&png_ptr->zstream);
+            png_ptr->zstream.avail_in = 0;
+
+            if (text ==  NULL)
+            {
+               text_size = prefix_size + png_sizeof(msg) + 1;
+               text = (png_charp)png_malloc_warn(png_ptr, text_size);
+               if (text ==  NULL)
+                 {
+                    png_free(png_ptr,chunkdata);
+                    png_error(png_ptr,"Not enough memory to decompress chunk");
+                 }
+               png_memcpy(text, chunkdata, prefix_size);
+            }
+
+            text[text_size - 1] = 0x00;
+
+            /* Copy what we can of the error message into the text chunk */
+            text_size = (png_size_t)(chunklength - (text - chunkdata) - 1);
+            text_size = png_sizeof(msg) > text_size ? text_size :
+               png_sizeof(msg);
+            png_memcpy(text + prefix_size, msg, text_size + 1);
+            break;
+         }
+         if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END)
+         {
+            if (text == NULL)
+            {
+               text_size = prefix_size +
+                   png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               text = (png_charp)png_malloc_warn(png_ptr, text_size + 1);
+               if (text ==  NULL)
+                 {
+                    png_free(png_ptr,chunkdata);
+                    png_error(png_ptr,"Not enough memory to decompress chunk.");
+                 }
+               png_memcpy(text + prefix_size, png_ptr->zbuf,
+                    text_size - prefix_size);
+               png_memcpy(text, chunkdata, prefix_size);
+               *(text + text_size) = 0x00;
+            }
+            else
+            {
+               png_charp tmp;
+
+               tmp = text;
+               text = (png_charp)png_malloc_warn(png_ptr,
+                  (png_uint_32)(text_size +
+                  png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1));
+               if (text == NULL)
+               {
+                  png_free(png_ptr, tmp);
+                  png_free(png_ptr, chunkdata);
+                  png_error(png_ptr,"Not enough memory to decompress chunk..");
+               }
+               png_memcpy(text, tmp, text_size);
+               png_free(png_ptr, tmp);
+               png_memcpy(text + text_size, png_ptr->zbuf,
+                  (png_ptr->zbuf_size - png_ptr->zstream.avail_out));
+               text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
+               *(text + text_size) = 0x00;
+            }
+            if (ret == Z_STREAM_END)
+               break;
+            else
+            {
+               png_ptr->zstream.next_out = png_ptr->zbuf;
+               png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            }
+         }
+      }
+      if (ret != Z_STREAM_END)
+      {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+         char umsg[52];
+
+         if (ret == Z_BUF_ERROR)
+            png_snprintf(umsg, 52,
+                "Buffer error in compressed datastream in %s chunk",
+                png_ptr->chunk_name);
+         else if (ret == Z_DATA_ERROR)
+            png_snprintf(umsg, 52,
+                "Data error in compressed datastream in %s chunk",
+                png_ptr->chunk_name);
+         else
+            png_snprintf(umsg, 52,
+                "Incomplete compressed datastream in %s chunk",
+                png_ptr->chunk_name);
+         png_warning(png_ptr, umsg);
+#else
+         png_warning(png_ptr,
+            "Incomplete compressed datastream in chunk other than IDAT");
+#endif
+         text_size=prefix_size;
+         if (text ==  NULL)
+         {
+            text = (png_charp)png_malloc_warn(png_ptr, text_size+1);
+            if (text == NULL)
+              {
+                png_free(png_ptr, chunkdata);
+                png_error(png_ptr,"Not enough memory for text.");
+              }
+            png_memcpy(text, chunkdata, prefix_size);
+         }
+         *(text + text_size) = 0x00;
+      }
+
+      inflateReset(&png_ptr->zstream);
+      png_ptr->zstream.avail_in = 0;
+
+      png_free(png_ptr, chunkdata);
+      chunkdata = text;
+      *newlength=text_size;
+   }
+   else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */
+   {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+      char umsg[50];
+
+      png_snprintf(umsg, 50,
+         "Unknown zTXt compression type %d", comp_type);
+      png_warning(png_ptr, umsg);
+#else
+      png_warning(png_ptr, "Unknown zTXt compression type");
+#endif
+
+      *(chunkdata + prefix_size) = 0x00;
+      *newlength=prefix_size;
+   }
+
+   return chunkdata;
+}
+#endif
+
+/* read and check the IDHR chunk */
+void /* PRIVATE */
+png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[13];
+   png_uint_32 width, height;
+   int bit_depth, color_type, compression_type, filter_type;
+   int interlace_type;
+
+   png_debug(1, "in png_handle_IHDR\n");
+
+   if (png_ptr->mode & PNG_HAVE_IHDR)
+      png_error(png_ptr, "Out of place IHDR");
+
+   /* check the length */
+   if (length != 13)
+      png_error(png_ptr, "Invalid IHDR chunk");
+
+   png_ptr->mode |= PNG_HAVE_IHDR;
+
+   png_crc_read(png_ptr, buf, 13);
+   png_crc_finish(png_ptr, 0);
+
+   width = png_get_uint_31(png_ptr, buf);
+   height = png_get_uint_31(png_ptr, buf + 4);
+   bit_depth = buf[8];
+   color_type = buf[9];
+   compression_type = buf[10];
+   filter_type = buf[11];
+   interlace_type = buf[12];
+
+   /* set internal variables */
+   png_ptr->width = width;
+   png_ptr->height = height;
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->interlaced = (png_byte)interlace_type;
+   png_ptr->color_type = (png_byte)color_type;
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   png_ptr->filter_type = (png_byte)filter_type;
+#endif
+   png_ptr->compression_type = (png_byte)compression_type;
+
+   /* find number of channels */
+   switch (png_ptr->color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+      case PNG_COLOR_TYPE_PALETTE:
+         png_ptr->channels = 1;
+         break;
+      case PNG_COLOR_TYPE_RGB:
+         png_ptr->channels = 3;
+         break;
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         png_ptr->channels = 2;
+         break;
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         png_ptr->channels = 4;
+         break;
+   }
+
+   /* set up other useful info */
+   png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth *
+   png_ptr->channels);
+   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width);
+   png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth);
+   png_debug1(3,"channels = %d\n", png_ptr->channels);
+   png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes);
+   png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth,
+      color_type, interlace_type, compression_type, filter_type);
+}
+
+/* read and check the palette */
+void /* PRIVATE */
+png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_color palette[PNG_MAX_PALETTE_LENGTH];
+   int num, i;
+#ifndef PNG_NO_POINTER_INDEXING
+   png_colorp pal_ptr;
+#endif
+
+   png_debug(1, "in png_handle_PLTE\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before PLTE");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid PLTE after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      png_error(png_ptr, "Duplicate PLTE chunk");
+
+   png_ptr->mode |= PNG_HAVE_PLTE;
+
+   if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+   {
+      png_warning(png_ptr,
+        "Ignoring PLTE chunk in grayscale PNG");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+#endif
+
+   if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3)
+   {
+      if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)
+      {
+         png_warning(png_ptr, "Invalid palette chunk");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      else
+      {
+         png_error(png_ptr, "Invalid palette chunk");
+      }
+   }
+
+   num = (int)length / 3;
+
+#ifndef PNG_NO_POINTER_INDEXING
+   for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      pal_ptr->red = buf[0];
+      pal_ptr->green = buf[1];
+      pal_ptr->blue = buf[2];
+   }
+#else
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[3];
+
+      png_crc_read(png_ptr, buf, 3);
+      /* don't depend upon png_color being any order */
+      palette[i].red = buf[0];
+      palette[i].green = buf[1];
+      palette[i].blue = buf[2];
+   }
+#endif
+
+   /* If we actually NEED the PLTE chunk (ie for a paletted image), we do
+      whatever the normal CRC configuration tells us.  However, if we
+      have an RGB image, the PLTE can be considered ancillary, so
+      we will act as though it is. */
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+#endif
+   {
+      png_crc_finish(png_ptr, 0);
+   }
+#if !defined(PNG_READ_OPT_PLTE_SUPPORTED)
+   else if (png_crc_error(png_ptr))  /* Only if we have a CRC error */
+   {
+      /* If we don't want to use the data from an ancillary chunk,
+         we have two options: an error abort, or a warning and we
+         ignore the data in this chunk (which should be OK, since
+         it's considered ancillary for a RGB or RGBA image). */
+      if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE))
+      {
+         if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)
+         {
+            png_chunk_error(png_ptr, "CRC error");
+         }
+         else
+         {
+            png_chunk_warning(png_ptr, "CRC error");
+            return;
+         }
+      }
+      /* Otherwise, we (optionally) emit a warning and use the chunk. */
+      else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN))
+      {
+         png_chunk_warning(png_ptr, "CRC error");
+      }
+   }
+#endif
+
+   png_set_PLTE(png_ptr, info_ptr, palette, num);
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+      {
+         if (png_ptr->num_trans > (png_uint_16)num)
+         {
+            png_warning(png_ptr, "Truncating incorrect tRNS chunk length");
+            png_ptr->num_trans = (png_uint_16)num;
+         }
+         if (info_ptr->num_trans > (png_uint_16)num)
+         {
+            png_warning(png_ptr, "Truncating incorrect info tRNS chunk length");
+            info_ptr->num_trans = (png_uint_16)num;
+         }
+      }
+   }
+#endif
+
+}
+
+void /* PRIVATE */
+png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_debug(1, "in png_handle_IEND\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT))
+   {
+      png_error(png_ptr, "No image in file");
+   }
+
+   png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND);
+
+   if (length != 0)
+   {
+      png_warning(png_ptr, "Incorrect IEND chunk length");
+   }
+   png_crc_finish(png_ptr, length);
+
+   info_ptr =info_ptr; /* quiet compiler warnings about unused info_ptr */
+}
+
+#if defined(PNG_READ_gAMA_SUPPORTED)
+void /* PRIVATE */
+png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_fixed_point igamma;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float file_gamma;
+#endif
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_gAMA\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before gAMA");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid gAMA after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place gAMA chunk");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+      )
+   {
+      png_warning(png_ptr, "Duplicate gAMA chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 4)
+   {
+      png_warning(png_ptr, "Incorrect gAMA chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   igamma = (png_fixed_point)png_get_uint_32(buf);
+   /* check for zero gamma */
+   if (igamma == 0)
+      {
+         png_warning(png_ptr,
+           "Ignoring gAMA chunk with gamma=0");
+         return;
+      }
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
+      if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
+      {
+         png_warning(png_ptr,
+           "Ignoring incorrect gAMA value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+         fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma);
+#endif
+         return;
+      }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   file_gamma = (float)igamma / (float)100000.0;
+#  ifdef PNG_READ_GAMMA_SUPPORTED
+     png_ptr->gamma = file_gamma;
+#  endif
+     png_set_gAMA(png_ptr, info_ptr, file_gamma);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_set_gAMA_fixed(png_ptr, info_ptr, igamma);
+#endif
+}
+#endif
+
+#if defined(PNG_READ_sBIT_SUPPORTED)
+void /* PRIVATE */
+png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_size_t truelen;
+   png_byte buf[4];
+
+   png_debug(1, "in png_handle_sBIT\n");
+
+   buf[0] = buf[1] = buf[2] = buf[3] = 0;
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sBIT");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sBIT after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+   {
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place sBIT chunk");
+   }
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT))
+   {
+      png_warning(png_ptr, "Duplicate sBIT chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 3;
+   else
+      truelen = (png_size_t)png_ptr->channels;
+
+   if (length != truelen || length > 4)
+   {
+      png_warning(png_ptr, "Incorrect sBIT chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[1];
+      png_ptr->sig_bit.blue = buf[2];
+      png_ptr->sig_bit.alpha = buf[3];
+   }
+   else
+   {
+      png_ptr->sig_bit.gray = buf[0];
+      png_ptr->sig_bit.red = buf[0];
+      png_ptr->sig_bit.green = buf[0];
+      png_ptr->sig_bit.blue = buf[0];
+      png_ptr->sig_bit.alpha = buf[1];
+   }
+   png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit));
+}
+#endif
+
+#if defined(PNG_READ_cHRM_SUPPORTED)
+void /* PRIVATE */
+png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[4];
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+#endif
+   png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green,
+      int_y_green, int_x_blue, int_y_blue;
+
+   png_uint_32 uint_x, uint_y;
+
+   png_debug(1, "in png_handle_cHRM\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before cHRM");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid cHRM after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Missing PLTE before cHRM");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)
+#if defined(PNG_READ_sRGB_SUPPORTED)
+      && !(info_ptr->valid & PNG_INFO_sRGB)
+#endif
+      )
+   {
+      png_warning(png_ptr, "Duplicate cHRM chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 32)
+   {
+      png_warning(png_ptr, "Incorrect cHRM chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x > 80000L || uint_y > 80000L ||
+      uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM white point");
+      png_crc_finish(png_ptr, 24);
+      return;
+   }
+   int_x_white = (png_fixed_point)uint_x;
+   int_y_white = (png_fixed_point)uint_y;
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM red point");
+      png_crc_finish(png_ptr, 16);
+      return;
+   }
+   int_x_red = (png_fixed_point)uint_x;
+   int_y_red = (png_fixed_point)uint_y;
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM green point");
+      png_crc_finish(png_ptr, 8);
+      return;
+   }
+   int_x_green = (png_fixed_point)uint_x;
+   int_y_green = (png_fixed_point)uint_y;
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_x = png_get_uint_32(buf);
+
+   png_crc_read(png_ptr, buf, 4);
+   uint_y = png_get_uint_32(buf);
+
+   if (uint_x + uint_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM blue point");
+      png_crc_finish(png_ptr, 0);
+      return;
+   }
+   int_x_blue = (png_fixed_point)uint_x;
+   int_y_blue = (png_fixed_point)uint_y;
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   white_x = (float)int_x_white / (float)100000.0;
+   white_y = (float)int_y_white / (float)100000.0;
+   red_x   = (float)int_x_red   / (float)100000.0;
+   red_y   = (float)int_y_red   / (float)100000.0;
+   green_x = (float)int_x_green / (float)100000.0;
+   green_y = (float)int_y_green / (float)100000.0;
+   blue_x  = (float)int_x_blue  / (float)100000.0;
+   blue_y  = (float)int_y_blue  / (float)100000.0;
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+   if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB))
+      {
+      if (PNG_OUT_OF_RANGE(int_x_white, 31270,  1000) ||
+          PNG_OUT_OF_RANGE(int_y_white, 32900,  1000) ||
+          PNG_OUT_OF_RANGE(int_x_red,   64000L, 1000) ||
+          PNG_OUT_OF_RANGE(int_y_red,   33000,  1000) ||
+          PNG_OUT_OF_RANGE(int_x_green, 30000,  1000) ||
+          PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) ||
+          PNG_OUT_OF_RANGE(int_x_blue,  15000,  1000) ||
+          PNG_OUT_OF_RANGE(int_y_blue,   6000,  1000))
+         {
+            png_warning(png_ptr,
+              "Ignoring incorrect cHRM value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+            fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n",
+               white_x, white_y, red_x, red_y);
+            fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n",
+               green_x, green_y, blue_x, blue_y);
+#else
+            fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n",
+               int_x_white, int_y_white, int_x_red, int_y_red);
+            fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n",
+               int_x_green, int_y_green, int_x_blue, int_y_blue);
+#endif
+#endif /* PNG_NO_CONSOLE_IO */
+         }
+         png_crc_finish(png_ptr, 0);
+         return;
+      }
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   png_set_cHRM(png_ptr, info_ptr,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_set_cHRM_fixed(png_ptr, info_ptr,
+      int_x_white, int_y_white, int_x_red, int_y_red, int_x_green,
+      int_y_green, int_x_blue, int_y_blue);
+#endif
+   if (png_crc_finish(png_ptr, 0))
+      return;
+}
+#endif
+
+#if defined(PNG_READ_sRGB_SUPPORTED)
+void /* PRIVATE */
+png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   int intent;
+   png_byte buf[1];
+
+   png_debug(1, "in png_handle_sRGB\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sRGB");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sRGB after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place sRGB chunk");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB))
+   {
+      png_warning(png_ptr, "Duplicate sRGB chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 1)
+   {
+      png_warning(png_ptr, "Incorrect sRGB chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 1);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   intent = buf[0];
+   /* check for bad intent */
+   if (intent >= PNG_sRGB_INTENT_LAST)
+   {
+      png_warning(png_ptr, "Unknown sRGB intent");
+      return;
+   }
+
+#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED)
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA))
+   {
+   png_fixed_point igamma;
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      igamma=info_ptr->int_gamma;
+#else
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      igamma=(png_fixed_point)(info_ptr->gamma * 100000.);
+#  endif
+#endif
+      if (PNG_OUT_OF_RANGE(igamma, 45500L, 500))
+      {
+         png_warning(png_ptr,
+           "Ignoring incorrect gAMA value when sRGB is also present");
+#ifndef PNG_NO_CONSOLE_IO
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+         fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma);
+#  else
+#    ifdef PNG_FLOATING_POINT_SUPPORTED
+         fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma);
+#    endif
+#  endif
+#endif
+      }
+   }
+#endif /* PNG_READ_gAMA_SUPPORTED */
+
+#ifdef PNG_READ_cHRM_SUPPORTED
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM))
+      if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_x_red,   64000L, 1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_red,   33000,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_x_blue,  15000,  1000) ||
+          PNG_OUT_OF_RANGE(info_ptr->int_y_blue,   6000,  1000))
+         {
+            png_warning(png_ptr,
+              "Ignoring incorrect cHRM value when sRGB is also present");
+         }
+#endif /* PNG_FIXED_POINT_SUPPORTED */
+#endif /* PNG_READ_cHRM_SUPPORTED */
+
+   png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent);
+}
+#endif /* PNG_READ_sRGB_SUPPORTED */
+
+#if defined(PNG_READ_iCCP_SUPPORTED)
+void /* PRIVATE */
+png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+{
+   png_charp chunkdata;
+   png_byte compression_type;
+   png_bytep pC;
+   png_charp profile;
+   png_uint_32 skip = 0;
+   png_uint_32 profile_size, profile_length;
+   png_size_t slength, prefix_length, data_length;
+
+   png_debug(1, "in png_handle_iCCP\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before iCCP");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid iCCP after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->mode & PNG_HAVE_PLTE)
+      /* Should be an error, but we can cope with it */
+      png_warning(png_ptr, "Out of place iCCP chunk");
+
+   if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP))
+   {
+      png_warning(png_ptr, "Duplicate iCCP chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "iCCP chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   chunkdata = (png_charp)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (profile = chunkdata; *profile; profile++)
+      /* empty loop to find end of name */ ;
+
+   ++profile;
+
+   /* there should be at least one zero (the compression type byte)
+      following the separator, and we should be on it  */
+   if ( profile >= chunkdata + slength - 1)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "Malformed iCCP chunk");
+      return;
+   }
+
+   /* compression_type should always be zero */
+   compression_type = *profile++;
+   if (compression_type)
+   {
+      png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk");
+      compression_type=0x00;  /* Reset it to zero (libpng-1.0.6 through 1.0.8
+                                 wrote nonzero) */
+   }
+
+   prefix_length = profile - chunkdata;
+   chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata,
+                                    slength, prefix_length, &data_length);
+
+   profile_length = data_length - prefix_length;
+
+   if ( prefix_length > data_length || profile_length < 4)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "Profile size field missing from iCCP chunk");
+      return;
+   }
+
+   /* Check the profile_size recorded in the first 32 bits of the ICC profile */
+   pC = (png_bytep)(chunkdata+prefix_length);
+   profile_size = ((*(pC  ))<<24) |
+                  ((*(pC+1))<<16) |
+                  ((*(pC+2))<< 8) |
+                  ((*(pC+3))    );
+
+   if(profile_size < profile_length)
+      profile_length = profile_size;
+
+   if(profile_size > profile_length)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "Ignoring truncated iCCP profile.");
+      return;
+   }
+
+   png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type,
+                chunkdata + prefix_length, profile_length);
+   png_free(png_ptr, chunkdata);
+}
+#endif /* PNG_READ_iCCP_SUPPORTED */
+
+#if defined(PNG_READ_sPLT_SUPPORTED)
+void /* PRIVATE */
+png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+{
+   png_bytep chunkdata;
+   png_bytep entry_start;
+   png_sPLT_t new_palette;
+#ifdef PNG_NO_POINTER_INDEXING
+   png_sPLT_entryp pp;
+#endif
+   int data_length, entry_size, i;
+   png_uint_32 skip = 0;
+   png_size_t slength;
+
+   png_debug(1, "in png_handle_sPLT\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sPLT");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sPLT after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "sPLT chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   chunkdata = (png_bytep)png_malloc(png_ptr, length + 1);
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (entry_start = chunkdata; *entry_start; entry_start++)
+      /* empty loop to find end of name */ ;
+   ++entry_start;
+
+   /* a sample depth should follow the separator, and we should be on it  */
+   if (entry_start > chunkdata + slength - 2)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "malformed sPLT chunk");
+      return;
+   }
+
+   new_palette.depth = *entry_start++;
+   entry_size = (new_palette.depth == 8 ? 6 : 10);
+   data_length = (slength - (entry_start - chunkdata));
+
+   /* integrity-check the data length */
+   if (data_length % entry_size)
+   {
+      png_free(png_ptr, chunkdata);
+      png_warning(png_ptr, "sPLT chunk has bad length");
+      return;
+   }
+
+   new_palette.nentries = (png_int_32) ( data_length / entry_size);
+   if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX /
+       png_sizeof(png_sPLT_entry)))
+   {
+       png_warning(png_ptr, "sPLT chunk too long");
+       return;
+   }
+   new_palette.entries = (png_sPLT_entryp)png_malloc_warn(
+       png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry));
+   if (new_palette.entries == NULL)
+   {
+       png_warning(png_ptr, "sPLT chunk requires too much memory");
+       return;
+   }
+
+#ifndef PNG_NO_POINTER_INDEXING
+   for (i = 0; i < new_palette.nentries; i++)
+   {
+      png_sPLT_entryp pp = new_palette.entries + i;
+
+      if (new_palette.depth == 8)
+      {
+          pp->red = *entry_start++;
+          pp->green = *entry_start++;
+          pp->blue = *entry_start++;
+          pp->alpha = *entry_start++;
+      }
+      else
+      {
+          pp->red   = png_get_uint_16(entry_start); entry_start += 2;
+          pp->green = png_get_uint_16(entry_start); entry_start += 2;
+          pp->blue  = png_get_uint_16(entry_start); entry_start += 2;
+          pp->alpha = png_get_uint_16(entry_start); entry_start += 2;
+      }
+      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
+   }
+#else
+   pp = new_palette.entries;
+   for (i = 0; i < new_palette.nentries; i++)
+   {
+
+      if (new_palette.depth == 8)
+      {
+          pp[i].red   = *entry_start++;
+          pp[i].green = *entry_start++;
+          pp[i].blue  = *entry_start++;
+          pp[i].alpha = *entry_start++;
+      }
+      else
+      {
+          pp[i].red   = png_get_uint_16(entry_start); entry_start += 2;
+          pp[i].green = png_get_uint_16(entry_start); entry_start += 2;
+          pp[i].blue  = png_get_uint_16(entry_start); entry_start += 2;
+          pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2;
+      }
+      pp->frequency = png_get_uint_16(entry_start); entry_start += 2;
+   }
+#endif
+
+   /* discard all chunk data except the name and stash that */
+   new_palette.name = (png_charp)chunkdata;
+
+   png_set_sPLT(png_ptr, info_ptr, &new_palette, 1);
+
+   png_free(png_ptr, chunkdata);
+   png_free(png_ptr, new_palette.entries);
+}
+#endif /* PNG_READ_sPLT_SUPPORTED */
+
+#if defined(PNG_READ_tRNS_SUPPORTED)
+void /* PRIVATE */
+png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte readbuf[PNG_MAX_PALETTE_LENGTH];
+   int bit_mask;
+
+   png_debug(1, "in png_handle_tRNS\n");
+
+   /* For non-indexed color, mask off any bits in the tRNS value that
+    * exceed the bit depth.  Some creators were writing extra bits there.
+    * This is not needed for indexed color. */
+   bit_mask = (1 << png_ptr->bit_depth) - 1;
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before tRNS");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid tRNS after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS))
+   {
+      png_warning(png_ptr, "Duplicate tRNS chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_byte buf[2];
+
+      if (length != 2)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+
+      png_crc_read(png_ptr, buf, 2);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.gray = png_get_uint_16(buf) & bit_mask;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_byte buf[6];
+
+      if (length != 6)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      png_crc_read(png_ptr, buf, (png_size_t)length);
+      png_ptr->num_trans = 1;
+      png_ptr->trans_values.red = png_get_uint_16(buf) & bit_mask;
+      png_ptr->trans_values.green = png_get_uint_16(buf + 2) & bit_mask;
+      png_ptr->trans_values.blue = png_get_uint_16(buf + 4) & bit_mask;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (!(png_ptr->mode & PNG_HAVE_PLTE))
+      {
+         /* Should be an error, but we can cope with it. */
+         png_warning(png_ptr, "Missing PLTE before tRNS");
+      }
+      if (length > (png_uint_32)png_ptr->num_palette ||
+          length > PNG_MAX_PALETTE_LENGTH)
+      {
+         png_warning(png_ptr, "Incorrect tRNS chunk length");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      if (length == 0)
+      {
+         png_warning(png_ptr, "Zero length tRNS chunk");
+         png_crc_finish(png_ptr, length);
+         return;
+      }
+      png_crc_read(png_ptr, readbuf, (png_size_t)length);
+      png_ptr->num_trans = (png_uint_16)length;
+   }
+   else
+   {
+      png_warning(png_ptr, "tRNS chunk not allowed with alpha channel");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_ptr->num_trans = 0;
+      return;
+   }
+
+   png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans,
+      &(png_ptr->trans_values));
+}
+#endif
+
+#if defined(PNG_READ_bKGD_SUPPORTED)
+void /* PRIVATE */
+png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_size_t truelen;
+   png_byte buf[6];
+
+   png_debug(1, "in png_handle_bKGD\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before bKGD");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid bKGD after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+            !(png_ptr->mode & PNG_HAVE_PLTE))
+   {
+      png_warning(png_ptr, "Missing PLTE before bKGD");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD))
+   {
+      png_warning(png_ptr, "Duplicate bKGD chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      truelen = 1;
+   else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      truelen = 6;
+   else
+      truelen = 2;
+
+   if (length != truelen)
+   {
+      png_warning(png_ptr, "Incorrect bKGD chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, truelen);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   /* We convert the index value into RGB components so that we can allow
+    * arbitrary RGB values for background when we have transparency, and
+    * so it is easy to determine the RGB values of the background color
+    * from the info_ptr struct. */
+   if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      png_ptr->background.index = buf[0];
+      if(info_ptr->num_palette)
+      {
+          if(buf[0] > info_ptr->num_palette)
+          {
+             png_warning(png_ptr, "Incorrect bKGD chunk index value");
+             return;
+          }
+          png_ptr->background.red =
+             (png_uint_16)png_ptr->palette[buf[0]].red;
+          png_ptr->background.green =
+             (png_uint_16)png_ptr->palette[buf[0]].green;
+          png_ptr->background.blue =
+             (png_uint_16)png_ptr->palette[buf[0]].blue;
+      }
+   }
+   else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */
+   {
+      png_ptr->background.red =
+      png_ptr->background.green =
+      png_ptr->background.blue =
+      png_ptr->background.gray = png_get_uint_16(buf);
+   }
+   else
+   {
+      png_ptr->background.red = png_get_uint_16(buf);
+      png_ptr->background.green = png_get_uint_16(buf + 2);
+      png_ptr->background.blue = png_get_uint_16(buf + 4);
+   }
+
+   png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background));
+}
+#endif
+
+#if defined(PNG_READ_hIST_SUPPORTED)
+void /* PRIVATE */
+png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   unsigned int num, i;
+   png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH];
+
+   png_debug(1, "in png_handle_hIST\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before hIST");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid hIST after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (!(png_ptr->mode & PNG_HAVE_PLTE))
+   {
+      png_warning(png_ptr, "Missing PLTE before hIST");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST))
+   {
+      png_warning(png_ptr, "Duplicate hIST chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   num = length / 2 ;
+   if (num != (unsigned int) png_ptr->num_palette || num >
+      (unsigned int) PNG_MAX_PALETTE_LENGTH)
+   {
+      png_warning(png_ptr, "Incorrect hIST chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   for (i = 0; i < num; i++)
+   {
+      png_byte buf[2];
+
+      png_crc_read(png_ptr, buf, 2);
+      readbuf[i] = png_get_uint_16(buf);
+   }
+
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   png_set_hIST(png_ptr, info_ptr, readbuf);
+}
+#endif
+
+#if defined(PNG_READ_pHYs_SUPPORTED)
+void /* PRIVATE */
+png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_uint_32 res_x, res_y;
+   int unit_type;
+
+   png_debug(1, "in png_handle_pHYs\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before pHYs");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid pHYs after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs))
+   {
+      png_warning(png_ptr, "Duplicate pHYs chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_warning(png_ptr, "Incorrect pHYs chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   res_x = png_get_uint_32(buf);
+   res_y = png_get_uint_32(buf + 4);
+   unit_type = buf[8];
+   png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type);
+}
+#endif
+
+#if defined(PNG_READ_oFFs_SUPPORTED)
+void /* PRIVATE */
+png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[9];
+   png_int_32 offset_x, offset_y;
+   int unit_type;
+
+   png_debug(1, "in png_handle_oFFs\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before oFFs");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid oFFs after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs))
+   {
+      png_warning(png_ptr, "Duplicate oFFs chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (length != 9)
+   {
+      png_warning(png_ptr, "Incorrect oFFs chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 9);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   offset_x = png_get_int_32(buf);
+   offset_y = png_get_int_32(buf + 4);
+   unit_type = buf[8];
+   png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type);
+}
+#endif
+
+#if defined(PNG_READ_pCAL_SUPPORTED)
+/* read the pCAL chunk (described in the PNG Extensions document) */
+void /* PRIVATE */
+png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_charp purpose;
+   png_int_32 X0, X1;
+   png_byte type, nparams;
+   png_charp buf, units, endptr;
+   png_charpp params;
+   png_size_t slength;
+   int i;
+
+   png_debug(1, "in png_handle_pCAL\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before pCAL");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid pCAL after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL))
+   {
+      png_warning(png_ptr, "Duplicate pCAL chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n",
+      length + 1);
+   purpose = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (purpose == NULL)
+     {
+       png_warning(png_ptr, "No memory for pCAL purpose.");
+       return;
+     }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)purpose, slength);
+
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, purpose);
+      return;
+   }
+
+   purpose[slength] = 0x00; /* null terminate the last string */
+
+   png_debug(3, "Finding end of pCAL purpose string\n");
+   for (buf = purpose; *buf; buf++)
+      /* empty loop */ ;
+
+   endptr = purpose + slength;
+
+   /* We need to have at least 12 bytes after the purpose string
+      in order to get the parameter information. */
+   if (endptr <= buf + 12)
+   {
+      png_warning(png_ptr, "Invalid pCAL data");
+      png_free(png_ptr, purpose);
+      return;
+   }
+
+   png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n");
+   X0 = png_get_int_32((png_bytep)buf+1);
+   X1 = png_get_int_32((png_bytep)buf+5);
+   type = buf[9];
+   nparams = buf[10];
+   units = buf + 11;
+
+   png_debug(3, "Checking pCAL equation type and number of parameters\n");
+   /* Check that we have the right number of parameters for known
+      equation types. */
+   if ((type == PNG_EQUATION_LINEAR && nparams != 2) ||
+       (type == PNG_EQUATION_BASE_E && nparams != 3) ||
+       (type == PNG_EQUATION_ARBITRARY && nparams != 3) ||
+       (type == PNG_EQUATION_HYPERBOLIC && nparams != 4))
+   {
+      png_warning(png_ptr, "Invalid pCAL parameters for equation type");
+      png_free(png_ptr, purpose);
+      return;
+   }
+   else if (type >= PNG_EQUATION_LAST)
+   {
+      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+   }
+
+   for (buf = units; *buf; buf++)
+      /* Empty loop to move past the units string. */ ;
+
+   png_debug(3, "Allocating pCAL parameters array\n");
+   params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams
+      *png_sizeof(png_charp))) ;
+   if (params == NULL)
+     {
+       png_free(png_ptr, purpose);
+       png_warning(png_ptr, "No memory for pCAL params.");
+       return;
+     }
+
+   /* Get pointers to the start of each parameter string. */
+   for (i = 0; i < (int)nparams; i++)
+   {
+      buf++; /* Skip the null string terminator from previous parameter. */
+
+      png_debug1(3, "Reading pCAL parameter %d\n", i);
+      for (params[i] = buf; *buf != 0x00 && buf <= endptr; buf++)
+         /* Empty loop to move past each parameter string */ ;
+
+      /* Make sure we haven't run out of data yet */
+      if (buf > endptr)
+      {
+         png_warning(png_ptr, "Invalid pCAL data");
+         png_free(png_ptr, purpose);
+         png_free(png_ptr, params);
+         return;
+      }
+   }
+
+   png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams,
+      units, params);
+
+   png_free(png_ptr, purpose);
+   png_free(png_ptr, params);
+}
+#endif
+
+#if defined(PNG_READ_sCAL_SUPPORTED)
+/* read the sCAL chunk */
+void /* PRIVATE */
+png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_charp buffer, ep;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   double width, height;
+   png_charp vp;
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_charp swidth, sheight;
+#endif
+#endif
+   png_size_t slength;
+
+   png_debug(1, "in png_handle_sCAL\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before sCAL");
+   else if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+      png_warning(png_ptr, "Invalid sCAL after IDAT");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL))
+   {
+      png_warning(png_ptr, "Duplicate sCAL chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n",
+      length + 1);
+   buffer = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (buffer == NULL)
+     {
+       png_warning(png_ptr, "Out of memory while processing sCAL chunk");
+       return;
+     }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)buffer, slength);
+
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, buffer);
+      return;
+   }
+
+   buffer[slength] = 0x00; /* null terminate the last string */
+
+   ep = buffer + 1;        /* skip unit byte */
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   width = png_strtod(png_ptr, ep, &vp);
+   if (*vp)
+   {
+       png_warning(png_ptr, "malformed width string in sCAL chunk");
+       return;
+   }
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1);
+   if (swidth == NULL)
+     {
+       png_warning(png_ptr, "Out of memory while processing sCAL chunk width");
+       return;
+     }
+   png_memcpy(swidth, ep, (png_size_t)png_strlen(ep));
+#endif
+#endif
+
+   for (ep = buffer; *ep; ep++)
+      /* empty loop */ ;
+   ep++;
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   height = png_strtod(png_ptr, ep, &vp);
+   if (*vp)
+   {
+       png_warning(png_ptr, "malformed height string in sCAL chunk");
+       return;
+   }
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1);
+   if (swidth == NULL)
+     {
+       png_warning(png_ptr, "Out of memory while processing sCAL chunk height");
+       return;
+     }
+   png_memcpy(sheight, ep, (png_size_t)png_strlen(ep));
+#endif
+#endif
+
+   if (buffer + slength < ep
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+      || width <= 0. || height <= 0.
+#endif
+      )
+   {
+      png_warning(png_ptr, "Invalid sCAL data");
+      png_free(png_ptr, buffer);
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+      png_free(png_ptr, swidth);
+      png_free(png_ptr, sheight);
+#endif
+      return;
+   }
+
+
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height);
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight);
+#endif
+#endif
+
+   png_free(png_ptr, buffer);
+#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED)
+   png_free(png_ptr, swidth);
+   png_free(png_ptr, sheight);
+#endif
+}
+#endif
+
+#if defined(PNG_READ_tIME_SUPPORTED)
+void /* PRIVATE */
+png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_byte buf[7];
+   png_time mod_time;
+
+   png_debug(1, "in png_handle_tIME\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Out of place tIME chunk");
+   else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME))
+   {
+      png_warning(png_ptr, "Duplicate tIME chunk");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+   if (length != 7)
+   {
+      png_warning(png_ptr, "Incorrect tIME chunk length");
+      png_crc_finish(png_ptr, length);
+      return;
+   }
+
+   png_crc_read(png_ptr, buf, 7);
+   if (png_crc_finish(png_ptr, 0))
+      return;
+
+   mod_time.second = buf[6];
+   mod_time.minute = buf[5];
+   mod_time.hour = buf[4];
+   mod_time.day = buf[3];
+   mod_time.month = buf[2];
+   mod_time.year = png_get_uint_16(buf);
+
+   png_set_tIME(png_ptr, info_ptr, &mod_time);
+}
+#endif
+
+#if defined(PNG_READ_tEXt_SUPPORTED)
+/* Note: this does not properly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp key;
+   png_charp text;
+   png_uint_32 skip = 0;
+   png_size_t slength;
+   int ret;
+
+   png_debug(1, "in png_handle_tEXt\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before tEXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   if (length > (png_uint_32)65535L)
+   {
+      png_warning(png_ptr, "tEXt chunk too large to fit in memory");
+      skip = length - (png_uint_32)65535L;
+      length = (png_uint_32)65535L;
+   }
+#endif
+
+   key = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (key == NULL)
+   {
+     png_warning(png_ptr, "No memory to process text chunk.");
+     return;
+   }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)key, slength);
+
+   if (png_crc_finish(png_ptr, skip))
+   {
+      png_free(png_ptr, key);
+      return;
+   }
+
+   key[slength] = 0x00;
+
+   for (text = key; *text; text++)
+      /* empty loop to find end of key */ ;
+
+   if (text != key + slength)
+      text++;
+
+   text_ptr = (png_textp)png_malloc_warn(png_ptr,
+      (png_uint_32)png_sizeof(png_text));
+   if (text_ptr == NULL)
+   {
+     png_warning(png_ptr, "Not enough memory to process text chunk.");
+     png_free(png_ptr, key);
+     return;
+   }
+   text_ptr->compression = PNG_TEXT_COMPRESSION_NONE;
+   text_ptr->key = key;
+#ifdef PNG_iTXt_SUPPORTED
+   text_ptr->lang = NULL;
+   text_ptr->lang_key = NULL;
+   text_ptr->itxt_length = 0;
+#endif
+   text_ptr->text = text;
+   text_ptr->text_length = png_strlen(text);
+
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, key);
+   png_free(png_ptr, text_ptr);
+   if (ret)
+     png_warning(png_ptr, "Insufficient memory to process text chunk.");
+}
+#endif
+
+#if defined(PNG_READ_zTXt_SUPPORTED)
+/* note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp chunkdata;
+   png_charp text;
+   int comp_type;
+   int ret;
+   png_size_t slength, prefix_len, data_len;
+
+   png_debug(1, "in png_handle_zTXt\n");
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before zTXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We will no doubt have problems with chunks even half this size, but
+      there is no hard and fast rule to tell us where to stop. */
+   if (length > (png_uint_32)65535L)
+   {
+     png_warning(png_ptr,"zTXt chunk too large to fit in memory");
+     png_crc_finish(png_ptr, length);
+     return;
+   }
+#endif
+
+   chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (chunkdata == NULL)
+   {
+     png_warning(png_ptr,"Out of memory processing zTXt chunk.");
+     return;
+   }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (text = chunkdata; *text; text++)
+      /* empty loop */ ;
+
+   /* zTXt must have some text after the chunkdataword */
+   if (text == chunkdata + slength - 1)
+   {
+      png_warning(png_ptr, "Truncated zTXt chunk");
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+   else
+   {
+       comp_type = *(++text);
+       if (comp_type != PNG_TEXT_COMPRESSION_zTXt)
+       {
+          png_warning(png_ptr, "Unknown compression type in zTXt chunk");
+          comp_type = PNG_TEXT_COMPRESSION_zTXt;
+       }
+       text++;        /* skip the compression_method byte */
+   }
+   prefix_len = text - chunkdata;
+
+   chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata,
+                                    (png_size_t)length, prefix_len, &data_len);
+
+   text_ptr = (png_textp)png_malloc_warn(png_ptr,
+     (png_uint_32)png_sizeof(png_text));
+   if (text_ptr == NULL)
+   {
+     png_warning(png_ptr,"Not enough memory to process zTXt chunk.");
+     png_free(png_ptr, chunkdata);
+     return;
+   }
+   text_ptr->compression = comp_type;
+   text_ptr->key = chunkdata;
+#ifdef PNG_iTXt_SUPPORTED
+   text_ptr->lang = NULL;
+   text_ptr->lang_key = NULL;
+   text_ptr->itxt_length = 0;
+#endif
+   text_ptr->text = chunkdata + prefix_len;
+   text_ptr->text_length = data_len;
+
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, text_ptr);
+   png_free(png_ptr, chunkdata);
+   if (ret)
+     png_error(png_ptr, "Insufficient memory to store zTXt chunk.");
+}
+#endif
+
+#if defined(PNG_READ_iTXt_SUPPORTED)
+/* note: this does not correctly handle chunks that are > 64K under DOS */
+void /* PRIVATE */
+png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_textp text_ptr;
+   png_charp chunkdata;
+   png_charp key, lang, text, lang_key;
+   int comp_flag;
+   int comp_type = 0;
+   int ret;
+   png_size_t slength, prefix_len, data_len;
+
+   png_debug(1, "in png_handle_iTXt\n");
+
+   if (!(png_ptr->mode & PNG_HAVE_IHDR))
+      png_error(png_ptr, "Missing IHDR before iTXt");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+      png_ptr->mode |= PNG_AFTER_IDAT;
+
+#ifdef PNG_MAX_MALLOC_64K
+   /* We will no doubt have problems with chunks even half this size, but
+      there is no hard and fast rule to tell us where to stop. */
+   if (length > (png_uint_32)65535L)
+   {
+     png_warning(png_ptr,"iTXt chunk too large to fit in memory");
+     png_crc_finish(png_ptr, length);
+     return;
+   }
+#endif
+
+   chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1);
+   if (chunkdata == NULL)
+   {
+     png_warning(png_ptr, "No memory to process iTXt chunk.");
+     return;
+   }
+   slength = (png_size_t)length;
+   png_crc_read(png_ptr, (png_bytep)chunkdata, slength);
+   if (png_crc_finish(png_ptr, 0))
+   {
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   chunkdata[slength] = 0x00;
+
+   for (lang = chunkdata; *lang; lang++)
+      /* empty loop */ ;
+   lang++;        /* skip NUL separator */
+
+   /* iTXt must have a language tag (possibly empty), two compression bytes,
+      translated keyword (possibly empty), and possibly some text after the
+      keyword */
+
+   if (lang >= chunkdata + slength - 3)
+   {
+      png_warning(png_ptr, "Truncated iTXt chunk");
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+   else
+   {
+       comp_flag = *lang++;
+       comp_type = *lang++;
+   }
+
+   for (lang_key = lang; *lang_key; lang_key++)
+      /* empty loop */ ;
+   lang_key++;        /* skip NUL separator */
+
+   for (text = lang_key; *text; text++)
+      /* empty loop */ ;
+   text++;        /* skip NUL separator */
+   if (text >= chunkdata + slength)
+   {
+      png_warning(png_ptr, "Malformed iTXt chunk");
+      png_free(png_ptr, chunkdata);
+      return;
+   }
+
+   prefix_len = text - chunkdata;
+
+   key=chunkdata;
+   if (comp_flag)
+       chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata,
+          (size_t)length, prefix_len, &data_len);
+   else
+       data_len=png_strlen(chunkdata + prefix_len);
+   text_ptr = (png_textp)png_malloc_warn(png_ptr,
+      (png_uint_32)png_sizeof(png_text));
+   if (text_ptr == NULL)
+   {
+     png_warning(png_ptr,"Not enough memory to process iTXt chunk.");
+     png_free(png_ptr, chunkdata);
+     return;
+   }
+   text_ptr->compression = (int)comp_flag + 1;
+   text_ptr->lang_key = chunkdata+(lang_key-key);
+   text_ptr->lang = chunkdata+(lang-key);
+   text_ptr->itxt_length = data_len;
+   text_ptr->text_length = 0;
+   text_ptr->key = chunkdata;
+   text_ptr->text = chunkdata + prefix_len;
+
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1);
+
+   png_free(png_ptr, text_ptr);
+   png_free(png_ptr, chunkdata);
+   if (ret)
+     png_error(png_ptr, "Insufficient memory to store iTXt chunk.");
+}
+#endif
+
+/* This function is called when we haven't found a handler for a
+   chunk.  If there isn't a problem with the chunk itself (ie bad
+   chunk name, CRC, or a critical chunk), the chunk is silently ignored
+   -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which
+   case it will be saved away to be written out later. */
+void /* PRIVATE */
+png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length)
+{
+   png_uint_32 skip = 0;
+
+   png_debug(1, "in png_handle_unknown\n");
+
+   if (png_ptr->mode & PNG_HAVE_IDAT)
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_CONST PNG_IDAT;
+#endif
+      if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4))  /* not an IDAT */
+         png_ptr->mode |= PNG_AFTER_IDAT;
+   }
+
+   png_check_chunk_name(png_ptr, png_ptr->chunk_name);
+
+   if (!(png_ptr->chunk_name[0] & 0x20))
+   {
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+      if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+           PNG_HANDLE_CHUNK_ALWAYS
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+           && png_ptr->read_user_chunk_fn == NULL
+#endif
+        )
+#endif
+          png_chunk_error(png_ptr, "unknown critical chunk");
+   }
+
+#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED)
+   if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) ||
+       (png_ptr->read_user_chunk_fn != NULL))
+   {
+#ifdef PNG_MAX_MALLOC_64K
+       if (length > (png_uint_32)65535L)
+       {
+           png_warning(png_ptr, "unknown chunk too large to fit in memory");
+           skip = length - (png_uint_32)65535L;
+           length = (png_uint_32)65535L;
+       }
+#endif
+       png_strncpy((png_charp)png_ptr->unknown_chunk.name,
+	 (png_charp)png_ptr->chunk_name,
+         png_sizeof((png_charp)png_ptr->chunk_name));
+       png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length);
+       png_ptr->unknown_chunk.size = (png_size_t)length;
+       png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length);
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+       if(png_ptr->read_user_chunk_fn != NULL)
+       {
+          /* callback to user unknown chunk handler */
+          int ret;
+          ret = (*(png_ptr->read_user_chunk_fn))
+            (png_ptr, &png_ptr->unknown_chunk);
+          if (ret < 0)
+             png_chunk_error(png_ptr, "error in user chunk");
+          if (ret == 0)
+          {
+             if (!(png_ptr->chunk_name[0] & 0x20))
+                if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) !=
+                     PNG_HANDLE_CHUNK_ALWAYS)
+                   png_chunk_error(png_ptr, "unknown critical chunk");
+             png_set_unknown_chunks(png_ptr, info_ptr,
+               &png_ptr->unknown_chunk, 1);
+          }
+       }
+#else
+       png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1);
+#endif
+       png_free(png_ptr, png_ptr->unknown_chunk.data);
+       png_ptr->unknown_chunk.data = NULL;
+   }
+   else
+#endif
+      skip = length;
+
+   png_crc_finish(png_ptr, skip);
+
+#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+   info_ptr = info_ptr; /* quiet compiler warnings about unused info_ptr */
+#endif
+}
+
+/* This function is called to verify that a chunk name is valid.
+   This function can't have the "critical chunk check" incorporated
+   into it, since in the future we will need to be able to call user
+   functions to handle unknown critical chunks after we check that
+   the chunk name itself is valid. */
+
+#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
+
+void /* PRIVATE */
+png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name)
+{
+   png_debug(1, "in png_check_chunk_name\n");
+   if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) ||
+       isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3]))
+   {
+      png_chunk_error(png_ptr, "invalid chunk type");
+   }
+}
+
+/* Combines the row recently read in with the existing pixels in the
+   row.  This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined,
+   a zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.  If
+   you want all pixels to be combined, pass 0xff (255) in mask.  */
+
+/* Optimized C version of utilities to read a PNG file
+ *
+ * Based on code contributed by Nirav Chhatrapati, Intel Corp., 1998.
+ * Interface to libpng contributed by Gilles Vollant, 1999.
+ * GNU C port by Greg Roelofs, 1999-2001.
+ *
+ */
+
+#if defined(PNG_OPTIMIZED_CODE_SUPPORTED)
+#if !defined(PNG_HAVE_MMX_COMBINE_ROW)
+
+/*===========================================================================*/
+/*                                                                           */
+/*                       P N G _ C O M B I N E _ R O W                       */
+/*                                                                           */
+/*===========================================================================*/
+
+
+#define BPP2  2
+#define BPP3  3 /* bytes per pixel (a.k.a. pixel_bytes) */
+#define BPP4  4
+#define BPP6  6 /* (defined only to help avoid cut-and-paste errors) */
+#define BPP8  8
+
+/* Combines the row recently read in with the previous row.
+   This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined; a
+   zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.
+   If you want all pixels to be combined, pass 0xff (255) in mask. */
+
+/* Use this routine for the x86 platform - it uses a faster MMX routine
+   if the machine supports MMX. */
+
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+
+#if defined(PNG_USE_LOCAL_ARRAYS)
+static PNG_CONST int FARDATA png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+static PNG_CONST int FARDATA png_pass_inc[7]   = {8, 8, 4, 4, 2, 2, 1};
+static PNG_CONST int FARDATA png_pass_width[7] = {8, 4, 4, 2, 2, 1, 1};
+#endif
+
+   png_debug(1, "in png_combine_row (pngrutil.c OPTIMIZED)\n");
+
+   if (mask == 0xff)
+   {
+      png_debug(2,"mask == 0xff:  doing single png_memcpy()\n");
+      png_memcpy(row, png_ptr->row_buf + 1,
+       (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,png_ptr->width));
+   }
+   else   /* (png_combine_row() is never called with mask == 0) */
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         /* most common case:  combining 24-bit RGB */
+         case 24:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP3 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP3 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP3 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7; /* reduce to mult of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP3 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP3;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 24 bpp */
+
+         case 32:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP4 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP4 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP4 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7; /* reduce to mult of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP4 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP4;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            }
+
+            break;
+         }       /* end 32 bpp */
+
+         case 8:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7; /* reduce to mult of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = len;  /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff /* *BPP1 */ ;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            }
+
+            break;
+         }       /* end 8 bpp */
+
+         case 1:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_inc, s_start, s_end;
+            int m;
+            int shift;
+            png_uint_32 i;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }       /* end 1 bpp */
+
+         case 2:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }       /* end 2 bpp */
+
+         case 4:        /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }       /* end 4 bpp */
+
+         case 16:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP2 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP2 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP2 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7;  /* reduce to mult of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP2 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP2;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            } /* end of else (_mmx_supported) */
+
+            break;
+         }       /* end 16 bpp */
+
+
+
+         case 48:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            {
+               register png_uint_32 i;
+               png_uint_32 initial_val = BPP6 * png_pass_start[png_ptr->pass];
+                 /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+               register int stride = BPP6 * png_pass_inc[png_ptr->pass];
+                 /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+               register int rep_bytes = BPP6 * png_pass_width[png_ptr->pass];
+                 /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+               png_uint_32 len = png_ptr->width &~7; /* reduce to mult of 8 */
+               int diff = (int) (png_ptr->width & 7); /* amount lost */
+               register png_uint_32 final_val = BPP6 * len;   /* GRR bugfix */
+
+               srcptr = png_ptr->row_buf + 1 + initial_val;
+               dstptr = row + initial_val;
+
+               for (i = initial_val; i < final_val; i += stride)
+               {
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+               if (diff)  /* number of leftover pixels:  3 for pngtest */
+               {
+                  final_val += diff*BPP6;
+                  for (; i < final_val; i += stride)
+                  {
+                     if (rep_bytes > (int)(final_val-i))
+                        rep_bytes = (int)(final_val-i);
+                     png_memcpy(dstptr, srcptr, rep_bytes);
+                     srcptr += stride;
+                     dstptr += stride;
+                  }
+               }
+            }
+            break;
+         }       /* end 48 bpp */
+
+         case 64:       /* png_ptr->row_info.pixel_depth */
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            register png_uint_32 i;
+            png_uint_32 initial_val = BPP8 * png_pass_start[png_ptr->pass];
+              /* png.c:  png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; */
+            register int stride = BPP8 * png_pass_inc[png_ptr->pass];
+              /* png.c:  png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; */
+            register int rep_bytes = BPP8 * png_pass_width[png_ptr->pass];
+              /* png.c:  png_pass_width[] = {8, 4, 4, 2, 2, 1, 1}; */
+            png_uint_32 len = png_ptr->width &~7;  /* reduce to mult of 8 */
+            int diff = (int) (png_ptr->width & 7); /* amount lost */
+            register png_uint_32 final_val = BPP8 * len;   /* GRR bugfix */
+
+            srcptr = png_ptr->row_buf + 1 + initial_val;
+            dstptr = row + initial_val;
+
+            for (i = initial_val; i < final_val; i += stride)
+            {
+               png_memcpy(dstptr, srcptr, rep_bytes);
+               srcptr += stride;
+               dstptr += stride;
+            }
+            if (diff)  /* number of leftover pixels:  3 for pngtest */
+            {
+               final_val += diff*BPP8;
+               for (; i < final_val; i += stride)
+               {
+                  if (rep_bytes > (int)(final_val-i))
+                     rep_bytes = (int)(final_val-i);
+                  png_memcpy(dstptr, srcptr, rep_bytes);
+                  srcptr += stride;
+                  dstptr += stride;
+               }
+            }
+
+            break;
+         }       /* end 64 bpp */
+
+         default: /* png_ptr->row_info.pixel_depth != 1,2,4,8,16,24,32,48,64 */
+         {
+            /* this should never happen */
+            png_warning(png_ptr, "Invalid row_info.pixel_depth in pngrutil");
+            break;
+         }
+      } /* end switch (png_ptr->row_info.pixel_depth) */
+
+   } /* end if (non-trivial mask) */
+
+} /* end png_combine_row() */
+#endif /* PNG_HAVE_MMX_COMBINE_ROW */
+
+
+
+/*===========================================================================*/
+/*                                                                           */
+/*                 P N G _ D O _ R E A D _ I N T E R L A C E                 */
+/*                                                                           */
+/*===========================================================================*/
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+#if !defined(PNG_HAVE_MMX_READ_INTERLACE)
+
+/* png_do_read_interlace() is called after any 16-bit to 8-bit conversion
+ * has taken place.  [GRR: what other steps come before and/or after?]
+ */
+
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+#if defined(PNG_USE_LOCAL_ARRAYS)
+static PNG_CONST int FARDATA png_pass_inc[7]   = {8, 8, 4, 4, 2, 2, 1};
+#endif
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+   png_uint_32 transformations = png_ptr->transformations;
+#endif
+   png_debug(1,"in png_do_read_interlace (pngrutil.c OPTIMIZED)\n");
+
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            dp = row + (png_size_t)((final_width - 1) >> 3);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)((row_info->width + 7) & 7);
+               dshift = (int)((final_width + 7) & 7);
+               s_start = 7;
+               s_end = 0;
+               s_inc = -1;
+            }
+            else
+#endif
+            {
+               sshift = 7 - (int)((row_info->width + 7) & 7);
+               dshift = 7 - (int)((final_width + 7) & 7);
+               s_start = 0;
+               s_end = 7;
+               s_inc = 1;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x1);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            dp = row + (png_size_t)((final_width - 1) >> 2);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 3) & 3) << 1);
+               dshift = (png_size_t)(((final_width + 3) & 3) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
+               dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x3);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            dp = row + (png_size_t)((final_width - 1) >> 1);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 1) & 1) << 2);
+               dshift = (png_size_t)(((final_width + 1) & 1) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
+               dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0xf);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+       /*====================================================================*/
+
+         default: /* 8-bit or larger (this is where the routine is modified) */
+         {
+            png_bytep sptr, dp;
+            png_uint_32 i;
+            png_size_t pixel_bytes;
+            int width = (int)row_info->width;
+
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            /* point sptr at the last pixel in the pre-expanded row: */
+            sptr = row + (width - 1) * pixel_bytes;
+
+            /* point dp at the last pixel position in the expanded row: */
+            dp = row + (final_width - 1) * pixel_bytes;
+
+            /* MMX not supported:  use modified C code - takes advantage
+             *   of inlining of png_memcpy for a constant */
+            /* GRR 19991007:  does it?  or should pixel_bytes in each
+             *   block be replaced with immediate value (e.g., 1)? */
+            /* GRR 19991017:  replaced with constants in each case */
+            {
+               if (pixel_bytes == 1)
+               {
+                  for (i = width; i; i--)
+                  {
+                     int j;
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        *dp-- = *sptr;
+                     }
+                     --sptr;
+                  }
+               }
+               else if (pixel_bytes == 3)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 3);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 3);
+                        dp -= 3;
+                     }
+                     sptr -= 3;
+                  }
+               }
+               else if (pixel_bytes == 2)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 2);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 2);
+                        dp -= 2;
+                     }
+                     sptr -= 2;
+                  }
+               }
+               else if (pixel_bytes == 4)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 4);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+#if defined(PNG_DEBUG) && defined(PNG_1_0_X)
+                        if (dp < row || dp+3 > row+png_ptr->row_buf_size)
+                        {
+                           printf("dp out of bounds: row=%d, dp=%d, rp=%d\n",
+                             row, dp, row+png_ptr->row_buf_size);
+                           printf("row_buf=%d\n",png_ptr->row_buf_size);
+                        }
+#endif
+                        png_memcpy(dp, v, 4);
+                        dp -= 4;
+                     }
+                     sptr -= 4;
+                  }
+               }
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 6);
+                        dp -= 6;
+                     }
+                     sptr -= 6;
+                  }
+               }
+               else if (pixel_bytes == 8)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 8);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 8);
+                        dp -= 8;
+                     }
+                     sptr -= 8;
+                  }
+               }
+               else     /* GRR:  should never be reached */
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+
+            }
+            break;
+         }
+      } /* end switch (row_info->pixel_depth) */
+
+      row_info->width = final_width;
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+
+} /* end png_do_read_interlace() */
+
+#endif /* PNG_HAVE_MMX_READ_INTERLACE */
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+
+
+#if !defined(PNG_HAVE_MMX_READ_FILTER_ROW)
+/*===========================================================================*/
+/*                                                                           */
+/*                   P N G _ R E A D _ F I L T E R _ R O W                   */
+/*                                                                           */
+/*===========================================================================*/
+
+
+/* Optimized png_read_filter_row routines */
+
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep
+   row, png_bytep prev_row, int filter)
+{
+#if defined(PNG_DEBUG)
+   char filnm[10];
+#endif
+
+
+#if defined(PNG_DEBUG)
+   png_debug(1, "in png_read_filter_row (pngrutil.c OPTIMIZED)\n");
+   switch (filter)
+   {
+      case 0:
+         png_snprintf(filnm, 10, "none");
+         break;
+
+      case 1:
+         png_snprintf(filnm, 10, "sub-%s",
+             "x86");
+         break;
+
+      case 2:
+         png_snprintf(filnm, 10, "up-%s",
+             "x86");
+         break;
+
+      case 3:
+         png_snprintf(filnm, 10, "avg-%s",
+             "x86");
+         break;
+
+      case 4:
+         png_snprintf(filnm, 10, "Paeth-%s",
+             "x86");
+         break;
+
+      default:
+         png_snprintf(filnm, 10, "unknown");
+         break;
+   }
+   png_debug2(0, "row_number=%5ld, %10s, ", png_ptr->row_number, filnm);
+   png_debug1(0, "row=0x%08lx, ", (unsigned long)row);
+   png_debug2(0, "pixdepth=%2d, bytes=%d, ", (int)row_info->pixel_depth,
+      (int)((row_info->pixel_depth + 7) >> 3));
+   png_debug1(0,"rowbytes=%8ld\n", row_info->rowbytes);
+#endif /* PNG_DEBUG */
+
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+
+      case PNG_FILTER_VALUE_SUB:
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_bytep rp = row + bpp;
+            png_bytep lp = row;
+
+            for (i = bpp; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+
+      case PNG_FILTER_VALUE_UP:
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+
+            for (i = 0; i < istop; ++i)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+
+      case PNG_FILTER_VALUE_AVG:
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++) >> 1)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++ + *lp++) >> 1)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+
+      case PNG_FILTER_VALUE_PAETH:
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_bytep cp = prev_row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)   /* use leftover rp,pp */
+            {
+               int a, b, c, pa, pb, pc, p;
+
+               a = *lp++;
+               b = *pp++;
+               c = *cp++;
+
+               p = b - c;
+               pc = a - c;
+
+#if defined(PNG_USE_ABS)
+               pa = abs(p);
+               pb = abs(pc);
+               pc = abs(p + pc);
+#else
+               pa = p < 0 ? -p : p;
+               pb = pc < 0 ? -pc : pc;
+               pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+               /*
+                  if (pa <= pb && pa <= pc)
+                     p = a;
+                  else if (pb <= pc)
+                     p = b;
+                  else
+                     p = c;
+                */
+
+               p = (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c;
+
+               *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+               rp++;
+            }
+         }
+         break;
+
+      default:
+         png_warning(png_ptr, "Ignoring bad row-filter type");
+         *row=0;
+         break;
+   }
+}
+
+#endif /* PNG_HAVE_MMX_READ_FILTER_ROW */
+#endif /* PNG_OPTIMIZED_CODE_SUPPORTED */
+
+#if !defined(PNG_USE_PNGGCCRD) && !defined(PNG_USE_PNGVCRD)
+#if !defined(PNG_OPTIMIZED_CODE_SUPPORTED)
+/* Use the unoptimized original C code.  This might be removed from a future
+ * version of libpng if testing proves it to be worthless. */
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+   png_debug(1,"in png_combine_row NOT OPTIMIZED\n");
+   if (mask == 0xff)
+   {
+      png_memcpy(row, png_ptr->row_buf + 1,
+         PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width));
+   }
+   else
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_inc, s_start, s_end;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x01;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_start, s_end, s_inc;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            int value;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x03;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            int s_start, s_end, s_inc;
+            int m = 0x80;
+            int shift;
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            int value;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+         default:
+         {
+            png_bytep sp = png_ptr->row_buf + 1;
+            png_bytep dp = row;
+            png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+            png_uint_32 i;
+            png_uint_32 row_width = png_ptr->width;
+            png_byte m = 0x80;
+
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (m & mask)
+               {
+                  png_memcpy(dp, sp, pixel_bytes);
+               }
+
+               sp += pixel_bytes;
+               dp += pixel_bytes;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+      }
+   }
+}
+
+#ifdef PNG_READ_INTERLACING_SUPPORTED
+/* OLD pre-1.0.9 interface:
+void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass,
+   png_uint_32 transformations)
+ */
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+   png_uint_32 transformations = png_ptr->transformations;
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+   /* offset to next interlace block */
+   PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1,"in png_do_read_interlace (pngrutil.c NOT OPTIMIZED)\n");
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 3);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+                sshift = (int)((row_info->width + 7) & 0x07);
+                dshift = (int)((final_width + 7) & 0x07);
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+            else
+#endif
+            {
+                sshift = 7 - (int)((row_info->width + 7) & 0x07);
+                dshift = 7 - (int)((final_width + 7) & 0x07);
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x01);
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2);
+            png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)(((row_info->width + 3) & 0x03) << 1);
+               dshift = (int)(((final_width + 3) & 0x03) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1);
+               dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x03);
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            png_bytep dp = row + (png_size_t)((final_width - 1) >> 1);
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+            int jstop = png_pass_inc[pass];
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)(((row_info->width + 1) & 0x01) << 2);
+               dshift = (int)(((final_width + 1) & 0x01) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2);
+               dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v = (png_byte)((*sp >> sshift) & 0xf);
+               int j;
+
+               for (j = 0; j < jstop; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+         default:
+         {
+            png_size_t pixel_bytes = (row_info->pixel_depth >> 3);
+            png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes;
+            png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes;
+
+            int jstop = png_pass_inc[pass];
+            png_uint_32 i;
+
+            for (i = 0; i < row_info->width; i++)
+            {
+               png_byte v[8];
+               int j;
+
+               png_memcpy(v, sp, pixel_bytes);
+               for (j = 0; j < jstop; j++)
+               {
+                  png_memcpy(dp, v, pixel_bytes);
+                  dp -= pixel_bytes;
+               }
+               sp -= pixel_bytes;
+            }
+            break;
+         }
+      }
+      row_info->width = final_width;
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+#if !defined(PNG_READ_PACKSWAP_SUPPORTED)
+   transformations = transformations; /* silence compiler warning */
+#endif
+}
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row,
+   png_bytep prev_row, int filter)
+{
+   png_debug(1, "in png_read_filter_row (NOT OPTIMIZED)\n");
+   png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter);
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+      case PNG_FILTER_VALUE_SUB:
+      {
+         png_uint_32 i;
+         png_uint_32 istop = row_info->rowbytes;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+         png_bytep rp = row + bpp;
+         png_bytep lp = row;
+
+         for (i = bpp; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_UP:
+      {
+         png_uint_32 i;
+         png_uint_32 istop = row_info->rowbytes;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+
+         for (i = 0; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_AVG:
+      {
+         png_uint_32 i;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+         png_bytep lp = row;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+         png_uint_32 istop = row_info->rowbytes - bpp;
+
+         for (i = 0; i < bpp; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               ((int)(*pp++) / 2 )) & 0xff);
+            rp++;
+         }
+
+         for (i = 0; i < istop; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) +
+               (int)(*pp++ + *lp++) / 2 ) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      case PNG_FILTER_VALUE_PAETH:
+      {
+         png_uint_32 i;
+         png_bytep rp = row;
+         png_bytep pp = prev_row;
+         png_bytep lp = row;
+         png_bytep cp = prev_row;
+         png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+         png_uint_32 istop=row_info->rowbytes - bpp;
+
+         for (i = 0; i < bpp; i++)
+         {
+            *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+            rp++;
+         }
+
+         for (i = 0; i < istop; i++)   /* use leftover rp,pp */
+         {
+            int a, b, c, pa, pb, pc, p;
+
+            a = *lp++;
+            b = *pp++;
+            c = *cp++;
+
+            p = b - c;
+            pc = a - c;
+
+#ifdef PNG_USE_ABS
+            pa = abs(p);
+            pb = abs(pc);
+            pc = abs(p + pc);
+#else
+            pa = p < 0 ? -p : p;
+            pb = pc < 0 ? -pc : pc;
+            pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+            /*
+               if (pa <= pb && pa <= pc)
+                  p = a;
+               else if (pb <= pc)
+                  p = b;
+               else
+                  p = c;
+             */
+
+            p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+            *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+            rp++;
+         }
+         break;
+      }
+      default:
+         png_warning(png_ptr, "Ignoring bad adaptive filter type");
+         *row=0;
+         break;
+   }
+}
+#endif /* !PNG_OPTIMIZED_CODE_SUPPORTED */
+#endif /* !PNG_USE_PNGGCCRD && !PNG_USE_PNGVCRD */
+
+void /* PRIVATE */
+png_read_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   png_debug(1, "in png_read_finish_row\n");
+   png_ptr->row_number++;
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      png_memset_check(png_ptr, png_ptr->prev_row, 0,
+         png_ptr->rowbytes + 1);
+      do
+      {
+         png_ptr->pass++;
+         if (png_ptr->pass >= 7)
+            break;
+         png_ptr->iwidth = (png_ptr->width +
+            png_pass_inc[png_ptr->pass] - 1 -
+            png_pass_start[png_ptr->pass]) /
+            png_pass_inc[png_ptr->pass];
+
+         png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,
+            png_ptr->iwidth) + 1;
+
+         if (!(png_ptr->transformations & PNG_INTERLACE))
+         {
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (!(png_ptr->num_rows))
+               continue;
+         }
+         else  /* if (png_ptr->transformations & PNG_INTERLACE) */
+            break;
+      } while (png_ptr->iwidth == 0);
+
+      if (png_ptr->pass < 7)
+         return;
+   }
+
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED))
+   {
+#ifdef PNG_USE_LOCAL_ARRAYS
+      PNG_CONST PNG_IDAT;
+#endif
+      char extra;
+      int ret;
+
+      png_ptr->zstream.next_out = (Byte *)&extra;
+      png_ptr->zstream.avail_out = (uInt)1;
+      for(;;)
+      {
+         if (!(png_ptr->zstream.avail_in))
+         {
+            while (!png_ptr->idat_size)
+            {
+               png_byte chunk_length[4];
+
+               png_crc_finish(png_ptr, 0);
+
+               png_read_data(png_ptr, chunk_length, 4);
+               png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length);
+               png_reset_crc(png_ptr);
+               png_crc_read(png_ptr, png_ptr->chunk_name, 4);
+               if (png_memcmp(png_ptr->chunk_name, (png_bytep)png_IDAT, 4))
+                  png_error(png_ptr, "Not enough image data");
+
+            }
+            png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size;
+            png_ptr->zstream.next_in = png_ptr->zbuf;
+            if (png_ptr->zbuf_size > png_ptr->idat_size)
+               png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size;
+            png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in);
+            png_ptr->idat_size -= png_ptr->zstream.avail_in;
+         }
+         ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
+         if (ret == Z_STREAM_END)
+         {
+            if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in ||
+               png_ptr->idat_size)
+               png_warning(png_ptr, "Extra compressed data");
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+         if (ret != Z_OK)
+            png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg :
+                      "Decompression Error");
+
+         if (!(png_ptr->zstream.avail_out))
+         {
+            png_warning(png_ptr, "Extra compressed data.");
+            png_ptr->mode |= PNG_AFTER_IDAT;
+            png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED;
+            break;
+         }
+
+      }
+      png_ptr->zstream.avail_out = 0;
+   }
+
+   if (png_ptr->idat_size || png_ptr->zstream.avail_in)
+      png_warning(png_ptr, "Extra compression data");
+
+   inflateReset(&png_ptr->zstream);
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+}
+
+void /* PRIVATE */
+png_read_start_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   int max_pixel_depth;
+   png_uint_32 row_bytes;
+
+   png_debug(1, "in png_read_start_row\n");
+   png_ptr->zstream.avail_in = 0;
+   png_init_read_transformations(png_ptr);
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+      else
+         png_ptr->num_rows = png_ptr->height;
+
+      png_ptr->iwidth = (png_ptr->width +
+         png_pass_inc[png_ptr->pass] - 1 -
+         png_pass_start[png_ptr->pass]) /
+         png_pass_inc[png_ptr->pass];
+
+         row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1;
+
+         png_ptr->irowbytes = (png_size_t)row_bytes;
+         if((png_uint_32)png_ptr->irowbytes != row_bytes)
+            png_error(png_ptr, "Rowbytes overflow in png_read_start_row");
+   }
+   else
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->iwidth = png_ptr->width;
+      png_ptr->irowbytes = png_ptr->rowbytes + 1;
+   }
+   max_pixel_depth = png_ptr->pixel_depth;
+
+#if defined(PNG_READ_PACK_SUPPORTED)
+   if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8)
+      max_pixel_depth = 8;
+#endif
+
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+   if (png_ptr->transformations & PNG_EXPAND)
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      {
+         if (png_ptr->num_trans)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 24;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth < 8)
+            max_pixel_depth = 8;
+         if (png_ptr->num_trans)
+            max_pixel_depth *= 2;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (png_ptr->num_trans)
+         {
+            max_pixel_depth *= 4;
+            max_pixel_depth /= 3;
+         }
+      }
+   }
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & (PNG_FILLER))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         max_pixel_depth = 32;
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY)
+      {
+         if (max_pixel_depth <= 8)
+            max_pixel_depth = 16;
+         else
+            max_pixel_depth = 32;
+      }
+      else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+      {
+         if (max_pixel_depth <= 32)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 64;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED)
+   if (png_ptr->transformations & PNG_GRAY_TO_RGB)
+   {
+      if (
+#if defined(PNG_READ_EXPAND_SUPPORTED)
+        (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) ||
+#endif
+#if defined(PNG_READ_FILLER_SUPPORTED)
+        (png_ptr->transformations & (PNG_FILLER)) ||
+#endif
+        png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         if (max_pixel_depth <= 16)
+            max_pixel_depth = 32;
+         else
+            max_pixel_depth = 64;
+      }
+      else
+      {
+         if (max_pixel_depth <= 8)
+           {
+             if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+               max_pixel_depth = 32;
+             else
+               max_pixel_depth = 24;
+           }
+         else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            max_pixel_depth = 64;
+         else
+            max_pixel_depth = 48;
+      }
+   }
+#endif
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \
+defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   if(png_ptr->transformations & PNG_USER_TRANSFORM)
+     {
+       int user_pixel_depth=png_ptr->user_transform_depth*
+         png_ptr->user_transform_channels;
+       if(user_pixel_depth > max_pixel_depth)
+         max_pixel_depth=user_pixel_depth;
+     }
+#endif
+
+   /* align the width on the next larger 8 pixels.  Mainly used
+      for interlacing */
+   row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7));
+   /* calculate the maximum bytes needed, adding a byte and a pixel
+      for safety's sake */
+   row_bytes = PNG_ROWBYTES(max_pixel_depth,row_bytes) +
+      1 + ((max_pixel_depth + 7) >> 3);
+#ifdef PNG_MAX_MALLOC_64K
+   if (row_bytes > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+   png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64);
+   png_ptr->row_buf = png_ptr->big_row_buf+32;
+#if defined(PNG_DEBUG) && defined(PNG_USE_PNGGCCRD) && defined(PNG_1_0_X)
+   png_ptr->row_buf_size = row_bytes;
+#endif
+
+#ifdef PNG_MAX_MALLOC_64K
+   if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L)
+      png_error(png_ptr, "This image requires a row greater than 64KB");
+#endif
+   if ((png_uint_32)png_ptr->rowbytes > (png_uint_32)(PNG_SIZE_MAX - 1))
+      png_error(png_ptr, "Row has too many bytes to allocate in memory.");
+   png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)(
+      png_ptr->rowbytes + 1));
+
+   png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1);
+
+   png_debug1(3, "width = %lu,\n", png_ptr->width);
+   png_debug1(3, "height = %lu,\n", png_ptr->height);
+   png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth);
+   png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows);
+   png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes);
+   png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes);
+
+   png_ptr->flags |= PNG_FLAG_ROW_INIT;
+}
+#endif /* PNG_READ_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngset.c b/distrib/libpng-1.2.19/pngset.c
new file mode 100644
index 0000000..8d1cbfc
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngset.c
@@ -0,0 +1,1284 @@
+
+/* pngset.c - storage of image information into info struct
+ *
+ * Last changed in libpng 1.2.17 May 15, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * The functions here are used during reads to store data from the file
+ * into the info struct, and during writes to store application data
+ * into the info struct for writing into the file.  This abstracts the
+ * info struct and allows us to change the structure in the future.
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+
+#if defined(PNG_bKGD_SUPPORTED)
+void PNGAPI
+png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background)
+{
+   png_debug1(1, "in %s storage function\n", "bKGD");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16));
+   info_ptr->valid |= PNG_INFO_bKGD;
+}
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_cHRM(png_structp png_ptr, png_infop info_ptr,
+   double white_x, double white_y, double red_x, double red_y,
+   double green_x, double green_y, double blue_x, double blue_y)
+{
+   png_debug1(1, "in %s storage function\n", "cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (white_x < 0.0 || white_y < 0.0 ||
+         red_x < 0.0 ||   red_y < 0.0 ||
+       green_x < 0.0 || green_y < 0.0 ||
+        blue_x < 0.0 ||  blue_y < 0.0)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set negative chromaticity value");
+      return;
+   }
+   if (white_x > 21474.83 || white_y > 21474.83 ||
+         red_x > 21474.83 ||   red_y > 21474.83 ||
+       green_x > 21474.83 || green_y > 21474.83 ||
+        blue_x > 21474.83 ||  blue_y > 21474.83)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set chromaticity value exceeding 21474.83");
+      return;
+   }
+
+   info_ptr->x_white = (float)white_x;
+   info_ptr->y_white = (float)white_y;
+   info_ptr->x_red   = (float)red_x;
+   info_ptr->y_red   = (float)red_y;
+   info_ptr->x_green = (float)green_x;
+   info_ptr->y_green = (float)green_y;
+   info_ptr->x_blue  = (float)blue_x;
+   info_ptr->y_blue  = (float)blue_y;
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5);
+   info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5);
+   info_ptr->int_x_red   = (png_fixed_point)(  red_x*100000.+0.5);
+   info_ptr->int_y_red   = (png_fixed_point)(  red_y*100000.+0.5);
+   info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5);
+   info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5);
+   info_ptr->int_x_blue  = (png_fixed_point)( blue_x*100000.+0.5);
+   info_ptr->int_y_blue  = (png_fixed_point)( blue_y*100000.+0.5);
+#endif
+   info_ptr->valid |= PNG_INFO_cHRM;
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr,
+   png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x,
+   png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y,
+   png_fixed_point blue_x, png_fixed_point blue_y)
+{
+   png_debug1(1, "in %s storage function\n", "cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (white_x < 0 || white_y < 0 ||
+         red_x < 0 ||   red_y < 0 ||
+       green_x < 0 || green_y < 0 ||
+        blue_x < 0 ||  blue_y < 0)
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set negative chromaticity value");
+      return;
+   }
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   if (white_x > (double) PNG_UINT_31_MAX ||
+       white_y > (double) PNG_UINT_31_MAX ||
+         red_x > (double) PNG_UINT_31_MAX ||
+         red_y > (double) PNG_UINT_31_MAX ||
+       green_x > (double) PNG_UINT_31_MAX ||
+       green_y > (double) PNG_UINT_31_MAX ||
+        blue_x > (double) PNG_UINT_31_MAX ||
+        blue_y > (double) PNG_UINT_31_MAX)
+#else
+   if (white_x > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+       white_y > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+         red_x > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+         red_y > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+       green_x > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+       green_y > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+        blue_x > (png_fixed_point) PNG_UINT_31_MAX/100000L ||
+        blue_y > (png_fixed_point) PNG_UINT_31_MAX/100000L)
+#endif
+   {
+      png_warning(png_ptr,
+        "Ignoring attempt to set chromaticity value exceeding 21474.83");
+      return;
+   }
+   info_ptr->int_x_white = white_x;
+   info_ptr->int_y_white = white_y;
+   info_ptr->int_x_red   = red_x;
+   info_ptr->int_y_red   = red_y;
+   info_ptr->int_x_green = green_x;
+   info_ptr->int_y_green = green_y;
+   info_ptr->int_x_blue  = blue_x;
+   info_ptr->int_y_blue  = blue_y;
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   info_ptr->x_white = (float)(white_x/100000.);
+   info_ptr->y_white = (float)(white_y/100000.);
+   info_ptr->x_red   = (float)(  red_x/100000.);
+   info_ptr->y_red   = (float)(  red_y/100000.);
+   info_ptr->x_green = (float)(green_x/100000.);
+   info_ptr->y_green = (float)(green_y/100000.);
+   info_ptr->x_blue  = (float)( blue_x/100000.);
+   info_ptr->y_blue  = (float)( blue_y/100000.);
+#endif
+   info_ptr->valid |= PNG_INFO_cHRM;
+}
+#endif
+#endif
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma)
+{
+   double gamma;
+   png_debug1(1, "in %s storage function\n", "gAMA");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* Check for overflow */
+   if (file_gamma > 21474.83)
+   {
+      png_warning(png_ptr, "Limiting gamma to 21474.83");
+      gamma=21474.83;
+   }
+   else
+      gamma=file_gamma;
+   info_ptr->gamma = (float)gamma;
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   info_ptr->int_gamma = (int)(gamma*100000.+.5);
+#endif
+   info_ptr->valid |= PNG_INFO_gAMA;
+   if(gamma == 0.0)
+      png_warning(png_ptr, "Setting gamma=0");
+}
+#endif
+void PNGAPI
+png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point
+   int_gamma)
+{
+   png_fixed_point gamma;
+
+   png_debug1(1, "in %s storage function\n", "gAMA");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX)
+   {
+     png_warning(png_ptr, "Limiting gamma to 21474.83");
+     gamma=PNG_UINT_31_MAX;
+   }
+   else
+   {
+     if (int_gamma < 0)
+     {
+       png_warning(png_ptr, "Setting negative gamma to zero");
+       gamma=0;
+     }
+     else
+       gamma=int_gamma;
+   }
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   info_ptr->gamma = (float)(gamma/100000.);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   info_ptr->int_gamma = gamma;
+#endif
+   info_ptr->valid |= PNG_INFO_gAMA;
+   if(gamma == 0)
+      png_warning(png_ptr, "Setting gamma=0");
+}
+#endif
+
+#if defined(PNG_hIST_SUPPORTED)
+void PNGAPI
+png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function\n", "hIST");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+   if (info_ptr->num_palette <= 0 || info_ptr->num_palette
+       > PNG_MAX_PALETTE_LENGTH)
+   {
+       png_warning(png_ptr,
+          "Invalid palette size, hIST allocation skipped.");
+       return;
+   }
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0);
+#endif
+   /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in version
+      1.2.1 */
+   png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr,
+      (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof (png_uint_16)));
+   if (png_ptr->hist == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for hIST chunk data.");
+       return;
+     }
+
+   for (i = 0; i < info_ptr->num_palette; i++)
+       png_ptr->hist[i] = hist[i];
+   info_ptr->hist = png_ptr->hist;
+   info_ptr->valid |= PNG_INFO_hIST;
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_HIST;
+#else
+   png_ptr->flags |= PNG_FLAG_FREE_HIST;
+#endif
+}
+#endif
+
+void PNGAPI
+png_set_IHDR(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 width, png_uint_32 height, int bit_depth,
+   int color_type, int interlace_type, int compression_type,
+   int filter_type)
+{
+   png_debug1(1, "in %s storage function\n", "IHDR");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   /* check for width and height valid values */
+   if (width == 0 || height == 0)
+      png_error(png_ptr, "Image width or height is zero in IHDR");
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   if (width > png_ptr->user_width_max || height > png_ptr->user_height_max)
+      png_error(png_ptr, "image size exceeds user limits in IHDR");
+#else
+   if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX)
+      png_error(png_ptr, "image size exceeds user limits in IHDR");
+#endif
+   if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX)
+      png_error(png_ptr, "Invalid image size in IHDR");
+   if ( width > (PNG_UINT_32_MAX
+                 >> 3)      /* 8-byte RGBA pixels */
+                 - 64       /* bigrowbuf hack */
+                 - 1        /* filter byte */
+                 - 7*8      /* rounding of width to multiple of 8 pixels */
+                 - 8)       /* extra max_pixel_depth pad */
+      png_warning(png_ptr, "Width is too large for libpng to process pixels");
+
+   /* check other values */
+   if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 &&
+      bit_depth != 8 && bit_depth != 16)
+      png_error(png_ptr, "Invalid bit depth in IHDR");
+
+   if (color_type < 0 || color_type == 1 ||
+      color_type == 5 || color_type > 6)
+      png_error(png_ptr, "Invalid color type in IHDR");
+
+   if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) ||
+       ((color_type == PNG_COLOR_TYPE_RGB ||
+         color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
+         color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8))
+      png_error(png_ptr, "Invalid color type/bit depth combination in IHDR");
+
+   if (interlace_type >= PNG_INTERLACE_LAST)
+      png_error(png_ptr, "Unknown interlace method in IHDR");
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+      png_error(png_ptr, "Unknown compression method in IHDR");
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   /* Accept filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not read a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted)
+      png_warning(png_ptr,"MNG features are not allowed in a PNG datastream");
+   if(filter_type != PNG_FILTER_TYPE_BASE)
+   {
+     if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+        (filter_type == PNG_INTRAPIXEL_DIFFERENCING) &&
+        ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
+        (color_type == PNG_COLOR_TYPE_RGB ||
+         color_type == PNG_COLOR_TYPE_RGB_ALPHA)))
+        png_error(png_ptr, "Unknown filter method in IHDR");
+     if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)
+        png_warning(png_ptr, "Invalid filter method in IHDR");
+   }
+#else
+   if(filter_type != PNG_FILTER_TYPE_BASE)
+      png_error(png_ptr, "Unknown filter method in IHDR");
+#endif
+
+   info_ptr->width = width;
+   info_ptr->height = height;
+   info_ptr->bit_depth = (png_byte)bit_depth;
+   info_ptr->color_type =(png_byte) color_type;
+   info_ptr->compression_type = (png_byte)compression_type;
+   info_ptr->filter_type = (png_byte)filter_type;
+   info_ptr->interlace_type = (png_byte)interlace_type;
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      info_ptr->channels = 1;
+   else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
+      info_ptr->channels = 3;
+   else
+      info_ptr->channels = 1;
+   if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
+      info_ptr->channels++;
+   info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth);
+
+   /* check for potential overflow */
+   if (width > (PNG_UINT_32_MAX
+                 >> 3)      /* 8-byte RGBA pixels */
+                 - 64       /* bigrowbuf hack */
+                 - 1        /* filter byte */
+                 - 7*8      /* rounding of width to multiple of 8 pixels */
+                 - 8)       /* extra max_pixel_depth pad */
+      info_ptr->rowbytes = (png_size_t)0;
+   else
+      info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width);
+}
+
+#if defined(PNG_oFFs_SUPPORTED)
+void PNGAPI
+png_set_oFFs(png_structp png_ptr, png_infop info_ptr,
+   png_int_32 offset_x, png_int_32 offset_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function\n", "oFFs");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_offset = offset_x;
+   info_ptr->y_offset = offset_y;
+   info_ptr->offset_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_oFFs;
+}
+#endif
+
+#if defined(PNG_pCAL_SUPPORTED)
+void PNGAPI
+png_set_pCAL(png_structp png_ptr, png_infop info_ptr,
+   png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams,
+   png_charp units, png_charpp params)
+{
+   png_uint_32 length;
+   int i;
+
+   png_debug1(1, "in %s storage function\n", "pCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   length = png_strlen(purpose) + 1;
+   png_debug1(3, "allocating purpose for info (%lu bytes)\n", length);
+   info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->pcal_purpose == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for pCAL purpose.");
+       return;
+     }
+   png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length);
+
+   png_debug(3, "storing X0, X1, type, and nparams in info\n");
+   info_ptr->pcal_X0 = X0;
+   info_ptr->pcal_X1 = X1;
+   info_ptr->pcal_type = (png_byte)type;
+   info_ptr->pcal_nparams = (png_byte)nparams;
+
+   length = png_strlen(units) + 1;
+   png_debug1(3, "allocating units for info (%lu bytes)\n", length);
+   info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->pcal_units == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for pCAL units.");
+       return;
+     }
+   png_memcpy(info_ptr->pcal_units, units, (png_size_t)length);
+
+   info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr,
+      (png_uint_32)((nparams + 1) * png_sizeof(png_charp)));
+   if (info_ptr->pcal_params == NULL)
+     {
+       png_warning(png_ptr, "Insufficient memory for pCAL params.");
+       return;
+     }
+
+   info_ptr->pcal_params[nparams] = NULL;
+
+   for (i = 0; i < nparams; i++)
+   {
+      length = png_strlen(params[i]) + 1;
+      png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length);
+      info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length);
+      if (info_ptr->pcal_params[i] == NULL)
+        {
+          png_warning(png_ptr, "Insufficient memory for pCAL parameter.");
+          return;
+        }
+      png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length);
+   }
+
+   info_ptr->valid |= PNG_INFO_pCAL;
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_PCAL;
+#endif
+}
+#endif
+
+#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL(png_structp png_ptr, png_infop info_ptr,
+             int unit, double width, double height)
+{
+   png_debug1(1, "in %s storage function\n", "sCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->scal_unit = (png_byte)unit;
+   info_ptr->scal_pixel_width = width;
+   info_ptr->scal_pixel_height = height;
+
+   info_ptr->valid |= PNG_INFO_sCAL;
+}
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void PNGAPI
+png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr,
+             int unit, png_charp swidth, png_charp sheight)
+{
+   png_uint_32 length;
+
+   png_debug1(1, "in %s storage function\n", "sCAL");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->scal_unit = (png_byte)unit;
+
+   length = png_strlen(swidth) + 1;
+   png_debug1(3, "allocating unit for info (%d bytes)\n", length);
+   info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->scal_s_width == NULL)
+   {
+      png_warning(png_ptr,
+       "Memory allocation failed while processing sCAL.");
+   }
+   png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length);
+
+   length = png_strlen(sheight) + 1;
+   png_debug1(3, "allocating unit for info (%d bytes)\n", length);
+   info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length);
+   if (info_ptr->scal_s_height == NULL)
+   {
+      png_free (png_ptr, info_ptr->scal_s_width);
+      png_warning(png_ptr,
+       "Memory allocation failed while processing sCAL.");
+   }
+   png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length);
+
+   info_ptr->valid |= PNG_INFO_sCAL;
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_SCAL;
+#endif
+}
+#endif
+#endif
+#endif
+
+#if defined(PNG_pHYs_SUPPORTED)
+void PNGAPI
+png_set_pHYs(png_structp png_ptr, png_infop info_ptr,
+   png_uint_32 res_x, png_uint_32 res_y, int unit_type)
+{
+   png_debug1(1, "in %s storage function\n", "pHYs");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->x_pixels_per_unit = res_x;
+   info_ptr->y_pixels_per_unit = res_y;
+   info_ptr->phys_unit_type = (png_byte)unit_type;
+   info_ptr->valid |= PNG_INFO_pHYs;
+}
+#endif
+
+void PNGAPI
+png_set_PLTE(png_structp png_ptr, png_infop info_ptr,
+   png_colorp palette, int num_palette)
+{
+
+   png_debug1(1, "in %s storage function\n", "PLTE");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH)
+     {
+       if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         png_error(png_ptr, "Invalid palette length");
+       else
+       {
+         png_warning(png_ptr, "Invalid palette length");
+         return;
+       }
+     }
+
+   /*
+    * It may not actually be necessary to set png_ptr->palette here;
+    * we do it for backward compatibility with the way the png_handle_tRNS
+    * function used to do the allocation.
+    */
+#ifdef PNG_FREE_ME_SUPPORTED
+   png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0);
+#endif
+
+   /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead
+      of num_palette entries,
+      in case of an invalid PNG file that has too-large sample values. */
+   png_ptr->palette = (png_colorp)png_malloc(png_ptr,
+      PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color));
+   png_memset(png_ptr->palette, 0, PNG_MAX_PALETTE_LENGTH *
+      png_sizeof(png_color));
+   png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color));
+   info_ptr->palette = png_ptr->palette;
+   info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette;
+
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_PLTE;
+#else
+   png_ptr->flags |= PNG_FLAG_FREE_PLTE;
+#endif
+
+   info_ptr->valid |= PNG_INFO_PLTE;
+}
+
+#if defined(PNG_sBIT_SUPPORTED)
+void PNGAPI
+png_set_sBIT(png_structp png_ptr, png_infop info_ptr,
+   png_color_8p sig_bit)
+{
+   png_debug1(1, "in %s storage function\n", "sBIT");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8));
+   info_ptr->valid |= PNG_INFO_sBIT;
+}
+#endif
+
+#if defined(PNG_sRGB_SUPPORTED)
+void PNGAPI
+png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent)
+{
+   png_debug1(1, "in %s storage function\n", "sRGB");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   info_ptr->srgb_intent = (png_byte)intent;
+   info_ptr->valid |= PNG_INFO_sRGB;
+}
+
+void PNGAPI
+png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr,
+   int intent)
+{
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float file_gamma;
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_fixed_point int_file_gamma;
+#endif
+#endif
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y;
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x,
+      int_green_y, int_blue_x, int_blue_y;
+#endif
+#endif
+   png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_set_sRGB(png_ptr, info_ptr, intent);
+
+#if defined(PNG_gAMA_SUPPORTED)
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   file_gamma = (float).45455;
+   png_set_gAMA(png_ptr, info_ptr, file_gamma);
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   int_file_gamma = 45455L;
+   png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma);
+#endif
+#endif
+
+#if defined(PNG_cHRM_SUPPORTED)
+#ifdef PNG_FIXED_POINT_SUPPORTED
+   int_white_x = 31270L;
+   int_white_y = 32900L;
+   int_red_x   = 64000L;
+   int_red_y   = 33000L;
+   int_green_x = 30000L;
+   int_green_y = 60000L;
+   int_blue_x  = 15000L;
+   int_blue_y  =  6000L;
+
+   png_set_cHRM_fixed(png_ptr, info_ptr,
+      int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y,
+      int_blue_x, int_blue_y);
+#endif
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+   white_x = (float).3127;
+   white_y = (float).3290;
+   red_x   = (float).64;
+   red_y   = (float).33;
+   green_x = (float).30;
+   green_y = (float).60;
+   blue_x  = (float).15;
+   blue_y  = (float).06;
+
+   png_set_cHRM(png_ptr, info_ptr,
+      white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y);
+#endif
+#endif
+}
+#endif
+
+
+#if defined(PNG_iCCP_SUPPORTED)
+void PNGAPI
+png_set_iCCP(png_structp png_ptr, png_infop info_ptr,
+             png_charp name, int compression_type,
+             png_charp profile, png_uint_32 proflen)
+{
+   png_charp new_iccp_name;
+   png_charp new_iccp_profile;
+
+   png_debug1(1, "in %s storage function\n", "iCCP");
+   if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL)
+      return;
+
+   new_iccp_name = (png_charp)png_malloc_warn(png_ptr, png_strlen(name)+1);
+   if (new_iccp_name == NULL)
+   {
+      png_warning(png_ptr, "Insufficient memory to process iCCP chunk.");
+      return;
+   }
+   png_strncpy(new_iccp_name, name, png_sizeof(new_iccp_name));
+   new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen);
+   if (new_iccp_profile == NULL)
+   {
+      png_free (png_ptr, new_iccp_name);
+      png_warning(png_ptr, "Insufficient memory to process iCCP profile.");
+      return;
+   }
+   png_memcpy(new_iccp_profile, profile, (png_size_t)proflen);
+
+   png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0);
+
+   info_ptr->iccp_proflen = proflen;
+   info_ptr->iccp_name = new_iccp_name;
+   info_ptr->iccp_profile = new_iccp_profile;
+   /* Compression is always zero but is here so the API and info structure
+    * does not have to change if we introduce multiple compression types */
+   info_ptr->iccp_compression = (png_byte)compression_type;
+#ifdef PNG_FREE_ME_SUPPORTED
+   info_ptr->free_me |= PNG_FREE_ICCP;
+#endif
+   info_ptr->valid |= PNG_INFO_iCCP;
+}
+#endif
+
+#if defined(PNG_TEXT_SUPPORTED)
+void PNGAPI
+png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr,
+   int num_text)
+{
+   int ret;
+   ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text);
+   if (ret)
+     png_error(png_ptr, "Insufficient memory to store text");
+}
+
+int /* PRIVATE */
+png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr,
+   int num_text)
+{
+   int i;
+
+   png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ?
+      "text" : (png_const_charp)png_ptr->chunk_name));
+
+   if (png_ptr == NULL || info_ptr == NULL || num_text == 0)
+      return(0);
+
+   /* Make sure we have enough space in the "text" array in info_struct
+    * to hold all of the incoming text_ptr objects.
+    */
+   if (info_ptr->num_text + num_text > info_ptr->max_text)
+   {
+      if (info_ptr->text != NULL)
+      {
+         png_textp old_text;
+         int old_max;
+
+         old_max = info_ptr->max_text;
+         info_ptr->max_text = info_ptr->num_text + num_text + 8;
+         old_text = info_ptr->text;
+         info_ptr->text = (png_textp)png_malloc_warn(png_ptr,
+            (png_uint_32)(info_ptr->max_text * png_sizeof (png_text)));
+         if (info_ptr->text == NULL)
+           {
+             png_free(png_ptr, old_text);
+             return(1);
+           }
+         png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max *
+            png_sizeof(png_text)));
+         png_free(png_ptr, old_text);
+      }
+      else
+      {
+         info_ptr->max_text = num_text + 8;
+         info_ptr->num_text = 0;
+         info_ptr->text = (png_textp)png_malloc_warn(png_ptr,
+            (png_uint_32)(info_ptr->max_text * png_sizeof (png_text)));
+         if (info_ptr->text == NULL)
+           return(1);
+#ifdef PNG_FREE_ME_SUPPORTED
+         info_ptr->free_me |= PNG_FREE_TEXT;
+#endif
+      }
+      png_debug1(3, "allocated %d entries for info_ptr->text\n",
+         info_ptr->max_text);
+   }
+   for (i = 0; i < num_text; i++)
+   {
+      png_size_t text_length,key_len;
+      png_size_t lang_len,lang_key_len;
+      png_textp textp = &(info_ptr->text[info_ptr->num_text]);
+
+      if (text_ptr[i].key == NULL)
+          continue;
+
+      key_len = png_strlen(text_ptr[i].key);
+
+      if(text_ptr[i].compression <= 0)
+      {
+        lang_len = 0;
+        lang_key_len = 0;
+      }
+      else
+#ifdef PNG_iTXt_SUPPORTED
+      {
+        /* set iTXt data */
+        if (text_ptr[i].lang != NULL)
+          lang_len = png_strlen(text_ptr[i].lang);
+        else
+          lang_len = 0;
+        if (text_ptr[i].lang_key != NULL)
+          lang_key_len = png_strlen(text_ptr[i].lang_key);
+        else
+          lang_key_len = 0;
+      }
+#else
+      {
+        png_warning(png_ptr, "iTXt chunk not supported.");
+        continue;
+      }
+#endif
+
+      if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0')
+      {
+         text_length = 0;
+#ifdef PNG_iTXt_SUPPORTED
+         if(text_ptr[i].compression > 0)
+            textp->compression = PNG_ITXT_COMPRESSION_NONE;
+         else
+#endif
+            textp->compression = PNG_TEXT_COMPRESSION_NONE;
+      }
+      else
+      {
+         text_length = png_strlen(text_ptr[i].text);
+         textp->compression = text_ptr[i].compression;
+      }
+
+      textp->key = (png_charp)png_malloc_warn(png_ptr,
+         (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4));
+      if (textp->key == NULL)
+        return(1);
+      png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n",
+         (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4),
+         (int)textp->key);
+
+      png_memcpy(textp->key, text_ptr[i].key,
+         (png_size_t)(key_len));
+      *(textp->key+key_len) = '\0';
+#ifdef PNG_iTXt_SUPPORTED
+      if (text_ptr[i].compression > 0)
+      {
+         textp->lang=textp->key + key_len + 1;
+         png_memcpy(textp->lang, text_ptr[i].lang, lang_len);
+         *(textp->lang+lang_len) = '\0';
+         textp->lang_key=textp->lang + lang_len + 1;
+         png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len);
+         *(textp->lang_key+lang_key_len) = '\0';
+         textp->text=textp->lang_key + lang_key_len + 1;
+      }
+      else
+#endif
+      {
+#ifdef PNG_iTXt_SUPPORTED
+         textp->lang=NULL;
+         textp->lang_key=NULL;
+#endif
+         textp->text=textp->key + key_len + 1;
+      }
+      if(text_length)
+         png_memcpy(textp->text, text_ptr[i].text,
+            (png_size_t)(text_length));
+      *(textp->text+text_length) = '\0';
+
+#ifdef PNG_iTXt_SUPPORTED
+      if(textp->compression > 0)
+      {
+         textp->text_length = 0;
+         textp->itxt_length = text_length;
+      }
+      else
+#endif
+      {
+         textp->text_length = text_length;
+#ifdef PNG_iTXt_SUPPORTED
+         textp->itxt_length = 0;
+#endif
+      }
+      info_ptr->num_text++;
+      png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text);
+   }
+   return(0);
+}
+#endif
+
+#if defined(PNG_tIME_SUPPORTED)
+void PNGAPI
+png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time)
+{
+   png_debug1(1, "in %s storage function\n", "tIME");
+   if (png_ptr == NULL || info_ptr == NULL ||
+       (png_ptr->mode & PNG_WROTE_tIME))
+      return;
+
+   png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time));
+   info_ptr->valid |= PNG_INFO_tIME;
+}
+#endif
+
+#if defined(PNG_tRNS_SUPPORTED)
+void PNGAPI
+png_set_tRNS(png_structp png_ptr, png_infop info_ptr,
+   png_bytep trans, int num_trans, png_color_16p trans_values)
+{
+   png_debug1(1, "in %s storage function\n", "tRNS");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if (trans != NULL)
+   {
+       /*
+        * It may not actually be necessary to set png_ptr->trans here;
+        * we do it for backward compatibility with the way the png_handle_tRNS
+        * function used to do the allocation.
+        */
+#ifdef PNG_FREE_ME_SUPPORTED
+       png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
+#endif
+       /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */
+       png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr,
+           (png_uint_32)PNG_MAX_PALETTE_LENGTH);
+       if (num_trans <= PNG_MAX_PALETTE_LENGTH)
+         png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans);
+#ifdef PNG_FREE_ME_SUPPORTED
+       info_ptr->free_me |= PNG_FREE_TRNS;
+#else
+       png_ptr->flags |= PNG_FLAG_FREE_TRNS;
+#endif
+   }
+
+   if (trans_values != NULL)
+   {
+      png_memcpy(&(info_ptr->trans_values), trans_values,
+         png_sizeof(png_color_16));
+      if (num_trans == 0)
+        num_trans = 1;
+   }
+   info_ptr->num_trans = (png_uint_16)num_trans;
+   info_ptr->valid |= PNG_INFO_tRNS;
+}
+#endif
+
+#if defined(PNG_sPLT_SUPPORTED)
+void PNGAPI
+png_set_sPLT(png_structp png_ptr,
+             png_infop info_ptr, png_sPLT_tp entries, int nentries)
+{
+    png_sPLT_tp np;
+    int i;
+
+    if (png_ptr == NULL || info_ptr == NULL)
+       return;
+
+    np = (png_sPLT_tp)png_malloc_warn(png_ptr,
+        (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t));
+    if (np == NULL)
+    {
+      png_warning(png_ptr, "No memory for sPLT palettes.");
+      return;
+    }
+
+    png_memcpy(np, info_ptr->splt_palettes,
+           info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t));
+    png_free(png_ptr, info_ptr->splt_palettes);
+    info_ptr->splt_palettes=NULL;
+
+    for (i = 0; i < nentries; i++)
+    {
+        png_sPLT_tp to = np + info_ptr->splt_palettes_num + i;
+        png_sPLT_tp from = entries + i;
+
+        to->name = (png_charp)png_malloc_warn(png_ptr,
+          png_strlen(from->name) + 1);
+        if (to->name == NULL)
+        {
+           png_warning(png_ptr,
+             "Out of memory while processing sPLT chunk");
+        }
+        /* TODO: use png_malloc_warn */
+        png_strncpy(to->name, from->name, png_strlen(from->name));
+        to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr,
+            from->nentries * png_sizeof(png_sPLT_entry));
+        /* TODO: use png_malloc_warn */
+        png_memcpy(to->entries, from->entries,
+            from->nentries * png_sizeof(png_sPLT_entry));
+        if (to->entries == NULL)
+        {
+           png_warning(png_ptr,
+             "Out of memory while processing sPLT chunk");
+           png_free(png_ptr,to->name);
+           to->name = NULL;
+        }
+        to->nentries = from->nentries;
+        to->depth = from->depth;
+    }
+
+    info_ptr->splt_palettes = np;
+    info_ptr->splt_palettes_num += nentries;
+    info_ptr->valid |= PNG_INFO_sPLT;
+#ifdef PNG_FREE_ME_SUPPORTED
+    info_ptr->free_me |= PNG_FREE_SPLT;
+#endif
+}
+#endif /* PNG_sPLT_SUPPORTED */
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+void PNGAPI
+png_set_unknown_chunks(png_structp png_ptr,
+   png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)
+{
+    png_unknown_chunkp np;
+    int i;
+
+    if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0)
+        return;
+
+    np = (png_unknown_chunkp)png_malloc_warn(png_ptr,
+        (info_ptr->unknown_chunks_num + num_unknowns) *
+        png_sizeof(png_unknown_chunk));
+    if (np == NULL)
+    {
+       png_warning(png_ptr,
+          "Out of memory while processing unknown chunk.");
+       return;
+    }
+
+    png_memcpy(np, info_ptr->unknown_chunks,
+           info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk));
+    png_free(png_ptr, info_ptr->unknown_chunks);
+    info_ptr->unknown_chunks=NULL;
+
+    for (i = 0; i < num_unknowns; i++)
+    {
+        png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i;
+        png_unknown_chunkp from = unknowns + i;
+
+        png_strncpy((png_charp)to->name, (png_charp)from->name, 5);
+        to->data = (png_bytep)png_malloc_warn(png_ptr, from->size);
+        if (to->data == NULL)
+        {
+           png_warning(png_ptr,
+              "Out of memory while processing unknown chunk.");
+        }
+        else
+        {
+           png_memcpy(to->data, from->data, from->size);
+           to->size = from->size;
+
+           /* note our location in the read or write sequence */
+           to->location = (png_byte)(png_ptr->mode & 0xff);
+        }
+    }
+
+    info_ptr->unknown_chunks = np;
+    info_ptr->unknown_chunks_num += num_unknowns;
+#ifdef PNG_FREE_ME_SUPPORTED
+    info_ptr->free_me |= PNG_FREE_UNKN;
+#endif
+}
+void PNGAPI
+png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr,
+   int chunk, int location)
+{
+   if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk <
+         (int)info_ptr->unknown_chunks_num)
+      info_ptr->unknown_chunks[chunk].location = (png_byte)location;
+}
+#endif
+
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \
+    defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED)
+void PNGAPI
+png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted)
+{
+   /* This function is deprecated in favor of png_permit_mng_features()
+      and will be removed from libpng-1.3.0 */
+   png_debug(1, "in png_permit_empty_plte, DEPRECATED.\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->mng_features_permitted = (png_byte)
+     ((png_ptr->mng_features_permitted & (~(PNG_FLAG_MNG_EMPTY_PLTE))) |
+     ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE)));
+}
+#endif
+#endif
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+png_uint_32 PNGAPI
+png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features)
+{
+   png_debug(1, "in png_permit_mng_features\n");
+   if (png_ptr == NULL)
+      return (png_uint_32)0;
+   png_ptr->mng_features_permitted =
+     (png_byte)(mng_features & PNG_ALL_MNG_FEATURES);
+   return (png_uint_32)png_ptr->mng_features_permitted;
+}
+#endif
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+void PNGAPI
+png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep
+   chunk_list, int num_chunks)
+{
+    png_bytep new_list, p;
+    int i, old_num_chunks;
+    if (png_ptr == NULL)
+       return;
+    if (num_chunks == 0)
+    {
+      if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE)
+        png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS;
+      else
+        png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS;
+
+      if(keep == PNG_HANDLE_CHUNK_ALWAYS)
+        png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS;
+      else
+        png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS;
+      return;
+    }
+    if (chunk_list == NULL)
+      return;
+    old_num_chunks=png_ptr->num_chunk_list;
+    new_list=(png_bytep)png_malloc(png_ptr,
+       (png_uint_32)(5*(num_chunks+old_num_chunks)));
+    if(png_ptr->chunk_list != NULL)
+    {
+       png_memcpy(new_list, png_ptr->chunk_list,
+          (png_size_t)(5*old_num_chunks));
+       png_free(png_ptr, png_ptr->chunk_list);
+       png_ptr->chunk_list=NULL;
+    }
+    png_memcpy(new_list+5*old_num_chunks, chunk_list,
+       (png_size_t)(5*num_chunks));
+    for (p=new_list+5*old_num_chunks+4, i=0; i<num_chunks; i++, p+=5)
+       *p=(png_byte)keep;
+    png_ptr->num_chunk_list=old_num_chunks+num_chunks;
+    png_ptr->chunk_list=new_list;
+#ifdef PNG_FREE_ME_SUPPORTED
+    png_ptr->free_me |= PNG_FREE_LIST;
+#endif
+}
+#endif
+
+#if defined(PNG_READ_USER_CHUNKS_SUPPORTED)
+void PNGAPI
+png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr,
+   png_user_chunk_ptr read_user_chunk_fn)
+{
+   png_debug(1, "in png_set_read_user_chunk_fn\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->read_user_chunk_fn = read_user_chunk_fn;
+   png_ptr->user_chunk_ptr = user_chunk_ptr;
+}
+#endif
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+void PNGAPI
+png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers)
+{
+   png_debug1(1, "in %s storage function\n", "rows");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers))
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0);
+   info_ptr->row_pointers = row_pointers;
+   if(row_pointers)
+      info_ptr->valid |= PNG_INFO_IDAT;
+}
+#endif
+
+#ifdef PNG_WRITE_SUPPORTED
+void PNGAPI
+png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size)
+{
+    if (png_ptr == NULL)
+       return;
+    if(png_ptr->zbuf)
+       png_free(png_ptr, png_ptr->zbuf);
+    png_ptr->zbuf_size = (png_size_t)size;
+    png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size);
+    png_ptr->zstream.next_out = png_ptr->zbuf;
+    png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+}
+#endif
+
+void PNGAPI
+png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask)
+{
+   if (png_ptr && info_ptr)
+      info_ptr->valid &= ~(mask);
+}
+
+
+#ifndef PNG_1_0_X
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+/* this function was added to libpng 1.2.0 and should always exist by default */
+void PNGAPI
+png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags)
+{
+#ifdef PNG_MMX_CODE_SUPPORTED
+    png_uint_32 settable_asm_flags;
+    png_uint_32 settable_mmx_flags;
+#endif
+    if (png_ptr == NULL)
+       return;
+#ifdef PNG_MMX_CODE_SUPPORTED
+
+    settable_mmx_flags =
+#ifdef PNG_HAVE_MMX_COMBINE_ROW
+                         PNG_ASM_FLAG_MMX_READ_COMBINE_ROW  |
+#endif
+#ifdef PNG_HAVE_MMX_READ_INTERLACE
+                         PNG_ASM_FLAG_MMX_READ_INTERLACE    |
+#endif
+#ifdef PNG_HAVE_MMX_READ_FILTER_ROW
+                         PNG_ASM_FLAG_MMX_READ_FILTER_SUB   |
+                         PNG_ASM_FLAG_MMX_READ_FILTER_UP    |
+                         PNG_ASM_FLAG_MMX_READ_FILTER_AVG   |
+                         PNG_ASM_FLAG_MMX_READ_FILTER_PAETH |
+#endif
+                         0;
+
+    /* could be some non-MMX ones in the future, but not currently: */
+    settable_asm_flags = settable_mmx_flags;
+
+    if (!(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED) ||
+        !(png_ptr->asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU))
+    {
+        /* clear all MMX flags if MMX isn't supported */
+        settable_asm_flags &= ~settable_mmx_flags;
+        png_ptr->asm_flags &= ~settable_mmx_flags;
+    }
+
+    /* we're replacing the settable bits with those passed in by the user,
+     * so first zero them out of the master copy, then bitwise-OR in the
+     * allowed subset that was requested */
+
+    png_ptr->asm_flags &= ~settable_asm_flags;               /* zero them */
+    png_ptr->asm_flags |= (asm_flags & settable_asm_flags);  /* set them */
+#endif /* ?PNG_MMX_CODE_SUPPORTED */
+}
+
+/* this function was added to libpng 1.2.0 */
+void PNGAPI
+png_set_mmx_thresholds (png_structp png_ptr,
+                        png_byte mmx_bitdepth_threshold,
+                        png_uint_32 mmx_rowbytes_threshold)
+{
+    if (png_ptr == NULL)
+       return;
+#ifdef PNG_MMX_CODE_SUPPORTED
+    png_ptr->mmx_bitdepth_threshold = mmx_bitdepth_threshold;
+    png_ptr->mmx_rowbytes_threshold = mmx_rowbytes_threshold;
+#endif /* ?PNG_MMX_CODE_SUPPORTED */
+}
+#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */
+
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+/* this function was added to libpng 1.2.6 */
+void PNGAPI
+png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max,
+    png_uint_32 user_height_max)
+{
+    /* Images with dimensions larger than these limits will be
+     * rejected by png_set_IHDR().  To accept any PNG datastream
+     * regardless of dimensions, set both limits to 0x7ffffffL.
+     */
+    if(png_ptr == NULL) return;
+    png_ptr->user_width_max = user_width_max;
+    png_ptr->user_height_max = user_height_max;
+}
+#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */
+
+#endif /* ?PNG_1_0_X */
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngtrans.c b/distrib/libpng-1.2.19/pngtrans.c
new file mode 100644
index 0000000..1640095
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngtrans.c
@@ -0,0 +1,662 @@
+
+/* pngtrans.c - transforms the data in a row (used by both readers and writers)
+ *
+ * Last changed in libpng 1.2.17 May 15, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED)
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* turn on BGR-to-RGB mapping */
+void PNGAPI
+png_set_bgr(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_bgr\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_BGR;
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* turn on 16 bit byte swapping */
+void PNGAPI
+png_set_swap(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_swap\n");
+   if(png_ptr == NULL) return;
+   if (png_ptr->bit_depth == 16)
+      png_ptr->transformations |= PNG_SWAP_BYTES;
+}
+#endif
+
+#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED)
+/* turn on pixel packing */
+void PNGAPI
+png_set_packing(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_packing\n");
+   if(png_ptr == NULL) return;
+   if (png_ptr->bit_depth < 8)
+   {
+      png_ptr->transformations |= PNG_PACK;
+      png_ptr->usr_bit_depth = 8;
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+/* turn on packed pixel swapping */
+void PNGAPI
+png_set_packswap(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_packswap\n");
+   if(png_ptr == NULL) return;
+   if (png_ptr->bit_depth < 8)
+      png_ptr->transformations |= PNG_PACKSWAP;
+}
+#endif
+
+#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED)
+void PNGAPI
+png_set_shift(png_structp png_ptr, png_color_8p true_bits)
+{
+   png_debug(1, "in png_set_shift\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_SHIFT;
+   png_ptr->shift = *true_bits;
+}
+#endif
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED) || \
+    defined(PNG_WRITE_INTERLACING_SUPPORTED)
+int PNGAPI
+png_set_interlace_handling(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_interlace handling\n");
+   if (png_ptr && png_ptr->interlaced)
+   {
+      png_ptr->transformations |= PNG_INTERLACE;
+      return (7);
+   }
+
+   return (1);
+}
+#endif
+
+#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED)
+/* Add a filler byte on read, or remove a filler or alpha byte on write.
+ * The filler type has changed in v0.95 to allow future 2-byte fillers
+ * for 48-bit input data, as well as to avoid problems with some compilers
+ * that don't like bytes as parameters.
+ */
+void PNGAPI
+png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_filler\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_FILLER;
+   png_ptr->filler = (png_byte)filler;
+   if (filler_loc == PNG_FILLER_AFTER)
+      png_ptr->flags |= PNG_FLAG_FILLER_AFTER;
+   else
+      png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER;
+
+   /* This should probably go in the "do_read_filler" routine.
+    * I attempted to do that in libpng-1.0.1a but that caused problems
+    * so I restored it in libpng-1.0.2a
+   */
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_RGB)
+   {
+      png_ptr->usr_channels = 4;
+   }
+
+   /* Also I added this in libpng-1.0.2a (what happens when we expand
+    * a less-than-8-bit grayscale to GA? */
+
+   if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8)
+   {
+      png_ptr->usr_channels = 2;
+   }
+}
+
+#if !defined(PNG_1_0_X)
+/* Added to libpng-1.2.7 */
+void PNGAPI
+png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc)
+{
+   png_debug(1, "in png_set_add_alpha\n");
+   if(png_ptr == NULL) return;
+   png_set_filler(png_ptr, filler, filler_loc);
+   png_ptr->transformations |= PNG_ADD_ALPHA;
+}
+#endif
+
+#endif
+
+#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_swap_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_swap_alpha\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_SWAP_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \
+    defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void PNGAPI
+png_set_invert_alpha(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_invert_alpha\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_INVERT_ALPHA;
+}
+#endif
+
+#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED)
+void PNGAPI
+png_set_invert_mono(png_structp png_ptr)
+{
+   png_debug(1, "in png_set_invert_mono\n");
+   if(png_ptr == NULL) return;
+   png_ptr->transformations |= PNG_INVERT_MONO;
+}
+
+/* invert monochrome grayscale data */
+void /* PRIVATE */
+png_do_invert(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_invert\n");
+  /* This test removed from libpng version 1.0.13 and 1.2.0:
+   *   if (row_info->bit_depth == 1 &&
+   */
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row == NULL || row_info == NULL)
+     return;
+#endif
+   if (row_info->color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i++)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp++;
+      }
+   }
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+      row_info->bit_depth == 8)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i+=2)
+      {
+         *rp = (png_byte)(~(*rp));
+         rp+=2;
+      }
+   }
+   else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+      row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop = row_info->rowbytes;
+
+      for (i = 0; i < istop; i+=4)
+      {
+         *rp = (png_byte)(~(*rp));
+         *(rp+1) = (png_byte)(~(*(rp+1)));
+         rp+=4;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED)
+/* swaps byte order on 16 bit depth images */
+void /* PRIVATE */
+png_do_swap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_swap\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->bit_depth == 16)
+   {
+      png_bytep rp = row;
+      png_uint_32 i;
+      png_uint_32 istop= row_info->width * row_info->channels;
+
+      for (i = 0; i < istop; i++, rp += 2)
+      {
+         png_byte t = *rp;
+         *rp = *(rp + 1);
+         *(rp + 1) = t;
+      }
+   }
+}
+#endif
+
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+static PNG_CONST png_byte onebppswaptable[256] = {
+   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0,
+   0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
+   0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8,
+   0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
+   0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4,
+   0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
+   0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC,
+   0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
+   0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2,
+   0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
+   0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA,
+   0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
+   0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6,
+   0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
+   0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE,
+   0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
+   0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1,
+   0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
+   0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9,
+   0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
+   0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5,
+   0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
+   0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED,
+   0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
+   0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3,
+   0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
+   0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB,
+   0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
+   0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7,
+   0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
+   0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF,
+   0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
+};
+
+static PNG_CONST png_byte twobppswaptable[256] = {
+   0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0,
+   0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0,
+   0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4,
+   0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4,
+   0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8,
+   0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8,
+   0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC,
+   0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC,
+   0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1,
+   0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1,
+   0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5,
+   0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5,
+   0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9,
+   0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9,
+   0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD,
+   0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD,
+   0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2,
+   0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2,
+   0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6,
+   0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6,
+   0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA,
+   0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA,
+   0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE,
+   0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE,
+   0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3,
+   0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3,
+   0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7,
+   0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7,
+   0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB,
+   0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB,
+   0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF,
+   0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF
+};
+
+static PNG_CONST png_byte fourbppswaptable[256] = {
+   0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
+   0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0,
+   0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71,
+   0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1,
+   0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72,
+   0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2,
+   0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73,
+   0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3,
+   0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74,
+   0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4,
+   0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75,
+   0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5,
+   0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76,
+   0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6,
+   0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77,
+   0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7,
+   0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
+   0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8,
+   0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79,
+   0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9,
+   0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A,
+   0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA,
+   0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B,
+   0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB,
+   0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C,
+   0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC,
+   0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D,
+   0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD,
+   0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E,
+   0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE,
+   0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F,
+   0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF
+};
+
+/* swaps pixel packing order within bytes */
+void /* PRIVATE */
+png_do_packswap(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_packswap\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       row_info->bit_depth < 8)
+   {
+      png_bytep rp, end, table;
+
+      end = row + row_info->rowbytes;
+
+      if (row_info->bit_depth == 1)
+         table = (png_bytep)onebppswaptable;
+      else if (row_info->bit_depth == 2)
+         table = (png_bytep)twobppswaptable;
+      else if (row_info->bit_depth == 4)
+         table = (png_bytep)fourbppswaptable;
+      else
+         return;
+
+      for (rp = row; rp < end; rp++)
+         *rp = table[*rp];
+   }
+}
+#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED) || \
+    defined(PNG_READ_STRIP_ALPHA_SUPPORTED)
+/* remove filler or alpha byte(s) */
+void /* PRIVATE */
+png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags)
+{
+   png_debug(1, "in png_do_strip_filler\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      png_bytep sp=row;
+      png_bytep dp=row;
+      png_uint_32 row_width=row_info->width;
+      png_uint_32 i;
+
+      if ((row_info->color_type == PNG_COLOR_TYPE_RGB ||
+         (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA &&
+         (flags & PNG_FLAG_STRIP_ALPHA))) &&
+         row_info->channels == 4)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from RGBX or RGBA to RGB */
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               dp+=3; sp+=4;
+               for (i = 1; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp++;
+               }
+            }
+            /* This converts from XRGB or ARGB to RGB */
+            else
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 24;
+            row_info->rowbytes = row_width * 3;
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */
+               sp += 8; dp += 6;
+               for (i = 1; i < row_width; i++)
+               {
+                  /* This could be (although png_memcpy is probably slower):
+                  png_memcpy(dp, sp, 6);
+                  sp += 8;
+                  dp += 6;
+                  */
+
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp += 2;
+               }
+            }
+            else
+            {
+               /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */
+               for (i = 0; i < row_width; i++)
+               {
+                  /* This could be (although png_memcpy is probably slower):
+                  png_memcpy(dp, sp, 6);
+                  sp += 8;
+                  dp += 6;
+                  */
+
+                  sp+=2;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 48;
+            row_info->rowbytes = row_width * 6;
+         }
+         row_info->channels = 3;
+      }
+      else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY ||
+         (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
+         (flags & PNG_FLAG_STRIP_ALPHA))) &&
+          row_info->channels == 2)
+      {
+         if (row_info->bit_depth == 8)
+         {
+            /* This converts from GX or GA to G */
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  sp++;
+               }
+            }
+            /* This converts from XG or AG to G */
+            else
+            {
+               for (i = 0; i < row_width; i++)
+               {
+                  sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 8;
+            row_info->rowbytes = row_width;
+         }
+         else /* if (row_info->bit_depth == 16) */
+         {
+            if (flags & PNG_FLAG_FILLER_AFTER)
+            {
+               /* This converts from GGXX or GGAA to GG */
+               sp += 4; dp += 2;
+               for (i = 1; i < row_width; i++)
+               {
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+                  sp += 2;
+               }
+            }
+            else
+            {
+               /* This converts from XXGG or AAGG to GG */
+               for (i = 0; i < row_width; i++)
+               {
+                  sp += 2;
+                  *dp++ = *sp++;
+                  *dp++ = *sp++;
+               }
+            }
+            row_info->pixel_depth = 16;
+            row_info->rowbytes = row_width * 2;
+         }
+         row_info->channels = 1;
+      }
+      if (flags & PNG_FLAG_STRIP_ALPHA)
+        row_info->color_type &= ~PNG_COLOR_MASK_ALPHA;
+   }
+}
+#endif
+
+#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED)
+/* swaps red and blue bytes within a pixel */
+void /* PRIVATE */
+png_do_bgr(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_bgr\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 3)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 4)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 2);
+               *(rp + 2) = save;
+            }
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 6)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+         {
+            png_bytep rp;
+            png_uint_32 i;
+
+            for (i = 0, rp = row; i < row_width; i++, rp += 8)
+            {
+               png_byte save = *rp;
+               *rp = *(rp + 4);
+               *(rp + 4) = save;
+               save = *(rp + 1);
+               *(rp + 1) = *(rp + 5);
+               *(rp + 5) = save;
+            }
+         }
+      }
+   }
+}
+#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */
+
+#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \
+    defined(PNG_LEGACY_SUPPORTED)
+void PNGAPI
+png_set_user_transform_info(png_structp png_ptr, png_voidp
+   user_transform_ptr, int user_transform_depth, int user_transform_channels)
+{
+   png_debug(1, "in png_set_user_transform_info\n");
+   if(png_ptr == NULL) return;
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   png_ptr->user_transform_ptr = user_transform_ptr;
+   png_ptr->user_transform_depth = (png_byte)user_transform_depth;
+   png_ptr->user_transform_channels = (png_byte)user_transform_channels;
+#else
+   if(user_transform_ptr || user_transform_depth || user_transform_channels)
+      png_warning(png_ptr,
+        "This version of libpng does not support user transform info");
+#endif
+}
+#endif
+
+/* This function returns a pointer to the user_transform_ptr associated with
+ * the user transform functions.  The application should free any memory
+ * associated with this pointer before png_write_destroy and png_read_destroy
+ * are called.
+ */
+png_voidp PNGAPI
+png_get_user_transform_ptr(png_structp png_ptr)
+{
+#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED)
+   if (png_ptr == NULL) return (NULL);
+   return ((png_voidp)png_ptr->user_transform_ptr);
+#else
+   return (NULL);
+#endif
+}
+#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngvcrd.c b/distrib/libpng-1.2.19/pngvcrd.c
new file mode 100644
index 0000000..34d42c9
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngvcrd.c
@@ -0,0 +1,3922 @@
+
+/* pngvcrd.c - mixed C/assembler version of utilities to read a PNG file
+ *
+ * For Intel x86 CPU and Microsoft Visual C++ compiler
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * Copyright (c) 1998, Intel Corporation
+ *
+ * Contributed by Nirav Chhatrapati, Intel Corporation, 1998
+ * Interface to libpng contributed by Gilles Vollant, 1999
+ *
+ *
+ * In png_do_read_interlace() in libpng versions 1.0.3a through 1.0.4d,
+ * a sign error in the post-MMX cleanup code for each pixel_depth resulted
+ * in bad pixels at the beginning of some rows of some images, and also
+ * (due to out-of-range memory reads and writes) caused heap corruption
+ * when compiled with MSVC 6.0.  The error was fixed in version 1.0.4e.
+ *
+ * [png_read_filter_row_mmx_avg() bpp == 2 bugfix, GRR 20000916]
+ *
+ * [runtime MMX configuration, GRR 20010102]
+ *
+ * [Copy 6 bytes per pixel, not 4, and use stride of 6, not 4, in the
+ *  second loop of interlace processing of 48-bit pixels, GR-P 20070717]
+ *
+ * [move instances of uAll union into local, except for two constant
+ * instances, GR-P 20070805]
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+
+#if defined(PNG_MMX_CODE_SUPPORTED) && defined(PNG_USE_PNGVCRD)
+
+
+static int mmx_supported=2;
+
+int PNGAPI
+png_mmx_support(void)
+{
+  int mmx_supported_local = 0;
+  _asm {
+    push ebx          //CPUID will trash these
+    push ecx
+    push edx
+
+    pushfd            //Save Eflag to stack
+    pop eax           //Get Eflag from stack into eax
+    mov ecx, eax      //Make another copy of Eflag in ecx
+    xor eax, 0x200000 //Toggle ID bit in Eflag [i.e. bit(21)]
+    push eax          //Save modified Eflag back to stack
+
+    popfd             //Restored modified value back to Eflag reg
+    pushfd            //Save Eflag to stack
+    pop eax           //Get Eflag from stack
+    push ecx          // save original Eflag to stack
+    popfd             // restore original Eflag
+    xor eax, ecx      //Compare the new Eflag with the original Eflag
+    jz NOT_SUPPORTED  //If the same, CPUID instruction is not supported,
+                      //skip following instructions and jump to
+                      //NOT_SUPPORTED label
+
+    xor eax, eax      //Set eax to zero
+
+    _asm _emit 0x0f   //CPUID instruction  (two bytes opcode)
+    _asm _emit 0xa2
+
+    cmp eax, 1        //make sure eax return non-zero value
+    jl NOT_SUPPORTED  //If eax is zero, mmx not supported
+
+    xor eax, eax      //set eax to zero
+    inc eax           //Now increment eax to 1.  This instruction is
+                      //faster than the instruction "mov eax, 1"
+
+    _asm _emit 0x0f   //CPUID instruction
+    _asm _emit 0xa2
+
+    and edx, 0x00800000  //mask out all bits but mmx bit(24)
+    cmp edx, 0        // 0 = mmx not supported
+    jz  NOT_SUPPORTED // non-zero = Yes, mmx IS supported
+
+    mov  mmx_supported_local, 1  //set return value to 1
+
+NOT_SUPPORTED:
+    mov  eax, mmx_supported_local  //move return value to eax
+    pop edx          //CPUID trashed these
+    pop ecx
+    pop ebx
+  }
+
+  //mmx_supported_local=0; // test code for force don't support MMX
+  //printf("MMX : %u (1=MMX supported)\n",mmx_supported_local);
+
+  mmx_supported = mmx_supported_local;
+  return mmx_supported_local;
+}
+
+/* Combines the row recently read in with the previous row.
+   This routine takes care of alpha and transparency if requested.
+   This routine also handles the two methods of progressive display
+   of interlaced images, depending on the mask value.
+   The mask value describes which pixels are to be combined with
+   the row.  The pattern always repeats every 8 pixels, so just 8
+   bits are needed.  A one indicates the pixel is to be combined; a
+   zero indicates the pixel is to be skipped.  This is in addition
+   to any alpha or transparency value associated with the pixel.  If
+   you want all pixels to be combined, pass 0xff (255) in mask.  */
+
+/* Use this routine for x86 platform - uses faster MMX routine if machine
+   supports MMX */
+
+void /* PRIVATE */
+png_combine_row(png_structp png_ptr, png_bytep row, int mask)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1,"in png_combine_row_asm\n");
+
+   if (mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+   if (mask == 0xff)
+   {
+      png_memcpy(row, png_ptr->row_buf + 1,
+       (png_size_t)PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+       png_ptr->width));
+   }
+   /* GRR:  add "else if (mask == 0)" case?
+    *       or does png_combine_row() not even get called in that case? */
+   else
+   {
+      switch (png_ptr->row_info.pixel_depth)
+      {
+         case 24:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+
+            __int64 mask2=0x0101010202020404,  //24bpp
+                    mask1=0x0408080810101020,
+                    mask0=0x2020404040808080;
+
+            srcptr = png_ptr->row_buf + 1;
+            dstptr = row;
+
+            unmask = ~mask;
+            len     = (png_ptr->width)&~7;
+            diff = (png_ptr->width)&7;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+                  movq       mm2,mask2
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+                  pand       mm2,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+                  pcmpeqb    mm2,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+                  cmp        ecx,0
+                  jz         mainloop24end
+
+mainloop24:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  movq       mm7,[ebx]
+                  pandn      mm6,mm7
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+
+                  movq       mm5,[esi+8]
+                  pand       mm5,mm1
+                  movq       mm7,mm1
+                  movq       mm6,[ebx+8]
+                  pandn      mm7,mm6
+                  por        mm5,mm7
+                  movq       [ebx+8],mm5
+
+                  movq       mm6,[esi+16]
+                  pand       mm6,mm2
+                  movq       mm4,mm2
+                  movq       mm7,[ebx+16]
+                  pandn      mm4,mm7
+                  por        mm6,mm4
+                  movq       [ebx+16],mm6
+
+                  add        esi,24            //inc by 24 bytes processed
+                  add        ebx,24
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop24
+
+mainloop24end:
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end24
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+secondloop24:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip24            //if CF = 0
+                  mov        ax,[esi]
+                  mov        [ebx],ax
+                  xor        eax,eax
+                  mov        al,[esi+2]
+                  mov        [ebx+2],al
+skip24:
+                  add        esi,3
+                  add        ebx,3
+
+                  dec        ecx
+                  jnz        secondloop24
+
+end24:
+                  emms
+               }
+            }
+            else /* mmx not supported - use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 24 bpp
+
+         case 32:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+
+            __int64 mask3=0x0101010102020202,  //32bpp
+                    mask2=0x0404040408080808,
+                    mask1=0x1010101020202020,
+                    mask0=0x4040404080808080;
+
+            srcptr = png_ptr->row_buf + 1;
+            dstptr = row;
+
+            unmask = ~mask;
+            len     = (png_ptr->width)&~7;
+            diff = (png_ptr->width)&7;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+                  movq       mm2,mask2
+                  movq       mm3,mask3
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+                  pand       mm2,mm7
+                  pand       mm3,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+                  pcmpeqb    mm2,mm6
+                  pcmpeqb    mm3,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+
+                  cmp        ecx,0             //lcr
+                  jz         mainloop32end
+
+mainloop32:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  movq       mm7,[ebx]
+                  pandn      mm6,mm7
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+                  movq       mm5,[esi+8]
+                  pand       mm5,mm1
+                  movq       mm7,mm1
+                  movq       mm6,[ebx+8]
+                  pandn      mm7,mm6
+                  por        mm5,mm7
+                  movq       [ebx+8],mm5
+
+                  movq       mm6,[esi+16]
+                  pand       mm6,mm2
+                  movq       mm4,mm2
+                  movq       mm7,[ebx+16]
+                  pandn      mm4,mm7
+                  por        mm6,mm4
+                  movq       [ebx+16],mm6
+
+                  movq       mm7,[esi+24]
+                  pand       mm7,mm3
+                  movq       mm5,mm3
+                  movq       mm4,[ebx+24]
+                  pandn      mm5,mm4
+                  por        mm7,mm5
+                  movq       [ebx+24],mm7
+
+                  add        esi,32            //inc by 32 bytes processed
+                  add        ebx,32
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop32
+
+mainloop32end:
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end32
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+secondloop32:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip32            //if CF = 0
+                  mov        eax,[esi]
+                  mov        [ebx],eax
+skip32:
+                  add        esi,4
+                  add        ebx,4
+
+                  dec        ecx
+                  jnz        secondloop32
+
+end32:
+                  emms
+               }
+            }
+            else /* mmx _not supported - Use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 32 bpp
+
+         case 8:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int m;
+            int diff, unmask;
+
+            __int64 mask0=0x0102040810204080;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+               m = 0x80;
+               unmask = ~mask;
+               len  = png_ptr->width &~7;  //reduce to multiple of 8
+               diff = png_ptr->width & 7;  //amount lost
+
+               _asm
+               {
+                  movd       mm7, unmask   //load bit pattern
+                  psubb      mm6,mm6       //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7       //fill register with 8 masks
+
+                  movq       mm0,mask0
+
+                  pand       mm0,mm7       //nonzero if keep byte
+                  pcmpeqb    mm0,mm6       //zeros->1s, v versa
+
+                  mov        ecx,len       //load length of line (pixels)
+                  mov        esi,srcptr    //load source
+                  mov        ebx,dstptr    //load dest
+                  cmp        ecx,0         //lcr
+                  je         mainloop8end
+
+mainloop8:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  pandn      mm6,[ebx]
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+                  add        esi,8         //inc by 8 bytes processed
+                  add        ebx,8
+                  sub        ecx,8         //dec by 8 pixels processed
+
+                  ja         mainloop8
+mainloop8end:
+
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end8
+
+                  mov        edx,mask
+                  sal        edx,24        //make low byte the high byte
+
+secondloop8:
+                  sal        edx,1         //move high bit to CF
+                  jnc        skip8         //if CF = 0
+                  mov        al,[esi]
+                  mov        [ebx],al
+skip8:
+                  inc        esi
+                  inc        ebx
+
+                  dec        ecx
+                  jnz        secondloop8
+end8:
+                  emms
+               }
+            }
+            else /* mmx not supported - use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 8 bpp
+
+         case 1:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_inc, s_start, s_end;
+            int m;
+            int shift;
+            png_uint_32 i;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+                s_start = 0;
+                s_end = 7;
+                s_inc = 1;
+            }
+            else
+#endif
+            {
+                s_start = 7;
+                s_end = 0;
+                s_inc = -1;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  int value;
+
+                  value = (*sp >> shift) & 0x1;
+                  *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+            else
+#endif
+            {
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0x3;
+                  *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int s_start, s_end, s_inc;
+            int m;
+            int shift;
+            png_uint_32 i;
+            int value;
+
+            sp = png_ptr->row_buf + 1;
+            dp = row;
+            m = 0x80;
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (png_ptr->transformations & PNG_PACKSWAP)
+            {
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+            else
+#endif
+            {
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            shift = s_start;
+
+            for (i = 0; i < png_ptr->width; i++)
+            {
+               if (m & mask)
+               {
+                  value = (*sp >> shift) & 0xf;
+                  *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff);
+                  *dp |= (png_byte)(value << shift);
+               }
+
+               if (shift == s_end)
+               {
+                  shift = s_start;
+                  sp++;
+                  dp++;
+               }
+               else
+                  shift += s_inc;
+               if (m == 1)
+                  m = 0x80;
+               else
+                  m >>= 1;
+            }
+            break;
+         }
+
+         case 16:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+            __int64 mask1=0x0101020204040808,
+                    mask0=0x1010202040408080;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+
+               unmask = ~mask;
+               len     = (png_ptr->width)&~7;
+               diff = (png_ptr->width)&7;
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+                  cmp        ecx,0             //lcr
+                  jz         mainloop16end
+
+mainloop16:
+                  movq       mm4,[esi]
+                  pand       mm4,mm0
+                  movq       mm6,mm0
+                  movq       mm7,[ebx]
+                  pandn      mm6,mm7
+                  por        mm4,mm6
+                  movq       [ebx],mm4
+
+                  movq       mm5,[esi+8]
+                  pand       mm5,mm1
+                  movq       mm7,mm1
+                  movq       mm6,[ebx+8]
+                  pandn      mm7,mm6
+                  por        mm5,mm7
+                  movq       [ebx+8],mm5
+
+                  add        esi,16            //inc by 16 bytes processed
+                  add        ebx,16
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop16
+
+mainloop16end:
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end16
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+secondloop16:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip16            //if CF = 0
+                  mov        ax,[esi]
+                  mov        [ebx],ax
+skip16:
+                  add        esi,2
+                  add        ebx,2
+
+                  dec        ecx
+                  jnz        secondloop16
+end16:
+                  emms
+               }
+            }
+            else /* mmx not supported - use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 16 bpp
+
+         case 48:
+         {
+            png_bytep srcptr;
+            png_bytep dstptr;
+            png_uint_32 len;
+            int unmask, diff;
+
+            __int64 mask5=0x0101010101010202,
+                    mask4=0x0202020204040404,
+                    mask3=0x0404080808080808,
+                    mask2=0x1010101010102020,
+                    mask1=0x2020202040404040,
+                    mask0=0x4040808080808080;
+
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               srcptr = png_ptr->row_buf + 1;
+               dstptr = row;
+
+               unmask = ~mask;
+               len     = (png_ptr->width)&~7;
+               diff = (png_ptr->width)&7;
+               _asm
+               {
+                  movd       mm7, unmask       //load bit pattern
+                  psubb      mm6,mm6           //zero mm6
+                  punpcklbw  mm7,mm7
+                  punpcklwd  mm7,mm7
+                  punpckldq  mm7,mm7           //fill register with 8 masks
+
+                  movq       mm0,mask0
+                  movq       mm1,mask1
+                  movq       mm2,mask2
+                  movq       mm3,mask3
+                  movq       mm4,mask4
+                  movq       mm5,mask5
+
+                  pand       mm0,mm7
+                  pand       mm1,mm7
+                  pand       mm2,mm7
+                  pand       mm3,mm7
+                  pand       mm4,mm7
+                  pand       mm5,mm7
+
+                  pcmpeqb    mm0,mm6
+                  pcmpeqb    mm1,mm6
+                  pcmpeqb    mm2,mm6
+                  pcmpeqb    mm3,mm6
+                  pcmpeqb    mm4,mm6
+                  pcmpeqb    mm5,mm6
+
+                  mov        ecx,len           //load length of line
+                  mov        esi,srcptr        //load source
+                  mov        ebx,dstptr        //load dest
+
+                  cmp        ecx,0
+                  jz         mainloop48end
+
+mainloop48:
+                  movq       mm7,[esi]
+                  pand       mm7,mm0
+                  movq       mm6,mm0
+                  pandn      mm6,[ebx]
+                  por        mm7,mm6
+                  movq       [ebx],mm7
+
+                  movq       mm6,[esi+8]
+                  pand       mm6,mm1
+                  movq       mm7,mm1
+                  pandn      mm7,[ebx+8]
+                  por        mm6,mm7
+                  movq       [ebx+8],mm6
+
+                  movq       mm6,[esi+16]
+                  pand       mm6,mm2
+                  movq       mm7,mm2
+                  pandn      mm7,[ebx+16]
+                  por        mm6,mm7
+                  movq       [ebx+16],mm6
+
+                  movq       mm7,[esi+24]
+                  pand       mm7,mm3
+                  movq       mm6,mm3
+                  pandn      mm6,[ebx+24]
+                  por        mm7,mm6
+                  movq       [ebx+24],mm7
+
+                  movq       mm6,[esi+32]
+                  pand       mm6,mm4
+                  movq       mm7,mm4
+                  pandn      mm7,[ebx+32]
+                  por        mm6,mm7
+                  movq       [ebx+32],mm6
+
+                  movq       mm7,[esi+40]
+                  pand       mm7,mm5
+                  movq       mm6,mm5
+                  pandn      mm6,[ebx+40]
+                  por        mm7,mm6
+                  movq       [ebx+40],mm7
+
+                  add        esi,48            //inc by 32 bytes processed
+                  add        ebx,48
+                  sub        ecx,8             //dec by 8 pixels processed
+
+                  ja         mainloop48
+mainloop48end:
+
+                  mov        ecx,diff
+                  cmp        ecx,0
+                  jz         end48
+
+                  mov        edx,mask
+                  sal        edx,24            //make low byte the high byte
+
+secondloop48:
+                  sal        edx,1             //move high bit to CF
+                  jnc        skip48            //if CF = 0
+                  mov        eax,[esi]
+                  mov        [ebx],eax
+                  mov        ax,[esi+4]       // These 2 lines added 20070717
+                  mov        [ebx+4],ax       // Glenn R-P
+skip48:
+                  add        esi,6            // Changed 4 to 6 on these 2
+                  add        ebx,6            // lines.  Glenn R-P 20070717
+
+                  dec        ecx
+                  jnz        secondloop48
+
+end48:
+                  emms
+               }
+            }
+            else /* mmx _not supported - Use modified C routine */
+            {
+               register unsigned int incr1, initial_val, final_val;
+               png_size_t pixel_bytes;
+               png_uint_32 i;
+               register int disp = png_pass_inc[png_ptr->pass];
+               int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+
+               pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+               srcptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+                  pixel_bytes;
+               dstptr = row + offset_table[png_ptr->pass]*pixel_bytes;
+               initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+               final_val = png_ptr->width*pixel_bytes;
+               incr1 = (disp)*pixel_bytes;
+               for (i = initial_val; i < final_val; i += incr1)
+               {
+                  png_memcpy(dstptr, srcptr, pixel_bytes);
+                  srcptr += incr1;
+                  dstptr += incr1;
+               }
+            } /* end of else */
+
+            break;
+         }       // end 48 bpp
+
+         default:
+         {
+            png_bytep sptr;
+            png_bytep dp;
+            png_size_t pixel_bytes;
+            int offset_table[7] = {0, 4, 0, 2, 0, 1, 0};
+            unsigned int i;
+            register int disp = png_pass_inc[png_ptr->pass];  // get the offset
+            register unsigned int incr1, initial_val, final_val;
+
+            pixel_bytes = (png_ptr->row_info.pixel_depth >> 3);
+            sptr = png_ptr->row_buf + 1 + offset_table[png_ptr->pass]*
+               pixel_bytes;
+            dp = row + offset_table[png_ptr->pass]*pixel_bytes;
+            initial_val = offset_table[png_ptr->pass]*pixel_bytes;
+            final_val = png_ptr->width*pixel_bytes;
+            incr1 = (disp)*pixel_bytes;
+            for (i = initial_val; i < final_val; i += incr1)
+            {
+               png_memcpy(dp, sptr, pixel_bytes);
+               sptr += incr1;
+               dp += incr1;
+            }
+            break;
+         }
+      } /* end switch (png_ptr->row_info.pixel_depth) */
+   } /* end if (non-trivial mask) */
+
+} /* end png_combine_row() */
+
+
+#if defined(PNG_READ_INTERLACING_SUPPORTED)
+
+void /* PRIVATE */
+png_do_read_interlace(png_structp png_ptr)
+{
+   png_row_infop row_info = &(png_ptr->row_info);
+   png_bytep row = png_ptr->row_buf + 1;
+   int pass = png_ptr->pass;
+   png_uint_32 transformations = png_ptr->transformations;
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1,"in png_do_read_interlace\n");
+
+   if (mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+   if (row != NULL && row_info != NULL)
+   {
+      png_uint_32 final_width;
+
+      final_width = row_info->width * png_pass_inc[pass];
+
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_byte v;
+            png_uint_32 i;
+            int j;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 3);
+            dp = row + (png_size_t)((final_width - 1) >> 3);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (int)((row_info->width + 7) & 7);
+               dshift = (int)((final_width + 7) & 7);
+               s_start = 7;
+               s_end = 0;
+               s_inc = -1;
+            }
+            else
+#endif
+            {
+               sshift = 7 - (int)((row_info->width + 7) & 7);
+               dshift = 7 - (int)((final_width + 7) & 7);
+               s_start = 0;
+               s_end = 7;
+               s_inc = 1;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               v = (png_byte)((*sp >> sshift) & 0x1);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 2:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 2);
+            dp = row + (png_size_t)((final_width - 1) >> 2);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 3) & 3) << 1);
+               dshift = (png_size_t)(((final_width + 3) & 3) << 1);
+               s_start = 6;
+               s_end = 0;
+               s_inc = -2;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((3 - ((row_info->width + 3) & 3)) << 1);
+               dshift = (png_size_t)((3 - ((final_width + 3) & 3)) << 1);
+               s_start = 0;
+               s_end = 6;
+               s_inc = 2;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0x3);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         case 4:
+         {
+            png_bytep sp, dp;
+            int sshift, dshift;
+            int s_start, s_end, s_inc;
+            png_uint_32 i;
+
+            sp = row + (png_size_t)((row_info->width - 1) >> 1);
+            dp = row + (png_size_t)((final_width - 1) >> 1);
+#if defined(PNG_READ_PACKSWAP_SUPPORTED)
+            if (transformations & PNG_PACKSWAP)
+            {
+               sshift = (png_size_t)(((row_info->width + 1) & 1) << 2);
+               dshift = (png_size_t)(((final_width + 1) & 1) << 2);
+               s_start = 4;
+               s_end = 0;
+               s_inc = -4;
+            }
+            else
+#endif
+            {
+               sshift = (png_size_t)((1 - ((row_info->width + 1) & 1)) << 2);
+               dshift = (png_size_t)((1 - ((final_width + 1) & 1)) << 2);
+               s_start = 0;
+               s_end = 4;
+               s_inc = 4;
+            }
+
+            for (i = row_info->width; i; i--)
+            {
+               png_byte v;
+               int j;
+
+               v = (png_byte)((*sp >> sshift) & 0xf);
+               for (j = 0; j < png_pass_inc[pass]; j++)
+               {
+                  *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff);
+                  *dp |= (png_byte)(v << dshift);
+                  if (dshift == s_end)
+                  {
+                     dshift = s_start;
+                     dp--;
+                  }
+                  else
+                     dshift += s_inc;
+               }
+               if (sshift == s_end)
+               {
+                  sshift = s_start;
+                  sp--;
+               }
+               else
+                  sshift += s_inc;
+            }
+            break;
+         }
+
+         default:         // This is the place where the routine is modified
+         {
+            __int64 const4 = 0x0000000000FFFFFF;
+            // __int64 const5 = 0x000000FFFFFF0000;  // unused...
+            __int64 const6 = 0x00000000000000FF;
+            png_bytep sptr, dp;
+            png_uint_32 i;
+            png_size_t pixel_bytes;
+            int width = row_info->width;
+
+            pixel_bytes = (row_info->pixel_depth >> 3);
+
+            sptr = row + (width - 1) * pixel_bytes;
+            dp = row + (final_width - 1) * pixel_bytes;
+            // New code by Nirav Chhatrapati - Intel Corporation
+            // sign fix by GRR
+            // NOTE:  there is NO MMX code for 48-bit and 64-bit images
+
+            // use MMX routine if machine supports it
+#if !defined(PNG_1_0_X)
+            if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)
+                /* && mmx_supported */ )
+#else
+            if (mmx_supported)
+#endif
+            {
+               if (pixel_bytes == 3)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) - 8;
+                     if (width_mmx < 0)
+                         width_mmx = 0;
+                     width -= width_mmx;        // 8 or 9 pix, 24 or 27 bytes
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 3
+                           sub edi, 9
+loop_pass4:
+                           movq mm0, [esi]     ; X X v2 v1 v0 v5 v4 v3
+                           movq mm7, mm0       ; X X v2 v1 v0 v5 v4 v3
+                           movq mm6, mm0       ; X X v2 v1 v0 v5 v4 v3
+                           psllq mm0, 24       ; v1 v0 v5 v4 v3 0 0 0
+                           pand mm7, const4    ; 0 0 0 0 0 v5 v4 v3
+                           psrlq mm6, 24       ; 0 0 0 X X v2 v1 v0
+                           por mm0, mm7        ; v1 v0 v5 v4 v3 v5 v4 v3
+                           movq mm5, mm6       ; 0 0 0 X X v2 v1 v0
+                           psllq mm6, 8        ; 0 0 X X v2 v1 v0 0
+                           movq [edi], mm0     ; move quad to memory
+                           psrlq mm5, 16       ; 0 0 0 0 0 X X v2
+                           pand mm5, const6    ; 0 0 0 0 0 0 0 v2
+                           por mm6, mm5        ; 0 0 X X v2 v1 v0 v2
+                           movd [edi+8], mm6   ; move double to memory
+                           sub esi, 6
+                           sub edi, 12
+                           sub ecx, 2
+                           jnz loop_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx*3;
+                     dp -= width_mmx*6;
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+
+                        png_memcpy(v, sptr, 3);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           png_memcpy(dp, v, 3);
+                           dp -= 3;
+                        }
+                        sptr -= 3;
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     _asm
+                     {
+                        mov esi, sptr
+                        mov edi, dp
+                        mov ecx, width
+                        sub edi, 9   // (png_pass_inc[pass] - 1)*pixel_bytes
+loop_pass2:
+                        movd mm0, [esi]     ; X X X X X v2 v1 v0
+                        pand mm0, const4    ; 0 0 0 0 0 v2 v1 v0
+                        movq mm1, mm0       ; 0 0 0 0 0 v2 v1 v0
+                        psllq mm0, 16       ; 0 0 0 v2 v1 v0 0 0
+                        movq mm2, mm0       ; 0 0 0 v2 v1 v0 0 0
+                        psllq mm0, 24       ; v2 v1 v0 0 0 0 0 0
+                        psrlq mm1, 8        ; 0 0 0 0 0 0 v2 v1
+                        por mm0, mm2        ; v2 v1 v0 v2 v1 v0 0 0
+                        por mm0, mm1        ; v2 v1 v0 v2 v1 v0 v2 v1
+                        movq [edi+4], mm0   ; move to memory
+                        psrlq mm0, 16       ; 0 0 v2 v1 v0 v2 v1 v0
+                        movd [edi], mm0     ; move to memory
+                        sub esi, 3
+                        sub edi, 12
+                        dec ecx
+                        jnz loop_pass2
+                        EMMS
+                     }
+                  }
+                  else if (width) /* && ((pass == 0) || (pass == 1))) */
+                  {
+                     _asm
+                     {
+                        mov esi, sptr
+                        mov edi, dp
+                        mov ecx, width
+                        sub edi, 21   // (png_pass_inc[pass] - 1)*pixel_bytes
+loop_pass0:
+                        movd mm0, [esi]     ; X X X X X v2 v1 v0
+                        pand mm0, const4    ; 0 0 0 0 0 v2 v1 v0
+                        movq mm1, mm0       ; 0 0 0 0 0 v2 v1 v0
+                        psllq mm0, 16       ; 0 0 0 v2 v1 v0 0 0
+                        movq mm2, mm0       ; 0 0 0 v2 v1 v0 0 0
+                        psllq mm0, 24       ; v2 v1 v0 0 0 0 0 0
+                        psrlq mm1, 8        ; 0 0 0 0 0 0 v2 v1
+                        por mm0, mm2        ; v2 v1 v0 v2 v1 v0 0 0
+                        por mm0, mm1        ; v2 v1 v0 v2 v1 v0 v2 v1
+                        movq mm3, mm0       ; v2 v1 v0 v2 v1 v0 v2 v1
+                        psllq mm0, 16       ; v0 v2 v1 v0 v2 v1 0 0
+                        movq mm4, mm3       ; v2 v1 v0 v2 v1 v0 v2 v1
+                        punpckhdq mm3, mm0  ; v0 v2 v1 v0 v2 v1 v0 v2
+                        movq [edi+16] , mm4
+                        psrlq mm0, 32       ; 0 0 0 0 v0 v2 v1 v0
+                        movq [edi+8] , mm3
+                        punpckldq mm0, mm4  ; v1 v0 v2 v1 v0 v2 v1 v0
+                        sub esi, 3
+                        movq [edi], mm0
+                        sub edi, 24
+                        //sub esi, 3
+                        dec ecx
+                        jnz loop_pass0
+                        EMMS
+                     }
+                  }
+               } /* end of pixel_bytes == 3 */
+
+               else if (pixel_bytes == 1)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 3) << 3);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub edi, 15
+                           sub esi, 7
+loop1_pass4:
+                           movq mm0, [esi]     ; v0 v1 v2 v3 v4 v5 v6 v7
+                           movq mm1, mm0       ; v0 v1 v2 v3 v4 v5 v6 v7
+                           punpcklbw mm0, mm0  ; v4 v4 v5 v5 v6 v6 v7 v7
+                           //movq mm1, mm0     ; v0 v0 v1 v1 v2 v2 v3 v3
+                           punpckhbw mm1, mm1  ;v0 v0 v1 v1 v2 v2 v3 v3
+                           movq [edi+8], mm1   ; move to memory v0 v1 v2 and v3
+                           sub esi, 8
+                           movq [edi], mm0     ; move to memory v4 v5 v6 and v7
+                           //sub esi, 4
+                           sub edi, 16
+                           sub ecx, 8
+                           jnz loop1_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*2;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        sptr --;
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub edi, 15
+                           sub esi, 3
+loop1_pass2:
+                           movd mm0, [esi]     ; X X X X v0 v1 v2 v3
+                           punpcklbw mm0, mm0  ; v0 v0 v1 v1 v2 v2 v3 v3
+                           movq mm1, mm0       ; v0 v0 v1 v1 v2 v2 v3 v3
+                           punpcklwd mm0, mm0  ; v2 v2 v2 v2 v3 v3 v3 v3
+                           punpckhwd mm1, mm1  ; v0 v0 v0 v0 v1 v1 v1 v1
+                           movq [edi], mm0     ; move to memory v2 and v3
+                           sub esi, 4
+                           movq [edi+8], mm1   ; move to memory v1     and v0
+                           sub edi, 16
+                           sub ecx, 4
+                           jnz loop1_pass2
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*4;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           *dp-- = *sptr;
+                        }
+                        sptr --;
+                     }
+                  }
+                  else if (width) /* && ((pass == 0) || (pass == 1))) */
+                  {
+                     int width_mmx = ((width >> 2) << 2);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub edi, 31
+                           sub esi, 3
+loop1_pass0:
+                           movd mm0, [esi]     ; X X X X v0 v1 v2 v3
+                           movq mm1, mm0       ; X X X X v0 v1 v2 v3
+                           punpcklbw mm0, mm0  ; v0 v0 v1 v1 v2 v2 v3 v3
+                           movq mm2, mm0       ; v0 v0 v1 v1 v2 v2 v3 v3
+                           punpcklwd mm0, mm0  ; v2 v2 v2 v2 v3 v3 v3 v3
+                           movq mm3, mm0       ; v2 v2 v2 v2 v3 v3 v3 v3
+                           punpckldq mm0, mm0  ; v3 v3 v3 v3 v3 v3 v3 v3
+                           punpckhdq mm3, mm3  ; v2 v2 v2 v2 v2 v2 v2 v2
+                           movq [edi], mm0     ; move to memory v3
+                           punpckhwd mm2, mm2  ; v0 v0 v0 v0 v1 v1 v1 v1
+                           movq [edi+8], mm3   ; move to memory v2
+                           movq mm4, mm2       ; v0 v0 v0 v0 v1 v1 v1 v1
+                           punpckldq mm2, mm2  ; v1 v1 v1 v1 v1 v1 v1 v1
+                           punpckhdq mm4, mm4  ; v0 v0 v0 v0 v0 v0 v0 v0
+                           movq [edi+16], mm2  ; move to memory v1
+                           movq [edi+24], mm4  ; move to memory v0
+                           sub esi, 4
+                           sub edi, 32
+                           sub ecx, 4
+                           jnz loop1_pass0
+                           EMMS
+                        }
+                     }
+
+                     sptr -= width_mmx;
+                     dp -= width_mmx*8;
+                     for (i = width; i; i--)
+                     {
+                        int j;
+
+                       /* I simplified this part in version 1.0.4e
+                        * here and in several other instances where
+                        * pixel_bytes == 1  -- GR-P
+                        *
+                        * Original code:
+                        *
+                        * png_byte v[8];
+                        * png_memcpy(v, sptr, pixel_bytes);
+                        * for (j = 0; j < png_pass_inc[pass]; j++)
+                        * {
+                        *    png_memcpy(dp, v, pixel_bytes);
+                        *    dp -= pixel_bytes;
+                        * }
+                        * sptr -= pixel_bytes;
+                        *
+                        * Replacement code is in the next three lines:
+                        */
+
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                           *dp-- = *sptr;
+                        sptr--;
+                     }
+                  }
+               } /* end of pixel_bytes == 1 */
+
+               else if (pixel_bytes == 2)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 2
+                           sub edi, 6
+loop2_pass4:
+                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
+                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
+                           sub esi, 4
+                           movq [edi], mm0
+                           sub edi, 8
+                           sub ecx, 2
+                           jnz loop2_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*2 - 2);            // sign fixed
+                     dp -= (width_mmx*4 - 2);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 2
+                           sub edi, 14
+loop2_pass2:
+                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
+                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
+                           movq mm1, mm0          ; v1 v0 v1 v0 v3 v2 v3 v2
+                           punpckldq mm0, mm0     ; v3 v2 v3 v2 v3 v2 v3 v2
+                           punpckhdq mm1, mm1     ; v1 v0 v1 v0 v1 v0 v1 v0
+                           movq [edi], mm0
+                           sub esi, 4
+                           movq [edi + 8], mm1
+                           //sub esi, 4
+                           sub edi, 16
+                           sub ecx, 2
+                           jnz loop2_pass2
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*2 - 2);            // sign fixed
+                     dp -= (width_mmx*8 - 2);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+                  else if (width) /* && ((pass == 0) || (pass == 1))) */
+                  {
+                     int width_mmx = ((width >> 1) << 1);
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 2
+                           sub edi, 30
+loop2_pass0:
+                           movd mm0, [esi]        ; X X X X v1 v0 v3 v2
+                           punpcklwd mm0, mm0     ; v1 v0 v1 v0 v3 v2 v3 v2
+                           movq mm1, mm0          ; v1 v0 v1 v0 v3 v2 v3 v2
+                           punpckldq mm0, mm0     ; v3 v2 v3 v2 v3 v2 v3 v2
+                           punpckhdq mm1, mm1     ; v1 v0 v1 v0 v1 v0 v1 v0
+                           movq [edi], mm0
+                           movq [edi + 8], mm0
+                           movq [edi + 16], mm1
+                           movq [edi + 24], mm1
+                           sub esi, 4
+                           sub edi, 32
+                           sub ecx, 2
+                           jnz loop2_pass0
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*2 - 2);            // sign fixed
+                     dp -= (width_mmx*16 - 2);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 2;
+                        png_memcpy(v, sptr, 2);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 2;
+                           png_memcpy(dp, v, 2);
+                        }
+                     }
+                  }
+               } /* end of pixel_bytes == 2 */
+
+               else if (pixel_bytes == 4)
+               {
+                  if (((pass == 4) || (pass == 5)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 4
+                           sub edi, 12
+loop4_pass4:
+                           movq mm0, [esi]      ; v3 v2 v1 v0 v7 v6 v5 v4
+                           movq mm1, mm0        ; v3 v2 v1 v0 v7 v6 v5 v4
+                           punpckldq mm0, mm0   ; v7 v6 v5 v4 v7 v6 v5 v4
+                           punpckhdq mm1, mm1   ; v3 v2 v1 v0 v3 v2 v1 v0
+                           movq [edi], mm0
+                           sub esi, 8
+                           movq [edi + 8], mm1
+                           sub edi, 16
+                           sub ecx, 2
+                           jnz loop4_pass4
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*4 - 4);          // sign fixed
+                     dp -= (width_mmx*8 - 4);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (((pass == 2) || (pass == 3)) && width)
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 4
+                           sub edi, 28
+loop4_pass2:
+                           movq mm0, [esi]      ; v3 v2 v1 v0 v7 v6 v5 v4
+                           movq mm1, mm0        ; v3 v2 v1 v0 v7 v6 v5 v4
+                           punpckldq mm0, mm0   ; v7 v6 v5 v4 v7 v6 v5 v4
+                           punpckhdq mm1, mm1   ; v3 v2 v1 v0 v3 v2 v1 v0
+                           movq [edi], mm0
+                           movq [edi + 8], mm0
+                           movq [edi+16], mm1
+                           movq [edi + 24], mm1
+                           sub esi, 8
+                           sub edi, 32
+                           sub ecx, 2
+                           jnz loop4_pass2
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*4 - 4);            // sign fixed
+                     dp -= (width_mmx*16 - 4);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+                  else if (width) /* && ((pass == 0) || (pass == 1))) */
+                  {
+                     int width_mmx = ((width >> 1) << 1) ;
+                     width -= width_mmx;
+                     if (width_mmx)
+                     {
+                        _asm
+                        {
+                           mov esi, sptr
+                           mov edi, dp
+                           mov ecx, width_mmx
+                           sub esi, 4
+                           sub edi, 60
+loop4_pass0:
+                           movq mm0, [esi]        ; v3 v2 v1 v0 v7 v6 v5 v4
+                           movq mm1, mm0          ; v3 v2 v1 v0 v7 v6 v5 v4
+                           punpckldq mm0, mm0     ; v7 v6 v5 v4 v7 v6 v5 v4
+                           punpckhdq mm1, mm1     ; v3 v2 v1 v0 v3 v2 v1 v0
+                           movq [edi], mm0
+                           movq [edi + 8], mm0
+                           movq [edi + 16], mm0
+                           movq [edi + 24], mm0
+                           movq [edi+32], mm1
+                           movq [edi + 40], mm1
+                           movq [edi+ 48], mm1
+                           sub esi, 8
+                           movq [edi + 56], mm1
+                           sub edi, 64
+                           sub ecx, 2
+                           jnz loop4_pass0
+                           EMMS
+                        }
+                     }
+
+                     sptr -= (width_mmx*4 - 4);            // sign fixed
+                     dp -= (width_mmx*32 - 4);            // sign fixed
+                     for (i = width; i; i--)
+                     {
+                        png_byte v[8];
+                        int j;
+                        sptr -= 4;
+                        png_memcpy(v, sptr, 4);
+                        for (j = 0; j < png_pass_inc[pass]; j++)
+                        {
+                           dp -= 4;
+                           png_memcpy(dp, v, 4);
+                        }
+                     }
+                  }
+
+               } /* end of pixel_bytes == 4 */
+
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, 6);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, 6);
+                        dp -= 6;
+                     }
+                     sptr -= 6;
+                  }
+               } /* end of pixel_bytes == 6 */
+
+               else
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr-= pixel_bytes;
+                  }
+               }
+            } /* end of mmx_supported */
+
+            else /* MMX not supported:  use modified C code - takes advantage
+                  * of inlining of memcpy for a constant */
+            {
+               if (pixel_bytes == 1)
+               {
+                  for (i = width; i; i--)
+                  {
+                     int j;
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                        *dp-- = *sptr;
+                     sptr--;
+                  }
+               }
+               else if (pixel_bytes == 3)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else if (pixel_bytes == 2)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else if (pixel_bytes == 4)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else if (pixel_bytes == 6)
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+               else
+               {
+                  for (i = width; i; i--)
+                  {
+                     png_byte v[8];
+                     int j;
+                     png_memcpy(v, sptr, pixel_bytes);
+                     for (j = 0; j < png_pass_inc[pass]; j++)
+                     {
+                        png_memcpy(dp, v, pixel_bytes);
+                        dp -= pixel_bytes;
+                     }
+                     sptr -= pixel_bytes;
+                  }
+               }
+
+            } /* end of MMX not supported */
+            break;
+         }
+      } /* end switch (row_info->pixel_depth) */
+
+      row_info->width = final_width;
+
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width);
+   }
+
+}
+
+#endif /* PNG_READ_INTERLACING_SUPPORTED */
+
+
+// These global constants are declared
+// here to ensure alignment on 8-byte boundaries.
+  union uAll {
+     __int64 use;
+     double  double_align;
+     long long long_long_align;
+  } ;
+  static PNG_CONST union uAll LBCarryMask = {0x0101010101010101},
+                              HBClearMask = {0x7f7f7f7f7f7f7f7f};
+
+// Optimized code for PNG Average filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_avg(png_row_infop row_info, png_bytep row
+                            , png_bytep prev_row)
+{
+  // These variables are declared
+  // here to ensure alignment on 8-byte boundaries.
+  union uAll ActiveMask, ShiftBpp, ShiftRem;
+
+   int bpp;
+   png_uint_32 FullLength;
+   png_uint_32 MMXLength;
+   //png_uint_32 len;
+   int diff;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   FullLength  = row_info->rowbytes; // # of bytes to filter
+   _asm {
+         // Init address pointers and offset
+         mov edi, row          // edi ==> Avg(x)
+         xor ebx, ebx          // ebx ==> x
+         mov edx, edi
+         mov esi, prev_row           // esi ==> Prior(x)
+         sub edx, bpp          // edx ==> Raw(x-bpp)
+
+         xor eax, eax
+         // Compute the Raw value for the first bpp bytes
+         //    Raw(x) = Avg(x) + (Prior(x)/2)
+davgrlp:
+         mov al, [esi + ebx]   // Load al with Prior(x)
+         inc ebx
+         shr al, 1             // divide by 2
+         add al, [edi+ebx-1]   // Add Avg(x); -1 to offset inc ebx
+         cmp ebx, bpp
+         mov [edi+ebx-1], al    // Write back Raw(x);
+                            // mov does not affect flags; -1 to offset inc ebx
+         jb davgrlp
+         // get # of bytes to alignment
+         mov diff, edi         // take start of row
+         add diff, ebx         // add bpp
+         add diff, 0xf         // add 7 + 8 to incr past alignment boundary
+         and diff, 0xfffffff8  // mask to alignment boundary
+         sub diff, edi         // subtract from start ==> value ebx at alignment
+         jz davggo
+         // fix alignment
+         // Compute the Raw value for the bytes upto the alignment boundary
+         //    Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+         xor ecx, ecx
+davglp1:
+         xor eax, eax
+         mov cl, [esi + ebx]        // load cl with Prior(x)
+         mov al, [edx + ebx]  // load al with Raw(x-bpp)
+         add ax, cx
+         inc ebx
+         shr ax, 1            // divide by 2
+         add al, [edi+ebx-1]  // Add Avg(x); -1 to offset inc ebx
+         cmp ebx, diff              // Check if at alignment boundary
+         mov [edi+ebx-1], al        // Write back Raw(x);
+                            // mov does not affect flags; -1 to offset inc ebx
+         jb davglp1               // Repeat until at alignment boundary
+davggo:
+         mov eax, FullLength
+         mov ecx, eax
+         sub eax, ebx          // subtract alignment fix
+         and eax, 0x00000007   // calc bytes over mult of 8
+         sub ecx, eax          // drop over bytes from original length
+         mov MMXLength, ecx
+   } // end _asm block
+   // Now do the math for the rest of the row
+   switch ( bpp )
+   {
+      case 3:
+      {
+         ActiveMask.use  = 0x0000000000ffffff;
+         ShiftBpp.use = 24;    // == 3 * 8
+         ShiftRem.use = 40;    // == 64 - 24
+         _asm {
+            // Re-init address pointers and offset
+            movq mm7, ActiveMask
+            mov ebx, diff      // ebx ==> x = offset to alignment boundary
+            movq mm5, LBCarryMask
+            mov edi, row       // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov esi, prev_row        // esi ==> Prior(x)
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                               // (we correct position in loop below)
+davg3lp:
+            movq mm0, [edi + ebx]      // Load mm0 with Avg(x)
+            // Add (Prev_row/2) to Average
+            movq mm3, mm5
+            psrlq mm2, ShiftRem      // Correct position Raw(x-bpp) data
+            movq mm1, [esi + ebx]    // Load mm1 with Prior(x)
+            movq mm6, mm7
+            pand mm3, mm1      // get lsb for each prev_row byte
+            psrlq mm1, 1       // divide prev_row bytes by 2
+            pand  mm1, mm4     // clear invalid bit 7 of each byte
+            paddb mm0, mm1     // add (Prev_row/2) to Avg for each byte
+            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm1, mm3      // now use mm1 for getting LBCarrys
+            pand mm1, mm2      // get LBCarrys for each byte where both
+                               // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1       // divide raw bytes by 2
+            pand  mm2, mm4     // clear invalid bit 7 of each byte
+            paddb mm2, mm1     // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6      // Leave only Active Group 1 bytes to add to Avg
+            paddb mm0, mm2     // add (Raw/2) + LBCarrys to Avg for each Active
+                               //  byte
+            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp  // shift the mm6 mask to cover bytes 3-5
+            movq mm2, mm0        // mov updated Raws to mm2
+            psllq mm2, ShiftBpp  // shift data to position correctly
+            movq mm1, mm3        // now use mm1 for getting LBCarrys
+            pand mm1, mm2      // get LBCarrys for each byte where both
+                               // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1       // divide raw bytes by 2
+            pand  mm2, mm4     // clear invalid bit 7 of each byte
+            paddb mm2, mm1     // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6      // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2     // add (Raw/2) + LBCarrys to Avg for each Active
+                               //  byte
+
+            // Add 3rd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp  // shift the mm6 mask to cover the last two
+                                 // bytes
+            movq mm2, mm0        // mov updated Raws to mm2
+            psllq mm2, ShiftBpp  // shift data to position correctly
+                              // Data only needs to be shifted once here to
+                              // get the correct x-bpp offset.
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6     // Leave only Active Group 2 bytes to add to Avg
+            add ebx, 8
+            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
+                              // byte
+
+            // Now ready to write back to memory
+            movq [edi + ebx - 8], mm0
+            // Move updated Raw(x) to use as Raw(x-bpp) for next loop
+            cmp ebx, MMXLength
+            movq mm2, mm0     // mov updated Raw(x) to mm2
+            jb davg3lp
+         } // end _asm block
+      }
+      break;
+
+      case 6:
+      case 4:
+      case 7:
+      case 5:
+      {
+         ActiveMask.use  = 0xffffffffffffffff;  // use shift below to clear
+                                                // appropriate inactive bytes
+         ShiftBpp.use = bpp << 3;
+         ShiftRem.use = 64 - ShiftBpp.use;
+         _asm {
+            movq mm4, HBClearMask
+            // Re-init address pointers and offset
+            mov ebx, diff       // ebx ==> x = offset to alignment boundary
+            // Load ActiveMask and clear all bytes except for 1st active group
+            movq mm7, ActiveMask
+            mov edi, row         // edi ==> Avg(x)
+            psrlq mm7, ShiftRem
+            mov esi, prev_row    // esi ==> Prior(x)
+            movq mm6, mm7
+            movq mm5, LBCarryMask
+            psllq mm6, ShiftBpp  // Create mask for 2nd active group
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                                 // (we correct position in loop below)
+davg4lp:
+            movq mm0, [edi + ebx]
+            psrlq mm2, ShiftRem  // shift data to position correctly
+            movq mm1, [esi + ebx]
+            // Add (Prev_row/2) to Average
+            movq mm3, mm5
+            pand mm3, mm1     // get lsb for each prev_row byte
+            psrlq mm1, 1      // divide prev_row bytes by 2
+            pand  mm1, mm4    // clear invalid bit 7 of each byte
+            paddb mm0, mm1    // add (Prev_row/2) to Avg for each byte
+            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm7     // Leave only Active Group 1 bytes to add to Avg
+            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
+                              // byte
+            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm2, mm0     // mov updated Raws to mm2
+            psllq mm2, ShiftBpp // shift data to position correctly
+            add ebx, 8
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6     // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2    // add (Raw/2) + LBCarrys to Avg for each Active
+                              // byte
+            cmp ebx, MMXLength
+            // Now ready to write back to memory
+            movq [edi + ebx - 8], mm0
+            // Prep Raw(x-bpp) for next loop
+            movq mm2, mm0     // mov updated Raws to mm2
+            jb davg4lp
+         } // end _asm block
+      }
+      break;
+      case 2:
+      {
+         ActiveMask.use  = 0x000000000000ffff;
+         ShiftBpp.use = 16;   // == 2 * 8     [BUGFIX]
+         ShiftRem.use = 48;   // == 64 - 16   [BUGFIX]
+         _asm {
+            // Load ActiveMask
+            movq mm7, ActiveMask
+            // Re-init address pointers and offset
+            mov ebx, diff     // ebx ==> x = offset to alignment boundary
+            movq mm5, LBCarryMask
+            mov edi, row      // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov esi, prev_row  // esi ==> Prior(x)
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                              // (we correct position in loop below)
+davg2lp:
+            movq mm0, [edi + ebx]
+            psrlq mm2, ShiftRem  // shift data to position correctly   [BUGFIX]
+            movq mm1, [esi + ebx]
+            // Add (Prev_row/2) to Average
+            movq mm3, mm5
+            pand mm3, mm1     // get lsb for each prev_row byte
+            psrlq mm1, 1      // divide prev_row bytes by 2
+            pand  mm1, mm4    // clear invalid bit 7 of each byte
+            movq mm6, mm7
+            paddb mm0, mm1    // add (Prev_row/2) to Avg for each byte
+            // Add 1st active group (Raw(x-bpp)/2) to Average with LBCarry
+            movq mm1, mm3     // now use mm1 for getting LBCarrys
+            pand mm1, mm2     // get LBCarrys for each byte where both
+                              // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1      // divide raw bytes by 2
+            pand  mm2, mm4    // clear invalid bit 7 of each byte
+            paddb mm2, mm1    // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6     // Leave only Active Group 1 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+            // Add 2nd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 2 & 3
+            movq mm2, mm0       // mov updated Raws to mm2
+            psllq mm2, ShiftBpp // shift data to position correctly
+            movq mm1, mm3       // now use mm1 for getting LBCarrys
+            pand mm1, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm2, mm1      // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6       // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+
+            // Add rdd active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp // shift the mm6 mask to cover bytes 4 & 5
+            movq mm2, mm0       // mov updated Raws to mm2
+            psllq mm2, ShiftBpp // shift data to position correctly
+                                // Data only needs to be shifted once here to
+                                // get the correct x-bpp offset.
+            movq mm1, mm3       // now use mm1 for getting LBCarrys
+            pand mm1, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm2, mm1      // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6       // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+
+            // Add 4th active group (Raw(x-bpp)/2) to Average with LBCarry
+            psllq mm6, ShiftBpp  // shift the mm6 mask to cover bytes 6 & 7
+            movq mm2, mm0        // mov updated Raws to mm2
+            psllq mm2, ShiftBpp  // shift data to position correctly
+                                 // Data only needs to be shifted once here to
+                                 // get the correct x-bpp offset.
+            add ebx, 8
+            movq mm1, mm3    // now use mm1 for getting LBCarrys
+            pand mm1, mm2    // get LBCarrys for each byte where both
+                             // lsb's were == 1 (Only valid for active group)
+            psrlq mm2, 1     // divide raw bytes by 2
+            pand  mm2, mm4   // clear invalid bit 7 of each byte
+            paddb mm2, mm1   // add LBCarrys to (Raw(x-bpp)/2) for each byte
+            pand mm2, mm6    // Leave only Active Group 2 bytes to add to Avg
+            paddb mm0, mm2 // add (Raw/2) + LBCarrys to Avg for each Active byte
+
+            cmp ebx, MMXLength
+            // Now ready to write back to memory
+            movq [edi + ebx - 8], mm0
+            // Prep Raw(x-bpp) for next loop
+            movq mm2, mm0    // mov updated Raws to mm2
+            jb davg2lp
+        } // end _asm block
+      }
+      break;
+
+      case 1:                 // bpp == 1
+      {
+         _asm {
+            // Re-init address pointers and offset
+            mov ebx, diff     // ebx ==> x = offset to alignment boundary
+            mov edi, row      // edi ==> Avg(x)
+            cmp ebx, FullLength  // Test if offset at end of array
+            jnb davg1end
+            // Do Paeth decode for remaining bytes
+            mov esi, prev_row    // esi ==> Prior(x)
+            mov edx, edi
+            xor ecx, ecx         // zero ecx before using cl & cx in loop below
+            sub edx, bpp         // edx ==> Raw(x-bpp)
+davg1lp:
+            // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+            xor eax, eax
+            mov cl, [esi + ebx]  // load cl with Prior(x)
+            mov al, [edx + ebx]  // load al with Raw(x-bpp)
+            add ax, cx
+            inc ebx
+            shr ax, 1            // divide by 2
+            add al, [edi+ebx-1]  // Add Avg(x); -1 to offset inc ebx
+            cmp ebx, FullLength  // Check if at end of array
+            mov [edi+ebx-1], al  // Write back Raw(x);
+                         // mov does not affect flags; -1 to offset inc ebx
+            jb davg1lp
+davg1end:
+         } // end _asm block
+      }
+      return;
+
+      case 8:             // bpp == 8
+      {
+         _asm {
+            // Re-init address pointers and offset
+            mov ebx, diff           // ebx ==> x = offset to alignment boundary
+            movq mm5, LBCarryMask
+            mov edi, row            // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov esi, prev_row       // esi ==> Prior(x)
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm2, [edi + ebx - 8]  // Load previous aligned 8 bytes
+                                // (NO NEED to correct position in loop below)
+davg8lp:
+            movq mm0, [edi + ebx]
+            movq mm3, mm5
+            movq mm1, [esi + ebx]
+            add ebx, 8
+            pand mm3, mm1       // get lsb for each prev_row byte
+            psrlq mm1, 1        // divide prev_row bytes by 2
+            pand mm3, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm1, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm3      // add LBCarrys to Avg for each byte
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm1      // add (Prev_row/2) to Avg for each byte
+            paddb mm0, mm2      // add (Raw/2) to Avg for each byte
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm0
+            movq mm2, mm0       // reuse as Raw(x-bpp)
+            jb davg8lp
+        } // end _asm block
+      }
+      break;
+      default:                  // bpp greater than 8
+      {
+        _asm {
+            movq mm5, LBCarryMask
+            // Re-init address pointers and offset
+            mov ebx, diff       // ebx ==> x = offset to alignment boundary
+            mov edi, row        // edi ==> Avg(x)
+            movq mm4, HBClearMask
+            mov edx, edi
+            mov esi, prev_row   // esi ==> Prior(x)
+            sub edx, bpp        // edx ==> Raw(x-bpp)
+davgAlp:
+            movq mm0, [edi + ebx]
+            movq mm3, mm5
+            movq mm1, [esi + ebx]
+            pand mm3, mm1       // get lsb for each prev_row byte
+            movq mm2, [edx + ebx]
+            psrlq mm1, 1        // divide prev_row bytes by 2
+            pand mm3, mm2       // get LBCarrys for each byte where both
+                                // lsb's were == 1
+            psrlq mm2, 1        // divide raw bytes by 2
+            pand  mm1, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm3      // add LBCarrys to Avg for each byte
+            pand  mm2, mm4      // clear invalid bit 7 of each byte
+            paddb mm0, mm1      // add (Prev_row/2) to Avg for each byte
+            add ebx, 8
+            paddb mm0, mm2      // add (Raw/2) to Avg for each byte
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm0
+            jb davgAlp
+        } // end _asm block
+      }
+      break;
+   }                         // end switch ( bpp )
+
+   _asm {
+         // MMX acceleration complete now do clean-up
+         // Check if any remaining bytes left to decode
+         mov ebx, MMXLength    // ebx ==> x = offset bytes remaining after MMX
+         mov edi, row          // edi ==> Avg(x)
+         cmp ebx, FullLength   // Test if offset at end of array
+         jnb davgend
+         // Do Paeth decode for remaining bytes
+         mov esi, prev_row     // esi ==> Prior(x)
+         mov edx, edi
+         xor ecx, ecx          // zero ecx before using cl & cx in loop below
+         sub edx, bpp          // edx ==> Raw(x-bpp)
+davglp2:
+         // Raw(x) = Avg(x) + ((Raw(x-bpp) + Prior(x))/2)
+         xor eax, eax
+         mov cl, [esi + ebx]   // load cl with Prior(x)
+         mov al, [edx + ebx]   // load al with Raw(x-bpp)
+         add ax, cx
+         inc ebx
+         shr ax, 1              // divide by 2
+         add al, [edi+ebx-1]    // Add Avg(x); -1 to offset inc ebx
+         cmp ebx, FullLength    // Check if at end of array
+         mov [edi+ebx-1], al    // Write back Raw(x);
+                          // mov does not affect flags; -1 to offset inc ebx
+         jb davglp2
+davgend:
+         emms             // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+// Optimized code for PNG Paeth filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_paeth(png_row_infop row_info, png_bytep row,
+                              png_bytep prev_row)
+{
+  // These variables are declared
+  // here to ensure alignment on 8-byte boundaries.
+  union uAll  ActiveMask, ActiveMask2, ActiveMaskEnd, ShiftBpp, ShiftRem;
+
+   png_uint_32 FullLength;
+   png_uint_32 MMXLength;
+   //png_uint_32 len;
+   int bpp;
+   int diff;
+   //int ptemp;
+   int patemp, pbtemp, pctemp;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   FullLength  = row_info->rowbytes; // # of bytes to filter
+   _asm
+   {
+         xor ebx, ebx        // ebx ==> x offset
+         mov edi, row
+         xor edx, edx        // edx ==> x-bpp offset
+         mov esi, prev_row
+         xor eax, eax
+
+         // Compute the Raw value for the first bpp bytes
+         // Note: the formula works out to be always
+         //   Paeth(x) = Raw(x) + Prior(x)      where x < bpp
+dpthrlp:
+         mov al, [edi + ebx]
+         add al, [esi + ebx]
+         inc ebx
+         cmp ebx, bpp
+         mov [edi + ebx - 1], al
+         jb dpthrlp
+         // get # of bytes to alignment
+         mov diff, edi         // take start of row
+         add diff, ebx         // add bpp
+         xor ecx, ecx
+         add diff, 0xf         // add 7 + 8 to incr past alignment boundary
+         and diff, 0xfffffff8  // mask to alignment boundary
+         sub diff, edi         // subtract from start ==> value ebx at alignment
+         jz dpthgo
+         // fix alignment
+dpthlp1:
+         xor eax, eax
+         // pav = p - a = (a + b - c) - a = b - c
+         mov al, [esi + ebx]   // load Prior(x) into al
+         mov cl, [esi + edx]   // load Prior(x-bpp) into cl
+         sub eax, ecx          // subtract Prior(x-bpp)
+         mov patemp, eax       // Save pav for later use
+         xor eax, eax
+         // pbv = p - b = (a + b - c) - b = a - c
+         mov al, [edi + edx]   // load Raw(x-bpp) into al
+         sub eax, ecx          // subtract Prior(x-bpp)
+         mov ecx, eax
+         // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+         add eax, patemp       // pcv = pav + pbv
+         // pc = abs(pcv)
+         test eax, 0x80000000
+         jz dpthpca
+         neg eax               // reverse sign of neg values
+dpthpca:
+         mov pctemp, eax       // save pc for later use
+         // pb = abs(pbv)
+         test ecx, 0x80000000
+         jz dpthpba
+         neg ecx               // reverse sign of neg values
+dpthpba:
+         mov pbtemp, ecx       // save pb for later use
+         // pa = abs(pav)
+         mov eax, patemp
+         test eax, 0x80000000
+         jz dpthpaa
+         neg eax               // reverse sign of neg values
+dpthpaa:
+         mov patemp, eax       // save pa for later use
+         // test if pa <= pb
+         cmp eax, ecx
+         jna dpthabb
+         // pa > pb; now test if pb <= pc
+         cmp ecx, pctemp
+         jna dpthbbc
+         // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth
+dpthbbc:
+         // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+         mov cl, [esi + ebx]   // load Prior(x) into cl
+         jmp dpthpaeth
+dpthabb:
+         // pa <= pb; now test if pa <= pc
+         cmp eax, pctemp
+         jna dpthabc
+         // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth
+dpthabc:
+         // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+         mov cl, [edi + edx]  // load Raw(x-bpp) into cl
+dpthpaeth:
+         inc ebx
+         inc edx
+         // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+         add [edi + ebx - 1], cl
+         cmp ebx, diff
+         jb dpthlp1
+dpthgo:
+         mov ecx, FullLength
+         mov eax, ecx
+         sub eax, ebx          // subtract alignment fix
+         and eax, 0x00000007   // calc bytes over mult of 8
+         sub ecx, eax          // drop over bytes from original length
+         mov MMXLength, ecx
+   } // end _asm block
+   // Now do the math for the rest of the row
+   switch ( bpp )
+   {
+      case 3:
+      {
+         ActiveMask.use = 0x0000000000ffffff;
+         ActiveMaskEnd.use = 0xffff000000000000;
+         ShiftBpp.use = 24;    // == bpp(3) * 8
+         ShiftRem.use = 40;    // == 64 - 24
+         _asm
+         {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            pxor mm0, mm0
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dpth3lp:
+            psrlq mm1, ShiftRem     // shift last 3 bytes to 1st 3 bytes
+            movq mm2, [esi + ebx]   // load b=Prior(x)
+            punpcklbw mm1, mm0      // Unpack High bytes of a
+            movq mm3, [esi+ebx-8]   // Prep c=Prior(x-bpp) bytes
+            punpcklbw mm2, mm0      // Unpack High bytes of b
+            psrlq mm3, ShiftRem     // shift last 3 bytes to 1st 3 bytes
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpcklbw mm3, mm0      // Unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4       // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5       // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
+            pand mm0, mm6       // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5    // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi + ebx]   // load c=Prior(x-bpp)
+            pand mm7, ActiveMask
+            movq mm2, mm3           // load b=Prior(x) step 1
+            paddb mm7, [edi + ebx]  // add Paeth predictor with Raw(x)
+            punpcklbw mm3, mm0      // Unpack High bytes of c
+            movq [edi + ebx], mm7   // write back updated value
+            movq mm1, mm7           // Now mm1 will be used as Raw(x-bpp)
+            // Now do Paeth for 2nd set of bytes (3-5)
+            psrlq mm2, ShiftBpp     // load b=Prior(x) step 2
+            punpcklbw mm1, mm0      // Unpack High bytes of a
+            pxor mm7, mm7
+            punpcklbw mm2, mm0      // Unpack High bytes of b
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            psubw mm5, mm3
+            psubw mm4, mm3
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) =
+            //       pav + pbv = pbv + pav
+            movq mm6, mm5
+            paddw mm6, mm4
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm5       // Create mask pbv bytes < 0
+            pcmpgtw mm7, mm4       // Create mask pav bytes < 0
+            pand mm0, mm5          // Only pbv bytes < 0 in mm0
+            pand mm7, mm4          // Only pav bytes < 0 in mm7
+            psubw mm5, mm0
+            psubw mm4, mm7
+            psubw mm5, mm0
+            psubw mm4, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            movq mm2, [esi + ebx]  // load b=Prior(x)
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, mm2           // load c=Prior(x-bpp) step 1
+            pand mm7, ActiveMask
+            punpckhbw mm2, mm0      // Unpack High bytes of b
+            psllq mm7, ShiftBpp     // Shift bytes to 2nd group of 3 bytes
+             // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            paddb mm7, [edi + ebx]  // add Paeth predictor with Raw(x)
+            psllq mm3, ShiftBpp     // load c=Prior(x-bpp) step 2
+            movq [edi + ebx], mm7   // write back updated value
+            movq mm1, mm7
+            punpckhbw mm3, mm0      // Unpack High bytes of c
+            psllq mm1, ShiftBpp     // Shift bytes
+                                    // Now mm1 will be used as Raw(x-bpp)
+            // Now do Paeth for 3rd, and final, set of bytes (6-7)
+            pxor mm7, mm7
+            punpckhbw mm1, mm0      // Unpack High bytes of a
+            psubw mm4, mm3
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            pxor mm0, mm0
+            paddw mm6, mm5
+
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
+            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
+            pand mm0, mm4       // Only pav bytes < 0 in mm7
+            pand mm7, mm5       // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
+            pand mm0, mm6       // Only pav bytes < 0 in mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5    // pa > pb?
+            movq mm0, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            pandn mm0, mm1
+            pandn mm7, mm4
+            paddw mm0, mm2
+            paddw mm7, mm5
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6    // pab > pc?
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm1, mm1
+            packuswb mm1, mm7
+            // Step ebx to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            pand mm1, ActiveMaskEnd
+            paddb mm1, [edi + ebx - 8] // add Paeth predictor with Raw(x)
+
+            cmp ebx, MMXLength
+            pxor mm0, mm0              // pxor does not affect flags
+            movq [edi + ebx - 8], mm1  // write back updated value
+                                 // mm1 will be used as Raw(x-bpp) next loop
+                           // mm3 ready to be used as Prior(x-bpp) next loop
+            jb dpth3lp
+         } // end _asm block
+      }
+      break;
+
+      case 6:
+      case 7:
+      case 5:
+      {
+         ActiveMask.use  = 0x00000000ffffffff;
+         ActiveMask2.use = 0xffffffff00000000;
+         ShiftBpp.use = bpp << 3;    // == bpp * 8
+         ShiftRem.use = 64 - ShiftBpp.use;
+         _asm
+         {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+            pxor mm0, mm0
+dpth6lp:
+            // Must shift to position Raw(x-bpp) data
+            psrlq mm1, ShiftRem
+            // Do first set of 4 bytes
+            movq mm3, [esi+ebx-8]      // read c=Prior(x-bpp) bytes
+            punpcklbw mm1, mm0      // Unpack Low bytes of a
+            movq mm2, [esi + ebx]   // load b=Prior(x)
+            punpcklbw mm2, mm0      // Unpack Low bytes of b
+            // Must shift to position Prior(x-bpp) data
+            psrlq mm3, ShiftRem
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpcklbw mm3, mm0      // Unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4    // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4       // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5    // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5       // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6    // Create mask pcv bytes < 0
+            pand mm0, mm6       // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5    // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6    // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi + ebx - 8]  // load c=Prior(x-bpp)
+            pand mm7, ActiveMask
+            psrlq mm3, ShiftRem
+            movq mm2, [esi + ebx]      // load b=Prior(x) step 1
+            paddb mm7, [edi + ebx]     // add Paeth predictor with Raw(x)
+            movq mm6, mm2
+            movq [edi + ebx], mm7      // write back updated value
+            movq mm1, [edi+ebx-8]
+            psllq mm6, ShiftBpp
+            movq mm5, mm7
+            psrlq mm1, ShiftRem
+            por mm3, mm6
+            psllq mm5, ShiftBpp
+            punpckhbw mm3, mm0         // Unpack High bytes of c
+            por mm1, mm5
+            // Do second set of 4 bytes
+            punpckhbw mm2, mm0         // Unpack High bytes of b
+            punpckhbw mm1, mm0         // Unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6           // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            // Step ex to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            packuswb mm1, mm7
+            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm1      // write back updated value
+                                // mm1 will be used as Raw(x-bpp) next loop
+            jb dpth6lp
+         } // end _asm block
+      }
+      break;
+
+      case 4:
+      {
+         ActiveMask.use  = 0x00000000ffffffff;
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            pxor mm0, mm0
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]    // Only time should need to read
+                                     //  a=Raw(x-bpp) bytes
+dpth4lp:
+            // Do first set of 4 bytes
+            movq mm3, [esi+ebx-8]    // read c=Prior(x-bpp) bytes
+            punpckhbw mm1, mm0       // Unpack Low bytes of a
+            movq mm2, [esi + ebx]    // load b=Prior(x)
+            punpcklbw mm2, mm0       // Unpack High bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpckhbw mm3, mm0       // Unpack High bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi + ebx]      // load c=Prior(x-bpp)
+            pand mm7, ActiveMask
+            movq mm2, mm3              // load b=Prior(x) step 1
+            paddb mm7, [edi + ebx]     // add Paeth predictor with Raw(x)
+            punpcklbw mm3, mm0         // Unpack High bytes of c
+            movq [edi + ebx], mm7      // write back updated value
+            movq mm1, mm7              // Now mm1 will be used as Raw(x-bpp)
+            // Do second set of 4 bytes
+            punpckhbw mm2, mm0         // Unpack Low bytes of b
+            punpcklbw mm1, mm0         // Unpack Low bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            // Step ex to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            packuswb mm1, mm7
+            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm1      // write back updated value
+                                // mm1 will be used as Raw(x-bpp) next loop
+            jb dpth4lp
+         } // end _asm block
+      }
+      break;
+      case 8:                          // bpp == 8
+      {
+         ActiveMask.use  = 0x00000000ffffffff;
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            mov esi, prev_row
+            pxor mm0, mm0
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]      // Only time should need to read
+                                       //  a=Raw(x-bpp) bytes
+dpth8lp:
+            // Do first set of 4 bytes
+            movq mm3, [esi+ebx-8]      // read c=Prior(x-bpp) bytes
+            punpcklbw mm1, mm0         // Unpack Low bytes of a
+            movq mm2, [esi + ebx]      // load b=Prior(x)
+            punpcklbw mm2, mm0         // Unpack Low bytes of b
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            punpcklbw mm3, mm0         // Unpack Low bytes of c
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            paddw mm7, mm3
+            pxor mm0, mm0
+            packuswb mm7, mm1
+            movq mm3, [esi+ebx-8]    // read c=Prior(x-bpp) bytes
+            pand mm7, ActiveMask
+            movq mm2, [esi + ebx]    // load b=Prior(x)
+            paddb mm7, [edi + ebx]   // add Paeth predictor with Raw(x)
+            punpckhbw mm3, mm0       // Unpack High bytes of c
+            movq [edi + ebx], mm7    // write back updated value
+            movq mm1, [edi+ebx-8]    // read a=Raw(x-bpp) bytes
+
+            // Do second set of 4 bytes
+            punpckhbw mm2, mm0       // Unpack High bytes of b
+            punpckhbw mm1, mm0       // Unpack High bytes of a
+            // pav = p - a = (a + b - c) - a = b - c
+            movq mm4, mm2
+            // pbv = p - b = (a + b - c) - b = a - c
+            movq mm5, mm1
+            psubw mm4, mm3
+            pxor mm7, mm7
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            movq mm6, mm4
+            psubw mm5, mm3
+            // pa = abs(p-a) = abs(pav)
+            // pb = abs(p-b) = abs(pbv)
+            // pc = abs(p-c) = abs(pcv)
+            pcmpgtw mm0, mm4       // Create mask pav bytes < 0
+            paddw mm6, mm5
+            pand mm0, mm4          // Only pav bytes < 0 in mm7
+            pcmpgtw mm7, mm5       // Create mask pbv bytes < 0
+            psubw mm4, mm0
+            pand mm7, mm5          // Only pbv bytes < 0 in mm0
+            psubw mm4, mm0
+            psubw mm5, mm7
+            pxor mm0, mm0
+            pcmpgtw mm0, mm6       // Create mask pcv bytes < 0
+            pand mm0, mm6          // Only pav bytes < 0 in mm7
+            psubw mm5, mm7
+            psubw mm6, mm0
+            //  test pa <= pb
+            movq mm7, mm4
+            psubw mm6, mm0
+            pcmpgtw mm7, mm5       // pa > pb?
+            movq mm0, mm7
+            // use mm7 mask to merge pa & pb
+            pand mm5, mm7
+            // use mm0 mask copy to merge a & b
+            pand mm2, mm0
+            pandn mm7, mm4
+            pandn mm0, mm1
+            paddw mm7, mm5
+            paddw mm0, mm2
+            //  test  ((pa <= pb)? pa:pb) <= pc
+            pcmpgtw mm7, mm6       // pab > pc?
+            pxor mm1, mm1
+            pand mm3, mm7
+            pandn mm7, mm0
+            pxor mm1, mm1
+            paddw mm7, mm3
+            pxor mm0, mm0
+            // Step ex to next set of 8 bytes and repeat loop til done
+            add ebx, 8
+            packuswb mm1, mm7
+            paddb mm1, [edi + ebx - 8]     // add Paeth predictor with Raw(x)
+            cmp ebx, MMXLength
+            movq [edi + ebx - 8], mm1      // write back updated value
+                            // mm1 will be used as Raw(x-bpp) next loop
+            jb dpth8lp
+         } // end _asm block
+      }
+      break;
+
+      case 1:                // bpp = 1
+      case 2:                // bpp = 2
+      default:               // bpp > 8
+      {
+         _asm {
+            mov ebx, diff
+            cmp ebx, FullLength
+            jnb dpthdend
+            mov edi, row
+            mov esi, prev_row
+            // Do Paeth decode for remaining bytes
+            mov edx, ebx
+            xor ecx, ecx        // zero ecx before using cl & cx in loop below
+            sub edx, bpp        // Set edx = ebx - bpp
+dpthdlp:
+            xor eax, eax
+            // pav = p - a = (a + b - c) - a = b - c
+            mov al, [esi + ebx]        // load Prior(x) into al
+            mov cl, [esi + edx]        // load Prior(x-bpp) into cl
+            sub eax, ecx                 // subtract Prior(x-bpp)
+            mov patemp, eax                 // Save pav for later use
+            xor eax, eax
+            // pbv = p - b = (a + b - c) - b = a - c
+            mov al, [edi + edx]        // load Raw(x-bpp) into al
+            sub eax, ecx                 // subtract Prior(x-bpp)
+            mov ecx, eax
+            // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+            add eax, patemp                 // pcv = pav + pbv
+            // pc = abs(pcv)
+            test eax, 0x80000000
+            jz dpthdpca
+            neg eax                     // reverse sign of neg values
+dpthdpca:
+            mov pctemp, eax             // save pc for later use
+            // pb = abs(pbv)
+            test ecx, 0x80000000
+            jz dpthdpba
+            neg ecx                     // reverse sign of neg values
+dpthdpba:
+            mov pbtemp, ecx             // save pb for later use
+            // pa = abs(pav)
+            mov eax, patemp
+            test eax, 0x80000000
+            jz dpthdpaa
+            neg eax                     // reverse sign of neg values
+dpthdpaa:
+            mov patemp, eax             // save pa for later use
+            // test if pa <= pb
+            cmp eax, ecx
+            jna dpthdabb
+            // pa > pb; now test if pb <= pc
+            cmp ecx, pctemp
+            jna dpthdbbc
+            // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+            jmp dpthdpaeth
+dpthdbbc:
+            // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+            mov cl, [esi + ebx]        // load Prior(x) into cl
+            jmp dpthdpaeth
+dpthdabb:
+            // pa <= pb; now test if pa <= pc
+            cmp eax, pctemp
+            jna dpthdabc
+            // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+            mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+            jmp dpthdpaeth
+dpthdabc:
+            // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+            mov cl, [edi + edx]  // load Raw(x-bpp) into cl
+dpthdpaeth:
+            inc ebx
+            inc edx
+            // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+            add [edi + ebx - 1], cl
+            cmp ebx, FullLength
+            jb dpthdlp
+dpthdend:
+         } // end _asm block
+      }
+      return;                   // No need to go further with this one
+   }                         // end switch ( bpp )
+   _asm
+   {
+         // MMX acceleration complete now do clean-up
+         // Check if any remaining bytes left to decode
+         mov ebx, MMXLength
+         cmp ebx, FullLength
+         jnb dpthend
+         mov edi, row
+         mov esi, prev_row
+         // Do Paeth decode for remaining bytes
+         mov edx, ebx
+         xor ecx, ecx         // zero ecx before using cl & cx in loop below
+         sub edx, bpp         // Set edx = ebx - bpp
+dpthlp2:
+         xor eax, eax
+         // pav = p - a = (a + b - c) - a = b - c
+         mov al, [esi + ebx]  // load Prior(x) into al
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         sub eax, ecx         // subtract Prior(x-bpp)
+         mov patemp, eax      // Save pav for later use
+         xor eax, eax
+         // pbv = p - b = (a + b - c) - b = a - c
+         mov al, [edi + edx]  // load Raw(x-bpp) into al
+         sub eax, ecx         // subtract Prior(x-bpp)
+         mov ecx, eax
+         // pcv = p - c = (a + b - c) -c = (a - c) + (b - c) = pav + pbv
+         add eax, patemp      // pcv = pav + pbv
+         // pc = abs(pcv)
+         test eax, 0x80000000
+         jz dpthpca2
+         neg eax              // reverse sign of neg values
+dpthpca2:
+         mov pctemp, eax      // save pc for later use
+         // pb = abs(pbv)
+         test ecx, 0x80000000
+         jz dpthpba2
+         neg ecx              // reverse sign of neg values
+dpthpba2:
+         mov pbtemp, ecx      // save pb for later use
+         // pa = abs(pav)
+         mov eax, patemp
+         test eax, 0x80000000
+         jz dpthpaa2
+         neg eax              // reverse sign of neg values
+dpthpaa2:
+         mov patemp, eax      // save pa for later use
+         // test if pa <= pb
+         cmp eax, ecx
+         jna dpthabb2
+         // pa > pb; now test if pb <= pc
+         cmp ecx, pctemp
+         jna dpthbbc2
+         // pb > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth2
+dpthbbc2:
+         // pb <= pc; Raw(x) = Paeth(x) + Prior(x)
+         mov cl, [esi + ebx]        // load Prior(x) into cl
+         jmp dpthpaeth2
+dpthabb2:
+         // pa <= pb; now test if pa <= pc
+         cmp eax, pctemp
+         jna dpthabc2
+         // pa > pc; Raw(x) = Paeth(x) + Prior(x-bpp)
+         mov cl, [esi + edx]  // load Prior(x-bpp) into cl
+         jmp dpthpaeth2
+dpthabc2:
+         // pa <= pc; Raw(x) = Paeth(x) + Raw(x-bpp)
+         mov cl, [edi + edx]  // load Raw(x-bpp) into cl
+dpthpaeth2:
+         inc ebx
+         inc edx
+         // Raw(x) = (Paeth(x) + Paeth_Predictor( a, b, c )) mod 256
+         add [edi + ebx - 1], cl
+         cmp ebx, FullLength
+         jb dpthlp2
+dpthend:
+         emms             // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+// Optimized code for PNG Sub filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_sub(png_row_infop row_info, png_bytep row)
+{
+  // These variables are declared
+  // here to ensure alignment on 8-byte boundaries.
+  union uAll ActiveMask, ShiftBpp, ShiftRem;
+
+   //int test;
+   int bpp;
+   png_uint_32 FullLength;
+   png_uint_32 MMXLength;
+   int diff;
+
+   bpp = (row_info->pixel_depth + 7) >> 3; // Get # bytes per pixel
+   FullLength  = row_info->rowbytes - bpp; // # of bytes to filter
+   _asm {
+        mov edi, row
+        mov esi, edi               // lp = row
+        add edi, bpp               // rp = row + bpp
+        xor eax, eax
+        // get # of bytes to alignment
+        mov diff, edi               // take start of row
+        add diff, 0xf               // add 7 + 8 to incr past
+                                        // alignment boundary
+        xor ebx, ebx
+        and diff, 0xfffffff8        // mask to alignment boundary
+        sub diff, edi               // subtract from start ==> value
+                                        //  ebx at alignment
+        jz dsubgo
+        // fix alignment
+dsublp1:
+        mov al, [esi+ebx]
+        add [edi+ebx], al
+        inc ebx
+        cmp ebx, diff
+        jb dsublp1
+dsubgo:
+        mov ecx, FullLength
+        mov edx, ecx
+        sub edx, ebx                  // subtract alignment fix
+        and edx, 0x00000007           // calc bytes over mult of 8
+        sub ecx, edx                  // drop over bytes from length
+        mov MMXLength, ecx
+   } // end _asm block
+
+   // Now do the math for the rest of the row
+   switch ( bpp )
+   {
+        case 3:
+        {
+         ActiveMask.use  = 0x0000ffffff000000;
+         ShiftBpp.use = 24;       // == 3 * 8
+         ShiftRem.use  = 40;      // == 64 - 24
+         _asm {
+            mov edi, row
+            movq mm7, ActiveMask  // Load ActiveMask for 2nd active byte group
+            mov esi, edi              // lp = row
+            add edi, bpp          // rp = row + bpp
+            movq mm6, mm7
+            mov ebx, diff
+            psllq mm6, ShiftBpp   // Move mask in mm6 to cover 3rd active
+                                  // byte group
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dsub3lp:
+            psrlq mm1, ShiftRem   // Shift data for adding 1st bpp bytes
+                          // no need for mask; shift clears inactive bytes
+            // Add 1st active group
+            movq mm0, [edi+ebx]
+            paddb mm0, mm1
+            // Add 2nd active group
+            movq mm1, mm0         // mov updated Raws to mm1
+            psllq mm1, ShiftBpp   // shift data to position correctly
+            pand mm1, mm7         // mask to use only 2nd active group
+            paddb mm0, mm1
+            // Add 3rd active group
+            movq mm1, mm0         // mov updated Raws to mm1
+            psllq mm1, ShiftBpp   // shift data to position correctly
+            pand mm1, mm6         // mask to use only 3rd active group
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0     // Write updated Raws back to array
+            // Prep for doing 1st add at top of loop
+            movq mm1, mm0
+            jb dsub3lp
+         } // end _asm block
+      }
+      break;
+
+      case 1:
+      {
+         // Placed here just in case this is a duplicate of the
+         // non-MMX code for the SUB filter in png_read_filter_row below
+         //
+         //         png_bytep rp;
+         //         png_bytep lp;
+         //         png_uint_32 i;
+         //         bpp = (row_info->pixel_depth + 7) >> 3;
+         //         for (i = (png_uint_32)bpp, rp = row + bpp, lp = row;
+         //            i < row_info->rowbytes; i++, rp++, lp++)
+         //      {
+         //            *rp = (png_byte)(((int)(*rp) + (int)(*lp)) & 0xff);
+         //      }
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            cmp ebx, FullLength
+            jnb dsub1end
+            mov esi, edi          // lp = row
+            xor eax, eax
+            add edi, bpp      // rp = row + bpp
+dsub1lp:
+            mov al, [esi+ebx]
+            add [edi+ebx], al
+            inc ebx
+            cmp ebx, FullLength
+            jb dsub1lp
+dsub1end:
+         } // end _asm block
+      }
+      return;
+
+      case 6:
+      case 7:
+      case 4:
+      case 5:
+      {
+         ShiftBpp.use = bpp << 3;
+         ShiftRem.use = 64 - ShiftBpp.use;
+         _asm {
+            mov edi, row
+            mov ebx, diff
+            mov esi, edi               // lp = row
+            add edi, bpp           // rp = row + bpp
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dsub4lp:
+            psrlq mm1, ShiftRem // Shift data for adding 1st bpp bytes
+                          // no need for mask; shift clears inactive bytes
+            movq mm0, [edi+ebx]
+            paddb mm0, mm1
+            // Add 2nd active group
+            movq mm1, mm0          // mov updated Raws to mm1
+            psllq mm1, ShiftBpp    // shift data to position correctly
+                                   // there is no need for any mask
+                                   // since shift clears inactive bits/bytes
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0
+            movq mm1, mm0          // Prep for doing 1st add at top of loop
+            jb dsub4lp
+         } // end _asm block
+      }
+      break;
+
+      case 2:
+      {
+         ActiveMask.use  = 0x00000000ffff0000;
+         ShiftBpp.use = 16;       // == 2 * 8
+         ShiftRem.use = 48;       // == 64 - 16
+         _asm {
+            movq mm7, ActiveMask  // Load ActiveMask for 2nd active byte group
+            mov ebx, diff
+            movq mm6, mm7
+            mov edi, row
+            psllq mm6, ShiftBpp     // Move mask in mm6 to cover 3rd active
+                                    //  byte group
+            mov esi, edi            // lp = row
+            movq mm5, mm6
+            add edi, bpp            // rp = row + bpp
+            psllq mm5, ShiftBpp     // Move mask in mm5 to cover 4th active
+                                    //  byte group
+            // PRIME the pump (load the first Raw(x-bpp) data set
+            movq mm1, [edi+ebx-8]
+dsub2lp:
+            // Add 1st active group
+            psrlq mm1, ShiftRem     // Shift data for adding 1st bpp bytes
+                                    // no need for mask; shift clears inactive
+                                    //  bytes
+            movq mm0, [edi+ebx]
+            paddb mm0, mm1
+            // Add 2nd active group
+            movq mm1, mm0           // mov updated Raws to mm1
+            psllq mm1, ShiftBpp     // shift data to position correctly
+            pand mm1, mm7           // mask to use only 2nd active group
+            paddb mm0, mm1
+            // Add 3rd active group
+            movq mm1, mm0           // mov updated Raws to mm1
+            psllq mm1, ShiftBpp     // shift data to position correctly
+            pand mm1, mm6           // mask to use only 3rd active group
+            paddb mm0, mm1
+            // Add 4th active group
+            movq mm1, mm0           // mov updated Raws to mm1
+            psllq mm1, ShiftBpp     // shift data to position correctly
+            pand mm1, mm5           // mask to use only 4th active group
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0   // Write updated Raws back to array
+            movq mm1, mm0           // Prep for doing 1st add at top of loop
+            jb dsub2lp
+         } // end _asm block
+      }
+      break;
+      case 8:
+      {
+         _asm {
+            mov edi, row
+            mov ebx, diff
+            mov esi, edi            // lp = row
+            add edi, bpp            // rp = row + bpp
+            mov ecx, MMXLength
+            movq mm7, [edi+ebx-8]   // PRIME the pump (load the first
+                                    // Raw(x-bpp) data set
+            and ecx, 0x0000003f     // calc bytes over mult of 64
+dsub8lp:
+            movq mm0, [edi+ebx]     // Load Sub(x) for 1st 8 bytes
+            paddb mm0, mm7
+            movq mm1, [edi+ebx+8]   // Load Sub(x) for 2nd 8 bytes
+            movq [edi+ebx], mm0    // Write Raw(x) for 1st 8 bytes
+                                   // Now mm0 will be used as Raw(x-bpp) for
+                                   // the 2nd group of 8 bytes.  This will be
+                                   // repeated for each group of 8 bytes with
+                                   // the 8th group being used as the Raw(x-bpp)
+                                   // for the 1st group of the next loop.
+            paddb mm1, mm0
+            movq mm2, [edi+ebx+16]  // Load Sub(x) for 3rd 8 bytes
+            movq [edi+ebx+8], mm1   // Write Raw(x) for 2nd 8 bytes
+            paddb mm2, mm1
+            movq mm3, [edi+ebx+24]  // Load Sub(x) for 4th 8 bytes
+            movq [edi+ebx+16], mm2  // Write Raw(x) for 3rd 8 bytes
+            paddb mm3, mm2
+            movq mm4, [edi+ebx+32]  // Load Sub(x) for 5th 8 bytes
+            movq [edi+ebx+24], mm3  // Write Raw(x) for 4th 8 bytes
+            paddb mm4, mm3
+            movq mm5, [edi+ebx+40]  // Load Sub(x) for 6th 8 bytes
+            movq [edi+ebx+32], mm4  // Write Raw(x) for 5th 8 bytes
+            paddb mm5, mm4
+            movq mm6, [edi+ebx+48]  // Load Sub(x) for 7th 8 bytes
+            movq [edi+ebx+40], mm5  // Write Raw(x) for 6th 8 bytes
+            paddb mm6, mm5
+            movq mm7, [edi+ebx+56]  // Load Sub(x) for 8th 8 bytes
+            movq [edi+ebx+48], mm6  // Write Raw(x) for 7th 8 bytes
+            add ebx, 64
+            paddb mm7, mm6
+            cmp ebx, ecx
+            movq [edi+ebx-8], mm7   // Write Raw(x) for 8th 8 bytes
+            jb dsub8lp
+            cmp ebx, MMXLength
+            jnb dsub8lt8
+dsub8lpA:
+            movq mm0, [edi+ebx]
+            add ebx, 8
+            paddb mm0, mm7
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0   // use -8 to offset early add to ebx
+            movq mm7, mm0           // Move calculated Raw(x) data to mm1 to
+                                    // be the new Raw(x-bpp) for the next loop
+            jb dsub8lpA
+dsub8lt8:
+         } // end _asm block
+      }
+      break;
+
+      default:                // bpp greater than 8 bytes
+      {
+         _asm {
+            mov ebx, diff
+            mov edi, row
+            mov esi, edi           // lp = row
+            add edi, bpp           // rp = row + bpp
+dsubAlp:
+            movq mm0, [edi+ebx]
+            movq mm1, [esi+ebx]
+            add ebx, 8
+            paddb mm0, mm1
+            cmp ebx, MMXLength
+            movq [edi+ebx-8], mm0  // mov does not affect flags; -8 to offset
+                                   //  add ebx
+            jb dsubAlp
+         } // end _asm block
+      }
+      break;
+
+   } // end switch ( bpp )
+
+   _asm {
+        mov ebx, MMXLength
+        mov edi, row
+        cmp ebx, FullLength
+        jnb dsubend
+        mov esi, edi               // lp = row
+        xor eax, eax
+        add edi, bpp               // rp = row + bpp
+dsublp2:
+        mov al, [esi+ebx]
+        add [edi+ebx], al
+        inc ebx
+        cmp ebx, FullLength
+        jb dsublp2
+dsubend:
+        emms             // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+// Optimized code for PNG Up filter decoder
+void /* PRIVATE */
+png_read_filter_row_mmx_up(png_row_infop row_info, png_bytep row,
+   png_bytep prev_row)
+{
+   png_uint_32 len;
+   len  = row_info->rowbytes;       // # of bytes to filter
+   _asm {
+      mov edi, row
+      // get # of bytes to alignment
+      mov ecx, edi
+      xor ebx, ebx
+      add ecx, 0x7
+      xor eax, eax
+      and ecx, 0xfffffff8
+      mov esi, prev_row
+      sub ecx, edi
+      jz dupgo
+      // fix alignment
+duplp1:
+      mov al, [edi+ebx]
+      add al, [esi+ebx]
+      inc ebx
+      cmp ebx, ecx
+      mov [edi + ebx-1], al  // mov does not affect flags; -1 to offset inc ebx
+      jb duplp1
+dupgo:
+      mov ecx, len
+      mov edx, ecx
+      sub edx, ebx                  // subtract alignment fix
+      and edx, 0x0000003f           // calc bytes over mult of 64
+      sub ecx, edx                  // drop over bytes from length
+      // Unrolled loop - use all MMX registers and interleave to reduce
+      // number of branch instructions (loops) and reduce partial stalls
+duploop:
+      movq mm1, [esi+ebx]
+      movq mm0, [edi+ebx]
+      movq mm3, [esi+ebx+8]
+      paddb mm0, mm1
+      movq mm2, [edi+ebx+8]
+      movq [edi+ebx], mm0
+      paddb mm2, mm3
+      movq mm5, [esi+ebx+16]
+      movq [edi+ebx+8], mm2
+      movq mm4, [edi+ebx+16]
+      movq mm7, [esi+ebx+24]
+      paddb mm4, mm5
+      movq mm6, [edi+ebx+24]
+      movq [edi+ebx+16], mm4
+      paddb mm6, mm7
+      movq mm1, [esi+ebx+32]
+      movq [edi+ebx+24], mm6
+      movq mm0, [edi+ebx+32]
+      movq mm3, [esi+ebx+40]
+      paddb mm0, mm1
+      movq mm2, [edi+ebx+40]
+      movq [edi+ebx+32], mm0
+      paddb mm2, mm3
+      movq mm5, [esi+ebx+48]
+      movq [edi+ebx+40], mm2
+      movq mm4, [edi+ebx+48]
+      movq mm7, [esi+ebx+56]
+      paddb mm4, mm5
+      movq mm6, [edi+ebx+56]
+      movq [edi+ebx+48], mm4
+      add ebx, 64
+      paddb mm6, mm7
+      cmp ebx, ecx
+      movq [edi+ebx-8], mm6 // (+56)movq does not affect flags;
+                                     // -8 to offset add ebx
+      jb duploop
+
+      cmp edx, 0                     // Test for bytes over mult of 64
+      jz dupend
+
+
+      // 2 lines added by lcreeve at netins.net
+      // (mail 11 Jul 98 in png-implement list)
+      cmp edx, 8 //test for less than 8 bytes
+      jb duplt8
+
+
+      add ecx, edx
+      and edx, 0x00000007           // calc bytes over mult of 8
+      sub ecx, edx                  // drop over bytes from length
+      jz duplt8
+      // Loop using MMX registers mm0 & mm1 to update 8 bytes simultaneously
+duplpA:
+      movq mm1, [esi+ebx]
+      movq mm0, [edi+ebx]
+      add ebx, 8
+      paddb mm0, mm1
+      cmp ebx, ecx
+      movq [edi+ebx-8], mm0 // movq does not affect flags; -8 to offset add ebx
+      jb duplpA
+      cmp edx, 0            // Test for bytes over mult of 8
+      jz dupend
+duplt8:
+      xor eax, eax
+      add ecx, edx          // move over byte count into counter
+      // Loop using x86 registers to update remaining bytes
+duplp2:
+      mov al, [edi + ebx]
+      add al, [esi + ebx]
+      inc ebx
+      cmp ebx, ecx
+      mov [edi + ebx-1], al // mov does not affect flags; -1 to offset inc ebx
+      jb duplp2
+dupend:
+      // Conversion of filtered row completed
+      emms          // End MMX instructions; prep for possible FP instrs.
+   } // end _asm block
+}
+
+
+// Optimized png_read_filter_row routines
+void /* PRIVATE */
+png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep
+   row, png_bytep prev_row, int filter)
+{
+#ifdef PNG_DEBUG
+   char filnm[10];
+#endif
+
+   if (mmx_supported == 2) {
+#if !defined(PNG_1_0_X)
+       /* this should have happened in png_init_mmx_flags() already */
+       png_warning(png_ptr, "asm_flags may not have been initialized");
+#endif
+       png_mmx_support();
+   }
+
+#ifdef PNG_DEBUG
+   png_debug(1, "in png_read_filter_row\n");
+   switch (filter)
+   {
+      case 0: png_snprintf(filnm, 10, "none");
+         break;
+#if !defined(PNG_1_0_X)
+      case 1: png_snprintf(filnm, 10, "sub-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "MMX" : "x86");
+         break;
+      case 2: png_snprintf(filnm, 10, "up-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "MMX" : "x86");
+         break;
+      case 3: png_snprintf(filnm, 10, "avg-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "MMX" : "x86");
+         break;
+      case 4: png_snprintf(filnm, 10, "Paeth-%s",
+        (png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "MMX":"x86");
+         break;
+#else
+      case 1: png_snprintf(filnm, 10, "sub");
+         break;
+      case 2: png_snprintf(filnm, 10, "up");
+         break;
+      case 3: png_snprintf(filnm, 10, "avg");
+         break;
+      case 4: png_snprintf(filnm, 10, "Paeth");
+         break;
+#endif
+      default: png_snprintf(filnm, 10, "unknw");
+         break;
+   }
+   png_debug2(0,"row=%5d, %s, ", png_ptr->row_number, filnm);
+   png_debug2(0, "pd=%2d, b=%d, ", (int)row_info->pixel_depth,
+      (int)((row_info->pixel_depth + 7) >> 3));
+   png_debug1(0,"len=%8d, ", row_info->rowbytes);
+#endif /* PNG_DEBUG */
+
+   switch (filter)
+   {
+      case PNG_FILTER_VALUE_NONE:
+         break;
+
+      case PNG_FILTER_VALUE_SUB:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_sub(row_info, row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_bytep rp = row + bpp;
+            png_bytep lp = row;
+
+            for (i = bpp; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      case PNG_FILTER_VALUE_UP:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_up(row_info, row, prev_row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_uint_32 istop = row_info->rowbytes;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+
+            for (i = 0; i < istop; ++i)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      case PNG_FILTER_VALUE_AVG:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_avg(row_info, row, prev_row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop = row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++) >> 1)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) +
+                  ((int)(*pp++ + *lp++) >> 1)) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      case PNG_FILTER_VALUE_PAETH:
+      {
+#if !defined(PNG_1_0_X)
+         if ((png_ptr->asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) &&
+             (row_info->pixel_depth >= png_ptr->mmx_bitdepth_threshold) &&
+             (row_info->rowbytes >= png_ptr->mmx_rowbytes_threshold))
+#else
+         if (mmx_supported)
+#endif
+         {
+            png_read_filter_row_mmx_paeth(row_info, row, prev_row);
+         }
+         else
+         {
+            png_uint_32 i;
+            png_bytep rp = row;
+            png_bytep pp = prev_row;
+            png_bytep lp = row;
+            png_bytep cp = prev_row;
+            png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3;
+            png_uint_32 istop=row_info->rowbytes - bpp;
+
+            for (i = 0; i < bpp; i++)
+            {
+               *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff);
+               rp++;
+            }
+
+            for (i = 0; i < istop; i++)   // use leftover rp,pp
+            {
+               int a, b, c, pa, pb, pc, p;
+
+               a = *lp++;
+               b = *pp++;
+               c = *cp++;
+
+               p = b - c;
+               pc = a - c;
+
+#ifdef PNG_USE_ABS
+               pa = abs(p);
+               pb = abs(pc);
+               pc = abs(p + pc);
+#else
+               pa = p < 0 ? -p : p;
+               pb = pc < 0 ? -pc : pc;
+               pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+               /*
+                  if (pa <= pb && pa <= pc)
+                     p = a;
+                  else if (pb <= pc)
+                     p = b;
+                  else
+                     p = c;
+                */
+
+               p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+               *rp = (png_byte)(((int)(*rp) + p) & 0xff);
+               rp++;
+            }
+         }
+         break;
+      }
+
+      default:
+         png_warning(png_ptr, "Ignoring bad row filter type");
+         *row=0;
+         break;
+   }
+}
+
+#endif /* PNG_MMX_CODE_SUPPORTED && PNG_USE_PNGVCRD */
diff --git a/distrib/libpng-1.2.19/pngwio.c b/distrib/libpng-1.2.19/pngwio.c
new file mode 100644
index 0000000..371a4fa
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngwio.c
@@ -0,0 +1,234 @@
+
+/* pngwio.c - functions for data output
+ *
+ * Last changed in libpng 1.2.13 November 13, 2006
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2006 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ *
+ * This file provides a location for all output.  Users who need
+ * special handling are expected to write functions that have the same
+ * arguments as these and perform similar functions, but that possibly
+ * use different output methods.  Note that you shouldn't change these
+ * functions, but rather write replacement functions and then change
+ * them at run time with png_set_write_fn(...).
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Write the data to whatever output you are using.  The default routine
+   writes to a file pointer.  Note that this routine sometimes gets called
+   with very small lengths, so you should implement some kind of simple
+   buffering if you are using unbuffered writes.  This should never be asked
+   to write more than 64K on a 16 bit machine.  */
+
+void /* PRIVATE */
+png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   if (png_ptr->write_data_fn != NULL )
+      (*(png_ptr->write_data_fn))(png_ptr, data, length);
+   else
+      png_error(png_ptr, "Call to NULL write function");
+}
+
+#if !defined(PNG_NO_STDIO)
+/* This is the function that does the actual writing of data.  If you are
+   not writing to a standard C stream, you should create a replacement
+   write_data function and use it at run time with png_set_write_fn(), rather
+   than changing the library. */
+#ifndef USE_FAR_KEYWORD
+void PNGAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+
+   if(png_ptr == NULL) return;
+#if defined(_WIN32_WCE)
+   if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) )
+      check = 0;
+#else
+   check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr));
+#endif
+   if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+#else
+/* this is the model-independent version. Since the standard I/O library
+   can't handle far buffers in the medium and small models, we have to copy
+   the data.
+*/
+
+#define NEAR_BUF_SIZE 1024
+#define MIN(a,b) (a <= b ? a : b)
+
+void PNGAPI
+png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   png_uint_32 check;
+   png_byte *near_data;  /* Needs to be "png_byte *" instead of "png_bytep" */
+   png_FILE_p io_ptr;
+
+   if(png_ptr == NULL) return;
+   /* Check if data really is near. If so, use usual code. */
+   near_data = (png_byte *)CVT_PTR_NOCHECK(data);
+   io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr);
+   if ((png_bytep)near_data == data)
+   {
+#if defined(_WIN32_WCE)
+      if ( !WriteFile(io_ptr, near_data, length, &check, NULL) )
+         check = 0;
+#else
+      check = fwrite(near_data, 1, length, io_ptr);
+#endif
+   }
+   else
+   {
+      png_byte buf[NEAR_BUF_SIZE];
+      png_size_t written, remaining, err;
+      check = 0;
+      remaining = length;
+      do
+      {
+         written = MIN(NEAR_BUF_SIZE, remaining);
+         png_memcpy(buf, data, written); /* copy far buffer to near buffer */
+#if defined(_WIN32_WCE)
+         if ( !WriteFile(io_ptr, buf, written, &err, NULL) )
+            err = 0;
+#else
+         err = fwrite(buf, 1, written, io_ptr);
+#endif
+         if (err != written)
+            break;
+         else
+            check += err;
+         data += written;
+         remaining -= written;
+      }
+      while (remaining != 0);
+   }
+   if (check != length)
+      png_error(png_ptr, "Write Error");
+}
+
+#endif
+#endif
+
+/* This function is called to output any data pending writing (normally
+   to disk).  After png_flush is called, there should be no data pending
+   writing in any buffers. */
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+void /* PRIVATE */
+png_flush(png_structp png_ptr)
+{
+   if (png_ptr->output_flush_fn != NULL)
+      (*(png_ptr->output_flush_fn))(png_ptr);
+}
+
+#if !defined(PNG_NO_STDIO)
+void PNGAPI
+png_default_flush(png_structp png_ptr)
+{
+#if !defined(_WIN32_WCE)
+   png_FILE_p io_ptr;
+#endif
+   if(png_ptr == NULL) return;
+#if !defined(_WIN32_WCE)
+   io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr));
+   if (io_ptr != NULL)
+      fflush(io_ptr);
+#endif
+}
+#endif
+#endif
+
+/* This function allows the application to supply new output functions for
+   libpng if standard C streams aren't being used.
+
+   This function takes as its arguments:
+   png_ptr       - pointer to a png output data structure
+   io_ptr        - pointer to user supplied structure containing info about
+                   the output functions.  May be NULL.
+   write_data_fn - pointer to a new output function that takes as its
+                   arguments a pointer to a png_struct, a pointer to
+                   data to be written, and a 32-bit unsigned int that is
+                   the number of bytes to be written.  The new write
+                   function should call png_error(png_ptr, "Error msg")
+                   to exit and output any fatal error messages.
+   flush_data_fn - pointer to a new flush function that takes as its
+                   arguments a pointer to a png_struct.  After a call to
+                   the flush function, there should be no data in any buffers
+                   or pending transmission.  If the output method doesn't do
+                   any buffering of ouput, a function prototype must still be
+                   supplied although it doesn't have to do anything.  If
+                   PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile
+                   time, output_flush_fn will be ignored, although it must be
+                   supplied for compatibility. */
+void PNGAPI
+png_set_write_fn(png_structp png_ptr, png_voidp io_ptr,
+   png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)
+{
+   if(png_ptr == NULL) return;
+   png_ptr->io_ptr = io_ptr;
+
+#if !defined(PNG_NO_STDIO)
+   if (write_data_fn != NULL)
+      png_ptr->write_data_fn = write_data_fn;
+   else
+      png_ptr->write_data_fn = png_default_write_data;
+#else
+   png_ptr->write_data_fn = write_data_fn;
+#endif
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+#if !defined(PNG_NO_STDIO)
+   if (output_flush_fn != NULL)
+      png_ptr->output_flush_fn = output_flush_fn;
+   else
+      png_ptr->output_flush_fn = png_default_flush;
+#else
+   png_ptr->output_flush_fn = output_flush_fn;
+#endif
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+   /* It is an error to read while writing a png file */
+   if (png_ptr->read_data_fn != NULL)
+   {
+      png_ptr->read_data_fn = NULL;
+      png_warning(png_ptr,
+         "Attempted to set both read_data_fn and write_data_fn in");
+      png_warning(png_ptr,
+         "the same structure.  Resetting read_data_fn to NULL.");
+   }
+}
+
+#if defined(USE_FAR_KEYWORD)
+#if defined(_MSC_VER)
+void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check)
+{
+   void *near_ptr;
+   void FAR *far_ptr;
+   FP_OFF(near_ptr) = FP_OFF(ptr);
+   far_ptr = (void FAR *)near_ptr;
+   if(check != 0)
+      if(FP_SEG(ptr) != FP_SEG(far_ptr))
+         png_error(png_ptr,"segment lost in conversion");
+   return(near_ptr);
+}
+#  else
+void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check)
+{
+   void *near_ptr;
+   void FAR *far_ptr;
+   near_ptr = (void FAR *)ptr;
+   far_ptr = (void FAR *)near_ptr;
+   if(check != 0)
+      if(far_ptr != ptr)
+         png_error(png_ptr,"segment lost in conversion");
+   return(near_ptr);
+}
+#   endif
+#   endif
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngwrite.c b/distrib/libpng-1.2.19/pngwrite.c
new file mode 100644
index 0000000..8d5b98a
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngwrite.c
@@ -0,0 +1,1530 @@
+
+/* pngwrite.c - general routines to write a PNG file
+ *
+ * Last changed in libpng 1.2.15 January 5, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+/* get internal access to png.h */
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Writes all the PNG information.  This is the suggested way to use the
+ * library.  If you have a new chunk to add, make a function to write it,
+ * and put it in the correct location here.  If you want the chunk written
+ * after the image data, put it in png_write_end().  I strongly encourage
+ * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing
+ * the chunk, as that will keep the code from breaking if you want to just
+ * write a plain PNG file.  If you have long comments, I suggest writing
+ * them in png_write_end(), and compressing them.
+ */
+void PNGAPI
+png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_write_info_before_PLTE\n");
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+   {
+   png_write_sig(png_ptr); /* write PNG signature */
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted))
+   {
+      png_warning(png_ptr,"MNG features are not allowed in a PNG datastream");
+      png_ptr->mng_features_permitted=0;
+   }
+#endif
+   /* write IHDR information. */
+   png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height,
+      info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type,
+      info_ptr->filter_type,
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+      info_ptr->interlace_type);
+#else
+      0);
+#endif
+   /* the rest of these check to see if the valid field has the appropriate
+      flag set, and if it does, writes the chunk. */
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_gAMA)
+   {
+#  ifdef PNG_FLOATING_POINT_SUPPORTED
+      png_write_gAMA(png_ptr, info_ptr->gamma);
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma);
+#  endif
+#endif
+   }
+#endif
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sRGB)
+      png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent);
+#endif
+#if defined(PNG_WRITE_iCCP_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_iCCP)
+      png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE,
+                     info_ptr->iccp_profile, (int)info_ptr->iccp_proflen);
+#endif
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sBIT)
+      png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type);
+#endif
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_cHRM)
+   {
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+      png_write_cHRM(png_ptr,
+         info_ptr->x_white, info_ptr->y_white,
+         info_ptr->x_red, info_ptr->y_red,
+         info_ptr->x_green, info_ptr->y_green,
+         info_ptr->x_blue, info_ptr->y_blue);
+#else
+#  ifdef PNG_FIXED_POINT_SUPPORTED
+      png_write_cHRM_fixed(png_ptr,
+         info_ptr->int_x_white, info_ptr->int_y_white,
+         info_ptr->int_x_red, info_ptr->int_y_red,
+         info_ptr->int_x_green, info_ptr->int_y_green,
+         info_ptr->int_x_blue, info_ptr->int_y_blue);
+#  endif
+#endif
+   }
+#endif
+#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
+   if (info_ptr->unknown_chunks_num)
+   {
+       png_unknown_chunk *up;
+
+       png_debug(5, "writing extra chunks\n");
+
+       for (up = info_ptr->unknown_chunks;
+            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+            up++)
+       {
+         int keep=png_handle_as_unknown(png_ptr, up->name);
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+            up->location && !(up->location & PNG_HAVE_PLTE) &&
+            !(up->location & PNG_HAVE_IDAT) &&
+            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+         {
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+       }
+   }
+#endif
+      png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE;
+   }
+}
+
+void PNGAPI
+png_write_info(png_structp png_ptr, png_infop info_ptr)
+{
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+   int i;
+#endif
+
+   png_debug(1, "in png_write_info\n");
+
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+
+   png_write_info_before_PLTE(png_ptr, info_ptr);
+
+   if (info_ptr->valid & PNG_INFO_PLTE)
+      png_write_PLTE(png_ptr, info_ptr->palette,
+         (png_uint_32)info_ptr->num_palette);
+   else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+      png_error(png_ptr, "Valid palette required for paletted images");
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_tRNS)
+      {
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+         /* invert the alpha channel (in tRNS) */
+         if ((png_ptr->transformations & PNG_INVERT_ALPHA) &&
+            info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+         {
+            int j;
+            for (j=0; j<(int)info_ptr->num_trans; j++)
+               info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]);
+         }
+#endif
+      png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values),
+         info_ptr->num_trans, info_ptr->color_type);
+      }
+#endif
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_bKGD)
+      png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type);
+#endif
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_hIST)
+      png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette);
+#endif
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_oFFs)
+      png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset,
+         info_ptr->offset_unit_type);
+#endif
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pCAL)
+      png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0,
+         info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams,
+         info_ptr->pcal_units, info_ptr->pcal_params);
+#endif
+#if defined(PNG_WRITE_sCAL_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sCAL)
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
+      png_write_sCAL(png_ptr, (int)info_ptr->scal_unit,
+          info_ptr->scal_pixel_width, info_ptr->scal_pixel_height);
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+      png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit,
+          info_ptr->scal_s_width, info_ptr->scal_s_height);
+#else
+      png_warning(png_ptr,
+          "png_write_sCAL not supported; sCAL chunk not written.");
+#endif
+#endif
+#endif
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_pHYs)
+      png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit,
+         info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type);
+#endif
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_tIME)
+   {
+      png_write_tIME(png_ptr, &(info_ptr->mod_time));
+      png_ptr->mode |= PNG_WROTE_tIME;
+   }
+#endif
+#if defined(PNG_WRITE_sPLT_SUPPORTED)
+   if (info_ptr->valid & PNG_INFO_sPLT)
+     for (i = 0; i < (int)info_ptr->splt_palettes_num; i++)
+       png_write_sPLT(png_ptr, info_ptr->splt_palettes + i);
+#endif
+#if defined(PNG_WRITE_TEXT_SUPPORTED)
+   /* Check to see if we need to write text chunks */
+   for (i = 0; i < info_ptr->num_text; i++)
+   {
+      png_debug2(2, "Writing header text chunk %d, type %d\n", i,
+         info_ptr->text[i].compression);
+      /* an internationalized chunk? */
+      if (info_ptr->text[i].compression > 0)
+      {
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+          /* write international chunk */
+          png_write_iTXt(png_ptr,
+                         info_ptr->text[i].compression,
+                         info_ptr->text[i].key,
+                         info_ptr->text[i].lang,
+                         info_ptr->text[i].lang_key,
+                         info_ptr->text[i].text);
+#else
+          png_warning(png_ptr, "Unable to write international text");
+#endif
+          /* Mark this chunk as written */
+          info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+      }
+      /* If we want a compressed text chunk */
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt)
+      {
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+         /* write compressed chunk */
+         png_write_zTXt(png_ptr, info_ptr->text[i].key,
+            info_ptr->text[i].text, 0,
+            info_ptr->text[i].compression);
+#else
+         png_warning(png_ptr, "Unable to write compressed text");
+#endif
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+      }
+      else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+      {
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+         /* write uncompressed chunk */
+         png_write_tEXt(png_ptr, info_ptr->text[i].key,
+                         info_ptr->text[i].text,
+                         0);
+#else
+         png_warning(png_ptr, "Unable to write uncompressed text");
+#endif
+         /* Mark this chunk as written */
+         info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+      }
+   }
+#endif
+#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
+   if (info_ptr->unknown_chunks_num)
+   {
+       png_unknown_chunk *up;
+
+       png_debug(5, "writing extra chunks\n");
+
+       for (up = info_ptr->unknown_chunks;
+            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+            up++)
+       {
+         int keep=png_handle_as_unknown(png_ptr, up->name);
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+            up->location && (up->location & PNG_HAVE_PLTE) &&
+            !(up->location & PNG_HAVE_IDAT) &&
+            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+         {
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+       }
+   }
+#endif
+}
+
+/* Writes the end of the PNG file.  If you don't want to write comments or
+ * time information, you can pass NULL for info.  If you already wrote these
+ * in png_write_info(), do not write them again here.  If you have long
+ * comments, I suggest writing them here, and compressing them.
+ */
+void PNGAPI
+png_write_end(png_structp png_ptr, png_infop info_ptr)
+{
+   png_debug(1, "in png_write_end\n");
+   if (png_ptr == NULL)
+      return;
+   if (!(png_ptr->mode & PNG_HAVE_IDAT))
+      png_error(png_ptr, "No IDATs written into file");
+
+   /* see if user wants us to write information chunks */
+   if (info_ptr != NULL)
+   {
+#if defined(PNG_WRITE_TEXT_SUPPORTED)
+      int i; /* local index variable */
+#endif
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+      /* check to see if user has supplied a time chunk */
+      if ((info_ptr->valid & PNG_INFO_tIME) &&
+         !(png_ptr->mode & PNG_WROTE_tIME))
+         png_write_tIME(png_ptr, &(info_ptr->mod_time));
+#endif
+#if defined(PNG_WRITE_TEXT_SUPPORTED)
+      /* loop through comment chunks */
+      for (i = 0; i < info_ptr->num_text; i++)
+      {
+         png_debug2(2, "Writing trailer text chunk %d, type %d\n", i,
+            info_ptr->text[i].compression);
+         /* an internationalized chunk? */
+         if (info_ptr->text[i].compression > 0)
+         {
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+             /* write international chunk */
+             png_write_iTXt(png_ptr,
+                         info_ptr->text[i].compression,
+                         info_ptr->text[i].key,
+                         info_ptr->text[i].lang,
+                         info_ptr->text[i].lang_key,
+                         info_ptr->text[i].text);
+#else
+             png_warning(png_ptr, "Unable to write international text");
+#endif
+             /* Mark this chunk as written */
+             info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+         }
+         else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt)
+         {
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+            /* write compressed chunk */
+            png_write_zTXt(png_ptr, info_ptr->text[i].key,
+               info_ptr->text[i].text, 0,
+               info_ptr->text[i].compression);
+#else
+            png_warning(png_ptr, "Unable to write compressed text");
+#endif
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR;
+         }
+         else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE)
+         {
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+            /* write uncompressed chunk */
+            png_write_tEXt(png_ptr, info_ptr->text[i].key,
+               info_ptr->text[i].text, 0);
+#else
+            png_warning(png_ptr, "Unable to write uncompressed text");
+#endif
+
+            /* Mark this chunk as written */
+            info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR;
+         }
+      }
+#endif
+#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED)
+   if (info_ptr->unknown_chunks_num)
+   {
+       png_unknown_chunk *up;
+
+       png_debug(5, "writing extra chunks\n");
+
+       for (up = info_ptr->unknown_chunks;
+            up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num;
+            up++)
+       {
+         int keep=png_handle_as_unknown(png_ptr, up->name);
+         if (keep != PNG_HANDLE_CHUNK_NEVER &&
+            up->location && (up->location & PNG_AFTER_IDAT) &&
+            ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS ||
+            (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS)))
+         {
+            png_write_chunk(png_ptr, up->name, up->data, up->size);
+         }
+       }
+   }
+#endif
+   }
+
+   png_ptr->mode |= PNG_AFTER_IDAT;
+
+   /* write end of PNG file */
+   png_write_IEND(png_ptr);
+}
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+#if !defined(_WIN32_WCE)
+/* "time.h" functions are not supported on WindowsCE */
+void PNGAPI
+png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime)
+{
+   png_debug(1, "in png_convert_from_struct_tm\n");
+   ptime->year = (png_uint_16)(1900 + ttime->tm_year);
+   ptime->month = (png_byte)(ttime->tm_mon + 1);
+   ptime->day = (png_byte)ttime->tm_mday;
+   ptime->hour = (png_byte)ttime->tm_hour;
+   ptime->minute = (png_byte)ttime->tm_min;
+   ptime->second = (png_byte)ttime->tm_sec;
+}
+
+void PNGAPI
+png_convert_from_time_t(png_timep ptime, time_t ttime)
+{
+   struct tm *tbuf;
+
+   png_debug(1, "in png_convert_from_time_t\n");
+   tbuf = gmtime(&ttime);
+   png_convert_from_struct_tm(ptime, tbuf);
+}
+#endif
+#endif
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+png_structp PNGAPI
+png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn)
+{
+#ifdef PNG_USER_MEM_SUPPORTED
+   return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn,
+      warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL));
+}
+
+/* Alternate initialize png_ptr structure, and allocate any memory needed */
+png_structp PNGAPI
+png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr,
+   png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr,
+   png_malloc_ptr malloc_fn, png_free_ptr free_fn)
+{
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_structp png_ptr;
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   jmp_buf jmpbuf;
+#endif
+#endif
+   int i;
+   png_debug(1, "in png_create_write_struct\n");
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG,
+      (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr);
+#else
+   png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+#endif /* PNG_USER_MEM_SUPPORTED */
+   if (png_ptr == NULL)
+      return (NULL);
+
+#if !defined(PNG_1_0_X)
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+#ifdef PNG_MMX_CODE_SUPPORTED
+   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
+#endif
+#endif
+#endif /* PNG_1_0_X */
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+#else
+   if (setjmp(png_ptr->jmpbuf))
+#endif
+   {
+      png_free(png_ptr, png_ptr->zbuf);
+      png_ptr->zbuf=NULL;
+      png_destroy_struct(png_ptr);
+      return (NULL);
+   }
+#ifdef USE_FAR_KEYWORD
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#endif
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn);
+#endif /* PNG_USER_MEM_SUPPORTED */
+   png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn);
+
+   i=0;
+   do
+   {
+     if(user_png_ver[i] != png_libpng_ver[i])
+        png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+   } while (png_libpng_ver[i++]);
+
+   if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH)
+   {
+     /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so
+      * we must recompile any applications that use any older library version.
+      * For versions after libpng 1.0, we will be compatible, so we need
+      * only check the first digit.
+      */
+     if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] ||
+         (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) ||
+         (user_png_ver[0] == '0' && user_png_ver[2] < '9'))
+     {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+        char msg[80];
+        if (user_png_ver)
+        {
+          png_snprintf(msg, 80,
+             "Application was compiled with png.h from libpng-%.20s",
+             user_png_ver);
+          png_warning(png_ptr, msg);
+        }
+        png_snprintf(msg, 80,
+           "Application  is  running with png.c from libpng-%.20s",
+           png_libpng_ver);
+        png_warning(png_ptr, msg);
+#endif
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+        png_ptr->flags=0;
+#endif
+        png_error(png_ptr,
+           "Incompatible libpng version in application and library");
+     }
+   }
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+      (png_uint_32)png_ptr->zbuf_size);
+
+   png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
+      png_flush_ptr_NULL);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
+      1, png_doublep_NULL, png_doublep_NULL);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+/* Applications that neglect to set up their own setjmp() and then encounter
+   a png_error() will longjmp here.  Since the jmpbuf is then meaningless we
+   abort instead of returning. */
+#ifdef USE_FAR_KEYWORD
+   if (setjmp(jmpbuf))
+      PNG_ABORT();
+   png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf));
+#else
+   if (setjmp(png_ptr->jmpbuf))
+      PNG_ABORT();
+#endif
+#endif
+   return (png_ptr);
+}
+
+/* Initialize png_ptr structure, and allocate any memory needed */
+#if defined(PNG_1_0_X) || defined(PNG_1_2_X)
+/* Deprecated. */
+#undef png_write_init
+void PNGAPI
+png_write_init(png_structp png_ptr)
+{
+   /* We only come here via pre-1.0.7-compiled applications */
+   png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0);
+}
+
+void PNGAPI
+png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size, png_size_t png_info_size)
+{
+   /* We only come here via pre-1.0.12-compiled applications */
+   if(png_ptr == NULL) return;
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+   if(png_sizeof(png_struct) > png_struct_size ||
+      png_sizeof(png_info) > png_info_size)
+   {
+      char msg[80];
+      png_ptr->warning_fn=NULL;
+      if (user_png_ver)
+      {
+        png_snprintf(msg, 80,
+           "Application was compiled with png.h from libpng-%.20s",
+           user_png_ver);
+        png_warning(png_ptr, msg);
+      }
+      png_snprintf(msg, 80,
+         "Application  is  running with png.c from libpng-%.20s",
+         png_libpng_ver);
+      png_warning(png_ptr, msg);
+   }
+#endif
+   if(png_sizeof(png_struct) > png_struct_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+       "The png struct allocated by the application for writing is too small.");
+     }
+   if(png_sizeof(png_info) > png_info_size)
+     {
+       png_ptr->error_fn=NULL;
+#ifdef PNG_ERROR_NUMBERS_SUPPORTED
+       png_ptr->flags=0;
+#endif
+       png_error(png_ptr,
+       "The info struct allocated by the application for writing is too small.");
+     }
+   png_write_init_3(&png_ptr, user_png_ver, png_struct_size);
+}
+#endif /* PNG_1_0_X || PNG_1_2_X */
+
+
+void PNGAPI
+png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver,
+   png_size_t png_struct_size)
+{
+   png_structp png_ptr=*ptr_ptr;
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp; /* to save current jump buffer */
+#endif
+
+   int i = 0;
+
+   if (png_ptr == NULL)
+      return;
+
+   do
+   {
+     if (user_png_ver[i] != png_libpng_ver[i])
+     {
+#ifdef PNG_LEGACY_SUPPORTED
+       png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
+#else
+       png_ptr->warning_fn=NULL;
+       png_warning(png_ptr,
+     "Application uses deprecated png_write_init() and should be recompiled.");
+       break;
+#endif
+     }
+   } while (png_libpng_ver[i++]);
+
+   png_debug(1, "in png_write_init_3\n");
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* save jump buffer and error functions */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   if (png_sizeof(png_struct) > png_struct_size)
+     {
+       png_destroy_struct(png_ptr);
+       png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG);
+       *ptr_ptr = png_ptr;
+     }
+
+   /* reset all variables to 0 */
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+   /* added at libpng-1.2.6 */
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+   png_ptr->user_width_max=PNG_USER_WIDTH_MAX;
+   png_ptr->user_height_max=PNG_USER_HEIGHT_MAX;
+#endif
+
+#if !defined(PNG_1_0_X)
+#ifdef PNG_ASSEMBLER_CODE_SUPPORTED
+#ifdef PNG_MMX_CODE_SUPPORTED
+   png_init_mmx_flags(png_ptr);   /* 1.2.0 addition */
+#endif
+#endif
+#endif /* PNG_1_0_X */
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* restore jump buffer */
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+
+   png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL,
+      png_flush_ptr_NULL);
+
+   /* initialize zbuf - compression buffer */
+   png_ptr->zbuf_size = PNG_ZBUF_SIZE;
+   png_ptr->zbuf = (png_bytep)png_malloc(png_ptr,
+      (png_uint_32)png_ptr->zbuf_size);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT,
+      1, png_doublep_NULL, png_doublep_NULL);
+#endif
+}
+
+/* Write a few rows of image data.  If the image is interlaced,
+ * either you will have to write the 7 sub images, or, if you
+ * have called png_set_interlace_handling(), you will have to
+ * "write" the image seven times.
+ */
+void PNGAPI
+png_write_rows(png_structp png_ptr, png_bytepp row,
+   png_uint_32 num_rows)
+{
+   png_uint_32 i; /* row counter */
+   png_bytepp rp; /* row pointer */
+
+   png_debug(1, "in png_write_rows\n");
+
+   if (png_ptr == NULL)
+      return;
+
+   /* loop through the rows */
+   for (i = 0, rp = row; i < num_rows; i++, rp++)
+   {
+      png_write_row(png_ptr, *rp);
+   }
+}
+
+/* Write the image.  You only need to call this function once, even
+ * if you are writing an interlaced image.
+ */
+void PNGAPI
+png_write_image(png_structp png_ptr, png_bytepp image)
+{
+   png_uint_32 i; /* row index */
+   int pass, num_pass; /* pass variables */
+   png_bytepp rp; /* points to current row */
+
+   if (png_ptr == NULL)
+      return;
+
+   png_debug(1, "in png_write_image\n");
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* intialize interlace handling.  If image is not interlaced,
+      this will set pass to 1 */
+   num_pass = png_set_interlace_handling(png_ptr);
+#else
+   num_pass = 1;
+#endif
+   /* loop through passes */
+   for (pass = 0; pass < num_pass; pass++)
+   {
+      /* loop through image */
+      for (i = 0, rp = image; i < png_ptr->height; i++, rp++)
+      {
+         png_write_row(png_ptr, *rp);
+      }
+   }
+}
+
+/* called by user to write a row of image data */
+void PNGAPI
+png_write_row(png_structp png_ptr, png_bytep row)
+{
+   if (png_ptr == NULL)
+      return;
+   png_debug2(1, "in png_write_row (row %ld, pass %d)\n",
+      png_ptr->row_number, png_ptr->pass);
+
+   /* initialize transformations and other stuff if first time */
+   if (png_ptr->row_number == 0 && png_ptr->pass == 0)
+   {
+   /* make sure we wrote the header info */
+   if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE))
+      png_error(png_ptr,
+         "png_write_info was never called before png_write_row.");
+
+   /* check for transforms that have been set but were defined out */
+#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined.");
+#endif
+#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined.");
+#endif
+
+      png_write_start_row(png_ptr);
+   }
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* if interlaced and not interested in row, return */
+   if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE))
+   {
+      switch (png_ptr->pass)
+      {
+         case 0:
+            if (png_ptr->row_number & 0x07)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 1:
+            if ((png_ptr->row_number & 0x07) || png_ptr->width < 5)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 2:
+            if ((png_ptr->row_number & 0x07) != 4)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 3:
+            if ((png_ptr->row_number & 0x03) || png_ptr->width < 3)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 4:
+            if ((png_ptr->row_number & 0x03) != 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 5:
+            if ((png_ptr->row_number & 0x01) || png_ptr->width < 2)
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+         case 6:
+            if (!(png_ptr->row_number & 0x01))
+            {
+               png_write_finish_row(png_ptr);
+               return;
+            }
+            break;
+      }
+   }
+#endif
+
+   /* set up row info for transformations */
+   png_ptr->row_info.color_type = png_ptr->color_type;
+   png_ptr->row_info.width = png_ptr->usr_width;
+   png_ptr->row_info.channels = png_ptr->usr_channels;
+   png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth;
+   png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth *
+      png_ptr->row_info.channels);
+
+   png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth,
+      png_ptr->row_info.width);
+
+   png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type);
+   png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width);
+   png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels);
+   png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth);
+   png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth);
+   png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes);
+
+   /* Copy user's row into buffer, leaving room for filter byte. */
+   png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row,
+      png_ptr->row_info.rowbytes);
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+   /* handle interlacing */
+   if (png_ptr->interlaced && png_ptr->pass < 6 &&
+      (png_ptr->transformations & PNG_INTERLACE))
+   {
+      png_do_write_interlace(&(png_ptr->row_info),
+         png_ptr->row_buf + 1, png_ptr->pass);
+      /* this should always get caught above, but still ... */
+      if (!(png_ptr->row_info.width))
+      {
+         png_write_finish_row(png_ptr);
+         return;
+      }
+   }
+#endif
+
+   /* handle other transformations */
+   if (png_ptr->transformations)
+      png_do_write_transformations(png_ptr);
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   /* Write filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not write a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING))
+   {
+      /* Intrapixel differencing */
+      png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1);
+   }
+#endif
+
+   /* Find a filter if necessary, filter the row and write it out. */
+   png_write_find_filter(png_ptr, &(png_ptr->row_info));
+
+   if (png_ptr->write_row_fn != NULL)
+      (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass);
+}
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+/* Set the automatic flush interval or 0 to turn flushing off */
+void PNGAPI
+png_set_flush(png_structp png_ptr, int nrows)
+{
+   png_debug(1, "in png_set_flush\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->flush_dist = (nrows < 0 ? 0 : nrows);
+}
+
+/* flush the current output buffers now */
+void PNGAPI
+png_write_flush(png_structp png_ptr)
+{
+   int wrote_IDAT;
+
+   png_debug(1, "in png_write_flush\n");
+   if (png_ptr == NULL)
+      return;
+   /* We have already written out all of the data */
+   if (png_ptr->row_number >= png_ptr->num_rows)
+     return;
+
+   do
+   {
+      int ret;
+
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH);
+      wrote_IDAT = 0;
+
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf,
+                        png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         wrote_IDAT = 1;
+      }
+   } while(wrote_IDAT == 1);
+
+   /* If there is any data left to be output, write it into a new IDAT */
+   if (png_ptr->zbuf_size != png_ptr->zstream.avail_out)
+   {
+      /* write the IDAT and reset the zlib output buffer */
+      png_write_IDAT(png_ptr, png_ptr->zbuf,
+                     png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+      png_ptr->zstream.next_out = png_ptr->zbuf;
+      png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   }
+   png_ptr->flush_rows = 0;
+   png_flush(png_ptr);
+}
+#endif /* PNG_WRITE_FLUSH_SUPPORTED */
+
+/* free all memory used by the write */
+void PNGAPI
+png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)
+{
+   png_structp png_ptr = NULL;
+   png_infop info_ptr = NULL;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn = NULL;
+   png_voidp mem_ptr = NULL;
+#endif
+
+   png_debug(1, "in png_destroy_write_struct\n");
+   if (png_ptr_ptr != NULL)
+   {
+      png_ptr = *png_ptr_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+      free_fn = png_ptr->free_fn;
+      mem_ptr = png_ptr->mem_ptr;
+#endif
+   }
+
+   if (info_ptr_ptr != NULL)
+      info_ptr = *info_ptr_ptr;
+
+   if (info_ptr != NULL)
+   {
+      png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1);
+
+#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
+      if (png_ptr->num_chunk_list)
+      {
+         png_free(png_ptr, png_ptr->chunk_list);
+         png_ptr->chunk_list=NULL;
+         png_ptr->num_chunk_list=0;
+      }
+#endif
+
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn,
+         (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)info_ptr);
+#endif
+      *info_ptr_ptr = NULL;
+   }
+
+   if (png_ptr != NULL)
+   {
+      png_write_destroy(png_ptr);
+#ifdef PNG_USER_MEM_SUPPORTED
+      png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn,
+         (png_voidp)mem_ptr);
+#else
+      png_destroy_struct((png_voidp)png_ptr);
+#endif
+      *png_ptr_ptr = NULL;
+   }
+}
+
+
+/* Free any memory used in png_ptr struct (old method) */
+void /* PRIVATE */
+png_write_destroy(png_structp png_ptr)
+{
+#ifdef PNG_SETJMP_SUPPORTED
+   jmp_buf tmp_jmp; /* save jump buffer */
+#endif
+   png_error_ptr error_fn;
+   png_error_ptr warning_fn;
+   png_voidp error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_free_ptr free_fn;
+#endif
+
+   png_debug(1, "in png_write_destroy\n");
+   /* free any memory zlib uses */
+   deflateEnd(&png_ptr->zstream);
+
+   /* free our memory.  png_free checks NULL for us. */
+   png_free(png_ptr, png_ptr->zbuf);
+   png_free(png_ptr, png_ptr->row_buf);
+   png_free(png_ptr, png_ptr->prev_row);
+   png_free(png_ptr, png_ptr->sub_row);
+   png_free(png_ptr, png_ptr->up_row);
+   png_free(png_ptr, png_ptr->avg_row);
+   png_free(png_ptr, png_ptr->paeth_row);
+
+#if defined(PNG_TIME_RFC1123_SUPPORTED)
+   png_free(png_ptr, png_ptr->time_buffer);
+#endif
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   png_free(png_ptr, png_ptr->prev_filters);
+   png_free(png_ptr, png_ptr->filter_weights);
+   png_free(png_ptr, png_ptr->inv_filter_weights);
+   png_free(png_ptr, png_ptr->filter_costs);
+   png_free(png_ptr, png_ptr->inv_filter_costs);
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   /* reset structure */
+   png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf));
+#endif
+
+   error_fn = png_ptr->error_fn;
+   warning_fn = png_ptr->warning_fn;
+   error_ptr = png_ptr->error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   free_fn = png_ptr->free_fn;
+#endif
+
+   png_memset(png_ptr, 0, png_sizeof (png_struct));
+
+   png_ptr->error_fn = error_fn;
+   png_ptr->warning_fn = warning_fn;
+   png_ptr->error_ptr = error_ptr;
+#ifdef PNG_USER_MEM_SUPPORTED
+   png_ptr->free_fn = free_fn;
+#endif
+
+#ifdef PNG_SETJMP_SUPPORTED
+   png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf));
+#endif
+}
+
+/* Allow the application to select one or more row filters to use. */
+void PNGAPI
+png_set_filter(png_structp png_ptr, int method, int filters)
+{
+   png_debug(1, "in png_set_filter\n");
+   if (png_ptr == NULL)
+      return;
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      (method == PNG_INTRAPIXEL_DIFFERENCING))
+         method = PNG_FILTER_TYPE_BASE;
+#endif
+   if (method == PNG_FILTER_TYPE_BASE)
+   {
+      switch (filters & (PNG_ALL_FILTERS | 0x07))
+      {
+#ifndef PNG_NO_WRITE_FILTER
+         case 5:
+         case 6:
+         case 7: png_warning(png_ptr, "Unknown row filter for method 0");
+#endif /* PNG_NO_WRITE_FILTER */
+         case PNG_FILTER_VALUE_NONE:
+              png_ptr->do_filter=PNG_FILTER_NONE; break;
+#ifndef PNG_NO_WRITE_FILTER
+         case PNG_FILTER_VALUE_SUB:
+              png_ptr->do_filter=PNG_FILTER_SUB; break;
+         case PNG_FILTER_VALUE_UP:
+              png_ptr->do_filter=PNG_FILTER_UP; break;
+         case PNG_FILTER_VALUE_AVG:
+              png_ptr->do_filter=PNG_FILTER_AVG; break;
+         case PNG_FILTER_VALUE_PAETH:
+              png_ptr->do_filter=PNG_FILTER_PAETH; break;
+         default: png_ptr->do_filter = (png_byte)filters; break;
+#else
+         default: png_warning(png_ptr, "Unknown row filter for method 0");
+#endif /* PNG_NO_WRITE_FILTER */
+      }
+
+      /* If we have allocated the row_buf, this means we have already started
+       * with the image and we should have allocated all of the filter buffers
+       * that have been selected.  If prev_row isn't already allocated, then
+       * it is too late to start using the filters that need it, since we
+       * will be missing the data in the previous row.  If an application
+       * wants to start and stop using particular filters during compression,
+       * it should start out with all of the filters, and then add and
+       * remove them after the start of compression.
+       */
+      if (png_ptr->row_buf != NULL)
+      {
+#ifndef PNG_NO_WRITE_FILTER
+         if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL)
+         {
+            png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+              (png_ptr->rowbytes + 1));
+            png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+         }
+
+         if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Up filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_UP;
+            }
+            else
+            {
+               png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+            }
+         }
+
+         if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Average filter after starting");
+               png_ptr->do_filter &= ~PNG_FILTER_AVG;
+            }
+            else
+            {
+               png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+            }
+         }
+
+         if ((png_ptr->do_filter & PNG_FILTER_PAETH) &&
+             png_ptr->paeth_row == NULL)
+         {
+            if (png_ptr->prev_row == NULL)
+            {
+               png_warning(png_ptr, "Can't add Paeth filter after starting");
+               png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH);
+            }
+            else
+            {
+               png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
+                  (png_ptr->rowbytes + 1));
+               png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+            }
+         }
+
+         if (png_ptr->do_filter == PNG_NO_FILTERS)
+#endif /* PNG_NO_WRITE_FILTER */
+            png_ptr->do_filter = PNG_FILTER_NONE;
+      }
+   }
+   else
+      png_error(png_ptr, "Unknown custom filter method");
+}
+
+/* This allows us to influence the way in which libpng chooses the "best"
+ * filter for the current scanline.  While the "minimum-sum-of-absolute-
+ * differences metric is relatively fast and effective, there is some
+ * question as to whether it can be improved upon by trying to keep the
+ * filtered data going to zlib more consistent, hopefully resulting in
+ * better compression.
+ */
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)      /* GRR 970116 */
+void PNGAPI
+png_set_filter_heuristics(png_structp png_ptr, int heuristic_method,
+   int num_weights, png_doublep filter_weights,
+   png_doublep filter_costs)
+{
+   int i;
+
+   png_debug(1, "in png_set_filter_heuristics\n");
+   if (png_ptr == NULL)
+      return;
+   if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST)
+   {
+      png_warning(png_ptr, "Unknown filter heuristic method");
+      return;
+   }
+
+   if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT)
+   {
+      heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED;
+   }
+
+   if (num_weights < 0 || filter_weights == NULL ||
+      heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED)
+   {
+      num_weights = 0;
+   }
+
+   png_ptr->num_prev_filters = (png_byte)num_weights;
+   png_ptr->heuristic_method = (png_byte)heuristic_method;
+
+   if (num_weights > 0)
+   {
+      if (png_ptr->prev_filters == NULL)
+      {
+         png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr,
+            (png_uint_32)(png_sizeof(png_byte) * num_weights));
+
+         /* To make sure that the weighting starts out fairly */
+         for (i = 0; i < num_weights; i++)
+         {
+            png_ptr->prev_filters[i] = 255;
+         }
+      }
+
+      if (png_ptr->filter_weights == NULL)
+      {
+         png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr,
+            (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
+
+         png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr,
+            (png_uint_32)(png_sizeof(png_uint_16) * num_weights));
+         for (i = 0; i < num_weights; i++)
+         {
+            png_ptr->inv_filter_weights[i] =
+            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+         }
+      }
+
+      for (i = 0; i < num_weights; i++)
+      {
+         if (filter_weights[i] < 0.0)
+         {
+            png_ptr->inv_filter_weights[i] =
+            png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR;
+         }
+         else
+         {
+            png_ptr->inv_filter_weights[i] =
+               (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5);
+            png_ptr->filter_weights[i] =
+               (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5);
+         }
+      }
+   }
+
+   /* If, in the future, there are other filter methods, this would
+    * need to be based on png_ptr->filter.
+    */
+   if (png_ptr->filter_costs == NULL)
+   {
+      png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr,
+         (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+      png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr,
+         (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST));
+
+      for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+      {
+         png_ptr->inv_filter_costs[i] =
+         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+      }
+   }
+
+   /* Here is where we set the relative costs of the different filters.  We
+    * should take the desired compression level into account when setting
+    * the costs, so that Paeth, for instance, has a high relative cost at low
+    * compression levels, while it has a lower relative cost at higher
+    * compression settings.  The filter types are in order of increasing
+    * relative cost, so it would be possible to do this with an algorithm.
+    */
+   for (i = 0; i < PNG_FILTER_VALUE_LAST; i++)
+   {
+      if (filter_costs == NULL || filter_costs[i] < 0.0)
+      {
+         png_ptr->inv_filter_costs[i] =
+         png_ptr->filter_costs[i] = PNG_COST_FACTOR;
+      }
+      else if (filter_costs[i] >= 1.0)
+      {
+         png_ptr->inv_filter_costs[i] =
+            (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5);
+         png_ptr->filter_costs[i] =
+            (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5);
+      }
+   }
+}
+#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */
+
+void PNGAPI
+png_set_compression_level(png_structp png_ptr, int level)
+{
+   png_debug(1, "in png_set_compression_level\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL;
+   png_ptr->zlib_level = level;
+}
+
+void PNGAPI
+png_set_compression_mem_level(png_structp png_ptr, int mem_level)
+{
+   png_debug(1, "in png_set_compression_mem_level\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL;
+   png_ptr->zlib_mem_level = mem_level;
+}
+
+void PNGAPI
+png_set_compression_strategy(png_structp png_ptr, int strategy)
+{
+   png_debug(1, "in png_set_compression_strategy\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY;
+   png_ptr->zlib_strategy = strategy;
+}
+
+void PNGAPI
+png_set_compression_window_bits(png_structp png_ptr, int window_bits)
+{
+   if (png_ptr == NULL)
+      return;
+   if (window_bits > 15)
+      png_warning(png_ptr, "Only compression windows <= 32k supported by PNG");
+   else if (window_bits < 8)
+      png_warning(png_ptr, "Only compression windows >= 256 supported by PNG");
+#ifndef WBITS_8_OK
+   /* avoid libpng bug with 256-byte windows */
+   if (window_bits == 8)
+     {
+       png_warning(png_ptr, "Compression window is being reset to 512");
+       window_bits=9;
+     }
+#endif
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS;
+   png_ptr->zlib_window_bits = window_bits;
+}
+
+void PNGAPI
+png_set_compression_method(png_structp png_ptr, int method)
+{
+   png_debug(1, "in png_set_compression_method\n");
+   if (png_ptr == NULL)
+      return;
+   if (method != 8)
+      png_warning(png_ptr, "Only compression method 8 is supported by PNG");
+   png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD;
+   png_ptr->zlib_method = method;
+}
+
+void PNGAPI
+png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn)
+{
+   if (png_ptr == NULL)
+      return;
+   png_ptr->write_row_fn = write_row_fn;
+}
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+void PNGAPI
+png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr
+   write_user_transform_fn)
+{
+   png_debug(1, "in png_set_write_user_transform_fn\n");
+   if (png_ptr == NULL)
+      return;
+   png_ptr->transformations |= PNG_USER_TRANSFORM;
+   png_ptr->write_user_transform_fn = write_user_transform_fn;
+}
+#endif
+
+
+#if defined(PNG_INFO_IMAGE_SUPPORTED)
+void PNGAPI
+png_write_png(png_structp png_ptr, png_infop info_ptr,
+              int transforms, voidp params)
+{
+   if (png_ptr == NULL || info_ptr == NULL)
+      return;
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+   /* invert the alpha channel from opacity to transparency */
+   if (transforms & PNG_TRANSFORM_INVERT_ALPHA)
+       png_set_invert_alpha(png_ptr);
+#endif
+
+   /* Write the file header information. */
+   png_write_info(png_ptr, info_ptr);
+
+   /* ------ these transformations don't touch the info structure ------- */
+
+#if defined(PNG_WRITE_INVERT_SUPPORTED)
+   /* invert monochrome pixels */
+   if (transforms & PNG_TRANSFORM_INVERT_MONO)
+       png_set_invert_mono(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+   /* Shift the pixels up to a legal bit depth and fill in
+    * as appropriate to correctly scale the image.
+    */
+   if ((transforms & PNG_TRANSFORM_SHIFT)
+               && (info_ptr->valid & PNG_INFO_sBIT))
+       png_set_shift(png_ptr, &info_ptr->sig_bit);
+#endif
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+   /* pack pixels into bytes */
+   if (transforms & PNG_TRANSFORM_PACKING)
+       png_set_packing(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+   /* swap location of alpha bytes from ARGB to RGBA */
+   if (transforms & PNG_TRANSFORM_SWAP_ALPHA)
+       png_set_swap_alpha(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_FILLER_SUPPORTED)
+   /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into
+    * RGB (4 channels -> 3 channels). The second parameter is not used.
+    */
+   if (transforms & PNG_TRANSFORM_STRIP_FILLER)
+       png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE);
+#endif
+
+#if defined(PNG_WRITE_BGR_SUPPORTED)
+   /* flip BGR pixels to RGB */
+   if (transforms & PNG_TRANSFORM_BGR)
+       png_set_bgr(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_SWAP_SUPPORTED)
+   /* swap bytes of 16-bit files to most significant byte first */
+   if (transforms & PNG_TRANSFORM_SWAP_ENDIAN)
+       png_set_swap(png_ptr);
+#endif
+
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+   /* swap bits of 1, 2, 4 bit packed pixel formats */
+   if (transforms & PNG_TRANSFORM_PACKSWAP)
+       png_set_packswap(png_ptr);
+#endif
+
+   /* ----------------------- end of transformations ------------------- */
+
+   /* write the bits */
+   if (info_ptr->valid & PNG_INFO_IDAT)
+       png_write_image(png_ptr, info_ptr->row_pointers);
+
+   /* It is REQUIRED to call this to finish writing the rest of the file */
+   png_write_end(png_ptr, info_ptr);
+
+   transforms = transforms; /* quiet compiler warnings */
+   params = params;
+}
+#endif
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngwtran.c b/distrib/libpng-1.2.19/pngwtran.c
new file mode 100644
index 0000000..0372fe6
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngwtran.c
@@ -0,0 +1,572 @@
+
+/* pngwtran.c - transforms the data in a row for PNG writers
+ *
+ * Last changed in libpng 1.2.9 April 14, 2006
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2006 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Transform the data according to the user's wishes.  The order of
+ * transformations is significant.
+ */
+void /* PRIVATE */
+png_do_write_transformations(png_structp png_ptr)
+{
+   png_debug(1, "in png_do_write_transformations\n");
+
+   if (png_ptr == NULL)
+      return;
+
+#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED)
+   if (png_ptr->transformations & PNG_USER_TRANSFORM)
+      if(png_ptr->write_user_transform_fn != NULL)
+        (*(png_ptr->write_user_transform_fn)) /* user write transform function */
+          (png_ptr,                    /* png_ptr */
+           &(png_ptr->row_info),       /* row_info:     */
+             /*  png_uint_32 width;          width of row */
+             /*  png_uint_32 rowbytes;       number of bytes in row */
+             /*  png_byte color_type;        color type of pixels */
+             /*  png_byte bit_depth;         bit depth of samples */
+             /*  png_byte channels;          number of channels (1-4) */
+             /*  png_byte pixel_depth;       bits per pixel (depth*channels) */
+           png_ptr->row_buf + 1);      /* start of pixel data for row */
+#endif
+#if defined(PNG_WRITE_FILLER_SUPPORTED)
+   if (png_ptr->transformations & PNG_FILLER)
+      png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         png_ptr->flags);
+#endif
+#if defined(PNG_WRITE_PACKSWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACKSWAP)
+      png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+   if (png_ptr->transformations & PNG_PACK)
+      png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         (png_uint_32)png_ptr->bit_depth);
+#endif
+#if defined(PNG_WRITE_SWAP_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_BYTES)
+      png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+   if (png_ptr->transformations & PNG_SHIFT)
+      png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1,
+         &(png_ptr->shift));
+#endif
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_SWAP_ALPHA)
+      png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_ALPHA)
+      png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_BGR_SUPPORTED)
+   if (png_ptr->transformations & PNG_BGR)
+      png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+#if defined(PNG_WRITE_INVERT_SUPPORTED)
+   if (png_ptr->transformations & PNG_INVERT_MONO)
+      png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1);
+#endif
+}
+
+#if defined(PNG_WRITE_PACK_SUPPORTED)
+/* Pack pixels into bytes.  Pass the true bit depth in bit_depth.  The
+ * row_info bit depth should be 8 (one pixel per byte).  The channels
+ * should be 1 (this only happens on grayscale and paletted images).
+ */
+void /* PRIVATE */
+png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth)
+{
+   png_debug(1, "in png_do_pack\n");
+   if (row_info->bit_depth == 8 &&
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+      row_info->channels == 1)
+   {
+      switch ((int)bit_depth)
+      {
+         case 1:
+         {
+            png_bytep sp, dp;
+            int mask, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            mask = 0x80;
+            v = 0;
+
+            for (i = 0; i < row_width; i++)
+            {
+               if (*sp != 0)
+                  v |= mask;
+               sp++;
+               if (mask > 1)
+                  mask >>= 1;
+               else
+               {
+                  mask = 0x80;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+            }
+            if (mask != 0x80)
+               *dp = (png_byte)v;
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp, dp;
+            int shift, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 6;
+            v = 0;
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x03);
+               v |= (value << shift);
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 2;
+               sp++;
+            }
+            if (shift != 6)
+               *dp = (png_byte)v;
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp, dp;
+            int shift, v;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            sp = row;
+            dp = row;
+            shift = 4;
+            v = 0;
+            for (i = 0; i < row_width; i++)
+            {
+               png_byte value;
+
+               value = (png_byte)(*sp & 0x0f);
+               v |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp = (png_byte)v;
+                  dp++;
+                  v = 0;
+               }
+               else
+                  shift -= 4;
+
+               sp++;
+            }
+            if (shift != 4)
+               *dp = (png_byte)v;
+            break;
+         }
+      }
+      row_info->bit_depth = (png_byte)bit_depth;
+      row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels);
+      row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+         row_info->width);
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_SHIFT_SUPPORTED)
+/* Shift pixel values to take advantage of whole range.  Pass the
+ * true number of bits in bit_depth.  The row should be packed
+ * according to row_info->bit_depth.  Thus, if you had a row of
+ * bit depth 4, but the pixels only had values from 0 to 7, you
+ * would pass 3 as bit_depth, and this routine would translate the
+ * data to 0 to 15.
+ */
+void /* PRIVATE */
+png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth)
+{
+   png_debug(1, "in png_do_shift\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL &&
+#else
+   if (
+#endif
+      row_info->color_type != PNG_COLOR_TYPE_PALETTE)
+   {
+      int shift_start[4], shift_dec[4];
+      int channels = 0;
+
+      if (row_info->color_type & PNG_COLOR_MASK_COLOR)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->red;
+         shift_dec[channels] = bit_depth->red;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->green;
+         shift_dec[channels] = bit_depth->green;
+         channels++;
+         shift_start[channels] = row_info->bit_depth - bit_depth->blue;
+         shift_dec[channels] = bit_depth->blue;
+         channels++;
+      }
+      else
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->gray;
+         shift_dec[channels] = bit_depth->gray;
+         channels++;
+      }
+      if (row_info->color_type & PNG_COLOR_MASK_ALPHA)
+      {
+         shift_start[channels] = row_info->bit_depth - bit_depth->alpha;
+         shift_dec[channels] = bit_depth->alpha;
+         channels++;
+      }
+
+      /* with low row depths, could only be grayscale, so one channel */
+      if (row_info->bit_depth < 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_byte mask;
+         png_uint_32 row_bytes = row_info->rowbytes;
+
+         if (bit_depth->gray == 1 && row_info->bit_depth == 2)
+            mask = 0x55;
+         else if (row_info->bit_depth == 4 && bit_depth->gray == 3)
+            mask = 0x11;
+         else
+            mask = 0xff;
+
+         for (i = 0; i < row_bytes; i++, bp++)
+         {
+            png_uint_16 v;
+            int j;
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & mask);
+            }
+         }
+      }
+      else if (row_info->bit_depth == 8)
+      {
+         png_bytep bp = row;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (i = 0; i < istop; i++, bp++)
+         {
+
+            png_uint_16 v;
+            int j;
+            int c = (int)(i%channels);
+
+            v = *bp;
+            *bp = 0;
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  *bp |= (png_byte)((v << j) & 0xff);
+               else
+                  *bp |= (png_byte)((v >> (-j)) & 0xff);
+            }
+         }
+      }
+      else
+      {
+         png_bytep bp;
+         png_uint_32 i;
+         png_uint_32 istop = channels * row_info->width;
+
+         for (bp = row, i = 0; i < istop; i++)
+         {
+            int c = (int)(i%channels);
+            png_uint_16 value, v;
+            int j;
+
+            v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1));
+            value = 0;
+            for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c])
+            {
+               if (j > 0)
+                  value |= (png_uint_16)((v << j) & (png_uint_16)0xffff);
+               else
+                  value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff);
+            }
+            *bp++ = (png_byte)(value >> 8);
+            *bp++ = (png_byte)(value & 0xff);
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_write_swap_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_swap_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This converts from ARGB to RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+         /* This converts from AARRGGBB to RRGGBBAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This converts from AG to GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save;
+            }
+         }
+         /* This converts from AAGG to GGAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               png_byte save[2];
+               save[0] = *(sp++);
+               save[1] = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = save[0];
+               *(dp++) = save[1];
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED)
+void /* PRIVATE */
+png_do_write_invert_alpha(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_invert_alpha\n");
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL)
+#endif
+   {
+      if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+      {
+         /* This inverts the alpha channel in RGBA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               /* does nothing
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               */
+               sp+=3; dp = sp;
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+         /* This inverts the alpha channel in RRGGBBAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               /* does nothing
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               */
+               sp+=6; dp = sp;
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+      }
+      else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+      {
+         /* This inverts the alpha channel in GA */
+         if (row_info->bit_depth == 8)
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               *(dp++) = *(sp++);
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+         /* This inverts the alpha channel in GGAA */
+         else
+         {
+            png_bytep sp, dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            for (i = 0, sp = dp = row; i < row_width; i++)
+            {
+               /* does nothing
+               *(dp++) = *(sp++);
+               *(dp++) = *(sp++);
+               */
+               sp+=2; dp = sp;
+               *(dp++) = (png_byte)(255 - *(sp++));
+               *(dp++) = (png_byte)(255 - *(sp++));
+            }
+         }
+      }
+   }
+}
+#endif
+
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+/* undoes intrapixel differencing  */
+void /* PRIVATE */
+png_do_write_intrapixel(png_row_infop row_info, png_bytep row)
+{
+   png_debug(1, "in png_do_write_intrapixel\n");
+   if (
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+       row != NULL && row_info != NULL &&
+#endif
+       (row_info->color_type & PNG_COLOR_MASK_COLOR))
+   {
+      int bytes_per_pixel;
+      png_uint_32 row_width = row_info->width;
+      if (row_info->bit_depth == 8)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 3;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 4;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            *(rp)   = (png_byte)((*rp     - *(rp+1))&0xff);
+            *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff);
+         }
+      }
+      else if (row_info->bit_depth == 16)
+      {
+         png_bytep rp;
+         png_uint_32 i;
+
+         if (row_info->color_type == PNG_COLOR_TYPE_RGB)
+            bytes_per_pixel = 6;
+         else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
+            bytes_per_pixel = 8;
+         else
+            return;
+
+         for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel)
+         {
+            png_uint_32 s0   = (*(rp  ) << 8) | *(rp+1);
+            png_uint_32 s1   = (*(rp+2) << 8) | *(rp+3);
+            png_uint_32 s2   = (*(rp+4) << 8) | *(rp+5);
+            png_uint_32 red  = (png_uint_32)((s0-s1) & 0xffffL);
+            png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL);
+            *(rp  ) = (png_byte)((red >> 8) & 0xff);
+            *(rp+1) = (png_byte)(red & 0xff);
+            *(rp+4) = (png_byte)((blue >> 8) & 0xff);
+            *(rp+5) = (png_byte)(blue & 0xff);
+         }
+      }
+   }
+}
+#endif /* PNG_MNG_FEATURES_SUPPORTED */
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/pngwutil.c b/distrib/libpng-1.2.19/pngwutil.c
new file mode 100644
index 0000000..849ee7d
--- /dev/null
+++ b/distrib/libpng-1.2.19/pngwutil.c
@@ -0,0 +1,2782 @@
+
+/* pngwutil.c - utilities to write a PNG file
+ *
+ * Last changed in libpng 1.2.19 August 18, 2007
+ * For conditions of distribution and use, see copyright notice in png.h
+ * Copyright (c) 1998-2007 Glenn Randers-Pehrson
+ * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
+ * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
+ */
+
+#define PNG_INTERNAL
+#include "png.h"
+#ifdef PNG_WRITE_SUPPORTED
+
+/* Place a 32-bit number into a buffer in PNG byte order.  We work
+ * with unsigned numbers for convenience, although one supported
+ * ancillary chunk uses signed (two's complement) numbers.
+ */
+void PNGAPI
+png_save_uint_32(png_bytep buf, png_uint_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+
+/* The png_save_int_32 function assumes integers are stored in two's
+ * complement format.  If this isn't the case, then this routine needs to
+ * be modified to write data in two's complement format.
+ */
+void PNGAPI
+png_save_int_32(png_bytep buf, png_int_32 i)
+{
+   buf[0] = (png_byte)((i >> 24) & 0xff);
+   buf[1] = (png_byte)((i >> 16) & 0xff);
+   buf[2] = (png_byte)((i >> 8) & 0xff);
+   buf[3] = (png_byte)(i & 0xff);
+}
+
+/* Place a 16-bit number into a buffer in PNG byte order.
+ * The parameter is declared unsigned int, not png_uint_16,
+ * just to avoid potential problems on pre-ANSI C compilers.
+ */
+void PNGAPI
+png_save_uint_16(png_bytep buf, unsigned int i)
+{
+   buf[0] = (png_byte)((i >> 8) & 0xff);
+   buf[1] = (png_byte)(i & 0xff);
+}
+
+/* Write a PNG chunk all at once.  The type is an array of ASCII characters
+ * representing the chunk name.  The array must be at least 4 bytes in
+ * length, and does not need to be null terminated.  To be safe, pass the
+ * pre-defined chunk names here, and if you need a new one, define it
+ * where the others are defined.  The length is the length of the data.
+ * All the data must be present.  If that is not possible, use the
+ * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end()
+ * functions instead.
+ */
+void PNGAPI
+png_write_chunk(png_structp png_ptr, png_bytep chunk_name,
+   png_bytep data, png_size_t length)
+{
+   if(png_ptr == NULL) return;
+   png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length);
+   png_write_chunk_data(png_ptr, data, length);
+   png_write_chunk_end(png_ptr);
+}
+
+/* Write the start of a PNG chunk.  The type is the chunk type.
+ * The total_length is the sum of the lengths of all the data you will be
+ * passing in png_write_chunk_data().
+ */
+void PNGAPI
+png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name,
+   png_uint_32 length)
+{
+   png_byte buf[4];
+   png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length);
+   if(png_ptr == NULL) return;
+
+   /* write the length */
+   png_save_uint_32(buf, length);
+   png_write_data(png_ptr, buf, (png_size_t)4);
+
+   /* write the chunk name */
+   png_write_data(png_ptr, chunk_name, (png_size_t)4);
+   /* reset the crc and run it over the chunk name */
+   png_reset_crc(png_ptr);
+   png_calculate_crc(png_ptr, chunk_name, (png_size_t)4);
+}
+
+/* Write the data of a PNG chunk started with png_write_chunk_start().
+ * Note that multiple calls to this function are allowed, and that the
+ * sum of the lengths from these calls *must* add up to the total_length
+ * given to png_write_chunk_start().
+ */
+void PNGAPI
+png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+   /* write the data, and run the CRC over it */
+   if(png_ptr == NULL) return;
+   if (data != NULL && length > 0)
+   {
+      png_calculate_crc(png_ptr, data, length);
+      png_write_data(png_ptr, data, length);
+   }
+}
+
+/* Finish a chunk started with png_write_chunk_start(). */
+void PNGAPI
+png_write_chunk_end(png_structp png_ptr)
+{
+   png_byte buf[4];
+
+   if(png_ptr == NULL) return;
+
+   /* write the crc */
+   png_save_uint_32(buf, png_ptr->crc);
+
+   png_write_data(png_ptr, buf, (png_size_t)4);
+}
+
+/* Simple function to write the signature.  If we have already written
+ * the magic bytes of the signature, or more likely, the PNG stream is
+ * being embedded into another stream and doesn't need its own signature,
+ * we should call png_set_sig_bytes() to tell libpng how many of the
+ * bytes have already been written.
+ */
+void /* PRIVATE */
+png_write_sig(png_structp png_ptr)
+{
+   png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+   /* write the rest of the 8 byte signature */
+   png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes],
+      (png_size_t)8 - png_ptr->sig_bytes);
+   if(png_ptr->sig_bytes < 3)
+      png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE;
+}
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED)
+/*
+ * This pair of functions encapsulates the operation of (a) compressing a
+ * text string, and (b) issuing it later as a series of chunk data writes.
+ * The compression_state structure is shared context for these functions
+ * set up by the caller in order to make the whole mess thread-safe.
+ */
+
+typedef struct
+{
+    char *input;   /* the uncompressed input data */
+    int input_len;   /* its length */
+    int num_output_ptr; /* number of output pointers used */
+    int max_output_ptr; /* size of output_ptr */
+    png_charpp output_ptr; /* array of pointers to output */
+} compression_state;
+
+/* compress given text into storage in the png_ptr structure */
+static int /* PRIVATE */
+png_text_compress(png_structp png_ptr,
+        png_charp text, png_size_t text_len, int compression,
+        compression_state *comp)
+{
+   int ret;
+
+   comp->num_output_ptr = 0;
+   comp->max_output_ptr = 0;
+   comp->output_ptr = NULL;
+   comp->input = NULL;
+   comp->input_len = 0;
+
+   /* we may just want to pass the text right through */
+   if (compression == PNG_TEXT_COMPRESSION_NONE)
+   {
+       comp->input = text;
+       comp->input_len = text_len;
+       return((int)text_len);
+   }
+
+   if (compression >= PNG_TEXT_COMPRESSION_LAST)
+   {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+      char msg[50];
+      png_snprintf(msg, 50, "Unknown compression type %d", compression);
+      png_warning(png_ptr, msg);
+#else
+      png_warning(png_ptr, "Unknown compression type");
+#endif
+   }
+
+   /* We can't write the chunk until we find out how much data we have,
+    * which means we need to run the compressor first and save the
+    * output.  This shouldn't be a problem, as the vast majority of
+    * comments should be reasonable, but we will set up an array of
+    * malloc'd pointers to be sure.
+    *
+    * If we knew the application was well behaved, we could simplify this
+    * greatly by assuming we can always malloc an output buffer large
+    * enough to hold the compressed text ((1001 * text_len / 1000) + 12)
+    * and malloc this directly.  The only time this would be a bad idea is
+    * if we can't malloc more than 64K and we have 64K of random input
+    * data, or if the input string is incredibly large (although this
+    * wouldn't cause a failure, just a slowdown due to swapping).
+    */
+
+   /* set up the compression buffers */
+   png_ptr->zstream.avail_in = (uInt)text_len;
+   png_ptr->zstream.next_in = (Bytef *)text;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf;
+
+   /* this is the same compression loop as in png_write_row() */
+   do
+   {
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      if (ret != Z_OK)
+      {
+         /* error */
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+      /* check to see if we need more room */
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* make sure the output array has room */
+         if (comp->num_output_ptr >= comp->max_output_ptr)
+         {
+            int old_max;
+
+            old_max = comp->max_output_ptr;
+            comp->max_output_ptr = comp->num_output_ptr + 4;
+            if (comp->output_ptr != NULL)
+            {
+               png_charpp old_ptr;
+
+               old_ptr = comp->output_ptr;
+               comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(comp->max_output_ptr *
+                  png_sizeof (png_charpp)));
+               png_memcpy(comp->output_ptr, old_ptr, old_max
+                  * png_sizeof (png_charp));
+               png_free(png_ptr, old_ptr);
+            }
+            else
+               comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                  (png_uint_32)(comp->max_output_ptr *
+                  png_sizeof (png_charp)));
+         }
+
+         /* save the data */
+         comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr,
+            (png_uint_32)png_ptr->zbuf_size);
+         png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
+            png_ptr->zbuf_size);
+         comp->num_output_ptr++;
+
+         /* and reset the buffer */
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+      }
+   /* continue until we don't have any more to compress */
+   } while (png_ptr->zstream.avail_in);
+
+   /* finish the compression */
+   do
+   {
+      /* tell zlib we are finished */
+      ret = deflate(&png_ptr->zstream, Z_FINISH);
+
+      if (ret == Z_OK)
+      {
+         /* check to see if we need more room */
+         if (!(png_ptr->zstream.avail_out))
+         {
+            /* check to make sure our output array has room */
+            if (comp->num_output_ptr >= comp->max_output_ptr)
+            {
+               int old_max;
+
+               old_max = comp->max_output_ptr;
+               comp->max_output_ptr = comp->num_output_ptr + 4;
+               if (comp->output_ptr != NULL)
+               {
+                  png_charpp old_ptr;
+
+                  old_ptr = comp->output_ptr;
+                  /* This could be optimized to realloc() */
+                  comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                     (png_uint_32)(comp->max_output_ptr *
+                     png_sizeof (png_charpp)));
+                  png_memcpy(comp->output_ptr, old_ptr,
+                     old_max * png_sizeof (png_charp));
+                  png_free(png_ptr, old_ptr);
+               }
+               else
+                  comp->output_ptr = (png_charpp)png_malloc(png_ptr,
+                     (png_uint_32)(comp->max_output_ptr *
+                     png_sizeof (png_charp)));
+            }
+
+            /* save off the data */
+            comp->output_ptr[comp->num_output_ptr] =
+               (png_charp)png_malloc(png_ptr, (png_uint_32)png_ptr->zbuf_size);
+            png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf,
+               png_ptr->zbuf_size);
+            comp->num_output_ptr++;
+
+            /* and reset the buffer pointers */
+            png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+            png_ptr->zstream.next_out = png_ptr->zbuf;
+         }
+      }
+      else if (ret != Z_STREAM_END)
+      {
+         /* we got an error */
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* text length is number of buffers plus last buffer */
+   text_len = png_ptr->zbuf_size * comp->num_output_ptr;
+   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+      text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out;
+
+   return((int)text_len);
+}
+
+/* ship the compressed text out via chunk writes */
+static void /* PRIVATE */
+png_write_compressed_data_out(png_structp png_ptr, compression_state *comp)
+{
+   int i;
+
+   /* handle the no-compression case */
+   if (comp->input)
+   {
+       png_write_chunk_data(png_ptr, (png_bytep)comp->input,
+                            (png_size_t)comp->input_len);
+       return;
+   }
+
+   /* write saved output buffers, if any */
+   for (i = 0; i < comp->num_output_ptr; i++)
+   {
+      png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i],
+         png_ptr->zbuf_size);
+      png_free(png_ptr, comp->output_ptr[i]);
+      comp->output_ptr[i]=NULL;
+   }
+   if (comp->max_output_ptr != 0)
+      png_free(png_ptr, comp->output_ptr);
+      comp->output_ptr=NULL;
+   /* write anything left in zbuf */
+   if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size)
+      png_write_chunk_data(png_ptr, png_ptr->zbuf,
+         png_ptr->zbuf_size - png_ptr->zstream.avail_out);
+
+   /* reset zlib for another zTXt/iTXt or image data */
+   deflateReset(&png_ptr->zstream);
+   png_ptr->zstream.data_type = Z_BINARY;
+}
+#endif
+
+/* Write the IHDR chunk, and update the png_struct with the necessary
+ * information.  Note that the rest of this code depends upon this
+ * information being correct.
+ */
+void /* PRIVATE */
+png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height,
+   int bit_depth, int color_type, int compression_type, int filter_type,
+   int interlace_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IHDR;
+#endif
+   png_byte buf[13]; /* buffer to store the IHDR info */
+
+   png_debug(1, "in png_write_IHDR\n");
+   /* Check that we have valid input data from the application info */
+   switch (color_type)
+   {
+      case PNG_COLOR_TYPE_GRAY:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8:
+            case 16: png_ptr->channels = 1; break;
+            default: png_error(png_ptr,"Invalid bit depth for grayscale image");
+         }
+         break;
+      case PNG_COLOR_TYPE_RGB:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for RGB image");
+         png_ptr->channels = 3;
+         break;
+      case PNG_COLOR_TYPE_PALETTE:
+         switch (bit_depth)
+         {
+            case 1:
+            case 2:
+            case 4:
+            case 8: png_ptr->channels = 1; break;
+            default: png_error(png_ptr, "Invalid bit depth for paletted image");
+         }
+         break;
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for grayscale+alpha image");
+         png_ptr->channels = 2;
+         break;
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+         if (bit_depth != 8 && bit_depth != 16)
+            png_error(png_ptr, "Invalid bit depth for RGBA image");
+         png_ptr->channels = 4;
+         break;
+      default:
+         png_error(png_ptr, "Invalid image color type specified");
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid compression type specified");
+      compression_type = PNG_COMPRESSION_TYPE_BASE;
+   }
+
+   /* Write filter_method 64 (intrapixel differencing) only if
+    * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and
+    * 2. Libpng did not write a PNG signature (this filter_method is only
+    *    used in PNG datastreams that are embedded in MNG datastreams) and
+    * 3. The application called png_permit_mng_features with a mask that
+    *    included PNG_FLAG_MNG_FILTER_64 and
+    * 4. The filter_method is 64 and
+    * 5. The color_type is RGB or RGBA
+    */
+   if (
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+      !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) &&
+      ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) &&
+      (color_type == PNG_COLOR_TYPE_RGB ||
+       color_type == PNG_COLOR_TYPE_RGB_ALPHA) &&
+      (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) &&
+#endif
+      filter_type != PNG_FILTER_TYPE_BASE)
+   {
+      png_warning(png_ptr, "Invalid filter type specified");
+      filter_type = PNG_FILTER_TYPE_BASE;
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   if (interlace_type != PNG_INTERLACE_NONE &&
+      interlace_type != PNG_INTERLACE_ADAM7)
+   {
+      png_warning(png_ptr, "Invalid interlace type specified");
+      interlace_type = PNG_INTERLACE_ADAM7;
+   }
+#else
+   interlace_type=PNG_INTERLACE_NONE;
+#endif
+
+   /* save off the relevent information */
+   png_ptr->bit_depth = (png_byte)bit_depth;
+   png_ptr->color_type = (png_byte)color_type;
+   png_ptr->interlaced = (png_byte)interlace_type;
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+   png_ptr->filter_type = (png_byte)filter_type;
+#endif
+   png_ptr->compression_type = (png_byte)compression_type;
+   png_ptr->width = width;
+   png_ptr->height = height;
+
+   png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels);
+   png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width);
+   /* set the usr info, so any transformations can modify it */
+   png_ptr->usr_width = png_ptr->width;
+   png_ptr->usr_bit_depth = png_ptr->bit_depth;
+   png_ptr->usr_channels = png_ptr->channels;
+
+   /* pack the header information into the buffer */
+   png_save_uint_32(buf, width);
+   png_save_uint_32(buf + 4, height);
+   buf[8] = (png_byte)bit_depth;
+   buf[9] = (png_byte)color_type;
+   buf[10] = (png_byte)compression_type;
+   buf[11] = (png_byte)filter_type;
+   buf[12] = (png_byte)interlace_type;
+
+   /* write the chunk */
+   png_write_chunk(png_ptr, (png_bytep)png_IHDR, buf, (png_size_t)13);
+
+   /* initialize zlib with PNG info */
+   png_ptr->zstream.zalloc = png_zalloc;
+   png_ptr->zstream.zfree = png_zfree;
+   png_ptr->zstream.opaque = (voidpf)png_ptr;
+   if (!(png_ptr->do_filter))
+   {
+      if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE ||
+         png_ptr->bit_depth < 8)
+         png_ptr->do_filter = PNG_FILTER_NONE;
+      else
+         png_ptr->do_filter = PNG_ALL_FILTERS;
+   }
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY))
+   {
+      if (png_ptr->do_filter != PNG_FILTER_NONE)
+         png_ptr->zlib_strategy = Z_FILTERED;
+      else
+         png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY;
+   }
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL))
+      png_ptr->zlib_level = Z_DEFAULT_COMPRESSION;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL))
+      png_ptr->zlib_mem_level = 8;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS))
+      png_ptr->zlib_window_bits = 15;
+   if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD))
+      png_ptr->zlib_method = 8;
+   if (deflateInit2(&png_ptr->zstream, png_ptr->zlib_level,
+      png_ptr->zlib_method, png_ptr->zlib_window_bits,
+      png_ptr->zlib_mem_level, png_ptr->zlib_strategy) != Z_OK)
+       png_error(png_ptr, "zlib failed to initialize compressor");
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   /* libpng is not interested in zstream.data_type */
+   /* set it to a predefined value, to avoid its evaluation inside zlib */
+   png_ptr->zstream.data_type = Z_BINARY;
+
+   png_ptr->mode = PNG_HAVE_IHDR;
+}
+
+/* write the palette.  We are careful not to trust png_color to be in the
+ * correct order for PNG, so people can redefine it to any convenient
+ * structure.
+ */
+void /* PRIVATE */
+png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_PLTE;
+#endif
+   png_uint_32 i;
+   png_colorp pal_ptr;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_PLTE\n");
+   if ((
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+        !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) &&
+#endif
+        num_pal == 0) || num_pal > 256)
+   {
+     if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
+     {
+        png_error(png_ptr, "Invalid number of colors in palette");
+     }
+     else
+     {
+        png_warning(png_ptr, "Invalid number of colors in palette");
+        return;
+     }
+   }
+
+   if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR))
+   {
+      png_warning(png_ptr,
+        "Ignoring request to write a PLTE chunk in grayscale PNG");
+      return;
+   }
+
+   png_ptr->num_palette = (png_uint_16)num_pal;
+   png_debug1(3, "num_palette = %d\n", png_ptr->num_palette);
+
+   png_write_chunk_start(png_ptr, png_PLTE, num_pal * 3);
+#ifndef PNG_NO_POINTER_INDEXING
+   for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++)
+   {
+      buf[0] = pal_ptr->red;
+      buf[1] = pal_ptr->green;
+      buf[2] = pal_ptr->blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+#else
+   /* This is a little slower but some buggy compilers need to do this instead */
+   pal_ptr=palette;
+   for (i = 0; i < num_pal; i++)
+   {
+      buf[0] = pal_ptr[i].red;
+      buf[1] = pal_ptr[i].green;
+      buf[2] = pal_ptr[i].blue;
+      png_write_chunk_data(png_ptr, buf, (png_size_t)3);
+   }
+#endif
+   png_write_chunk_end(png_ptr);
+   png_ptr->mode |= PNG_HAVE_PLTE;
+}
+
+/* write an IDAT chunk */
+void /* PRIVATE */
+png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IDAT;
+#endif
+   png_debug(1, "in png_write_IDAT\n");
+
+   /* Optimize the CMF field in the zlib stream. */
+   /* This hack of the zlib stream is compliant to the stream specification. */
+   if (!(png_ptr->mode & PNG_HAVE_IDAT) &&
+       png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE)
+   {
+      unsigned int z_cmf = data[0];  /* zlib compression method and flags */
+      if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70)
+      {
+         /* Avoid memory underflows and multiplication overflows. */
+         /* The conditions below are practically always satisfied;
+            however, they still must be checked. */
+         if (length >= 2 &&
+             png_ptr->height < 16384 && png_ptr->width < 16384)
+         {
+            png_uint_32 uncompressed_idat_size = png_ptr->height *
+               ((png_ptr->width *
+               png_ptr->channels * png_ptr->bit_depth + 15) >> 3);
+            unsigned int z_cinfo = z_cmf >> 4;
+            unsigned int half_z_window_size = 1 << (z_cinfo + 7);
+            while (uncompressed_idat_size <= half_z_window_size &&
+                   half_z_window_size >= 256)
+            {
+               z_cinfo--;
+               half_z_window_size >>= 1;
+            }
+            z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4);
+            if (data[0] != (png_byte)z_cmf)
+            {
+               data[0] = (png_byte)z_cmf;
+               data[1] &= 0xe0;
+               data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f);
+            }
+         }
+      }
+      else
+         png_error(png_ptr,
+            "Invalid zlib compression method or flags in IDAT");
+   }
+
+   png_write_chunk(png_ptr, (png_bytep)png_IDAT, data, length);
+   png_ptr->mode |= PNG_HAVE_IDAT;
+}
+
+/* write an IEND chunk */
+void /* PRIVATE */
+png_write_IEND(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_IEND;
+#endif
+   png_debug(1, "in png_write_IEND\n");
+   png_write_chunk(png_ptr, (png_bytep)png_IEND, png_bytep_NULL,
+     (png_size_t)0);
+   png_ptr->mode |= PNG_HAVE_IEND;
+}
+
+#if defined(PNG_WRITE_gAMA_SUPPORTED)
+/* write a gAMA chunk */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_gAMA(png_structp png_ptr, double file_gamma)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_gAMA;
+#endif
+   png_uint_32 igamma;
+   png_byte buf[4];
+
+   png_debug(1, "in png_write_gAMA\n");
+   /* file_gamma is saved in 1/100,000ths */
+   igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5);
+   png_save_uint_32(buf, igamma);
+   png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_gAMA;
+#endif
+   png_byte buf[4];
+
+   png_debug(1, "in png_write_gAMA\n");
+   /* file_gamma is saved in 1/100,000ths */
+   png_save_uint_32(buf, (png_uint_32)file_gamma);
+   png_write_chunk(png_ptr, (png_bytep)png_gAMA, buf, (png_size_t)4);
+}
+#endif
+#endif
+
+#if defined(PNG_WRITE_sRGB_SUPPORTED)
+/* write a sRGB chunk */
+void /* PRIVATE */
+png_write_sRGB(png_structp png_ptr, int srgb_intent)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sRGB;
+#endif
+   png_byte buf[1];
+
+   png_debug(1, "in png_write_sRGB\n");
+   if(srgb_intent >= PNG_sRGB_INTENT_LAST)
+         png_warning(png_ptr,
+            "Invalid sRGB rendering intent specified");
+   buf[0]=(png_byte)srgb_intent;
+   png_write_chunk(png_ptr, (png_bytep)png_sRGB, buf, (png_size_t)1);
+}
+#endif
+
+#if defined(PNG_WRITE_iCCP_SUPPORTED)
+/* write an iCCP chunk */
+void /* PRIVATE */
+png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type,
+   png_charp profile, int profile_len)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_iCCP;
+#endif
+   png_size_t name_len;
+   png_charp new_name;
+   compression_state comp;
+   int embedded_profile_len = 0;
+
+   png_debug(1, "in png_write_iCCP\n");
+
+   comp.num_output_ptr = 0;
+   comp.max_output_ptr = 0;
+   comp.output_ptr = NULL;
+   comp.input = NULL;
+   comp.input_len = 0;
+
+   if (name == NULL || (name_len = png_check_keyword(png_ptr, name,
+      &new_name)) == 0)
+   {
+      png_warning(png_ptr, "Empty keyword in iCCP chunk");
+      return;
+   }
+
+   if (compression_type != PNG_COMPRESSION_TYPE_BASE)
+      png_warning(png_ptr, "Unknown compression type in iCCP chunk");
+
+   if (profile == NULL)
+      profile_len = 0;
+
+   if (profile_len > 3)
+      embedded_profile_len =
+          ((*( (png_bytep)profile  ))<<24) |
+          ((*( (png_bytep)profile+1))<<16) |
+          ((*( (png_bytep)profile+2))<< 8) |
+          ((*( (png_bytep)profile+3))    );
+
+   if (profile_len < embedded_profile_len)
+     {
+        png_warning(png_ptr,
+          "Embedded profile length too large in iCCP chunk");
+        return;
+     }
+
+   if (profile_len > embedded_profile_len)
+     {
+        png_warning(png_ptr,
+          "Truncating profile to actual length in iCCP chunk");
+        profile_len = embedded_profile_len;
+     }
+
+   if (profile_len)
+       profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len,
+          PNG_COMPRESSION_TYPE_BASE, &comp);
+
+   /* make sure we include the NULL after the name and the compression type */
+   png_write_chunk_start(png_ptr, png_iCCP,
+          (png_uint_32)name_len+profile_len+2);
+   new_name[name_len+1]=0x00;
+   png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2);
+
+   if (profile_len)
+      png_write_compressed_data_out(png_ptr, &comp);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_name);
+}
+#endif
+
+#if defined(PNG_WRITE_sPLT_SUPPORTED)
+/* write a sPLT chunk */
+void /* PRIVATE */
+png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sPLT;
+#endif
+   png_size_t name_len;
+   png_charp new_name;
+   png_byte entrybuf[10];
+   int entry_size = (spalette->depth == 8 ? 6 : 10);
+   int palette_size = entry_size * spalette->nentries;
+   png_sPLT_entryp ep;
+#ifdef PNG_NO_POINTER_INDEXING
+   int i;
+#endif
+
+   png_debug(1, "in png_write_sPLT\n");
+   if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr,
+      spalette->name, &new_name))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in sPLT chunk");
+      return;
+   }
+
+   /* make sure we include the NULL after the name */
+   png_write_chunk_start(png_ptr, png_sPLT,
+          (png_uint_32)(name_len + 2 + palette_size));
+   png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 1);
+   png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, 1);
+
+   /* loop through each palette entry, writing appropriately */
+#ifndef PNG_NO_POINTER_INDEXING
+   for (ep = spalette->entries; ep<spalette->entries+spalette->nentries; ep++)
+   {
+       if (spalette->depth == 8)
+       {
+           entrybuf[0] = (png_byte)ep->red;
+           entrybuf[1] = (png_byte)ep->green;
+           entrybuf[2] = (png_byte)ep->blue;
+           entrybuf[3] = (png_byte)ep->alpha;
+           png_save_uint_16(entrybuf + 4, ep->frequency);
+       }
+       else
+       {
+           png_save_uint_16(entrybuf + 0, ep->red);
+           png_save_uint_16(entrybuf + 2, ep->green);
+           png_save_uint_16(entrybuf + 4, ep->blue);
+           png_save_uint_16(entrybuf + 6, ep->alpha);
+           png_save_uint_16(entrybuf + 8, ep->frequency);
+       }
+       png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size);
+   }
+#else
+   ep=spalette->entries;
+   for (i=0; i>spalette->nentries; i++)
+   {
+       if (spalette->depth == 8)
+       {
+           entrybuf[0] = (png_byte)ep[i].red;
+           entrybuf[1] = (png_byte)ep[i].green;
+           entrybuf[2] = (png_byte)ep[i].blue;
+           entrybuf[3] = (png_byte)ep[i].alpha;
+           png_save_uint_16(entrybuf + 4, ep[i].frequency);
+       }
+       else
+       {
+           png_save_uint_16(entrybuf + 0, ep[i].red);
+           png_save_uint_16(entrybuf + 2, ep[i].green);
+           png_save_uint_16(entrybuf + 4, ep[i].blue);
+           png_save_uint_16(entrybuf + 6, ep[i].alpha);
+           png_save_uint_16(entrybuf + 8, ep[i].frequency);
+       }
+       png_write_chunk_data(png_ptr, entrybuf, entry_size);
+   }
+#endif
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_name);
+}
+#endif
+
+#if defined(PNG_WRITE_sBIT_SUPPORTED)
+/* write the sBIT chunk */
+void /* PRIVATE */
+png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sBIT;
+#endif
+   png_byte buf[4];
+   png_size_t size;
+
+   png_debug(1, "in png_write_sBIT\n");
+   /* make sure we don't depend upon the order of PNG_COLOR_8 */
+   if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_byte maxbits;
+
+      maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 :
+                png_ptr->usr_bit_depth);
+      if (sbit->red == 0 || sbit->red > maxbits ||
+          sbit->green == 0 || sbit->green > maxbits ||
+          sbit->blue == 0 || sbit->blue > maxbits)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[0] = sbit->red;
+      buf[1] = sbit->green;
+      buf[2] = sbit->blue;
+      size = 3;
+   }
+   else
+   {
+      if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[0] = sbit->gray;
+      size = 1;
+   }
+
+   if (color_type & PNG_COLOR_MASK_ALPHA)
+   {
+      if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth)
+      {
+         png_warning(png_ptr, "Invalid sBIT depth specified");
+         return;
+      }
+      buf[size++] = sbit->alpha;
+   }
+
+   png_write_chunk(png_ptr, (png_bytep)png_sBIT, buf, size);
+}
+#endif
+
+#if defined(PNG_WRITE_cHRM_SUPPORTED)
+/* write the cHRM chunk */
+#ifdef PNG_FLOATING_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_cHRM(png_structp png_ptr, double white_x, double white_y,
+   double red_x, double red_y, double green_x, double green_y,
+   double blue_x, double blue_y)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_cHRM;
+#endif
+   png_byte buf[32];
+   png_uint_32 itemp;
+
+   png_debug(1, "in png_write_cHRM\n");
+   /* each value is saved in 1/100,000ths */
+   if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 ||
+       white_x + white_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM white point specified");
+#if !defined(PNG_NO_CONSOLE_IO)
+      fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y);
+#endif
+      return;
+   }
+   itemp = (png_uint_32)(white_x * 100000.0 + 0.5);
+   png_save_uint_32(buf, itemp);
+   itemp = (png_uint_32)(white_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 4, itemp);
+
+   if (red_x < 0 ||  red_y < 0 || red_x + red_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM red point specified");
+      return;
+   }
+   itemp = (png_uint_32)(red_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 8, itemp);
+   itemp = (png_uint_32)(red_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 12, itemp);
+
+   if (green_x < 0 || green_y < 0 || green_x + green_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM green point specified");
+      return;
+   }
+   itemp = (png_uint_32)(green_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 16, itemp);
+   itemp = (png_uint_32)(green_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 20, itemp);
+
+   if (blue_x < 0 || blue_y < 0 || blue_x + blue_y > 1.0)
+   {
+      png_warning(png_ptr, "Invalid cHRM blue point specified");
+      return;
+   }
+   itemp = (png_uint_32)(blue_x * 100000.0 + 0.5);
+   png_save_uint_32(buf + 24, itemp);
+   itemp = (png_uint_32)(blue_y * 100000.0 + 0.5);
+   png_save_uint_32(buf + 28, itemp);
+
+   png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32);
+}
+#endif
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x,
+   png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y,
+   png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x,
+   png_fixed_point blue_y)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_cHRM;
+#endif
+   png_byte buf[32];
+
+   png_debug(1, "in png_write_cHRM\n");
+   /* each value is saved in 1/100,000ths */
+   if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid fixed cHRM white point specified");
+#if !defined(PNG_NO_CONSOLE_IO)
+      fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y);
+#endif
+      return;
+   }
+   png_save_uint_32(buf, (png_uint_32)white_x);
+   png_save_uint_32(buf + 4, (png_uint_32)white_y);
+
+   if (red_x + red_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid cHRM fixed red point specified");
+      return;
+   }
+   png_save_uint_32(buf + 8, (png_uint_32)red_x);
+   png_save_uint_32(buf + 12, (png_uint_32)red_y);
+
+   if (green_x + green_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid fixed cHRM green point specified");
+      return;
+   }
+   png_save_uint_32(buf + 16, (png_uint_32)green_x);
+   png_save_uint_32(buf + 20, (png_uint_32)green_y);
+
+   if (blue_x + blue_y > 100000L)
+   {
+      png_warning(png_ptr, "Invalid fixed cHRM blue point specified");
+      return;
+   }
+   png_save_uint_32(buf + 24, (png_uint_32)blue_x);
+   png_save_uint_32(buf + 28, (png_uint_32)blue_y);
+
+   png_write_chunk(png_ptr, (png_bytep)png_cHRM, buf, (png_size_t)32);
+}
+#endif
+#endif
+
+#if defined(PNG_WRITE_tRNS_SUPPORTED)
+/* write the tRNS chunk */
+void /* PRIVATE */
+png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran,
+   int num_trans, int color_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_tRNS;
+#endif
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_tRNS\n");
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette)
+      {
+         png_warning(png_ptr,"Invalid number of transparent colors specified");
+         return;
+      }
+      /* write the chunk out as it is */
+      png_write_chunk(png_ptr, (png_bytep)png_tRNS, trans, (png_size_t)num_trans);
+   }
+   else if (color_type == PNG_COLOR_TYPE_GRAY)
+   {
+      /* one 16 bit value */
+      if(tran->gray >= (1 << png_ptr->bit_depth))
+      {
+         png_warning(png_ptr,
+           "Ignoring attempt to write tRNS chunk out-of-range for bit_depth");
+         return;
+      }
+      png_save_uint_16(buf, tran->gray);
+      png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)2);
+   }
+   else if (color_type == PNG_COLOR_TYPE_RGB)
+   {
+      /* three 16 bit values */
+      png_save_uint_16(buf, tran->red);
+      png_save_uint_16(buf + 2, tran->green);
+      png_save_uint_16(buf + 4, tran->blue);
+      if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+         {
+            png_warning(png_ptr,
+              "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8");
+            return;
+         }
+      png_write_chunk(png_ptr, (png_bytep)png_tRNS, buf, (png_size_t)6);
+   }
+   else
+   {
+      png_warning(png_ptr, "Can't write tRNS with an alpha channel");
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_bKGD_SUPPORTED)
+/* write the background chunk */
+void /* PRIVATE */
+png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_bKGD;
+#endif
+   png_byte buf[6];
+
+   png_debug(1, "in png_write_bKGD\n");
+   if (color_type == PNG_COLOR_TYPE_PALETTE)
+   {
+      if (
+#if defined(PNG_MNG_FEATURES_SUPPORTED)
+          (png_ptr->num_palette ||
+          (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) &&
+#endif
+         back->index > png_ptr->num_palette)
+      {
+         png_warning(png_ptr, "Invalid background palette index");
+         return;
+      }
+      buf[0] = back->index;
+      png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)1);
+   }
+   else if (color_type & PNG_COLOR_MASK_COLOR)
+   {
+      png_save_uint_16(buf, back->red);
+      png_save_uint_16(buf + 2, back->green);
+      png_save_uint_16(buf + 4, back->blue);
+      if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4]))
+         {
+            png_warning(png_ptr,
+              "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8");
+            return;
+         }
+      png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)6);
+   }
+   else
+   {
+      if(back->gray >= (1 << png_ptr->bit_depth))
+      {
+         png_warning(png_ptr,
+           "Ignoring attempt to write bKGD chunk out-of-range for bit_depth");
+         return;
+      }
+      png_save_uint_16(buf, back->gray);
+      png_write_chunk(png_ptr, (png_bytep)png_bKGD, buf, (png_size_t)2);
+   }
+}
+#endif
+
+#if defined(PNG_WRITE_hIST_SUPPORTED)
+/* write the histogram */
+void /* PRIVATE */
+png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_hIST;
+#endif
+   int i;
+   png_byte buf[3];
+
+   png_debug(1, "in png_write_hIST\n");
+   if (num_hist > (int)png_ptr->num_palette)
+   {
+      png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist,
+         png_ptr->num_palette);
+      png_warning(png_ptr, "Invalid number of histogram entries specified");
+      return;
+   }
+
+   png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(num_hist * 2));
+   for (i = 0; i < num_hist; i++)
+   {
+      png_save_uint_16(buf, hist[i]);
+      png_write_chunk_data(png_ptr, buf, (png_size_t)2);
+   }
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \
+    defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED)
+/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification,
+ * and if invalid, correct the keyword rather than discarding the entire
+ * chunk.  The PNG 1.0 specification requires keywords 1-79 characters in
+ * length, forbids leading or trailing whitespace, multiple internal spaces,
+ * and the non-break space (0x80) from ISO 8859-1.  Returns keyword length.
+ *
+ * The new_key is allocated to hold the corrected keyword and must be freed
+ * by the calling routine.  This avoids problems with trying to write to
+ * static keywords without having to have duplicate copies of the strings.
+ */
+png_size_t /* PRIVATE */
+png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key)
+{
+   png_size_t key_len;
+   png_charp kp, dp;
+   int kflag;
+   int kwarn=0;
+
+   png_debug(1, "in png_check_keyword\n");
+   *new_key = NULL;
+
+   if (key == NULL || (key_len = png_strlen(key)) == 0)
+   {
+      png_warning(png_ptr, "zero length keyword");
+      return ((png_size_t)0);
+   }
+
+   png_debug1(2, "Keyword to be checked is '%s'\n", key);
+
+   *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2));
+   if (*new_key == NULL)
+   {
+      png_warning(png_ptr, "Out of memory while procesing keyword");
+      return ((png_size_t)0);
+   }
+
+   /* Replace non-printing characters with a blank and print a warning */
+   for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++)
+   {
+      if ((png_byte)*kp < 0x20 ||
+         ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1))
+      {
+#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE)
+         char msg[40];
+
+         png_snprintf(msg, 40,
+           "invalid keyword character 0x%02X", (png_byte)*kp);
+         png_warning(png_ptr, msg);
+#else
+         png_warning(png_ptr, "invalid character in keyword");
+#endif
+         *dp = ' ';
+      }
+      else
+      {
+         *dp = *kp;
+      }
+   }
+   *dp = '\0';
+
+   /* Remove any trailing white space. */
+   kp = *new_key + key_len - 1;
+   if (*kp == ' ')
+   {
+      png_warning(png_ptr, "trailing spaces removed from keyword");
+
+      while (*kp == ' ')
+      {
+        *(kp--) = '\0';
+        key_len--;
+      }
+   }
+
+   /* Remove any leading white space. */
+   kp = *new_key;
+   if (*kp == ' ')
+   {
+      png_warning(png_ptr, "leading spaces removed from keyword");
+
+      while (*kp == ' ')
+      {
+        kp++;
+        key_len--;
+      }
+   }
+
+   png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp);
+
+   /* Remove multiple internal spaces. */
+   for (kflag = 0, dp = *new_key; *kp != '\0'; kp++)
+   {
+      if (*kp == ' ' && kflag == 0)
+      {
+         *(dp++) = *kp;
+         kflag = 1;
+      }
+      else if (*kp == ' ')
+      {
+         key_len--;
+         kwarn=1;
+      }
+      else
+      {
+         *(dp++) = *kp;
+         kflag = 0;
+      }
+   }
+   *dp = '\0';
+   if(kwarn)
+      png_warning(png_ptr, "extra interior spaces removed from keyword");
+
+   if (key_len == 0)
+   {
+      png_free(png_ptr, *new_key);
+      *new_key=NULL;
+      png_warning(png_ptr, "Zero length keyword");
+   }
+
+   if (key_len > 79)
+   {
+      png_warning(png_ptr, "keyword length must be 1 - 79 characters");
+      new_key[79] = '\0';
+      key_len = 79;
+   }
+
+   return (key_len);
+}
+#endif
+
+#if defined(PNG_WRITE_tEXt_SUPPORTED)
+/* write a tEXt chunk */
+void /* PRIVATE */
+png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text,
+   png_size_t text_len)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_tEXt;
+#endif
+   png_size_t key_len;
+   png_charp new_key;
+
+   png_debug(1, "in png_write_tEXt\n");
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in tEXt chunk");
+      return;
+   }
+
+   if (text == NULL || *text == '\0')
+      text_len = 0;
+   else
+      text_len = png_strlen(text);
+
+   /* make sure we include the 0 after the key */
+   png_write_chunk_start(png_ptr, (png_bytep)png_tEXt, (png_uint_32)key_len+text_len+1);
+   /*
+    * We leave it to the application to meet PNG-1.0 requirements on the
+    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
+    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    */
+   png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1);
+   if (text_len)
+      png_write_chunk_data(png_ptr, (png_bytep)text, text_len);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_key);
+}
+#endif
+
+#if defined(PNG_WRITE_zTXt_SUPPORTED)
+/* write a compressed text chunk */
+void /* PRIVATE */
+png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text,
+   png_size_t text_len, int compression)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_zTXt;
+#endif
+   png_size_t key_len;
+   char buf[1];
+   png_charp new_key;
+   compression_state comp;
+
+   png_debug(1, "in png_write_zTXt\n");
+
+   comp.num_output_ptr = 0;
+   comp.max_output_ptr = 0;
+   comp.output_ptr = NULL;
+   comp.input = NULL;
+   comp.input_len = 0;
+
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in zTXt chunk");
+      return;
+   }
+
+   if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE)
+   {
+      png_write_tEXt(png_ptr, new_key, text, (png_size_t)0);
+      png_free(png_ptr, new_key);
+      return;
+   }
+
+   text_len = png_strlen(text);
+
+   /* compute the compressed data; do it now for the length */
+   text_len = png_text_compress(png_ptr, text, text_len, compression,
+       &comp);
+
+   /* write start of chunk */
+   png_write_chunk_start(png_ptr, (png_bytep)png_zTXt, (png_uint_32)
+      (key_len+text_len+2));
+   /* write key */
+   png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1);
+   png_free(png_ptr, new_key);
+
+   buf[0] = (png_byte)compression;
+   /* write compression */
+   png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1);
+   /* write the compressed data */
+   png_write_compressed_data_out(png_ptr, &comp);
+
+   /* close the chunk */
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_iTXt_SUPPORTED)
+/* write an iTXt chunk */
+void /* PRIVATE */
+png_write_iTXt(png_structp png_ptr, int compression, png_charp key,
+    png_charp lang, png_charp lang_key, png_charp text)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_iTXt;
+#endif
+   png_size_t lang_len, key_len, lang_key_len, text_len;
+   png_charp new_lang, new_key;
+   png_byte cbuf[2];
+   compression_state comp;
+
+   png_debug(1, "in png_write_iTXt\n");
+
+   comp.num_output_ptr = 0;
+   comp.max_output_ptr = 0;
+   comp.output_ptr = NULL;
+   comp.input = NULL;
+
+   if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0)
+   {
+      png_warning(png_ptr, "Empty keyword in iTXt chunk");
+      return;
+   }
+   if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0)
+   {
+      png_warning(png_ptr, "Empty language field in iTXt chunk");
+      new_lang = NULL;
+      lang_len = 0;
+   }
+
+   if (lang_key == NULL)
+     lang_key_len = 0;
+   else
+     lang_key_len = png_strlen(lang_key);
+
+   if (text == NULL)
+      text_len = 0;
+   else
+     text_len = png_strlen(text);
+
+   /* compute the compressed data; do it now for the length */
+   text_len = png_text_compress(png_ptr, text, text_len, compression-2,
+      &comp);
+
+
+   /* make sure we include the compression flag, the compression byte,
+    * and the NULs after the key, lang, and lang_key parts */
+
+   png_write_chunk_start(png_ptr, (png_bytep)png_iTXt,
+          (png_uint_32)(
+        5 /* comp byte, comp flag, terminators for key, lang and lang_key */
+        + key_len
+        + lang_len
+        + lang_key_len
+        + text_len));
+
+   /*
+    * We leave it to the application to meet PNG-1.0 requirements on the
+    * contents of the text.  PNG-1.0 through PNG-1.2 discourage the use of
+    * any non-Latin-1 characters except for NEWLINE.  ISO PNG will forbid them.
+    * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG.
+    */
+   png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1);
+
+   /* set the compression flag */
+   if (compression == PNG_ITXT_COMPRESSION_NONE || \
+       compression == PNG_TEXT_COMPRESSION_NONE)
+       cbuf[0] = 0;
+   else /* compression == PNG_ITXT_COMPRESSION_zTXt */
+       cbuf[0] = 1;
+   /* set the compression method */
+   cbuf[1] = 0;
+   png_write_chunk_data(png_ptr, cbuf, 2);
+
+   cbuf[0] = 0;
+   png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1);
+   png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1);
+   png_write_compressed_data_out(png_ptr, &comp);
+
+   png_write_chunk_end(png_ptr);
+   png_free(png_ptr, new_key);
+   if (new_lang)
+     png_free(png_ptr, new_lang);
+}
+#endif
+
+#if defined(PNG_WRITE_oFFs_SUPPORTED)
+/* write the oFFs chunk */
+void /* PRIVATE */
+png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset,
+   int unit_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_oFFs;
+#endif
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_oFFs\n");
+   if (unit_type >= PNG_OFFSET_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for oFFs chunk");
+
+   png_save_int_32(buf, x_offset);
+   png_save_int_32(buf + 4, y_offset);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_chunk(png_ptr, png_oFFs, buf, (png_size_t)9);
+}
+#endif
+#if defined(PNG_WRITE_pCAL_SUPPORTED)
+/* write the pCAL chunk (described in the PNG extensions document) */
+void /* PRIVATE */
+png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0,
+   png_int_32 X1, int type, int nparams, png_charp units, png_charpp params)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_pCAL;
+#endif
+   png_size_t purpose_len, units_len, total_len;
+   png_uint_32p params_len;
+   png_byte buf[10];
+   png_charp new_purpose;
+   int i;
+
+   png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams);
+   if (type >= PNG_EQUATION_LAST)
+      png_warning(png_ptr, "Unrecognized equation type for pCAL chunk");
+
+   purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1;
+   png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len);
+   units_len = png_strlen(units) + (nparams == 0 ? 0 : 1);
+   png_debug1(3, "pCAL units length = %d\n", (int)units_len);
+   total_len = purpose_len + units_len + 10;
+
+   params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams
+      *png_sizeof(png_uint_32)));
+
+   /* Find the length of each parameter, making sure we don't count the
+      null terminator for the last parameter. */
+   for (i = 0; i < nparams; i++)
+   {
+      params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1);
+      png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]);
+      total_len += (png_size_t)params_len[i];
+   }
+
+   png_debug1(3, "pCAL total length = %d\n", (int)total_len);
+   png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len);
+   png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len);
+   png_save_int_32(buf, X0);
+   png_save_int_32(buf + 4, X1);
+   buf[8] = (png_byte)type;
+   buf[9] = (png_byte)nparams;
+   png_write_chunk_data(png_ptr, buf, (png_size_t)10);
+   png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len);
+
+   png_free(png_ptr, new_purpose);
+
+   for (i = 0; i < nparams; i++)
+   {
+      png_write_chunk_data(png_ptr, (png_bytep)params[i],
+         (png_size_t)params_len[i]);
+   }
+
+   png_free(png_ptr, params_len);
+   png_write_chunk_end(png_ptr);
+}
+#endif
+
+#if defined(PNG_WRITE_sCAL_SUPPORTED)
+/* write the sCAL chunk */
+#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO)
+void /* PRIVATE */
+png_write_sCAL(png_structp png_ptr, int unit, double width, double height)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sCAL;
+#endif
+   char buf[64];
+   png_size_t total_len;
+
+   png_debug(1, "in png_write_sCAL\n");
+
+   buf[0] = (char)unit;
+#if defined(_WIN32_WCE)
+/* sprintf() function is not supported on WindowsCE */
+   {
+      wchar_t wc_buf[32];
+      size_t wc_len;
+      swprintf(wc_buf, TEXT("%12.12e"), width);
+      wc_len = wcslen(wc_buf);
+      WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, NULL);
+      total_len = wc_len + 2;
+      swprintf(wc_buf, TEXT("%12.12e"), height);
+      wc_len = wcslen(wc_buf);
+      WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len,
+         NULL, NULL);
+      total_len += wc_len;
+   }
+#else
+   png_snprintf(buf + 1, 63, "%12.12e", width);
+   total_len = 1 + png_strlen(buf + 1) + 1;
+   png_snprintf(buf + total_len, 64-total_len, "%12.12e", height);
+   total_len += png_strlen(buf + total_len);
+#endif
+
+   png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len);
+   png_write_chunk(png_ptr, png_sCAL, (png_bytep)buf, total_len);
+}
+#else
+#ifdef PNG_FIXED_POINT_SUPPORTED
+void /* PRIVATE */
+png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width,
+   png_charp height)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_sCAL;
+#endif
+   png_byte buf[64];
+   png_size_t wlen, hlen, total_len;
+
+   png_debug(1, "in png_write_sCAL_s\n");
+
+   wlen = png_strlen(width);
+   hlen = png_strlen(height);
+   total_len = wlen + hlen + 2;
+   if (total_len > 64)
+   {
+      png_warning(png_ptr, "Can't write sCAL (buffer too small)");
+      return;
+   }
+
+   buf[0] = (png_byte)unit;
+   png_memcpy(buf + 1, width, wlen + 1);      /* append the '\0' here */
+   png_memcpy(buf + wlen + 2, height, hlen);  /* do NOT append the '\0' here */
+
+   png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len);
+   png_write_chunk(png_ptr, png_sCAL, buf, total_len);
+}
+#endif
+#endif
+#endif
+
+#if defined(PNG_WRITE_pHYs_SUPPORTED)
+/* write the pHYs chunk */
+void /* PRIVATE */
+png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit,
+   png_uint_32 y_pixels_per_unit,
+   int unit_type)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_pHYs;
+#endif
+   png_byte buf[9];
+
+   png_debug(1, "in png_write_pHYs\n");
+   if (unit_type >= PNG_RESOLUTION_LAST)
+      png_warning(png_ptr, "Unrecognized unit type for pHYs chunk");
+
+   png_save_uint_32(buf, x_pixels_per_unit);
+   png_save_uint_32(buf + 4, y_pixels_per_unit);
+   buf[8] = (png_byte)unit_type;
+
+   png_write_chunk(png_ptr, png_pHYs, buf, (png_size_t)9);
+}
+#endif
+
+#if defined(PNG_WRITE_tIME_SUPPORTED)
+/* Write the tIME chunk.  Use either png_convert_from_struct_tm()
+ * or png_convert_from_time_t(), or fill in the structure yourself.
+ */
+void /* PRIVATE */
+png_write_tIME(png_structp png_ptr, png_timep mod_time)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   PNG_tIME;
+#endif
+   png_byte buf[7];
+
+   png_debug(1, "in png_write_tIME\n");
+   if (mod_time->month  > 12 || mod_time->month  < 1 ||
+       mod_time->day    > 31 || mod_time->day    < 1 ||
+       mod_time->hour   > 23 || mod_time->second > 60)
+   {
+      png_warning(png_ptr, "Invalid time specified for tIME chunk");
+      return;
+   }
+
+   png_save_uint_16(buf, mod_time->year);
+   buf[2] = mod_time->month;
+   buf[3] = mod_time->day;
+   buf[4] = mod_time->hour;
+   buf[5] = mod_time->minute;
+   buf[6] = mod_time->second;
+
+   png_write_chunk(png_ptr, png_tIME, buf, (png_size_t)7);
+}
+#endif
+
+/* initializes the row writing capability of libpng */
+void /* PRIVATE */
+png_write_start_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   png_size_t buf_size;
+
+   png_debug(1, "in png_write_start_row\n");
+   buf_size = (png_size_t)(PNG_ROWBYTES(
+      png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1);
+
+   /* set up row buffer */
+   png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size);
+   png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE;
+
+#ifndef PNG_NO_WRITE_FILTERING
+   /* set up filtering buffer, if using this filter */
+   if (png_ptr->do_filter & PNG_FILTER_SUB)
+   {
+      png_ptr->sub_row = (png_bytep)png_malloc(png_ptr,
+         (png_ptr->rowbytes + 1));
+      png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB;
+   }
+
+   /* We only need to keep the previous row if we are using one of these. */
+   if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH))
+   {
+     /* set up previous row buffer */
+      png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size);
+      png_memset(png_ptr->prev_row, 0, buf_size);
+
+      if (png_ptr->do_filter & PNG_FILTER_UP)
+      {
+         png_ptr->up_row = (png_bytep)png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->up_row[0] = PNG_FILTER_VALUE_UP;
+      }
+
+      if (png_ptr->do_filter & PNG_FILTER_AVG)
+      {
+         png_ptr->avg_row = (png_bytep)png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG;
+      }
+
+      if (png_ptr->do_filter & PNG_FILTER_PAETH)
+      {
+         png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr,
+            (png_ptr->rowbytes + 1));
+         png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH;
+      }
+#endif /* PNG_NO_WRITE_FILTERING */
+   }
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* if interlaced, we need to set up width and height of pass */
+   if (png_ptr->interlaced)
+   {
+      if (!(png_ptr->transformations & PNG_INTERLACE))
+      {
+         png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 -
+            png_pass_ystart[0]) / png_pass_yinc[0];
+         png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 -
+            png_pass_start[0]) / png_pass_inc[0];
+      }
+      else
+      {
+         png_ptr->num_rows = png_ptr->height;
+         png_ptr->usr_width = png_ptr->width;
+      }
+   }
+   else
+#endif
+   {
+      png_ptr->num_rows = png_ptr->height;
+      png_ptr->usr_width = png_ptr->width;
+   }
+   png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+   png_ptr->zstream.next_out = png_ptr->zbuf;
+}
+
+/* Internal use only.  Called when finished processing a row of data. */
+void /* PRIVATE */
+png_write_finish_row(png_structp png_ptr)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+
+   /* start of interlace block in the y direction */
+   int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1};
+
+   /* offset to next interlace block in the y direction */
+   int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2};
+#endif
+
+   int ret;
+
+   png_debug(1, "in png_write_finish_row\n");
+   /* next row */
+   png_ptr->row_number++;
+
+   /* see if we are done */
+   if (png_ptr->row_number < png_ptr->num_rows)
+      return;
+
+#ifdef PNG_WRITE_INTERLACING_SUPPORTED
+   /* if interlaced, go to next pass */
+   if (png_ptr->interlaced)
+   {
+      png_ptr->row_number = 0;
+      if (png_ptr->transformations & PNG_INTERLACE)
+      {
+         png_ptr->pass++;
+      }
+      else
+      {
+         /* loop until we find a non-zero width or height pass */
+         do
+         {
+            png_ptr->pass++;
+            if (png_ptr->pass >= 7)
+               break;
+            png_ptr->usr_width = (png_ptr->width +
+               png_pass_inc[png_ptr->pass] - 1 -
+               png_pass_start[png_ptr->pass]) /
+               png_pass_inc[png_ptr->pass];
+            png_ptr->num_rows = (png_ptr->height +
+               png_pass_yinc[png_ptr->pass] - 1 -
+               png_pass_ystart[png_ptr->pass]) /
+               png_pass_yinc[png_ptr->pass];
+            if (png_ptr->transformations & PNG_INTERLACE)
+               break;
+         } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0);
+
+      }
+
+      /* reset the row above the image for the next pass */
+      if (png_ptr->pass < 7)
+      {
+         if (png_ptr->prev_row != NULL)
+            png_memset(png_ptr->prev_row, 0,
+               (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels*
+               png_ptr->usr_bit_depth,png_ptr->width))+1);
+         return;
+      }
+   }
+#endif
+
+   /* if we get here, we've just written the last row, so we need
+      to flush the compressor */
+   do
+   {
+      /* tell the compressor we are done */
+      ret = deflate(&png_ptr->zstream, Z_FINISH);
+      /* check for an error */
+      if (ret == Z_OK)
+      {
+         /* check to see if we need more room */
+         if (!(png_ptr->zstream.avail_out))
+         {
+            png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+            png_ptr->zstream.next_out = png_ptr->zbuf;
+            png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+         }
+      }
+      else if (ret != Z_STREAM_END)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+   } while (ret != Z_STREAM_END);
+
+   /* write any extra space */
+   if (png_ptr->zstream.avail_out < png_ptr->zbuf_size)
+   {
+      png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size -
+         png_ptr->zstream.avail_out);
+   }
+
+   deflateReset(&png_ptr->zstream);
+   png_ptr->zstream.data_type = Z_BINARY;
+}
+
+#if defined(PNG_WRITE_INTERLACING_SUPPORTED)
+/* Pick out the correct pixels for the interlace pass.
+ * The basic idea here is to go through the row with a source
+ * pointer and a destination pointer (sp and dp), and copy the
+ * correct pixels for the pass.  As the row gets compacted,
+ * sp will always be >= dp, so we should never overwrite anything.
+ * See the default: case for the easiest code to understand.
+ */
+void /* PRIVATE */
+png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass)
+{
+#ifdef PNG_USE_LOCAL_ARRAYS
+   /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */
+
+   /* start of interlace block */
+   int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0};
+
+   /* offset to next interlace block */
+   int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1};
+#endif
+
+   png_debug(1, "in png_do_write_interlace\n");
+   /* we don't have to do anything on the last pass (6) */
+#if defined(PNG_USELESS_TESTS_SUPPORTED)
+   if (row != NULL && row_info != NULL && pass < 6)
+#else
+   if (pass < 6)
+#endif
+   {
+      /* each pixel depth is handled separately */
+      switch (row_info->pixel_depth)
+      {
+         case 1:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            d = 0;
+            shift = 7;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 3);
+               value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 7;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift--;
+
+            }
+            if (shift != 7)
+               *dp = (png_byte)d;
+            break;
+         }
+         case 2:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 6;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 2);
+               value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 6;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift -= 2;
+            }
+            if (shift != 6)
+                   *dp = (png_byte)d;
+            break;
+         }
+         case 4:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            int shift;
+            int d;
+            int value;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+
+            dp = row;
+            shift = 4;
+            d = 0;
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               sp = row + (png_size_t)(i >> 1);
+               value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f;
+               d |= (value << shift);
+
+               if (shift == 0)
+               {
+                  shift = 4;
+                  *dp++ = (png_byte)d;
+                  d = 0;
+               }
+               else
+                  shift -= 4;
+            }
+            if (shift != 4)
+               *dp = (png_byte)d;
+            break;
+         }
+         default:
+         {
+            png_bytep sp;
+            png_bytep dp;
+            png_uint_32 i;
+            png_uint_32 row_width = row_info->width;
+            png_size_t pixel_bytes;
+
+            /* start at the beginning */
+            dp = row;
+            /* find out how many bytes each pixel takes up */
+            pixel_bytes = (row_info->pixel_depth >> 3);
+            /* loop through the row, only looking at the pixels that
+               matter */
+            for (i = png_pass_start[pass]; i < row_width;
+               i += png_pass_inc[pass])
+            {
+               /* find out where the original pixel is */
+               sp = row + (png_size_t)i * pixel_bytes;
+               /* move the pixel */
+               if (dp != sp)
+                  png_memcpy(dp, sp, pixel_bytes);
+               /* next pixel */
+               dp += pixel_bytes;
+            }
+            break;
+         }
+      }
+      /* set new row width */
+      row_info->width = (row_info->width +
+         png_pass_inc[pass] - 1 -
+         png_pass_start[pass]) /
+         png_pass_inc[pass];
+         row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,
+            row_info->width);
+   }
+}
+#endif
+
+/* This filters the row, chooses which filter to use, if it has not already
+ * been specified by the application, and then writes the row out with the
+ * chosen filter.
+ */
+#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1)
+#define PNG_HISHIFT 10
+#define PNG_LOMASK ((png_uint_32)0xffffL)
+#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT))
+void /* PRIVATE */
+png_write_find_filter(png_structp png_ptr, png_row_infop row_info)
+{
+   png_bytep prev_row, best_row, row_buf;
+   png_uint_32 mins, bpp;
+   png_byte filter_to_do = png_ptr->do_filter;
+   png_uint_32 row_bytes = row_info->rowbytes;
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   int num_p_filters = (int)png_ptr->num_prev_filters;
+#endif
+
+   png_debug(1, "in png_write_find_filter\n");
+   /* find out how many bytes offset each pixel is */
+   bpp = (row_info->pixel_depth + 7) >> 3;
+
+   prev_row = png_ptr->prev_row;
+   best_row = row_buf = png_ptr->row_buf;
+#ifndef PNG_NO_WRITE_FILTER
+   mins = PNG_MAXSUM;
+
+   /* The prediction method we use is to find which method provides the
+    * smallest value when summing the absolute values of the distances
+    * from zero, using anything >= 128 as negative numbers.  This is known
+    * as the "minimum sum of absolute differences" heuristic.  Other
+    * heuristics are the "weighted minimum sum of absolute differences"
+    * (experimental and can in theory improve compression), and the "zlib
+    * predictive" method (not implemented yet), which does test compressions
+    * of lines using different filter methods, and then chooses the
+    * (series of) filter(s) that give minimum compressed data size (VERY
+    * computationally expensive).
+    *
+    * GRR 980525:  consider also
+    *   (1) minimum sum of absolute differences from running average (i.e.,
+    *       keep running sum of non-absolute differences & count of bytes)
+    *       [track dispersion, too?  restart average if dispersion too large?]
+    *  (1b) minimum sum of absolute differences from sliding average, probably
+    *       with window size <= deflate window (usually 32K)
+    *   (2) minimum sum of squared differences from zero or running average
+    *       (i.e., ~ root-mean-square approach)
+    */
+
+
+   /* We don't need to test the 'no filter' case if this is the only filter
+    * that has been chosen, as it doesn't actually do anything to the data.
+    */
+   if ((filter_to_do & PNG_FILTER_NONE) &&
+       filter_to_do != PNG_FILTER_NONE)
+   {
+      png_bytep rp;
+      png_uint_32 sum = 0;
+      png_uint_32 i;
+      int v;
+
+      for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++)
+      {
+         v = *rp;
+         sum += (v < 128) ? v : 256 - v;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         png_uint_32 sumhi, sumlo;
+         int j;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */
+
+         /* Reduce the sum if we match any of the previous rows */
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         /* Factor in the cost of this filter (this is here for completeness,
+          * but it makes no sense to have a "cost" for the NONE filter, as
+          * it has the minimum possible computational cost - none).
+          */
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+      mins = sum;
+   }
+
+   /* sub filter */
+   if (filter_to_do == PNG_FILTER_SUB)
+   /* it's the only filter so no testing is needed */
+   {
+      png_bytep rp, lp, dp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+           i++, rp++, dp++)
+      {
+         *dp = *rp;
+      }
+      for (lp = row_buf + 1; i < row_bytes;
+         i++, rp++, lp++, dp++)
+      {
+         *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+      }
+      best_row = png_ptr->sub_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_SUB)
+   {
+      png_bytep rp, dp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      /* We temporarily increase the "minimum sum" by the factor we
+       * would reduce the sum of this filter, so that we can do the
+       * early exit comparison without scaling the sum each time.
+       */
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp;
+           i++, rp++, dp++)
+      {
+         v = *dp = *rp;
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+      for (lp = row_buf + 1; i < row_bytes;
+         i++, rp++, lp++, dp++)
+      {
+         v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB)
+            {
+               sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->sub_row;
+      }
+   }
+
+   /* up filter */
+   if (filter_to_do == PNG_FILTER_UP)
+   {
+      png_bytep rp, dp, pp;
+      png_uint_32 i;
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+           pp = prev_row + 1; i < row_bytes;
+           i++, rp++, pp++, dp++)
+      {
+         *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff);
+      }
+      best_row = png_ptr->up_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_UP)
+   {
+      png_bytep rp, dp, pp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1,
+           pp = prev_row + 1; i < row_bytes; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->up_row;
+      }
+   }
+
+   /* avg filter */
+   if (filter_to_do == PNG_FILTER_AVG)
+   {
+      png_bytep rp, dp, pp, lp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+      }
+      for (lp = row_buf + 1; i < row_bytes; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2))
+                 & 0xff);
+      }
+      best_row = png_ptr->avg_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_AVG)
+   {
+      png_bytep rp, dp, pp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+      for (lp = row_buf + 1; i < row_bytes; i++)
+      {
+         v = *dp++ =
+          (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         mins = sum;
+         best_row = png_ptr->avg_row;
+      }
+   }
+
+   /* Paeth filter */
+   if (filter_to_do == PNG_FILTER_PAETH)
+   {
+      png_bytep rp, dp, pp, cp, lp;
+      png_uint_32 i;
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+      }
+
+      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+      {
+         int a, b, c, pa, pb, pc, p;
+
+         b = *pp++;
+         c = *cp++;
+         a = *lp++;
+
+         p = b - c;
+         pc = a - c;
+
+#ifdef PNG_USE_ABS
+         pa = abs(p);
+         pb = abs(pc);
+         pc = abs(p + pc);
+#else
+         pa = p < 0 ? -p : p;
+         pb = pc < 0 ? -pc : pc;
+         pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+
+         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+
+         *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+      }
+      best_row = png_ptr->paeth_row;
+   }
+
+   else if (filter_to_do & PNG_FILTER_PAETH)
+   {
+      png_bytep rp, dp, pp, cp, lp;
+      png_uint_32 sum = 0, lmins = mins;
+      png_uint_32 i;
+      int v;
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 lmhi, lmlo;
+         lmlo = lmins & PNG_LOMASK;
+         lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+            {
+               lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+         lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+
+         if (lmhi > PNG_HIMASK)
+            lmins = PNG_MAXSUM;
+         else
+            lmins = (lmhi << PNG_HISHIFT) + lmlo;
+      }
+#endif
+
+      for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1,
+           pp = prev_row + 1; i < bpp; i++)
+      {
+         v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+      }
+
+      for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++)
+      {
+         int a, b, c, pa, pb, pc, p;
+
+         b = *pp++;
+         c = *cp++;
+         a = *lp++;
+
+#ifndef PNG_SLOW_PAETH
+         p = b - c;
+         pc = a - c;
+#ifdef PNG_USE_ABS
+         pa = abs(p);
+         pb = abs(pc);
+         pc = abs(p + pc);
+#else
+         pa = p < 0 ? -p : p;
+         pb = pc < 0 ? -pc : pc;
+         pc = (p + pc) < 0 ? -(p + pc) : p + pc;
+#endif
+         p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c;
+#else /* PNG_SLOW_PAETH */
+         p = a + b - c;
+         pa = abs(p - a);
+         pb = abs(p - b);
+         pc = abs(p - c);
+         if (pa <= pb && pa <= pc)
+            p = a;
+         else if (pb <= pc)
+            p = b;
+         else
+            p = c;
+#endif /* PNG_SLOW_PAETH */
+
+         v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff);
+
+         sum += (v < 128) ? v : 256 - v;
+
+         if (sum > lmins)  /* We are already worse, don't continue. */
+            break;
+      }
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+      if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED)
+      {
+         int j;
+         png_uint_32 sumhi, sumlo;
+         sumlo = sum & PNG_LOMASK;
+         sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK;
+
+         for (j = 0; j < num_p_filters; j++)
+         {
+            if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH)
+            {
+               sumlo = (sumlo * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+               sumhi = (sumhi * png_ptr->filter_weights[j]) >>
+                  PNG_WEIGHT_SHIFT;
+            }
+         }
+
+         sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+         sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >>
+            PNG_COST_SHIFT;
+
+         if (sumhi > PNG_HIMASK)
+            sum = PNG_MAXSUM;
+         else
+            sum = (sumhi << PNG_HISHIFT) + sumlo;
+      }
+#endif
+
+      if (sum < mins)
+      {
+         best_row = png_ptr->paeth_row;
+      }
+   }
+#endif /* PNG_NO_WRITE_FILTER */
+   /* Do the actual writing of the filtered row data from the chosen filter. */
+
+   png_write_filtered_row(png_ptr, best_row);
+
+#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED)
+   /* Save the type of filter we picked this time for future calculations */
+   if (png_ptr->num_prev_filters > 0)
+   {
+      int j;
+      for (j = 1; j < num_p_filters; j++)
+      {
+         png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1];
+      }
+      png_ptr->prev_filters[j] = best_row[0];
+   }
+#endif
+}
+
+
+/* Do the actual writing of a previously filtered row. */
+void /* PRIVATE */
+png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row)
+{
+   png_debug(1, "in png_write_filtered_row\n");
+   png_debug1(2, "filter = %d\n", filtered_row[0]);
+   /* set up the zlib input buffer */
+
+   png_ptr->zstream.next_in = filtered_row;
+   png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1;
+   /* repeat until we have compressed all the data */
+   do
+   {
+      int ret; /* return of zlib */
+
+      /* compress the data */
+      ret = deflate(&png_ptr->zstream, Z_NO_FLUSH);
+      /* check for compression errors */
+      if (ret != Z_OK)
+      {
+         if (png_ptr->zstream.msg != NULL)
+            png_error(png_ptr, png_ptr->zstream.msg);
+         else
+            png_error(png_ptr, "zlib error");
+      }
+
+      /* see if it is time to write another IDAT */
+      if (!(png_ptr->zstream.avail_out))
+      {
+         /* write the IDAT and reset the zlib output buffer */
+         png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size);
+         png_ptr->zstream.next_out = png_ptr->zbuf;
+         png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
+      }
+   /* repeat until all data has been compressed */
+   } while (png_ptr->zstream.avail_in);
+
+   /* swap the current and previous rows */
+   if (png_ptr->prev_row != NULL)
+   {
+      png_bytep tptr;
+
+      tptr = png_ptr->prev_row;
+      png_ptr->prev_row = png_ptr->row_buf;
+      png_ptr->row_buf = tptr;
+   }
+
+   /* finish row - updates counters and flushes zlib if last row */
+   png_write_finish_row(png_ptr);
+
+#if defined(PNG_WRITE_FLUSH_SUPPORTED)
+   png_ptr->flush_rows++;
+
+   if (png_ptr->flush_dist > 0 &&
+       png_ptr->flush_rows >= png_ptr->flush_dist)
+   {
+      png_write_flush(png_ptr);
+   }
+#endif
+}
+#endif /* PNG_WRITE_SUPPORTED */
diff --git a/distrib/libpng-1.2.19/sources.make b/distrib/libpng-1.2.19/sources.make
new file mode 100644
index 0000000..d8088c7
--- /dev/null
+++ b/distrib/libpng-1.2.19/sources.make
@@ -0,0 +1,14 @@
+# this file is included by various Makefiles and defines the set of sources used by our version of LibPng
+#
+LIBPNG_SOURCES := png.c pngerror.c pngget.c pngmem.c pngpread.c pngread.c \
+                  pngrio.c pngrtran.c pngrutil.c pngset.c pngtrans.c pngvcrd.c pngwio.c \
+                  pngwrite.c pngwtran.c pngwutil.c
+
+ifeq ($(HOST_OS),darwin)
+    LIBPNG_CFLAGS += -DPNG_NO_MMX_CODE
+else
+    LIBPNG_SOURCES += pnggccrd.c
+endif
+
+LIBPNG_SOURCES := $(LIBPNG_SOURCES:%=$(LIBPNG_DIR)/%)
+
diff --git a/distrib/make-distrib.sh b/distrib/make-distrib.sh
new file mode 100755
index 0000000..f53d766
--- /dev/null
+++ b/distrib/make-distrib.sh
@@ -0,0 +1,110 @@
+#!/bin/bash
+#
+# this script is used to build a source distribution package for the Android emulator
+# the package includes:
+#  - the sources of our patched SDL library
+#  - the sources of our patched QEMU emulator
+#  - appropriate scripts to rebuild the emulator binary
+#
+
+# create temporary directory
+TMPROOT=/tmp/android-package
+DATE=$(date +%Y%m%d)
+PACKAGE=android-emulator-$DATE
+TMPDIR=$TMPROOT/$PACKAGE
+if ! ( rm -rf $TMPROOT && mkdir -p $TMPDIR ) then
+    echo "could not create temporary directory $TMPDIR"
+    exit 3
+fi
+
+locate_qemu_viewpath ()
+{
+    viewpath=$(p4 files $0 | sed -e "s/\(.*\)#.*/\\1/g")
+    # assumes that this program is in the 'distrib' directory of the QEMU sources
+    echo $(dirname $(dirname $viewpath))
+}
+
+locate_depot_files ()
+{
+    root=$(p4 where $1) || (
+        echo "you need to map $1 into your workspace to build an emulator source release package"
+        exit 3
+    )
+    root=$(echo $root | cut -d" " -f3 | sed -e "s%/\.\.\.%%")
+    echo $root
+}
+
+locate_source_files ()
+{
+    files=$(p4 files $1/... | grep -v "delete change" | sed -e "s/\(.*\)#.*/\\1/g")
+    files=$(echo $files | sed -e "s%$1/%%g")
+    echo $files
+}
+
+# locate SDL root directory in client workspace
+if [ -z "$SDLROOT" ] ; then
+    SDLROOT=$(locate_depot_files //toolchain/sdl/...)
+    echo "SDLROOT is $SDLROOT"
+fi
+
+if [ ! -x "$SDLROOT" ] ; then
+    if [ -z "$TOP" ] ; then
+        echo "please define the TOP variable"
+        exit 3
+    fi
+    echo "unable to find $SDLROOT as the SDL root directory"
+    echo "please define SDLROOT to point to the correct location"
+    exit 3
+fi
+
+# locate QEMU root directory
+if  [ -z "$QEMUROOT" ] ; then
+    QEMUVIEW=$(locate_qemu_viewpath)
+    echo "QEMUVIEW is $QEMUVIEW"
+    QEMUROOT=$(locate_depot_files $QEMUVIEW/...)
+    echo "QEMUROOT is $QEMUROOT"
+fi
+
+if [ ! -x "$QEMUROOT" ] ; then
+    if [ -z "$TOP" ] ; then
+        echo "please define the TOP variable"
+        exit 3
+    fi
+    echo "unable to find $QEMUROOT as the QEMU root directory"
+    echo "please define QEMUROOT to point to the correct location"
+    exit 3
+fi
+
+copy_source_files ()
+{
+  DSTDIR=$1
+  SRCDIR=$2
+  files=$(locate_source_files $3)
+  mkdir $DSTDIR && for f in $files; do
+    mkdir -p $(dirname $DSTDIR/$f);
+    cp $SRCDIR/$f $DSTDIR/$f
+  done
+}
+
+# copy and cleanup the SDL sources
+echo "copying SDL sources"
+SDLDIR=$TMPDIR/sdl
+copy_source_files $SDLDIR $SDLROOT //toolchain/sdl
+
+# copy and cleanup the QEMU sources
+echo "copying QEMU sources"
+QEMUDIR=$TMPDIR/qemu
+copy_source_files $QEMUDIR $QEMUROOT $QEMUVIEW
+
+echo "copying control scripts"
+cp $QEMUDIR/distrib/build-emulator.sh $TMPDIR/build-emulator.sh
+cp $QEMUDIR/distrib/README $TMPDIR/README
+
+echo "packaging release into a tarball"
+cd $TMPROOT
+tar cjf $PACKAGE.tar.bz2 $PACKAGE
+
+echo "cleaning up"
+rm -rf $TMPDIR
+
+echo "please grab $TMPROOT/$PACKAGE.tar.bz2"
diff --git a/distrib/update-audio.sh b/distrib/update-audio.sh
new file mode 100755
index 0000000..56bada2
--- /dev/null
+++ b/distrib/update-audio.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+#
+# this script is used to update the prebuilt libqemu-audio.a file in the Android source tree
+# we use a prebuilt package because we don't want to force the installation of the ALSA / EsounD / Whatever
+# development packages on every developer machine, or every build server.
+#
+
+# assumes this script is located in the 'distrib' sub-directory
+cd `dirname $0`
+cd ..
+
+locate_depot_files ()
+{
+    root=$(p4 where $1) || (
+        echo "you need to map $1 into your workspace to build an emulator source release package"
+        exit 3
+    )
+    root=$(echo $root | cut -d" " -f3 | sed -e "s%/\.\.\.%%")
+    echo $root
+}
+
+# find the prebuilt directory
+OS=`uname -s`
+EXE=""
+case "$OS" in
+    Darwin)
+        CPU=`uname -p`
+        if [ "$CPU" == "i386" ] ; then
+            OS=darwin-x86
+        else
+            OS=darwin-ppc
+        fi
+        ;;
+    Linux)
+        CPU=`uname -m`
+        case "$CPU" in
+        i?86|x86_64|amd64)
+            CPU=x86
+            ;;
+        esac
+        OS=linux-$CPU
+        ;;
+    *_NT-*)
+        OS=windows
+        EXE=.exe
+        ;;
+esac
+
+PREBUILT=$(locate_depot_files //branches/cupcake/android/prebuilt/$OS)
+
+# find the GNU Make program
+is_gnu_make ()
+{
+    version=$($1 -v | grep GNU)
+    if test -n "$version"; then
+        echo "$1"
+    else
+        echo ""
+    fi
+}
+
+if test -z "$GNUMAKE"; then
+    GNUMAKE=`which make` && GNUMAKE=$(is_gnu_make $GNUMAKE)
+fi
+
+if test -z "$GNUMAKE"; then
+    GNUMAKE=`which gmake` && GNUMAKE=$(is_gnu_make $GNUMAKE)
+fi
+
+if test -z "$GNUMAKE"; then
+    echo "could not find GNU Make on this machine. please define GNUMAKE to point to it"
+    exit 3
+fi
+
+TEST=$(is_gnu_make $GNUMAKE)
+if test -z "$TEST"; then
+    echo "it seems that '$GNUMAKE' is not a working GNU Make binary. please check the definition of GNUMAKE"
+    exit 3
+fi
+
+# ensure we have a recent audio library built
+#
+#echo "GNUMAKE is $GNUMAKE"
+source=objs/libqemu-audio.a
+./android-configure.sh
+$GNUMAKE $source BUILD_QEMU_AUDIO_LIB=true || (echo "could not build the audio library. Aborting" && exit 1)
+
+# now do a p4 edit, a copy and ask for submission
+#
+TARGET=$PREBUILT/emulator/libqemu-audio.a
+
+p4 edit $TARGET || (echo "could not p4 edit $TARGET" && exit 3)
+cp -f $source $TARGET
+echo "please do: p4 submit $TARGET"
+
diff --git a/distrib/zlib-1.2.3/Makefile b/distrib/zlib-1.2.3/Makefile
new file mode 100644
index 0000000..9cf80c9
--- /dev/null
+++ b/distrib/zlib-1.2.3/Makefile
@@ -0,0 +1,15 @@
+# Makefile used to compile zlib statically
+#
+ZLIB_LIB    := $(SRC_PATH)/libz.a
+ZLIB_CFLAGS := -I$(ZLIB_DIR)
+
+include $(ZLIB_DIR)/sources.make
+ZLIB_OBJS := $(ZLIB_SOURCES:%.c=%.o)
+
+$(ZLIB_LIB): $(ZLIB_OBJS)
+	ar ru $(ZLIB_LIB) $(ZLIB_OBJS)
+
+$(ZLIB_OBJS): CFLAGS += $(ZLIB_CFLAGS)
+
+clean-zlib:
+	rm -f $(ZLIB_OBJS) $(zlib_lib)
diff --git a/distrib/zlib-1.2.3/adler32.c b/distrib/zlib-1.2.3/adler32.c
new file mode 100644
index 0000000..007ba26
--- /dev/null
+++ b/distrib/zlib-1.2.3/adler32.c
@@ -0,0 +1,149 @@
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#define BASE 65521UL    /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf,i)  {adler += (buf)[i]; sum2 += adler;}
+#define DO2(buf,i)  DO1(buf,i); DO1(buf,i+1);
+#define DO4(buf,i)  DO2(buf,i); DO2(buf,i+2);
+#define DO8(buf,i)  DO4(buf,i); DO4(buf,i+4);
+#define DO16(buf)   DO8(buf,0); DO8(buf,8);
+
+/* use NO_DIVIDE if your processor does not do division in hardware */
+#ifdef NO_DIVIDE
+#  define MOD(a) \
+    do { \
+        if (a >= (BASE << 16)) a -= (BASE << 16); \
+        if (a >= (BASE << 15)) a -= (BASE << 15); \
+        if (a >= (BASE << 14)) a -= (BASE << 14); \
+        if (a >= (BASE << 13)) a -= (BASE << 13); \
+        if (a >= (BASE << 12)) a -= (BASE << 12); \
+        if (a >= (BASE << 11)) a -= (BASE << 11); \
+        if (a >= (BASE << 10)) a -= (BASE << 10); \
+        if (a >= (BASE << 9)) a -= (BASE << 9); \
+        if (a >= (BASE << 8)) a -= (BASE << 8); \
+        if (a >= (BASE << 7)) a -= (BASE << 7); \
+        if (a >= (BASE << 6)) a -= (BASE << 6); \
+        if (a >= (BASE << 5)) a -= (BASE << 5); \
+        if (a >= (BASE << 4)) a -= (BASE << 4); \
+        if (a >= (BASE << 3)) a -= (BASE << 3); \
+        if (a >= (BASE << 2)) a -= (BASE << 2); \
+        if (a >= (BASE << 1)) a -= (BASE << 1); \
+        if (a >= BASE) a -= BASE; \
+    } while (0)
+#  define MOD4(a) \
+    do { \
+        if (a >= (BASE << 4)) a -= (BASE << 4); \
+        if (a >= (BASE << 3)) a -= (BASE << 3); \
+        if (a >= (BASE << 2)) a -= (BASE << 2); \
+        if (a >= (BASE << 1)) a -= (BASE << 1); \
+        if (a >= BASE) a -= BASE; \
+    } while (0)
+#else
+#  define MOD(a) a %= BASE
+#  define MOD4(a) a %= BASE
+#endif
+
+/* ========================================================================= */
+uLong ZEXPORT adler32(adler, buf, len)
+    uLong adler;
+    const Bytef *buf;
+    uInt len;
+{
+    unsigned long sum2;
+    unsigned n;
+
+    /* split Adler-32 into component sums */
+    sum2 = (adler >> 16) & 0xffff;
+    adler &= 0xffff;
+
+    /* in case user likes doing a byte at a time, keep it fast */
+    if (len == 1) {
+        adler += buf[0];
+        if (adler >= BASE)
+            adler -= BASE;
+        sum2 += adler;
+        if (sum2 >= BASE)
+            sum2 -= BASE;
+        return adler | (sum2 << 16);
+    }
+
+    /* initial Adler-32 value (deferred check for len == 1 speed) */
+    if (buf == Z_NULL)
+        return 1L;
+
+    /* in case short lengths are provided, keep it somewhat fast */
+    if (len < 16) {
+        while (len--) {
+            adler += *buf++;
+            sum2 += adler;
+        }
+        if (adler >= BASE)
+            adler -= BASE;
+        MOD4(sum2);             /* only added so many BASE's */
+        return adler | (sum2 << 16);
+    }
+
+    /* do length NMAX blocks -- requires just one modulo operation */
+    while (len >= NMAX) {
+        len -= NMAX;
+        n = NMAX / 16;          /* NMAX is divisible by 16 */
+        do {
+            DO16(buf);          /* 16 sums unrolled */
+            buf += 16;
+        } while (--n);
+        MOD(adler);
+        MOD(sum2);
+    }
+
+    /* do remaining bytes (less than NMAX, still just one modulo) */
+    if (len) {                  /* avoid modulos if none remaining */
+        while (len >= 16) {
+            len -= 16;
+            DO16(buf);
+            buf += 16;
+        }
+        while (len--) {
+            adler += *buf++;
+            sum2 += adler;
+        }
+        MOD(adler);
+        MOD(sum2);
+    }
+
+    /* return recombined sums */
+    return adler | (sum2 << 16);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT adler32_combine(adler1, adler2, len2)
+    uLong adler1;
+    uLong adler2;
+    z_off_t len2;
+{
+    unsigned long sum1;
+    unsigned long sum2;
+    unsigned rem;
+
+    /* the derivation of this formula is left as an exercise for the reader */
+    rem = (unsigned)(len2 % BASE);
+    sum1 = adler1 & 0xffff;
+    sum2 = rem * sum1;
+    MOD(sum2);
+    sum1 += (adler2 & 0xffff) + BASE - 1;
+    sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem;
+    if (sum1 > BASE) sum1 -= BASE;
+    if (sum1 > BASE) sum1 -= BASE;
+    if (sum2 > (BASE << 1)) sum2 -= (BASE << 1);
+    if (sum2 > BASE) sum2 -= BASE;
+    return sum1 | (sum2 << 16);
+}
diff --git a/distrib/zlib-1.2.3/compress.c b/distrib/zlib-1.2.3/compress.c
new file mode 100644
index 0000000..df04f01
--- /dev/null
+++ b/distrib/zlib-1.2.3/compress.c
@@ -0,0 +1,79 @@
+/* compress.c -- compress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least 0.1% larger than sourceLen plus
+   12 bytes. Upon exit, destLen is the actual size of the compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+int ZEXPORT compress2 (dest, destLen, source, sourceLen, level)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+    int level;
+{
+    z_stream stream;
+    int err;
+
+    stream.next_in = (Bytef*)source;
+    stream.avail_in = (uInt)sourceLen;
+#ifdef MAXSEG_64K
+    /* Check for source > 64K on 16-bit machine: */
+    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+#endif
+    stream.next_out = dest;
+    stream.avail_out = (uInt)*destLen;
+    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+    stream.opaque = (voidpf)0;
+
+    err = deflateInit(&stream, level);
+    if (err != Z_OK) return err;
+
+    err = deflate(&stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        deflateEnd(&stream);
+        return err == Z_OK ? Z_BUF_ERROR : err;
+    }
+    *destLen = stream.total_out;
+
+    err = deflateEnd(&stream);
+    return err;
+}
+
+/* ===========================================================================
+ */
+int ZEXPORT compress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION);
+}
+
+/* ===========================================================================
+     If the default memLevel or windowBits for deflateInit() is changed, then
+   this function needs to be updated.
+ */
+uLong ZEXPORT compressBound (sourceLen)
+    uLong sourceLen;
+{
+    return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11;
+}
diff --git a/distrib/zlib-1.2.3/configure b/distrib/zlib-1.2.3/configure
new file mode 100755
index 0000000..d7ffdc3
--- /dev/null
+++ b/distrib/zlib-1.2.3/configure
@@ -0,0 +1,459 @@
+#!/bin/sh
+# configure script for zlib. This script is needed only if
+# you wish to build a shared library and your system supports them,
+# of if you need special compiler, flags or install directory.
+# Otherwise, you can just use directly "make test; make install"
+#
+# To create a shared library, use "configure --shared"; by default a static
+# library is created. If the primitive shared library support provided here
+# does not work, use ftp://prep.ai.mit.edu/pub/gnu/libtool-*.tar.gz
+#
+# To impose specific compiler or flags or install directory, use for example:
+#    prefix=$HOME CC=cc CFLAGS="-O4" ./configure
+# or for csh/tcsh users:
+#    (setenv prefix $HOME; setenv CC cc; setenv CFLAGS "-O4"; ./configure)
+# LDSHARED is the command to be used to create a shared library
+
+# Incorrect settings of CC or CFLAGS may prevent creating a shared library.
+# If you have problems, try without defining CC and CFLAGS before reporting
+# an error.
+
+LIBS=libz.a
+LDFLAGS="-L. ${LIBS}"
+VER=`sed -n -e '/VERSION "/s/.*"\(.*\)".*/\1/p' < zlib.h`
+VER2=`sed -n -e '/VERSION "/s/.*"\([0-9]*\\.[0-9]*\)\\..*/\1/p' < zlib.h`
+VER1=`sed -n -e '/VERSION "/s/.*"\([0-9]*\)\\..*/\1/p' < zlib.h`
+AR=${AR-"ar rc"}
+RANLIB=${RANLIB-"ranlib"}
+prefix=${prefix-/usr/local}
+exec_prefix=${exec_prefix-'${prefix}'}
+libdir=${libdir-'${exec_prefix}/lib'}
+includedir=${includedir-'${prefix}/include'}
+mandir=${mandir-'${prefix}/share/man'}
+shared_ext='.so'
+shared=0
+gcc=0
+old_cc="$CC"
+old_cflags="$CFLAGS"
+
+while test $# -ge 1
+do
+case "$1" in
+    -h* | --h*)
+      echo 'usage:'
+      echo '  configure [--shared] [--prefix=PREFIX]  [--exec_prefix=EXPREFIX]'
+      echo '     [--libdir=LIBDIR] [--includedir=INCLUDEDIR]'
+        exit 0;;
+    -p*=* | --p*=*) prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -e*=* | --e*=*) exec_prefix=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -l*=* | --libdir=*) libdir=`echo $1 | sed 's/[-a-z_]*=//'`; shift;;
+    -i*=* | --includedir=*) includedir=`echo $1 | sed 's/[-a-z_]*=//'`;shift;;
+    -p* | --p*) prefix="$2"; shift; shift;;
+    -e* | --e*) exec_prefix="$2"; shift; shift;;
+    -l* | --l*) libdir="$2"; shift; shift;;
+    -i* | --i*) includedir="$2"; shift; shift;;
+    -s* | --s*) shared=1; shift;;
+    *) echo "unknown option: $1"; echo "$0 --help for help"; exit 1;;
+    esac
+done
+
+test=ztest$$
+cat > $test.c <<EOF
+extern int getchar();
+int hello() {return getchar();}
+EOF
+
+test -z "$CC" && echo Checking for gcc...
+cc=${CC-gcc}
+cflags=${CFLAGS-"-O3"}
+# to force the asm version use: CFLAGS="-O3 -DASMV" ./configure
+case "$cc" in
+  *gcc*) gcc=1;;
+esac
+
+if test "$gcc" -eq 1 && ($cc -c $cflags $test.c) 2>/dev/null; then
+  CC="$cc"
+  SFLAGS=${CFLAGS-"-fPIC -O3"}
+  CFLAGS="$cflags"
+  case `(uname -s || echo unknown) 2>/dev/null` in
+  Linux | linux | GNU | GNU/*) LDSHARED=${LDSHARED-"$cc -shared -Wl,-soname,libz.so.1"};;
+  CYGWIN* | Cygwin* | cygwin* | OS/2* )
+             EXE='.exe';;
+  QNX*)  # This is for QNX6. I suppose that the QNX rule below is for QNX2,QNX4
+         # (alain.bonnefoy@icbt.com)
+                 LDSHARED=${LDSHARED-"$cc -shared -Wl,-hlibz.so.1"};;
+  HP-UX*)
+         LDSHARED=${LDSHARED-"$cc -shared $SFLAGS"}
+         case `(uname -m || echo unknown) 2>/dev/null` in
+         ia64)
+                 shared_ext='.so'
+                 SHAREDLIB='libz.so';;
+         *)
+                 shared_ext='.sl'
+                 SHAREDLIB='libz.sl';;
+         esac;;
+  Darwin*)   shared_ext='.dylib'
+             SHAREDLIB=libz$shared_ext
+             SHAREDLIBV=libz.$VER$shared_ext
+             SHAREDLIBM=libz.$VER1$shared_ext
+             LDSHARED=${LDSHARED-"$cc -dynamiclib -install_name $libdir/$SHAREDLIBM -compatibility_version $VER1 -current_version $VER"};;
+  *)             LDSHARED=${LDSHARED-"$cc -shared"};;
+  esac
+else
+  # find system name and corresponding cc options
+  CC=${CC-cc}
+  case `(uname -sr || echo unknown) 2>/dev/null` in
+  HP-UX*)    SFLAGS=${CFLAGS-"-O +z"}
+             CFLAGS=${CFLAGS-"-O"}
+#            LDSHARED=${LDSHARED-"ld -b +vnocompatwarnings"}
+             LDSHARED=${LDSHARED-"ld -b"}
+         case `(uname -m || echo unknown) 2>/dev/null` in
+         ia64)
+             shared_ext='.so'
+             SHAREDLIB='libz.so';;
+         *)
+             shared_ext='.sl'
+             SHAREDLIB='libz.sl';;
+         esac;;
+  IRIX*)     SFLAGS=${CFLAGS-"-ansi -O2 -rpath ."}
+             CFLAGS=${CFLAGS-"-ansi -O2"}
+             LDSHARED=${LDSHARED-"cc -shared"};;
+  OSF1\ V4*) SFLAGS=${CFLAGS-"-O -std1"}
+             CFLAGS=${CFLAGS-"-O -std1"}
+             LDSHARED=${LDSHARED-"cc -shared  -Wl,-soname,libz.so -Wl,-msym -Wl,-rpath,$(libdir) -Wl,-set_version,${VER}:1.0"};;
+  OSF1*)     SFLAGS=${CFLAGS-"-O -std1"}
+             CFLAGS=${CFLAGS-"-O -std1"}
+             LDSHARED=${LDSHARED-"cc -shared"};;
+  QNX*)      SFLAGS=${CFLAGS-"-4 -O"}
+             CFLAGS=${CFLAGS-"-4 -O"}
+             LDSHARED=${LDSHARED-"cc"}
+             RANLIB=${RANLIB-"true"}
+             AR="cc -A";;
+  SCO_SV\ 3.2*) SFLAGS=${CFLAGS-"-O3 -dy -KPIC "}
+             CFLAGS=${CFLAGS-"-O3"}
+             LDSHARED=${LDSHARED-"cc -dy -KPIC -G"};;
+  SunOS\ 5*) SFLAGS=${CFLAGS-"-fast -xcg89 -KPIC -R."}
+             CFLAGS=${CFLAGS-"-fast -xcg89"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  SunOS\ 4*) SFLAGS=${CFLAGS-"-O2 -PIC"}
+             CFLAGS=${CFLAGS-"-O2"}
+             LDSHARED=${LDSHARED-"ld"};;
+  SunStudio\ 9*) SFLAGS=${CFLAGS-"-DUSE_MMAP -fast -xcode=pic32 -xtarget=ultra3 -xarch=v9b"}
+             CFLAGS=${CFLAGS-"-DUSE_MMAP -fast -xtarget=ultra3 -xarch=v9b"}
+             LDSHARED=${LDSHARED-"cc -xarch=v9b"};;
+  UNIX_System_V\ 4.2.0)
+             SFLAGS=${CFLAGS-"-KPIC -O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  UNIX_SV\ 4.2MP)
+             SFLAGS=${CFLAGS-"-Kconform_pic -O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  OpenUNIX\ 5)
+             SFLAGS=${CFLAGS-"-KPIC -O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -G"};;
+  AIX*)  # Courtesy of dbakker@arrayasolutions.com
+             SFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
+             CFLAGS=${CFLAGS-"-O -qmaxmem=8192"}
+             LDSHARED=${LDSHARED-"xlc -G"};;
+  # send working options for other systems to support@gzip.org
+  *)         SFLAGS=${CFLAGS-"-O"}
+             CFLAGS=${CFLAGS-"-O"}
+             LDSHARED=${LDSHARED-"cc -shared"};;
+  esac
+fi
+
+SHAREDLIB=${SHAREDLIB-"libz$shared_ext"}
+SHAREDLIBV=${SHAREDLIBV-"libz$shared_ext.$VER"}
+SHAREDLIBM=${SHAREDLIBM-"libz$shared_ext.$VER1"}
+
+if test $shared -eq 1; then
+  echo Checking for shared library support...
+  # we must test in two steps (cc then ld), required at least on SunOS 4.x
+  if test "`($CC -c $SFLAGS $test.c) 2>&1`" = "" &&
+     test "`($LDSHARED -o $test$shared_ext $test.o) 2>&1`" = ""; then
+    CFLAGS="$SFLAGS"
+    LIBS="$SHAREDLIBV"
+    echo Building shared library $SHAREDLIBV with $CC.
+  elif test -z "$old_cc" -a -z "$old_cflags"; then
+    echo No shared library support.
+    shared=0;
+  else
+    echo 'No shared library support; try without defining CC and CFLAGS'
+    shared=0;
+  fi
+fi
+if test $shared -eq 0; then
+  LDSHARED="$CC"
+  echo Building static library $LIBS version $VER with $CC.
+else
+  LDFLAGS="-L. ${SHAREDLIBV}"
+fi
+
+cat > $test.c <<EOF
+#include <unistd.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  sed < zconf.in.h "/HAVE_UNISTD_H/s%0%1%" > zconf.h
+  echo "Checking for unistd.h... Yes."
+else
+  cp -p zconf.in.h zconf.h
+  echo "Checking for unistd.h... No."
+fi
+
+cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+#include "zconf.h"
+
+int main()
+{
+#ifndef STDC
+  choke me
+#endif
+
+  return 0;
+}
+EOF
+
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  echo "Checking whether to use vs[n]printf() or s[n]printf()... using vs[n]printf()"
+
+  cat > $test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+  return 0;
+}
+
+int main()
+{
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+  if test "`($CC $CFLAGS -o $test $test.c) 2>&1`" = ""; then
+    echo "Checking for vsnprintf() in stdio.h... Yes."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+  int n;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  n = vsnprintf(buf, sizeof(buf), fmt, ap);
+  va_end(ap);
+  return n;
+}
+
+int main()
+{
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of vsnprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_vsnprintf_void"
+      echo "Checking for return value of vsnprintf()... No."
+      echo "  WARNING: apparently vsnprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  else
+    CFLAGS="$CFLAGS -DNO_vsnprintf"
+    echo "Checking for vsnprintf() in stdio.h... No."
+    echo "  WARNING: vsnprintf() not found, falling back to vsprintf(). zlib"
+    echo "  can build but will be open to possible buffer-overflow security"
+    echo "  vulnerabilities."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+#include <stdarg.h>
+
+int mytest(char *fmt, ...)
+{
+  int n;
+  char buf[20];
+  va_list ap;
+
+  va_start(ap, fmt);
+  n = vsprintf(buf, fmt, ap);
+  va_end(ap);
+  return n;
+}
+
+int main()
+{
+  return (mytest("Hello%d\n", 1));
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of vsprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_vsprintf_void"
+      echo "Checking for return value of vsprintf()... No."
+      echo "  WARNING: apparently vsprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  fi
+else
+  echo "Checking whether to use vs[n]printf() or s[n]printf()... using s[n]printf()"
+
+  cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+  char buf[20];
+
+  snprintf(buf, sizeof(buf), "%s", "foo");
+  return 0;
+}
+
+int main()
+{
+  return (mytest());
+}
+EOF
+
+  if test "`($CC $CFLAGS -o $test $test.c) 2>&1`" = ""; then
+    echo "Checking for snprintf() in stdio.h... Yes."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+  char buf[20];
+
+  return snprintf(buf, sizeof(buf), "%s", "foo");
+}
+
+int main()
+{
+  return (mytest());
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of snprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_snprintf_void"
+      echo "Checking for return value of snprintf()... No."
+      echo "  WARNING: apparently snprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  else
+    CFLAGS="$CFLAGS -DNO_snprintf"
+    echo "Checking for snprintf() in stdio.h... No."
+    echo "  WARNING: snprintf() not found, falling back to sprintf(). zlib"
+    echo "  can build but will be open to possible buffer-overflow security"
+    echo "  vulnerabilities."
+
+    cat >$test.c <<EOF
+#include <stdio.h>
+
+int mytest()
+{
+  char buf[20];
+
+  return sprintf(buf, "%s", "foo");
+}
+
+int main()
+{
+  return (mytest());
+}
+EOF
+
+    if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+      echo "Checking for return value of sprintf()... Yes."
+    else
+      CFLAGS="$CFLAGS -DHAS_sprintf_void"
+      echo "Checking for return value of sprintf()... No."
+      echo "  WARNING: apparently sprintf() does not return a value. zlib"
+      echo "  can build but will be open to possible string-format security"
+      echo "  vulnerabilities."
+    fi
+  fi
+fi
+
+cat >$test.c <<EOF
+#include <errno.h>
+int main() { return 0; }
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  echo "Checking for errno.h... Yes."
+else
+  echo "Checking for errno.h... No."
+  CFLAGS="$CFLAGS -DNO_ERRNO_H"
+fi
+
+cat > $test.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+caddr_t hello() {
+  return mmap((caddr_t)0, (off_t)0, PROT_READ, MAP_SHARED, 0, (off_t)0);
+}
+EOF
+if test "`($CC -c $CFLAGS $test.c) 2>&1`" = ""; then
+  CFLAGS="$CFLAGS -DUSE_MMAP"
+  echo Checking for mmap support... Yes.
+else
+  echo Checking for mmap support... No.
+fi
+
+CPP=${CPP-"$CC -E"}
+case $CFLAGS in
+  *ASMV*)
+    if test "`nm $test.o | grep _hello`" = ""; then
+      CPP="$CPP -DNO_UNDERLINE"
+      echo Checking for underline in external names... No.
+    else
+      echo Checking for underline in external names... Yes.
+    fi;;
+esac
+
+rm -f $test.[co] $test $test$shared_ext
+
+# udpate Makefile
+sed < Makefile.in "
+/^CC *=/s#=.*#=$CC#
+/^CFLAGS *=/s#=.*#=$CFLAGS#
+/^CPP *=/s#=.*#=$CPP#
+/^LDSHARED *=/s#=.*#=$LDSHARED#
+/^LIBS *=/s#=.*#=$LIBS#
+/^SHAREDLIB *=/s#=.*#=$SHAREDLIB#
+/^SHAREDLIBV *=/s#=.*#=$SHAREDLIBV#
+/^SHAREDLIBM *=/s#=.*#=$SHAREDLIBM#
+/^AR *=/s#=.*#=$AR#
+/^RANLIB *=/s#=.*#=$RANLIB#
+/^EXE *=/s#=.*#=$EXE#
+/^prefix *=/s#=.*#=$prefix#
+/^exec_prefix *=/s#=.*#=$exec_prefix#
+/^libdir *=/s#=.*#=$libdir#
+/^includedir *=/s#=.*#=$includedir#
+/^mandir *=/s#=.*#=$mandir#
+/^LDFLAGS *=/s#=.*#=$LDFLAGS#
+" > Makefile
diff --git a/distrib/zlib-1.2.3/crc32.c b/distrib/zlib-1.2.3/crc32.c
new file mode 100644
index 0000000..f658a9e
--- /dev/null
+++ b/distrib/zlib-1.2.3/crc32.c
@@ -0,0 +1,423 @@
+/* crc32.c -- compute the CRC-32 of a data stream
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster
+ * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing
+ * tables for updating the shift register in one step with three exclusive-ors
+ * instead of four steps with four exclusive-ors.  This results in about a
+ * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3.
+ */
+
+/* @(#) $Id$ */
+
+/*
+  Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore
+  protection on the static variables used to control the first-use generation
+  of the crc tables.  Therefore, if you #define DYNAMIC_CRC_TABLE, you should
+  first call get_crc_table() to initialize the tables before allowing more than
+  one thread to use crc32().
+ */
+
+#ifdef MAKECRCH
+#  include <stdio.h>
+#  ifndef DYNAMIC_CRC_TABLE
+#    define DYNAMIC_CRC_TABLE
+#  endif /* !DYNAMIC_CRC_TABLE */
+#endif /* MAKECRCH */
+
+#include "zutil.h"      /* for STDC and FAR definitions */
+
+#define local static
+
+/* Find a four-byte integer type for crc32_little() and crc32_big(). */
+#ifndef NOBYFOUR
+#  ifdef STDC           /* need ANSI C limits.h to determine sizes */
+#    include <limits.h>
+#    define BYFOUR
+#    if (UINT_MAX == 0xffffffffUL)
+       typedef unsigned int u4;
+#    else
+#      if (ULONG_MAX == 0xffffffffUL)
+         typedef unsigned long u4;
+#      else
+#        if (USHRT_MAX == 0xffffffffUL)
+           typedef unsigned short u4;
+#        else
+#          undef BYFOUR     /* can't find a four-byte integer type! */
+#        endif
+#      endif
+#    endif
+#  endif /* STDC */
+#endif /* !NOBYFOUR */
+
+/* Definitions for doing the crc four data bytes at a time. */
+#ifdef BYFOUR
+#  define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \
+                (((w)&0xff00)<<8)+(((w)&0xff)<<24))
+   local unsigned long crc32_little OF((unsigned long,
+                        const unsigned char FAR *, unsigned));
+   local unsigned long crc32_big OF((unsigned long,
+                        const unsigned char FAR *, unsigned));
+#  define TBLS 8
+#else
+#  define TBLS 1
+#endif /* BYFOUR */
+
+/* Local functions for crc concatenation */
+local unsigned long gf2_matrix_times OF((unsigned long *mat,
+                                         unsigned long vec));
+local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat));
+
+#ifdef DYNAMIC_CRC_TABLE
+
+local volatile int crc_table_empty = 1;
+local unsigned long FAR crc_table[TBLS][256];
+local void make_crc_table OF((void));
+#ifdef MAKECRCH
+   local void write_table OF((FILE *, const unsigned long FAR *));
+#endif /* MAKECRCH */
+/*
+  Generate tables for a byte-wise 32-bit CRC calculation on the polynomial:
+  x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
+
+  Polynomials over GF(2) are represented in binary, one bit per coefficient,
+  with the lowest powers in the most significant bit.  Then adding polynomials
+  is just exclusive-or, and multiplying a polynomial by x is a right shift by
+  one.  If we call the above polynomial p, and represent a byte as the
+  polynomial q, also with the lowest power in the most significant bit (so the
+  byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
+  where a mod b means the remainder after dividing a by b.
+
+  This calculation is done using the shift-register method of multiplying and
+  taking the remainder.  The register is initialized to zero, and for each
+  incoming bit, x^32 is added mod p to the register if the bit is a one (where
+  x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
+  x (which is shifting right by one and adding x^32 mod p if the bit shifted
+  out is a one).  We start with the highest power (least significant bit) of
+  q and repeat for all eight bits of q.
+
+  The first table is simply the CRC of all possible eight bit values.  This is
+  all the information needed to generate CRCs on data a byte at a time for all
+  combinations of CRC register values and incoming bytes.  The remaining tables
+  allow for word-at-a-time CRC calculation for both big-endian and little-
+  endian machines, where a word is four bytes.
+*/
+local void make_crc_table()
+{
+    unsigned long c;
+    int n, k;
+    unsigned long poly;                 /* polynomial exclusive-or pattern */
+    /* terms of polynomial defining this crc (except x^32): */
+    static volatile int first = 1;      /* flag to limit concurrent making */
+    static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26};
+
+    /* See if another task is already doing this (not thread-safe, but better
+       than nothing -- significantly reduces duration of vulnerability in
+       case the advice about DYNAMIC_CRC_TABLE is ignored) */
+    if (first) {
+        first = 0;
+
+        /* make exclusive-or pattern from polynomial (0xedb88320UL) */
+        poly = 0UL;
+        for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++)
+            poly |= 1UL << (31 - p[n]);
+
+        /* generate a crc for every 8-bit value */
+        for (n = 0; n < 256; n++) {
+            c = (unsigned long)n;
+            for (k = 0; k < 8; k++)
+                c = c & 1 ? poly ^ (c >> 1) : c >> 1;
+            crc_table[0][n] = c;
+        }
+
+#ifdef BYFOUR
+        /* generate crc for each value followed by one, two, and three zeros,
+           and then the byte reversal of those as well as the first table */
+        for (n = 0; n < 256; n++) {
+            c = crc_table[0][n];
+            crc_table[4][n] = REV(c);
+            for (k = 1; k < 4; k++) {
+                c = crc_table[0][c & 0xff] ^ (c >> 8);
+                crc_table[k][n] = c;
+                crc_table[k + 4][n] = REV(c);
+            }
+        }
+#endif /* BYFOUR */
+
+        crc_table_empty = 0;
+    }
+    else {      /* not first */
+        /* wait for the other guy to finish (not efficient, but rare) */
+        while (crc_table_empty)
+            ;
+    }
+
+#ifdef MAKECRCH
+    /* write out CRC tables to crc32.h */
+    {
+        FILE *out;
+
+        out = fopen("crc32.h", "w");
+        if (out == NULL) return;
+        fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n");
+        fprintf(out, " * Generated automatically by crc32.c\n */\n\n");
+        fprintf(out, "local const unsigned long FAR ");
+        fprintf(out, "crc_table[TBLS][256] =\n{\n  {\n");
+        write_table(out, crc_table[0]);
+#  ifdef BYFOUR
+        fprintf(out, "#ifdef BYFOUR\n");
+        for (k = 1; k < 8; k++) {
+            fprintf(out, "  },\n  {\n");
+            write_table(out, crc_table[k]);
+        }
+        fprintf(out, "#endif\n");
+#  endif /* BYFOUR */
+        fprintf(out, "  }\n};\n");
+        fclose(out);
+    }
+#endif /* MAKECRCH */
+}
+
+#ifdef MAKECRCH
+local void write_table(out, table)
+    FILE *out;
+    const unsigned long FAR *table;
+{
+    int n;
+
+    for (n = 0; n < 256; n++)
+        fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : "    ", table[n],
+                n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", "));
+}
+#endif /* MAKECRCH */
+
+#else /* !DYNAMIC_CRC_TABLE */
+/* ========================================================================
+ * Tables of CRC-32s of all single-byte values, made by make_crc_table().
+ */
+#include "crc32.h"
+#endif /* DYNAMIC_CRC_TABLE */
+
+/* =========================================================================
+ * This function can be used by asm versions of crc32()
+ */
+const unsigned long FAR * ZEXPORT get_crc_table()
+{
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+        make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+    return (const unsigned long FAR *)crc_table;
+}
+
+/* ========================================================================= */
+#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8)
+#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1
+
+/* ========================================================================= */
+unsigned long ZEXPORT crc32(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    if (buf == Z_NULL) return 0UL;
+
+#ifdef DYNAMIC_CRC_TABLE
+    if (crc_table_empty)
+        make_crc_table();
+#endif /* DYNAMIC_CRC_TABLE */
+
+#ifdef BYFOUR
+    if (sizeof(void *) == sizeof(ptrdiff_t)) {
+        u4 endian;
+
+        endian = 1;
+        if (*((unsigned char *)(&endian)))
+            return crc32_little(crc, buf, len);
+        else
+            return crc32_big(crc, buf, len);
+    }
+#endif /* BYFOUR */
+    crc = crc ^ 0xffffffffUL;
+    while (len >= 8) {
+        DO8;
+        len -= 8;
+    }
+    if (len) do {
+        DO1;
+    } while (--len);
+    return crc ^ 0xffffffffUL;
+}
+
+#ifdef BYFOUR
+
+/* ========================================================================= */
+#define DOLIT4 c ^= *buf4++; \
+        c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \
+            crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24]
+#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4
+
+/* ========================================================================= */
+local unsigned long crc32_little(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    register u4 c;
+    register const u4 FAR *buf4;
+
+    c = (u4)crc;
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+        len--;
+    }
+
+    buf4 = (const u4 FAR *)(const void FAR *)buf;
+    while (len >= 32) {
+        DOLIT32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOLIT4;
+        len -= 4;
+    }
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)c;
+}
+
+/* ========================================================================= */
+#define DOBIG4 c ^= *++buf4; \
+        c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \
+            crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24]
+#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4
+
+/* ========================================================================= */
+local unsigned long crc32_big(crc, buf, len)
+    unsigned long crc;
+    const unsigned char FAR *buf;
+    unsigned len;
+{
+    register u4 c;
+    register const u4 FAR *buf4;
+
+    c = REV((u4)crc);
+    c = ~c;
+    while (len && ((ptrdiff_t)buf & 3)) {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+        len--;
+    }
+
+    buf4 = (const u4 FAR *)(const void FAR *)buf;
+    buf4--;
+    while (len >= 32) {
+        DOBIG32;
+        len -= 32;
+    }
+    while (len >= 4) {
+        DOBIG4;
+        len -= 4;
+    }
+    buf4++;
+    buf = (const unsigned char FAR *)buf4;
+
+    if (len) do {
+        c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8);
+    } while (--len);
+    c = ~c;
+    return (unsigned long)(REV(c));
+}
+
+#endif /* BYFOUR */
+
+#define GF2_DIM 32      /* dimension of GF(2) vectors (length of CRC) */
+
+/* ========================================================================= */
+local unsigned long gf2_matrix_times(mat, vec)
+    unsigned long *mat;
+    unsigned long vec;
+{
+    unsigned long sum;
+
+    sum = 0;
+    while (vec) {
+        if (vec & 1)
+            sum ^= *mat;
+        vec >>= 1;
+        mat++;
+    }
+    return sum;
+}
+
+/* ========================================================================= */
+local void gf2_matrix_square(square, mat)
+    unsigned long *square;
+    unsigned long *mat;
+{
+    int n;
+
+    for (n = 0; n < GF2_DIM; n++)
+        square[n] = gf2_matrix_times(mat, mat[n]);
+}
+
+/* ========================================================================= */
+uLong ZEXPORT crc32_combine(crc1, crc2, len2)
+    uLong crc1;
+    uLong crc2;
+    z_off_t len2;
+{
+    int n;
+    unsigned long row;
+    unsigned long even[GF2_DIM];    /* even-power-of-two zeros operator */
+    unsigned long odd[GF2_DIM];     /* odd-power-of-two zeros operator */
+
+    /* degenerate case */
+    if (len2 == 0)
+        return crc1;
+
+    /* put operator for one zero bit in odd */
+    odd[0] = 0xedb88320L;           /* CRC-32 polynomial */
+    row = 1;
+    for (n = 1; n < GF2_DIM; n++) {
+        odd[n] = row;
+        row <<= 1;
+    }
+
+    /* put operator for two zero bits in even */
+    gf2_matrix_square(even, odd);
+
+    /* put operator for four zero bits in odd */
+    gf2_matrix_square(odd, even);
+
+    /* apply len2 zeros to crc1 (first square will put the operator for one
+       zero byte, eight zero bits, in even) */
+    do {
+        /* apply zeros operator for this bit of len2 */
+        gf2_matrix_square(even, odd);
+        if (len2 & 1)
+            crc1 = gf2_matrix_times(even, crc1);
+        len2 >>= 1;
+
+        /* if no more bits set, then done */
+        if (len2 == 0)
+            break;
+
+        /* another iteration of the loop with odd and even swapped */
+        gf2_matrix_square(odd, even);
+        if (len2 & 1)
+            crc1 = gf2_matrix_times(odd, crc1);
+        len2 >>= 1;
+
+        /* if no more bits set, then done */
+    } while (len2 != 0);
+
+    /* return combined crc */
+    crc1 ^= crc2;
+    return crc1;
+}
diff --git a/distrib/zlib-1.2.3/crc32.h b/distrib/zlib-1.2.3/crc32.h
new file mode 100644
index 0000000..8053b61
--- /dev/null
+++ b/distrib/zlib-1.2.3/crc32.h
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const unsigned long FAR crc_table[TBLS][256] =
+{
+  {
+    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+    0x2d02ef8dUL
+#ifdef BYFOUR
+  },
+  {
+    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+    0x9324fd72UL
+  },
+  {
+    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+    0xbe9834edUL
+  },
+  {
+    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+    0xde0506f1UL
+  },
+  {
+    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+    0x8def022dUL
+  },
+  {
+    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+    0x72fd2493UL
+  },
+  {
+    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+    0xed3498beUL
+  },
+  {
+    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+    0xf10605deUL
+#endif
+  }
+};
diff --git a/distrib/zlib-1.2.3/deflate.c b/distrib/zlib-1.2.3/deflate.c
new file mode 100644
index 0000000..29ce1f6
--- /dev/null
+++ b/distrib/zlib-1.2.3/deflate.c
@@ -0,0 +1,1736 @@
+/* deflate.c -- compress data using the deflation algorithm
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process depends on being able to identify portions
+ *      of the input text which are identical to earlier input (within a
+ *      sliding window trailing behind the input currently being processed).
+ *
+ *      The most straightforward technique turns out to be the fastest for
+ *      most input files: try all possible matches and select the longest.
+ *      The key feature of this algorithm is that insertions into the string
+ *      dictionary are very simple and thus fast, and deletions are avoided
+ *      completely. Insertions are performed at each input character, whereas
+ *      string matches are performed only when the previous match ends. So it
+ *      is preferable to spend more time in matches to allow very fast string
+ *      insertions and avoid deletions. The matching algorithm for small
+ *      strings is inspired from that of Rabin & Karp. A brute force approach
+ *      is used to find longer strings when a small match has been found.
+ *      A similar algorithm is used in comic (by Jan-Mark Wams) and freeze
+ *      (by Leonid Broukhis).
+ *         A previous version of this file used a more sophisticated algorithm
+ *      (by Fiala and Greene) which is guaranteed to run in linear amortized
+ *      time, but has a larger average cost, uses more memory and is patented.
+ *      However the F&G algorithm may be faster for some highly redundant
+ *      files if the parameter max_chain_length (described below) is too large.
+ *
+ *  ACKNOWLEDGEMENTS
+ *
+ *      The idea of lazy evaluation of matches is due to Jan-Mark Wams, and
+ *      I found it in 'freeze' written by Leonid Broukhis.
+ *      Thanks to many people for bug reports and testing.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"DEFLATE Compressed Data Format Specification".
+ *      Available in http://www.ietf.org/rfc/rfc1951.txt
+ *
+ *      A description of the Rabin and Karp algorithm is given in the book
+ *         "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
+ *
+ *      Fiala,E.R., and Greene,D.H.
+ *         Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "deflate.h"
+
+const char deflate_copyright[] =
+   " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/* ===========================================================================
+ *  Function prototypes.
+ */
+typedef enum {
+    need_more,      /* block not completed, need more input or more output */
+    block_done,     /* block flush performed */
+    finish_started, /* finish started, need only more output at next deflate */
+    finish_done     /* finish done, accept no more input or output */
+} block_state;
+
+typedef block_state (*compress_func) OF((deflate_state *s, int flush));
+/* Compression function. Returns the block state after the call. */
+
+local void fill_window    OF((deflate_state *s));
+local block_state deflate_stored OF((deflate_state *s, int flush));
+local block_state deflate_fast   OF((deflate_state *s, int flush));
+#ifndef FASTEST
+local block_state deflate_slow   OF((deflate_state *s, int flush));
+#endif
+local void lm_init        OF((deflate_state *s));
+local void putShortMSB    OF((deflate_state *s, uInt b));
+local void flush_pending  OF((z_streamp strm));
+local int read_buf        OF((z_streamp strm, Bytef *buf, unsigned size));
+#ifndef FASTEST
+#ifdef ASMV
+      void match_init OF((void)); /* asm code initialization */
+      uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#else
+local uInt longest_match  OF((deflate_state *s, IPos cur_match));
+#endif
+#endif
+local uInt longest_match_fast OF((deflate_state *s, IPos cur_match));
+
+#ifdef DEBUG
+local  void check_match OF((deflate_state *s, IPos start, IPos match,
+                            int length));
+#endif
+
+/* ===========================================================================
+ * Local data
+ */
+
+#define NIL 0
+/* Tail of hash chains */
+
+#ifndef TOO_FAR
+#  define TOO_FAR 4096
+#endif
+/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+typedef struct config_s {
+   ush good_length; /* reduce lazy search above this match length */
+   ush max_lazy;    /* do not perform lazy search above this match length */
+   ush nice_length; /* quit search above this match length */
+   ush max_chain;
+   compress_func func;
+} config;
+
+#ifdef FASTEST
+local const config configuration_table[2] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}}; /* max speed, no lazy matches */
+#else
+local const config configuration_table[10] = {
+/*      good lazy nice chain */
+/* 0 */ {0,    0,  0,    0, deflate_stored},  /* store only */
+/* 1 */ {4,    4,  8,    4, deflate_fast}, /* max speed, no lazy matches */
+/* 2 */ {4,    5, 16,    8, deflate_fast},
+/* 3 */ {4,    6, 32,   32, deflate_fast},
+
+/* 4 */ {4,    4, 16,   16, deflate_slow},  /* lazy matches */
+/* 5 */ {8,   16, 32,   32, deflate_slow},
+/* 6 */ {8,   16, 128, 128, deflate_slow},
+/* 7 */ {8,   32, 128, 256, deflate_slow},
+/* 8 */ {32, 128, 258, 1024, deflate_slow},
+/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */
+#endif
+
+/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4
+ * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
+ * meaning.
+ */
+
+#define EQUAL 0
+/* result of memcmp for equal strings */
+
+#ifndef NO_DUMMY_DECL
+struct static_tree_desc_s {int dummy;}; /* for buggy compilers */
+#endif
+
+/* ===========================================================================
+ * Update a hash value with the given input byte
+ * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
+ *    input characters, so that a running hash key can be computed from the
+ *    previous key instead of complete recalculation each time.
+ */
+#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask)
+
+
+/* ===========================================================================
+ * Insert string str in the dictionary and set match_head to the previous head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * If this file is compiled with -DFASTEST, the compression level is forced
+ * to 1, and no hash chains are maintained.
+ * IN  assertion: all calls to to INSERT_STRING are made with consecutive
+ *    input characters and the first MIN_MATCH bytes of str are valid
+ *    (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#ifdef FASTEST
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#else
+#define INSERT_STRING(s, str, match_head) \
+   (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \
+    match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \
+    s->head[s->ins_h] = (Pos)(str))
+#endif
+
+/* ===========================================================================
+ * Initialize the hash table (avoiding 64K overflow for 16 bit systems).
+ * prev[] will be initialized on the fly.
+ */
+#define CLEAR_HASH(s) \
+    s->head[s->hash_size-1] = NIL; \
+    zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head));
+
+/* ========================================================================= */
+int ZEXPORT deflateInit_(strm, level, version, stream_size)
+    z_streamp strm;
+    int level;
+    const char *version;
+    int stream_size;
+{
+    return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL,
+                         Z_DEFAULT_STRATEGY, version, stream_size);
+    /* To do: ignore strm->next_in if we use it as window */
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy,
+                  version, stream_size)
+    z_streamp strm;
+    int  level;
+    int  method;
+    int  windowBits;
+    int  memLevel;
+    int  strategy;
+    const char *version;
+    int stream_size;
+{
+    deflate_state *s;
+    int wrap = 1;
+    static const char my_version[] = ZLIB_VERSION;
+
+    ushf *overlay;
+    /* We overlay pending_buf and d_buf+l_buf. This works since the average
+     * output size for (length,distance) codes is <= 24 bits.
+     */
+
+    if (version == Z_NULL || version[0] != my_version[0] ||
+        stream_size != sizeof(z_stream)) {
+        return Z_VERSION_ERROR;
+    }
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+
+    strm->msg = Z_NULL;
+    if (strm->zalloc == (alloc_func)0) {
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+
+#ifdef FASTEST
+    if (level != 0) level = 1;
+#else
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+
+    if (windowBits < 0) { /* suppress zlib wrapper */
+        wrap = 0;
+        windowBits = -windowBits;
+    }
+#ifdef GZIP
+    else if (windowBits > 15) {
+        wrap = 2;       /* write gzip wrapper instead */
+        windowBits -= 16;
+    }
+#endif
+    if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED ||
+        windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+        strategy < 0 || strategy > Z_FIXED) {
+        return Z_STREAM_ERROR;
+    }
+    if (windowBits == 8) windowBits = 9;  /* until 256-byte window bug fixed */
+    s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state));
+    if (s == Z_NULL) return Z_MEM_ERROR;
+    strm->state = (struct internal_state FAR *)s;
+    s->strm = strm;
+
+    s->wrap = wrap;
+    s->gzhead = Z_NULL;
+    s->w_bits = windowBits;
+    s->w_size = 1 << s->w_bits;
+    s->w_mask = s->w_size - 1;
+
+    s->hash_bits = memLevel + 7;
+    s->hash_size = 1 << s->hash_bits;
+    s->hash_mask = s->hash_size - 1;
+    s->hash_shift =  ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH);
+
+    s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte));
+    s->prev   = (Posf *)  ZALLOC(strm, s->w_size, sizeof(Pos));
+    s->head   = (Posf *)  ZALLOC(strm, s->hash_size, sizeof(Pos));
+
+    s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+    overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+    s->pending_buf = (uchf *) overlay;
+    s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L);
+
+    if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL ||
+        s->pending_buf == Z_NULL) {
+        s->status = FINISH_STATE;
+        strm->msg = (char*)ERR_MSG(Z_MEM_ERROR);
+        deflateEnd (strm);
+        return Z_MEM_ERROR;
+    }
+    s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+    s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+
+    s->level = level;
+    s->strategy = strategy;
+    s->method = (Byte)method;
+
+    return deflateReset(strm);
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength)
+    z_streamp strm;
+    const Bytef *dictionary;
+    uInt  dictLength;
+{
+    deflate_state *s;
+    uInt length = dictLength;
+    uInt n;
+    IPos hash_head = 0;
+
+    if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL ||
+        strm->state->wrap == 2 ||
+        (strm->state->wrap == 1 && strm->state->status != INIT_STATE))
+        return Z_STREAM_ERROR;
+
+    s = strm->state;
+    if (s->wrap)
+        strm->adler = adler32(strm->adler, dictionary, dictLength);
+
+    if (length < MIN_MATCH) return Z_OK;
+    if (length > MAX_DIST(s)) {
+        length = MAX_DIST(s);
+        dictionary += dictLength - length; /* use the tail of the dictionary */
+    }
+    zmemcpy(s->window, dictionary, length);
+    s->strstart = length;
+    s->block_start = (long)length;
+
+    /* Insert all strings in the hash table (except for the last two bytes).
+     * s->lookahead stays null, so s->ins_h will be recomputed at the next
+     * call of fill_window.
+     */
+    s->ins_h = s->window[0];
+    UPDATE_HASH(s, s->ins_h, s->window[1]);
+    for (n = 0; n <= length - MIN_MATCH; n++) {
+        INSERT_STRING(s, n, hash_head);
+    }
+    if (hash_head) hash_head = 0;  /* to make compiler happy */
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateReset (strm)
+    z_streamp strm;
+{
+    deflate_state *s;
+
+    if (strm == Z_NULL || strm->state == Z_NULL ||
+        strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) {
+        return Z_STREAM_ERROR;
+    }
+
+    strm->total_in = strm->total_out = 0;
+    strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */
+    strm->data_type = Z_UNKNOWN;
+
+    s = (deflate_state *)strm->state;
+    s->pending = 0;
+    s->pending_out = s->pending_buf;
+
+    if (s->wrap < 0) {
+        s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */
+    }
+    s->status = s->wrap ? INIT_STATE : BUSY_STATE;
+    strm->adler =
+#ifdef GZIP
+        s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
+#endif
+        adler32(0L, Z_NULL, 0);
+    s->last_flush = Z_NO_FLUSH;
+
+    _tr_init(s);
+    lm_init(s);
+
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateSetHeader (strm, head)
+    z_streamp strm;
+    gz_headerp head;
+{
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    if (strm->state->wrap != 2) return Z_STREAM_ERROR;
+    strm->state->gzhead = head;
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflatePrime (strm, bits, value)
+    z_streamp strm;
+    int bits;
+    int value;
+{
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    strm->state->bi_valid = bits;
+    strm->state->bi_buf = (ush)(value & ((1 << bits) - 1));
+    return Z_OK;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateParams(strm, level, strategy)
+    z_streamp strm;
+    int level;
+    int strategy;
+{
+    deflate_state *s;
+    compress_func func;
+    int err = Z_OK;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    s = strm->state;
+
+#ifdef FASTEST
+    if (level != 0) level = 1;
+#else
+    if (level == Z_DEFAULT_COMPRESSION) level = 6;
+#endif
+    if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
+        return Z_STREAM_ERROR;
+    }
+    func = configuration_table[s->level].func;
+
+    if (func != configuration_table[level].func && strm->total_in != 0) {
+        /* Flush the last buffer: */
+        err = deflate(strm, Z_PARTIAL_FLUSH);
+    }
+    if (s->level != level) {
+        s->level = level;
+        s->max_lazy_match   = configuration_table[level].max_lazy;
+        s->good_match       = configuration_table[level].good_length;
+        s->nice_match       = configuration_table[level].nice_length;
+        s->max_chain_length = configuration_table[level].max_chain;
+    }
+    s->strategy = strategy;
+    return err;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain)
+    z_streamp strm;
+    int good_length;
+    int max_lazy;
+    int nice_length;
+    int max_chain;
+{
+    deflate_state *s;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    s = strm->state;
+    s->good_match = good_length;
+    s->max_lazy_match = max_lazy;
+    s->nice_match = nice_length;
+    s->max_chain_length = max_chain;
+    return Z_OK;
+}
+
+/* =========================================================================
+ * For the default windowBits of 15 and memLevel of 8, this function returns
+ * a close to exact, as well as small, upper bound on the compressed size.
+ * They are coded as constants here for a reason--if the #define's are
+ * changed, then this function needs to be changed as well.  The return
+ * value for 15 and 8 only works for those exact settings.
+ *
+ * For any setting other than those defaults for windowBits and memLevel,
+ * the value returned is a conservative worst case for the maximum expansion
+ * resulting from using fixed blocks instead of stored blocks, which deflate
+ * can emit on compressed data for some combinations of the parameters.
+ *
+ * This function could be more sophisticated to provide closer upper bounds
+ * for every combination of windowBits and memLevel, as well as wrap.
+ * But even the conservative upper bound of about 14% expansion does not
+ * seem onerous for output buffer allocation.
+ */
+uLong ZEXPORT deflateBound(strm, sourceLen)
+    z_streamp strm;
+    uLong sourceLen;
+{
+    deflate_state *s;
+    uLong destLen;
+
+    /* conservative upper bound */
+    destLen = sourceLen +
+              ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11;
+
+    /* if can't get parameters, return conservative bound */
+    if (strm == Z_NULL || strm->state == Z_NULL)
+        return destLen;
+
+    /* if not default parameters, return conservative bound */
+    s = strm->state;
+    if (s->w_bits != 15 || s->hash_bits != 8 + 7)
+        return destLen;
+
+    /* default settings: return tight bound for that case */
+    return compressBound(sourceLen);
+}
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+local void putShortMSB (s, b)
+    deflate_state *s;
+    uInt b;
+{
+    put_byte(s, (Byte)(b >> 8));
+    put_byte(s, (Byte)(b & 0xff));
+}
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->next_out buffer and copying into it.
+ * (See also read_buf()).
+ */
+local void flush_pending(strm)
+    z_streamp strm;
+{
+    unsigned len = strm->state->pending;
+
+    if (len > strm->avail_out) len = strm->avail_out;
+    if (len == 0) return;
+
+    zmemcpy(strm->next_out, strm->state->pending_out, len);
+    strm->next_out  += len;
+    strm->state->pending_out  += len;
+    strm->total_out += len;
+    strm->avail_out  -= len;
+    strm->state->pending -= len;
+    if (strm->state->pending == 0) {
+        strm->state->pending_out = strm->state->pending_buf;
+    }
+}
+
+/* ========================================================================= */
+int ZEXPORT deflate (strm, flush)
+    z_streamp strm;
+    int flush;
+{
+    int old_flush; /* value of flush param for previous deflate call */
+    deflate_state *s;
+
+    if (strm == Z_NULL || strm->state == Z_NULL ||
+        flush > Z_FINISH || flush < 0) {
+        return Z_STREAM_ERROR;
+    }
+    s = strm->state;
+
+    if (strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0) ||
+        (s->status == FINISH_STATE && flush != Z_FINISH)) {
+        ERR_RETURN(strm, Z_STREAM_ERROR);
+    }
+    if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR);
+
+    s->strm = strm; /* just in case */
+    old_flush = s->last_flush;
+    s->last_flush = flush;
+
+    /* Write the header */
+    if (s->status == INIT_STATE) {
+#ifdef GZIP
+        if (s->wrap == 2) {
+            strm->adler = crc32(0L, Z_NULL, 0);
+            put_byte(s, 31);
+            put_byte(s, 139);
+            put_byte(s, 8);
+            if (s->gzhead == NULL) {
+                put_byte(s, 0);
+                put_byte(s, 0);
+                put_byte(s, 0);
+                put_byte(s, 0);
+                put_byte(s, 0);
+                put_byte(s, s->level == 9 ? 2 :
+                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+                             4 : 0));
+                put_byte(s, OS_CODE);
+                s->status = BUSY_STATE;
+            }
+            else {
+                put_byte(s, (s->gzhead->text ? 1 : 0) +
+                            (s->gzhead->hcrc ? 2 : 0) +
+                            (s->gzhead->extra == Z_NULL ? 0 : 4) +
+                            (s->gzhead->name == Z_NULL ? 0 : 8) +
+                            (s->gzhead->comment == Z_NULL ? 0 : 16)
+                        );
+                put_byte(s, (Byte)(s->gzhead->time & 0xff));
+                put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
+                put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
+                put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff));
+                put_byte(s, s->level == 9 ? 2 :
+                            (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ?
+                             4 : 0));
+                put_byte(s, s->gzhead->os & 0xff);
+                if (s->gzhead->extra != NULL) {
+                    put_byte(s, s->gzhead->extra_len & 0xff);
+                    put_byte(s, (s->gzhead->extra_len >> 8) & 0xff);
+                }
+                if (s->gzhead->hcrc)
+                    strm->adler = crc32(strm->adler, s->pending_buf,
+                                        s->pending);
+                s->gzindex = 0;
+                s->status = EXTRA_STATE;
+            }
+        }
+        else
+#endif
+        {
+            uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
+            uInt level_flags;
+
+            if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2)
+                level_flags = 0;
+            else if (s->level < 6)
+                level_flags = 1;
+            else if (s->level == 6)
+                level_flags = 2;
+            else
+                level_flags = 3;
+            header |= (level_flags << 6);
+            if (s->strstart != 0) header |= PRESET_DICT;
+            header += 31 - (header % 31);
+
+            s->status = BUSY_STATE;
+            putShortMSB(s, header);
+
+            /* Save the adler32 of the preset dictionary: */
+            if (s->strstart != 0) {
+                putShortMSB(s, (uInt)(strm->adler >> 16));
+                putShortMSB(s, (uInt)(strm->adler & 0xffff));
+            }
+            strm->adler = adler32(0L, Z_NULL, 0);
+        }
+    }
+#ifdef GZIP
+    if (s->status == EXTRA_STATE) {
+        if (s->gzhead->extra != NULL) {
+            uInt beg = s->pending;  /* start of bytes to update crc */
+
+            while (s->gzindex < (s->gzhead->extra_len & 0xffff)) {
+                if (s->pending == s->pending_buf_size) {
+                    if (s->gzhead->hcrc && s->pending > beg)
+                        strm->adler = crc32(strm->adler, s->pending_buf + beg,
+                                            s->pending - beg);
+                    flush_pending(strm);
+                    beg = s->pending;
+                    if (s->pending == s->pending_buf_size)
+                        break;
+                }
+                put_byte(s, s->gzhead->extra[s->gzindex]);
+                s->gzindex++;
+            }
+            if (s->gzhead->hcrc && s->pending > beg)
+                strm->adler = crc32(strm->adler, s->pending_buf + beg,
+                                    s->pending - beg);
+            if (s->gzindex == s->gzhead->extra_len) {
+                s->gzindex = 0;
+                s->status = NAME_STATE;
+            }
+        }
+        else
+            s->status = NAME_STATE;
+    }
+    if (s->status == NAME_STATE) {
+        if (s->gzhead->name != NULL) {
+            uInt beg = s->pending;  /* start of bytes to update crc */
+            int val;
+
+            do {
+                if (s->pending == s->pending_buf_size) {
+                    if (s->gzhead->hcrc && s->pending > beg)
+                        strm->adler = crc32(strm->adler, s->pending_buf + beg,
+                                            s->pending - beg);
+                    flush_pending(strm);
+                    beg = s->pending;
+                    if (s->pending == s->pending_buf_size) {
+                        val = 1;
+                        break;
+                    }
+                }
+                val = s->gzhead->name[s->gzindex++];
+                put_byte(s, val);
+            } while (val != 0);
+            if (s->gzhead->hcrc && s->pending > beg)
+                strm->adler = crc32(strm->adler, s->pending_buf + beg,
+                                    s->pending - beg);
+            if (val == 0) {
+                s->gzindex = 0;
+                s->status = COMMENT_STATE;
+            }
+        }
+        else
+            s->status = COMMENT_STATE;
+    }
+    if (s->status == COMMENT_STATE) {
+        if (s->gzhead->comment != NULL) {
+            uInt beg = s->pending;  /* start of bytes to update crc */
+            int val;
+
+            do {
+                if (s->pending == s->pending_buf_size) {
+                    if (s->gzhead->hcrc && s->pending > beg)
+                        strm->adler = crc32(strm->adler, s->pending_buf + beg,
+                                            s->pending - beg);
+                    flush_pending(strm);
+                    beg = s->pending;
+                    if (s->pending == s->pending_buf_size) {
+                        val = 1;
+                        break;
+                    }
+                }
+                val = s->gzhead->comment[s->gzindex++];
+                put_byte(s, val);
+            } while (val != 0);
+            if (s->gzhead->hcrc && s->pending > beg)
+                strm->adler = crc32(strm->adler, s->pending_buf + beg,
+                                    s->pending - beg);
+            if (val == 0)
+                s->status = HCRC_STATE;
+        }
+        else
+            s->status = HCRC_STATE;
+    }
+    if (s->status == HCRC_STATE) {
+        if (s->gzhead->hcrc) {
+            if (s->pending + 2 > s->pending_buf_size)
+                flush_pending(strm);
+            if (s->pending + 2 <= s->pending_buf_size) {
+                put_byte(s, (Byte)(strm->adler & 0xff));
+                put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+                strm->adler = crc32(0L, Z_NULL, 0);
+                s->status = BUSY_STATE;
+            }
+        }
+        else
+            s->status = BUSY_STATE;
+    }
+#endif
+
+    /* Flush as much pending output as possible */
+    if (s->pending != 0) {
+        flush_pending(strm);
+        if (strm->avail_out == 0) {
+            /* Since avail_out is 0, deflate will be called again with
+             * more output space, but possibly with both pending and
+             * avail_in equal to zero. There won't be anything to do,
+             * but this is not an error situation so make sure we
+             * return OK instead of BUF_ERROR at next call of deflate:
+             */
+            s->last_flush = -1;
+            return Z_OK;
+        }
+
+    /* Make sure there is something to do and avoid duplicate consecutive
+     * flushes. For repeated and useless calls with Z_FINISH, we keep
+     * returning Z_STREAM_END instead of Z_BUF_ERROR.
+     */
+    } else if (strm->avail_in == 0 && flush <= old_flush &&
+               flush != Z_FINISH) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* User must not provide more input after the first FINISH: */
+    if (s->status == FINISH_STATE && strm->avail_in != 0) {
+        ERR_RETURN(strm, Z_BUF_ERROR);
+    }
+
+    /* Start a new block or continue the current one.
+     */
+    if (strm->avail_in != 0 || s->lookahead != 0 ||
+        (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) {
+        block_state bstate;
+
+        bstate = (*(configuration_table[s->level].func))(s, flush);
+
+        if (bstate == finish_started || bstate == finish_done) {
+            s->status = FINISH_STATE;
+        }
+        if (bstate == need_more || bstate == finish_started) {
+            if (strm->avail_out == 0) {
+                s->last_flush = -1; /* avoid BUF_ERROR next call, see above */
+            }
+            return Z_OK;
+            /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+             * of deflate should use the same flush parameter to make sure
+             * that the flush is complete. So we don't have to output an
+             * empty block here, this will be done at next call. This also
+             * ensures that for a very small output buffer, we emit at most
+             * one empty block.
+             */
+        }
+        if (bstate == block_done) {
+            if (flush == Z_PARTIAL_FLUSH) {
+                _tr_align(s);
+            } else { /* FULL_FLUSH or SYNC_FLUSH */
+                _tr_stored_block(s, (char*)0, 0L, 0);
+                /* For a full flush, this empty block will be recognized
+                 * as a special marker by inflate_sync().
+                 */
+                if (flush == Z_FULL_FLUSH) {
+                    CLEAR_HASH(s);             /* forget history */
+                }
+            }
+            flush_pending(strm);
+            if (strm->avail_out == 0) {
+              s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+              return Z_OK;
+            }
+        }
+    }
+    Assert(strm->avail_out > 0, "bug2");
+
+    if (flush != Z_FINISH) return Z_OK;
+    if (s->wrap <= 0) return Z_STREAM_END;
+
+    /* Write the trailer */
+#ifdef GZIP
+    if (s->wrap == 2) {
+        put_byte(s, (Byte)(strm->adler & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 16) & 0xff));
+        put_byte(s, (Byte)((strm->adler >> 24) & 0xff));
+        put_byte(s, (Byte)(strm->total_in & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 8) & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 16) & 0xff));
+        put_byte(s, (Byte)((strm->total_in >> 24) & 0xff));
+    }
+    else
+#endif
+    {
+        putShortMSB(s, (uInt)(strm->adler >> 16));
+        putShortMSB(s, (uInt)(strm->adler & 0xffff));
+    }
+    flush_pending(strm);
+    /* If avail_out is zero, the application will call deflate again
+     * to flush the rest.
+     */
+    if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */
+    return s->pending != 0 ? Z_OK : Z_STREAM_END;
+}
+
+/* ========================================================================= */
+int ZEXPORT deflateEnd (strm)
+    z_streamp strm;
+{
+    int status;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+
+    status = strm->state->status;
+    if (status != INIT_STATE &&
+        status != EXTRA_STATE &&
+        status != NAME_STATE &&
+        status != COMMENT_STATE &&
+        status != HCRC_STATE &&
+        status != BUSY_STATE &&
+        status != FINISH_STATE) {
+      return Z_STREAM_ERROR;
+    }
+
+    /* Deallocate in reverse order of allocations: */
+    TRY_FREE(strm, strm->state->pending_buf);
+    TRY_FREE(strm, strm->state->head);
+    TRY_FREE(strm, strm->state->prev);
+    TRY_FREE(strm, strm->state->window);
+
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+
+    return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK;
+}
+
+/* =========================================================================
+ * Copy the source state to the destination state.
+ * To simplify the source, this is not supported for 16-bit MSDOS (which
+ * doesn't have enough memory anyway to duplicate compression states).
+ */
+int ZEXPORT deflateCopy (dest, source)
+    z_streamp dest;
+    z_streamp source;
+{
+#ifdef MAXSEG_64K
+    return Z_STREAM_ERROR;
+#else
+    deflate_state *ds;
+    deflate_state *ss;
+    ushf *overlay;
+
+
+    if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) {
+        return Z_STREAM_ERROR;
+    }
+
+    ss = source->state;
+
+    zmemcpy(dest, source, sizeof(z_stream));
+
+    ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state));
+    if (ds == Z_NULL) return Z_MEM_ERROR;
+    dest->state = (struct internal_state FAR *) ds;
+    zmemcpy(ds, ss, sizeof(deflate_state));
+    ds->strm = dest;
+
+    ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte));
+    ds->prev   = (Posf *)  ZALLOC(dest, ds->w_size, sizeof(Pos));
+    ds->head   = (Posf *)  ZALLOC(dest, ds->hash_size, sizeof(Pos));
+    overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2);
+    ds->pending_buf = (uchf *) overlay;
+
+    if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL ||
+        ds->pending_buf == Z_NULL) {
+        deflateEnd (dest);
+        return Z_MEM_ERROR;
+    }
+    /* following zmemcpy do not work for 16-bit MSDOS */
+    zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte));
+    zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos));
+    zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos));
+    zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size);
+
+    ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf);
+    ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush);
+    ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize;
+
+    ds->l_desc.dyn_tree = ds->dyn_ltree;
+    ds->d_desc.dyn_tree = ds->dyn_dtree;
+    ds->bl_desc.dyn_tree = ds->bl_tree;
+
+    return Z_OK;
+#endif /* MAXSEG_64K */
+}
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read.  All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->next_in buffer and copying from it.
+ * (See also flush_pending()).
+ */
+local int read_buf(strm, buf, size)
+    z_streamp strm;
+    Bytef *buf;
+    unsigned size;
+{
+    unsigned len = strm->avail_in;
+
+    if (len > size) len = size;
+    if (len == 0) return 0;
+
+    strm->avail_in  -= len;
+
+    if (strm->state->wrap == 1) {
+        strm->adler = adler32(strm->adler, strm->next_in, len);
+    }
+#ifdef GZIP
+    else if (strm->state->wrap == 2) {
+        strm->adler = crc32(strm->adler, strm->next_in, len);
+    }
+#endif
+    zmemcpy(buf, strm->next_in, len);
+    strm->next_in  += len;
+    strm->total_in += len;
+
+    return (int)len;
+}
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+local void lm_init (s)
+    deflate_state *s;
+{
+    s->window_size = (ulg)2L*s->w_size;
+
+    CLEAR_HASH(s);
+
+    /* Set the default configuration parameters:
+     */
+    s->max_lazy_match   = configuration_table[s->level].max_lazy;
+    s->good_match       = configuration_table[s->level].good_length;
+    s->nice_match       = configuration_table[s->level].nice_length;
+    s->max_chain_length = configuration_table[s->level].max_chain;
+
+    s->strstart = 0;
+    s->block_start = 0L;
+    s->lookahead = 0;
+    s->match_length = s->prev_length = MIN_MATCH-1;
+    s->match_available = 0;
+    s->ins_h = 0;
+#ifndef FASTEST
+#ifdef ASMV
+    match_init(); /* initialize the asm code */
+#endif
+#endif
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+#ifndef ASMV
+/* For 80x86 and 680x0, an optimized version will be provided in match.asm or
+ * match.S. The code will be functionally equivalent.
+ */
+local uInt longest_match(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    unsigned chain_length = s->max_chain_length;/* max hash chain length */
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    int best_len = s->prev_length;              /* best match length so far */
+    int nice_match = s->nice_match;             /* stop if match long enough */
+    IPos limit = s->strstart > (IPos)MAX_DIST(s) ?
+        s->strstart - (IPos)MAX_DIST(s) : NIL;
+    /* Stop when cur_match becomes <= limit. To simplify the code,
+     * we prevent matches with the string of window index 0.
+     */
+    Posf *prev = s->prev;
+    uInt wmask = s->w_mask;
+
+#ifdef UNALIGNED_OK
+    /* Compare two bytes at a time. Note: this is not always beneficial.
+     * Try with and without -DUNALIGNED_OK to check.
+     */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1;
+    register ush scan_start = *(ushf*)scan;
+    register ush scan_end   = *(ushf*)(scan+best_len-1);
+#else
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+    register Byte scan_end1  = scan[best_len-1];
+    register Byte scan_end   = scan[best_len];
+#endif
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    /* Do not waste too much time if we already have a good match: */
+    if (s->prev_length >= s->good_match) {
+        chain_length >>= 2;
+    }
+    /* Do not look for matches beyond the end of the input. This is necessary
+     * to make deflate deterministic.
+     */
+    if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead;
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    do {
+        Assert(cur_match < s->strstart, "no future");
+        match = s->window + cur_match;
+
+        /* Skip to next match if the match length cannot increase
+         * or if the match length is less than 2.  Note that the checks below
+         * for insufficient lookahead only occur occasionally for performance
+         * reasons.  Therefore uninitialized memory will be accessed, and
+         * conditional jumps will be made that depend on those values.
+         * However the length of the match is limited to the lookahead, so
+         * the output of deflate is not affected by the uninitialized values.
+         */
+#if (defined(UNALIGNED_OK) && MAX_MATCH == 258)
+        /* This code assumes sizeof(unsigned short) == 2. Do not use
+         * UNALIGNED_OK if your compiler uses a different size.
+         */
+        if (*(ushf*)(match+best_len-1) != scan_end ||
+            *(ushf*)match != scan_start) continue;
+
+        /* It is not necessary to compare scan[2] and match[2] since they are
+         * always equal when the other bytes match, given that the hash keys
+         * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at
+         * strstart+3, +5, ... up to strstart+257. We check for insufficient
+         * lookahead only every 4th comparison; the 128th check will be made
+         * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is
+         * necessary to put more guard bytes at the end of the window, or
+         * to check more often for insufficient lookahead.
+         */
+        Assert(scan[2] == match[2], "scan[2]?");
+        scan++, match++;
+        do {
+        } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
+                 scan < strend);
+        /* The funny "do {}" generates better code on most compilers */
+
+        /* Here, scan <= window+strstart+257 */
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+        if (*scan == *match) scan++;
+
+        len = (MAX_MATCH - 1) - (int)(strend-scan);
+        scan = strend - (MAX_MATCH-1);
+
+#else /* UNALIGNED_OK */
+
+        if (match[best_len]   != scan_end  ||
+            match[best_len-1] != scan_end1 ||
+            *match            != *scan     ||
+            *++match          != scan[1])      continue;
+
+        /* The check at best_len-1 can be removed because it will be made
+         * again later. (This heuristic is not always a win.)
+         * It is not necessary to compare scan[2] and match[2] since they
+         * are always equal when the other bytes match, given that
+         * the hash keys are equal and that HASH_BITS >= 8.
+         */
+        scan += 2, match++;
+        Assert(*scan == *match, "match[2]?");
+
+        /* We check for insufficient lookahead only every 8th comparison;
+         * the 256th check will be made at strstart+258.
+         */
+        do {
+        } while (*++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 *++scan == *++match && *++scan == *++match &&
+                 scan < strend);
+
+        Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+        len = MAX_MATCH - (int)(strend - scan);
+        scan = strend - MAX_MATCH;
+
+#endif /* UNALIGNED_OK */
+
+        if (len > best_len) {
+            s->match_start = cur_match;
+            best_len = len;
+            if (len >= nice_match) break;
+#ifdef UNALIGNED_OK
+            scan_end = *(ushf*)(scan+best_len-1);
+#else
+            scan_end1  = scan[best_len-1];
+            scan_end   = scan[best_len];
+#endif
+        }
+    } while ((cur_match = prev[cur_match & wmask]) > limit
+             && --chain_length != 0);
+
+    if ((uInt)best_len <= s->lookahead) return (uInt)best_len;
+    return s->lookahead;
+}
+#endif /* ASMV */
+#endif /* FASTEST */
+
+/* ---------------------------------------------------------------------------
+ * Optimized version for level == 1 or strategy == Z_RLE only
+ */
+local uInt longest_match_fast(s, cur_match)
+    deflate_state *s;
+    IPos cur_match;                             /* current match */
+{
+    register Bytef *scan = s->window + s->strstart; /* current string */
+    register Bytef *match;                       /* matched string */
+    register int len;                           /* length of current match */
+    register Bytef *strend = s->window + s->strstart + MAX_MATCH;
+
+    /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+     * It is easy to get rid of this optimization if necessary.
+     */
+    Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+    Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+    Assert(cur_match < s->strstart, "no future");
+
+    match = s->window + cur_match;
+
+    /* Return failure if the match length is less than 2:
+     */
+    if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1;
+
+    /* The check at best_len-1 can be removed because it will be made
+     * again later. (This heuristic is not always a win.)
+     * It is not necessary to compare scan[2] and match[2] since they
+     * are always equal when the other bytes match, given that
+     * the hash keys are equal and that HASH_BITS >= 8.
+     */
+    scan += 2, match += 2;
+    Assert(*scan == *match, "match[2]?");
+
+    /* We check for insufficient lookahead only every 8th comparison;
+     * the 256th check will be made at strstart+258.
+     */
+    do {
+    } while (*++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             *++scan == *++match && *++scan == *++match &&
+             scan < strend);
+
+    Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+    len = MAX_MATCH - (int)(strend - scan);
+
+    if (len < MIN_MATCH) return MIN_MATCH - 1;
+
+    s->match_start = cur_match;
+    return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead;
+}
+
+#ifdef DEBUG
+/* ===========================================================================
+ * Check that the match at match_start is indeed a match.
+ */
+local void check_match(s, start, match, length)
+    deflate_state *s;
+    IPos start, match;
+    int length;
+{
+    /* check that the match is indeed a match */
+    if (zmemcmp(s->window + match,
+                s->window + start, length) != EQUAL) {
+        fprintf(stderr, " start %u, match %u, length %d\n",
+                start, match, length);
+        do {
+            fprintf(stderr, "%c%c", s->window[match++], s->window[start++]);
+        } while (--length != 0);
+        z_error("invalid match");
+    }
+    if (z_verbose > 1) {
+        fprintf(stderr,"\\[%d,%d]", start-match, length);
+        do { putc(s->window[start++], stderr); } while (--length != 0);
+    }
+}
+#else
+#  define check_match(s, start, match, length)
+#endif /* DEBUG */
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ *    At least one byte has been read, or avail_in == 0; reads are
+ *    performed for at least two bytes (required for the zip translate_eol
+ *    option -- not supported here).
+ */
+local void fill_window(s)
+    deflate_state *s;
+{
+    register unsigned n, m;
+    register Posf *p;
+    unsigned more;    /* Amount of free space at the end of the window. */
+    uInt wsize = s->w_size;
+
+    do {
+        more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart);
+
+        /* Deal with !@#$% 64K limit: */
+        if (sizeof(int) <= 2) {
+            if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+                more = wsize;
+
+            } else if (more == (unsigned)(-1)) {
+                /* Very unlikely, but possible on 16 bit machine if
+                 * strstart == 0 && lookahead == 1 (input done a byte at time)
+                 */
+                more--;
+            }
+        }
+
+        /* If the window is almost full and there is insufficient lookahead,
+         * move the upper half to the lower one to make room in the upper half.
+         */
+        if (s->strstart >= wsize+MAX_DIST(s)) {
+
+            zmemcpy(s->window, s->window+wsize, (unsigned)wsize);
+            s->match_start -= wsize;
+            s->strstart    -= wsize; /* we now have strstart >= MAX_DIST */
+            s->block_start -= (long) wsize;
+
+            /* Slide the hash table (could be avoided with 32 bit values
+               at the expense of memory usage). We slide even when level == 0
+               to keep the hash table consistent if we switch back to level > 0
+               later. (Using level 0 permanently is not an optimal usage of
+               zlib, so we don't care about this pathological case.)
+             */
+            /* %%% avoid this when Z_RLE */
+            n = s->hash_size;
+            p = &s->head[n];
+            do {
+                m = *--p;
+                *p = (Pos)(m >= wsize ? m-wsize : NIL);
+            } while (--n);
+
+            n = wsize;
+#ifndef FASTEST
+            p = &s->prev[n];
+            do {
+                m = *--p;
+                *p = (Pos)(m >= wsize ? m-wsize : NIL);
+                /* If n is not on any hash chain, prev[n] is garbage but
+                 * its value will never be used.
+                 */
+            } while (--n);
+#endif
+            more += wsize;
+        }
+        if (s->strm->avail_in == 0) return;
+
+        /* If there was no sliding:
+         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+         *    more == window_size - lookahead - strstart
+         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+         * => more >= window_size - 2*WSIZE + 2
+         * In the BIG_MEM or MMAP case (not yet supported),
+         *   window_size == input_size + MIN_LOOKAHEAD  &&
+         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+         * Otherwise, window_size == 2*WSIZE so more >= 2.
+         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+         */
+        Assert(more >= 2, "more < 2");
+
+        n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more);
+        s->lookahead += n;
+
+        /* Initialize the hash value now that we have some input: */
+        if (s->lookahead >= MIN_MATCH) {
+            s->ins_h = s->window[s->strstart];
+            UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+            Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+        }
+        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+         * but this is not important since only literal bytes will be emitted.
+         */
+
+    } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0);
+}
+
+/* ===========================================================================
+ * Flush the current block, with given end-of-file flag.
+ * IN assertion: strstart is set to the end of the current match.
+ */
+#define FLUSH_BLOCK_ONLY(s, eof) { \
+   _tr_flush_block(s, (s->block_start >= 0L ? \
+                   (charf *)&s->window[(unsigned)s->block_start] : \
+                   (charf *)Z_NULL), \
+                (ulg)((long)s->strstart - s->block_start), \
+                (eof)); \
+   s->block_start = s->strstart; \
+   flush_pending(s->strm); \
+   Tracev((stderr,"[FLUSH]")); \
+}
+
+/* Same but force premature exit if necessary. */
+#define FLUSH_BLOCK(s, eof) { \
+   FLUSH_BLOCK_ONLY(s, eof); \
+   if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+local block_state deflate_stored(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+     * to pending_buf_size, and each stored block has a 5 byte header:
+     */
+    ulg max_block_size = 0xffff;
+    ulg max_start;
+
+    if (max_block_size > s->pending_buf_size - 5) {
+        max_block_size = s->pending_buf_size - 5;
+    }
+
+    /* Copy as much as possible from input to output: */
+    for (;;) {
+        /* Fill the window as much as possible: */
+        if (s->lookahead <= 1) {
+
+            Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+                   s->block_start >= (long)s->w_size, "slide too late");
+
+            fill_window(s);
+            if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more;
+
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+        Assert(s->block_start >= 0L, "block gone");
+
+        s->strstart += s->lookahead;
+        s->lookahead = 0;
+
+        /* Emit a stored block if pending_buf will be full: */
+        max_start = s->block_start + max_block_size;
+        if (s->strstart == 0 || (ulg)s->strstart >= max_start) {
+            /* strstart == 0 is possible when wraparound on 16-bit machine */
+            s->lookahead = (uInt)(s->strstart - max_start);
+            s->strstart = (uInt)max_start;
+            FLUSH_BLOCK(s, 0);
+        }
+        /* Flush if we may have to slide, otherwise block_start may become
+         * negative and the data will be gone:
+         */
+        if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) {
+            FLUSH_BLOCK(s, 0);
+        }
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+local block_state deflate_fast(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head = NIL; /* head of the hash chain */
+    int bflush;           /* set if current block must be flushed */
+
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         * At this point we have always match_length < MIN_MATCH
+         */
+        if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+#ifdef FASTEST
+            if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) ||
+                (s->strategy == Z_RLE && s->strstart - hash_head == 1)) {
+                s->match_length = longest_match_fast (s, hash_head);
+            }
+#else
+            if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) {
+                s->match_length = longest_match (s, hash_head);
+            } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+                s->match_length = longest_match_fast (s, hash_head);
+            }
+#endif
+            /* longest_match() or longest_match_fast() sets match_start */
+        }
+        if (s->match_length >= MIN_MATCH) {
+            check_match(s, s->strstart, s->match_start, s->match_length);
+
+            _tr_tally_dist(s, s->strstart - s->match_start,
+                           s->match_length - MIN_MATCH, bflush);
+
+            s->lookahead -= s->match_length;
+
+            /* Insert new strings in the hash table only if the match length
+             * is not too large. This saves time but degrades compression.
+             */
+#ifndef FASTEST
+            if (s->match_length <= s->max_insert_length &&
+                s->lookahead >= MIN_MATCH) {
+                s->match_length--; /* string at strstart already in table */
+                do {
+                    s->strstart++;
+                    INSERT_STRING(s, s->strstart, hash_head);
+                    /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+                     * always MIN_MATCH bytes ahead.
+                     */
+                } while (--s->match_length != 0);
+                s->strstart++;
+            } else
+#endif
+            {
+                s->strstart += s->match_length;
+                s->match_length = 0;
+                s->ins_h = s->window[s->strstart];
+                UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]);
+#if MIN_MATCH != 3
+                Call UPDATE_HASH() MIN_MATCH-3 more times
+#endif
+                /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+                 * matter since it will be recomputed at next deflate call.
+                 */
+            }
+        } else {
+            /* No match, output a literal byte */
+            Tracevv((stderr,"%c", s->window[s->strstart]));
+            _tr_tally_lit (s, s->window[s->strstart], bflush);
+            s->lookahead--;
+            s->strstart++;
+        }
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+
+#ifndef FASTEST
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+local block_state deflate_slow(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    IPos hash_head = NIL;    /* head of hash chain */
+    int bflush;              /* set if current block must be flushed */
+
+    /* Process the input block. */
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the next match, plus MIN_MATCH bytes to insert the
+         * string following the next match.
+         */
+        if (s->lookahead < MIN_LOOKAHEAD) {
+            fill_window(s);
+            if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* Insert the string window[strstart .. strstart+2] in the
+         * dictionary, and set hash_head to the head of the hash chain:
+         */
+        if (s->lookahead >= MIN_MATCH) {
+            INSERT_STRING(s, s->strstart, hash_head);
+        }
+
+        /* Find the longest match, discarding those <= prev_length.
+         */
+        s->prev_length = s->match_length, s->prev_match = s->match_start;
+        s->match_length = MIN_MATCH-1;
+
+        if (hash_head != NIL && s->prev_length < s->max_lazy_match &&
+            s->strstart - hash_head <= MAX_DIST(s)) {
+            /* To simplify the code, we prevent matches with the string
+             * of window index 0 (in particular we have to avoid a match
+             * of the string with itself at the start of the input file).
+             */
+            if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) {
+                s->match_length = longest_match (s, hash_head);
+            } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) {
+                s->match_length = longest_match_fast (s, hash_head);
+            }
+            /* longest_match() or longest_match_fast() sets match_start */
+
+            if (s->match_length <= 5 && (s->strategy == Z_FILTERED
+#if TOO_FAR <= 32767
+                || (s->match_length == MIN_MATCH &&
+                    s->strstart - s->match_start > TOO_FAR)
+#endif
+                )) {
+
+                /* If prev_match is also MIN_MATCH, match_start is garbage
+                 * but we will ignore the current match anyway.
+                 */
+                s->match_length = MIN_MATCH-1;
+            }
+        }
+        /* If there was a match at the previous step and the current
+         * match is not better, output the previous match:
+         */
+        if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) {
+            uInt max_insert = s->strstart + s->lookahead - MIN_MATCH;
+            /* Do not insert strings in hash table beyond this. */
+
+            check_match(s, s->strstart-1, s->prev_match, s->prev_length);
+
+            _tr_tally_dist(s, s->strstart -1 - s->prev_match,
+                           s->prev_length - MIN_MATCH, bflush);
+
+            /* Insert in hash table all strings up to the end of the match.
+             * strstart-1 and strstart are already inserted. If there is not
+             * enough lookahead, the last two strings are not inserted in
+             * the hash table.
+             */
+            s->lookahead -= s->prev_length-1;
+            s->prev_length -= 2;
+            do {
+                if (++s->strstart <= max_insert) {
+                    INSERT_STRING(s, s->strstart, hash_head);
+                }
+            } while (--s->prev_length != 0);
+            s->match_available = 0;
+            s->match_length = MIN_MATCH-1;
+            s->strstart++;
+
+            if (bflush) FLUSH_BLOCK(s, 0);
+
+        } else if (s->match_available) {
+            /* If there was no match at the previous position, output a
+             * single literal. If there was a match but the current match
+             * is longer, truncate the previous match to a single literal.
+             */
+            Tracevv((stderr,"%c", s->window[s->strstart-1]));
+            _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+            if (bflush) {
+                FLUSH_BLOCK_ONLY(s, 0);
+            }
+            s->strstart++;
+            s->lookahead--;
+            if (s->strm->avail_out == 0) return need_more;
+        } else {
+            /* There is no previous match to compare with, wait for
+             * the next step to decide.
+             */
+            s->match_available = 1;
+            s->strstart++;
+            s->lookahead--;
+        }
+    }
+    Assert (flush != Z_NO_FLUSH, "no flush?");
+    if (s->match_available) {
+        Tracevv((stderr,"%c", s->window[s->strstart-1]));
+        _tr_tally_lit(s, s->window[s->strstart-1], bflush);
+        s->match_available = 0;
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif /* FASTEST */
+
+#if 0
+/* ===========================================================================
+ * For Z_RLE, simply look for runs of bytes, generate matches only of distance
+ * one.  Do not maintain a hash table.  (It will be regenerated if this run of
+ * deflate switches away from Z_RLE.)
+ */
+local block_state deflate_rle(s, flush)
+    deflate_state *s;
+    int flush;
+{
+    int bflush;         /* set if current block must be flushed */
+    uInt run;           /* length of run */
+    uInt max;           /* maximum length of run */
+    uInt prev;          /* byte at distance one to match */
+    Bytef *scan;        /* scan for end of run */
+
+    for (;;) {
+        /* Make sure that we always have enough lookahead, except
+         * at the end of the input file. We need MAX_MATCH bytes
+         * for the longest encodable run.
+         */
+        if (s->lookahead < MAX_MATCH) {
+            fill_window(s);
+            if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) {
+                return need_more;
+            }
+            if (s->lookahead == 0) break; /* flush the current block */
+        }
+
+        /* See how many times the previous byte repeats */
+        run = 0;
+        if (s->strstart > 0) {      /* if there is a previous byte, that is */
+            max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH;
+            scan = s->window + s->strstart - 1;
+            prev = *scan++;
+            do {
+                if (*scan++ != prev)
+                    break;
+            } while (++run < max);
+        }
+
+        /* Emit match if have run of MIN_MATCH or longer, else emit literal */
+        if (run >= MIN_MATCH) {
+            check_match(s, s->strstart, s->strstart - 1, run);
+            _tr_tally_dist(s, 1, run - MIN_MATCH, bflush);
+            s->lookahead -= run;
+            s->strstart += run;
+        } else {
+            /* No match, output a literal byte */
+            Tracevv((stderr,"%c", s->window[s->strstart]));
+            _tr_tally_lit (s, s->window[s->strstart], bflush);
+            s->lookahead--;
+            s->strstart++;
+        }
+        if (bflush) FLUSH_BLOCK(s, 0);
+    }
+    FLUSH_BLOCK(s, flush == Z_FINISH);
+    return flush == Z_FINISH ? finish_done : block_done;
+}
+#endif
diff --git a/distrib/zlib-1.2.3/deflate.h b/distrib/zlib-1.2.3/deflate.h
new file mode 100644
index 0000000..05a5ab3
--- /dev/null
+++ b/distrib/zlib-1.2.3/deflate.h
@@ -0,0 +1,331 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2004 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer creation by deflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip encoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define INIT_STATE    42
+#define EXTRA_STATE   69
+#define NAME_STATE    73
+#define COMMENT_STATE 91
+#define HCRC_STATE   103
+#define BUSY_STATE   113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+    union {
+        ush  freq;       /* frequency count */
+        ush  code;       /* bit string */
+    } fc;
+    union {
+        ush  dad;        /* father node in Huffman tree */
+        ush  len;        /* length of bit string */
+    } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+typedef struct static_tree_desc_s  static_tree_desc;
+
+typedef struct tree_desc_s {
+    ct_data *dyn_tree;           /* the dynamic tree */
+    int     max_code;            /* largest code with non zero frequency */
+    static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+    z_streamp strm;      /* pointer back to this zlib stream */
+    int   status;        /* as the name implies */
+    Bytef *pending_buf;  /* output still pending */
+    ulg   pending_buf_size; /* size of pending_buf */
+    Bytef *pending_out;  /* next pending byte to output to the stream */
+    uInt   pending;      /* nb of bytes in the pending buffer */
+    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */
+    gz_headerp  gzhead;  /* gzip header information to write */
+    uInt   gzindex;      /* where in extra, name, or comment */
+    Byte  method;        /* STORED (for zip only) or DEFLATED */
+    int   last_flush;    /* value of flush param for previous deflate call */
+
+                /* used by deflate.c: */
+
+    uInt  w_size;        /* LZ77 window size (32K by default) */
+    uInt  w_bits;        /* log2(w_size)  (8..16) */
+    uInt  w_mask;        /* w_size - 1 */
+
+    Bytef *window;
+    /* Sliding window. Input bytes are read into the second half of the window,
+     * and move to the first half later to keep a dictionary of at least wSize
+     * bytes. With this organization, matches are limited to a distance of
+     * wSize-MAX_MATCH bytes, but this ensures that IO is always
+     * performed with a length multiple of the block size. Also, it limits
+     * the window size to 64K, which is quite useful on MSDOS.
+     * To do: use the user input buffer as sliding window.
+     */
+
+    ulg window_size;
+    /* Actual size of window: 2*wSize, except when the user input buffer
+     * is directly used as sliding window.
+     */
+
+    Posf *prev;
+    /* Link to older string with same hash index. To limit the size of this
+     * array to 64K, this link is maintained only for the last 32K strings.
+     * An index in this array is thus a window index modulo 32K.
+     */
+
+    Posf *head; /* Heads of the hash chains or NIL. */
+
+    uInt  ins_h;          /* hash index of string to be inserted */
+    uInt  hash_size;      /* number of elements in hash table */
+    uInt  hash_bits;      /* log2(hash_size) */
+    uInt  hash_mask;      /* hash_size-1 */
+
+    uInt  hash_shift;
+    /* Number of bits by which ins_h must be shifted at each input
+     * step. It must be such that after MIN_MATCH steps, the oldest
+     * byte no longer takes part in the hash key, that is:
+     *   hash_shift * MIN_MATCH >= hash_bits
+     */
+
+    long block_start;
+    /* Window position at the beginning of the current output block. Gets
+     * negative when the window is moved backwards.
+     */
+
+    uInt match_length;           /* length of best match */
+    IPos prev_match;             /* previous match */
+    int match_available;         /* set if previous match exists */
+    uInt strstart;               /* start of string to insert */
+    uInt match_start;            /* start of matching string */
+    uInt lookahead;              /* number of valid bytes ahead in window */
+
+    uInt prev_length;
+    /* Length of the best match at previous step. Matches not greater than this
+     * are discarded. This is used in the lazy match evaluation.
+     */
+
+    uInt max_chain_length;
+    /* To speed up deflation, hash chains are never searched beyond this
+     * length.  A higher limit improves compression ratio but degrades the
+     * speed.
+     */
+
+    uInt max_lazy_match;
+    /* Attempt to find a better match only when the current match is strictly
+     * smaller than this value. This mechanism is used only for compression
+     * levels >= 4.
+     */
+#   define max_insert_length  max_lazy_match
+    /* Insert new strings in the hash table only if the match length is not
+     * greater than this length. This saves time but degrades compression.
+     * max_insert_length is used only for compression levels <= 3.
+     */
+
+    int level;    /* compression level (1..9) */
+    int strategy; /* favor or force Huffman coding*/
+
+    uInt good_match;
+    /* Use a faster search when the previous match is longer than this */
+
+    int nice_match; /* Stop searching when current match exceeds this */
+
+                /* used by trees.c: */
+    /* Didn't use ct_data typedef below to supress compiler warning */
+    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
+    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
+
+    struct tree_desc_s l_desc;               /* desc. for literal tree */
+    struct tree_desc_s d_desc;               /* desc. for distance tree */
+    struct tree_desc_s bl_desc;              /* desc. for bit length tree */
+
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
+    int heap_len;               /* number of elements in the heap */
+    int heap_max;               /* element of largest frequency */
+    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+     * The same heap array is used to build all trees.
+     */
+
+    uch depth[2*L_CODES+1];
+    /* Depth of each subtree used as tie breaker for trees of equal frequency
+     */
+
+    uchf *l_buf;          /* buffer for literals or lengths */
+
+    uInt  lit_bufsize;
+    /* Size of match buffer for literals/lengths.  There are 4 reasons for
+     * limiting lit_bufsize to 64K:
+     *   - frequencies can be kept in 16 bit counters
+     *   - if compression is not successful for the first block, all input
+     *     data is still in the window so we can still emit a stored block even
+     *     when input comes from standard input.  (This can also be done for
+     *     all blocks if lit_bufsize is not greater than 32K.)
+     *   - if compression is not successful for a file smaller than 64K, we can
+     *     even emit a stored file instead of a stored block (saving 5 bytes).
+     *     This is applicable only for zip (not gzip or zlib).
+     *   - creating new Huffman trees less frequently may not provide fast
+     *     adaptation to changes in the input data statistics. (Take for
+     *     example a binary file with poorly compressible code followed by
+     *     a highly compressible string table.) Smaller buffer sizes give
+     *     fast adaptation but have of course the overhead of transmitting
+     *     trees more frequently.
+     *   - I can't count above 4
+     */
+
+    uInt last_lit;      /* running index in l_buf */
+
+    ushf *d_buf;
+    /* Buffer for distances. To simplify the code, d_buf and l_buf have
+     * the same number of elements. To use different lengths, an extra flag
+     * array would be necessary.
+     */
+
+    ulg opt_len;        /* bit length of current block with optimal trees */
+    ulg static_len;     /* bit length of current block with static trees */
+    uInt matches;       /* number of string matches in current block */
+    int last_eob_len;   /* bit length of EOB code for last block */
+
+#ifdef DEBUG
+    ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */
+#endif
+
+    ush bi_buf;
+    /* Output buffer. bits are inserted starting at the bottom (least
+     * significant bits).
+     */
+    int bi_valid;
+    /* Number of valid bits in bi_buf.  All bits above the last valid bit
+     * are always zero.
+     */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+        /* in trees.c */
+void _tr_init         OF((deflate_state *s));
+int  _tr_tally        OF((deflate_state *s, unsigned dist, unsigned lc));
+void _tr_flush_block  OF((deflate_state *s, charf *buf, ulg stored_len,
+                          int eof));
+void _tr_align        OF((deflate_state *s));
+void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len,
+                          int eof));
+
+#define d_code(dist) \
+   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+  extern uch _length_code[];
+  extern uch _dist_code[];
+#else
+  extern const uch _length_code[];
+  extern const uch _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+  { uch cc = (c); \
+    s->d_buf[s->last_lit] = 0; \
+    s->l_buf[s->last_lit++] = cc; \
+    s->dyn_ltree[cc].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+   }
+# define _tr_tally_dist(s, distance, length, flush) \
+  { uch len = (length); \
+    ush dist = (distance); \
+    s->d_buf[s->last_lit] = dist; \
+    s->l_buf[s->last_lit++] = len; \
+    dist--; \
+    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+    s->dyn_dtree[d_code(dist)].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+  }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+              flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/distrib/zlib-1.2.3/gzio.c b/distrib/zlib-1.2.3/gzio.c
new file mode 100644
index 0000000..7e90f49
--- /dev/null
+++ b/distrib/zlib-1.2.3/gzio.c
@@ -0,0 +1,1026 @@
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ * Compile this file with -DNO_GZCOMPRESS to avoid the compression code.
+ */
+
+/* @(#) $Id$ */
+
+#include <stdio.h>
+
+#include "zutil.h"
+
+#ifdef NO_DEFLATE       /* for compatibility with old definition */
+#  define NO_GZCOMPRESS
+#endif
+
+#ifndef NO_DUMMY_DECL
+struct internal_state {int dummy;}; /* for buggy compilers */
+#endif
+
+#ifndef Z_BUFSIZE
+#  ifdef MAXSEG_64K
+#    define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
+#  else
+#    define Z_BUFSIZE 16384
+#  endif
+#endif
+#ifndef Z_PRINTF_BUFSIZE
+#  define Z_PRINTF_BUFSIZE 4096
+#endif
+
+#ifdef __MVS__
+#  pragma map (fdopen , "\174\174FDOPEN")
+   FILE *fdopen(int, const char *);
+#endif
+
+#ifndef STDC
+extern voidp  malloc OF((uInt size));
+extern void   free   OF((voidpf ptr));
+#endif
+
+#define ALLOC(size) malloc(size)
+#define TRYFREE(p) {if (p) free(p);}
+
+static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
+#define COMMENT      0x10 /* bit 4 set: file comment present */
+#define RESERVED     0xE0 /* bits 5..7: reserved */
+
+typedef struct gz_stream {
+    z_stream stream;
+    int      z_err;   /* error code for last stream operation */
+    int      z_eof;   /* set if end of input file */
+    FILE     *file;   /* .gz file */
+    Byte     *inbuf;  /* input buffer */
+    Byte     *outbuf; /* output buffer */
+    uLong    crc;     /* crc32 of uncompressed data */
+    char     *msg;    /* error message */
+    char     *path;   /* path name for debugging only */
+    int      transparent; /* 1 if input file is not a .gz file */
+    char     mode;    /* 'w' or 'r' */
+    z_off_t  start;   /* start of compressed data in file (header skipped) */
+    z_off_t  in;      /* bytes into deflate or inflate */
+    z_off_t  out;     /* bytes out of deflate or inflate */
+    int      back;    /* one character push-back */
+    int      last;    /* true if push-back is last character */
+} gz_stream;
+
+
+local gzFile gz_open      OF((const char *path, const char *mode, int  fd));
+local int do_flush        OF((gzFile file, int flush));
+local int    get_byte     OF((gz_stream *s));
+local void   check_header OF((gz_stream *s));
+local int    destroy      OF((gz_stream *s));
+local void   putLong      OF((FILE *file, uLong x));
+local uLong  getLong      OF((gz_stream *s));
+
+/* ===========================================================================
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+   or path name (if fd == -1).
+     gz_open returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).
+*/
+local gzFile gz_open (path, mode, fd)
+    const char *path;
+    const char *mode;
+    int  fd;
+{
+    int err;
+    int level = Z_DEFAULT_COMPRESSION; /* compression level */
+    int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+    char *p = (char*)mode;
+    gz_stream *s;
+    char fmode[80]; /* copy of mode, without the compression level */
+    char *m = fmode;
+
+    if (!path || !mode) return Z_NULL;
+
+    s = (gz_stream *)ALLOC(sizeof(gz_stream));
+    if (!s) return Z_NULL;
+
+    s->stream.zalloc = (alloc_func)0;
+    s->stream.zfree = (free_func)0;
+    s->stream.opaque = (voidpf)0;
+    s->stream.next_in = s->inbuf = Z_NULL;
+    s->stream.next_out = s->outbuf = Z_NULL;
+    s->stream.avail_in = s->stream.avail_out = 0;
+    s->file = NULL;
+    s->z_err = Z_OK;
+    s->z_eof = 0;
+    s->in = 0;
+    s->out = 0;
+    s->back = EOF;
+    s->crc = crc32(0L, Z_NULL, 0);
+    s->msg = NULL;
+    s->transparent = 0;
+
+    s->path = (char*)ALLOC(strlen(path)+1);
+    if (s->path == NULL) {
+        return destroy(s), (gzFile)Z_NULL;
+    }
+    strcpy(s->path, path); /* do this early for debugging */
+
+    s->mode = '\0';
+    do {
+        if (*p == 'r') s->mode = 'r';
+        if (*p == 'w' || *p == 'a') s->mode = 'w';
+        if (*p >= '0' && *p <= '9') {
+            level = *p - '0';
+        } else if (*p == 'f') {
+          strategy = Z_FILTERED;
+        } else if (*p == 'h') {
+          strategy = Z_HUFFMAN_ONLY;
+        } else if (*p == 'R') {
+          strategy = Z_RLE;
+        } else {
+            *m++ = *p; /* copy the mode */
+        }
+    } while (*p++ && m != fmode + sizeof(fmode));
+    if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL;
+
+    if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+        err = Z_STREAM_ERROR;
+#else
+        err = deflateInit2(&(s->stream), level,
+                           Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
+        /* windowBits is passed < 0 to suppress zlib header */
+
+        s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+#endif
+        if (err != Z_OK || s->outbuf == Z_NULL) {
+            return destroy(s), (gzFile)Z_NULL;
+        }
+    } else {
+        s->stream.next_in  = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE);
+
+        err = inflateInit2(&(s->stream), -MAX_WBITS);
+        /* windowBits is passed < 0 to tell that there is no zlib header.
+         * Note that in this case inflate *requires* an extra "dummy" byte
+         * after the compressed stream in order to complete decompression and
+         * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+         * present after the compressed stream.
+         */
+        if (err != Z_OK || s->inbuf == Z_NULL) {
+            return destroy(s), (gzFile)Z_NULL;
+        }
+    }
+    s->stream.avail_out = Z_BUFSIZE;
+
+    errno = 0;
+    s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode);
+
+    if (s->file == NULL) {
+        return destroy(s), (gzFile)Z_NULL;
+    }
+    if (s->mode == 'w') {
+        /* Write a very simple .gz header:
+         */
+        fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
+             Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE);
+        s->start = 10L;
+        /* We use 10L instead of ftell(s->file) to because ftell causes an
+         * fflush on some systems. This version of the library doesn't use
+         * start anyway in write mode, so this initialization is not
+         * necessary.
+         */
+    } else {
+        check_header(s); /* skip the .gz header */
+        s->start = ftell(s->file) - s->stream.avail_in;
+    }
+
+    return (gzFile)s;
+}
+
+/* ===========================================================================
+     Opens a gzip (.gz) file for reading or writing.
+*/
+gzFile ZEXPORT gzopen (path, mode)
+    const char *path;
+    const char *mode;
+{
+    return gz_open (path, mode, -1);
+}
+
+/* ===========================================================================
+     Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+   to mimic the behavio(u)r of fdopen.
+*/
+gzFile ZEXPORT gzdopen (fd, mode)
+    int fd;
+    const char *mode;
+{
+    char name[46];      /* allow for up to 128-bit integers */
+
+    if (fd < 0) return (gzFile)Z_NULL;
+    sprintf(name, "<fd:%d>", fd); /* for debugging */
+
+    return gz_open (name, mode, fd);
+}
+
+/* ===========================================================================
+ * Update the compression level and strategy
+ */
+int ZEXPORT gzsetparams (file, level, strategy)
+    gzFile file;
+    int level;
+    int strategy;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    /* Make room to allow flushing */
+    if (s->stream.avail_out == 0) {
+
+        s->stream.next_out = s->outbuf;
+        if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+            s->z_err = Z_ERRNO;
+        }
+        s->stream.avail_out = Z_BUFSIZE;
+    }
+
+    return deflateParams (&(s->stream), level, strategy);
+}
+
+/* ===========================================================================
+     Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+   for end of file.
+   IN assertion: the stream s has been sucessfully opened for reading.
+*/
+local int get_byte(s)
+    gz_stream *s;
+{
+    if (s->z_eof) return EOF;
+    if (s->stream.avail_in == 0) {
+        errno = 0;
+        s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+        if (s->stream.avail_in == 0) {
+            s->z_eof = 1;
+            if (ferror(s->file)) s->z_err = Z_ERRNO;
+            return EOF;
+        }
+        s->stream.next_in = s->inbuf;
+    }
+    s->stream.avail_in--;
+    return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+      Check the gzip header of a gz_stream opened for reading. Set the stream
+    mode to transparent if the gzip magic header is not present; set s->err
+    to Z_DATA_ERROR if the magic header is present but the rest of the header
+    is incorrect.
+    IN assertion: the stream s has already been created sucessfully;
+       s->stream.avail_in is zero for the first time, but may be non-zero
+       for concatenated .gz files.
+*/
+local void check_header(s)
+    gz_stream *s;
+{
+    int method; /* method byte */
+    int flags;  /* flags byte */
+    uInt len;
+    int c;
+
+    /* Assure two bytes in the buffer so we can peek ahead -- handle case
+       where first byte of header is at the end of the buffer after the last
+       gzip segment */
+    len = s->stream.avail_in;
+    if (len < 2) {
+        if (len) s->inbuf[0] = s->stream.next_in[0];
+        errno = 0;
+        len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file);
+        if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO;
+        s->stream.avail_in += len;
+        s->stream.next_in = s->inbuf;
+        if (s->stream.avail_in < 2) {
+            s->transparent = s->stream.avail_in;
+            return;
+        }
+    }
+
+    /* Peek ahead to check the gzip magic header */
+    if (s->stream.next_in[0] != gz_magic[0] ||
+        s->stream.next_in[1] != gz_magic[1]) {
+        s->transparent = 1;
+        return;
+    }
+    s->stream.avail_in -= 2;
+    s->stream.next_in += 2;
+
+    /* Check the rest of the gzip header */
+    method = get_byte(s);
+    flags = get_byte(s);
+    if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+        s->z_err = Z_DATA_ERROR;
+        return;
+    }
+
+    /* Discard time, xflags and OS code: */
+    for (len = 0; len < 6; len++) (void)get_byte(s);
+
+    if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+        len  =  (uInt)get_byte(s);
+        len += ((uInt)get_byte(s))<<8;
+        /* len is garbage if EOF but the loop below will quit anyway */
+        while (len-- != 0 && get_byte(s) != EOF) ;
+    }
+    if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+        while ((c = get_byte(s)) != 0 && c != EOF) ;
+    }
+    if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
+        while ((c = get_byte(s)) != 0 && c != EOF) ;
+    }
+    if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
+        for (len = 0; len < 2; len++) (void)get_byte(s);
+    }
+    s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+}
+
+ /* ===========================================================================
+ * Cleanup then free the given gz_stream. Return a zlib error code.
+   Try freeing in the reverse order of allocations.
+ */
+local int destroy (s)
+    gz_stream *s;
+{
+    int err = Z_OK;
+
+    if (!s) return Z_STREAM_ERROR;
+
+    TRYFREE(s->msg);
+
+    if (s->stream.state != NULL) {
+        if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+            err = Z_STREAM_ERROR;
+#else
+            err = deflateEnd(&(s->stream));
+#endif
+        } else if (s->mode == 'r') {
+            err = inflateEnd(&(s->stream));
+        }
+    }
+    if (s->file != NULL && fclose(s->file)) {
+#ifdef ESPIPE
+        if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */
+#endif
+            err = Z_ERRNO;
+    }
+    if (s->z_err < 0) err = s->z_err;
+
+    TRYFREE(s->inbuf);
+    TRYFREE(s->outbuf);
+    TRYFREE(s->path);
+    TRYFREE(s);
+    return err;
+}
+
+/* ===========================================================================
+     Reads the given number of uncompressed bytes from the compressed file.
+   gzread returns the number of bytes actually read (0 for end of file).
+*/
+int ZEXPORT gzread (file, buf, len)
+    gzFile file;
+    voidp buf;
+    unsigned len;
+{
+    gz_stream *s = (gz_stream*)file;
+    Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+    Byte  *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+
+    if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR;
+
+    if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
+    if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
+
+    next_out = (Byte*)buf;
+    s->stream.next_out = (Bytef*)buf;
+    s->stream.avail_out = len;
+
+    if (s->stream.avail_out && s->back != EOF) {
+        *next_out++ = s->back;
+        s->stream.next_out++;
+        s->stream.avail_out--;
+        s->back = EOF;
+        s->out++;
+        start++;
+        if (s->last) {
+            s->z_err = Z_STREAM_END;
+            return 1;
+        }
+    }
+
+    while (s->stream.avail_out != 0) {
+
+        if (s->transparent) {
+            /* Copy first the lookahead bytes: */
+            uInt n = s->stream.avail_in;
+            if (n > s->stream.avail_out) n = s->stream.avail_out;
+            if (n > 0) {
+                zmemcpy(s->stream.next_out, s->stream.next_in, n);
+                next_out += n;
+                s->stream.next_out = next_out;
+                s->stream.next_in   += n;
+                s->stream.avail_out -= n;
+                s->stream.avail_in  -= n;
+            }
+            if (s->stream.avail_out > 0) {
+                s->stream.avail_out -=
+                    (uInt)fread(next_out, 1, s->stream.avail_out, s->file);
+            }
+            len -= s->stream.avail_out;
+            s->in  += len;
+            s->out += len;
+            if (len == 0) s->z_eof = 1;
+            return (int)len;
+        }
+        if (s->stream.avail_in == 0 && !s->z_eof) {
+
+            errno = 0;
+            s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file);
+            if (s->stream.avail_in == 0) {
+                s->z_eof = 1;
+                if (ferror(s->file)) {
+                    s->z_err = Z_ERRNO;
+                    break;
+                }
+            }
+            s->stream.next_in = s->inbuf;
+        }
+        s->in += s->stream.avail_in;
+        s->out += s->stream.avail_out;
+        s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+        s->in -= s->stream.avail_in;
+        s->out -= s->stream.avail_out;
+
+        if (s->z_err == Z_STREAM_END) {
+            /* Check CRC and original size */
+            s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+            start = s->stream.next_out;
+
+            if (getLong(s) != s->crc) {
+                s->z_err = Z_DATA_ERROR;
+            } else {
+                (void)getLong(s);
+                /* The uncompressed length returned by above getlong() may be
+                 * different from s->out in case of concatenated .gz files.
+                 * Check for such files:
+                 */
+                check_header(s);
+                if (s->z_err == Z_OK) {
+                    inflateReset(&(s->stream));
+                    s->crc = crc32(0L, Z_NULL, 0);
+                }
+            }
+        }
+        if (s->z_err != Z_OK || s->z_eof) break;
+    }
+    s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+    if (len == s->stream.avail_out &&
+        (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO))
+        return -1;
+    return (int)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+int ZEXPORT gzgetc(file)
+    gzFile file;
+{
+    unsigned char c;
+
+    return gzread(file, &c, 1) == 1 ? c : -1;
+}
+
+
+/* ===========================================================================
+      Push one byte back onto the stream.
+*/
+int ZEXPORT gzungetc(c, file)
+    int c;
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF;
+    s->back = c;
+    s->out--;
+    s->last = (s->z_err == Z_STREAM_END);
+    if (s->last) s->z_err = Z_OK;
+    s->z_eof = 0;
+    return c;
+}
+
+
+/* ===========================================================================
+      Reads bytes from the compressed file until len-1 characters are
+   read, or a newline character is read and transferred to buf, or an
+   end-of-file condition is encountered.  The string is then terminated
+   with a null character.
+      gzgets returns buf, or Z_NULL in case of error.
+
+      The current implementation is not optimized at all.
+*/
+char * ZEXPORT gzgets(file, buf, len)
+    gzFile file;
+    char *buf;
+    int len;
+{
+    char *b = buf;
+    if (buf == Z_NULL || len <= 0) return Z_NULL;
+
+    while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ;
+    *buf = '\0';
+    return b == buf && len > 0 ? Z_NULL : b;
+}
+
+
+#ifndef NO_GZCOMPRESS
+/* ===========================================================================
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of bytes actually written (0 in case of error).
+*/
+int ZEXPORT gzwrite (file, buf, len)
+    gzFile file;
+    voidpc buf;
+    unsigned len;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    s->stream.next_in = (Bytef*)buf;
+    s->stream.avail_in = len;
+
+    while (s->stream.avail_in != 0) {
+
+        if (s->stream.avail_out == 0) {
+
+            s->stream.next_out = s->outbuf;
+            if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) {
+                s->z_err = Z_ERRNO;
+                break;
+            }
+            s->stream.avail_out = Z_BUFSIZE;
+        }
+        s->in += s->stream.avail_in;
+        s->out += s->stream.avail_out;
+        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+        s->in -= s->stream.avail_in;
+        s->out -= s->stream.avail_out;
+        if (s->z_err != Z_OK) break;
+    }
+    s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+    return (int)(len - s->stream.avail_in);
+}
+
+
+/* ===========================================================================
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).
+*/
+#ifdef STDC
+#include <stdarg.h>
+
+int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...)
+{
+    char buf[Z_PRINTF_BUFSIZE];
+    va_list va;
+    int len;
+
+    buf[sizeof(buf) - 1] = 0;
+    va_start(va, format);
+#ifdef NO_vsnprintf
+#  ifdef HAS_vsprintf_void
+    (void)vsprintf(buf, format, va);
+    va_end(va);
+    for (len = 0; len < sizeof(buf); len++)
+        if (buf[len] == 0) break;
+#  else
+    len = vsprintf(buf, format, va);
+    va_end(va);
+#  endif
+#else
+#  ifdef HAS_vsnprintf_void
+    (void)vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+    len = strlen(buf);
+#  else
+    len = vsnprintf(buf, sizeof(buf), format, va);
+    va_end(va);
+#  endif
+#endif
+    if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+        return 0;
+    return gzwrite(file, buf, (unsigned)len);
+}
+#else /* not ANSI C */
+
+int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+                       a11, a12, a13, a14, a15, a16, a17, a18, a19, a20)
+    gzFile file;
+    const char *format;
+    int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+        a11, a12, a13, a14, a15, a16, a17, a18, a19, a20;
+{
+    char buf[Z_PRINTF_BUFSIZE];
+    int len;
+
+    buf[sizeof(buf) - 1] = 0;
+#ifdef NO_snprintf
+#  ifdef HAS_sprintf_void
+    sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+            a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    for (len = 0; len < sizeof(buf); len++)
+        if (buf[len] == 0) break;
+#  else
+    len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8,
+                a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#else
+#  ifdef HAS_snprintf_void
+    snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+             a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+    len = strlen(buf);
+#  else
+    len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8,
+                 a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20);
+#  endif
+#endif
+    if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0)
+        return 0;
+    return gzwrite(file, buf, len);
+}
+#endif
+
+/* ===========================================================================
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+int ZEXPORT gzputc(file, c)
+    gzFile file;
+    int c;
+{
+    unsigned char cc = (unsigned char) c; /* required for big endian systems */
+
+    return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1;
+}
+
+
+/* ===========================================================================
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+int ZEXPORT gzputs(file, s)
+    gzFile file;
+    const char *s;
+{
+    return gzwrite(file, (char*)s, (unsigned)strlen(s));
+}
+
+
+/* ===========================================================================
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function.
+*/
+local int do_flush (file, flush)
+    gzFile file;
+    int flush;
+{
+    uInt len;
+    int done = 0;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+    s->stream.avail_in = 0; /* should be zero already anyway */
+
+    for (;;) {
+        len = Z_BUFSIZE - s->stream.avail_out;
+
+        if (len != 0) {
+            if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) {
+                s->z_err = Z_ERRNO;
+                return Z_ERRNO;
+            }
+            s->stream.next_out = s->outbuf;
+            s->stream.avail_out = Z_BUFSIZE;
+        }
+        if (done) break;
+        s->out += s->stream.avail_out;
+        s->z_err = deflate(&(s->stream), flush);
+        s->out -= s->stream.avail_out;
+
+        /* Ignore the second of two consecutive flushes: */
+        if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+        /* deflate has finished flushing only when it hasn't used up
+         * all the available space in the output buffer:
+         */
+        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+    }
+    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT gzflush (file, flush)
+     gzFile file;
+     int flush;
+{
+    gz_stream *s = (gz_stream*)file;
+    int err = do_flush (file, flush);
+
+    if (err) return err;
+    fflush(s->file);
+    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+#endif /* NO_GZCOMPRESS */
+
+/* ===========================================================================
+      Sets the starting position for the next gzread or gzwrite on the given
+   compressed file. The offset represents a number of bytes in the
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error.
+      SEEK_END is not implemented, returns error.
+      In this version of the library, gzseek can be extremely slow.
+*/
+z_off_t ZEXPORT gzseek (file, offset, whence)
+    gzFile file;
+    z_off_t offset;
+    int whence;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || whence == SEEK_END ||
+        s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+        return -1L;
+    }
+
+    if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+        return -1L;
+#else
+        if (whence == SEEK_SET) {
+            offset -= s->in;
+        }
+        if (offset < 0) return -1L;
+
+        /* At this point, offset is the number of zero bytes to write. */
+        if (s->inbuf == Z_NULL) {
+            s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */
+            if (s->inbuf == Z_NULL) return -1L;
+            zmemzero(s->inbuf, Z_BUFSIZE);
+        }
+        while (offset > 0)  {
+            uInt size = Z_BUFSIZE;
+            if (offset < Z_BUFSIZE) size = (uInt)offset;
+
+            size = gzwrite(file, s->inbuf, size);
+            if (size == 0) return -1L;
+
+            offset -= size;
+        }
+        return s->in;
+#endif
+    }
+    /* Rest of function is for reading only */
+
+    /* compute absolute position */
+    if (whence == SEEK_CUR) {
+        offset += s->out;
+    }
+    if (offset < 0) return -1L;
+
+    if (s->transparent) {
+        /* map to fseek */
+        s->back = EOF;
+        s->stream.avail_in = 0;
+        s->stream.next_in = s->inbuf;
+        if (fseek(s->file, offset, SEEK_SET) < 0) return -1L;
+
+        s->in = s->out = offset;
+        return offset;
+    }
+
+    /* For a negative seek, rewind and use positive seek */
+    if (offset >= s->out) {
+        offset -= s->out;
+    } else if (gzrewind(file) < 0) {
+        return -1L;
+    }
+    /* offset is now the number of bytes to skip. */
+
+    if (offset != 0 && s->outbuf == Z_NULL) {
+        s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
+        if (s->outbuf == Z_NULL) return -1L;
+    }
+    if (offset && s->back != EOF) {
+        s->back = EOF;
+        s->out++;
+        offset--;
+        if (s->last) s->z_err = Z_STREAM_END;
+    }
+    while (offset > 0)  {
+        int size = Z_BUFSIZE;
+        if (offset < Z_BUFSIZE) size = (int)offset;
+
+        size = gzread(file, s->outbuf, (uInt)size);
+        if (size <= 0) return -1L;
+        offset -= size;
+    }
+    return s->out;
+}
+
+/* ===========================================================================
+     Rewinds input file.
+*/
+int ZEXPORT gzrewind (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'r') return -1;
+
+    s->z_err = Z_OK;
+    s->z_eof = 0;
+    s->back = EOF;
+    s->stream.avail_in = 0;
+    s->stream.next_in = s->inbuf;
+    s->crc = crc32(0L, Z_NULL, 0);
+    if (!s->transparent) (void)inflateReset(&s->stream);
+    s->in = 0;
+    s->out = 0;
+    return fseek(s->file, s->start, SEEK_SET);
+}
+
+/* ===========================================================================
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+*/
+z_off_t ZEXPORT gztell (file)
+    gzFile file;
+{
+    return gzseek(file, 0L, SEEK_CUR);
+}
+
+/* ===========================================================================
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+int ZEXPORT gzeof (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    /* With concatenated compressed files that can have embedded
+     * crc trailers, z_eof is no longer the only/best indicator of EOF
+     * on a gz_stream. Handle end-of-stream error explicitly here.
+     */
+    if (s == NULL || s->mode != 'r') return 0;
+    if (s->z_eof) return 1;
+    return s->z_err == Z_STREAM_END;
+}
+
+/* ===========================================================================
+     Returns 1 if reading and doing so transparently, otherwise zero.
+*/
+int ZEXPORT gzdirect (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL || s->mode != 'r') return 0;
+    return s->transparent;
+}
+
+/* ===========================================================================
+   Outputs a long in LSB order to the given file
+*/
+local void putLong (file, x)
+    FILE *file;
+    uLong x;
+{
+    int n;
+    for (n = 0; n < 4; n++) {
+        fputc((int)(x & 0xff), file);
+        x >>= 8;
+    }
+}
+
+/* ===========================================================================
+   Reads a long in LSB order from the given gz_stream. Sets z_err in case
+   of error.
+*/
+local uLong getLong (s)
+    gz_stream *s;
+{
+    uLong x = (uLong)get_byte(s);
+    int c;
+
+    x += ((uLong)get_byte(s))<<8;
+    x += ((uLong)get_byte(s))<<16;
+    c = get_byte(s);
+    if (c == EOF) s->z_err = Z_DATA_ERROR;
+    x += ((uLong)c)<<24;
+    return x;
+}
+
+/* ===========================================================================
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state.
+*/
+int ZEXPORT gzclose (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) return Z_STREAM_ERROR;
+
+    if (s->mode == 'w') {
+#ifdef NO_GZCOMPRESS
+        return Z_STREAM_ERROR;
+#else
+        if (do_flush (file, Z_FINISH) != Z_OK)
+            return destroy((gz_stream*)file);
+
+        putLong (s->file, s->crc);
+        putLong (s->file, (uLong)(s->in & 0xffffffff));
+#endif
+    }
+    return destroy((gz_stream*)file);
+}
+
+#ifdef STDC
+#  define zstrerror(errnum) strerror(errnum)
+#else
+#  define zstrerror(errnum) ""
+#endif
+
+/* ===========================================================================
+     Returns the error message for the last error which occurred on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occurred in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+const char * ZEXPORT gzerror (file, errnum)
+    gzFile file;
+    int *errnum;
+{
+    char *m;
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) {
+        *errnum = Z_STREAM_ERROR;
+        return (const char*)ERR_MSG(Z_STREAM_ERROR);
+    }
+    *errnum = s->z_err;
+    if (*errnum == Z_OK) return (const char*)"";
+
+    m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg);
+
+    if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err);
+
+    TRYFREE(s->msg);
+    s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3);
+    if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR);
+    strcpy(s->msg, s->path);
+    strcat(s->msg, ": ");
+    strcat(s->msg, m);
+    return (const char*)s->msg;
+}
+
+/* ===========================================================================
+     Clear the error and end-of-file flags, and do the same for the real file.
+*/
+void ZEXPORT gzclearerr (file)
+    gzFile file;
+{
+    gz_stream *s = (gz_stream*)file;
+
+    if (s == NULL) return;
+    if (s->z_err != Z_STREAM_END) s->z_err = Z_OK;
+    s->z_eof = 0;
+    clearerr(s->file);
+}
diff --git a/distrib/zlib-1.2.3/infback.c b/distrib/zlib-1.2.3/infback.c
new file mode 100644
index 0000000..455dbc9
--- /dev/null
+++ b/distrib/zlib-1.2.3/infback.c
@@ -0,0 +1,623 @@
+/* infback.c -- inflate using a call-back interface
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+   This code is largely copied from inflate.c.  Normally either infback.o or
+   inflate.o would be linked into an application--not both.  The interface
+   with inffast.c is retained so that optimized assembler-coded versions of
+   inflate_fast() can be used with either inflate.c or infback.c.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+
+/*
+   strm provides memory allocation functions in zalloc and zfree, or
+   Z_NULL to use the library memory allocation functions.
+
+   windowBits is in the range 8..15, and window is a user-supplied
+   window and output buffer that is 2**windowBits bytes.
+ */
+int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size)
+z_streamp strm;
+int windowBits;
+unsigned char FAR *window;
+const char *version;
+int stream_size;
+{
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL || window == Z_NULL ||
+        windowBits < 8 || windowBits > 15)
+        return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+    state = (struct inflate_state FAR *)ZALLOC(strm, 1,
+                                               sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (struct internal_state FAR *)state;
+    state->dmax = 32768U;
+    state->wbits = windowBits;
+    state->wsize = 1U << windowBits;
+    state->window = window;
+    state->write = 0;
+    state->whave = 0;
+    return Z_OK;
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+/* Macros for inflateBack(): */
+
+/* Load returned state from inflate_fast() */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Set state from registers for inflate_fast() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Assure that some input is available.  If input is requested, but denied,
+   then return a Z_BUF_ERROR from inflateBack(). */
+#define PULL() \
+    do { \
+        if (have == 0) { \
+            have = in(in_desc, &next); \
+            if (have == 0) { \
+                next = Z_NULL; \
+                ret = Z_BUF_ERROR; \
+                goto inf_leave; \
+            } \
+        } \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflateBack()
+   with an error if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        PULL(); \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflateBack() with
+   an error. */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/* Assure that some output space is available, by writing out the window
+   if it's full.  If the write fails, return from inflateBack() with a
+   Z_BUF_ERROR. */
+#define ROOM() \
+    do { \
+        if (left == 0) { \
+            put = state->window; \
+            left = state->wsize; \
+            state->whave = left; \
+            if (out(out_desc, put, left)) { \
+                ret = Z_BUF_ERROR; \
+                goto inf_leave; \
+            } \
+        } \
+    } while (0)
+
+/*
+   strm provides the memory allocation functions and window buffer on input,
+   and provides information on the unused input on return.  For Z_DATA_ERROR
+   returns, strm will also provide an error message.
+
+   in() and out() are the call-back input and output functions.  When
+   inflateBack() needs more input, it calls in().  When inflateBack() has
+   filled the window with output, or when it completes with data in the
+   window, it calls out() to write out the data.  The application must not
+   change the provided input until in() is called again or inflateBack()
+   returns.  The application must not change the window/output buffer until
+   inflateBack() returns.
+
+   in() and out() are called with a descriptor parameter provided in the
+   inflateBack() call.  This parameter can be a structure that provides the
+   information required to do the read or write, as well as accumulated
+   information on the input and output such as totals and check values.
+
+   in() should return zero on failure.  out() should return non-zero on
+   failure.  If either in() or out() fails, than inflateBack() returns a
+   Z_BUF_ERROR.  strm->next_in can be checked for Z_NULL to see whether it
+   was in() or out() that caused in the error.  Otherwise,  inflateBack()
+   returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format
+   error, or Z_MEM_ERROR if it could not allocate memory for the state.
+   inflateBack() can also return Z_STREAM_ERROR if the input parameters
+   are not correct, i.e. strm is Z_NULL or the state was not initialized.
+ */
+int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc)
+z_streamp strm;
+in_func in;
+void FAR *in_desc;
+out_func out;
+void FAR *out_desc;
+{
+    struct inflate_state FAR *state;
+    unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code this;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    /* Check that the strm exists and that the state was initialized */
+    if (strm == Z_NULL || strm->state == Z_NULL)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* Reset the state */
+    strm->msg = Z_NULL;
+    state->mode = TYPE;
+    state->last = 0;
+    state->whave = 0;
+    next = strm->next_in;
+    have = next != Z_NULL ? strm->avail_in : 0;
+    hold = 0;
+    bits = 0;
+    put = state->window;
+    left = state->wsize;
+
+    /* Inflate until end of block marked as last */
+    for (;;)
+        switch (state->mode) {
+        case TYPE:
+            /* determine and dispatch block type */
+            if (state->last) {
+                BYTEBITS();
+                state->mode = DONE;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN;              /* decode codes */
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+
+        case STORED:
+            /* get and verify stored block length */
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+
+            /* copy stored block from input to output */
+            while (state->length != 0) {
+                copy = state->length;
+                PULL();
+                ROOM();
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+
+        case TABLE:
+            /* get dynamic table entries descriptor */
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+
+            /* get code length code lengths (not a typo) */
+            state->have = 0;
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+
+            /* get length and distance code code lengths */
+            state->have = 0;
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    this = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (this.val < 16) {
+                    NEEDBITS(this.bits);
+                    DROPBITS(this.bits);
+                    state->lens[state->have++] = this.val;
+                }
+                else {
+                    if (this.val == 16) {
+                        NEEDBITS(this.bits + 2);
+                        DROPBITS(this.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = (unsigned)(state->lens[state->have - 1]);
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (this.val == 17) {
+                        NEEDBITS(this.bits + 3);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(this.bits + 7);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* handle error breaks in while */
+            if (state->mode == BAD) break;
+
+            /* build code tables */
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (code const FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN;
+
+        case LEN:
+            /* use inflate_fast() if we have enough input and output */
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                if (state->whave < state->wsize)
+                    state->whave = state->wsize - left;
+                inflate_fast(strm, state->wsize);
+                LOAD();
+                break;
+            }
+
+            /* get a literal, length, or end-of-block code */
+            for (;;) {
+                this = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (this.op && (this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            state->length = (unsigned)this.val;
+
+            /* process literal */
+            if (this.op == 0) {
+                Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", this.val));
+                ROOM();
+                *put++ = (unsigned char)(state->length);
+                left--;
+                state->mode = LEN;
+                break;
+            }
+
+            /* process end of block */
+            if (this.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->mode = TYPE;
+                break;
+            }
+
+            /* invalid code */
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+
+            /* length code -- get extra bits, if any */
+            state->extra = (unsigned)(this.op) & 15;
+            if (state->extra != 0) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+
+            /* get distance code */
+            for (;;) {
+                this = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)this.val;
+
+            /* get distance extra bits, if any */
+            state->extra = (unsigned)(this.op) & 15;
+            if (state->extra != 0) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            if (state->offset > state->wsize - (state->whave < state->wsize ?
+                                                left : 0)) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+
+            /* copy match from window to output */
+            do {
+                ROOM();
+                copy = state->wsize - state->offset;
+                if (copy < left) {
+                    from = put + copy;
+                    copy = left - copy;
+                }
+                else {
+                    from = put - state->offset;
+                    copy = left;
+                }
+                if (copy > state->length) copy = state->length;
+                state->length -= copy;
+                left -= copy;
+                do {
+                    *put++ = *from++;
+                } while (--copy);
+            } while (state->length != 0);
+            break;
+
+        case DONE:
+            /* inflate stream terminated properly -- write leftover output */
+            ret = Z_STREAM_END;
+            if (left < state->wsize) {
+                if (out(out_desc, state->window, state->wsize - left))
+                    ret = Z_BUF_ERROR;
+            }
+            goto inf_leave;
+
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+
+        default:                /* can't happen, but makes compilers happy */
+            ret = Z_STREAM_ERROR;
+            goto inf_leave;
+        }
+
+    /* Return unused input */
+  inf_leave:
+    strm->next_in = next;
+    strm->avail_in = have;
+    return ret;
+}
+
+int ZEXPORT inflateBackEnd(strm)
+z_streamp strm;
+{
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
diff --git a/distrib/zlib-1.2.3/inffast.c b/distrib/zlib-1.2.3/inffast.c
new file mode 100644
index 0000000..bbee92e
--- /dev/null
+++ b/distrib/zlib-1.2.3/inffast.c
@@ -0,0 +1,318 @@
+/* inffast.c -- fast decoding
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifndef ASMINF
+
+/* Allow machine dependent optimization for post-increment or pre-increment.
+   Based on testing to date,
+   Pre-increment preferred for:
+   - PowerPC G3 (Adler)
+   - MIPS R5000 (Randers-Pehrson)
+   Post-increment preferred for:
+   - none
+   No measurable difference:
+   - Pentium III (Anderson)
+   - M68060 (Nikl)
+ */
+#ifdef POSTINC
+#  define OFF 0
+#  define PUP(a) *(a)++
+#else
+#  define OFF 1
+#  define PUP(a) *++(a)
+#endif
+
+/*
+   Decode literal, length, and distance codes and write out the resulting
+   literal and match bytes until either not enough input or output is
+   available, an end-of-block is encountered, or a data error is encountered.
+   When large enough input and output buffers are supplied to inflate(), for
+   example, a 16K input buffer and a 64K output buffer, more than 95% of the
+   inflate execution time is spent in this routine.
+
+   Entry assumptions:
+
+        state->mode == LEN
+        strm->avail_in >= 6
+        strm->avail_out >= 258
+        start >= strm->avail_out
+        state->bits < 8
+
+   On return, state->mode is one of:
+
+        LEN -- ran out of enough output space or enough available input
+        TYPE -- reached end of block code, inflate() to interpret next block
+        BAD -- error in block data
+
+   Notes:
+
+    - The maximum input bits used by a length/distance pair is 15 bits for the
+      length code, 5 bits for the length extra, 15 bits for the distance code,
+      and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
+      Therefore if strm->avail_in >= 6, then there is enough input to avoid
+      checking for available input while decoding.
+
+    - The maximum bytes that a single length/distance pair can output is 258
+      bytes, which is the maximum length that can be coded.  inflate_fast()
+      requires strm->avail_out >= 258 for each loop to avoid checking for
+      output space.
+ */
+void inflate_fast(strm, start)
+z_streamp strm;
+unsigned start;         /* inflate()'s starting value for strm->avail_out */
+{
+    struct inflate_state FAR *state;
+    unsigned char FAR *in;      /* local strm->next_in */
+    unsigned char FAR *last;    /* while in < last, enough input available */
+    unsigned char FAR *out;     /* local strm->next_out */
+    unsigned char FAR *beg;     /* inflate()'s initial strm->next_out */
+    unsigned char FAR *end;     /* while out < end, enough space available */
+#ifdef INFLATE_STRICT
+    unsigned dmax;              /* maximum distance from zlib header */
+#endif
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned write;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if wsize != 0 */
+    unsigned long hold;         /* local strm->hold */
+    unsigned bits;              /* local strm->bits */
+    code const FAR *lcode;      /* local strm->lencode */
+    code const FAR *dcode;      /* local strm->distcode */
+    unsigned lmask;             /* mask for first level of length codes */
+    unsigned dmask;             /* mask for first level of distance codes */
+    code this;                  /* retrieved table entry */
+    unsigned op;                /* code bits, operation, extra bits, or */
+                                /*  window position, window bytes to copy */
+    unsigned len;               /* match length, unused bytes */
+    unsigned dist;              /* match distance */
+    unsigned char FAR *from;    /* where to copy match from */
+
+    /* copy state to local variables */
+    state = (struct inflate_state FAR *)strm->state;
+    in = strm->next_in - OFF;
+    last = in + (strm->avail_in - 5);
+    out = strm->next_out - OFF;
+    beg = out - (start - strm->avail_out);
+    end = out + (strm->avail_out - 257);
+#ifdef INFLATE_STRICT
+    dmax = state->dmax;
+#endif
+    wsize = state->wsize;
+    whave = state->whave;
+    write = state->write;
+    window = state->window;
+    hold = state->hold;
+    bits = state->bits;
+    lcode = state->lencode;
+    dcode = state->distcode;
+    lmask = (1U << state->lenbits) - 1;
+    dmask = (1U << state->distbits) - 1;
+
+    /* decode literals and length/distances until end-of-block or not enough
+       input data or output space */
+    do {
+        if (bits < 15) {
+            hold += (unsigned long)(PUP(in)) << bits;
+            bits += 8;
+            hold += (unsigned long)(PUP(in)) << bits;
+            bits += 8;
+        }
+        this = lcode[hold & lmask];
+      dolen:
+        op = (unsigned)(this.bits);
+        hold >>= op;
+        bits -= op;
+        op = (unsigned)(this.op);
+        if (op == 0) {                          /* literal */
+            Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+                    "inflate:         literal '%c'\n" :
+                    "inflate:         literal 0x%02x\n", this.val));
+            PUP(out) = (unsigned char)(this.val);
+        }
+        else if (op & 16) {                     /* length base */
+            len = (unsigned)(this.val);
+            op &= 15;                           /* number of extra bits */
+            if (op) {
+                if (bits < op) {
+                    hold += (unsigned long)(PUP(in)) << bits;
+                    bits += 8;
+                }
+                len += (unsigned)hold & ((1U << op) - 1);
+                hold >>= op;
+                bits -= op;
+            }
+            Tracevv((stderr, "inflate:         length %u\n", len));
+            if (bits < 15) {
+                hold += (unsigned long)(PUP(in)) << bits;
+                bits += 8;
+                hold += (unsigned long)(PUP(in)) << bits;
+                bits += 8;
+            }
+            this = dcode[hold & dmask];
+          dodist:
+            op = (unsigned)(this.bits);
+            hold >>= op;
+            bits -= op;
+            op = (unsigned)(this.op);
+            if (op & 16) {                      /* distance base */
+                dist = (unsigned)(this.val);
+                op &= 15;                       /* number of extra bits */
+                if (bits < op) {
+                    hold += (unsigned long)(PUP(in)) << bits;
+                    bits += 8;
+                    if (bits < op) {
+                        hold += (unsigned long)(PUP(in)) << bits;
+                        bits += 8;
+                    }
+                }
+                dist += (unsigned)hold & ((1U << op) - 1);
+#ifdef INFLATE_STRICT
+                if (dist > dmax) {
+                    strm->msg = (char *)"invalid distance too far back";
+                    state->mode = BAD;
+                    break;
+                }
+#endif
+                hold >>= op;
+                bits -= op;
+                Tracevv((stderr, "inflate:         distance %u\n", dist));
+                op = (unsigned)(out - beg);     /* max distance in output */
+                if (dist > op) {                /* see if copy from window */
+                    op = dist - op;             /* distance back in window */
+                    if (op > whave) {
+                        strm->msg = (char *)"invalid distance too far back";
+                        state->mode = BAD;
+                        break;
+                    }
+                    from = window - OFF;
+                    if (write == 0) {           /* very common case */
+                        from += wsize - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    else if (write < op) {      /* wrap around window */
+                        from += wsize + write - op;
+                        op -= write;
+                        if (op < len) {         /* some from end of window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = window - OFF;
+                            if (write < len) {  /* some from start of window */
+                                op = write;
+                                len -= op;
+                                do {
+                                    PUP(out) = PUP(from);
+                                } while (--op);
+                                from = out - dist;      /* rest from output */
+                            }
+                        }
+                    }
+                    else {                      /* contiguous in window */
+                        from += write - op;
+                        if (op < len) {         /* some from window */
+                            len -= op;
+                            do {
+                                PUP(out) = PUP(from);
+                            } while (--op);
+                            from = out - dist;  /* rest from output */
+                        }
+                    }
+                    while (len > 2) {
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        len -= 3;
+                    }
+                    if (len) {
+                        PUP(out) = PUP(from);
+                        if (len > 1)
+                            PUP(out) = PUP(from);
+                    }
+                }
+                else {
+                    from = out - dist;          /* copy direct from output */
+                    do {                        /* minimum length is three */
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        PUP(out) = PUP(from);
+                        len -= 3;
+                    } while (len > 2);
+                    if (len) {
+                        PUP(out) = PUP(from);
+                        if (len > 1)
+                            PUP(out) = PUP(from);
+                    }
+                }
+            }
+            else if ((op & 64) == 0) {          /* 2nd level distance code */
+                this = dcode[this.val + (hold & ((1U << op) - 1))];
+                goto dodist;
+            }
+            else {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+        }
+        else if ((op & 64) == 0) {              /* 2nd level length code */
+            this = lcode[this.val + (hold & ((1U << op) - 1))];
+            goto dolen;
+        }
+        else if (op & 32) {                     /* end-of-block */
+            Tracevv((stderr, "inflate:         end of block\n"));
+            state->mode = TYPE;
+            break;
+        }
+        else {
+            strm->msg = (char *)"invalid literal/length code";
+            state->mode = BAD;
+            break;
+        }
+    } while (in < last && out < end);
+
+    /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+    len = bits >> 3;
+    in -= len;
+    bits -= len << 3;
+    hold &= (1U << bits) - 1;
+
+    /* update state and return */
+    strm->next_in = in + OFF;
+    strm->next_out = out + OFF;
+    strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last));
+    strm->avail_out = (unsigned)(out < end ?
+                                 257 + (end - out) : 257 - (out - end));
+    state->hold = hold;
+    state->bits = bits;
+    return;
+}
+
+/*
+   inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe):
+   - Using bit fields for code structure
+   - Different op definition to avoid & for extra bits (do & for table bits)
+   - Three separate decoding do-loops for direct, window, and write == 0
+   - Special case for distance > 1 copies to do overlapped load and store copy
+   - Explicit branch predictions (based on measured branch probabilities)
+   - Deferring match copy and interspersed it with decoding subsequent codes
+   - Swapping literal/length else
+   - Swapping window/direct else
+   - Larger unrolled copy loops (three is about right)
+   - Moving len -= 3 statement into middle of loop
+ */
+
+#endif /* !ASMINF */
diff --git a/distrib/zlib-1.2.3/inffast.h b/distrib/zlib-1.2.3/inffast.h
new file mode 100644
index 0000000..1e88d2d
--- /dev/null
+++ b/distrib/zlib-1.2.3/inffast.h
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+void inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/distrib/zlib-1.2.3/inffixed.h b/distrib/zlib-1.2.3/inffixed.h
new file mode 100644
index 0000000..75ed4b5
--- /dev/null
+++ b/distrib/zlib-1.2.3/inffixed.h
@@ -0,0 +1,94 @@
+    /* inffixed.h -- table for decoding fixed codes
+     * Generated automatically by makefixed().
+     */
+
+    /* WARNING: this file should *not* be used by applications. It
+       is part of the implementation of the compression library and
+       is subject to change. Applications should only use zlib.h.
+     */
+
+    static const code lenfix[512] = {
+        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+        {0,9,255}
+    };
+
+    static const code distfix[32] = {
+        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+        {22,5,193},{64,5,0}
+    };
diff --git a/distrib/zlib-1.2.3/inflate.c b/distrib/zlib-1.2.3/inflate.c
new file mode 100644
index 0000000..792fdee
--- /dev/null
+++ b/distrib/zlib-1.2.3/inflate.c
@@ -0,0 +1,1368 @@
+/* inflate.c -- zlib decompression
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ * Change history:
+ *
+ * 1.2.beta0    24 Nov 2002
+ * - First version -- complete rewrite of inflate to simplify code, avoid
+ *   creation of window when not needed, minimize use of window when it is
+ *   needed, make inffast.c even faster, implement gzip decoding, and to
+ *   improve code readability and style over the previous zlib inflate code
+ *
+ * 1.2.beta1    25 Nov 2002
+ * - Use pointers for available input and output checking in inffast.c
+ * - Remove input and output counters in inffast.c
+ * - Change inffast.c entry and loop from avail_in >= 7 to >= 6
+ * - Remove unnecessary second byte pull from length extra in inffast.c
+ * - Unroll direct copy to three copies per loop in inffast.c
+ *
+ * 1.2.beta2    4 Dec 2002
+ * - Change external routine names to reduce potential conflicts
+ * - Correct filename to inffixed.h for fixed tables in inflate.c
+ * - Make hbuf[] unsigned char to match parameter type in inflate.c
+ * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset)
+ *   to avoid negation problem on Alphas (64 bit) in inflate.c
+ *
+ * 1.2.beta3    22 Dec 2002
+ * - Add comments on state->bits assertion in inffast.c
+ * - Add comments on op field in inftrees.h
+ * - Fix bug in reuse of allocated window after inflateReset()
+ * - Remove bit fields--back to byte structure for speed
+ * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths
+ * - Change post-increments to pre-increments in inflate_fast(), PPC biased?
+ * - Add compile time option, POSTINC, to use post-increments instead (Intel?)
+ * - Make MATCH copy in inflate() much faster for when inflate_fast() not used
+ * - Use local copies of stream next and avail values, as well as local bit
+ *   buffer and bit count in inflate()--for speed when inflate_fast() not used
+ *
+ * 1.2.beta4    1 Jan 2003
+ * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings
+ * - Move a comment on output buffer sizes from inffast.c to inflate.c
+ * - Add comments in inffast.c to introduce the inflate_fast() routine
+ * - Rearrange window copies in inflate_fast() for speed and simplification
+ * - Unroll last copy for window match in inflate_fast()
+ * - Use local copies of window variables in inflate_fast() for speed
+ * - Pull out common write == 0 case for speed in inflate_fast()
+ * - Make op and len in inflate_fast() unsigned for consistency
+ * - Add FAR to lcode and dcode declarations in inflate_fast()
+ * - Simplified bad distance check in inflate_fast()
+ * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new
+ *   source file infback.c to provide a call-back interface to inflate for
+ *   programs like gzip and unzip -- uses window as output buffer to avoid
+ *   window copying
+ *
+ * 1.2.beta5    1 Jan 2003
+ * - Improved inflateBack() interface to allow the caller to provide initial
+ *   input in strm.
+ * - Fixed stored blocks bug in inflateBack()
+ *
+ * 1.2.beta6    4 Jan 2003
+ * - Added comments in inffast.c on effectiveness of POSTINC
+ * - Typecasting all around to reduce compiler warnings
+ * - Changed loops from while (1) or do {} while (1) to for (;;), again to
+ *   make compilers happy
+ * - Changed type of window in inflateBackInit() to unsigned char *
+ *
+ * 1.2.beta7    27 Jan 2003
+ * - Changed many types to unsigned or unsigned short to avoid warnings
+ * - Added inflateCopy() function
+ *
+ * 1.2.0        9 Mar 2003
+ * - Changed inflateBack() interface to provide separate opaque descriptors
+ *   for the in() and out() functions
+ * - Changed inflateBack() argument and in_func typedef to swap the length
+ *   and buffer address return values for the input function
+ * - Check next_in and next_out for Z_NULL on entry to inflate()
+ *
+ * The history for versions after 1.2.0 are in ChangeLog in zlib distribution.
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+#include "inflate.h"
+#include "inffast.h"
+
+#ifdef MAKEFIXED
+#  ifndef BUILDFIXED
+#    define BUILDFIXED
+#  endif
+#endif
+
+/* function prototypes */
+local void fixedtables OF((struct inflate_state FAR *state));
+local int updatewindow OF((z_streamp strm, unsigned out));
+#ifdef BUILDFIXED
+   void makefixed OF((void));
+#endif
+local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf,
+                              unsigned len));
+
+int ZEXPORT inflateReset(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    strm->total_in = strm->total_out = state->total = 0;
+    strm->msg = Z_NULL;
+    strm->adler = 1;        /* to support ill-conceived Java test suite */
+    state->mode = HEAD;
+    state->last = 0;
+    state->havedict = 0;
+    state->dmax = 32768U;
+    state->head = Z_NULL;
+    state->wsize = 0;
+    state->whave = 0;
+    state->write = 0;
+    state->hold = 0;
+    state->bits = 0;
+    state->lencode = state->distcode = state->next = state->codes;
+    Tracev((stderr, "inflate: reset\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflatePrime(strm, bits, value)
+z_streamp strm;
+int bits;
+int value;
+{
+    struct inflate_state FAR *state;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR;
+    value &= (1L << bits) - 1;
+    state->hold += value << state->bits;
+    state->bits += bits;
+    return Z_OK;
+}
+
+int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size)
+z_streamp strm;
+int windowBits;
+const char *version;
+int stream_size;
+{
+    struct inflate_state FAR *state;
+
+    if (version == Z_NULL || version[0] != ZLIB_VERSION[0] ||
+        stream_size != (int)(sizeof(z_stream)))
+        return Z_VERSION_ERROR;
+    if (strm == Z_NULL) return Z_STREAM_ERROR;
+    strm->msg = Z_NULL;                 /* in case we return an error */
+    if (strm->zalloc == (alloc_func)0) {
+        strm->zalloc = zcalloc;
+        strm->opaque = (voidpf)0;
+    }
+    if (strm->zfree == (free_func)0) strm->zfree = zcfree;
+    state = (struct inflate_state FAR *)
+            ZALLOC(strm, 1, sizeof(struct inflate_state));
+    if (state == Z_NULL) return Z_MEM_ERROR;
+    Tracev((stderr, "inflate: allocated\n"));
+    strm->state = (struct internal_state FAR *)state;
+    if (windowBits < 0) {
+        state->wrap = 0;
+        windowBits = -windowBits;
+    }
+    else {
+        state->wrap = (windowBits >> 4) + 1;
+#ifdef GUNZIP
+        if (windowBits < 48) windowBits &= 15;
+#endif
+    }
+    if (windowBits < 8 || windowBits > 15) {
+        ZFREE(strm, state);
+        strm->state = Z_NULL;
+        return Z_STREAM_ERROR;
+    }
+    state->wbits = (unsigned)windowBits;
+    state->window = Z_NULL;
+    return inflateReset(strm);
+}
+
+int ZEXPORT inflateInit_(strm, version, stream_size)
+z_streamp strm;
+const char *version;
+int stream_size;
+{
+    return inflateInit2_(strm, DEF_WBITS, version, stream_size);
+}
+
+/*
+   Return state with length and distance decoding tables and index sizes set to
+   fixed code decoding.  Normally this returns fixed tables from inffixed.h.
+   If BUILDFIXED is defined, then instead this routine builds the tables the
+   first time it's called, and returns those tables the first time and
+   thereafter.  This reduces the size of the code by about 2K bytes, in
+   exchange for a little execution time.  However, BUILDFIXED should not be
+   used for threaded applications, since the rewriting of the tables and virgin
+   may not be thread-safe.
+ */
+local void fixedtables(state)
+struct inflate_state FAR *state;
+{
+#ifdef BUILDFIXED
+    static int virgin = 1;
+    static code *lenfix, *distfix;
+    static code fixed[544];
+
+    /* build fixed huffman tables if first call (may not be thread safe) */
+    if (virgin) {
+        unsigned sym, bits;
+        static code *next;
+
+        /* literal/length table */
+        sym = 0;
+        while (sym < 144) state->lens[sym++] = 8;
+        while (sym < 256) state->lens[sym++] = 9;
+        while (sym < 280) state->lens[sym++] = 7;
+        while (sym < 288) state->lens[sym++] = 8;
+        next = fixed;
+        lenfix = next;
+        bits = 9;
+        inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work);
+
+        /* distance table */
+        sym = 0;
+        while (sym < 32) state->lens[sym++] = 5;
+        distfix = next;
+        bits = 5;
+        inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work);
+
+        /* do this just once */
+        virgin = 0;
+    }
+#else /* !BUILDFIXED */
+#   include "inffixed.h"
+#endif /* BUILDFIXED */
+    state->lencode = lenfix;
+    state->lenbits = 9;
+    state->distcode = distfix;
+    state->distbits = 5;
+}
+
+#ifdef MAKEFIXED
+#include <stdio.h>
+
+/*
+   Write out the inffixed.h that is #include'd above.  Defining MAKEFIXED also
+   defines BUILDFIXED, so the tables are built on the fly.  makefixed() writes
+   those tables to stdout, which would be piped to inffixed.h.  A small program
+   can simply call makefixed to do this:
+
+    void makefixed(void);
+
+    int main(void)
+    {
+        makefixed();
+        return 0;
+    }
+
+   Then that can be linked with zlib built with MAKEFIXED defined and run:
+
+    a.out > inffixed.h
+ */
+void makefixed()
+{
+    unsigned low, size;
+    struct inflate_state state;
+
+    fixedtables(&state);
+    puts("    /* inffixed.h -- table for decoding fixed codes");
+    puts("     * Generated automatically by makefixed().");
+    puts("     */");
+    puts("");
+    puts("    /* WARNING: this file should *not* be used by applications.");
+    puts("       It is part of the implementation of this library and is");
+    puts("       subject to change. Applications should only use zlib.h.");
+    puts("     */");
+    puts("");
+    size = 1U << 9;
+    printf("    static const code lenfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 7) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits,
+               state.lencode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+    size = 1U << 5;
+    printf("\n    static const code distfix[%u] = {", size);
+    low = 0;
+    for (;;) {
+        if ((low % 6) == 0) printf("\n        ");
+        printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits,
+               state.distcode[low].val);
+        if (++low == size) break;
+        putchar(',');
+    }
+    puts("\n    };");
+}
+#endif /* MAKEFIXED */
+
+/*
+   Update the window with the last wsize (normally 32K) bytes written before
+   returning.  If window does not exist yet, create it.  This is only called
+   when a window is already in use, or when output has been written during this
+   inflate call, but the end of the deflate stream has not been reached yet.
+   It is also called to create a window for dictionary data when a dictionary
+   is loaded.
+
+   Providing output buffers larger than 32K to inflate() should provide a speed
+   advantage, since only the last 32K of output is copied to the sliding window
+   upon return from inflate(), and since all distances after the first 32K of
+   output will fall in the output data, making match copies simpler and faster.
+   The advantage may be dependent on the size of the processor's data caches.
+ */
+local int updatewindow(strm, out)
+z_streamp strm;
+unsigned out;
+{
+    struct inflate_state FAR *state;
+    unsigned copy, dist;
+
+    state = (struct inflate_state FAR *)strm->state;
+
+    /* if it hasn't been done already, allocate space for the window */
+    if (state->window == Z_NULL) {
+        state->window = (unsigned char FAR *)
+                        ZALLOC(strm, 1U << state->wbits,
+                               sizeof(unsigned char));
+        if (state->window == Z_NULL) return 1;
+    }
+
+    /* if window not in use yet, initialize */
+    if (state->wsize == 0) {
+        state->wsize = 1U << state->wbits;
+        state->write = 0;
+        state->whave = 0;
+    }
+
+    /* copy state->wsize or less output bytes into the circular window */
+    copy = out - strm->avail_out;
+    if (copy >= state->wsize) {
+        zmemcpy(state->window, strm->next_out - state->wsize, state->wsize);
+        state->write = 0;
+        state->whave = state->wsize;
+    }
+    else {
+        dist = state->wsize - state->write;
+        if (dist > copy) dist = copy;
+        zmemcpy(state->window + state->write, strm->next_out - copy, dist);
+        copy -= dist;
+        if (copy) {
+            zmemcpy(state->window, strm->next_out - copy, copy);
+            state->write = copy;
+            state->whave = state->wsize;
+        }
+        else {
+            state->write += dist;
+            if (state->write == state->wsize) state->write = 0;
+            if (state->whave < state->wsize) state->whave += dist;
+        }
+    }
+    return 0;
+}
+
+/* Macros for inflate(): */
+
+/* check function to use adler32() for zlib or crc32() for gzip */
+#ifdef GUNZIP
+#  define UPDATE(check, buf, len) \
+    (state->flags ? crc32(check, buf, len) : adler32(check, buf, len))
+#else
+#  define UPDATE(check, buf, len) adler32(check, buf, len)
+#endif
+
+/* check macros for header crc */
+#ifdef GUNZIP
+#  define CRC2(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        check = crc32(check, hbuf, 2); \
+    } while (0)
+
+#  define CRC4(check, word) \
+    do { \
+        hbuf[0] = (unsigned char)(word); \
+        hbuf[1] = (unsigned char)((word) >> 8); \
+        hbuf[2] = (unsigned char)((word) >> 16); \
+        hbuf[3] = (unsigned char)((word) >> 24); \
+        check = crc32(check, hbuf, 4); \
+    } while (0)
+#endif
+
+/* Load registers with state in inflate() for speed */
+#define LOAD() \
+    do { \
+        put = strm->next_out; \
+        left = strm->avail_out; \
+        next = strm->next_in; \
+        have = strm->avail_in; \
+        hold = state->hold; \
+        bits = state->bits; \
+    } while (0)
+
+/* Restore state from registers in inflate() */
+#define RESTORE() \
+    do { \
+        strm->next_out = put; \
+        strm->avail_out = left; \
+        strm->next_in = next; \
+        strm->avail_in = have; \
+        state->hold = hold; \
+        state->bits = bits; \
+    } while (0)
+
+/* Clear the input bit accumulator */
+#define INITBITS() \
+    do { \
+        hold = 0; \
+        bits = 0; \
+    } while (0)
+
+/* Get a byte of input into the bit accumulator, or return from inflate()
+   if there is no input available. */
+#define PULLBYTE() \
+    do { \
+        if (have == 0) goto inf_leave; \
+        have--; \
+        hold += (unsigned long)(*next++) << bits; \
+        bits += 8; \
+    } while (0)
+
+/* Assure that there are at least n bits in the bit accumulator.  If there is
+   not enough available input to do that, then return from inflate(). */
+#define NEEDBITS(n) \
+    do { \
+        while (bits < (unsigned)(n)) \
+            PULLBYTE(); \
+    } while (0)
+
+/* Return the low n bits of the bit accumulator (n < 16) */
+#define BITS(n) \
+    ((unsigned)hold & ((1U << (n)) - 1))
+
+/* Remove n bits from the bit accumulator */
+#define DROPBITS(n) \
+    do { \
+        hold >>= (n); \
+        bits -= (unsigned)(n); \
+    } while (0)
+
+/* Remove zero to seven bits as needed to go to a byte boundary */
+#define BYTEBITS() \
+    do { \
+        hold >>= bits & 7; \
+        bits -= bits & 7; \
+    } while (0)
+
+/* Reverse the bytes in a 32-bit value */
+#define REVERSE(q) \
+    ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+     (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+/*
+   inflate() uses a state machine to process as much input data and generate as
+   much output data as possible before returning.  The state machine is
+   structured roughly as follows:
+
+    for (;;) switch (state) {
+    ...
+    case STATEn:
+        if (not enough input data or output space to make progress)
+            return;
+        ... make progress ...
+        state = STATEm;
+        break;
+    ...
+    }
+
+   so when inflate() is called again, the same case is attempted again, and
+   if the appropriate resources are provided, the machine proceeds to the
+   next state.  The NEEDBITS() macro is usually the way the state evaluates
+   whether it can proceed or should return.  NEEDBITS() does the return if
+   the requested bits are not available.  The typical use of the BITS macros
+   is:
+
+        NEEDBITS(n);
+        ... do something with BITS(n) ...
+        DROPBITS(n);
+
+   where NEEDBITS(n) either returns from inflate() if there isn't enough
+   input left to load n bits into the accumulator, or it continues.  BITS(n)
+   gives the low n bits in the accumulator.  When done, DROPBITS(n) drops
+   the low n bits off the accumulator.  INITBITS() clears the accumulator
+   and sets the number of available bits to zero.  BYTEBITS() discards just
+   enough bits to put the accumulator on a byte boundary.  After BYTEBITS()
+   and a NEEDBITS(8), then BITS(8) would return the next byte in the stream.
+
+   NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return
+   if there is no input available.  The decoding of variable length codes uses
+   PULLBYTE() directly in order to pull just enough bytes to decode the next
+   code, and no more.
+
+   Some states loop until they get enough input, making sure that enough
+   state information is maintained to continue the loop where it left off
+   if NEEDBITS() returns in the loop.  For example, want, need, and keep
+   would all have to actually be part of the saved state in case NEEDBITS()
+   returns:
+
+    case STATEw:
+        while (want < need) {
+            NEEDBITS(n);
+            keep[want++] = BITS(n);
+            DROPBITS(n);
+        }
+        state = STATEx;
+    case STATEx:
+
+   As shown above, if the next state is also the next case, then the break
+   is omitted.
+
+   A state may also return if there is not enough output space available to
+   complete that state.  Those states are copying stored data, writing a
+   literal byte, and copying a matching string.
+
+   When returning, a "goto inf_leave" is used to update the total counters,
+   update the check value, and determine whether any progress has been made
+   during that inflate() call in order to return the proper return code.
+   Progress is defined as a change in either strm->avail_in or strm->avail_out.
+   When there is a window, goto inf_leave will update the window with the last
+   output written.  If a goto inf_leave occurs in the middle of decompression
+   and there is no window currently, goto inf_leave will create one and copy
+   output to the window for the next call of inflate().
+
+   In this implementation, the flush parameter of inflate() only affects the
+   return code (per zlib.h).  inflate() always writes as much as possible to
+   strm->next_out, given the space available and the provided input--the effect
+   documented in zlib.h of Z_SYNC_FLUSH.  Furthermore, inflate() always defers
+   the allocation of and copying into a sliding window until necessary, which
+   provides the effect documented in zlib.h for Z_FINISH when the entire input
+   stream available.  So the only thing the flush parameter actually does is:
+   when flush is set to Z_FINISH, inflate() cannot return Z_OK.  Instead it
+   will return Z_BUF_ERROR if it has not reached the end of the stream.
+ */
+
+int ZEXPORT inflate(strm, flush)
+z_streamp strm;
+int flush;
+{
+    struct inflate_state FAR *state;
+    unsigned char FAR *next;    /* next input */
+    unsigned char FAR *put;     /* next output */
+    unsigned have, left;        /* available input and output */
+    unsigned long hold;         /* bit buffer */
+    unsigned bits;              /* bits in bit buffer */
+    unsigned in, out;           /* save starting available input and output */
+    unsigned copy;              /* number of stored or match bytes to copy */
+    unsigned char FAR *from;    /* where to copy match bytes from */
+    code this;                  /* current decoding table entry */
+    code last;                  /* parent table entry */
+    unsigned len;               /* length to copy for repeats, bits to drop */
+    int ret;                    /* return code */
+#ifdef GUNZIP
+    unsigned char hbuf[4];      /* buffer for gzip header crc calculation */
+#endif
+    static const unsigned short order[19] = /* permutation of code lengths */
+        {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL ||
+        (strm->next_in == Z_NULL && strm->avail_in != 0))
+        return Z_STREAM_ERROR;
+
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->mode == TYPE) state->mode = TYPEDO;      /* skip check */
+    LOAD();
+    in = have;
+    out = left;
+    ret = Z_OK;
+    for (;;)
+        switch (state->mode) {
+        case HEAD:
+            if (state->wrap == 0) {
+                state->mode = TYPEDO;
+                break;
+            }
+            NEEDBITS(16);
+#ifdef GUNZIP
+            if ((state->wrap & 2) && hold == 0x8b1f) {  /* gzip header */
+                state->check = crc32(0L, Z_NULL, 0);
+                CRC2(state->check, hold);
+                INITBITS();
+                state->mode = FLAGS;
+                break;
+            }
+            state->flags = 0;           /* expect zlib header */
+            if (state->head != Z_NULL)
+                state->head->done = -1;
+            if (!(state->wrap & 1) ||   /* check if zlib header allowed */
+#else
+            if (
+#endif
+                ((BITS(8) << 8) + (hold >> 8)) % 31) {
+                strm->msg = (char *)"incorrect header check";
+                state->mode = BAD;
+                break;
+            }
+            if (BITS(4) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            DROPBITS(4);
+            len = BITS(4) + 8;
+            if (len > state->wbits) {
+                strm->msg = (char *)"invalid window size";
+                state->mode = BAD;
+                break;
+            }
+            state->dmax = 1U << len;
+            Tracev((stderr, "inflate:   zlib header ok\n"));
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = hold & 0x200 ? DICTID : TYPE;
+            INITBITS();
+            break;
+#ifdef GUNZIP
+        case FLAGS:
+            NEEDBITS(16);
+            state->flags = (int)(hold);
+            if ((state->flags & 0xff) != Z_DEFLATED) {
+                strm->msg = (char *)"unknown compression method";
+                state->mode = BAD;
+                break;
+            }
+            if (state->flags & 0xe000) {
+                strm->msg = (char *)"unknown header flags set";
+                state->mode = BAD;
+                break;
+            }
+            if (state->head != Z_NULL)
+                state->head->text = (int)((hold >> 8) & 1);
+            if (state->flags & 0x0200) CRC2(state->check, hold);
+            INITBITS();
+            state->mode = TIME;
+        case TIME:
+            NEEDBITS(32);
+            if (state->head != Z_NULL)
+                state->head->time = hold;
+            if (state->flags & 0x0200) CRC4(state->check, hold);
+            INITBITS();
+            state->mode = OS;
+        case OS:
+            NEEDBITS(16);
+            if (state->head != Z_NULL) {
+                state->head->xflags = (int)(hold & 0xff);
+                state->head->os = (int)(hold >> 8);
+            }
+            if (state->flags & 0x0200) CRC2(state->check, hold);
+            INITBITS();
+            state->mode = EXLEN;
+        case EXLEN:
+            if (state->flags & 0x0400) {
+                NEEDBITS(16);
+                state->length = (unsigned)(hold);
+                if (state->head != Z_NULL)
+                    state->head->extra_len = (unsigned)hold;
+                if (state->flags & 0x0200) CRC2(state->check, hold);
+                INITBITS();
+            }
+            else if (state->head != Z_NULL)
+                state->head->extra = Z_NULL;
+            state->mode = EXTRA;
+        case EXTRA:
+            if (state->flags & 0x0400) {
+                copy = state->length;
+                if (copy > have) copy = have;
+                if (copy) {
+                    if (state->head != Z_NULL &&
+                        state->head->extra != Z_NULL) {
+                        len = state->head->extra_len - state->length;
+                        zmemcpy(state->head->extra + len, next,
+                                len + copy > state->head->extra_max ?
+                                state->head->extra_max - len : copy);
+                    }
+                    if (state->flags & 0x0200)
+                        state->check = crc32(state->check, next, copy);
+                    have -= copy;
+                    next += copy;
+                    state->length -= copy;
+                }
+                if (state->length) goto inf_leave;
+            }
+            state->length = 0;
+            state->mode = NAME;
+        case NAME:
+            if (state->flags & 0x0800) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                    if (state->head != Z_NULL &&
+                            state->head->name != Z_NULL &&
+                            state->length < state->head->name_max)
+                        state->head->name[state->length++] = len;
+                } while (len && copy < have);
+                if (state->flags & 0x0200)
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            else if (state->head != Z_NULL)
+                state->head->name = Z_NULL;
+            state->length = 0;
+            state->mode = COMMENT;
+        case COMMENT:
+            if (state->flags & 0x1000) {
+                if (have == 0) goto inf_leave;
+                copy = 0;
+                do {
+                    len = (unsigned)(next[copy++]);
+                    if (state->head != Z_NULL &&
+                            state->head->comment != Z_NULL &&
+                            state->length < state->head->comm_max)
+                        state->head->comment[state->length++] = len;
+                } while (len && copy < have);
+                if (state->flags & 0x0200)
+                    state->check = crc32(state->check, next, copy);
+                have -= copy;
+                next += copy;
+                if (len) goto inf_leave;
+            }
+            else if (state->head != Z_NULL)
+                state->head->comment = Z_NULL;
+            state->mode = HCRC;
+        case HCRC:
+            if (state->flags & 0x0200) {
+                NEEDBITS(16);
+                if (hold != (state->check & 0xffff)) {
+                    strm->msg = (char *)"header crc mismatch";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+            }
+            if (state->head != Z_NULL) {
+                state->head->hcrc = (int)((state->flags >> 9) & 1);
+                state->head->done = 1;
+            }
+            strm->adler = state->check = crc32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+            break;
+#endif
+        case DICTID:
+            NEEDBITS(32);
+            strm->adler = state->check = REVERSE(hold);
+            INITBITS();
+            state->mode = DICT;
+        case DICT:
+            if (state->havedict == 0) {
+                RESTORE();
+                return Z_NEED_DICT;
+            }
+            strm->adler = state->check = adler32(0L, Z_NULL, 0);
+            state->mode = TYPE;
+        case TYPE:
+            if (flush == Z_BLOCK) goto inf_leave;
+        case TYPEDO:
+            if (state->last) {
+                BYTEBITS();
+                state->mode = CHECK;
+                break;
+            }
+            NEEDBITS(3);
+            state->last = BITS(1);
+            DROPBITS(1);
+            switch (BITS(2)) {
+            case 0:                             /* stored block */
+                Tracev((stderr, "inflate:     stored block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = STORED;
+                break;
+            case 1:                             /* fixed block */
+                fixedtables(state);
+                Tracev((stderr, "inflate:     fixed codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = LEN;              /* decode codes */
+                break;
+            case 2:                             /* dynamic block */
+                Tracev((stderr, "inflate:     dynamic codes block%s\n",
+                        state->last ? " (last)" : ""));
+                state->mode = TABLE;
+                break;
+            case 3:
+                strm->msg = (char *)"invalid block type";
+                state->mode = BAD;
+            }
+            DROPBITS(2);
+            break;
+        case STORED:
+            BYTEBITS();                         /* go to byte boundary */
+            NEEDBITS(32);
+            if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) {
+                strm->msg = (char *)"invalid stored block lengths";
+                state->mode = BAD;
+                break;
+            }
+            state->length = (unsigned)hold & 0xffff;
+            Tracev((stderr, "inflate:       stored length %u\n",
+                    state->length));
+            INITBITS();
+            state->mode = COPY;
+        case COPY:
+            copy = state->length;
+            if (copy) {
+                if (copy > have) copy = have;
+                if (copy > left) copy = left;
+                if (copy == 0) goto inf_leave;
+                zmemcpy(put, next, copy);
+                have -= copy;
+                next += copy;
+                left -= copy;
+                put += copy;
+                state->length -= copy;
+                break;
+            }
+            Tracev((stderr, "inflate:       stored end\n"));
+            state->mode = TYPE;
+            break;
+        case TABLE:
+            NEEDBITS(14);
+            state->nlen = BITS(5) + 257;
+            DROPBITS(5);
+            state->ndist = BITS(5) + 1;
+            DROPBITS(5);
+            state->ncode = BITS(4) + 4;
+            DROPBITS(4);
+#ifndef PKZIP_BUG_WORKAROUND
+            if (state->nlen > 286 || state->ndist > 30) {
+                strm->msg = (char *)"too many length or distance symbols";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            Tracev((stderr, "inflate:       table sizes ok\n"));
+            state->have = 0;
+            state->mode = LENLENS;
+        case LENLENS:
+            while (state->have < state->ncode) {
+                NEEDBITS(3);
+                state->lens[order[state->have++]] = (unsigned short)BITS(3);
+                DROPBITS(3);
+            }
+            while (state->have < 19)
+                state->lens[order[state->have++]] = 0;
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 7;
+            ret = inflate_table(CODES, state->lens, 19, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid code lengths set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       code lengths ok\n"));
+            state->have = 0;
+            state->mode = CODELENS;
+        case CODELENS:
+            while (state->have < state->nlen + state->ndist) {
+                for (;;) {
+                    this = state->lencode[BITS(state->lenbits)];
+                    if ((unsigned)(this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                if (this.val < 16) {
+                    NEEDBITS(this.bits);
+                    DROPBITS(this.bits);
+                    state->lens[state->have++] = this.val;
+                }
+                else {
+                    if (this.val == 16) {
+                        NEEDBITS(this.bits + 2);
+                        DROPBITS(this.bits);
+                        if (state->have == 0) {
+                            strm->msg = (char *)"invalid bit length repeat";
+                            state->mode = BAD;
+                            break;
+                        }
+                        len = state->lens[state->have - 1];
+                        copy = 3 + BITS(2);
+                        DROPBITS(2);
+                    }
+                    else if (this.val == 17) {
+                        NEEDBITS(this.bits + 3);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 3 + BITS(3);
+                        DROPBITS(3);
+                    }
+                    else {
+                        NEEDBITS(this.bits + 7);
+                        DROPBITS(this.bits);
+                        len = 0;
+                        copy = 11 + BITS(7);
+                        DROPBITS(7);
+                    }
+                    if (state->have + copy > state->nlen + state->ndist) {
+                        strm->msg = (char *)"invalid bit length repeat";
+                        state->mode = BAD;
+                        break;
+                    }
+                    while (copy--)
+                        state->lens[state->have++] = (unsigned short)len;
+                }
+            }
+
+            /* handle error breaks in while */
+            if (state->mode == BAD) break;
+
+            /* build code tables */
+            state->next = state->codes;
+            state->lencode = (code const FAR *)(state->next);
+            state->lenbits = 9;
+            ret = inflate_table(LENS, state->lens, state->nlen, &(state->next),
+                                &(state->lenbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid literal/lengths set";
+                state->mode = BAD;
+                break;
+            }
+            state->distcode = (code const FAR *)(state->next);
+            state->distbits = 6;
+            ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist,
+                            &(state->next), &(state->distbits), state->work);
+            if (ret) {
+                strm->msg = (char *)"invalid distances set";
+                state->mode = BAD;
+                break;
+            }
+            Tracev((stderr, "inflate:       codes ok\n"));
+            state->mode = LEN;
+        case LEN:
+            if (have >= 6 && left >= 258) {
+                RESTORE();
+                inflate_fast(strm, out);
+                LOAD();
+                break;
+            }
+            for (;;) {
+                this = state->lencode[BITS(state->lenbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if (this.op && (this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->lencode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            state->length = (unsigned)this.val;
+            if ((int)(this.op) == 0) {
+                Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ?
+                        "inflate:         literal '%c'\n" :
+                        "inflate:         literal 0x%02x\n", this.val));
+                state->mode = LIT;
+                break;
+            }
+            if (this.op & 32) {
+                Tracevv((stderr, "inflate:         end of block\n"));
+                state->mode = TYPE;
+                break;
+            }
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid literal/length code";
+                state->mode = BAD;
+                break;
+            }
+            state->extra = (unsigned)(this.op) & 15;
+            state->mode = LENEXT;
+        case LENEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->length += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+            Tracevv((stderr, "inflate:         length %u\n", state->length));
+            state->mode = DIST;
+        case DIST:
+            for (;;) {
+                this = state->distcode[BITS(state->distbits)];
+                if ((unsigned)(this.bits) <= bits) break;
+                PULLBYTE();
+            }
+            if ((this.op & 0xf0) == 0) {
+                last = this;
+                for (;;) {
+                    this = state->distcode[last.val +
+                            (BITS(last.bits + last.op) >> last.bits)];
+                    if ((unsigned)(last.bits + this.bits) <= bits) break;
+                    PULLBYTE();
+                }
+                DROPBITS(last.bits);
+            }
+            DROPBITS(this.bits);
+            if (this.op & 64) {
+                strm->msg = (char *)"invalid distance code";
+                state->mode = BAD;
+                break;
+            }
+            state->offset = (unsigned)this.val;
+            state->extra = (unsigned)(this.op) & 15;
+            state->mode = DISTEXT;
+        case DISTEXT:
+            if (state->extra) {
+                NEEDBITS(state->extra);
+                state->offset += BITS(state->extra);
+                DROPBITS(state->extra);
+            }
+#ifdef INFLATE_STRICT
+            if (state->offset > state->dmax) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+#endif
+            if (state->offset > state->whave + out - left) {
+                strm->msg = (char *)"invalid distance too far back";
+                state->mode = BAD;
+                break;
+            }
+            Tracevv((stderr, "inflate:         distance %u\n", state->offset));
+            state->mode = MATCH;
+        case MATCH:
+            if (left == 0) goto inf_leave;
+            copy = out - left;
+            if (state->offset > copy) {         /* copy from window */
+                copy = state->offset - copy;
+                if (copy > state->write) {
+                    copy -= state->write;
+                    from = state->window + (state->wsize - copy);
+                }
+                else
+                    from = state->window + (state->write - copy);
+                if (copy > state->length) copy = state->length;
+            }
+            else {                              /* copy from output */
+                from = put - state->offset;
+                copy = state->length;
+            }
+            if (copy > left) copy = left;
+            left -= copy;
+            state->length -= copy;
+            do {
+                *put++ = *from++;
+            } while (--copy);
+            if (state->length == 0) state->mode = LEN;
+            break;
+        case LIT:
+            if (left == 0) goto inf_leave;
+            *put++ = (unsigned char)(state->length);
+            left--;
+            state->mode = LEN;
+            break;
+        case CHECK:
+            if (state->wrap) {
+                NEEDBITS(32);
+                out -= left;
+                strm->total_out += out;
+                state->total += out;
+                if (out)
+                    strm->adler = state->check =
+                        UPDATE(state->check, put - out, out);
+                out = left;
+                if ((
+#ifdef GUNZIP
+                     state->flags ? hold :
+#endif
+                     REVERSE(hold)) != state->check) {
+                    strm->msg = (char *)"incorrect data check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   check matches trailer\n"));
+            }
+#ifdef GUNZIP
+            state->mode = LENGTH;
+        case LENGTH:
+            if (state->wrap && state->flags) {
+                NEEDBITS(32);
+                if (hold != (state->total & 0xffffffffUL)) {
+                    strm->msg = (char *)"incorrect length check";
+                    state->mode = BAD;
+                    break;
+                }
+                INITBITS();
+                Tracev((stderr, "inflate:   length matches trailer\n"));
+            }
+#endif
+            state->mode = DONE;
+        case DONE:
+            ret = Z_STREAM_END;
+            goto inf_leave;
+        case BAD:
+            ret = Z_DATA_ERROR;
+            goto inf_leave;
+        case MEM:
+            return Z_MEM_ERROR;
+        case SYNC:
+        default:
+            return Z_STREAM_ERROR;
+        }
+
+    /*
+       Return from inflate(), updating the total counts and the check value.
+       If there was no progress during the inflate() call, return a buffer
+       error.  Call updatewindow() to create and/or update the window state.
+       Note: a memory error from inflate() is non-recoverable.
+     */
+  inf_leave:
+    RESTORE();
+    if (state->wsize || (state->mode < CHECK && out != strm->avail_out))
+        if (updatewindow(strm, out)) {
+            state->mode = MEM;
+            return Z_MEM_ERROR;
+        }
+    in -= strm->avail_in;
+    out -= strm->avail_out;
+    strm->total_in += in;
+    strm->total_out += out;
+    state->total += out;
+    if (state->wrap && out)
+        strm->adler = state->check =
+            UPDATE(state->check, strm->next_out - out, out);
+    strm->data_type = state->bits + (state->last ? 64 : 0) +
+                      (state->mode == TYPE ? 128 : 0);
+    if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK)
+        ret = Z_BUF_ERROR;
+    return ret;
+}
+
+int ZEXPORT inflateEnd(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+    if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->window != Z_NULL) ZFREE(strm, state->window);
+    ZFREE(strm, strm->state);
+    strm->state = Z_NULL;
+    Tracev((stderr, "inflate: end\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength)
+z_streamp strm;
+const Bytef *dictionary;
+uInt dictLength;
+{
+    struct inflate_state FAR *state;
+    unsigned long id;
+
+    /* check state */
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (state->wrap != 0 && state->mode != DICT)
+        return Z_STREAM_ERROR;
+
+    /* check for correct dictionary id */
+    if (state->mode == DICT) {
+        id = adler32(0L, Z_NULL, 0);
+        id = adler32(id, dictionary, dictLength);
+        if (id != state->check)
+            return Z_DATA_ERROR;
+    }
+
+    /* copy dictionary to window */
+    if (updatewindow(strm, strm->avail_out)) {
+        state->mode = MEM;
+        return Z_MEM_ERROR;
+    }
+    if (dictLength > state->wsize) {
+        zmemcpy(state->window, dictionary + dictLength - state->wsize,
+                state->wsize);
+        state->whave = state->wsize;
+    }
+    else {
+        zmemcpy(state->window + state->wsize - dictLength, dictionary,
+                dictLength);
+        state->whave = dictLength;
+    }
+    state->havedict = 1;
+    Tracev((stderr, "inflate:   dictionary set\n"));
+    return Z_OK;
+}
+
+int ZEXPORT inflateGetHeader(strm, head)
+z_streamp strm;
+gz_headerp head;
+{
+    struct inflate_state FAR *state;
+
+    /* check state */
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if ((state->wrap & 2) == 0) return Z_STREAM_ERROR;
+
+    /* save header structure */
+    state->head = head;
+    head->done = 0;
+    return Z_OK;
+}
+
+/*
+   Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff.  Return when found
+   or when out of input.  When called, *have is the number of pattern bytes
+   found in order so far, in 0..3.  On return *have is updated to the new
+   state.  If on return *have equals four, then the pattern was found and the
+   return value is how many bytes were read including the last byte of the
+   pattern.  If *have is less than four, then the pattern has not been found
+   yet and the return value is len.  In the latter case, syncsearch() can be
+   called again with more data and the *have state.  *have is initialized to
+   zero for the first call.
+ */
+local unsigned syncsearch(have, buf, len)
+unsigned FAR *have;
+unsigned char FAR *buf;
+unsigned len;
+{
+    unsigned got;
+    unsigned next;
+
+    got = *have;
+    next = 0;
+    while (next < len && got < 4) {
+        if ((int)(buf[next]) == (got < 2 ? 0 : 0xff))
+            got++;
+        else if (buf[next])
+            got = 0;
+        else
+            got = 4 - got;
+        next++;
+    }
+    *have = got;
+    return next;
+}
+
+int ZEXPORT inflateSync(strm)
+z_streamp strm;
+{
+    unsigned len;               /* number of bytes to look at or looked at */
+    unsigned long in, out;      /* temporary to save total_in and total_out */
+    unsigned char buf[4];       /* to restore bit buffer to byte string */
+    struct inflate_state FAR *state;
+
+    /* check parameters */
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR;
+
+    /* if first time, start search in bit buffer */
+    if (state->mode != SYNC) {
+        state->mode = SYNC;
+        state->hold <<= state->bits & 7;
+        state->bits -= state->bits & 7;
+        len = 0;
+        while (state->bits >= 8) {
+            buf[len++] = (unsigned char)(state->hold);
+            state->hold >>= 8;
+            state->bits -= 8;
+        }
+        state->have = 0;
+        syncsearch(&(state->have), buf, len);
+    }
+
+    /* search available input */
+    len = syncsearch(&(state->have), strm->next_in, strm->avail_in);
+    strm->avail_in -= len;
+    strm->next_in += len;
+    strm->total_in += len;
+
+    /* return no joy or set up to restart inflate() on a new block */
+    if (state->have != 4) return Z_DATA_ERROR;
+    in = strm->total_in;  out = strm->total_out;
+    inflateReset(strm);
+    strm->total_in = in;  strm->total_out = out;
+    state->mode = TYPE;
+    return Z_OK;
+}
+
+/*
+   Returns true if inflate is currently at the end of a block generated by
+   Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
+   implementation to provide an additional safety check. PPP uses
+   Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored
+   block. When decompressing, PPP checks that at the end of input packet,
+   inflate is waiting for these length bytes.
+ */
+int ZEXPORT inflateSyncPoint(strm)
+z_streamp strm;
+{
+    struct inflate_state FAR *state;
+
+    if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)strm->state;
+    return state->mode == STORED && state->bits == 0;
+}
+
+int ZEXPORT inflateCopy(dest, source)
+z_streamp dest;
+z_streamp source;
+{
+    struct inflate_state FAR *state;
+    struct inflate_state FAR *copy;
+    unsigned char FAR *window;
+    unsigned wsize;
+
+    /* check input */
+    if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL ||
+        source->zalloc == (alloc_func)0 || source->zfree == (free_func)0)
+        return Z_STREAM_ERROR;
+    state = (struct inflate_state FAR *)source->state;
+
+    /* allocate space */
+    copy = (struct inflate_state FAR *)
+           ZALLOC(source, 1, sizeof(struct inflate_state));
+    if (copy == Z_NULL) return Z_MEM_ERROR;
+    window = Z_NULL;
+    if (state->window != Z_NULL) {
+        window = (unsigned char FAR *)
+                 ZALLOC(source, 1U << state->wbits, sizeof(unsigned char));
+        if (window == Z_NULL) {
+            ZFREE(source, copy);
+            return Z_MEM_ERROR;
+        }
+    }
+
+    /* copy state */
+    zmemcpy(dest, source, sizeof(z_stream));
+    zmemcpy(copy, state, sizeof(struct inflate_state));
+    if (state->lencode >= state->codes &&
+        state->lencode <= state->codes + ENOUGH - 1) {
+        copy->lencode = copy->codes + (state->lencode - state->codes);
+        copy->distcode = copy->codes + (state->distcode - state->codes);
+    }
+    copy->next = copy->codes + (state->next - state->codes);
+    if (window != Z_NULL) {
+        wsize = 1U << state->wbits;
+        zmemcpy(window, state->window, wsize);
+    }
+    copy->window = window;
+    dest->state = (struct internal_state FAR *)copy;
+    return Z_OK;
+}
diff --git a/distrib/zlib-1.2.3/inflate.h b/distrib/zlib-1.2.3/inflate.h
new file mode 100644
index 0000000..07bd3e7
--- /dev/null
+++ b/distrib/zlib-1.2.3/inflate.h
@@ -0,0 +1,115 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2004 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip decoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+    HEAD,       /* i: waiting for magic header */
+    FLAGS,      /* i: waiting for method and flags (gzip) */
+    TIME,       /* i: waiting for modification time (gzip) */
+    OS,         /* i: waiting for extra flags and operating system (gzip) */
+    EXLEN,      /* i: waiting for extra length (gzip) */
+    EXTRA,      /* i: waiting for extra bytes (gzip) */
+    NAME,       /* i: waiting for end of file name (gzip) */
+    COMMENT,    /* i: waiting for end of comment (gzip) */
+    HCRC,       /* i: waiting for header crc (gzip) */
+    DICTID,     /* i: waiting for dictionary check value */
+    DICT,       /* waiting for inflateSetDictionary() call */
+        TYPE,       /* i: waiting for type bits, including last-flag bit */
+        TYPEDO,     /* i: same, but skip check to exit inflate on new block */
+        STORED,     /* i: waiting for stored size (length and complement) */
+        COPY,       /* i/o: waiting for input or output to copy stored block */
+        TABLE,      /* i: waiting for dynamic block table lengths */
+        LENLENS,    /* i: waiting for code length code lengths */
+        CODELENS,   /* i: waiting for length/lit and distance code lengths */
+            LEN,        /* i: waiting for length/lit code */
+            LENEXT,     /* i: waiting for length extra bits */
+            DIST,       /* i: waiting for distance code */
+            DISTEXT,    /* i: waiting for distance extra bits */
+            MATCH,      /* o: waiting for output space to copy string */
+            LIT,        /* o: waiting for output space to write literal */
+    CHECK,      /* i: waiting for 32-bit check value */
+    LENGTH,     /* i: waiting for 32-bit length (gzip) */
+    DONE,       /* finished check, done -- remain here until reset */
+    BAD,        /* got a data error -- remain here until reset */
+    MEM,        /* got an inflate() memory error -- remain here until reset */
+    SYNC        /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+    State transitions between above modes -
+
+    (most modes can go to the BAD or MEM mode -- not shown for clarity)
+
+    Process header:
+        HEAD -> (gzip) or (zlib)
+        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME
+        NAME -> COMMENT -> HCRC -> TYPE
+        (zlib) -> DICTID or TYPE
+        DICTID -> DICT -> TYPE
+    Read deflate blocks:
+            TYPE -> STORED or TABLE or LEN or CHECK
+            STORED -> COPY -> TYPE
+            TABLE -> LENLENS -> CODELENS -> LEN
+    Read deflate codes:
+                LEN -> LENEXT or LIT or TYPE
+                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+                LIT -> LEN
+    Process trailer:
+        CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls.  Approximately 7K bytes. */
+struct inflate_state {
+    inflate_mode mode;          /* current inflate mode */
+    int last;                   /* true if processing last block */
+    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip */
+    int havedict;               /* true if dictionary provided */
+    int flags;                  /* gzip header method and flags (0 if zlib) */
+    unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */
+    unsigned long check;        /* protected copy of check value */
+    unsigned long total;        /* protected copy of output count */
+    gz_headerp head;            /* where to save gzip header information */
+        /* sliding window */
+    unsigned wbits;             /* log base 2 of requested window size */
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned write;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if needed */
+        /* bit accumulator */
+    unsigned long hold;         /* input bit accumulator */
+    unsigned bits;              /* number of bits in "in" */
+        /* for string and stored block copying */
+    unsigned length;            /* literal or length of data to copy */
+    unsigned offset;            /* distance back to copy string from */
+        /* for table and code decoding */
+    unsigned extra;             /* extra bits needed */
+        /* fixed and dynamic code tables */
+    code const FAR *lencode;    /* starting table for length/literal codes */
+    code const FAR *distcode;   /* starting table for distance codes */
+    unsigned lenbits;           /* index bits for lencode */
+    unsigned distbits;          /* index bits for distcode */
+        /* dynamic table building */
+    unsigned ncode;             /* number of code length code lengths */
+    unsigned nlen;              /* number of length code lengths */
+    unsigned ndist;             /* number of distance code lengths */
+    unsigned have;              /* number of code lengths in lens[] */
+    code FAR *next;             /* next available space in codes[] */
+    unsigned short lens[320];   /* temporary storage for code lengths */
+    unsigned short work[288];   /* work area for code table building */
+    code codes[ENOUGH];         /* space for code tables */
+};
diff --git a/distrib/zlib-1.2.3/inftrees.c b/distrib/zlib-1.2.3/inftrees.c
new file mode 100644
index 0000000..8a9c13f
--- /dev/null
+++ b/distrib/zlib-1.2.3/inftrees.c
@@ -0,0 +1,329 @@
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+#include "zutil.h"
+#include "inftrees.h"
+
+#define MAXBITS 15
+
+const char inflate_copyright[] =
+   " inflate 1.2.3 Copyright 1995-2005 Mark Adler ";
+/*
+  If you use the zlib library in a product, an acknowledgment is welcome
+  in the documentation of your product. If for some reason you cannot
+  include such an acknowledgment, I would appreciate that you keep this
+  copyright string in the executable of your product.
+ */
+
+/*
+   Build a set of tables to decode the provided canonical Huffman code.
+   The code lengths are lens[0..codes-1].  The result starts at *table,
+   whose indices are 0..2^bits-1.  work is a writable array of at least
+   lens shorts, which is used as a work area.  type is the type of code
+   to be generated, CODES, LENS, or DISTS.  On return, zero is success,
+   -1 is an invalid code, and +1 means that ENOUGH isn't enough.  table
+   on return points to the next available entry's address.  bits is the
+   requested root table index bits, and on return it is the actual root
+   table index bits.  It will differ if the request is greater than the
+   longest code or if it is less than the shortest code.
+ */
+int inflate_table(type, lens, codes, table, bits, work)
+codetype type;
+unsigned short FAR *lens;
+unsigned codes;
+code FAR * FAR *table;
+unsigned FAR *bits;
+unsigned short FAR *work;
+{
+    unsigned len;               /* a code's length in bits */
+    unsigned sym;               /* index of code symbols */
+    unsigned min, max;          /* minimum and maximum code lengths */
+    unsigned root;              /* number of index bits for root table */
+    unsigned curr;              /* number of index bits for current table */
+    unsigned drop;              /* code bits to drop for sub-table */
+    int left;                   /* number of prefix codes available */
+    unsigned used;              /* code entries in table used */
+    unsigned huff;              /* Huffman code */
+    unsigned incr;              /* for incrementing code, index */
+    unsigned fill;              /* index for replicating entries */
+    unsigned low;               /* low bits for current root entry */
+    unsigned mask;              /* mask for low root bits */
+    code this;                  /* table entry for duplication */
+    code FAR *next;             /* next available space in table */
+    const unsigned short FAR *base;     /* base value table to use */
+    const unsigned short FAR *extra;    /* extra bits table to use */
+    int end;                    /* use base and extra for symbol > end */
+    unsigned short count[MAXBITS+1];    /* number of codes of each length */
+    unsigned short offs[MAXBITS+1];     /* offsets in table for each length */
+    static const unsigned short lbase[31] = { /* Length codes 257..285 base */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+    static const unsigned short lext[31] = { /* Length codes 257..285 extra */
+        16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+        19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196};
+    static const unsigned short dbase[32] = { /* Distance codes 0..29 base */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577, 0, 0};
+    static const unsigned short dext[32] = { /* Distance codes 0..29 extra */
+        16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+        23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+        28, 28, 29, 29, 64, 64};
+
+    /*
+       Process a set of code lengths to create a canonical Huffman code.  The
+       code lengths are lens[0..codes-1].  Each length corresponds to the
+       symbols 0..codes-1.  The Huffman code is generated by first sorting the
+       symbols by length from short to long, and retaining the symbol order
+       for codes with equal lengths.  Then the code starts with all zero bits
+       for the first code of the shortest length, and the codes are integer
+       increments for the same length, and zeros are appended as the length
+       increases.  For the deflate format, these bits are stored backwards
+       from their more natural integer increment ordering, and so when the
+       decoding tables are built in the large loop below, the integer codes
+       are incremented backwards.
+
+       This routine assumes, but does not check, that all of the entries in
+       lens[] are in the range 0..MAXBITS.  The caller must assure this.
+       1..MAXBITS is interpreted as that code length.  zero means that that
+       symbol does not occur in this code.
+
+       The codes are sorted by computing a count of codes for each length,
+       creating from that a table of starting indices for each length in the
+       sorted table, and then entering the symbols in order in the sorted
+       table.  The sorted table is work[], with that space being provided by
+       the caller.
+
+       The length counts are used for other purposes as well, i.e. finding
+       the minimum and maximum length codes, determining if there are any
+       codes at all, checking for a valid set of lengths, and looking ahead
+       at length counts to determine sub-table sizes when building the
+       decoding tables.
+     */
+
+    /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+    for (len = 0; len <= MAXBITS; len++)
+        count[len] = 0;
+    for (sym = 0; sym < codes; sym++)
+        count[lens[sym]]++;
+
+    /* bound code lengths, force root to be within code lengths */
+    root = *bits;
+    for (max = MAXBITS; max >= 1; max--)
+        if (count[max] != 0) break;
+    if (root > max) root = max;
+    if (max == 0) {                     /* no symbols to code at all */
+        this.op = (unsigned char)64;    /* invalid code marker */
+        this.bits = (unsigned char)1;
+        this.val = (unsigned short)0;
+        *(*table)++ = this;             /* make a table to force an error */
+        *(*table)++ = this;
+        *bits = 1;
+        return 0;     /* no symbols, but wait for decoding to report error */
+    }
+    for (min = 1; min <= MAXBITS; min++)
+        if (count[min] != 0) break;
+    if (root < min) root = min;
+
+    /* check for an over-subscribed or incomplete set of lengths */
+    left = 1;
+    for (len = 1; len <= MAXBITS; len++) {
+        left <<= 1;
+        left -= count[len];
+        if (left < 0) return -1;        /* over-subscribed */
+    }
+    if (left > 0 && (type == CODES || max != 1))
+        return -1;                      /* incomplete set */
+
+    /* generate offsets into symbol table for each length for sorting */
+    offs[1] = 0;
+    for (len = 1; len < MAXBITS; len++)
+        offs[len + 1] = offs[len] + count[len];
+
+    /* sort symbols by length, by symbol order within each length */
+    for (sym = 0; sym < codes; sym++)
+        if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym;
+
+    /*
+       Create and fill in decoding tables.  In this loop, the table being
+       filled is at next and has curr index bits.  The code being used is huff
+       with length len.  That code is converted to an index by dropping drop
+       bits off of the bottom.  For codes where len is less than drop + curr,
+       those top drop + curr - len bits are incremented through all values to
+       fill the table with replicated entries.
+
+       root is the number of index bits for the root table.  When len exceeds
+       root, sub-tables are created pointed to by the root entry with an index
+       of the low root bits of huff.  This is saved in low to check for when a
+       new sub-table should be started.  drop is zero when the root table is
+       being filled, and drop is root when sub-tables are being filled.
+
+       When a new sub-table is needed, it is necessary to look ahead in the
+       code lengths to determine what size sub-table is needed.  The length
+       counts are used for this, and so count[] is decremented as codes are
+       entered in the tables.
+
+       used keeps track of how many table entries have been allocated from the
+       provided *table space.  It is checked when a LENS table is being made
+       against the space in *table, ENOUGH, minus the maximum space needed by
+       the worst case distance code, MAXD.  This should never happen, but the
+       sufficiency of ENOUGH has not been proven exhaustively, hence the check.
+       This assumes that when type == LENS, bits == 9.
+
+       sym increments through all symbols, and the loop terminates when
+       all codes of length max, i.e. all codes, have been processed.  This
+       routine permits incomplete codes, so another loop after this one fills
+       in the rest of the decoding tables with invalid code markers.
+     */
+
+    /* set up for code type */
+    switch (type) {
+    case CODES:
+        base = extra = work;    /* dummy value--not used */
+        end = 19;
+        break;
+    case LENS:
+        base = lbase;
+        base -= 257;
+        extra = lext;
+        extra -= 257;
+        end = 256;
+        break;
+    default:            /* DISTS */
+        base = dbase;
+        extra = dext;
+        end = -1;
+    }
+
+    /* initialize state for loop */
+    huff = 0;                   /* starting code */
+    sym = 0;                    /* starting code symbol */
+    len = min;                  /* starting code length */
+    next = *table;              /* current table to fill in */
+    curr = root;                /* current table index bits */
+    drop = 0;                   /* current bits to drop from code for index */
+    low = (unsigned)(-1);       /* trigger new sub-table when len > root */
+    used = 1U << root;          /* use root table entries */
+    mask = used - 1;            /* mask for comparing low */
+
+    /* check available table space */
+    if (type == LENS && used >= ENOUGH - MAXD)
+        return 1;
+
+    /* process all codes and make table entries */
+    for (;;) {
+        /* create table entry */
+        this.bits = (unsigned char)(len - drop);
+        if ((int)(work[sym]) < end) {
+            this.op = (unsigned char)0;
+            this.val = work[sym];
+        }
+        else if ((int)(work[sym]) > end) {
+            this.op = (unsigned char)(extra[work[sym]]);
+            this.val = base[work[sym]];
+        }
+        else {
+            this.op = (unsigned char)(32 + 64);         /* end of block */
+            this.val = 0;
+        }
+
+        /* replicate for those indices with low len bits equal to huff */
+        incr = 1U << (len - drop);
+        fill = 1U << curr;
+        min = fill;                 /* save offset to next table */
+        do {
+            fill -= incr;
+            next[(huff >> drop) + fill] = this;
+        } while (fill != 0);
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+
+        /* go to next symbol, update count, len */
+        sym++;
+        if (--(count[len]) == 0) {
+            if (len == max) break;
+            len = lens[work[sym]];
+        }
+
+        /* create new sub-table if needed */
+        if (len > root && (huff & mask) != low) {
+            /* if first time, transition to sub-tables */
+            if (drop == 0)
+                drop = root;
+
+            /* increment past last table */
+            next += min;            /* here min is 1 << curr */
+
+            /* determine length of next table */
+            curr = len - drop;
+            left = (int)(1 << curr);
+            while (curr + drop < max) {
+                left -= count[curr + drop];
+                if (left <= 0) break;
+                curr++;
+                left <<= 1;
+            }
+
+            /* check for enough space */
+            used += 1U << curr;
+            if (type == LENS && used >= ENOUGH - MAXD)
+                return 1;
+
+            /* point entry in root table to sub-table */
+            low = huff & mask;
+            (*table)[low].op = (unsigned char)curr;
+            (*table)[low].bits = (unsigned char)root;
+            (*table)[low].val = (unsigned short)(next - *table);
+        }
+    }
+
+    /*
+       Fill in rest of table for incomplete codes.  This loop is similar to the
+       loop above in incrementing huff for table indices.  It is assumed that
+       len is equal to curr + drop, so there is no loop needed to increment
+       through high index bits.  When the current sub-table is filled, the loop
+       drops back to the root table to fill in any remaining entries there.
+     */
+    this.op = (unsigned char)64;                /* invalid code marker */
+    this.bits = (unsigned char)(len - drop);
+    this.val = (unsigned short)0;
+    while (huff != 0) {
+        /* when done with sub-table, drop back to root table */
+        if (drop != 0 && (huff & mask) != low) {
+            drop = 0;
+            len = root;
+            next = *table;
+            this.bits = (unsigned char)len;
+        }
+
+        /* put invalid code marker in table */
+        next[huff >> drop] = this;
+
+        /* backwards increment the len-bit code huff */
+        incr = 1U << (len - 1);
+        while (huff & incr)
+            incr >>= 1;
+        if (incr != 0) {
+            huff &= incr - 1;
+            huff += incr;
+        }
+        else
+            huff = 0;
+    }
+
+    /* set return parameters */
+    *table += used;
+    *bits = root;
+    return 0;
+}
diff --git a/distrib/zlib-1.2.3/inftrees.h b/distrib/zlib-1.2.3/inftrees.h
new file mode 100644
index 0000000..b1104c8
--- /dev/null
+++ b/distrib/zlib-1.2.3/inftrees.h
@@ -0,0 +1,55 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2005 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables.  Each entry provides either the
+   information needed to do the operation requested by the code that
+   indexed that table entry, or it provides a pointer to another
+   table that indexes more bits of the code.  op indicates whether
+   the entry is a pointer to another table, a literal, a length or
+   distance, an end-of-block, or an invalid code.  For a table
+   pointer, the low four bits of op is the number of index bits of
+   that table.  For a length or distance, the low four bits of op
+   is the number of extra bits to get after the code.  bits is
+   the number of bits in this code or part of the code to drop off
+   of the bit buffer.  val is the actual byte to output in the case
+   of a literal, the base length or distance, or the offset from
+   the current table to the next table.  Each entry is four bytes. */
+typedef struct {
+    unsigned char op;           /* operation, extra bits, table bits */
+    unsigned char bits;         /* bits in this part of the code */
+    unsigned short val;         /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+    00000000 - literal
+    0000tttt - table link, tttt != 0 is the number of table index bits
+    0001eeee - length or distance, eeee is the number of extra bits
+    01100000 - end of block
+    01000000 - invalid code
+ */
+
+/* Maximum size of dynamic tree.  The maximum found in a long but non-
+   exhaustive search was 1444 code structures (852 for length/literals
+   and 592 for distances, the latter actually the result of an
+   exhaustive search).  The true maximum is not known, but the value
+   below is more than safe. */
+#define ENOUGH 2048
+#define MAXD 592
+
+/* Type of code to build for inftable() */
+typedef enum {
+    CODES,
+    LENS,
+    DISTS
+} codetype;
+
+extern int inflate_table OF((codetype type, unsigned short FAR *lens,
+                             unsigned codes, code FAR * FAR *table,
+                             unsigned FAR *bits, unsigned short FAR *work));
diff --git a/distrib/zlib-1.2.3/sources.make b/distrib/zlib-1.2.3/sources.make
new file mode 100644
index 0000000..c15fd78
--- /dev/null
+++ b/distrib/zlib-1.2.3/sources.make
@@ -0,0 +1,4 @@
+# this is included by various Makefiles
+ZLIB_SOURCES := adler32.c compress.c crc32.c deflate.c gzio.c infback.c inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c
+ZLIB_SOURCES := $(ZLIB_SOURCES:%=$(ZLIB_DIR)/%)
+
diff --git a/distrib/zlib-1.2.3/trees.c b/distrib/zlib-1.2.3/trees.c
new file mode 100644
index 0000000..395e4e1
--- /dev/null
+++ b/distrib/zlib-1.2.3/trees.c
@@ -0,0 +1,1219 @@
+/* trees.c -- output deflated data using Huffman coding
+ * Copyright (C) 1995-2005 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/*
+ *  ALGORITHM
+ *
+ *      The "deflation" process uses several Huffman trees. The more
+ *      common source values are represented by shorter bit sequences.
+ *
+ *      Each code tree is stored in a compressed form which is itself
+ * a Huffman encoding of the lengths of all the code strings (in
+ * ascending order by source values).  The actual code strings are
+ * reconstructed from the lengths in the inflate process, as described
+ * in the deflate specification.
+ *
+ *  REFERENCES
+ *
+ *      Deutsch, L.P.,"'Deflate' Compressed Data Format Specification".
+ *      Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc
+ *
+ *      Storer, James A.
+ *          Data Compression:  Methods and Theory, pp. 49-50.
+ *          Computer Science Press, 1988.  ISBN 0-7167-8156-5.
+ *
+ *      Sedgewick, R.
+ *          Algorithms, p290.
+ *          Addison-Wesley, 1983. ISBN 0-201-06672-6.
+ */
+
+/* @(#) $Id$ */
+
+/* #define GEN_TREES_H */
+
+#include "deflate.h"
+
+#ifdef DEBUG
+#  include <ctype.h>
+#endif
+
+/* ===========================================================================
+ * Constants
+ */
+
+#define MAX_BL_BITS 7
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+#define END_BLOCK 256
+/* end of block literal code */
+
+#define REP_3_6      16
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+#define REPZ_3_10    17
+/* repeat a zero length 3-10 times  (3 bits of repeat count) */
+
+#define REPZ_11_138  18
+/* repeat a zero length 11-138 times  (7 bits of repeat count) */
+
+local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */
+   = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0};
+
+local const int extra_dbits[D_CODES] /* extra bits for each distance code */
+   = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13};
+
+local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */
+   = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7};
+
+local const uch bl_order[BL_CODES]
+   = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+#define Buf_size (8 * 2*sizeof(char))
+/* Number of bits used within bi_buf. (bi_buf might be implemented on
+ * more than 16 bits on some systems.)
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+#define DIST_CODE_LEN  512 /* see definition of array dist_code below */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+/* non ANSI compilers may not accept trees.h */
+
+local ct_data static_ltree[L_CODES+2];
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+local ct_data static_dtree[D_CODES];
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+uch _dist_code[DIST_CODE_LEN];
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+uch _length_code[MAX_MATCH-MIN_MATCH+1];
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+local int base_length[LENGTH_CODES];
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+local int base_dist[D_CODES];
+/* First normalized distance for each code (0 = distance of 1) */
+
+#else
+#  include "trees.h"
+#endif /* GEN_TREES_H */
+
+struct static_tree_desc_s {
+    const ct_data *static_tree;  /* static tree or NULL */
+    const intf *extra_bits;      /* extra bits for each code or NULL */
+    int     extra_base;          /* base index for extra_bits */
+    int     elems;               /* max number of elements in the tree */
+    int     max_length;          /* max bit length for the codes */
+};
+
+local static_tree_desc  static_l_desc =
+{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS};
+
+local static_tree_desc  static_d_desc =
+{static_dtree, extra_dbits, 0,          D_CODES, MAX_BITS};
+
+local static_tree_desc  static_bl_desc =
+{(const ct_data *)0, extra_blbits, 0,   BL_CODES, MAX_BL_BITS};
+
+/* ===========================================================================
+ * Local (static) routines in this file.
+ */
+
+local void tr_static_init OF((void));
+local void init_block     OF((deflate_state *s));
+local void pqdownheap     OF((deflate_state *s, ct_data *tree, int k));
+local void gen_bitlen     OF((deflate_state *s, tree_desc *desc));
+local void gen_codes      OF((ct_data *tree, int max_code, ushf *bl_count));
+local void build_tree     OF((deflate_state *s, tree_desc *desc));
+local void scan_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local void send_tree      OF((deflate_state *s, ct_data *tree, int max_code));
+local int  build_bl_tree  OF((deflate_state *s));
+local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes,
+                              int blcodes));
+local void compress_block OF((deflate_state *s, ct_data *ltree,
+                              ct_data *dtree));
+local void set_data_type  OF((deflate_state *s));
+local unsigned bi_reverse OF((unsigned value, int length));
+local void bi_windup      OF((deflate_state *s));
+local void bi_flush       OF((deflate_state *s));
+local void copy_block     OF((deflate_state *s, charf *buf, unsigned len,
+                              int header));
+
+#ifdef GEN_TREES_H
+local void gen_trees_header OF((void));
+#endif
+
+#ifndef DEBUG
+#  define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len)
+   /* Send a code of the given tree. c and tree must not have side effects */
+
+#else /* DEBUG */
+#  define send_code(s, c, tree) \
+     { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \
+       send_bits(s, tree[c].Code, tree[c].Len); }
+#endif
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+#define put_short(s, w) { \
+    put_byte(s, (uch)((w) & 0xff)); \
+    put_byte(s, (uch)((ush)(w) >> 8)); \
+}
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+#ifdef DEBUG
+local void send_bits      OF((deflate_state *s, int value, int length));
+
+local void send_bits(s, value, length)
+    deflate_state *s;
+    int value;  /* value to send */
+    int length; /* number of bits */
+{
+    Tracevv((stderr," l %2d v %4x ", length, value));
+    Assert(length > 0 && length <= 15, "invalid length");
+    s->bits_sent += (ulg)length;
+
+    /* If not enough room in bi_buf, use (valid) bits from bi_buf and
+     * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid))
+     * unused bits in value.
+     */
+    if (s->bi_valid > (int)Buf_size - length) {
+        s->bi_buf |= (value << s->bi_valid);
+        put_short(s, s->bi_buf);
+        s->bi_buf = (ush)value >> (Buf_size - s->bi_valid);
+        s->bi_valid += length - Buf_size;
+    } else {
+        s->bi_buf |= value << s->bi_valid;
+        s->bi_valid += length;
+    }
+}
+#else /* !DEBUG */
+
+#define send_bits(s, value, length) \
+{ int len = length;\
+  if (s->bi_valid > (int)Buf_size - len) {\
+    int val = value;\
+    s->bi_buf |= (val << s->bi_valid);\
+    put_short(s, s->bi_buf);\
+    s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\
+    s->bi_valid += len - Buf_size;\
+  } else {\
+    s->bi_buf |= (value) << s->bi_valid;\
+    s->bi_valid += len;\
+  }\
+}
+#endif /* DEBUG */
+
+
+/* the arguments must not have side effects */
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+local void tr_static_init()
+{
+#if defined(GEN_TREES_H) || !defined(STDC)
+    static int static_init_done = 0;
+    int n;        /* iterates over tree elements */
+    int bits;     /* bit counter */
+    int length;   /* length value */
+    int code;     /* code value */
+    int dist;     /* distance index */
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    if (static_init_done) return;
+
+    /* For some embedded targets, global variables are not initialized: */
+    static_l_desc.static_tree = static_ltree;
+    static_l_desc.extra_bits = extra_lbits;
+    static_d_desc.static_tree = static_dtree;
+    static_d_desc.extra_bits = extra_dbits;
+    static_bl_desc.extra_bits = extra_blbits;
+
+    /* Initialize the mapping length (0..255) -> length code (0..28) */
+    length = 0;
+    for (code = 0; code < LENGTH_CODES-1; code++) {
+        base_length[code] = length;
+        for (n = 0; n < (1<<extra_lbits[code]); n++) {
+            _length_code[length++] = (uch)code;
+        }
+    }
+    Assert (length == 256, "tr_static_init: length != 256");
+    /* Note that the length 255 (match length 258) can be represented
+     * in two different ways: code 284 + 5 bits or code 285, so we
+     * overwrite length_code[255] to use the best encoding:
+     */
+    _length_code[length-1] = (uch)code;
+
+    /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+    dist = 0;
+    for (code = 0 ; code < 16; code++) {
+        base_dist[code] = dist;
+        for (n = 0; n < (1<<extra_dbits[code]); n++) {
+            _dist_code[dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: dist != 256");
+    dist >>= 7; /* from now on, all distances are divided by 128 */
+    for ( ; code < D_CODES; code++) {
+        base_dist[code] = dist << 7;
+        for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) {
+            _dist_code[256 + dist++] = (uch)code;
+        }
+    }
+    Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+    /* Construct the codes of the static literal tree */
+    for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0;
+    n = 0;
+    while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++;
+    while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++;
+    while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++;
+    while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++;
+    /* Codes 286 and 287 do not exist, but we must include them in the
+     * tree construction to get a canonical Huffman tree (longest code
+     * all ones)
+     */
+    gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count);
+
+    /* The static distance tree is trivial: */
+    for (n = 0; n < D_CODES; n++) {
+        static_dtree[n].Len = 5;
+        static_dtree[n].Code = bi_reverse((unsigned)n, 5);
+    }
+    static_init_done = 1;
+
+#  ifdef GEN_TREES_H
+    gen_trees_header();
+#  endif
+#endif /* defined(GEN_TREES_H) || !defined(STDC) */
+}
+
+/* ===========================================================================
+ * Genererate the file trees.h describing the static trees.
+ */
+#ifdef GEN_TREES_H
+#  ifndef DEBUG
+#    include <stdio.h>
+#  endif
+
+#  define SEPARATOR(i, last, width) \
+      ((i) == (last)? "\n};\n\n" :    \
+       ((i) % (width) == (width)-1 ? ",\n" : ", "))
+
+void gen_trees_header()
+{
+    FILE *header = fopen("trees.h", "w");
+    int i;
+
+    Assert (header != NULL, "Can't open trees.h");
+    fprintf(header,
+            "/* header created automatically with -DGEN_TREES_H */\n\n");
+
+    fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n");
+    for (i = 0; i < L_CODES+2; i++) {
+        fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code,
+                static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5));
+    }
+
+    fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+        fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code,
+                static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5));
+    }
+
+    fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n");
+    for (i = 0; i < DIST_CODE_LEN; i++) {
+        fprintf(header, "%2u%s", _dist_code[i],
+                SEPARATOR(i, DIST_CODE_LEN-1, 20));
+    }
+
+    fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n");
+    for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) {
+        fprintf(header, "%2u%s", _length_code[i],
+                SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20));
+    }
+
+    fprintf(header, "local const int base_length[LENGTH_CODES] = {\n");
+    for (i = 0; i < LENGTH_CODES; i++) {
+        fprintf(header, "%1u%s", base_length[i],
+                SEPARATOR(i, LENGTH_CODES-1, 20));
+    }
+
+    fprintf(header, "local const int base_dist[D_CODES] = {\n");
+    for (i = 0; i < D_CODES; i++) {
+        fprintf(header, "%5u%s", base_dist[i],
+                SEPARATOR(i, D_CODES-1, 10));
+    }
+
+    fclose(header);
+}
+#endif /* GEN_TREES_H */
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+void _tr_init(s)
+    deflate_state *s;
+{
+    tr_static_init();
+
+    s->l_desc.dyn_tree = s->dyn_ltree;
+    s->l_desc.stat_desc = &static_l_desc;
+
+    s->d_desc.dyn_tree = s->dyn_dtree;
+    s->d_desc.stat_desc = &static_d_desc;
+
+    s->bl_desc.dyn_tree = s->bl_tree;
+    s->bl_desc.stat_desc = &static_bl_desc;
+
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+    s->last_eob_len = 8; /* enough lookahead for inflate */
+#ifdef DEBUG
+    s->compressed_len = 0L;
+    s->bits_sent = 0L;
+#endif
+
+    /* Initialize the first block of the first file: */
+    init_block(s);
+}
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+local void init_block(s)
+    deflate_state *s;
+{
+    int n; /* iterates over tree elements */
+
+    /* Initialize the trees. */
+    for (n = 0; n < L_CODES;  n++) s->dyn_ltree[n].Freq = 0;
+    for (n = 0; n < D_CODES;  n++) s->dyn_dtree[n].Freq = 0;
+    for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0;
+
+    s->dyn_ltree[END_BLOCK].Freq = 1;
+    s->opt_len = s->static_len = 0L;
+    s->last_lit = s->matches = 0;
+}
+
+#define SMALLEST 1
+/* Index within the heap array of least frequent node in the Huffman tree */
+
+
+/* ===========================================================================
+ * Remove the smallest element from the heap and recreate the heap with
+ * one less element. Updates heap and heap_len.
+ */
+#define pqremove(s, tree, top) \
+{\
+    top = s->heap[SMALLEST]; \
+    s->heap[SMALLEST] = s->heap[s->heap_len--]; \
+    pqdownheap(s, tree, SMALLEST); \
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+#define smaller(tree, n, m, depth) \
+   (tree[n].Freq < tree[m].Freq || \
+   (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m]))
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+local void pqdownheap(s, tree, k)
+    deflate_state *s;
+    ct_data *tree;  /* the tree to restore */
+    int k;               /* node to move down */
+{
+    int v = s->heap[k];
+    int j = k << 1;  /* left son of k */
+    while (j <= s->heap_len) {
+        /* Set j to the smallest of the two sons: */
+        if (j < s->heap_len &&
+            smaller(tree, s->heap[j+1], s->heap[j], s->depth)) {
+            j++;
+        }
+        /* Exit if v is smaller than both sons */
+        if (smaller(tree, v, s->heap[j], s->depth)) break;
+
+        /* Exchange v with the smallest son */
+        s->heap[k] = s->heap[j];  k = j;
+
+        /* And continue down the tree, setting j to the left son of k */
+        j <<= 1;
+    }
+    s->heap[k] = v;
+}
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ *    above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ *     array bl_count contains the frequencies for each bit length.
+ *     The length opt_len is updated; static_len is also updated if stree is
+ *     not null.
+ */
+local void gen_bitlen(s, desc)
+    deflate_state *s;
+    tree_desc *desc;    /* the tree descriptor */
+{
+    ct_data *tree        = desc->dyn_tree;
+    int max_code         = desc->max_code;
+    const ct_data *stree = desc->stat_desc->static_tree;
+    const intf *extra    = desc->stat_desc->extra_bits;
+    int base             = desc->stat_desc->extra_base;
+    int max_length       = desc->stat_desc->max_length;
+    int h;              /* heap index */
+    int n, m;           /* iterate over the tree elements */
+    int bits;           /* bit length */
+    int xbits;          /* extra bits */
+    ush f;              /* frequency */
+    int overflow = 0;   /* number of elements with bit length too large */
+
+    for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0;
+
+    /* In a first pass, compute the optimal bit lengths (which may
+     * overflow in the case of the bit length tree).
+     */
+    tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */
+
+    for (h = s->heap_max+1; h < HEAP_SIZE; h++) {
+        n = s->heap[h];
+        bits = tree[tree[n].Dad].Len + 1;
+        if (bits > max_length) bits = max_length, overflow++;
+        tree[n].Len = (ush)bits;
+        /* We overwrite tree[n].Dad which is no longer needed */
+
+        if (n > max_code) continue; /* not a leaf node */
+
+        s->bl_count[bits]++;
+        xbits = 0;
+        if (n >= base) xbits = extra[n-base];
+        f = tree[n].Freq;
+        s->opt_len += (ulg)f * (bits + xbits);
+        if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits);
+    }
+    if (overflow == 0) return;
+
+    Trace((stderr,"\nbit length overflow\n"));
+    /* This happens for example on obj2 and pic of the Calgary corpus */
+
+    /* Find the first bit length which could increase: */
+    do {
+        bits = max_length-1;
+        while (s->bl_count[bits] == 0) bits--;
+        s->bl_count[bits]--;      /* move one leaf down the tree */
+        s->bl_count[bits+1] += 2; /* move one overflow item as its brother */
+        s->bl_count[max_length]--;
+        /* The brother of the overflow item also moves one step up,
+         * but this does not affect bl_count[max_length]
+         */
+        overflow -= 2;
+    } while (overflow > 0);
+
+    /* Now recompute all bit lengths, scanning in increasing frequency.
+     * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+     * lengths instead of fixing only the wrong ones. This idea is taken
+     * from 'ar' written by Haruhiko Okumura.)
+     */
+    for (bits = max_length; bits != 0; bits--) {
+        n = s->bl_count[bits];
+        while (n != 0) {
+            m = s->heap[--h];
+            if (m > max_code) continue;
+            if ((unsigned) tree[m].Len != (unsigned) bits) {
+                Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+                s->opt_len += ((long)bits - (long)tree[m].Len)
+                              *(long)tree[m].Freq;
+                tree[m].Len = (ush)bits;
+            }
+            n--;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ *     zero code length.
+ */
+local void gen_codes (tree, max_code, bl_count)
+    ct_data *tree;             /* the tree to decorate */
+    int max_code;              /* largest code with non zero frequency */
+    ushf *bl_count;            /* number of codes at each bit length */
+{
+    ush next_code[MAX_BITS+1]; /* next code value for each bit length */
+    ush code = 0;              /* running code value */
+    int bits;                  /* bit index */
+    int n;                     /* code index */
+
+    /* The distribution counts are first used to generate the code values
+     * without bit reversal.
+     */
+    for (bits = 1; bits <= MAX_BITS; bits++) {
+        next_code[bits] = code = (code + bl_count[bits-1]) << 1;
+    }
+    /* Check that the bit counts in bl_count are consistent. The last code
+     * must be all ones.
+     */
+    Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+            "inconsistent bit counts");
+    Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+    for (n = 0;  n <= max_code; n++) {
+        int len = tree[n].Len;
+        if (len == 0) continue;
+        /* Now reverse the bits */
+        tree[n].Code = bi_reverse(next_code[len]++, len);
+
+        Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+             n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+    }
+}
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ *     and corresponding code. The length opt_len is updated; static_len is
+ *     also updated if stree is not null. The field max_code is set.
+ */
+local void build_tree(s, desc)
+    deflate_state *s;
+    tree_desc *desc; /* the tree descriptor */
+{
+    ct_data *tree         = desc->dyn_tree;
+    const ct_data *stree  = desc->stat_desc->static_tree;
+    int elems             = desc->stat_desc->elems;
+    int n, m;          /* iterate over heap elements */
+    int max_code = -1; /* largest code with non zero frequency */
+    int node;          /* new node being created */
+
+    /* Construct the initial heap, with least frequent element in
+     * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+     * heap[0] is not used.
+     */
+    s->heap_len = 0, s->heap_max = HEAP_SIZE;
+
+    for (n = 0; n < elems; n++) {
+        if (tree[n].Freq != 0) {
+            s->heap[++(s->heap_len)] = max_code = n;
+            s->depth[n] = 0;
+        } else {
+            tree[n].Len = 0;
+        }
+    }
+
+    /* The pkzip format requires that at least one distance code exists,
+     * and that at least one bit should be sent even if there is only one
+     * possible code. So to avoid special checks later on we force at least
+     * two codes of non zero frequency.
+     */
+    while (s->heap_len < 2) {
+        node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0);
+        tree[node].Freq = 1;
+        s->depth[node] = 0;
+        s->opt_len--; if (stree) s->static_len -= stree[node].Len;
+        /* node is 0 or 1 so it does not have extra bits */
+    }
+    desc->max_code = max_code;
+
+    /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+     * establish sub-heaps of increasing lengths:
+     */
+    for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n);
+
+    /* Construct the Huffman tree by repeatedly combining the least two
+     * frequent nodes.
+     */
+    node = elems;              /* next internal node of the tree */
+    do {
+        pqremove(s, tree, n);  /* n = node of least frequency */
+        m = s->heap[SMALLEST]; /* m = node of next least frequency */
+
+        s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */
+        s->heap[--(s->heap_max)] = m;
+
+        /* Create a new node father of n and m */
+        tree[node].Freq = tree[n].Freq + tree[m].Freq;
+        s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ?
+                                s->depth[n] : s->depth[m]) + 1);
+        tree[n].Dad = tree[m].Dad = (ush)node;
+#ifdef DUMP_BL_TREE
+        if (tree == s->bl_tree) {
+            fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)",
+                    node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq);
+        }
+#endif
+        /* and insert the new node in the heap */
+        s->heap[SMALLEST] = node++;
+        pqdownheap(s, tree, SMALLEST);
+
+    } while (s->heap_len >= 2);
+
+    s->heap[--(s->heap_max)] = s->heap[SMALLEST];
+
+    /* At this point, the fields freq and dad are set. We can now
+     * generate the bit lengths.
+     */
+    gen_bitlen(s, (tree_desc *)desc);
+
+    /* The field len is now set, we can generate the bit codes */
+    gen_codes ((ct_data *)tree, max_code, s->bl_count);
+}
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+local void scan_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree;   /* the tree to be scanned */
+    int max_code;    /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    if (nextlen == 0) max_count = 138, min_count = 3;
+    tree[max_code+1].Len = (ush)0xffff; /* guard */
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            s->bl_tree[curlen].Freq += count;
+        } else if (curlen != 0) {
+            if (curlen != prevlen) s->bl_tree[curlen].Freq++;
+            s->bl_tree[REP_3_6].Freq++;
+        } else if (count <= 10) {
+            s->bl_tree[REPZ_3_10].Freq++;
+        } else {
+            s->bl_tree[REPZ_11_138].Freq++;
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+local void send_tree (s, tree, max_code)
+    deflate_state *s;
+    ct_data *tree; /* the tree to be scanned */
+    int max_code;       /* and its largest code of non zero frequency */
+{
+    int n;                     /* iterates over all tree elements */
+    int prevlen = -1;          /* last emitted length */
+    int curlen;                /* length of current code */
+    int nextlen = tree[0].Len; /* length of next code */
+    int count = 0;             /* repeat count of the current code */
+    int max_count = 7;         /* max repeat count */
+    int min_count = 4;         /* min repeat count */
+
+    /* tree[max_code+1].Len = -1; */  /* guard already set */
+    if (nextlen == 0) max_count = 138, min_count = 3;
+
+    for (n = 0; n <= max_code; n++) {
+        curlen = nextlen; nextlen = tree[n+1].Len;
+        if (++count < max_count && curlen == nextlen) {
+            continue;
+        } else if (count < min_count) {
+            do { send_code(s, curlen, s->bl_tree); } while (--count != 0);
+
+        } else if (curlen != 0) {
+            if (curlen != prevlen) {
+                send_code(s, curlen, s->bl_tree); count--;
+            }
+            Assert(count >= 3 && count <= 6, " 3_6?");
+            send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2);
+
+        } else if (count <= 10) {
+            send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3);
+
+        } else {
+            send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7);
+        }
+        count = 0; prevlen = curlen;
+        if (nextlen == 0) {
+            max_count = 138, min_count = 3;
+        } else if (curlen == nextlen) {
+            max_count = 6, min_count = 3;
+        } else {
+            max_count = 7, min_count = 4;
+        }
+    }
+}
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+local int build_bl_tree(s)
+    deflate_state *s;
+{
+    int max_blindex;  /* index of last bit length code of non zero freq */
+
+    /* Determine the bit length frequencies for literal and distance trees */
+    scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code);
+    scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code);
+
+    /* Build the bit length tree: */
+    build_tree(s, (tree_desc *)(&(s->bl_desc)));
+    /* opt_len now includes the length of the tree representations, except
+     * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+     */
+
+    /* Determine the number of bit length codes to send. The pkzip format
+     * requires that at least 4 bit length codes be sent. (appnote.txt says
+     * 3 but the actual value used is 4.)
+     */
+    for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) {
+        if (s->bl_tree[bl_order[max_blindex]].Len != 0) break;
+    }
+    /* Update opt_len to include the bit length tree and counts */
+    s->opt_len += 3*(max_blindex+1) + 5+5+4;
+    Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+            s->opt_len, s->static_len));
+
+    return max_blindex;
+}
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+local void send_all_trees(s, lcodes, dcodes, blcodes)
+    deflate_state *s;
+    int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+    int rank;                    /* index in bl_order */
+
+    Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+    Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+            "too many codes");
+    Tracev((stderr, "\nbl counts: "));
+    send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */
+    send_bits(s, dcodes-1,   5);
+    send_bits(s, blcodes-4,  4); /* not -3 as stated in appnote.txt */
+    for (rank = 0; rank < blcodes; rank++) {
+        Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+        send_bits(s, s->bl_tree[bl_order[rank]].Len, 3);
+    }
+    Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */
+    Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+    send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */
+    Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+/* ===========================================================================
+ * Send a stored block
+ */
+void _tr_stored_block(s, buf, stored_len, eof)
+    deflate_state *s;
+    charf *buf;       /* input block */
+    ulg stored_len;   /* length of input block */
+    int eof;          /* true if this is the last block for a file */
+{
+    send_bits(s, (STORED_BLOCK<<1)+eof, 3);  /* send block type */
+#ifdef DEBUG
+    s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L;
+    s->compressed_len += (stored_len + 4) << 3;
+#endif
+    copy_block(s, buf, (unsigned)stored_len, 1); /* with header */
+}
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ * The current inflate code requires 9 bits of lookahead. If the
+ * last two codes for the previous block (real code plus EOB) were coded
+ * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode
+ * the last real code. In this case we send two empty static blocks instead
+ * of one. (There are no problems if the previous block is stored or fixed.)
+ * To simplify the code, we assume the worst case of last real code encoded
+ * on one bit only.
+ */
+void _tr_align(s)
+    deflate_state *s;
+{
+    send_bits(s, STATIC_TREES<<1, 3);
+    send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+    s->compressed_len += 10L; /* 3 for block type, 7 for EOB */
+#endif
+    bi_flush(s);
+    /* Of the 10 bits for the empty block, we have already sent
+     * (10 - bi_valid) bits. The lookahead for the last real code (before
+     * the EOB of the previous block) was thus at least one plus the length
+     * of the EOB plus what we have just sent of the empty static block.
+     */
+    if (1 + s->last_eob_len + 10 - s->bi_valid < 9) {
+        send_bits(s, STATIC_TREES<<1, 3);
+        send_code(s, END_BLOCK, static_ltree);
+#ifdef DEBUG
+        s->compressed_len += 10L;
+#endif
+        bi_flush(s);
+    }
+    s->last_eob_len = 7;
+}
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+void _tr_flush_block(s, buf, stored_len, eof)
+    deflate_state *s;
+    charf *buf;       /* input block, or NULL if too old */
+    ulg stored_len;   /* length of input block */
+    int eof;          /* true if this is the last block for a file */
+{
+    ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+    int max_blindex = 0;  /* index of last bit length code of non zero freq */
+
+    /* Build the Huffman trees unless a stored block is forced */
+    if (s->level > 0) {
+
+        /* Check if the file is binary or text */
+        if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN)
+            set_data_type(s);
+
+        /* Construct the literal and distance trees */
+        build_tree(s, (tree_desc *)(&(s->l_desc)));
+        Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+                s->static_len));
+
+        build_tree(s, (tree_desc *)(&(s->d_desc)));
+        Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+                s->static_len));
+        /* At this point, opt_len and static_len are the total bit lengths of
+         * the compressed block data, excluding the tree representations.
+         */
+
+        /* Build the bit length tree for the above two trees, and get the index
+         * in bl_order of the last bit length code to send.
+         */
+        max_blindex = build_bl_tree(s);
+
+        /* Determine the best encoding. Compute the block lengths in bytes. */
+        opt_lenb = (s->opt_len+3+7)>>3;
+        static_lenb = (s->static_len+3+7)>>3;
+
+        Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+                opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+                s->last_lit));
+
+        if (static_lenb <= opt_lenb) opt_lenb = static_lenb;
+
+    } else {
+        Assert(buf != (char*)0, "lost buf");
+        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+    }
+
+#ifdef FORCE_STORED
+    if (buf != (char*)0) { /* force stored block */
+#else
+    if (stored_len+4 <= opt_lenb && buf != (char*)0) {
+                       /* 4: two words for the lengths */
+#endif
+        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+         * Otherwise we can't have processed more than WSIZE input bytes since
+         * the last block flush, because compression would have been
+         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+         * transform a block into a stored block.
+         */
+        _tr_stored_block(s, buf, stored_len, eof);
+
+#ifdef FORCE_STATIC
+    } else if (static_lenb >= 0) { /* force static trees */
+#else
+    } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) {
+#endif
+        send_bits(s, (STATIC_TREES<<1)+eof, 3);
+        compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree);
+#ifdef DEBUG
+        s->compressed_len += 3 + s->static_len;
+#endif
+    } else {
+        send_bits(s, (DYN_TREES<<1)+eof, 3);
+        send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1,
+                       max_blindex+1);
+        compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree);
+#ifdef DEBUG
+        s->compressed_len += 3 + s->opt_len;
+#endif
+    }
+    Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+    /* The above check is made mod 2^32, for files larger than 512 MB
+     * and uLong implemented on 32 bits.
+     */
+    init_block(s);
+
+    if (eof) {
+        bi_windup(s);
+#ifdef DEBUG
+        s->compressed_len += 7;  /* align on byte boundary */
+#endif
+    }
+    Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+           s->compressed_len-7*eof));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+int _tr_tally (s, dist, lc)
+    deflate_state *s;
+    unsigned dist;  /* distance of matched string */
+    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+    s->d_buf[s->last_lit] = (ush)dist;
+    s->l_buf[s->last_lit++] = (uch)lc;
+    if (dist == 0) {
+        /* lc is the unmatched char */
+        s->dyn_ltree[lc].Freq++;
+    } else {
+        s->matches++;
+        /* Here, lc is the match length - MIN_MATCH */
+        dist--;             /* dist = match distance - 1 */
+        Assert((ush)dist < (ush)MAX_DIST(s) &&
+               (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+               (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");
+
+        s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++;
+        s->dyn_dtree[d_code(dist)].Freq++;
+    }
+
+#ifdef TRUNCATE_BLOCK
+    /* Try to guess if it is profitable to stop the current block here */
+    if ((s->last_lit & 0x1fff) == 0 && s->level > 2) {
+        /* Compute an upper bound for the compressed length */
+        ulg out_length = (ulg)s->last_lit*8L;
+        ulg in_length = (ulg)((long)s->strstart - s->block_start);
+        int dcode;
+        for (dcode = 0; dcode < D_CODES; dcode++) {
+            out_length += (ulg)s->dyn_dtree[dcode].Freq *
+                (5L+extra_dbits[dcode]);
+        }
+        out_length >>= 3;
+        Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+               s->last_lit, in_length, out_length,
+               100L - out_length*100L/in_length));
+        if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1;
+    }
+#endif
+    return (s->last_lit == s->lit_bufsize-1);
+    /* We avoid equality with lit_bufsize because of wraparound at 64K
+     * on 16 bit machines and because stored blocks are restricted to
+     * 64K-1 bytes.
+     */
+}
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+local void compress_block(s, ltree, dtree)
+    deflate_state *s;
+    ct_data *ltree; /* literal tree */
+    ct_data *dtree; /* distance tree */
+{
+    unsigned dist;      /* distance of matched string */
+    int lc;             /* match length or unmatched char (if dist == 0) */
+    unsigned lx = 0;    /* running index in l_buf */
+    unsigned code;      /* the code to send */
+    int extra;          /* number of extra bits to send */
+
+    if (s->last_lit != 0) do {
+        dist = s->d_buf[lx];
+        lc = s->l_buf[lx++];
+        if (dist == 0) {
+            send_code(s, lc, ltree); /* send a literal byte */
+            Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+        } else {
+            /* Here, lc is the match length - MIN_MATCH */
+            code = _length_code[lc];
+            send_code(s, code+LITERALS+1, ltree); /* send the length code */
+            extra = extra_lbits[code];
+            if (extra != 0) {
+                lc -= base_length[code];
+                send_bits(s, lc, extra);       /* send the extra length bits */
+            }
+            dist--; /* dist is now the match distance - 1 */
+            code = d_code(dist);
+            Assert (code < D_CODES, "bad d_code");
+
+            send_code(s, code, dtree);       /* send the distance code */
+            extra = extra_dbits[code];
+            if (extra != 0) {
+                dist -= base_dist[code];
+                send_bits(s, dist, extra);   /* send the extra distance bits */
+            }
+        } /* literal or match pair ? */
+
+        /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+        Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+               "pendingBuf overflow");
+
+    } while (lx < s->last_lit);
+
+    send_code(s, END_BLOCK, ltree);
+    s->last_eob_len = ltree[END_BLOCK].Len;
+}
+
+/* ===========================================================================
+ * Set the data type to BINARY or TEXT, using a crude approximation:
+ * set it to Z_TEXT if all symbols are either printable characters (33 to 255)
+ * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise.
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+local void set_data_type(s)
+    deflate_state *s;
+{
+    int n;
+
+    for (n = 0; n < 9; n++)
+        if (s->dyn_ltree[n].Freq != 0)
+            break;
+    if (n == 9)
+        for (n = 14; n < 32; n++)
+            if (s->dyn_ltree[n].Freq != 0)
+                break;
+    s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY;
+}
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+local unsigned bi_reverse(code, len)
+    unsigned code; /* the value to invert */
+    int len;       /* its bit length */
+{
+    register unsigned res = 0;
+    do {
+        res |= code & 1;
+        code >>= 1, res <<= 1;
+    } while (--len > 0);
+    return res >> 1;
+}
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+local void bi_flush(s)
+    deflate_state *s;
+{
+    if (s->bi_valid == 16) {
+        put_short(s, s->bi_buf);
+        s->bi_buf = 0;
+        s->bi_valid = 0;
+    } else if (s->bi_valid >= 8) {
+        put_byte(s, (Byte)s->bi_buf);
+        s->bi_buf >>= 8;
+        s->bi_valid -= 8;
+    }
+}
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+local void bi_windup(s)
+    deflate_state *s;
+{
+    if (s->bi_valid > 8) {
+        put_short(s, s->bi_buf);
+    } else if (s->bi_valid > 0) {
+        put_byte(s, (Byte)s->bi_buf);
+    }
+    s->bi_buf = 0;
+    s->bi_valid = 0;
+#ifdef DEBUG
+    s->bits_sent = (s->bits_sent+7) & ~7;
+#endif
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+local void copy_block(s, buf, len, header)
+    deflate_state *s;
+    charf    *buf;    /* the input data */
+    unsigned len;     /* its length */
+    int      header;  /* true if block header must be written */
+{
+    bi_windup(s);        /* align on byte boundary */
+    s->last_eob_len = 8; /* enough lookahead for inflate */
+
+    if (header) {
+        put_short(s, (ush)len);
+        put_short(s, (ush)~len);
+#ifdef DEBUG
+        s->bits_sent += 2*16;
+#endif
+    }
+#ifdef DEBUG
+    s->bits_sent += (ulg)len<<3;
+#endif
+    while (len--) {
+        put_byte(s, *buf++);
+    }
+}
diff --git a/distrib/zlib-1.2.3/trees.h b/distrib/zlib-1.2.3/trees.h
new file mode 100644
index 0000000..72facf9
--- /dev/null
+++ b/distrib/zlib-1.2.3/trees.h
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},
+{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},
+{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},
+{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},
+{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},
+{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},
+{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},
+{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},
+{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},
+{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},
+{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},
+{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},
+{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},
+{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},
+{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},
+{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},
+{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},
+{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},
+{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},
+{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},
+{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},
+{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},
+{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},
+{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},
+{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},
+{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},
+{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},
+{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},
+{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},
+{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},
+{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},
+{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},
+{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},
+{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},
+{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},
+{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},
+{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},
+{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},
+{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},
+{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},
+{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},
+{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},
+{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},
+{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},
+{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},
+{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},
+{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},
+{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},
+{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},
+{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},
+{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},
+{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},
+{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},
+{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},
+{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},
+{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},
+{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},
+{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch _dist_code[DIST_CODE_LEN] = {
+ 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,
+ 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
+   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
+ 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
+};
+
diff --git a/distrib/zlib-1.2.3/uncompr.c b/distrib/zlib-1.2.3/uncompr.c
new file mode 100644
index 0000000..b59e3d0
--- /dev/null
+++ b/distrib/zlib-1.2.3/uncompr.c
@@ -0,0 +1,61 @@
+/* uncompr.c -- decompress a memory buffer
+ * Copyright (C) 1995-2003 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+/* ===========================================================================
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted.
+*/
+int ZEXPORT uncompress (dest, destLen, source, sourceLen)
+    Bytef *dest;
+    uLongf *destLen;
+    const Bytef *source;
+    uLong sourceLen;
+{
+    z_stream stream;
+    int err;
+
+    stream.next_in = (Bytef*)source;
+    stream.avail_in = (uInt)sourceLen;
+    /* Check for source > 64K on 16-bit machine: */
+    if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
+
+    stream.next_out = dest;
+    stream.avail_out = (uInt)*destLen;
+    if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
+
+    stream.zalloc = (alloc_func)0;
+    stream.zfree = (free_func)0;
+
+    err = inflateInit(&stream);
+    if (err != Z_OK) return err;
+
+    err = inflate(&stream, Z_FINISH);
+    if (err != Z_STREAM_END) {
+        inflateEnd(&stream);
+        if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+            return Z_DATA_ERROR;
+        return err;
+    }
+    *destLen = stream.total_out;
+
+    err = inflateEnd(&stream);
+    return err;
+}
diff --git a/distrib/zlib-1.2.3/zconf.h b/distrib/zlib-1.2.3/zconf.h
new file mode 100644
index 0000000..03a9431
--- /dev/null
+++ b/distrib/zlib-1.2.3/zconf.h
@@ -0,0 +1,332 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+/*
+ * If you *really* need a unique prefix for all types and library functions,
+ * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
+ */
+#ifdef Z_PREFIX
+#  define deflateInit_          z_deflateInit_
+#  define deflate               z_deflate
+#  define deflateEnd            z_deflateEnd
+#  define inflateInit_          z_inflateInit_
+#  define inflate               z_inflate
+#  define inflateEnd            z_inflateEnd
+#  define deflateInit2_         z_deflateInit2_
+#  define deflateSetDictionary  z_deflateSetDictionary
+#  define deflateCopy           z_deflateCopy
+#  define deflateReset          z_deflateReset
+#  define deflateParams         z_deflateParams
+#  define deflateBound          z_deflateBound
+#  define deflatePrime          z_deflatePrime
+#  define inflateInit2_         z_inflateInit2_
+#  define inflateSetDictionary  z_inflateSetDictionary
+#  define inflateSync           z_inflateSync
+#  define inflateSyncPoint      z_inflateSyncPoint
+#  define inflateCopy           z_inflateCopy
+#  define inflateReset          z_inflateReset
+#  define inflateBack           z_inflateBack
+#  define inflateBackEnd        z_inflateBackEnd
+#  define compress              z_compress
+#  define compress2             z_compress2
+#  define compressBound         z_compressBound
+#  define uncompress            z_uncompress
+#  define adler32               z_adler32
+#  define crc32                 z_crc32
+#  define get_crc_table         z_get_crc_table
+#  define zError                z_zError
+
+#  define alloc_func            z_alloc_func
+#  define free_func             z_free_func
+#  define in_func               z_in_func
+#  define out_func              z_out_func
+#  define Byte                  z_Byte
+#  define uInt                  z_uInt
+#  define uLong                 z_uLong
+#  define Bytef                 z_Bytef
+#  define charf                 z_charf
+#  define intf                  z_intf
+#  define uIntf                 z_uIntf
+#  define uLongf                z_uLongf
+#  define voidpf                z_voidpf
+#  define voidp                 z_voidp
+#endif
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+#  define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+#  define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+#  ifndef WIN32
+#    define WIN32
+#  endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+#    ifndef SYS16BIT
+#      define SYS16BIT
+#    endif
+#  endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+#  ifndef STDC
+#    define STDC
+#  endif
+#  if __STDC_VERSION__ >= 199901L
+#    ifndef STDC99
+#      define STDC99
+#    endif
+#  endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+#  define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
+#  define STDC
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const       /* note: need a more gentle solution here */
+#  endif
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+#  define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+#  if defined(M_I86SM) || defined(M_I86MM)
+     /* MSC small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef _MSC_VER
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#  if (defined(__SMALL__) || defined(__MEDIUM__))
+     /* Turbo C small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef __BORLANDC__
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+   /* If building or using zlib as a DLL, define ZLIB_DLL.
+    * This is not mandatory, but it offers a little performance increase.
+    */
+#  ifdef ZLIB_DLL
+#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+#      ifdef ZLIB_INTERNAL
+#        define ZEXTERN extern __declspec(dllexport)
+#      else
+#        define ZEXTERN extern __declspec(dllimport)
+#      endif
+#    endif
+#  endif  /* ZLIB_DLL */
+   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+    * define ZLIB_WINAPI.
+    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+    */
+#  ifdef ZLIB_WINAPI
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    include <windows.h>
+     /* No need for _export, use ZLIB.DEF instead. */
+     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+#    define ZEXPORT WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA WINAPIV
+#    else
+#      define ZEXPORTVA FAR CDECL
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  ifdef ZLIB_DLL
+#    ifdef ZLIB_INTERNAL
+#      define ZEXPORT   __declspec(dllexport)
+#      define ZEXPORTVA __declspec(dllexport)
+#    else
+#      define ZEXPORT   __declspec(dllimport)
+#      define ZEXPORTVA __declspec(dllimport)
+#    endif
+#  endif
+#endif
+
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void const *voidpc;
+   typedef void FAR   *voidpf;
+   typedef void       *voidp;
+#else
+   typedef Byte const *voidpc;
+   typedef Byte FAR   *voidpf;
+   typedef Byte       *voidp;
+#endif
+
+#if 0           /* HAVE_UNISTD_H -- this line is updated by ./configure */
+#  include <sys/types.h> /* for off_t */
+#  include <unistd.h>    /* for SEEK_* and off_t */
+#  ifdef VMS
+#    include <unixio.h>   /* for off_t */
+#  endif
+#  define z_off_t off_t
+#endif
+#ifndef SEEK_SET
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+#ifndef z_off_t
+#  define z_off_t long
+#endif
+
+#if defined(__OS400__)
+#  define NO_vsnprintf
+#endif
+
+#if defined(__MVS__)
+#  define NO_vsnprintf
+#  ifdef FAR
+#    undef FAR
+#  endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+#   pragma map(deflateInit_,"DEIN")
+#   pragma map(deflateInit2_,"DEIN2")
+#   pragma map(deflateEnd,"DEEND")
+#   pragma map(deflateBound,"DEBND")
+#   pragma map(inflateInit_,"ININ")
+#   pragma map(inflateInit2_,"ININ2")
+#   pragma map(inflateEnd,"INEND")
+#   pragma map(inflateSync,"INSY")
+#   pragma map(inflateSetDictionary,"INSEDI")
+#   pragma map(compressBound,"CMBND")
+#   pragma map(inflate_table,"INTABL")
+#   pragma map(inflate_fast,"INFA")
+#   pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/distrib/zlib-1.2.3/zlib.h b/distrib/zlib-1.2.3/zlib.h
new file mode 100644
index 0000000..0228179
--- /dev/null
+++ b/distrib/zlib-1.2.3/zlib.h
@@ -0,0 +1,1357 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.3, July 18th, 2005
+
+  Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt
+  (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.3"
+#define ZLIB_VERNUM 0x1230
+
+/*
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms will be added later and will have the same
+  stream interface.
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+     The compressed data format used by default by the in-memory functions is
+  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+  around a deflate stream, which is itself documented in RFC 1951.
+
+     The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio using the functions that start
+  with "gz".  The gzip format is different from the zlib format.  gzip is a
+  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+     This library can optionally read and write gzip streams in memory as well.
+
+     The zlib format was designed to be compact and fast for use in memory
+  and on communications channels.  The gzip format was designed for single-
+  file compression on file systems, has a larger header than zlib to maintain
+  directory information, and uses a different, slower check method than zlib.
+
+     The library does not install any signal handler. The decoder checks
+  the consistency of the compressed data, so the library should never
+  crash even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    Bytef    *next_in;  /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total nb of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total nb of bytes output so far */
+
+    char     *msg;      /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: binary or text */
+    uLong   adler;      /* adler32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+     gzip header information passed to and from zlib routines.  See RFC 1952
+  for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+    int     text;       /* true if compressed data believed to be text */
+    uLong   time;       /* modification time */
+    int     xflags;     /* extra flags (not used when writing a gzip file) */
+    int     os;         /* operating system */
+    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */
+    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */
+    uInt    extra_max;  /* space at extra (only when reading header) */
+    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */
+    uInt    name_max;   /* space at name (only when reading header) */
+    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */
+    uInt    comm_max;   /* space at comment (only when reading header) */
+    int     hcrc;       /* true if there was or will be a header crc */
+    int     done;       /* true when done reading gzip header (not used
+                           when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+   The application must update next_in and avail_in when avail_in has
+   dropped to zero. It must update next_out and avail_out when avail_out
+   has dropped to zero. The application must initialize zalloc, zfree and
+   opaque before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application.
+
+   The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   opaque value.
+
+   zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.
+
+   On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by zalloc for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+   The fields total_in and total_out can be used for statistics or
+   progress reports. After compression, total_in holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+#define Z_BLOCK         5
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative
+ * values are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_RLE                 3
+#define Z_FIXED               4
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_TEXT     1
+#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+   This check is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+     Initializes the internal stream state for compression. The fields
+   zalloc, zfree and opaque must be initialized before by the caller.
+   If zalloc and zfree are set to Z_NULL, deflateInit updates them to
+   use default allocation functions.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at
+   all (the input data is simply copied a block at a time).
+   Z_DEFAULT_COMPRESSION requests a default compromise between speed and
+   compression (currently equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if level is not a valid compression level,
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).
+   msg is set to null if there is no error message.  deflateInit does not
+   perform any compression: this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce some
+  output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows. deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly. This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary (in interactive applications).
+    Some output may be provided even if flush is not set.
+
+  Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating avail_in or avail_out accordingly; avail_out
+  should never be zero before the call. The application can consume the
+  compressed output when it wants, for example when the output buffer is full
+  (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK
+  and with zero avail_out, it must be called again after making room in the
+  output buffer because there might be more output pending.
+
+    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+  decide how much data to accumualte before producing output, in order to
+  maximize compression.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far. (In particular
+  avail_in is zero after the call if enough output space has been provided
+  before the call.)  Flushing may degrade compression for some compression
+  algorithms and so it should be used only when necessary.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired. Using Z_FULL_FLUSH too often can seriously degrade
+  compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+  avail_out is greater than six to avoid repeated flush markers due to
+  avail_out == 0 on return.
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there
+  was enough output space; if deflate returns with Z_OK, this function must be
+  called again with Z_FINISH and more output space (updated avail_out) but no
+  more input data, until it returns with Z_STREAM_END or an error. After
+  deflate has returned Z_STREAM_END, the only possible operations on the
+  stream are deflateReset or deflateEnd.
+
+    Z_FINISH can be used immediately after deflateInit if all the compression
+  is to be done in a single step. In this case, avail_out must be at least
+  the value returned by deflateBound (see below). If deflate does not return
+  Z_STREAM_END, then it must be called again as described above.
+
+    deflate() sets strm->adler to the adler32 checksum of all input read
+  so far (that is, total_in bytes).
+
+    deflate() may update strm->data_type if it can make a good guess about
+  the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered
+  binary. This field is only for information purposes and does not affect
+  the compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible
+  (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not
+  fatal, and deflate() can be called again with more input and more output
+  space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded). In the error case,
+   msg may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+     Initializes the internal stream state for decompression. The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller. If next_in is not Z_NULL and avail_in is large enough (the exact
+   value depends on the compression method), inflateInit determines the
+   compression method from the zlib header and allocates all data structures
+   accordingly; otherwise the allocation will be deferred to the first call of
+   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+   use default allocation functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller.  msg is set to null if there is no error
+   message. inflateInit does not perform any decompression apart from reading
+   the zlib header if present: this will be done by inflate().  (So next_in and
+   avail_in may be modified, but next_out and avail_out are unchanged.)
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full. It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+  The detailed semantics are as follows. inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing
+    will resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there
+    is no more input data or no more space in the output buffer (see below
+    about the flush parameter).
+
+  Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (avail_out == 0), or after each
+  call of inflate(). If inflate returns Z_OK and with zero avail_out, it
+  must be called again after making room in the output buffer because there
+  might be more output pending.
+
+    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH,
+  Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much
+  output as possible to the output buffer. Z_BLOCK requests that inflate() stop
+  if and when it gets to the next deflate block boundary. When decoding the
+  zlib or gzip format, this will cause inflate() to return immediately after
+  the header and before the first block. When doing a raw inflate, inflate()
+  will go ahead and process the first block, and will return when it gets to
+  the end of that block, or when it runs out of data.
+
+    The Z_BLOCK option assists in appending to or combining deflate streams.
+  Also to assist in this, on return inflate() will set strm->data_type to the
+  number of unused bits in the last byte taken from strm->next_in, plus 64
+  if inflate() is currently decoding the last block in the deflate stream,
+  plus 128 if inflate() returned immediately after decoding an end-of-block
+  code or decoding the complete header up to just before the first byte of the
+  deflate stream. The end-of-block will not be indicated until all of the
+  uncompressed data from that block has been written to strm->next_out.  The
+  number of unused bits may in general be greater than seven, except when
+  bit 7 of data_type is set, in which case the number of unused bits will be
+  less than eight.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of inflate), the parameter flush should be set to
+  Z_FINISH. In this case all pending input is processed and all pending
+  output is flushed; avail_out must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+  is never required, but can be used to inform inflate that a faster approach
+  may be used for the single inflate() call.
+
+     In this implementation, inflate() always flushes as much output as
+  possible to the output buffer, and always uses the faster approach on the
+  first call. So the only effect of the flush parameter in this implementation
+  is on the return value of inflate(), as noted below, or when it returns early
+  because Z_BLOCK is used.
+
+     If a preset dictionary is needed after this call (see inflateSetDictionary
+  below), inflate sets strm->adler to the adler32 checksum of the dictionary
+  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+  strm->adler to the adler32 checksum of all output produced so far (that is,
+  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+  below. At the end of the stream, inflate() checks that its computed adler32
+  checksum is equal to that saved by the compressor and returns Z_STREAM_END
+  only if the checksum is correct.
+
+    inflate() will decompress and check either zlib-wrapped or gzip-wrapped
+  deflate data.  The header type is detected automatically.  Any information
+  contained in the gzip header is not retained, so applications that need that
+  information should instead use raw inflate, see inflateInit2() below, or
+  inflateBack() and perform their own processing of the gzip header and
+  trailer.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect check
+  value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+  if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory,
+  Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+  output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and
+  inflate() can be called again with more input and more output space to
+  continue decompressing. If Z_DATA_ERROR is returned, the application may then
+  call inflateSync() to look for a good compression block if a partial recovery
+  of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent. In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+                                     int  level,
+                                     int  method,
+                                     int  windowBits,
+                                     int  memLevel,
+                                     int  strategy));
+
+     This is another version of deflateInit with more compression options. The
+   fields next_in, zalloc, zfree and opaque must be initialized before by
+   the caller.
+
+     The method parameter is the compression method. It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer). It should be in the range 8..15 for this
+   version of the library. Larger values of this parameter result in better
+   compression at the expense of memory usage. The default value is 15 if
+   deflateInit is used instead.
+
+     windowBits can also be -8..-15 for raw deflate. In this case, -windowBits
+   determines the window size. deflate() will then generate raw deflate data
+   with no zlib header or trailer, and will not compute an adler32 check value.
+
+     windowBits can also be greater than 15 for optional gzip encoding. Add
+   16 to windowBits to write a simple gzip header and trailer around the
+   compressed data instead of a zlib wrapper. The gzip header will have no
+   file name, no extra data, no comment, no modification time (set to zero),
+   no header crc, and the operating system will be set to 255 (unknown).  If a
+   gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state. memLevel=1 uses minimum memory but
+   is slow and reduces compression ratio; memLevel=9 uses maximum memory
+   for optimal speed. The default value is 8. See zconf.h for total memory
+   usage as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm. Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match), or Z_RLE to limit match distances to one (run-length
+   encoding). Filtered data consists mostly of small values with a somewhat
+   random distribution. In this case, the compression algorithm is tuned to
+   compress them better. The effect of Z_FILTERED is to force more Huffman
+   coding and less string matching; it is somewhat intermediate between
+   Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as
+   Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy
+   parameter only affects the compression ratio but not the correctness of the
+   compressed output even if it is not set appropriately.  Z_FIXED prevents the
+   use of dynamic Huffman codes, allowing for a simpler decoder for special
+   applications.
+
+      deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid
+   method). msg is set to null if there is no error message.  deflateInit2 does
+   not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output. This function must be called
+   immediately after deflateInit, deflateInit2 or deflateReset, before any
+   call of deflate. The compressor and decompressor must use exactly the same
+   dictionary (see inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary. Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size in
+   deflate or deflate2. Thus the strings most likely to be useful should be
+   put at the end of the dictionary, not at the front. In addition, the
+   current implementation of deflate will use at most the window size minus
+   262 bytes of the provided dictionary.
+
+     Upon return of this function, strm->adler is set to the adler32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor. (The adler32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.) If a raw deflate was requested, then the
+   adler32 value is not computed and strm->adler is not set.
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if the compression method is bsort). deflateSetDictionary does not
+   perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter. The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and
+   can consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being NULL). msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to deflateEnd followed by deflateInit,
+   but does not free and reallocate all the internal compression state.
+   The stream will keep the same compression level and any other attributes
+   that may have been set by deflateInit2.
+
+      deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+                                      int level,
+                                      int strategy));
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2.  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different
+   strategy. If the compression level is changed, the input available so far
+   is compressed with the old level (and may be flushed); the new level will
+   take effect only at the next call of deflate().
+
+     Before the call of deflateParams, the stream state must be set as for
+   a call of deflate(), since the currently available input may have to
+   be compressed and flushed. In particular, strm->avail_out must be non-zero.
+
+     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR
+   if strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+                                    int good_length,
+                                    int max_lazy,
+                                    int nice_length,
+                                    int max_chain));
+/*
+     Fine tune deflate's internal compression parameters.  This should only be
+   used by someone who understands the algorithm used by zlib's deflate for
+   searching for the best matching string, and even then only by the most
+   fanatic optimizer trying to squeeze out the last compressed bit for their
+   specific input data.  Read the deflate.c source code for the meaning of the
+   max_lazy, good_length, nice_length, and max_chain parameters.
+
+     deflateTune() can be called after deflateInit() or deflateInit2(), and
+   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+                                       uLong sourceLen));
+/*
+     deflateBound() returns an upper bound on the compressed size after
+   deflation of sourceLen bytes.  It must be called after deflateInit()
+   or deflateInit2().  This would be used to allocate an output buffer
+   for deflation in a single pass, and so would be called before deflate().
+*/
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     deflatePrime() inserts bits in the deflate output stream.  The intent
+  is that this function is used to start off the deflate output with the
+  bits leftover from a previous deflate stream when appending to it.  As such,
+  this function can only be used for raw deflate, and must be used before the
+  first deflate() call after a deflateInit2() or deflateReset().  bits must be
+  less than or equal to 16, and that many of the least significant bits of
+  value will be inserted in the output.
+
+      deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+                                         gz_headerp head));
+/*
+      deflateSetHeader() provides gzip header information for when a gzip
+   stream is requested by deflateInit2().  deflateSetHeader() may be called
+   after deflateInit2() or deflateReset() and before the first call of
+   deflate().  The text, time, os, extra field, name, and comment information
+   in the provided gz_header structure are written to the gzip header (xflag is
+   ignored -- the extra flags are set according to the compression level).  The
+   caller must assure that, if not Z_NULL, name and comment are terminated with
+   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+   available there.  If hcrc is true, a gzip header crc is included.  Note that
+   the current versions of the command-line version of gzip (up through version
+   1.3.x) do not support header crc's, and will report that it is a "multi-part
+   gzip file" and give up.
+
+      If deflateSetHeader is not used, the default gzip header has text false,
+   the time set to zero, and os set to 255, with no extra, name, or comment
+   fields.  The gzip header is returned to the default state by deflateReset().
+
+      deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+                                     int  windowBits));
+
+     This is another version of inflateInit with an extra parameter. The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library. The default value is 15 if inflateInit is used
+   instead. windowBits must be greater than or equal to the windowBits value
+   provided to deflateInit2() while compressing, or it must be equal to 15 if
+   deflateInit2() was not used. If a compressed stream with a larger window
+   size is given as input, inflate() will return with the error code
+   Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     windowBits can also be -8..-15 for raw inflate. In this case, -windowBits
+   determines the window size. inflate() will then process raw deflate data,
+   not looking for a zlib or gzip header, not generating a check value, and not
+   looking for any check values for comparison at the end of the stream. This
+   is for use with other formats that use the deflate compressed data format
+   such as zip.  Those formats provide their own check values. If a custom
+   format is developed using the raw deflate format for compressed data, it is
+   recommended that a check value such as an adler32 or a crc32 be applied to
+   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
+   most applications, the zlib format should be used as is. Note that comments
+   above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+     windowBits can also be greater than 15 for optional gzip decoding. Add
+   32 to windowBits to enable zlib and gzip decoding with automatic header
+   detection, or add 16 to decode only the gzip format (the zlib format will
+   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is
+   a crc32 instead of an adler32.
+
+     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg
+   is set to null if there is no error message.  inflateInit2 does not perform
+   any decompression apart from reading the zlib header if present: this will
+   be done by inflate(). (So next_in and avail_in may be modified, but next_out
+   and avail_out are unchanged.)
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence. This function must be called immediately after a call of inflate,
+   if that call returned Z_NEED_DICT. The dictionary chosen by the compressor
+   can be determined from the adler32 value returned by that call of inflate.
+   The compressor and decompressor must use exactly the same dictionary (see
+   deflateSetDictionary).  For raw inflate, this function can be called
+   immediately after inflateInit2() or inflateReset() and before any call of
+   inflate() to set the dictionary.  The application must insure that the
+   dictionary that was used for compression is provided.
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (such as NULL dictionary) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect adler32 value). inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+    Skips invalid compressed data until a full flush point (see above the
+  description of deflate with Z_FULL_FLUSH) can be found, or until all
+  available input is skipped. No output is provided.
+
+    inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR
+  if no more input was provided, Z_DATA_ERROR if no flush point has been found,
+  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of total_in which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call inflateSync, providing more input each time,
+  until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when randomly accessing a large stream.  The
+   first pass through the stream can periodically record the inflate state,
+   allowing restarting inflate at those points when randomly accessing the
+   stream.
+
+     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being NULL). msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.
+   The stream will keep attributes that may have been set by inflateInit2.
+
+      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     This function inserts bits in the inflate input stream.  The intent is
+  that this function is used to start inflating at a bit position in the
+  middle of a byte.  The provided bits will be used before any bytes are used
+  from next_in.  This function should only be used with raw inflate, and
+  should be used before the first inflate() call after inflateInit2() or
+  inflateReset().  bits must be less than or equal to 16, and that many of the
+  least significant bits of value will be inserted in the input.
+
+      inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+                                         gz_headerp head));
+/*
+      inflateGetHeader() requests that gzip header information be stored in the
+   provided gz_header structure.  inflateGetHeader() may be called after
+   inflateInit2() or inflateReset(), and before the first call of inflate().
+   As inflate() processes the gzip stream, head->done is zero until the header
+   is completed, at which time head->done is set to one.  If a zlib stream is
+   being decoded, then head->done is set to -1 to indicate that there will be
+   no gzip header information forthcoming.  Note that Z_BLOCK can be used to
+   force inflate() to return immediately after header processing is complete
+   and before any actual data is decompressed.
+
+      The text, time, xflags, and os fields are filled in with the gzip header
+   contents.  hcrc is set to true if there is a header CRC.  (The header CRC
+   was valid if done is set to one.)  If extra is not Z_NULL, then extra_max
+   contains the maximum number of bytes to write to extra.  Once done is true,
+   extra_len contains the actual extra field length, and extra contains the
+   extra field, or that field truncated if extra_max is less than extra_len.
+   If name is not Z_NULL, then up to name_max characters are written there,
+   terminated with a zero unless the length is greater than name_max.  If
+   comment is not Z_NULL, then up to comm_max characters are written there,
+   terminated with a zero unless the length is greater than comm_max.  When
+   any of extra, name, or comment are not Z_NULL and the respective field is
+   not present in the header, then that field is set to Z_NULL to signal its
+   absence.  This allows the use of deflateSetHeader() with the returned
+   structure to duplicate the header.  However if those fields are set to
+   allocated memory, then the application will need to save those pointers
+   elsewhere so that they can be eventually freed.
+
+      If inflateGetHeader is not used, then the header information is simply
+   discarded.  The header is always checked for validity, including the header
+   CRC if present.  inflateReset() will reset the process to discard the header
+   information.  The application would need to call inflateGetHeader() again to
+   retrieve the header from the next gzip stream.
+
+      inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+                                        unsigned char FAR *window));
+
+     Initialize the internal stream state for decompression using inflateBack()
+   calls.  The fields zalloc, zfree and opaque in strm must be initialized
+   before the call.  If zalloc and zfree are Z_NULL, then the default library-
+   derived memory allocation routines are used.  windowBits is the base two
+   logarithm of the window size, in the range 8..15.  window is a caller
+   supplied buffer of that size.  Except for special applications where it is
+   assured that deflate was used with small window sizes, windowBits must be 15
+   and a 32K byte window must be supplied to be able to decompress general
+   deflate streams.
+
+     See inflateBack() for the usage of these routines.
+
+     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+   the paramaters are invalid, Z_MEM_ERROR if the internal state could not
+   be allocated, or Z_VERSION_ERROR if the version of the library does not
+   match the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+                                    in_func in, void FAR *in_desc,
+                                    out_func out, void FAR *out_desc));
+/*
+     inflateBack() does a raw inflate with a single call using a call-back
+   interface for input and output.  This is more efficient than inflate() for
+   file i/o applications in that it avoids copying between the output and the
+   sliding window by simply making the window itself the output buffer.  This
+   function trusts the application to not change the output buffer passed by
+   the output function, at least until inflateBack() returns.
+
+     inflateBackInit() must be called first to allocate the internal state
+   and to initialize the state with the user-provided window buffer.
+   inflateBack() may then be used multiple times to inflate a complete, raw
+   deflate stream with each call.  inflateBackEnd() is then called to free
+   the allocated state.
+
+     A raw deflate stream is one with no zlib or gzip header or trailer.
+   This routine would normally be used in a utility that reads zip or gzip
+   files and writes out uncompressed files.  The utility would decode the
+   header and process the trailer on its own, hence this routine expects
+   only the raw deflate stream to decompress.  This is different from the
+   normal behavior of inflate(), which expects either a zlib or gzip header and
+   trailer around the deflate stream.
+
+     inflateBack() uses two subroutines supplied by the caller that are then
+   called by inflateBack() for input and output.  inflateBack() calls those
+   routines until it reads a complete deflate stream and writes out all of the
+   uncompressed data, or until it encounters an error.  The function's
+   parameters and return types are defined above in the in_func and out_func
+   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
+   number of bytes of provided input, and a pointer to that input in buf.  If
+   there is no input available, in() must return zero--buf is ignored in that
+   case--and inflateBack() will return a buffer error.  inflateBack() will call
+   out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].  out()
+   should return zero on success, or non-zero on failure.  If out() returns
+   non-zero, inflateBack() will return with an error.  Neither in() nor out()
+   are permitted to change the contents of the window provided to
+   inflateBackInit(), which is also the buffer that out() uses to write from.
+   The length written by out() will be at most the window size.  Any non-zero
+   amount of input may be provided by in().
+
+     For convenience, inflateBack() can be provided input on the first call by
+   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
+   in() will be called.  Therefore strm->next_in must be initialized before
+   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
+   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
+   must also be initialized, and then if strm->avail_in is not zero, input will
+   initially be taken from strm->next_in[0 .. strm->avail_in - 1].
+
+     The in_desc and out_desc parameters of inflateBack() is passed as the
+   first parameter of in() and out() respectively when they are called.  These
+   descriptors can be optionally used to pass any information that the caller-
+   supplied in() and out() functions need to do their job.
+
+     On return, inflateBack() will set strm->next_in and strm->avail_in to
+   pass back any unused input that was provided by the last in() call.  The
+   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+   if in() or out() returned an error, Z_DATA_ERROR if there was a format
+   error in the deflate stream (in which case strm->msg is set to indicate the
+   nature of the error), or Z_STREAM_ERROR if the stream was not properly
+   initialized.  In the case of Z_BUF_ERROR, an input or output error can be
+   distinguished using strm->next_in which will be Z_NULL only if in() returned
+   an error.  If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to
+   out() returning non-zero.  (in() will always be called before out(), so
+   strm->next_in is assured to be defined if out() returns non-zero.)  Note
+   that inflateBack() cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+     All memory allocated by inflateBackInit() is freed.
+
+     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+   state was inconsistent.
+*/
+
+ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));
+/* Return flags indicating compile-time options.
+
+    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+     1.0: size of uInt
+     3.2: size of uLong
+     5.4: size of voidpf (pointer)
+     7.6: size of z_off_t
+
+    Compiler, assembler, and debug options:
+     8: DEBUG
+     9: ASMV or ASMINF -- use ASM code
+     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+     11: 0 (reserved)
+
+    One-time table building (smaller code, but not thread-safe if true):
+     12: BUILDFIXED -- build static block decoding tables when needed
+     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+     14,15: 0 (reserved)
+
+    Library content (indicates missing functionality):
+     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+                          deflate code when not needed)
+     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+                    and decode gzip streams (to avoid linking crc code)
+     18-19: 0 (reserved)
+
+    Operation variations (changes in library functionality):
+     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+     21: FASTEST -- deflate algorithm with only one, lowest compression level
+     22,23: 0 (reserved)
+
+    The sprintf variant used by gzprintf (zero is best):
+     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+    Remainder:
+     27-31: 0 (reserved)
+ */
+
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the
+   basic stream-oriented functions. To simplify the interface, some
+   default options are assumed (compression level and memory usage,
+   standard memory allocation functions). The source code of these
+   utility functions can easily be modified if you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
+                                 const Bytef *source, uLong sourceLen));
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be at least the value returned
+   by compressBound(sourceLen). Upon exit, destLen is the actual size of the
+   compressed buffer.
+     This function can be used to compress a whole file at once if the
+   input file is mmap'ed.
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
+                                  const Bytef *source, uLong sourceLen,
+                                  int level));
+/*
+     Compresses the source buffer into the destination buffer. The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer. Upon entry, destLen is the total size of the
+   destination buffer, which must be at least the value returned by
+   compressBound(sourceLen). Upon exit, destLen is the actual size of the
+   compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+     compressBound() returns an upper bound on the compressed size after
+   compress() or compress2() on sourceLen bytes.  It would be used before
+   a compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer. Upon entry, destLen is the total
+   size of the destination buffer, which must be large enough to hold the
+   entire uncompressed data. (The size of the uncompressed data must have
+   been saved previously by the compressor and transmitted to the decompressor
+   by some mechanism outside the scope of this compression library.)
+   Upon exit, destLen is the actual size of the compressed buffer.
+     This function can be used to decompress a whole file at once if the
+   input file is mmap'ed.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.
+*/
+
+
+typedef voidp gzFile;
+
+ZEXTERN gzFile ZEXPORT gzopen  OF((const char *path, const char *mode));
+/*
+     Opens a gzip (.gz) file for reading or writing. The mode parameter
+   is as in fopen ("rb" or "wb") but can also include a compression level
+   ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for
+   Huffman only compression as in "wb1h", or 'R' for run-length encoding
+   as in "wb1R". (See the description of deflateInit2 for more information
+   about the strategy parameter.)
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.
+
+     gzopen returns NULL if the file could not be opened or if there was
+   insufficient memory to allocate the (de)compression state; errno
+   can be checked to distinguish the two cases (if errno is zero, the
+   zlib error is Z_MEM_ERROR).  */
+
+ZEXTERN gzFile ZEXPORT gzdopen  OF((int fd, const char *mode));
+/*
+     gzdopen() associates a gzFile with the file descriptor fd.  File
+   descriptors are obtained from calls like open, dup, creat, pipe or
+   fileno (in the file has been previously opened with fopen).
+   The mode parameter is as in gzopen.
+     The next call of gzclose on the returned gzFile will also close the
+   file descriptor fd, just like fclose(fdopen(fd), mode) closes the file
+   descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode).
+     gzdopen returns NULL if there was insufficient memory to allocate
+   the (de)compression state.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+     Dynamically update the compression level or strategy. See the description
+   of deflateInit2 for the meaning of these parameters.
+     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+   opened for writing.
+*/
+
+ZEXTERN int ZEXPORT    gzread  OF((gzFile file, voidp buf, unsigned len));
+/*
+     Reads the given number of uncompressed bytes from the compressed file.
+   If the input file was not in gzip format, gzread copies the given number
+   of bytes into the buffer.
+     gzread returns the number of uncompressed bytes actually read (0 for
+   end of file, -1 for error). */
+
+ZEXTERN int ZEXPORT    gzwrite OF((gzFile file,
+                                   voidpc buf, unsigned len));
+/*
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of uncompressed bytes actually written
+   (0 in case of error).
+*/
+
+ZEXTERN int ZEXPORTVA   gzprintf OF((gzFile file, const char *format, ...));
+/*
+     Converts, formats, and writes the args to the compressed file under
+   control of the format string, as in fprintf. gzprintf returns the number of
+   uncompressed bytes actually written (0 in case of error).  The number of
+   uncompressed bytes written is limited to 4095. The caller should assure that
+   this limit is not exceeded. If it is exceeded, then gzprintf() will return
+   return an error (0) with nothing written. In this case, there may also be a
+   buffer overflow with unpredictable consequences, which is possible only if
+   zlib was compiled with the insecure functions sprintf() or vsprintf()
+   because the secure snprintf() or vsnprintf() functions were not available.
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+      Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+      gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+      Reads bytes from the compressed file until len-1 characters are read, or
+   a newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  The string is then terminated with a null
+   character.
+      gzgets returns buf, or Z_NULL in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzputc OF((gzFile file, int c));
+/*
+      Writes c, converted to an unsigned char, into the compressed file.
+   gzputc returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT    gzgetc OF((gzFile file));
+/*
+      Reads one byte from the compressed file. gzgetc returns this byte
+   or -1 in case of end of file or error.
+*/
+
+ZEXTERN int ZEXPORT    gzungetc OF((int c, gzFile file));
+/*
+      Push one character back onto the stream to be read again later.
+   Only one character of push-back is allowed.  gzungetc() returns the
+   character pushed, or -1 on failure.  gzungetc() will fail if a
+   character has been pushed but not read yet, or if c is -1. The pushed
+   character will be discarded if the stream is repositioned with gzseek()
+   or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT    gzflush OF((gzFile file, int flush));
+/*
+     Flushes all pending output into the compressed file. The parameter
+   flush is as in the deflate() function. The return value is the zlib
+   error number (see function gzerror below). gzflush returns Z_OK if
+   the flush parameter is Z_FINISH and all output could be flushed.
+     gzflush should be called only when strictly necessary because it can
+   degrade compression.
+*/
+
+ZEXTERN z_off_t ZEXPORT    gzseek OF((gzFile file,
+                                      z_off_t offset, int whence));
+/*
+      Sets the starting position for the next gzread or gzwrite on the
+   given compressed file. The offset represents a number of bytes in the
+   uncompressed data stream. The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow. If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+      gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
+/*
+     Rewinds the given file. This function is supported only for reading.
+
+   gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
+/*
+     Returns the starting position for the next gzread or gzwrite on the
+   given compressed file. This position represents a number of bytes in the
+   uncompressed data stream.
+
+   gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+     Returns 1 when EOF has previously been detected reading the given
+   input stream, otherwise zero.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+     Returns 1 if file is being read directly without decompression, otherwise
+   zero.
+*/
+
+ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
+/*
+     Flushes all pending output if necessary, closes the compressed file
+   and deallocates all the (de)compression state. The return value is the zlib
+   error number (see function gzerror below).
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+     Returns the error message for the last error which occurred on the
+   given compressed file. errnum is set to zlib error number. If an
+   error occurred in the file system and not in the compression library,
+   errnum is set to Z_ERRNO and the application may consult errno
+   to get the exact error code.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+     Clears the error and end-of-file flags for file. This is analogous to the
+   clearerr() function in stdio. This is useful for continuing to read a gzip
+   file that is being written concurrently.
+*/
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the
+   compression library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+                                          z_off_t len2));
+/*
+     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1
+   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of
+   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.
+*/
+
+ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
+/*
+     Update a running CRC-32 with the bytes buf[0..len-1] and return the
+   updated CRC-32. If buf is NULL, this function returns the required initial
+   value for the for the crc. Pre- and post-conditioning (one's complement) is
+   performed within this function so it shouldn't be done by the application.
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+/*
+     Combine two CRC-32 check values into one.  For two sequences of bytes,
+   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
+   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+   len2.
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
+                                      int windowBits, int memLevel,
+                                      int strategy, const char *version,
+                                      int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
+                                      const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+                                         unsigned char FAR *window,
+                                         const char *version,
+                                         int stream_size));
+#define deflateInit(strm, level) \
+        deflateInit_((strm), (level),       ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit(strm) \
+        inflateInit_((strm),                ZLIB_VERSION, sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                      (strategy),           ZLIB_VERSION, sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+        inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+        inflateBackInit_((strm), (windowBits), (window), \
+        ZLIB_VERSION, sizeof(z_stream))
+
+
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+    struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+ZEXTERN const char   * ZEXPORT zError           OF((int));
+ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp z));
+ZEXTERN const uLongf * ZEXPORT get_crc_table    OF((void));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/distrib/zlib-1.2.3/zutil.c b/distrib/zlib-1.2.3/zutil.c
new file mode 100644
index 0000000..d55f594
--- /dev/null
+++ b/distrib/zlib-1.2.3/zutil.c
@@ -0,0 +1,318 @@
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#include "zutil.h"
+
+#ifndef NO_DUMMY_DECL
+struct internal_state      {int dummy;}; /* for buggy compilers */
+#endif
+
+const char * const z_errmsg[10] = {
+"need dictionary",     /* Z_NEED_DICT       2  */
+"stream end",          /* Z_STREAM_END      1  */
+"",                    /* Z_OK              0  */
+"file error",          /* Z_ERRNO         (-1) */
+"stream error",        /* Z_STREAM_ERROR  (-2) */
+"data error",          /* Z_DATA_ERROR    (-3) */
+"insufficient memory", /* Z_MEM_ERROR     (-4) */
+"buffer error",        /* Z_BUF_ERROR     (-5) */
+"incompatible version",/* Z_VERSION_ERROR (-6) */
+""};
+
+
+const char * ZEXPORT zlibVersion()
+{
+    return ZLIB_VERSION;
+}
+
+uLong ZEXPORT zlibCompileFlags()
+{
+    uLong flags;
+
+    flags = 0;
+    switch (sizeof(uInt)) {
+    case 2:     break;
+    case 4:     flags += 1;     break;
+    case 8:     flags += 2;     break;
+    default:    flags += 3;
+    }
+    switch (sizeof(uLong)) {
+    case 2:     break;
+    case 4:     flags += 1 << 2;        break;
+    case 8:     flags += 2 << 2;        break;
+    default:    flags += 3 << 2;
+    }
+    switch (sizeof(voidpf)) {
+    case 2:     break;
+    case 4:     flags += 1 << 4;        break;
+    case 8:     flags += 2 << 4;        break;
+    default:    flags += 3 << 4;
+    }
+    switch (sizeof(z_off_t)) {
+    case 2:     break;
+    case 4:     flags += 1 << 6;        break;
+    case 8:     flags += 2 << 6;        break;
+    default:    flags += 3 << 6;
+    }
+#ifdef DEBUG
+    flags += 1 << 8;
+#endif
+#if defined(ASMV) || defined(ASMINF)
+    flags += 1 << 9;
+#endif
+#ifdef ZLIB_WINAPI
+    flags += 1 << 10;
+#endif
+#ifdef BUILDFIXED
+    flags += 1 << 12;
+#endif
+#ifdef DYNAMIC_CRC_TABLE
+    flags += 1 << 13;
+#endif
+#ifdef NO_GZCOMPRESS
+    flags += 1L << 16;
+#endif
+#ifdef NO_GZIP
+    flags += 1L << 17;
+#endif
+#ifdef PKZIP_BUG_WORKAROUND
+    flags += 1L << 20;
+#endif
+#ifdef FASTEST
+    flags += 1L << 21;
+#endif
+#ifdef STDC
+#  ifdef NO_vsnprintf
+        flags += 1L << 25;
+#    ifdef HAS_vsprintf_void
+        flags += 1L << 26;
+#    endif
+#  else
+#    ifdef HAS_vsnprintf_void
+        flags += 1L << 26;
+#    endif
+#  endif
+#else
+        flags += 1L << 24;
+#  ifdef NO_snprintf
+        flags += 1L << 25;
+#    ifdef HAS_sprintf_void
+        flags += 1L << 26;
+#    endif
+#  else
+#    ifdef HAS_snprintf_void
+        flags += 1L << 26;
+#    endif
+#  endif
+#endif
+    return flags;
+}
+
+#ifdef DEBUG
+
+#  ifndef verbose
+#    define verbose 0
+#  endif
+int z_verbose = verbose;
+
+void z_error (m)
+    char *m;
+{
+    fprintf(stderr, "%s\n", m);
+    exit(1);
+}
+#endif
+
+/* exported to allow conversion of error code to string for compress() and
+ * uncompress()
+ */
+const char * ZEXPORT zError(err)
+    int err;
+{
+    return ERR_MSG(err);
+}
+
+#if defined(_WIN32_WCE)
+    /* The Microsoft C Run-Time Library for Windows CE doesn't have
+     * errno.  We define it as a global variable to simplify porting.
+     * Its value is always 0 and should not be used.
+     */
+    int errno = 0;
+#endif
+
+#ifndef HAVE_MEMCPY
+
+void zmemcpy(dest, source, len)
+    Bytef* dest;
+    const Bytef* source;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = *source++; /* ??? to be unrolled */
+    } while (--len != 0);
+}
+
+int zmemcmp(s1, s2, len)
+    const Bytef* s1;
+    const Bytef* s2;
+    uInt  len;
+{
+    uInt j;
+
+    for (j = 0; j < len; j++) {
+        if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1;
+    }
+    return 0;
+}
+
+void zmemzero(dest, len)
+    Bytef* dest;
+    uInt  len;
+{
+    if (len == 0) return;
+    do {
+        *dest++ = 0;  /* ??? to be unrolled */
+    } while (--len != 0);
+}
+#endif
+
+
+#ifdef SYS16BIT
+
+#ifdef __TURBOC__
+/* Turbo C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+/* Turbo C malloc() does not allow dynamic allocation of 64K bytes
+ * and farmalloc(64K) returns a pointer with an offset of 8, so we
+ * must fix the pointer. Warning: the pointer must be put back to its
+ * original form in order to free it, use zcfree().
+ */
+
+#define MAX_PTR 10
+/* 10*64K = 640K */
+
+local int next_ptr = 0;
+
+typedef struct ptr_table_s {
+    voidpf org_ptr;
+    voidpf new_ptr;
+} ptr_table;
+
+local ptr_table table[MAX_PTR];
+/* This table is used to remember the original form of pointers
+ * to large buffers (64K). Such pointers are normalized with a zero offset.
+ * Since MSDOS is not a preemptive multitasking OS, this table is not
+ * protected from concurrent access. This hack doesn't work anyway on
+ * a protected system like OS/2. Use Microsoft C instead.
+ */
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    voidpf buf = opaque; /* just to make some compilers happy */
+    ulg bsize = (ulg)items*size;
+
+    /* If we allocate less than 65520 bytes, we assume that farmalloc
+     * will return a usable pointer which doesn't have to be normalized.
+     */
+    if (bsize < 65520L) {
+        buf = farmalloc(bsize);
+        if (*(ush*)&buf != 0) return buf;
+    } else {
+        buf = farmalloc(bsize + 16L);
+    }
+    if (buf == NULL || next_ptr >= MAX_PTR) return NULL;
+    table[next_ptr].org_ptr = buf;
+
+    /* Normalize the pointer to seg:0 */
+    *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4;
+    *(ush*)&buf = 0;
+    table[next_ptr++].new_ptr = buf;
+    return buf;
+}
+
+void  zcfree (voidpf opaque, voidpf ptr)
+{
+    int n;
+    if (*(ush*)&ptr != 0) { /* object < 64K */
+        farfree(ptr);
+        return;
+    }
+    /* Find the original pointer */
+    for (n = 0; n < next_ptr; n++) {
+        if (ptr != table[n].new_ptr) continue;
+
+        farfree(table[n].org_ptr);
+        while (++n < next_ptr) {
+            table[n-1] = table[n];
+        }
+        next_ptr--;
+        return;
+    }
+    ptr = opaque; /* just to make some compilers happy */
+    Assert(0, "zcfree: ptr not found");
+}
+
+#endif /* __TURBOC__ */
+
+
+#ifdef M_I86
+/* Microsoft C in 16-bit mode */
+
+#  define MY_ZCALLOC
+
+#if (!defined(_MSC_VER) || (_MSC_VER <= 600))
+#  define _halloc  halloc
+#  define _hfree   hfree
+#endif
+
+voidpf zcalloc (voidpf opaque, unsigned items, unsigned size)
+{
+    if (opaque) opaque = 0; /* to make compiler happy */
+    return _halloc((long)items, size);
+}
+
+void  zcfree (voidpf opaque, voidpf ptr)
+{
+    if (opaque) opaque = 0; /* to make compiler happy */
+    _hfree(ptr);
+}
+
+#endif /* M_I86 */
+
+#endif /* SYS16BIT */
+
+
+#ifndef MY_ZCALLOC /* Any system without a special alloc function */
+
+#ifndef STDC
+extern voidp  malloc OF((uInt size));
+extern voidp  calloc OF((uInt items, uInt size));
+extern void   free   OF((voidpf ptr));
+#endif
+
+voidpf zcalloc (opaque, items, size)
+    voidpf opaque;
+    unsigned items;
+    unsigned size;
+{
+    if (opaque) items += size - size; /* make compiler happy */
+    return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) :
+                              (voidpf)calloc(items, size);
+}
+
+void  zcfree (opaque, ptr)
+    voidpf opaque;
+    voidpf ptr;
+{
+    free(ptr);
+    if (opaque) return; /* make compiler happy */
+}
+
+#endif /* MY_ZCALLOC */
diff --git a/distrib/zlib-1.2.3/zutil.h b/distrib/zlib-1.2.3/zutil.h
new file mode 100644
index 0000000..b7d5eff
--- /dev/null
+++ b/distrib/zlib-1.2.3/zutil.h
@@ -0,0 +1,269 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#define ZLIB_INTERNAL
+#include "zlib.h"
+
+#ifdef STDC
+#  ifndef _WIN32_WCE
+#    include <stddef.h>
+#  endif
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+#   ifdef _WIN32_WCE
+      /* The Microsoft C Run-Time Library for Windows CE doesn't have
+       * errno.  We define it as a global variable to simplify porting.
+       * Its value is always 0 and should not be used.  We rename it to
+       * avoid conflict with other libraries that use the same workaround.
+       */
+#     define errno z_errno
+#   endif
+    extern int errno;
+#else
+#  ifndef _WIN32_WCE
+#    include <errno.h>
+#  endif
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+  return (strm->msg = (char*)ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+        /* common constants */
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+        /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+#  define OS_CODE  0x00
+#  if defined(__TURBOC__) || defined(__BORLANDC__)
+#    if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+       /* Allow compilation with ANSI keywords only enabled */
+       void _Cdecl farfree( void *block );
+       void *_Cdecl farmalloc( unsigned long nbytes );
+#    else
+#      include <alloc.h>
+#    endif
+#  else /* MSC or DJGPP */
+#    include <malloc.h>
+#  endif
+#endif
+
+#ifdef AMIGA
+#  define OS_CODE  0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+#  define OS_CODE  0x02
+#  define F_OPEN(name, mode) \
+     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+#  define OS_CODE  0x05
+#endif
+
+#ifdef OS2
+#  define OS_CODE  0x06
+#  ifdef M_I86
+     #include <malloc.h>
+#  endif
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+#  define OS_CODE  0x07
+#  if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#    include <unix.h> /* for fdopen */
+#  else
+#    ifndef fdopen
+#      define fdopen(fd,mode) NULL /* No fdopen() */
+#    endif
+#  endif
+#endif
+
+#ifdef TOPS20
+#  define OS_CODE  0x0a
+#endif
+
+#ifdef WIN32
+#  ifndef __CYGWIN__  /* Cygwin is Unix, not Win32 */
+#    define OS_CODE  0x0b
+#  endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+#  define OS_CODE  0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+#  define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600))
+#  if defined(_WIN32_WCE)
+#    define fdopen(fd,mode) NULL /* No fdopen() */
+#    ifndef _PTRDIFF_T_DEFINED
+       typedef int ptrdiff_t;
+#      define _PTRDIFF_T_DEFINED
+#    endif
+#  else
+#    define fdopen(fd,type)  _fdopen(fd,type)
+#  endif
+#endif
+
+        /* common defaults */
+
+#ifndef OS_CODE
+#  define OS_CODE  0x03  /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+#  define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+         /* functions */
+
+#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+#if defined(__CYGWIN__)
+#  ifndef HAVE_VSNPRINTF
+#    define HAVE_VSNPRINTF
+#  endif
+#endif
+#ifndef HAVE_VSNPRINTF
+#  ifdef MSDOS
+     /* vsnprintf may exist on some MS-DOS compilers (DJGPP?),
+        but for now we just assume it doesn't. */
+#    define NO_vsnprintf
+#  endif
+#  ifdef __TURBOC__
+#    define NO_vsnprintf
+#  endif
+#  ifdef WIN32
+     /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
+#    if !defined(vsnprintf) && !defined(NO_vsnprintf)
+#      define vsnprintf _vsnprintf
+#    endif
+#  endif
+#  ifdef __SASC
+#    define NO_vsnprintf
+#  endif
+#endif
+#ifdef VMS
+#  define NO_vsnprintf
+#endif
+
+#if defined(pyr)
+#  define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+  * You may have to use the same strategy for Borland C (untested).
+  * The __SC__ check is for Symantec.
+  */
+#  define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+#  define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+#    define zmemcpy _fmemcpy
+#    define zmemcmp _fmemcmp
+#    define zmemzero(dest, len) _fmemset(dest, 0, len)
+#  else
+#    define zmemcpy memcpy
+#    define zmemcmp memcmp
+#    define zmemzero(dest, len) memset(dest, 0, len)
+#  endif
+#else
+   extern void zmemcpy  OF((Bytef* dest, const Bytef* source, uInt len));
+   extern int  zmemcmp  OF((const Bytef* s1, const Bytef* s2, uInt len));
+   extern void zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  include <stdio.h>
+   extern int z_verbose;
+   extern void z_error    OF((char *m));
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) {if (z_verbose>=0) fprintf x ;}
+#  define Tracev(x) {if (z_verbose>0) fprintf x ;}
+#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+
+voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size));
+void   zcfree  OF((voidpf opaque, voidpf ptr));
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+#endif /* ZUTIL_H */
diff --git a/docs/AUDIO.TXT b/docs/AUDIO.TXT
new file mode 100644
index 0000000..1a25b77
--- /dev/null
+++ b/docs/AUDIO.TXT
@@ -0,0 +1,160 @@
+HOW AUDIO EMULATION WORKS IN QEMU:
+==================================
+
+Things are a bit tricky, but here's a rough description:
+
+  QEMUSoundCard: models a given emulated sound card
+  SWVoiceOut:    models an audio output from a QEMUSoundCard
+  SWVoiceIn:     models an audio input from a QEMUSoundCard
+
+  HWVoiceOut:    models an audio output (backend) on the host.
+  HWVoiceIn:     models an audio input (backend) on the host.
+
+Each voice can have its own settings in terms of sample size, endianess, rate, etc...
+
+
+Emulation for a given soundcard typically does:
+
+  1/ Create a QEMUSoundCard object and register it with AUD_register_card()
+  2/ For each emulated output, call AUD_open_out() to create a SWVoiceOut object.
+  3/ For each emulated input, call AUD_open_in() to create a SWVoiceIn object.
+
+  Note that you must pass a callback function to AUD_open_out() and AUD_open_in();
+  more on this later.
+
+  Each SWVoiceOut is associated to a single HWVoiceOut, each SWVoiceIn is
+  associated to a single HWVoiceIn.
+
+  However you can have several SWVoiceOut associated to the same HWVoiceOut
+  (same thing for SWVoiceIn/HWVoiceIn).
+
+SOUND PLAYBACK DETAILS:
+=======================
+
+Each HWVoiceOut has the following too:
+
+  - A fixed-size circular buffer of stereo samples (for stereo).
+    whose format is either floats or int64_t per sample (depending on build configuration).
+
+  - A 'samples' field giving the (constant) number of sample pairs in the stereo buffer.
+
+  - A target conversion function, called 'clip()' that is used to read from the stereo
+    buffer and write into a platform-specific sound buffers (e.g. WinWave-managed buffers
+    on Windows).
+
+  - A 'rpos' offset into the circular buffer which tells where to read the next samples
+    from the stereo buffer for the next conversion through 'clip'.
+
+  - A 'run_out' method that is called each time to tell the output backend to
+    send samples from the stereo buffer to the host sound card/server. This method
+    shall also modify 'rpos' and returns the number of samples 'played'. A more detailed
+    description of this process appears below.
+
+  - A 'write' method callback used to write a buffer of emulated sound samples from
+    a SWVoiceOut into the stereo buffer. *All* backends simply call the generic
+    function audio_pcm_sw_write() to implement this. It's difficult to see why
+    it's needed at all ?
+
+    (Similarly, all backends have a 'read' methods which simply calls 'audio_pcm_sw_read')
+
+Each SWVoiceOut has the following:
+
+  - a 'conv()' function used to read sound samples from the emulated sound card and
+    copy/mix them to the corresponding HWVoiceOut's stereo buffer.
+
+  - a 'total_hw_samples_mixed' which correspond to the number of samples that have
+    already been mixed into the target HWVoiceOut stereo buffer (starting from the
+    HWVoiceOut's 'rpos' offset). NOTE: this is a count of samples in the HWVoiceOut
+    stereo buffer, not emulated hardware sound samples, which can have different
+    properties (frequency, size, endianess).
+
+  - a 'ratio' value, which is the ratio of the target HWVoiceOut's frequency by
+    the SWVoiceOut's frequency, multiplied by (1 << 32), as a 64-bit integer.
+
+    So, if the HWVoiceOut has a frequency of 44kHz, and the SWVoiceOut has a frequency
+    of 11kHz, then ratio will be (44/11*(1 << 32)) = 0x4_0000_0000
+
+  - a callback provided by the emulated hardware when the SWVoiceOut is created.
+    This function is used to mix the SWVoiceOut's samples into the target
+    HWVoiceOut stereo buffer (it must also perform frequency interpolation,
+    volume adjustment, etc..).
+
+    This callback normally calls another helper functions in the audio subsystem
+    (AUD_write()) to to the mixing/volume-adjustment from emulated hardware sample
+    buffers.
+
+Here's a small graphics that explains it better:
+
+   SWVoiceOut:  emulated hardware sound buffers:
+
+          |
+          |   (mixed through AUD_write() from user-provided callback)
+          |
+          v
+
+   HWVoiceOut: stereo sample circular buffer
+
+          |
+          |   (through HWVoiceOut's 'clip' function, invoked from the
+          |    'run_out' method)
+          v
+
+   backend-specific sound buffers
+
+THERE IS NO COMMON TIMEBASE BETWEEN ALL LAYERS. DON'T EXPECT ANY HIGH-ACCURACY /
+LOW-LATENCY IN THIS IMPLEMENTATION.
+
+
+The function audio_timer() in audio/audio.c is called periodically and it is used as
+a pulse to perform sound buffer transfers and mixing. More specifically for audio
+output voices:
+
+- For each HWVoiceOut, find the number of active SWVoiceOut, and the minimum number
+  of 'total_hw_samples_mixed' that have already been written to the buffer. We will
+  call this value the number of 'live' samples in the stereo buffer.
+
+- if 'live' is 0, call the callback of each active SWVoiceOut to fill the stereo
+  buffer, if needed, then exit.
+
+- otherwise, call the 'run_out' method of the HWVoiceOut object. This will change
+  the value of 'rpos' and return the number of samples played. Then the
+  'total_hw_samples_mixed' field of all active SWVoiceOuts is decremented by
+  'played', and the callback is called to re-fill the stereo buffer.
+
+It's important to note that the SWVoiceOut callback:
+
+- takes a 'free' parameter which is the number of emulated sound samples that can
+  be sent to the hardware stereo buffer (before rate adjustment, i.e. not the number
+  of sound samples in the SWVoiceOut emulated hardware sound buffer).
+
+- must call AUD_write(sw, buff, count), where 'buff' points to emulated sound
+  samples, and their 'count', which must be <= the 'free' parameter.
+
+- the implementation of AUD_write() will call the 'write' method of the target
+  HWVoiceOut, which in turns calls the function audio_pcm_sw_write() which does
+  standard rate/volume adjustment before mixing the conversion into the target
+  stereo buffer. It also increases the 'total_hw_samples_mixed' value of the
+  SWVoiceOut.
+
+- audio_pcm_sw_write() returns the number of sound sample *bytes* that have
+  been mixed into the stereo buffer, and so does AUD_write().
+
+So, in the end, we have the pseudo-code:
+
+    every sound timer ticks:
+      for hw in list_HWVoiceOut:
+         live = MIN([sw.total_hw_samples_mixed for sw in hw.list_SWVoiceOut ])
+         if live > 0:
+            played = hw.run_out(live)
+            for sw in hw.list_SWVoiceOut:
+                sw.total_hw_samples_mixed -= played
+
+        for sw in hw.list_SWVoiceOut:
+            free = hw.samples - sw.total_hw_samples_mixed
+            if free > 0:
+                sw.callback(sw, free)
+
+SOUND RECORDING DETAILS:
+========================
+
+Things are similar but in reverse order.
diff --git a/docs/KERNEL.TXT b/docs/KERNEL.TXT
new file mode 100644
index 0000000..7387e55
--- /dev/null
+++ b/docs/KERNEL.TXT
@@ -0,0 +1,25 @@
+HOW TO REBUILT THE ANDROID EMULATOR-SPECIFIC KERNEL:
+====================================================
+
+You need to have the Android toolchain in your path
+(i.e. 'arm-eabi-gcc --version' must work)
+
+then:
+
+git clone git://android.git.kernel.org/kernel/common.git kernel-common
+cd kernel-common
+git checkout origin/android-goldfish-2.6.27
+
+export CROSS_COMPILE=arm-eabi-
+export ARCH=arm
+export SUBARCH=arm
+make goldfish_defconfig    # configure the kernel
+make -j2                   # build it
+
+=> this generates a file named arch/arm/boot/zImage
+
+Now, you can use it with:
+
+  emulator -kernel path/to/your/new/zImage <other-options>
+
+Voila !
diff --git a/dyngen-exec.h b/dyngen-exec.h
new file mode 100644
index 0000000..9260b6f
--- /dev/null
+++ b/dyngen-exec.h
@@ -0,0 +1,305 @@
+/*
+ *  dyngen defines for micro operation code
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#if !defined(__DYNGEN_EXEC_H__)
+#define __DYNGEN_EXEC_H__
+
+/* prevent Solaris from trying to typedef FILE in gcc's
+   include/floatingpoint.h which will conflict with the
+   definition down below */
+#ifdef __sun__
+#define _FILEDEFED
+#endif
+
+/* NOTE: standard headers should be used with special care at this
+   point because host CPU registers are used as global variables. Some
+   host headers do not allow that. */
+#include <stddef.h>
+
+#ifdef __OpenBSD__
+#include <sys/types.h>
+#else
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+// Linux/Sparc64 defines uint64_t
+#if !(defined (__sparc_v9__) && defined(__linux__)) && !(defined(__APPLE__) && defined(__x86_64__))
+/* XXX may be done for all 64 bits targets ? */
+#if defined (__x86_64__) || defined(__ia64) || defined(__s390x__) || defined(__alpha__) || defined(__powerpc64__)
+typedef unsigned long uint64_t;
+#else
+typedef unsigned long long uint64_t;
+#endif
+#endif
+
+/* if Solaris/__sun__, don't typedef int8_t, as it will be typedef'd
+   prior to this and will cause an error in compliation, conflicting
+   with /usr/include/sys/int_types.h, line 75 */
+#ifndef __sun__
+typedef signed char int8_t;
+#endif
+typedef signed short int16_t;
+typedef signed int int32_t;
+// Linux/Sparc64 defines int64_t
+#if !(defined (__sparc_v9__) && defined(__linux__)) && !(defined(__APPLE__) && defined(__x86_64__))
+#if defined (__x86_64__) || defined(__ia64) || defined(__s390x__) || defined(__alpha__) || defined(__powerpc64__)
+typedef signed long int64_t;
+#else
+typedef signed long long int64_t;
+#endif
+#endif
+#endif
+
+/* XXX: This may be wrong for 64-bit ILP32 hosts.  */
+typedef void * host_reg_t;
+
+#define INT8_MIN		(-128)
+#define INT16_MIN		(-32767-1)
+#define INT32_MIN		(-2147483647-1)
+#define INT64_MIN		(-(int64_t)(9223372036854775807)-1)
+#define INT8_MAX		(127)
+#define INT16_MAX		(32767)
+#define INT32_MAX		(2147483647)
+#define INT64_MAX		((int64_t)(9223372036854775807))
+#define UINT8_MAX		(255)
+#define UINT16_MAX		(65535)
+#define UINT32_MAX		(4294967295U)
+#define UINT64_MAX		((uint64_t)(18446744073709551615))
+
+#ifdef _BSD
+typedef struct __sFILE FILE;
+#else
+typedef struct FILE FILE;
+#endif
+extern int fprintf(FILE *, const char *, ...);
+extern int fputs(const char *, FILE *);
+extern int printf(const char *, ...);
+#undef NULL
+#define NULL 0
+
+#if defined(__i386__)
+#define AREG0 "ebp"
+#define AREG1 "ebx"
+#define AREG2 "esi"
+#define AREG3 "edi"
+#elif defined(__x86_64__)
+#define AREG0 "r14"
+#define AREG1 "r15"
+#define AREG2 "r12"
+#define AREG3 "r13"
+//#define AREG4 "rbp"
+//#define AREG5 "rbx"
+#elif defined(__powerpc__)
+#define AREG0 "r27"
+#define AREG1 "r24"
+#define AREG2 "r25"
+#define AREG3 "r26"
+/* XXX: suppress this hack */
+#if defined(CONFIG_USER_ONLY)
+#define AREG4 "r16"
+#define AREG5 "r17"
+#define AREG6 "r18"
+#define AREG7 "r19"
+#define AREG8 "r20"
+#define AREG9 "r21"
+#define AREG10 "r22"
+#define AREG11 "r23"
+#endif
+#elif defined(__arm__)
+#define AREG0 "r7"
+#define AREG1 "r4"
+#define AREG2 "r5"
+#define AREG3 "r6"
+#elif defined(__hppa__)
+#define AREG0 "r17"
+#define AREG1 "r14"
+#define AREG2 "r15"
+#define AREG3 "r16"
+#elif defined(__mips__)
+#define AREG0 "fp"
+#define AREG1 "s0"
+#define AREG2 "s1"
+#define AREG3 "s2"
+#define AREG4 "s3"
+#define AREG5 "s4"
+#define AREG6 "s5"
+#define AREG7 "s6"
+#define AREG8 "s7"
+#elif defined(__sparc__)
+#ifdef HOST_SOLARIS
+#define AREG0 "g2"
+#define AREG1 "g3"
+#define AREG2 "g4"
+#define AREG3 "g5"
+#define AREG4 "g6"
+#else
+#ifdef __sparc_v9__
+#define AREG0 "g5"
+#define AREG1 "g6"
+#define AREG2 "g7"
+#else
+#define AREG0 "g6"
+#define AREG1 "g1"
+#define AREG2 "g2"
+#define AREG3 "g3"
+#define AREG4 "l0"
+#define AREG5 "l1"
+#define AREG6 "l2"
+#define AREG7 "l3"
+#define AREG8 "l4"
+#define AREG9 "l5"
+#define AREG10 "l6"
+#define AREG11 "l7"
+#endif
+#endif
+#elif defined(__s390__)
+#define AREG0 "r10"
+#define AREG1 "r7"
+#define AREG2 "r8"
+#define AREG3 "r9"
+#elif defined(__alpha__)
+/* Note $15 is the frame pointer, so anything in op-i386.c that would
+   require a frame pointer, like alloca, would probably loose.  */
+#define AREG0 "$15"
+#define AREG1 "$9"
+#define AREG2 "$10"
+#define AREG3 "$11"
+#define AREG4 "$12"
+#define AREG5 "$13"
+#define AREG6 "$14"
+#elif defined(__mc68000)
+#define AREG0 "%a5"
+#define AREG1 "%a4"
+#define AREG2 "%d7"
+#define AREG3 "%d6"
+#define AREG4 "%d5"
+#elif defined(__ia64__)
+#define AREG0 "r7"
+#define AREG1 "r4"
+#define AREG2 "r5"
+#define AREG3 "r6"
+#else
+#error unsupported CPU
+#endif
+
+/* force GCC to generate only one epilog at the end of the function */
+#define FORCE_RET() __asm__ __volatile__("" : : : "memory");
+
+#ifndef OPPROTO
+#define OPPROTO
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s)	tostring(s)
+#define tostring(s)	#s
+
+#if defined(__alpha__) || defined(__s390__)
+/* the symbols are considered non exported so a br immediate is generated */
+#define __hidden __attribute__((visibility("hidden")))
+#else
+#define __hidden
+#endif
+
+#if defined(__alpha__)
+/* Suggested by Richard Henderson. This will result in code like
+        ldah $0,__op_param1($29)        !gprelhigh
+        lda $0,__op_param1($0)          !gprellow
+   We can then conveniently change $29 to $31 and adapt the offsets to
+   emit the appropriate constant.  */
+extern int __op_param1 __hidden;
+extern int __op_param2 __hidden;
+extern int __op_param3 __hidden;
+#define PARAM1 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param1)); _r; })
+#define PARAM2 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param2)); _r; })
+#define PARAM3 ({ int _r; asm("" : "=r"(_r) : "0" (&__op_param3)); _r; })
+#elif defined(__s390__)
+extern int __op_param1 __hidden;
+extern int __op_param2 __hidden;
+extern int __op_param3 __hidden;
+#define PARAM1 ({ int _r; asm("bras %0,8; .long " ASM_NAME(__op_param1) "; l %0,0(%0)" : "=r"(_r) : ); _r; })
+#define PARAM2 ({ int _r; asm("bras %0,8; .long " ASM_NAME(__op_param2) "; l %0,0(%0)" : "=r"(_r) : ); _r; })
+#define PARAM3 ({ int _r; asm("bras %0,8; .long " ASM_NAME(__op_param3) "; l %0,0(%0)" : "=r"(_r) : ); _r; })
+#else
+#if defined(__APPLE__)
+static int __op_param1, __op_param2, __op_param3;
+#else
+extern int __op_param1, __op_param2, __op_param3;
+#endif
+#define PARAM1 ((long)(&__op_param1))
+#define PARAM2 ((long)(&__op_param2))
+#define PARAM3 ((long)(&__op_param3))
+#endif /* !defined(__alpha__) */
+
+extern int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
+
+#if defined(_WIN32) || defined(__APPLE__)
+#define ASM_NAME(x) "_" #x
+#else
+#define ASM_NAME(x) #x
+#endif
+
+#if defined(__i386__)
+#define EXIT_TB() asm volatile ("ret")
+#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
+#elif defined(__x86_64__)
+#define EXIT_TB() asm volatile ("ret")
+#define GOTO_LABEL_PARAM(n) asm volatile ("jmp " ASM_NAME(__op_gen_label) #n)
+#elif defined(__powerpc__)
+#define EXIT_TB() asm volatile ("blr")
+#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
+#elif defined(__s390__)
+#define EXIT_TB() asm volatile ("br %r14")
+#define GOTO_LABEL_PARAM(n) asm volatile ("larl %r7,12; l %r7,0(%r7); br %r7; .long " ASM_NAME(__op_gen_label) #n)
+#elif defined(__alpha__)
+#define EXIT_TB() asm volatile ("ret")
+#elif defined(__ia64__)
+#define EXIT_TB() asm volatile ("br.ret.sptk.many b0;;")
+#define GOTO_LABEL_PARAM(n) asm volatile ("br.sptk.many " \
+					  ASM_NAME(__op_gen_label) #n)
+#elif defined(__sparc__)
+#define EXIT_TB() asm volatile ("jmpl %i0 + 8, %g0; nop")
+#define GOTO_LABEL_PARAM(n) asm volatile ("ba " ASM_NAME(__op_gen_label) #n ";nop")
+#elif defined(__arm__)
+#define EXIT_TB() asm volatile ("b exec_loop")
+#define GOTO_LABEL_PARAM(n) asm volatile ("b " ASM_NAME(__op_gen_label) #n)
+#elif defined(__mc68000)
+#define EXIT_TB() asm volatile ("rts")
+#elif defined(__mips__)
+#define EXIT_TB() asm volatile ("jr $ra")
+#define GOTO_LABEL_PARAM(n) asm volatile (".set noat; la $1, " ASM_NAME(__op_gen_label) #n "; jr $1; .set at")
+#elif defined(__hppa__)
+#define GOTO_LABEL_PARAM(n) asm volatile ("b,n " ASM_NAME(__op_gen_label) #n)
+#else
+#error unsupported CPU
+#endif
+
+/* The return address may point to the start of the next instruction.
+   Subtracting one gets us the call instruction itself.  */
+#if defined(__s390__)
+# define GETPC() ((void*)(((unsigned long)__builtin_return_address(0) & 0x7fffffffUL) - 1))
+#elif defined(__arm__)
+/* Thumb return addresses have the low bit set, so we need to subtract two.
+   This is still safe in ARM mode because instructions are 4 bytes.  */
+# define GETPC() ((void *)((unsigned long)__builtin_return_address(0) - 2))
+#else
+# define GETPC() ((void *)((unsigned long)__builtin_return_address(0) - 1))
+#endif
+
+#endif /* !defined(__DYNGEN_EXEC_H__) */
diff --git a/dynlink.h b/dynlink.h
new file mode 100644
index 0000000..f156b37
--- /dev/null
+++ b/dynlink.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2008 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Lazy Dynamic Linking Support
+ *
+ * This header file is meant to be included multiple times.
+ *
+ * It is used to define function pointers to symbols in external
+ * shared objects (Unix dynamic libraries) which will be lazily resolved
+ * at runtime, by calling a specific initialization function.
+ *
+ * You must define, before including this header, a DYNLINK_FUNCTIONS
+ * macro which must contain a sequence of DYNLINK_FUNC(ret,name,sig)
+ * statements.
+ *
+ * In each statement, 'ret' is a function return type, 'name' is
+ * the function's name as provided by the library, and 'sig' is
+ * the function signature, including enclosing parentheses.
+ *
+ * Here's an example:
+ *
+ *  #define DYNLINK_FUNCTIONS \
+ *     DYNLINK_FUNC(int,open,(const char*, int)) \
+ *     DYNLINK_FUNC(int,read,(int,char*,int)) \
+ *     DYNLINK_FUNC(int,close,(int)) \
+ *
+ *
+ * You must also define a DYNLINK_FUNCTIONS_INIT macro which contains the
+ * name of a generated function used to initialize the function pointers.
+ * (see below)
+ */
+
+#ifndef DYNLINK_FUNCTIONS
+#error DYNLINK_FUNCTIONS should be defined when including this file
+#endif
+
+#ifndef DYNLINK_FUNCTIONS_INIT
+#error DYNLINK_FUNCTIONS_INIT should be defined when including this file
+#endif
+
+/* just in case */
+#undef DYNLINK_FUNC
+
+/* define pointers to dynamic library functions as static pointers.
+ */
+#define  DYNLINK_FUNC(ret,name,sig) \
+      static ret  (*_dynlink_##name) sig ;
+
+#define  DYNLINK_STR(name)   DYNLINK_STR_(name)
+#define  DYNLINK_STR_(name)  #name
+
+DYNLINK_FUNCTIONS
+#undef DYNLINK_FUNC
+
+/* now define a function that tries to load all dynlink function
+ * pointers. returns 0 on success, or -1 on error (i.e. if any of
+ * the functions could not be loaded).
+ *
+ * 'library' must be the result of a succesful dlopen() call
+ *
+ * You must define DYNLINK_FUNCTIONS_INIT
+ */
+static int
+DYNLINK_FUNCTIONS_INIT(void*  library)
+{
+#define  DYNLINK_FUNC(ret,name,sig) \
+    do { \
+        _dynlink_##name = dlsym( library, DYNLINK_STR(name) ); \
+        if (_dynlink_##name == NULL) goto Fail; \
+    } while (0);
+
+    DYNLINK_FUNCTIONS
+#undef DYNLINK_FUNC
+
+    return 0;
+Fail:
+    return -1;
+}
+
+/* in user code, use  FF(function_name) to invoke the
+ * corresponding dynamic function named 'function_name'
+ * after initialization succeeded.
+ */
+#ifndef FF
+#define FF(name)   (*_dynlink_##name)
+#endif
+
+/* clear macros */
+#undef DYNLINK_FUNC
+#undef DYNLINK_FUNCTIONS
+#undef DYNLINK_FUNCTIONS_INIT
diff --git a/elf.h b/elf.h
new file mode 100644
index 0000000..861f1d3
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,1172 @@
+#ifndef _QEMU_ELF_H
+#define _QEMU_ELF_H
+
+#include <inttypes.h>
+
+/* 32-bit ELF base types. */
+typedef uint32_t Elf32_Addr;
+typedef uint16_t Elf32_Half;
+typedef uint32_t Elf32_Off;
+typedef int32_t  Elf32_Sword;
+typedef uint32_t Elf32_Word;
+
+/* 64-bit ELF base types. */
+typedef uint64_t Elf64_Addr;
+typedef uint16_t Elf64_Half;
+typedef int16_t	 Elf64_SHalf;
+typedef uint64_t Elf64_Off;
+typedef int32_t	 Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef uint64_t Elf64_Xword;
+typedef int64_t  Elf64_Sxword;
+
+/* These constants are for the segment types stored in the image headers */
+#define PT_NULL    0
+#define PT_LOAD    1
+#define PT_DYNAMIC 2
+#define PT_INTERP  3
+#define PT_NOTE    4
+#define PT_SHLIB   5
+#define PT_PHDR    6
+#define PT_LOPROC  0x70000000
+#define PT_HIPROC  0x7fffffff
+#define PT_MIPS_REGINFO		0x70000000
+#define PT_MIPS_OPTIONS		0x70000001
+
+/* Flags in the e_flags field of the header */
+/* MIPS architecture level. */
+#define EF_MIPS_ARCH_1		0x00000000	/* -mips1 code.  */
+#define EF_MIPS_ARCH_2		0x10000000	/* -mips2 code.  */
+#define EF_MIPS_ARCH_3		0x20000000	/* -mips3 code.  */
+#define EF_MIPS_ARCH_4		0x30000000	/* -mips4 code.  */
+#define EF_MIPS_ARCH_5		0x40000000	/* -mips5 code.  */
+#define EF_MIPS_ARCH_32		0x50000000	/* MIPS32 code.  */
+#define EF_MIPS_ARCH_64		0x60000000	/* MIPS64 code.  */
+
+/* The ABI of a file. */
+#define EF_MIPS_ABI_O32		0x00001000	/* O32 ABI.  */
+#define EF_MIPS_ABI_O64		0x00002000	/* O32 extended for 64 bit.  */
+
+#define EF_MIPS_NOREORDER 0x00000001
+#define EF_MIPS_PIC       0x00000002
+#define EF_MIPS_CPIC      0x00000004
+#define EF_MIPS_ABI2		0x00000020
+#define EF_MIPS_OPTIONS_FIRST	0x00000080
+#define EF_MIPS_32BITMODE	0x00000100
+#define EF_MIPS_ABI		0x0000f000
+#define EF_MIPS_ARCH      0xf0000000
+
+/* These constants define the different elf file types */
+#define ET_NONE   0
+#define ET_REL    1
+#define ET_EXEC   2
+#define ET_DYN    3
+#define ET_CORE   4
+#define ET_LOPROC 0xff00
+#define ET_HIPROC 0xffff
+
+/* These constants define the various ELF target machines */
+#define EM_NONE  0
+#define EM_M32   1
+#define EM_SPARC 2
+#define EM_386   3
+#define EM_68K   4
+#define EM_88K   5
+#define EM_486   6   /* Perhaps disused */
+#define EM_860   7
+
+#define EM_MIPS		8	/* MIPS R3000 (officially, big-endian only) */
+
+#define EM_MIPS_RS4_BE 10	/* MIPS R4000 big-endian */
+
+#define EM_PARISC      15	/* HPPA */
+
+#define EM_SPARC32PLUS 18	/* Sun's "v8plus" */
+
+#define EM_PPC	       20	/* PowerPC */
+#define EM_PPC64       21       /* PowerPC64 */
+
+#define EM_ARM		40		/* ARM */
+
+#define EM_SH	       42	/* SuperH */
+
+#define EM_SPARCV9     43	/* SPARC v9 64-bit */
+
+#define EM_IA_64	50	/* HP/Intel IA-64 */
+
+#define EM_X86_64	62	/* AMD x86-64 */
+
+#define EM_S390		22	/* IBM S/390 */
+
+#define EM_CRIS         76      /* Axis Communications 32-bit embedded processor */
+
+#define EM_V850		87	/* NEC v850 */
+
+#define EM_H8_300H      47      /* Hitachi H8/300H */
+#define EM_H8S          48      /* Hitachi H8S     */
+
+/*
+ * This is an interim value that we will use until the committee comes
+ * up with a final number.
+ */
+#define EM_ALPHA	0x9026
+
+/* Bogus old v850 magic number, used by old tools.  */
+#define EM_CYGNUS_V850	0x9080
+
+/*
+ * This is the old interim value for S/390 architecture
+ */
+#define EM_S390_OLD     0xA390
+
+/* This is the info that is needed to parse the dynamic section of the file */
+#define DT_NULL		0
+#define DT_NEEDED	1
+#define DT_PLTRELSZ	2
+#define DT_PLTGOT	3
+#define DT_HASH		4
+#define DT_STRTAB	5
+#define DT_SYMTAB	6
+#define DT_RELA		7
+#define DT_RELASZ	8
+#define DT_RELAENT	9
+#define DT_STRSZ	10
+#define DT_SYMENT	11
+#define DT_INIT		12
+#define DT_FINI		13
+#define DT_SONAME	14
+#define DT_RPATH 	15
+#define DT_SYMBOLIC	16
+#define DT_REL	        17
+#define DT_RELSZ	18
+#define DT_RELENT	19
+#define DT_PLTREL	20
+#define DT_DEBUG	21
+#define DT_TEXTREL	22
+#define DT_JMPREL	23
+#define DT_LOPROC	0x70000000
+#define DT_HIPROC	0x7fffffff
+#define DT_MIPS_RLD_VERSION	0x70000001
+#define DT_MIPS_TIME_STAMP	0x70000002
+#define DT_MIPS_ICHECKSUM	0x70000003
+#define DT_MIPS_IVERSION	0x70000004
+#define DT_MIPS_FLAGS		0x70000005
+  #define RHF_NONE		  0
+  #define RHF_HARDWAY		  1
+  #define RHF_NOTPOT		  2
+#define DT_MIPS_BASE_ADDRESS	0x70000006
+#define DT_MIPS_CONFLICT	0x70000008
+#define DT_MIPS_LIBLIST		0x70000009
+#define DT_MIPS_LOCAL_GOTNO	0x7000000a
+#define DT_MIPS_CONFLICTNO	0x7000000b
+#define DT_MIPS_LIBLISTNO	0x70000010
+#define DT_MIPS_SYMTABNO	0x70000011
+#define DT_MIPS_UNREFEXTNO	0x70000012
+#define DT_MIPS_GOTSYM		0x70000013
+#define DT_MIPS_HIPAGENO	0x70000014
+#define DT_MIPS_RLD_MAP		0x70000016
+
+/* This info is needed when parsing the symbol table */
+#define STB_LOCAL  0
+#define STB_GLOBAL 1
+#define STB_WEAK   2
+
+#define STT_NOTYPE  0
+#define STT_OBJECT  1
+#define STT_FUNC    2
+#define STT_SECTION 3
+#define STT_FILE    4
+
+#define ELF_ST_BIND(x)		((x) >> 4)
+#define ELF_ST_TYPE(x)		(((unsigned int) x) & 0xf)
+#define ELF32_ST_BIND(x)	ELF_ST_BIND(x)
+#define ELF32_ST_TYPE(x)	ELF_ST_TYPE(x)
+#define ELF64_ST_BIND(x)	ELF_ST_BIND(x)
+#define ELF64_ST_TYPE(x)	ELF_ST_TYPE(x)
+
+/* Symbolic values for the entries in the auxiliary table
+   put on the initial stack */
+#define AT_NULL   0	/* end of vector */
+#define AT_IGNORE 1	/* entry should be ignored */
+#define AT_EXECFD 2	/* file descriptor of program */
+#define AT_PHDR   3	/* program headers for program */
+#define AT_PHENT  4	/* size of program header entry */
+#define AT_PHNUM  5	/* number of program headers */
+#define AT_PAGESZ 6	/* system page size */
+#define AT_BASE   7	/* base address of interpreter */
+#define AT_FLAGS  8	/* flags */
+#define AT_ENTRY  9	/* entry point of program */
+#define AT_NOTELF 10	/* program is not ELF */
+#define AT_UID    11	/* real uid */
+#define AT_EUID   12	/* effective uid */
+#define AT_GID    13	/* real gid */
+#define AT_EGID   14	/* effective gid */
+#define AT_PLATFORM 15  /* string identifying CPU for optimizations */
+#define AT_HWCAP  16    /* arch dependent hints at CPU capabilities */
+#define AT_CLKTCK 17	/* frequency at which times() increments */
+
+typedef struct dynamic{
+  Elf32_Sword d_tag;
+  union{
+    Elf32_Sword	d_val;
+    Elf32_Addr	d_ptr;
+  } d_un;
+} Elf32_Dyn;
+
+typedef struct {
+  Elf64_Sxword d_tag;		/* entry tag value */
+  union {
+    Elf64_Xword d_val;
+    Elf64_Addr d_ptr;
+  } d_un;
+} Elf64_Dyn;
+
+/* The following are used with relocations */
+#define ELF32_R_SYM(x) ((x) >> 8)
+#define ELF32_R_TYPE(x) ((x) & 0xff)
+
+#define ELF64_R_SYM(i)			((i) >> 32)
+#define ELF64_R_TYPE(i)			((i) & 0xffffffff)
+#define ELF64_R_TYPE_DATA(i)            (((ELF64_R_TYPE(i) >> 8) ^ 0x00800000) - 0x00800000)
+
+#define R_386_NONE	0
+#define R_386_32	1
+#define R_386_PC32	2
+#define R_386_GOT32	3
+#define R_386_PLT32	4
+#define R_386_COPY	5
+#define R_386_GLOB_DAT	6
+#define R_386_JMP_SLOT	7
+#define R_386_RELATIVE	8
+#define R_386_GOTOFF	9
+#define R_386_GOTPC	10
+#define R_386_NUM	11
+
+#define R_MIPS_NONE		0
+#define R_MIPS_16		1
+#define R_MIPS_32		2
+#define R_MIPS_REL32		3
+#define R_MIPS_26		4
+#define R_MIPS_HI16		5
+#define R_MIPS_LO16		6
+#define R_MIPS_GPREL16		7
+#define R_MIPS_LITERAL		8
+#define R_MIPS_GOT16		9
+#define R_MIPS_PC16		10
+#define R_MIPS_CALL16		11
+#define R_MIPS_GPREL32		12
+/* The remaining relocs are defined on Irix, although they are not
+   in the MIPS ELF ABI.  */
+#define R_MIPS_UNUSED1		13
+#define R_MIPS_UNUSED2		14
+#define R_MIPS_UNUSED3		15
+#define R_MIPS_SHIFT5		16
+#define R_MIPS_SHIFT6		17
+#define R_MIPS_64		18
+#define R_MIPS_GOT_DISP		19
+#define R_MIPS_GOT_PAGE		20
+#define R_MIPS_GOT_OFST		21
+/*
+ * The following two relocation types are specified in the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_GOTHI16		22
+#define R_MIPS_GOTLO16		23
+#define R_MIPS_SUB		24
+#define R_MIPS_INSERT_A		25
+#define R_MIPS_INSERT_B		26
+#define R_MIPS_DELETE		27
+#define R_MIPS_HIGHER		28
+#define R_MIPS_HIGHEST		29
+/*
+ * The following two relocation types are specified in the MIPS ABI
+ * conformance guide version 1.2 but not yet in the psABI.
+ */
+#define R_MIPS_CALLHI16		30
+#define R_MIPS_CALLLO16		31
+/*
+ * This range is reserved for vendor specific relocations.
+ */
+#define R_MIPS_LOVENDOR		100
+#define R_MIPS_HIVENDOR		127
+
+
+/*
+ * Sparc ELF relocation types
+ */
+#define	R_SPARC_NONE		0
+#define	R_SPARC_8		1
+#define	R_SPARC_16		2
+#define	R_SPARC_32		3
+#define	R_SPARC_DISP8		4
+#define	R_SPARC_DISP16		5
+#define	R_SPARC_DISP32		6
+#define	R_SPARC_WDISP30		7
+#define	R_SPARC_WDISP22		8
+#define	R_SPARC_HI22		9
+#define	R_SPARC_22		10
+#define	R_SPARC_13		11
+#define	R_SPARC_LO10		12
+#define	R_SPARC_GOT10		13
+#define	R_SPARC_GOT13		14
+#define	R_SPARC_GOT22		15
+#define	R_SPARC_PC10		16
+#define	R_SPARC_PC22		17
+#define	R_SPARC_WPLT30		18
+#define	R_SPARC_COPY		19
+#define	R_SPARC_GLOB_DAT	20
+#define	R_SPARC_JMP_SLOT	21
+#define	R_SPARC_RELATIVE	22
+#define	R_SPARC_UA32		23
+#define R_SPARC_PLT32		24
+#define R_SPARC_HIPLT22		25
+#define R_SPARC_LOPLT10		26
+#define R_SPARC_PCPLT32		27
+#define R_SPARC_PCPLT22		28
+#define R_SPARC_PCPLT10		29
+#define R_SPARC_10		30
+#define R_SPARC_11		31
+#define R_SPARC_64		32
+#define R_SPARC_OLO10           33
+#define R_SPARC_HH22            34
+#define R_SPARC_HM10            35
+#define R_SPARC_LM22            36
+#define R_SPARC_WDISP16		40
+#define R_SPARC_WDISP19		41
+#define R_SPARC_7		43
+#define R_SPARC_5		44
+#define R_SPARC_6		45
+
+/* Bits present in AT_HWCAP, primarily for Sparc32.  */
+
+#define HWCAP_SPARC_FLUSH       1    /* CPU supports flush instruction. */
+#define HWCAP_SPARC_STBAR       2
+#define HWCAP_SPARC_SWAP        4
+#define HWCAP_SPARC_MULDIV      8
+#define HWCAP_SPARC_V9		16
+#define HWCAP_SPARC_ULTRA3	32
+
+/*
+ * 68k ELF relocation types
+ */
+#define R_68K_NONE	0
+#define R_68K_32	1
+#define R_68K_16	2
+#define R_68K_8		3
+#define R_68K_PC32	4
+#define R_68K_PC16	5
+#define R_68K_PC8	6
+#define R_68K_GOT32	7
+#define R_68K_GOT16	8
+#define R_68K_GOT8	9
+#define R_68K_GOT32O	10
+#define R_68K_GOT16O	11
+#define R_68K_GOT8O	12
+#define R_68K_PLT32	13
+#define R_68K_PLT16	14
+#define R_68K_PLT8	15
+#define R_68K_PLT32O	16
+#define R_68K_PLT16O	17
+#define R_68K_PLT8O	18
+#define R_68K_COPY	19
+#define R_68K_GLOB_DAT	20
+#define R_68K_JMP_SLOT	21
+#define R_68K_RELATIVE	22
+
+/*
+ * Alpha ELF relocation types
+ */
+#define R_ALPHA_NONE            0       /* No reloc */
+#define R_ALPHA_REFLONG         1       /* Direct 32 bit */
+#define R_ALPHA_REFQUAD         2       /* Direct 64 bit */
+#define R_ALPHA_GPREL32         3       /* GP relative 32 bit */
+#define R_ALPHA_LITERAL         4       /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE          5       /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP          6       /* Add displacement to GP */
+#define R_ALPHA_BRADDR          7       /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT            8       /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16          9       /* PC relative 16 bit */
+#define R_ALPHA_SREL32          10      /* PC relative 32 bit */
+#define R_ALPHA_SREL64          11      /* PC relative 64 bit */
+#define R_ALPHA_GPRELHIGH       17      /* GP relative 32 bit, high 16 bits */
+#define R_ALPHA_GPRELLOW        18      /* GP relative 32 bit, low 16 bits */
+#define R_ALPHA_GPREL16         19      /* GP relative 16 bit */
+#define R_ALPHA_COPY            24      /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT        25      /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT        26      /* Create PLT entry */
+#define R_ALPHA_RELATIVE        27      /* Adjust by program base */
+#define R_ALPHA_BRSGP		28
+#define R_ALPHA_TLSGD           29
+#define R_ALPHA_TLS_LDM         30
+#define R_ALPHA_DTPMOD64        31
+#define R_ALPHA_GOTDTPREL       32
+#define R_ALPHA_DTPREL64        33
+#define R_ALPHA_DTPRELHI        34
+#define R_ALPHA_DTPRELLO        35
+#define R_ALPHA_DTPREL16        36
+#define R_ALPHA_GOTTPREL        37
+#define R_ALPHA_TPREL64         38
+#define R_ALPHA_TPRELHI         39
+#define R_ALPHA_TPRELLO         40
+#define R_ALPHA_TPREL16         41
+
+#define SHF_ALPHA_GPREL		0x10000000
+
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE		0
+#define R_PPC_ADDR32		1	/* 32bit absolute address */
+#define R_PPC_ADDR24		2	/* 26bit address, 2 bits ignored.  */
+#define R_PPC_ADDR16		3	/* 16bit absolute address */
+#define R_PPC_ADDR16_LO		4	/* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI		5	/* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA		6	/* adjusted high 16bit */
+#define R_PPC_ADDR14		7	/* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN	8
+#define R_PPC_ADDR14_BRNTAKEN	9
+#define R_PPC_REL24		10	/* PC relative 26 bit */
+#define R_PPC_REL14		11	/* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN	12
+#define R_PPC_REL14_BRNTAKEN	13
+#define R_PPC_GOT16		14
+#define R_PPC_GOT16_LO		15
+#define R_PPC_GOT16_HI		16
+#define R_PPC_GOT16_HA		17
+#define R_PPC_PLTREL24		18
+#define R_PPC_COPY		19
+#define R_PPC_GLOB_DAT		20
+#define R_PPC_JMP_SLOT		21
+#define R_PPC_RELATIVE		22
+#define R_PPC_LOCAL24PC		23
+#define R_PPC_UADDR32		24
+#define R_PPC_UADDR16		25
+#define R_PPC_REL32		26
+#define R_PPC_PLT32		27
+#define R_PPC_PLTREL32		28
+#define R_PPC_PLT16_LO		29
+#define R_PPC_PLT16_HI		30
+#define R_PPC_PLT16_HA		31
+#define R_PPC_SDAREL16		32
+#define R_PPC_SECTOFF		33
+#define R_PPC_SECTOFF_LO	34
+#define R_PPC_SECTOFF_HI	35
+#define R_PPC_SECTOFF_HA	36
+/* Keep this the last entry.  */
+#define R_PPC_NUM		37
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field.  */
+#define EF_ARM_RELEXEC     0x01
+#define EF_ARM_HASENTRY    0x02
+#define EF_ARM_INTERWORK   0x04
+#define EF_ARM_APCS_26     0x08
+#define EF_ARM_APCS_FLOAT  0x10
+#define EF_ARM_PIC         0x20
+#define EF_ALIGN8          0x40		/* 8-bit structure alignment is in use */
+#define EF_NEW_ABI         0x80
+#define EF_OLD_ABI         0x100
+
+/* Additional symbol types for Thumb */
+#define STT_ARM_TFUNC      0xd
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT  0x10000000   /* Section contains an entry point */
+#define SHF_ARM_COMDEF     0x80000000   /* Section may be multiply defined
+					   in the input to a link step */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB          0x10000000   /* Segment contains the location
+					   addressed by the static base */
+
+/* ARM relocs.  */
+#define R_ARM_NONE		0	/* No reloc */
+#define R_ARM_PC24		1	/* PC relative 26 bit branch */
+#define R_ARM_ABS32		2	/* Direct 32 bit  */
+#define R_ARM_REL32		3	/* PC relative 32 bit */
+#define R_ARM_PC13		4
+#define R_ARM_ABS16		5	/* Direct 16 bit */
+#define R_ARM_ABS12		6	/* Direct 12 bit */
+#define R_ARM_THM_ABS5		7
+#define R_ARM_ABS8		8	/* Direct 8 bit */
+#define R_ARM_SBREL32		9
+#define R_ARM_THM_PC22		10
+#define R_ARM_THM_PC8		11
+#define R_ARM_AMP_VCALL9	12
+#define R_ARM_SWI24		13
+#define R_ARM_THM_SWI8		14
+#define R_ARM_XPC25		15
+#define R_ARM_THM_XPC22		16
+#define R_ARM_COPY		20	/* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT		21	/* Create GOT entry */
+#define R_ARM_JUMP_SLOT		22	/* Create PLT entry */
+#define R_ARM_RELATIVE		23	/* Adjust by program base */
+#define R_ARM_GOTOFF		24	/* 32 bit offset to GOT */
+#define R_ARM_GOTPC		25	/* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32		26	/* 32 bit GOT entry */
+#define R_ARM_PLT32		27	/* 32 bit PLT address */
+#define R_ARM_CALL              28
+#define R_ARM_JUMP24            29
+#define R_ARM_GNU_VTENTRY	100
+#define R_ARM_GNU_VTINHERIT	101
+#define R_ARM_THM_PC11		102	/* thumb unconditional branch */
+#define R_ARM_THM_PC9		103	/* thumb conditional branch */
+#define R_ARM_RXPC25		249
+#define R_ARM_RSBREL32		250
+#define R_ARM_THM_RPC22		251
+#define R_ARM_RREL32		252
+#define R_ARM_RABS22		253
+#define R_ARM_RPC24		254
+#define R_ARM_RBASE		255
+/* Keep this the last entry.  */
+#define R_ARM_NUM		256
+
+/* s390 relocations defined by the ABIs */
+#define R_390_NONE		0	/* No reloc.  */
+#define R_390_8			1	/* Direct 8 bit.  */
+#define R_390_12		2	/* Direct 12 bit.  */
+#define R_390_16		3	/* Direct 16 bit.  */
+#define R_390_32		4	/* Direct 32 bit.  */
+#define R_390_PC32		5	/* PC relative 32 bit.	*/
+#define R_390_GOT12		6	/* 12 bit GOT offset.  */
+#define R_390_GOT32		7	/* 32 bit GOT offset.  */
+#define R_390_PLT32		8	/* 32 bit PC relative PLT address.  */
+#define R_390_COPY		9	/* Copy symbol at runtime.  */
+#define R_390_GLOB_DAT		10	/* Create GOT entry.  */
+#define R_390_JMP_SLOT		11	/* Create PLT entry.  */
+#define R_390_RELATIVE		12	/* Adjust by program base.  */
+#define R_390_GOTOFF32		13	/* 32 bit offset to GOT.	 */
+#define R_390_GOTPC		14	/* 32 bit PC rel. offset to GOT.  */
+#define R_390_GOT16		15	/* 16 bit GOT offset.  */
+#define R_390_PC16		16	/* PC relative 16 bit.	*/
+#define R_390_PC16DBL		17	/* PC relative 16 bit shifted by 1.  */
+#define R_390_PLT16DBL		18	/* 16 bit PC rel. PLT shifted by 1.  */
+#define R_390_PC32DBL		19	/* PC relative 32 bit shifted by 1.  */
+#define R_390_PLT32DBL		20	/* 32 bit PC rel. PLT shifted by 1.  */
+#define R_390_GOTPCDBL		21	/* 32 bit PC rel. GOT shifted by 1.  */
+#define R_390_64		22	/* Direct 64 bit.  */
+#define R_390_PC64		23	/* PC relative 64 bit.	*/
+#define R_390_GOT64		24	/* 64 bit GOT offset.  */
+#define R_390_PLT64		25	/* 64 bit PC relative PLT address.  */
+#define R_390_GOTENT		26	/* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16		27	/* 16 bit offset to GOT. */
+#define R_390_GOTOFF64		28	/* 64 bit offset to GOT. */
+#define R_390_GOTPLT12		29	/* 12 bit offset to jump slot.	*/
+#define R_390_GOTPLT16		30	/* 16 bit offset to jump slot.	*/
+#define R_390_GOTPLT32		31	/* 32 bit offset to jump slot.	*/
+#define R_390_GOTPLT64		32	/* 64 bit offset to jump slot.	*/
+#define R_390_GOTPLTENT		33	/* 32 bit rel. offset to jump slot.  */
+#define R_390_PLTOFF16		34	/* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32		35	/* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64		36	/* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD		37	/* Tag for load insn in TLS code. */
+#define R_390_TLS_GDCALL	38	/* Tag for function call in general
+                                           dynamic TLS code.  */
+#define R_390_TLS_LDCALL	39	/* Tag for function call in local
+                                           dynamic TLS code.  */
+#define R_390_TLS_GD32		40	/* Direct 32 bit for general dynamic
+                                           thread local data.  */
+#define R_390_TLS_GD64		41	/* Direct 64 bit for general dynamic
+                                           thread local data.  */
+#define R_390_TLS_GOTIE12	42	/* 12 bit GOT offset for static TLS
+                                           block offset.  */
+#define R_390_TLS_GOTIE32	43	/* 32 bit GOT offset for static TLS
+                                           block offset.  */
+#define R_390_TLS_GOTIE64	44	/* 64 bit GOT offset for static TLS
+                                           block offset.  */
+#define R_390_TLS_LDM32		45	/* Direct 32 bit for local dynamic
+                                           thread local data in LD code.  */
+#define R_390_TLS_LDM64		46	/* Direct 64 bit for local dynamic
+                                           thread local data in LD code.  */
+#define R_390_TLS_IE32		47	/* 32 bit address of GOT entry for
+                                           negated static TLS block offset.  */
+#define R_390_TLS_IE64		48	/* 64 bit address of GOT entry for
+                                           negated static TLS block offset.  */
+#define R_390_TLS_IEENT		49	/* 32 bit rel. offset to GOT entry for
+                                           negated static TLS block offset.  */
+#define R_390_TLS_LE32		50	/* 32 bit negated offset relative to
+                                           static TLS block.  */
+#define R_390_TLS_LE64		51	/* 64 bit negated offset relative to
+                                           static TLS block.  */
+#define R_390_TLS_LDO32		52	/* 32 bit offset relative to TLS
+                                           block.  */
+#define R_390_TLS_LDO64		53	/* 64 bit offset relative to TLS
+                                           block.  */
+#define R_390_TLS_DTPMOD	54	/* ID of module containing symbol.  */
+#define R_390_TLS_DTPOFF	55	/* Offset in TLS block.  */
+#define R_390_TLS_TPOFF		56	/* Negate offset in static TLS
+                                           block.  */
+/* Keep this the last entry.  */
+#define R_390_NUM	57
+
+/* x86-64 relocation types */
+#define R_X86_64_NONE		0	/* No reloc */
+#define R_X86_64_64		1	/* Direct 64 bit  */
+#define R_X86_64_PC32		2	/* PC relative 32 bit signed */
+#define R_X86_64_GOT32		3	/* 32 bit GOT entry */
+#define R_X86_64_PLT32		4	/* 32 bit PLT address */
+#define R_X86_64_COPY		5	/* Copy symbol at runtime */
+#define R_X86_64_GLOB_DAT	6	/* Create GOT entry */
+#define R_X86_64_JUMP_SLOT	7	/* Create PLT entry */
+#define R_X86_64_RELATIVE	8	/* Adjust by program base */
+#define R_X86_64_GOTPCREL	9	/* 32 bit signed pc relative
+					   offset to GOT */
+#define R_X86_64_32		10	/* Direct 32 bit zero extended */
+#define R_X86_64_32S		11	/* Direct 32 bit sign extended */
+#define R_X86_64_16		12	/* Direct 16 bit zero extended */
+#define R_X86_64_PC16		13	/* 16 bit sign extended pc relative */
+#define R_X86_64_8		14	/* Direct 8 bit sign extended  */
+#define R_X86_64_PC8		15	/* 8 bit sign extended pc relative */
+
+#define R_X86_64_NUM		16
+
+/* Legal values for e_flags field of Elf64_Ehdr.  */
+
+#define EF_ALPHA_32BIT		1	/* All addresses are below 2GB */
+
+/* HPPA specific definitions.  */
+
+/* Legal values for e_flags field of Elf32_Ehdr.  */
+
+#define EF_PARISC_TRAPNIL	0x00010000 /* Trap nil pointer dereference.  */
+#define EF_PARISC_EXT		0x00020000 /* Program uses arch. extensions. */
+#define EF_PARISC_LSB		0x00040000 /* Program expects little endian. */
+#define EF_PARISC_WIDE		0x00080000 /* Program expects wide mode.  */
+#define EF_PARISC_NO_KABP	0x00100000 /* No kernel assisted branch
+					      prediction.  */
+#define EF_PARISC_LAZYSWAP	0x00400000 /* Allow lazy swapping.  */
+#define EF_PARISC_ARCH		0x0000ffff /* Architecture version.  */
+
+/* Defined values for `e_flags & EF_PARISC_ARCH' are:  */
+
+#define EFA_PARISC_1_0		    0x020b /* PA-RISC 1.0 big-endian.  */
+#define EFA_PARISC_1_1		    0x0210 /* PA-RISC 1.1 big-endian.  */
+#define EFA_PARISC_2_0		    0x0214 /* PA-RISC 2.0 big-endian.  */
+
+/* Additional section indeces.  */
+
+#define SHN_PARISC_ANSI_COMMON	0xff00	   /* Section for tenatively declared
+					      symbols in ANSI C.  */
+#define SHN_PARISC_HUGE_COMMON	0xff01	   /* Common blocks in huge model.  */
+
+/* Legal values for sh_type field of Elf32_Shdr.  */
+
+#define SHT_PARISC_EXT		0x70000000 /* Contains product specific ext. */
+#define SHT_PARISC_UNWIND	0x70000001 /* Unwind information.  */
+#define SHT_PARISC_DOC		0x70000002 /* Debug info for optimized code. */
+
+/* Legal values for sh_flags field of Elf32_Shdr.  */
+
+#define SHF_PARISC_SHORT	0x20000000 /* Section with short addressing. */
+#define SHF_PARISC_HUGE		0x40000000 /* Section far from gp.  */
+#define SHF_PARISC_SBP		0x80000000 /* Static branch prediction code. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type).  */
+
+#define STT_PARISC_MILLICODE	13	/* Millicode function entry point.  */
+
+#define STT_HP_OPAQUE		(STT_LOOS + 0x1)
+#define STT_HP_STUB		(STT_LOOS + 0x2)
+
+/* HPPA relocs.  */
+
+#define R_PARISC_NONE		0	/* No reloc.  */
+#define R_PARISC_DIR32		1	/* Direct 32-bit reference.  */
+#define R_PARISC_DIR21L		2	/* Left 21 bits of eff. address.  */
+#define R_PARISC_DIR17R		3	/* Right 17 bits of eff. address.  */
+#define R_PARISC_DIR17F		4	/* 17 bits of eff. address.  */
+#define R_PARISC_DIR14R		6	/* Right 14 bits of eff. address.  */
+#define R_PARISC_PCREL32	9	/* 32-bit rel. address.  */
+#define R_PARISC_PCREL21L	10	/* Left 21 bits of rel. address.  */
+#define R_PARISC_PCREL17R	11	/* Right 17 bits of rel. address.  */
+#define R_PARISC_PCREL17F	12	/* 17 bits of rel. address.  */
+#define R_PARISC_PCREL14R	14	/* Right 14 bits of rel. address.  */
+#define R_PARISC_DPREL21L	18	/* Left 21 bits of rel. address.  */
+#define R_PARISC_DPREL14R	22	/* Right 14 bits of rel. address.  */
+#define R_PARISC_GPREL21L	26	/* GP-relative, left 21 bits.  */
+#define R_PARISC_GPREL14R	30	/* GP-relative, right 14 bits.  */
+#define R_PARISC_LTOFF21L	34	/* LT-relative, left 21 bits.  */
+#define R_PARISC_LTOFF14R	38	/* LT-relative, right 14 bits.  */
+#define R_PARISC_SECREL32	41	/* 32 bits section rel. address.  */
+#define R_PARISC_SEGBASE	48	/* No relocation, set segment base.  */
+#define R_PARISC_SEGREL32	49	/* 32 bits segment rel. address.  */
+#define R_PARISC_PLTOFF21L	50	/* PLT rel. address, left 21 bits.  */
+#define R_PARISC_PLTOFF14R	54	/* PLT rel. address, right 14 bits.  */
+#define R_PARISC_LTOFF_FPTR32	57	/* 32 bits LT-rel. function pointer. */
+#define R_PARISC_LTOFF_FPTR21L	58	/* LT-rel. fct ptr, left 21 bits. */
+#define R_PARISC_LTOFF_FPTR14R	62	/* LT-rel. fct ptr, right 14 bits. */
+#define R_PARISC_FPTR64		64	/* 64 bits function address.  */
+#define R_PARISC_PLABEL32	65	/* 32 bits function address.  */
+#define R_PARISC_PCREL64	72	/* 64 bits PC-rel. address.  */
+#define R_PARISC_PCREL22F	74	/* 22 bits PC-rel. address.  */
+#define R_PARISC_PCREL14WR	75	/* PC-rel. address, right 14 bits.  */
+#define R_PARISC_PCREL14DR	76	/* PC rel. address, right 14 bits.  */
+#define R_PARISC_PCREL16F	77	/* 16 bits PC-rel. address.  */
+#define R_PARISC_PCREL16WF	78	/* 16 bits PC-rel. address.  */
+#define R_PARISC_PCREL16DF	79	/* 16 bits PC-rel. address.  */
+#define R_PARISC_DIR64		80	/* 64 bits of eff. address.  */
+#define R_PARISC_DIR14WR	83	/* 14 bits of eff. address.  */
+#define R_PARISC_DIR14DR	84	/* 14 bits of eff. address.  */
+#define R_PARISC_DIR16F		85	/* 16 bits of eff. address.  */
+#define R_PARISC_DIR16WF	86	/* 16 bits of eff. address.  */
+#define R_PARISC_DIR16DF	87	/* 16 bits of eff. address.  */
+#define R_PARISC_GPREL64	88	/* 64 bits of GP-rel. address.  */
+#define R_PARISC_GPREL14WR	91	/* GP-rel. address, right 14 bits.  */
+#define R_PARISC_GPREL14DR	92	/* GP-rel. address, right 14 bits.  */
+#define R_PARISC_GPREL16F	93	/* 16 bits GP-rel. address.  */
+#define R_PARISC_GPREL16WF	94	/* 16 bits GP-rel. address.  */
+#define R_PARISC_GPREL16DF	95	/* 16 bits GP-rel. address.  */
+#define R_PARISC_LTOFF64	96	/* 64 bits LT-rel. address.  */
+#define R_PARISC_LTOFF14WR	99	/* LT-rel. address, right 14 bits.  */
+#define R_PARISC_LTOFF14DR	100	/* LT-rel. address, right 14 bits.  */
+#define R_PARISC_LTOFF16F	101	/* 16 bits LT-rel. address.  */
+#define R_PARISC_LTOFF16WF	102	/* 16 bits LT-rel. address.  */
+#define R_PARISC_LTOFF16DF	103	/* 16 bits LT-rel. address.  */
+#define R_PARISC_SECREL64	104	/* 64 bits section rel. address.  */
+#define R_PARISC_SEGREL64	112	/* 64 bits segment rel. address.  */
+#define R_PARISC_PLTOFF14WR	115	/* PLT-rel. address, right 14 bits.  */
+#define R_PARISC_PLTOFF14DR	116	/* PLT-rel. address, right 14 bits.  */
+#define R_PARISC_PLTOFF16F	117	/* 16 bits LT-rel. address.  */
+#define R_PARISC_PLTOFF16WF	118	/* 16 bits PLT-rel. address.  */
+#define R_PARISC_PLTOFF16DF	119	/* 16 bits PLT-rel. address.  */
+#define R_PARISC_LTOFF_FPTR64	120	/* 64 bits LT-rel. function ptr.  */
+#define R_PARISC_LTOFF_FPTR14WR	123	/* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR14DR	124	/* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR16F	125	/* 16 bits LT-rel. function ptr.  */
+#define R_PARISC_LTOFF_FPTR16WF	126	/* 16 bits LT-rel. function ptr.  */
+#define R_PARISC_LTOFF_FPTR16DF	127	/* 16 bits LT-rel. function ptr.  */
+#define R_PARISC_LORESERVE	128
+#define R_PARISC_COPY		128	/* Copy relocation.  */
+#define R_PARISC_IPLT		129	/* Dynamic reloc, imported PLT */
+#define R_PARISC_EPLT		130	/* Dynamic reloc, exported PLT */
+#define R_PARISC_TPREL32	153	/* 32 bits TP-rel. address.  */
+#define R_PARISC_TPREL21L	154	/* TP-rel. address, left 21 bits.  */
+#define R_PARISC_TPREL14R	158	/* TP-rel. address, right 14 bits.  */
+#define R_PARISC_LTOFF_TP21L	162	/* LT-TP-rel. address, left 21 bits. */
+#define R_PARISC_LTOFF_TP14R	166	/* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14F	167	/* 14 bits LT-TP-rel. address.  */
+#define R_PARISC_TPREL64	216	/* 64 bits TP-rel. address.  */
+#define R_PARISC_TPREL14WR	219	/* TP-rel. address, right 14 bits.  */
+#define R_PARISC_TPREL14DR	220	/* TP-rel. address, right 14 bits.  */
+#define R_PARISC_TPREL16F	221	/* 16 bits TP-rel. address.  */
+#define R_PARISC_TPREL16WF	222	/* 16 bits TP-rel. address.  */
+#define R_PARISC_TPREL16DF	223	/* 16 bits TP-rel. address.  */
+#define R_PARISC_LTOFF_TP64	224	/* 64 bits LT-TP-rel. address.  */
+#define R_PARISC_LTOFF_TP14WR	227	/* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14DR	228	/* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP16F	229	/* 16 bits LT-TP-rel. address.  */
+#define R_PARISC_LTOFF_TP16WF	230	/* 16 bits LT-TP-rel. address.  */
+#define R_PARISC_LTOFF_TP16DF	231	/* 16 bits LT-TP-rel. address.  */
+#define R_PARISC_HIRESERVE	255
+
+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr.  */
+
+#define PT_HP_TLS		(PT_LOOS + 0x0)
+#define PT_HP_CORE_NONE		(PT_LOOS + 0x1)
+#define PT_HP_CORE_VERSION	(PT_LOOS + 0x2)
+#define PT_HP_CORE_KERNEL	(PT_LOOS + 0x3)
+#define PT_HP_CORE_COMM		(PT_LOOS + 0x4)
+#define PT_HP_CORE_PROC		(PT_LOOS + 0x5)
+#define PT_HP_CORE_LOADABLE	(PT_LOOS + 0x6)
+#define PT_HP_CORE_STACK	(PT_LOOS + 0x7)
+#define PT_HP_CORE_SHM		(PT_LOOS + 0x8)
+#define PT_HP_CORE_MMF		(PT_LOOS + 0x9)
+#define PT_HP_PARALLEL		(PT_LOOS + 0x10)
+#define PT_HP_FASTBIND		(PT_LOOS + 0x11)
+#define PT_HP_OPT_ANNOT		(PT_LOOS + 0x12)
+#define PT_HP_HSL_ANNOT		(PT_LOOS + 0x13)
+#define PT_HP_STACK		(PT_LOOS + 0x14)
+
+#define PT_PARISC_ARCHEXT	0x70000000
+#define PT_PARISC_UNWIND	0x70000001
+
+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr.  */
+
+#define PF_PARISC_SBP		0x08000000
+
+#define PF_HP_PAGE_SIZE		0x00100000
+#define PF_HP_FAR_SHARED	0x00200000
+#define PF_HP_NEAR_SHARED	0x00400000
+#define PF_HP_CODE		0x01000000
+#define PF_HP_MODIFY		0x02000000
+#define PF_HP_LAZYSWAP		0x04000000
+#define PF_HP_SBP		0x08000000
+
+/* IA-64 specific declarations.  */
+
+/* Processor specific flags for the Ehdr e_flags field.  */
+#define EF_IA_64_MASKOS		0x0000000f	/* os-specific flags */
+#define EF_IA_64_ABI64		0x00000010	/* 64-bit ABI */
+#define EF_IA_64_ARCH		0xff000000	/* arch. version mask */
+
+/* Processor specific values for the Phdr p_type field.  */
+#define PT_IA_64_ARCHEXT	(PT_LOPROC + 0)	/* arch extension bits */
+#define PT_IA_64_UNWIND		(PT_LOPROC + 1)	/* ia64 unwind bits */
+
+/* Processor specific flags for the Phdr p_flags field.  */
+#define PF_IA_64_NORECOV	0x80000000	/* spec insns w/o recovery */
+
+/* Processor specific values for the Shdr sh_type field.  */
+#define SHT_IA_64_EXT		(SHT_LOPROC + 0) /* extension bits */
+#define SHT_IA_64_UNWIND	(SHT_LOPROC + 1) /* unwind bits */
+
+/* Processor specific flags for the Shdr sh_flags field.  */
+#define SHF_IA_64_SHORT		0x10000000	/* section near gp */
+#define SHF_IA_64_NORECOV	0x20000000	/* spec insns w/o recovery */
+
+/* Processor specific values for the Dyn d_tag field.  */
+#define DT_IA_64_PLT_RESERVE	(DT_LOPROC + 0)
+#define DT_IA_64_NUM		1
+
+/* IA-64 relocations.  */
+#define R_IA64_NONE		0x00	/* none */
+#define R_IA64_IMM14		0x21	/* symbol + addend, add imm14 */
+#define R_IA64_IMM22		0x22	/* symbol + addend, add imm22 */
+#define R_IA64_IMM64		0x23	/* symbol + addend, mov imm64 */
+#define R_IA64_DIR32MSB		0x24	/* symbol + addend, data4 MSB */
+#define R_IA64_DIR32LSB		0x25	/* symbol + addend, data4 LSB */
+#define R_IA64_DIR64MSB		0x26	/* symbol + addend, data8 MSB */
+#define R_IA64_DIR64LSB		0x27	/* symbol + addend, data8 LSB */
+#define R_IA64_GPREL22		0x2a	/* @gprel(sym + add), add imm22 */
+#define R_IA64_GPREL64I		0x2b	/* @gprel(sym + add), mov imm64 */
+#define R_IA64_GPREL32MSB	0x2c	/* @gprel(sym + add), data4 MSB */
+#define R_IA64_GPREL32LSB	0x2d	/* @gprel(sym + add), data4 LSB */
+#define R_IA64_GPREL64MSB	0x2e	/* @gprel(sym + add), data8 MSB */
+#define R_IA64_GPREL64LSB	0x2f	/* @gprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF22		0x32	/* @ltoff(sym + add), add imm22 */
+#define R_IA64_LTOFF64I		0x33	/* @ltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF22		0x3a	/* @pltoff(sym + add), add imm22 */
+#define R_IA64_PLTOFF64I	0x3b	/* @pltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF64MSB	0x3e	/* @pltoff(sym + add), data8 MSB */
+#define R_IA64_PLTOFF64LSB	0x3f	/* @pltoff(sym + add), data8 LSB */
+#define R_IA64_FPTR64I		0x43	/* @fptr(sym + add), mov imm64 */
+#define R_IA64_FPTR32MSB	0x44	/* @fptr(sym + add), data4 MSB */
+#define R_IA64_FPTR32LSB	0x45	/* @fptr(sym + add), data4 LSB */
+#define R_IA64_FPTR64MSB	0x46	/* @fptr(sym + add), data8 MSB */
+#define R_IA64_FPTR64LSB	0x47	/* @fptr(sym + add), data8 LSB */
+#define R_IA64_PCREL60B		0x48	/* @pcrel(sym + add), brl */
+#define R_IA64_PCREL21B		0x49	/* @pcrel(sym + add), ptb, call */
+#define R_IA64_PCREL21M		0x4a	/* @pcrel(sym + add), chk.s */
+#define R_IA64_PCREL21F		0x4b	/* @pcrel(sym + add), fchkf */
+#define R_IA64_PCREL32MSB	0x4c	/* @pcrel(sym + add), data4 MSB */
+#define R_IA64_PCREL32LSB	0x4d	/* @pcrel(sym + add), data4 LSB */
+#define R_IA64_PCREL64MSB	0x4e	/* @pcrel(sym + add), data8 MSB */
+#define R_IA64_PCREL64LSB	0x4f	/* @pcrel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_FPTR22	0x52	/* @ltoff(@fptr(s+a)), imm22 */
+#define R_IA64_LTOFF_FPTR64I	0x53	/* @ltoff(@fptr(s+a)), imm64 */
+#define R_IA64_LTOFF_FPTR32MSB	0x54	/* @ltoff(@fptr(s+a)), data4 MSB */
+#define R_IA64_LTOFF_FPTR32LSB	0x55	/* @ltoff(@fptr(s+a)), data4 LSB */
+#define R_IA64_LTOFF_FPTR64MSB	0x56	/* @ltoff(@fptr(s+a)), data8 MSB */
+#define R_IA64_LTOFF_FPTR64LSB	0x57	/* @ltoff(@fptr(s+a)), data8 LSB */
+#define R_IA64_SEGREL32MSB	0x5c	/* @segrel(sym + add), data4 MSB */
+#define R_IA64_SEGREL32LSB	0x5d	/* @segrel(sym + add), data4 LSB */
+#define R_IA64_SEGREL64MSB	0x5e	/* @segrel(sym + add), data8 MSB */
+#define R_IA64_SEGREL64LSB	0x5f	/* @segrel(sym + add), data8 LSB */
+#define R_IA64_SECREL32MSB	0x64	/* @secrel(sym + add), data4 MSB */
+#define R_IA64_SECREL32LSB	0x65	/* @secrel(sym + add), data4 LSB */
+#define R_IA64_SECREL64MSB	0x66	/* @secrel(sym + add), data8 MSB */
+#define R_IA64_SECREL64LSB	0x67	/* @secrel(sym + add), data8 LSB */
+#define R_IA64_REL32MSB		0x6c	/* data 4 + REL */
+#define R_IA64_REL32LSB		0x6d	/* data 4 + REL */
+#define R_IA64_REL64MSB		0x6e	/* data 8 + REL */
+#define R_IA64_REL64LSB		0x6f	/* data 8 + REL */
+#define R_IA64_LTV32MSB		0x74	/* symbol + addend, data4 MSB */
+#define R_IA64_LTV32LSB		0x75	/* symbol + addend, data4 LSB */
+#define R_IA64_LTV64MSB		0x76	/* symbol + addend, data8 MSB */
+#define R_IA64_LTV64LSB		0x77	/* symbol + addend, data8 LSB */
+#define R_IA64_PCREL21BI	0x79	/* @pcrel(sym + add), 21bit inst */
+#define R_IA64_PCREL22		0x7a	/* @pcrel(sym + add), 22bit inst */
+#define R_IA64_PCREL64I		0x7b	/* @pcrel(sym + add), 64bit inst */
+#define R_IA64_IPLTMSB		0x80	/* dynamic reloc, imported PLT, MSB */
+#define R_IA64_IPLTLSB		0x81	/* dynamic reloc, imported PLT, LSB */
+#define R_IA64_COPY		0x84	/* copy relocation */
+#define R_IA64_SUB		0x85	/* Addend and symbol difference */
+#define R_IA64_LTOFF22X		0x86	/* LTOFF22, relaxable.  */
+#define R_IA64_LDXMOV		0x87	/* Use of LTOFF22X.  */
+#define R_IA64_TPREL14		0x91	/* @tprel(sym + add), imm14 */
+#define R_IA64_TPREL22		0x92	/* @tprel(sym + add), imm22 */
+#define R_IA64_TPREL64I		0x93	/* @tprel(sym + add), imm64 */
+#define R_IA64_TPREL64MSB	0x96	/* @tprel(sym + add), data8 MSB */
+#define R_IA64_TPREL64LSB	0x97	/* @tprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_TPREL22	0x9a	/* @ltoff(@tprel(s+a)), imm2 */
+#define R_IA64_DTPMOD64MSB	0xa6	/* @dtpmod(sym + add), data8 MSB */
+#define R_IA64_DTPMOD64LSB	0xa7	/* @dtpmod(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPMOD22	0xaa	/* @ltoff(@dtpmod(sym + add)), imm22 */
+#define R_IA64_DTPREL14		0xb1	/* @dtprel(sym + add), imm14 */
+#define R_IA64_DTPREL22		0xb2	/* @dtprel(sym + add), imm22 */
+#define R_IA64_DTPREL64I	0xb3	/* @dtprel(sym + add), imm64 */
+#define R_IA64_DTPREL32MSB	0xb4	/* @dtprel(sym + add), data4 MSB */
+#define R_IA64_DTPREL32LSB	0xb5	/* @dtprel(sym + add), data4 LSB */
+#define R_IA64_DTPREL64MSB	0xb6	/* @dtprel(sym + add), data8 MSB */
+#define R_IA64_DTPREL64LSB	0xb7	/* @dtprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPREL22	0xba	/* @ltoff(@dtprel(s+a)), imm22 */
+
+typedef struct elf32_rel {
+  Elf32_Addr	r_offset;
+  Elf32_Word	r_info;
+} Elf32_Rel;
+
+typedef struct elf64_rel {
+  Elf64_Addr r_offset;	/* Location at which to apply the action */
+  Elf64_Xword r_info;	/* index and type of relocation */
+} Elf64_Rel;
+
+typedef struct elf32_rela{
+  Elf32_Addr	r_offset;
+  Elf32_Word	r_info;
+  Elf32_Sword	r_addend;
+} Elf32_Rela;
+
+typedef struct elf64_rela {
+  Elf64_Addr r_offset;	/* Location at which to apply the action */
+  Elf64_Xword r_info;	/* index and type of relocation */
+  Elf64_Sxword r_addend;	/* Constant addend used to compute value */
+} Elf64_Rela;
+
+typedef struct elf32_sym{
+  Elf32_Word	st_name;
+  Elf32_Addr	st_value;
+  Elf32_Word	st_size;
+  unsigned char	st_info;
+  unsigned char	st_other;
+  Elf32_Half	st_shndx;
+} Elf32_Sym;
+
+typedef struct elf64_sym {
+  Elf64_Word st_name;		/* Symbol name, index in string tbl */
+  unsigned char	st_info;	/* Type and binding attributes */
+  unsigned char	st_other;	/* No defined meaning, 0 */
+  Elf64_Half st_shndx;		/* Associated section index */
+  Elf64_Addr st_value;		/* Value of the symbol */
+  Elf64_Xword st_size;		/* Associated symbol size */
+} Elf64_Sym;
+
+
+#define EI_NIDENT	16
+
+typedef struct elf32_hdr{
+  unsigned char	e_ident[EI_NIDENT];
+  Elf32_Half	e_type;
+  Elf32_Half	e_machine;
+  Elf32_Word	e_version;
+  Elf32_Addr	e_entry;  /* Entry point */
+  Elf32_Off	e_phoff;
+  Elf32_Off	e_shoff;
+  Elf32_Word	e_flags;
+  Elf32_Half	e_ehsize;
+  Elf32_Half	e_phentsize;
+  Elf32_Half	e_phnum;
+  Elf32_Half	e_shentsize;
+  Elf32_Half	e_shnum;
+  Elf32_Half	e_shstrndx;
+} Elf32_Ehdr;
+
+typedef struct elf64_hdr {
+  unsigned char	e_ident[16];		/* ELF "magic number" */
+  Elf64_Half e_type;
+  Elf64_Half e_machine;
+  Elf64_Word e_version;
+  Elf64_Addr e_entry;		/* Entry point virtual address */
+  Elf64_Off e_phoff;		/* Program header table file offset */
+  Elf64_Off e_shoff;		/* Section header table file offset */
+  Elf64_Word e_flags;
+  Elf64_Half e_ehsize;
+  Elf64_Half e_phentsize;
+  Elf64_Half e_phnum;
+  Elf64_Half e_shentsize;
+  Elf64_Half e_shnum;
+  Elf64_Half e_shstrndx;
+} Elf64_Ehdr;
+
+/* These constants define the permissions on sections in the program
+   header, p_flags. */
+#define PF_R		0x4
+#define PF_W		0x2
+#define PF_X		0x1
+
+typedef struct elf32_phdr{
+  Elf32_Word	p_type;
+  Elf32_Off	p_offset;
+  Elf32_Addr	p_vaddr;
+  Elf32_Addr	p_paddr;
+  Elf32_Word	p_filesz;
+  Elf32_Word	p_memsz;
+  Elf32_Word	p_flags;
+  Elf32_Word	p_align;
+} Elf32_Phdr;
+
+typedef struct elf64_phdr {
+  Elf64_Word p_type;
+  Elf64_Word p_flags;
+  Elf64_Off p_offset;		/* Segment file offset */
+  Elf64_Addr p_vaddr;		/* Segment virtual address */
+  Elf64_Addr p_paddr;		/* Segment physical address */
+  Elf64_Xword p_filesz;		/* Segment size in file */
+  Elf64_Xword p_memsz;		/* Segment size in memory */
+  Elf64_Xword p_align;		/* Segment alignment, file & memory */
+} Elf64_Phdr;
+
+/* sh_type */
+#define SHT_NULL	0
+#define SHT_PROGBITS	1
+#define SHT_SYMTAB	2
+#define SHT_STRTAB	3
+#define SHT_RELA	4
+#define SHT_HASH	5
+#define SHT_DYNAMIC	6
+#define SHT_NOTE	7
+#define SHT_NOBITS	8
+#define SHT_REL		9
+#define SHT_SHLIB	10
+#define SHT_DYNSYM	11
+#define SHT_NUM		12
+#define SHT_LOPROC	0x70000000
+#define SHT_HIPROC	0x7fffffff
+#define SHT_LOUSER	0x80000000
+#define SHT_HIUSER	0xffffffff
+#define SHT_MIPS_LIST		0x70000000
+#define SHT_MIPS_CONFLICT	0x70000002
+#define SHT_MIPS_GPTAB		0x70000003
+#define SHT_MIPS_UCODE		0x70000004
+
+/* sh_flags */
+#define SHF_WRITE	0x1
+#define SHF_ALLOC	0x2
+#define SHF_EXECINSTR	0x4
+#define SHF_MASKPROC	0xf0000000
+#define SHF_MIPS_GPREL	0x10000000
+
+/* special section indexes */
+#define SHN_UNDEF	0
+#define SHN_LORESERVE	0xff00
+#define SHN_LOPROC	0xff00
+#define SHN_HIPROC	0xff1f
+#define SHN_ABS		0xfff1
+#define SHN_COMMON	0xfff2
+#define SHN_HIRESERVE	0xffff
+#define SHN_MIPS_ACCOMON	0xff00
+
+typedef struct elf32_shdr {
+  Elf32_Word	sh_name;
+  Elf32_Word	sh_type;
+  Elf32_Word	sh_flags;
+  Elf32_Addr	sh_addr;
+  Elf32_Off	sh_offset;
+  Elf32_Word	sh_size;
+  Elf32_Word	sh_link;
+  Elf32_Word	sh_info;
+  Elf32_Word	sh_addralign;
+  Elf32_Word	sh_entsize;
+} Elf32_Shdr;
+
+typedef struct elf64_shdr {
+  Elf64_Word sh_name;		/* Section name, index in string tbl */
+  Elf64_Word sh_type;		/* Type of section */
+  Elf64_Xword sh_flags;		/* Miscellaneous section attributes */
+  Elf64_Addr sh_addr;		/* Section virtual addr at execution */
+  Elf64_Off sh_offset;		/* Section file offset */
+  Elf64_Xword sh_size;		/* Size of section in bytes */
+  Elf64_Word sh_link;		/* Index of another section */
+  Elf64_Word sh_info;		/* Additional section information */
+  Elf64_Xword sh_addralign;	/* Section alignment */
+  Elf64_Xword sh_entsize;	/* Entry size if section holds table */
+} Elf64_Shdr;
+
+#define	EI_MAG0		0		/* e_ident[] indexes */
+#define	EI_MAG1		1
+#define	EI_MAG2		2
+#define	EI_MAG3		3
+#define	EI_CLASS	4
+#define	EI_DATA		5
+#define	EI_VERSION	6
+#define	EI_PAD		7
+
+#define	ELFMAG0		0x7f		/* EI_MAG */
+#define	ELFMAG1		'E'
+#define	ELFMAG2		'L'
+#define	ELFMAG3		'F'
+#define	ELFMAG		"\177ELF"
+#define	SELFMAG		4
+
+#define	ELFCLASSNONE	0		/* EI_CLASS */
+#define	ELFCLASS32	1
+#define	ELFCLASS64	2
+#define	ELFCLASSNUM	3
+
+#define ELFDATANONE	0		/* e_ident[EI_DATA] */
+#define ELFDATA2LSB	1
+#define ELFDATA2MSB	2
+
+#define EV_NONE		0		/* e_version, EI_VERSION */
+#define EV_CURRENT	1
+#define EV_NUM		2
+
+/* Notes used in ET_CORE */
+#define NT_PRSTATUS	1
+#define NT_PRFPREG	2
+#define NT_PRPSINFO	3
+#define NT_TASKSTRUCT	4
+#define NT_PRXFPREG     0x46e62b7f      /* copied from gdb5.1/include/elf/common.h */
+
+
+/* Note header in a PT_NOTE section */
+typedef struct elf32_note {
+  Elf32_Word	n_namesz;	/* Name size */
+  Elf32_Word	n_descsz;	/* Content size */
+  Elf32_Word	n_type;		/* Content type */
+} Elf32_Nhdr;
+
+/* Note header in a PT_NOTE section */
+typedef struct elf64_note {
+  Elf64_Word n_namesz;	/* Name size */
+  Elf64_Word n_descsz;	/* Content size */
+  Elf64_Word n_type;	/* Content type */
+} Elf64_Nhdr;
+
+#ifdef ELF_CLASS
+#if ELF_CLASS == ELFCLASS32
+
+#define elfhdr		elf32_hdr
+#define elf_phdr	elf32_phdr
+#define elf_note	elf32_note
+#define elf_shdr	elf32_shdr
+#define elf_sym		elf32_sym
+#define elf_addr_t	Elf32_Off
+
+#ifdef ELF_USES_RELOCA
+# define ELF_RELOC      Elf32_Rela
+#else
+# define ELF_RELOC      Elf32_Rel
+#endif
+
+#else
+
+#define elfhdr		elf64_hdr
+#define elf_phdr	elf64_phdr
+#define elf_note	elf64_note
+#define elf_shdr	elf64_shdr
+#define elf_sym		elf64_sym
+#define elf_addr_t	Elf64_Off
+
+#ifdef ELF_USES_RELOCA
+# define ELF_RELOC      Elf64_Rela
+#else
+# define ELF_RELOC      Elf64_Rel
+#endif
+
+#endif /* ELF_CLASS */
+
+#ifndef ElfW
+# if ELF_CLASS == ELFCLASS32
+#  define ElfW(x)  Elf32_ ## x
+#  define ELFW(x)  ELF32_ ## x
+# else
+#  define ElfW(x)  Elf64_ ## x
+#  define ELFW(x)  ELF64_ ## x
+# endif
+#endif
+
+#endif /* ELF_CLASS */
+
+
+#endif /* _QEMU_ELF_H */
diff --git a/elf_ops.h b/elf_ops.h
new file mode 100644
index 0000000..6126565
--- /dev/null
+++ b/elf_ops.h
@@ -0,0 +1,217 @@
+static void glue(bswap_ehdr, SZ)(struct elfhdr *ehdr)
+{
+    bswap16s(&ehdr->e_type);			/* Object file type */
+    bswap16s(&ehdr->e_machine);		/* Architecture */
+    bswap32s(&ehdr->e_version);		/* Object file version */
+    bswapSZs(&ehdr->e_entry);		/* Entry point virtual address */
+    bswapSZs(&ehdr->e_phoff);		/* Program header table file offset */
+    bswapSZs(&ehdr->e_shoff);		/* Section header table file offset */
+    bswap32s(&ehdr->e_flags);		/* Processor-specific flags */
+    bswap16s(&ehdr->e_ehsize);		/* ELF header size in bytes */
+    bswap16s(&ehdr->e_phentsize);		/* Program header table entry size */
+    bswap16s(&ehdr->e_phnum);		/* Program header table entry count */
+    bswap16s(&ehdr->e_shentsize);		/* Section header table entry size */
+    bswap16s(&ehdr->e_shnum);		/* Section header table entry count */
+    bswap16s(&ehdr->e_shstrndx);		/* Section header string table index */
+}
+
+static void glue(bswap_phdr, SZ)(struct elf_phdr *phdr)
+{
+    bswap32s(&phdr->p_type);			/* Segment type */
+    bswapSZs(&phdr->p_offset);		/* Segment file offset */
+    bswapSZs(&phdr->p_vaddr);		/* Segment virtual address */
+    bswapSZs(&phdr->p_paddr);		/* Segment physical address */
+    bswapSZs(&phdr->p_filesz);		/* Segment size in file */
+    bswapSZs(&phdr->p_memsz);		/* Segment size in memory */
+    bswap32s(&phdr->p_flags);		/* Segment flags */
+    bswapSZs(&phdr->p_align);		/* Segment alignment */
+}
+
+static void glue(bswap_shdr, SZ)(struct elf_shdr *shdr)
+{
+    bswap32s(&shdr->sh_name);
+    bswap32s(&shdr->sh_type);
+    bswapSZs(&shdr->sh_flags);
+    bswapSZs(&shdr->sh_addr);
+    bswapSZs(&shdr->sh_offset);
+    bswapSZs(&shdr->sh_size);
+    bswap32s(&shdr->sh_link);
+    bswap32s(&shdr->sh_info);
+    bswapSZs(&shdr->sh_addralign);
+    bswapSZs(&shdr->sh_entsize);
+}
+
+static void glue(bswap_sym, SZ)(struct elf_sym *sym)
+{
+    bswap32s(&sym->st_name);
+    bswapSZs(&sym->st_value);
+    bswapSZs(&sym->st_size);
+    bswap16s(&sym->st_shndx);
+}
+
+static struct elf_shdr *glue(find_section, SZ)(struct elf_shdr *shdr_table,
+                                               int n, int type)
+{
+    int i;
+    for(i=0;i<n;i++) {
+        if (shdr_table[i].sh_type == type)
+            return shdr_table + i;
+    }
+    return NULL;
+}
+
+static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab)
+{
+    struct elf_shdr *symtab, *strtab, *shdr_table = NULL;
+    struct elf_sym *syms = NULL;
+#if (SZ == 64)
+    struct elf32_sym *syms32 = NULL;
+#endif
+    struct syminfo *s;
+    int nsyms, i;
+    char *str = NULL;
+
+    shdr_table = load_at(fd, ehdr->e_shoff,
+                         sizeof(struct elf_shdr) * ehdr->e_shnum);
+    if (!shdr_table)
+        return -1;
+
+    if (must_swab) {
+        for (i = 0; i < ehdr->e_shnum; i++) {
+            glue(bswap_shdr, SZ)(shdr_table + i);
+        }
+    }
+
+    symtab = glue(find_section, SZ)(shdr_table, ehdr->e_shnum, SHT_SYMTAB);
+    if (!symtab)
+        goto fail;
+    syms = load_at(fd, symtab->sh_offset, symtab->sh_size);
+    if (!syms)
+        goto fail;
+
+    nsyms = symtab->sh_size / sizeof(struct elf_sym);
+#if (SZ == 64)
+    syms32 = qemu_mallocz(nsyms * sizeof(struct elf32_sym));
+#endif
+    for (i = 0; i < nsyms; i++) {
+        if (must_swab)
+            glue(bswap_sym, SZ)(&syms[i]);
+#if (SZ == 64)
+	syms32[i].st_name = syms[i].st_name;
+	syms32[i].st_info = syms[i].st_info;
+	syms32[i].st_other = syms[i].st_other;
+	syms32[i].st_shndx = syms[i].st_shndx;
+	syms32[i].st_value = syms[i].st_value & 0xffffffff;
+	syms32[i].st_size = syms[i].st_size & 0xffffffff;
+#endif
+    }
+    /* String table */
+    if (symtab->sh_link >= ehdr->e_shnum)
+        goto fail;
+    strtab = &shdr_table[symtab->sh_link];
+
+    str = load_at(fd, strtab->sh_offset, strtab->sh_size);
+    if (!str)
+	goto fail;
+
+    /* Commit */
+    s = qemu_mallocz(sizeof(*s));
+#if (SZ == 64)
+    s->disas_symtab = syms32;
+    qemu_free(syms);
+#else
+    s->disas_symtab = syms;
+#endif
+    s->disas_num_syms = nsyms;
+    s->disas_strtab = str;
+    s->next = syminfos;
+    syminfos = s;
+    qemu_free(shdr_table);
+    return 0;
+ fail:
+#if (SZ == 64)
+    qemu_free(syms32);
+#endif
+    qemu_free(syms);
+    qemu_free(str);
+    qemu_free(shdr_table);
+    return -1;
+}
+
+static int glue(load_elf, SZ)(int fd, int64_t virt_to_phys_addend,
+                              int must_swab, uint64_t *pentry,
+                              uint64_t *lowaddr, uint64_t *highaddr)
+{
+    struct elfhdr ehdr;
+    struct elf_phdr *phdr = NULL, *ph;
+    int size, i, total_size;
+    elf_word mem_size;
+    uint64_t addr, low = 0, high = 0;
+    uint8_t *data = NULL;
+
+    if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
+        goto fail;
+    if (must_swab) {
+        glue(bswap_ehdr, SZ)(&ehdr);
+    }
+
+    if (ELF_MACHINE != ehdr.e_machine)
+        goto fail;
+
+    if (pentry)
+   	*pentry = (uint64_t)(elf_sword)ehdr.e_entry;
+
+    glue(load_symbols, SZ)(&ehdr, fd, must_swab);
+
+    size = ehdr.e_phnum * sizeof(phdr[0]);
+    lseek(fd, ehdr.e_phoff, SEEK_SET);
+    phdr = qemu_mallocz(size);
+    if (!phdr)
+        goto fail;
+    if (read(fd, phdr, size) != size)
+        goto fail;
+    if (must_swab) {
+        for(i = 0; i < ehdr.e_phnum; i++) {
+            ph = &phdr[i];
+            glue(bswap_phdr, SZ)(ph);
+        }
+    }
+
+    total_size = 0;
+    for(i = 0; i < ehdr.e_phnum; i++) {
+        ph = &phdr[i];
+        if (ph->p_type == PT_LOAD) {
+            mem_size = ph->p_memsz;
+            /* XXX: avoid allocating */
+            data = qemu_mallocz(mem_size);
+            if (ph->p_filesz > 0) {
+                if (lseek(fd, ph->p_offset, SEEK_SET) < 0)
+                    goto fail;
+                if (read(fd, data, ph->p_filesz) != ph->p_filesz)
+                    goto fail;
+            }
+            addr = ph->p_vaddr + virt_to_phys_addend;
+
+            cpu_physical_memory_write_rom(addr, data, mem_size);
+
+            total_size += mem_size;
+            if (!low || addr < low)
+                low = addr;
+            if (!high || (addr + mem_size) > high)
+                high = addr + mem_size;
+
+            qemu_free(data);
+            data = NULL;
+        }
+    }
+    qemu_free(phdr);
+    if (lowaddr)
+        *lowaddr = (uint64_t)(elf_sword)low;
+    if (highaddr)
+        *highaddr = (uint64_t)(elf_sword)high;
+    return total_size;
+ fail:
+    qemu_free(data);
+    qemu_free(phdr);
+    return -1;
+}
diff --git a/exec-all.h b/exec-all.h
new file mode 100644
index 0000000..a223bff
--- /dev/null
+++ b/exec-all.h
@@ -0,0 +1,392 @@
+/*
+ * internal execution defines for qemu
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* allow to see translation results - the slowdown should be negligible, so we leave it */
+#define DEBUG_DISAS
+
+/* is_jmp field values */
+#define DISAS_NEXT    0 /* next instruction can be analyzed */
+#define DISAS_JUMP    1 /* only pc was modified dynamically */
+#define DISAS_UPDATE  2 /* cpu state was modified dynamically */
+#define DISAS_TB_JUMP 3 /* only pc was modified statically */
+
+typedef struct TranslationBlock TranslationBlock;
+
+/* XXX: make safe guess about sizes */
+#define MAX_OP_PER_INSTR 64
+/* A Call op needs up to 6 + 2N parameters (N = number of arguments).  */
+#define MAX_OPC_PARAM 10
+#define OPC_BUF_SIZE 512
+#define OPC_MAX_SIZE (OPC_BUF_SIZE - MAX_OP_PER_INSTR)
+
+/* Maximum size a TCG op can expand to.  This is complicated because a
+   single op may require several host instructions and regirster reloads.
+   For now take a wild guess at 128 bytes, which should allow at least
+   a couple of fixup instructions per argument.  */
+#define TCG_MAX_OP_SIZE 128
+
+#define OPPARAM_BUF_SIZE (OPC_BUF_SIZE * MAX_OPC_PARAM)
+
+extern target_ulong gen_opc_pc[OPC_BUF_SIZE];
+extern target_ulong gen_opc_npc[OPC_BUF_SIZE];
+extern uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+extern uint16_t gen_opc_icount[OPC_BUF_SIZE];
+extern target_ulong gen_opc_jump_pc[2];
+extern uint32_t gen_opc_hflags[OPC_BUF_SIZE];
+
+typedef void (GenOpFunc)(void);
+typedef void (GenOpFunc1)(long);
+typedef void (GenOpFunc2)(long, long);
+typedef void (GenOpFunc3)(long, long, long);
+
+#include "qemu-log.h"
+
+void gen_intermediate_code(CPUState *env, struct TranslationBlock *tb);
+void gen_intermediate_code_pc(CPUState *env, struct TranslationBlock *tb);
+void gen_pc_load(CPUState *env, struct TranslationBlock *tb,
+                 unsigned long searched_pc, int pc_pos, void *puc);
+
+unsigned long code_gen_max_block_size(void);
+void cpu_gen_init(void);
+int cpu_gen_code(CPUState *env, struct TranslationBlock *tb,
+                 int *gen_code_size_ptr);
+int cpu_restore_state(struct TranslationBlock *tb,
+                      CPUState *env, unsigned long searched_pc,
+                      void *puc);
+int cpu_restore_state_copy(struct TranslationBlock *tb,
+                           CPUState *env, unsigned long searched_pc,
+                           void *puc);
+void cpu_resume_from_signal(CPUState *env1, void *puc);
+void cpu_io_recompile(CPUState *env, void *retaddr);
+TranslationBlock *tb_gen_code(CPUState *env, 
+                              target_ulong pc, target_ulong cs_base, int flags,
+                              int cflags);
+void cpu_exec_init(CPUState *env);
+int page_unprotect(target_ulong address, unsigned long pc, void *puc);
+void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end,
+                                   int is_cpu_write_access);
+void tb_invalidate_page_range(target_ulong start, target_ulong end);
+void tlb_flush_page(CPUState *env, target_ulong addr);
+void tlb_flush(CPUState *env, int flush_global);
+int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+                      target_phys_addr_t paddr, int prot,
+                      int mmu_idx, int is_softmmu);
+static inline int tlb_set_page(CPUState *env1, target_ulong vaddr,
+                               target_phys_addr_t paddr, int prot,
+                               int mmu_idx, int is_softmmu)
+{
+    if (prot & PAGE_READ)
+        prot |= PAGE_EXEC;
+    return tlb_set_page_exec(env1, vaddr, paddr, prot, mmu_idx, is_softmmu);
+}
+
+#define CODE_GEN_ALIGN           16 /* must be >= of the size of a icache line */
+
+#define CODE_GEN_PHYS_HASH_BITS     15
+#define CODE_GEN_PHYS_HASH_SIZE     (1 << CODE_GEN_PHYS_HASH_BITS)
+
+#define MIN_CODE_GEN_BUFFER_SIZE     (1024 * 1024)
+
+/* estimated block size for TB allocation */
+/* XXX: use a per code average code fragment size and modulate it
+   according to the host CPU */
+#if defined(CONFIG_SOFTMMU)
+#define CODE_GEN_AVG_BLOCK_SIZE 128
+#else
+#define CODE_GEN_AVG_BLOCK_SIZE 64
+#endif
+
+#if defined(__powerpc__) || defined(__x86_64__) || defined(__arm__)
+#define USE_DIRECT_JUMP
+#endif
+#if defined(__i386__) && !defined(_WIN32)
+#define USE_DIRECT_JUMP
+#endif
+
+struct TranslationBlock {
+    target_ulong pc;   /* simulated PC corresponding to this block (EIP + CS base) */
+    target_ulong cs_base; /* CS base for this block */
+    uint64_t flags; /* flags defining in which context the code was generated */
+    uint16_t size;      /* size of target code for this block (1 <=
+                           size <= TARGET_PAGE_SIZE) */
+    uint16_t cflags;    /* compile flags */
+#define CF_COUNT_MASK  0x7fff
+#define CF_LAST_IO     0x8000 /* Last insn may be an IO access.  */
+
+    uint8_t *tc_ptr;    /* pointer to the translated code */
+    /* next matching tb for physical address. */
+    struct TranslationBlock *phys_hash_next;
+    /* first and second physical page containing code. The lower bit
+       of the pointer tells the index in page_next[] */
+    struct TranslationBlock *page_next[2];
+    target_ulong page_addr[2];
+
+    /* the following data are used to directly call another TB from
+       the code of this one. */
+    uint16_t tb_next_offset[2]; /* offset of original jump target */
+#ifdef USE_DIRECT_JUMP
+    uint16_t tb_jmp_offset[4]; /* offset of jump instruction */
+#else
+    unsigned long tb_next[2]; /* address of jump generated code */
+#endif
+    /* list of TBs jumping to this one. This is a circular list using
+       the two least significant bits of the pointers to tell what is
+       the next pointer: 0 = jmp_next[0], 1 = jmp_next[1], 2 =
+       jmp_first */
+    struct TranslationBlock *jmp_next[2];
+    struct TranslationBlock *jmp_first;
+
+#ifdef CONFIG_TRACE
+    struct BBRec *bb_rec;
+    uint64_t prev_time;
+#endif
+    uint32_t icount;
+};
+
+static inline unsigned int tb_jmp_cache_hash_page(target_ulong pc)
+{
+    target_ulong tmp;
+    tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
+    return (tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK;
+}
+
+static inline unsigned int tb_jmp_cache_hash_func(target_ulong pc)
+{
+    target_ulong tmp;
+    tmp = pc ^ (pc >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS));
+    return (((tmp >> (TARGET_PAGE_BITS - TB_JMP_PAGE_BITS)) & TB_JMP_PAGE_MASK)
+	    | (tmp & TB_JMP_ADDR_MASK));
+}
+
+static inline unsigned int tb_phys_hash_func(unsigned long pc)
+{
+    return pc & (CODE_GEN_PHYS_HASH_SIZE - 1);
+}
+
+TranslationBlock *tb_alloc(target_ulong pc);
+void tb_free(TranslationBlock *tb);
+void tb_flush(CPUState *env);
+void tb_link_phys(TranslationBlock *tb,
+                  target_ulong phys_pc, target_ulong phys_page2);
+void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr);
+
+extern TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
+extern uint8_t *code_gen_ptr;
+extern int code_gen_max_blocks;
+
+#if defined(USE_DIRECT_JUMP)
+
+#if defined(__powerpc__)
+extern void ppc_tb_set_jmp_target(unsigned long jmp_addr, unsigned long addr);
+#define tb_set_jmp_target1 ppc_tb_set_jmp_target
+#elif defined(__i386__) || defined(__x86_64__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+    /* patch the branch destination */
+    *(uint32_t *)jmp_addr = addr - (jmp_addr + 4);
+    /* no need to flush icache explicitly */
+}
+#elif defined(__arm__)
+static inline void tb_set_jmp_target1(unsigned long jmp_addr, unsigned long addr)
+{
+    register unsigned long _beg __asm ("a1");
+    register unsigned long _end __asm ("a2");
+    register unsigned long _flg __asm ("a3");
+
+    /* we could use a ldr pc, [pc, #-4] kind of branch and avoid the flush */
+    *(uint32_t *)jmp_addr |= ((addr - (jmp_addr + 8)) >> 2) & 0xffffff;
+
+    /* flush icache */
+    _beg = jmp_addr;
+    _end = jmp_addr + 4;
+    _flg = 0;
+    __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
+}
+#endif
+
+static inline void tb_set_jmp_target(TranslationBlock *tb,
+                                     int n, unsigned long addr)
+{
+    unsigned long offset;
+
+    offset = tb->tb_jmp_offset[n];
+    tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
+    offset = tb->tb_jmp_offset[n + 2];
+    if (offset != 0xffff)
+        tb_set_jmp_target1((unsigned long)(tb->tc_ptr + offset), addr);
+}
+
+#else
+
+/* set the jump target */
+static inline void tb_set_jmp_target(TranslationBlock *tb,
+                                     int n, unsigned long addr)
+{
+    tb->tb_next[n] = addr;
+}
+
+#endif
+
+static inline void tb_add_jump(TranslationBlock *tb, int n,
+                               TranslationBlock *tb_next)
+{
+    /* NOTE: this test is only needed for thread safety */
+    if (!tb->jmp_next[n]) {
+        /* patch the native jump address */
+        tb_set_jmp_target(tb, n, (unsigned long)tb_next->tc_ptr);
+
+        /* add in TB jmp circular list */
+        tb->jmp_next[n] = tb_next->jmp_first;
+        tb_next->jmp_first = (TranslationBlock *)((long)(tb) | (n));
+    }
+}
+
+TranslationBlock *tb_find_pc(unsigned long pc_ptr);
+
+#if defined(_WIN32)
+#define ASM_DATA_SECTION ".section \".data\"\n"
+#define ASM_PREVIOUS_SECTION ".section .text\n"
+#elif defined(__APPLE__)
+#define ASM_DATA_SECTION ".data\n"
+#define ASM_PREVIOUS_SECTION ".text\n"
+#else
+#define ASM_DATA_SECTION ".section \".data\"\n"
+#define ASM_PREVIOUS_SECTION ".previous\n"
+#endif
+
+#define ASM_OP_LABEL_NAME(n, opname) \
+    ASM_NAME(__op_label) #n "." ASM_NAME(opname)
+
+extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
+extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+extern void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+
+#include "qemu-lock.h"
+
+extern spinlock_t tb_lock;
+
+extern int tb_invalidated_flag;
+
+#if !defined(CONFIG_USER_ONLY)
+
+void tlb_fill(target_ulong addr, int is_write, int mmu_idx,
+              void *retaddr);
+
+#include "softmmu_defs.h"
+
+#define ACCESS_TYPE (NB_MMU_MODES + 1)
+#define MEMSUFFIX _code
+#define env cpu_single_env
+
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+#undef env
+
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr)
+{
+    return addr;
+}
+#else
+/* NOTE: this function can trigger an exception */
+/* NOTE2: the returned address is not exactly the physical address: it
+   is the offset relative to phys_ram_base */
+static inline target_ulong get_phys_addr_code(CPUState *env1, target_ulong addr)
+{
+    int mmu_idx, page_index, pd;
+
+    page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    mmu_idx = cpu_mmu_index(env1);
+    if (unlikely(env1->tlb_table[mmu_idx][page_index].addr_code !=
+                 (addr & TARGET_PAGE_MASK))) {
+        ldub_code(addr);
+    }
+    pd = env1->tlb_table[mmu_idx][page_index].addr_code & ~TARGET_PAGE_MASK;
+    if (pd > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+#if defined(TARGET_SPARC) || defined(TARGET_MIPS)
+        do_unassigned_access(addr, 0, 1, 0);
+#else
+        cpu_abort(env1, "Trying to execute code outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);
+#endif
+    }
+    return addr + env1->tlb_table[mmu_idx][page_index].addend - (unsigned long)phys_ram_base;
+}
+
+/* Deterministic execution requires that IO only be performed on the last
+   instruction of a TB so that interrupts take effect immediately.  */
+static inline int can_do_io(CPUState *env)
+{
+    if (!use_icount)
+        return 1;
+
+    /* If not executing code then assume we are ok.  */
+    if (!env->current_tb)
+        return 1;
+
+    return env->can_do_io != 0;
+}
+#endif
+
+#ifdef USE_KQEMU
+#define KQEMU_MODIFY_PAGE_MASK (0xff & ~(VGA_DIRTY_FLAG | CODE_DIRTY_FLAG))
+
+#define MSR_QPI_COMMBASE 0xfabe0010
+
+int kqemu_init(CPUState *env);
+int kqemu_cpu_exec(CPUState *env);
+void kqemu_flush_page(CPUState *env, target_ulong addr);
+void kqemu_flush(CPUState *env, int global);
+void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr);
+void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr);
+void kqemu_set_phys_mem(uint64_t start_addr, ram_addr_t size, 
+                        ram_addr_t phys_offset);
+void kqemu_cpu_interrupt(CPUState *env);
+void kqemu_record_dump(void);
+
+extern uint32_t kqemu_comm_base;
+
+static inline int kqemu_is_ok(CPUState *env)
+{
+    return(env->kqemu_enabled &&
+           (env->cr[0] & CR0_PE_MASK) &&
+           !(env->hflags & HF_INHIBIT_IRQ_MASK) &&
+           (env->eflags & IF_MASK) &&
+           !(env->eflags & VM_MASK) &&
+           (env->kqemu_enabled == 2 ||
+            ((env->hflags & HF_CPL_MASK) == 3 &&
+             (env->eflags & IOPL_MASK) != IOPL_MASK)));
+}
+
+#endif
diff --git a/exec.c b/exec.c
new file mode 100644
index 0000000..547801b
--- /dev/null
+++ b/exec.c
@@ -0,0 +1,3201 @@
+/*
+ *  virtual page mapping and translated block handling
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <sys/types.h>
+#include <sys/mman.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "qemu-common.h"
+#include "tcg.h"
+#include "hw/hw.h"
+#if defined(CONFIG_USER_ONLY)
+#include <qemu.h>
+#endif
+
+//#define DEBUG_TB_INVALIDATE
+//#define DEBUG_FLUSH
+//#define DEBUG_TLB
+//#define DEBUG_UNASSIGNED
+
+/* make various TB consistency checks */
+//#define DEBUG_TB_CHECK
+//#define DEBUG_TLB_CHECK
+
+//#define DEBUG_IOPORT
+//#define DEBUG_SUBPAGE
+
+#if !defined(CONFIG_USER_ONLY)
+/* TB consistency checks only implemented for usermode emulation.  */
+#undef DEBUG_TB_CHECK
+#endif
+
+#define SMC_BITMAP_USE_THRESHOLD 10
+
+#define MMAP_AREA_START        0x00000000
+#define MMAP_AREA_END          0xa8000000
+
+#if defined(TARGET_SPARC64)
+#define TARGET_PHYS_ADDR_SPACE_BITS 41
+#elif defined(TARGET_SPARC)
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#elif defined(TARGET_ALPHA)
+#define TARGET_PHYS_ADDR_SPACE_BITS 42
+#define TARGET_VIRT_ADDR_SPACE_BITS 42
+#elif defined(TARGET_PPC64)
+#define TARGET_PHYS_ADDR_SPACE_BITS 42
+#elif defined(TARGET_X86_64) && !defined(USE_KQEMU)
+#define TARGET_PHYS_ADDR_SPACE_BITS 42
+#elif defined(TARGET_I386) && !defined(USE_KQEMU)
+#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#else
+/* Note: for compatibility with kqemu, we use 32 bits for x86_64 */
+#define TARGET_PHYS_ADDR_SPACE_BITS 32
+#endif
+
+TranslationBlock *tbs;
+int code_gen_max_blocks;
+TranslationBlock *tb_phys_hash[CODE_GEN_PHYS_HASH_SIZE];
+int nb_tbs;
+/* any access to the tbs or the page table must use this lock */
+spinlock_t tb_lock = SPIN_LOCK_UNLOCKED;
+
+#if defined(__arm__) || defined(__sparc_v9__)
+/* The prologue must be reachable with a direct jump. ARM and Sparc64
+ have limited branch ranges (possibly also PPC) so place it in a
+ section close to code segment. */
+#define code_gen_section                                \
+    __attribute__((__section__(".gen_code")))           \
+    __attribute__((aligned (32)))
+#else
+#define code_gen_section                                \
+    __attribute__((aligned (32)))
+#endif
+
+uint8_t code_gen_prologue[1024] code_gen_section;
+uint8_t *code_gen_buffer;
+unsigned long code_gen_buffer_size;
+/* threshold to flush the translated code buffer */
+unsigned long code_gen_buffer_max_size; 
+uint8_t *code_gen_ptr;
+
+#if !defined(CONFIG_USER_ONLY)
+ram_addr_t phys_ram_size;
+int phys_ram_fd;
+uint8_t *phys_ram_base;
+uint8_t *phys_ram_dirty;
+static ram_addr_t phys_ram_alloc_offset = 0;
+#endif
+
+CPUState *first_cpu;
+/* current CPU in the current thread. It is only valid inside
+   cpu_exec() */
+CPUState *cpu_single_env;
+/* 0 = Do not count executed instructions.
+   1 = Precise instruction counting.
+   2 = Adaptive rate instruction counting.  */
+int use_icount = 0;
+/* Current instruction counter.  While executing translated code this may
+   include some instructions that have not yet been executed.  */
+int64_t qemu_icount;
+
+typedef struct PageDesc {
+    /* list of TBs intersecting this ram page */
+    TranslationBlock *first_tb;
+    /* in order to optimize self modifying code, we count the number
+       of lookups we do to a given page to use a bitmap */
+    unsigned int code_write_count;
+    uint8_t *code_bitmap;
+#if defined(CONFIG_USER_ONLY)
+    unsigned long flags;
+#endif
+} PageDesc;
+
+typedef struct PhysPageDesc {
+    /* offset in host memory of the page + io_index in the low bits */
+    ram_addr_t phys_offset;
+} PhysPageDesc;
+
+#define L2_BITS 10
+#if defined(CONFIG_USER_ONLY) && defined(TARGET_VIRT_ADDR_SPACE_BITS)
+/* XXX: this is a temporary hack for alpha target.
+ *      In the future, this is to be replaced by a multi-level table
+ *      to actually be able to handle the complete 64 bits address space.
+ */
+#define L1_BITS (TARGET_VIRT_ADDR_SPACE_BITS - L2_BITS - TARGET_PAGE_BITS)
+#else
+#define L1_BITS (32 - L2_BITS - TARGET_PAGE_BITS)
+#endif
+
+#define L1_SIZE (1 << L1_BITS)
+#define L2_SIZE (1 << L2_BITS)
+
+unsigned long qemu_real_host_page_size;
+unsigned long qemu_host_page_bits;
+unsigned long qemu_host_page_size;
+unsigned long qemu_host_page_mask;
+
+/* XXX: for system emulation, it could just be an array */
+static PageDesc *l1_map[L1_SIZE];
+PhysPageDesc **l1_phys_map;
+
+#if !defined(CONFIG_USER_ONLY)
+static void io_mem_init(void);
+
+/* io memory support */
+CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
+CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
+void *io_mem_opaque[IO_MEM_NB_ENTRIES];
+static int io_mem_nb;
+static int io_mem_watch;
+#endif
+
+/* log support */
+const char *logfilename = "/tmp/qemu.log";
+FILE *logfile;
+int loglevel;
+static int log_append = 0;
+
+/* statistics */
+static int tlb_flush_count;
+static int tb_flush_count;
+static int tb_phys_invalidate_count;
+
+#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK)
+typedef struct subpage_t {
+    target_phys_addr_t base;
+    CPUReadMemoryFunc **mem_read[TARGET_PAGE_SIZE][4];
+    CPUWriteMemoryFunc **mem_write[TARGET_PAGE_SIZE][4];
+    void *opaque[TARGET_PAGE_SIZE][2][4];
+} subpage_t;
+
+#ifdef _WIN32
+static void map_exec(void *addr, long size)
+{
+    DWORD old_protect;
+    VirtualProtect(addr, size,
+                   PAGE_EXECUTE_READWRITE, &old_protect);
+    
+}
+#else
+static void map_exec(void *addr, long size)
+{
+    unsigned long start, end, page_size;
+    
+    page_size = getpagesize();
+    start = (unsigned long)addr;
+    start &= ~(page_size - 1);
+    
+    end = (unsigned long)addr + size;
+    end += page_size - 1;
+    end &= ~(page_size - 1);
+    
+    mprotect((void *)start, end - start,
+             PROT_READ | PROT_WRITE | PROT_EXEC);
+}
+#endif
+
+static void page_init(void)
+{
+    /* NOTE: we can always suppose that qemu_host_page_size >=
+       TARGET_PAGE_SIZE */
+#ifdef _WIN32
+    {
+        SYSTEM_INFO system_info;
+        DWORD old_protect;
+
+        GetSystemInfo(&system_info);
+        qemu_real_host_page_size = system_info.dwPageSize;
+    }
+#else
+    qemu_real_host_page_size = getpagesize();
+#endif
+    if (qemu_host_page_size == 0)
+        qemu_host_page_size = qemu_real_host_page_size;
+    if (qemu_host_page_size < TARGET_PAGE_SIZE)
+        qemu_host_page_size = TARGET_PAGE_SIZE;
+    qemu_host_page_bits = 0;
+    while ((1 << qemu_host_page_bits) < qemu_host_page_size)
+        qemu_host_page_bits++;
+    qemu_host_page_mask = ~(qemu_host_page_size - 1);
+    l1_phys_map = qemu_vmalloc(L1_SIZE * sizeof(void *));
+    memset(l1_phys_map, 0, L1_SIZE * sizeof(void *));
+
+#if !defined(_WIN32) && defined(CONFIG_USER_ONLY)
+    {
+        long long startaddr, endaddr;
+        FILE *f;
+        int n;
+
+        mmap_lock();
+        last_brk = (unsigned long)sbrk(0);
+        f = fopen("/proc/self/maps", "r");
+        if (f) {
+            do {
+                n = fscanf (f, "%llx-%llx %*[^\n]\n", &startaddr, &endaddr);
+                if (n == 2) {
+                    startaddr = MIN(startaddr,
+                                    (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1);
+                    endaddr = MIN(endaddr,
+                                    (1ULL << TARGET_PHYS_ADDR_SPACE_BITS) - 1);
+                    page_set_flags(startaddr & TARGET_PAGE_MASK,
+                                   TARGET_PAGE_ALIGN(endaddr),
+                                   PAGE_RESERVED); 
+                }
+            } while (!feof(f));
+            fclose(f);
+        }
+        mmap_unlock();
+    }
+#endif
+}
+
+static inline PageDesc **page_l1_map(target_ulong index)
+{
+#if TARGET_LONG_BITS > 32
+    /* Host memory outside guest VM.  For 32-bit targets we have already
+       excluded high addresses.  */
+    if (index > ((target_ulong)L2_SIZE * L1_SIZE))
+        return NULL;
+#endif
+    return &l1_map[index >> L2_BITS];
+}
+
+static inline PageDesc *page_find_alloc(target_ulong index)
+{
+    PageDesc **lp, *p;
+    lp = page_l1_map(index);
+    if (!lp)
+        return NULL;
+
+    p = *lp;
+    if (!p) {
+        /* allocate if not found */
+#if defined(CONFIG_USER_ONLY)
+        unsigned long addr;
+        size_t len = sizeof(PageDesc) * L2_SIZE;
+        /* Don't use qemu_malloc because it may recurse.  */
+        p = mmap(0, len, PROT_READ | PROT_WRITE,
+                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        *lp = p;
+        addr = h2g(p);
+        if (addr == (target_ulong)addr) {
+            page_set_flags(addr & TARGET_PAGE_MASK,
+                           TARGET_PAGE_ALIGN(addr + len),
+                           PAGE_RESERVED); 
+        }
+#else
+        p = qemu_mallocz(sizeof(PageDesc) * L2_SIZE);
+        *lp = p;
+#endif
+    }
+    return p + (index & (L2_SIZE - 1));
+}
+
+static inline PageDesc *page_find(target_ulong index)
+{
+    PageDesc **lp, *p;
+    lp = page_l1_map(index);
+    if (!lp)
+        return NULL;
+
+    p = *lp;
+    if (!p)
+        return 0;
+    return p + (index & (L2_SIZE - 1));
+}
+
+static PhysPageDesc *phys_page_find_alloc(target_phys_addr_t index, int alloc)
+{
+    void **lp, **p;
+    PhysPageDesc *pd;
+
+    p = (void **)l1_phys_map;
+#if TARGET_PHYS_ADDR_SPACE_BITS > 32
+
+#if TARGET_PHYS_ADDR_SPACE_BITS > (32 + L1_BITS)
+#error unsupported TARGET_PHYS_ADDR_SPACE_BITS
+#endif
+    lp = p + ((index >> (L1_BITS + L2_BITS)) & (L1_SIZE - 1));
+    p = *lp;
+    if (!p) {
+        /* allocate if not found */
+        if (!alloc)
+            return NULL;
+        p = qemu_vmalloc(sizeof(void *) * L1_SIZE);
+        memset(p, 0, sizeof(void *) * L1_SIZE);
+        *lp = p;
+    }
+#endif
+    lp = p + ((index >> L2_BITS) & (L1_SIZE - 1));
+    pd = *lp;
+    if (!pd) {
+        int i;
+        /* allocate if not found */
+        if (!alloc)
+            return NULL;
+        pd = qemu_vmalloc(sizeof(PhysPageDesc) * L2_SIZE);
+        *lp = pd;
+        for (i = 0; i < L2_SIZE; i++)
+          pd[i].phys_offset = IO_MEM_UNASSIGNED;
+    }
+    return ((PhysPageDesc *)pd) + (index & (L2_SIZE - 1));
+}
+
+static inline PhysPageDesc *phys_page_find(target_phys_addr_t index)
+{
+    return phys_page_find_alloc(index, 0);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+static void tlb_protect_code(ram_addr_t ram_addr);
+static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr,
+                                    target_ulong vaddr);
+#define mmap_lock() do { } while(0)
+#define mmap_unlock() do { } while(0)
+#endif
+
+#define DEFAULT_CODE_GEN_BUFFER_SIZE (32 * 1024 * 1024)
+
+#if defined(CONFIG_USER_ONLY)
+/* Currently it is not recommanded to allocate big chunks of data in
+   user mode. It will change when a dedicated libc will be used */
+#define USE_STATIC_CODE_GEN_BUFFER
+#endif
+
+#ifdef USE_STATIC_CODE_GEN_BUFFER
+static uint8_t static_code_gen_buffer[DEFAULT_CODE_GEN_BUFFER_SIZE];
+#endif
+
+static void code_gen_alloc(unsigned long tb_size)
+{
+#ifdef USE_STATIC_CODE_GEN_BUFFER
+    code_gen_buffer = static_code_gen_buffer;
+    code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
+    map_exec(code_gen_buffer, code_gen_buffer_size);
+#else
+    code_gen_buffer_size = tb_size;
+    if (code_gen_buffer_size == 0) {
+#if defined(CONFIG_USER_ONLY)
+        /* in user mode, phys_ram_size is not meaningful */
+        code_gen_buffer_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
+#else
+        /* XXX: needs ajustments */
+        code_gen_buffer_size = (int)(phys_ram_size / 4);
+#endif
+    }
+    if (code_gen_buffer_size < MIN_CODE_GEN_BUFFER_SIZE)
+        code_gen_buffer_size = MIN_CODE_GEN_BUFFER_SIZE;
+    /* The code gen buffer location may have constraints depending on
+       the host cpu and OS */
+#if defined(__linux__) 
+    {
+        int flags;
+        void *start = NULL;
+
+        flags = MAP_PRIVATE | MAP_ANONYMOUS;
+#if defined(__x86_64__)
+        flags |= MAP_32BIT;
+        /* Cannot map more than that */
+        if (code_gen_buffer_size > (800 * 1024 * 1024))
+            code_gen_buffer_size = (800 * 1024 * 1024);
+#elif defined(__sparc_v9__)
+        // Map the buffer below 2G, so we can use direct calls and branches
+        flags |= MAP_FIXED;
+        start = (void *) 0x60000000UL;
+        if (code_gen_buffer_size > (512 * 1024 * 1024))
+            code_gen_buffer_size = (512 * 1024 * 1024);
+#endif
+        code_gen_buffer = mmap(start, code_gen_buffer_size,
+                               PROT_WRITE | PROT_READ | PROT_EXEC,
+                               flags, -1, 0);
+        if (code_gen_buffer == MAP_FAILED) {
+            fprintf(stderr, "Could not allocate dynamic translator buffer\n");
+            exit(1);
+        }
+    }
+#else
+    code_gen_buffer = qemu_malloc(code_gen_buffer_size);
+    if (!code_gen_buffer) {
+        fprintf(stderr, "Could not allocate dynamic translator buffer\n");
+        exit(1);
+    }
+    map_exec(code_gen_buffer, code_gen_buffer_size);
+#endif
+#endif /* !USE_STATIC_CODE_GEN_BUFFER */
+    map_exec(code_gen_prologue, sizeof(code_gen_prologue));
+    code_gen_buffer_max_size = code_gen_buffer_size - 
+        code_gen_max_block_size();
+    code_gen_max_blocks = code_gen_buffer_size / CODE_GEN_AVG_BLOCK_SIZE;
+    tbs = qemu_malloc(code_gen_max_blocks * sizeof(TranslationBlock));
+}
+
+/* Must be called before using the QEMU cpus. 'tb_size' is the size
+   (in bytes) allocated to the translation buffer. Zero means default
+   size. */
+void cpu_exec_init_all(unsigned long tb_size)
+{
+    cpu_gen_init();
+    code_gen_alloc(tb_size);
+    code_gen_ptr = code_gen_buffer;
+    page_init();
+#if !defined(CONFIG_USER_ONLY)
+    io_mem_init();
+#endif
+}
+
+#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
+
+#define CPU_COMMON_SAVE_VERSION 1
+
+static void cpu_common_save(QEMUFile *f, void *opaque)
+{
+    CPUState *env = opaque;
+
+    qemu_put_be32s(f, &env->halted);
+    qemu_put_be32s(f, &env->interrupt_request);
+}
+
+static int cpu_common_load(QEMUFile *f, void *opaque, int version_id)
+{
+    CPUState *env = opaque;
+
+    if (version_id != CPU_COMMON_SAVE_VERSION)
+        return -EINVAL;
+
+    qemu_get_be32s(f, &env->halted);
+    qemu_get_be32s(f, &env->interrupt_request);
+    tlb_flush(env, 1);
+
+    return 0;
+}
+#endif
+
+void cpu_exec_init(CPUState *env)
+{
+    CPUState **penv;
+    int cpu_index;
+
+    env->next_cpu = NULL;
+    penv = &first_cpu;
+    cpu_index = 0;
+    while (*penv != NULL) {
+        penv = (CPUState **)&(*penv)->next_cpu;
+        cpu_index++;
+    }
+    env->cpu_index = cpu_index;
+    env->nb_watchpoints = 0;
+    *penv = env;
+#if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY)
+    register_savevm("cpu_common", cpu_index, CPU_COMMON_SAVE_VERSION,
+                    cpu_common_save, cpu_common_load, env);
+    register_savevm("cpu", cpu_index, CPU_SAVE_VERSION,
+                    cpu_save, cpu_load, env);
+#endif
+}
+
+static inline void invalidate_page_bitmap(PageDesc *p)
+{
+    if (p->code_bitmap) {
+        qemu_free(p->code_bitmap);
+        p->code_bitmap = NULL;
+    }
+    p->code_write_count = 0;
+}
+
+/* set to NULL all the 'first_tb' fields in all PageDescs */
+static void page_flush_tb(void)
+{
+    int i, j;
+    PageDesc *p;
+
+    for(i = 0; i < L1_SIZE; i++) {
+        p = l1_map[i];
+        if (p) {
+            for(j = 0; j < L2_SIZE; j++) {
+                p->first_tb = NULL;
+                invalidate_page_bitmap(p);
+                p++;
+            }
+        }
+    }
+}
+
+/* flush all the translation blocks */
+/* XXX: tb_flush is currently not thread safe */
+void tb_flush(CPUState *env1)
+{
+    CPUState *env;
+#if defined(DEBUG_FLUSH)
+    printf("qemu: flush code_size=%ld nb_tbs=%d avg_tb_size=%ld\n",
+           (unsigned long)(code_gen_ptr - code_gen_buffer),
+           nb_tbs, nb_tbs > 0 ?
+           ((unsigned long)(code_gen_ptr - code_gen_buffer)) / nb_tbs : 0);
+#endif
+    if ((unsigned long)(code_gen_ptr - code_gen_buffer) > code_gen_buffer_size)
+        cpu_abort(env1, "Internal error: code buffer overflow\n");
+
+    nb_tbs = 0;
+
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+    }
+
+    memset (tb_phys_hash, 0, CODE_GEN_PHYS_HASH_SIZE * sizeof (void *));
+    page_flush_tb();
+
+    code_gen_ptr = code_gen_buffer;
+    /* XXX: flush processor icache at this point if cache flush is
+       expensive */
+    tb_flush_count++;
+}
+
+#ifdef DEBUG_TB_CHECK
+
+static void tb_invalidate_check(target_ulong address)
+{
+    TranslationBlock *tb;
+    int i;
+    address &= TARGET_PAGE_MASK;
+    for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) {
+        for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) {
+            if (!(address + TARGET_PAGE_SIZE <= tb->pc ||
+                  address >= tb->pc + tb->size)) {
+                printf("ERROR invalidate: address=%08lx PC=%08lx size=%04x\n",
+                       address, (long)tb->pc, tb->size);
+            }
+        }
+    }
+}
+
+/* verify that all the pages have correct rights for code */
+static void tb_page_check(void)
+{
+    TranslationBlock *tb;
+    int i, flags1, flags2;
+
+    for(i = 0;i < CODE_GEN_PHYS_HASH_SIZE; i++) {
+        for(tb = tb_phys_hash[i]; tb != NULL; tb = tb->phys_hash_next) {
+            flags1 = page_get_flags(tb->pc);
+            flags2 = page_get_flags(tb->pc + tb->size - 1);
+            if ((flags1 & PAGE_WRITE) || (flags2 & PAGE_WRITE)) {
+                printf("ERROR page flags: PC=%08lx size=%04x f1=%x f2=%x\n",
+                       (long)tb->pc, tb->size, flags1, flags2);
+            }
+        }
+    }
+}
+
+void tb_jmp_check(TranslationBlock *tb)
+{
+    TranslationBlock *tb1;
+    unsigned int n1;
+
+    /* suppress any remaining jumps to this TB */
+    tb1 = tb->jmp_first;
+    for(;;) {
+        n1 = (long)tb1 & 3;
+        tb1 = (TranslationBlock *)((long)tb1 & ~3);
+        if (n1 == 2)
+            break;
+        tb1 = tb1->jmp_next[n1];
+    }
+    /* check end of list */
+    if (tb1 != tb) {
+        printf("ERROR: jmp_list from 0x%08lx\n", (long)tb);
+    }
+}
+
+#endif
+
+/* invalidate one TB */
+static inline void tb_remove(TranslationBlock **ptb, TranslationBlock *tb,
+                             int next_offset)
+{
+    TranslationBlock *tb1;
+    for(;;) {
+        tb1 = *ptb;
+        if (tb1 == tb) {
+            *ptb = *(TranslationBlock **)((char *)tb1 + next_offset);
+            break;
+        }
+        ptb = (TranslationBlock **)((char *)tb1 + next_offset);
+    }
+}
+
+static inline void tb_page_remove(TranslationBlock **ptb, TranslationBlock *tb)
+{
+    TranslationBlock *tb1;
+    unsigned int n1;
+
+    for(;;) {
+        tb1 = *ptb;
+        n1 = (long)tb1 & 3;
+        tb1 = (TranslationBlock *)((long)tb1 & ~3);
+        if (tb1 == tb) {
+            *ptb = tb1->page_next[n1];
+            break;
+        }
+        ptb = &tb1->page_next[n1];
+    }
+}
+
+static inline void tb_jmp_remove(TranslationBlock *tb, int n)
+{
+    TranslationBlock *tb1, **ptb;
+    unsigned int n1;
+
+    ptb = &tb->jmp_next[n];
+    tb1 = *ptb;
+    if (tb1) {
+        /* find tb(n) in circular list */
+        for(;;) {
+            tb1 = *ptb;
+            n1 = (long)tb1 & 3;
+            tb1 = (TranslationBlock *)((long)tb1 & ~3);
+            if (n1 == n && tb1 == tb)
+                break;
+            if (n1 == 2) {
+                ptb = &tb1->jmp_first;
+            } else {
+                ptb = &tb1->jmp_next[n1];
+            }
+        }
+        /* now we can suppress tb(n) from the list */
+        *ptb = tb->jmp_next[n];
+
+        tb->jmp_next[n] = NULL;
+    }
+}
+
+/* reset the jump entry 'n' of a TB so that it is not chained to
+   another TB */
+static inline void tb_reset_jump(TranslationBlock *tb, int n)
+{
+    tb_set_jmp_target(tb, n, (unsigned long)(tb->tc_ptr + tb->tb_next_offset[n]));
+}
+
+void tb_phys_invalidate(TranslationBlock *tb, target_ulong page_addr)
+{
+    CPUState *env;
+    PageDesc *p;
+    unsigned int h, n1;
+    target_phys_addr_t phys_pc;
+    TranslationBlock *tb1, *tb2;
+
+    /* remove the TB from the hash list */
+    phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
+    h = tb_phys_hash_func(phys_pc);
+    tb_remove(&tb_phys_hash[h], tb,
+              offsetof(TranslationBlock, phys_hash_next));
+
+    /* remove the TB from the page list */
+    if (tb->page_addr[0] != page_addr) {
+        p = page_find(tb->page_addr[0] >> TARGET_PAGE_BITS);
+        tb_page_remove(&p->first_tb, tb);
+        invalidate_page_bitmap(p);
+    }
+    if (tb->page_addr[1] != -1 && tb->page_addr[1] != page_addr) {
+        p = page_find(tb->page_addr[1] >> TARGET_PAGE_BITS);
+        tb_page_remove(&p->first_tb, tb);
+        invalidate_page_bitmap(p);
+    }
+
+    tb_invalidated_flag = 1;
+
+    /* remove the TB from the hash list */
+    h = tb_jmp_cache_hash_func(tb->pc);
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        if (env->tb_jmp_cache[h] == tb)
+            env->tb_jmp_cache[h] = NULL;
+    }
+
+    /* suppress this TB from the two jump lists */
+    tb_jmp_remove(tb, 0);
+    tb_jmp_remove(tb, 1);
+
+    /* suppress any remaining jumps to this TB */
+    tb1 = tb->jmp_first;
+    for(;;) {
+        n1 = (long)tb1 & 3;
+        if (n1 == 2)
+            break;
+        tb1 = (TranslationBlock *)((long)tb1 & ~3);
+        tb2 = tb1->jmp_next[n1];
+        tb_reset_jump(tb1, n1);
+        tb1->jmp_next[n1] = NULL;
+        tb1 = tb2;
+    }
+    tb->jmp_first = (TranslationBlock *)((long)tb | 2); /* fail safe */
+
+    tb_phys_invalidate_count++;
+}
+
+static inline void set_bits(uint8_t *tab, int start, int len)
+{
+    int end, mask, end1;
+
+    end = start + len;
+    tab += start >> 3;
+    mask = 0xff << (start & 7);
+    if ((start & ~7) == (end & ~7)) {
+        if (start < end) {
+            mask &= ~(0xff << (end & 7));
+            *tab |= mask;
+        }
+    } else {
+        *tab++ |= mask;
+        start = (start + 8) & ~7;
+        end1 = end & ~7;
+        while (start < end1) {
+            *tab++ = 0xff;
+            start += 8;
+        }
+        if (start < end) {
+            mask = ~(0xff << (end & 7));
+            *tab |= mask;
+        }
+    }
+}
+
+static void build_page_bitmap(PageDesc *p)
+{
+    int n, tb_start, tb_end;
+    TranslationBlock *tb;
+
+    p->code_bitmap = qemu_mallocz(TARGET_PAGE_SIZE / 8);
+    if (!p->code_bitmap)
+        return;
+
+    tb = p->first_tb;
+    while (tb != NULL) {
+        n = (long)tb & 3;
+        tb = (TranslationBlock *)((long)tb & ~3);
+        /* NOTE: this is subtle as a TB may span two physical pages */
+        if (n == 0) {
+            /* NOTE: tb_end may be after the end of the page, but
+               it is not a problem */
+            tb_start = tb->pc & ~TARGET_PAGE_MASK;
+            tb_end = tb_start + tb->size;
+            if (tb_end > TARGET_PAGE_SIZE)
+                tb_end = TARGET_PAGE_SIZE;
+        } else {
+            tb_start = 0;
+            tb_end = ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
+        }
+        set_bits(p->code_bitmap, tb_start, tb_end - tb_start);
+        tb = tb->page_next[n];
+    }
+}
+
+TranslationBlock *tb_gen_code(CPUState *env,
+                              target_ulong pc, target_ulong cs_base,
+                              int flags, int cflags)
+{
+    TranslationBlock *tb;
+    uint8_t *tc_ptr;
+    target_ulong phys_pc, phys_page2, virt_page2;
+    int code_gen_size;
+
+    phys_pc = get_phys_addr_code(env, pc);
+    tb = tb_alloc(pc);
+    if (!tb) {
+        /* flush must be done */
+        tb_flush(env);
+        /* cannot fail at this point */
+        tb = tb_alloc(pc);
+        /* Don't forget to invalidate previous TB info.  */
+        tb_invalidated_flag = 1;
+    }
+    tc_ptr = code_gen_ptr;
+    tb->tc_ptr = tc_ptr;
+    tb->cs_base = cs_base;
+    tb->flags = flags;
+    tb->cflags = cflags;
+#ifdef CONFIG_TRACE
+    tb->bb_rec = NULL;
+    tb->prev_time = 0;
+#endif
+    cpu_gen_code(env, tb, &code_gen_size);
+    code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
+
+    /* check next page if needed */
+    virt_page2 = (pc + tb->size - 1) & TARGET_PAGE_MASK;
+    phys_page2 = -1;
+    if ((pc & TARGET_PAGE_MASK) != virt_page2) {
+        phys_page2 = get_phys_addr_code(env, virt_page2);
+    }
+    tb_link_phys(tb, phys_pc, phys_page2);
+    return tb;
+}
+
+/* invalidate all TBs which intersect with the target physical page
+   starting in range [start;end[. NOTE: start and end must refer to
+   the same physical page. 'is_cpu_write_access' should be true if called
+   from a real cpu write access: the virtual CPU will exit the current
+   TB if code is modified inside this TB. */
+void tb_invalidate_phys_page_range(target_phys_addr_t start, target_phys_addr_t end,
+                                   int is_cpu_write_access)
+{
+    int n, current_tb_modified, current_tb_not_found, current_flags;
+    CPUState *env = cpu_single_env;
+    PageDesc *p;
+    TranslationBlock *tb, *tb_next, *current_tb, *saved_tb;
+    target_ulong tb_start, tb_end;
+    target_ulong current_pc, current_cs_base;
+
+    p = page_find(start >> TARGET_PAGE_BITS);
+    if (!p)
+        return;
+    if (!p->code_bitmap &&
+        ++p->code_write_count >= SMC_BITMAP_USE_THRESHOLD &&
+        is_cpu_write_access) {
+        /* build code bitmap */
+        build_page_bitmap(p);
+    }
+
+    /* we remove all the TBs in the range [start, end[ */
+    /* XXX: see if in some cases it could be faster to invalidate all the code */
+    current_tb_not_found = is_cpu_write_access;
+    current_tb_modified = 0;
+    current_tb = NULL; /* avoid warning */
+    current_pc = 0; /* avoid warning */
+    current_cs_base = 0; /* avoid warning */
+    current_flags = 0; /* avoid warning */
+    tb = p->first_tb;
+    while (tb != NULL) {
+        n = (long)tb & 3;
+        tb = (TranslationBlock *)((long)tb & ~3);
+        tb_next = tb->page_next[n];
+        /* NOTE: this is subtle as a TB may span two physical pages */
+        if (n == 0) {
+            /* NOTE: tb_end may be after the end of the page, but
+               it is not a problem */
+            tb_start = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
+            tb_end = tb_start + tb->size;
+        } else {
+            tb_start = tb->page_addr[1];
+            tb_end = tb_start + ((tb->pc + tb->size) & ~TARGET_PAGE_MASK);
+        }
+        if (!(tb_end <= start || tb_start >= end)) {
+#ifdef TARGET_HAS_PRECISE_SMC
+            if (current_tb_not_found) {
+                current_tb_not_found = 0;
+                current_tb = NULL;
+                if (env->mem_io_pc) {
+                    /* now we have a real cpu fault */
+                    current_tb = tb_find_pc(env->mem_io_pc);
+                }
+            }
+            if (current_tb == tb &&
+                (current_tb->cflags & CF_COUNT_MASK) != 1) {
+                /* If we are modifying the current TB, we must stop
+                its execution. We could be more precise by checking
+                that the modification is after the current PC, but it
+                would require a specialized function to partially
+                restore the CPU state */
+
+                current_tb_modified = 1;
+                cpu_restore_state(current_tb, env,
+                                  env->mem_io_pc, NULL);
+#if defined(TARGET_I386)
+                current_flags = env->hflags;
+                current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
+                current_cs_base = (target_ulong)env->segs[R_CS].base;
+                current_pc = current_cs_base + env->eip;
+#else
+#error unsupported CPU
+#endif
+            }
+#endif /* TARGET_HAS_PRECISE_SMC */
+            /* we need to do that to handle the case where a signal
+               occurs while doing tb_phys_invalidate() */
+            saved_tb = NULL;
+            if (env) {
+                saved_tb = env->current_tb;
+                env->current_tb = NULL;
+            }
+            tb_phys_invalidate(tb, -1);
+            if (env) {
+                env->current_tb = saved_tb;
+                if (env->interrupt_request && env->current_tb)
+                    cpu_interrupt(env, env->interrupt_request);
+            }
+        }
+        tb = tb_next;
+    }
+#if !defined(CONFIG_USER_ONLY)
+    /* if no code remaining, no need to continue to use slow writes */
+    if (!p->first_tb) {
+        invalidate_page_bitmap(p);
+        if (is_cpu_write_access) {
+            tlb_unprotect_code_phys(env, start, env->mem_io_vaddr);
+        }
+    }
+#endif
+#ifdef TARGET_HAS_PRECISE_SMC
+    if (current_tb_modified) {
+        /* we generate a block containing just the instruction
+           modifying the memory. It will ensure that it cannot modify
+           itself */
+        env->current_tb = NULL;
+        tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
+        cpu_resume_from_signal(env, NULL);
+    }
+#endif
+}
+
+/* len must be <= 8 and start must be a multiple of len */
+static inline void tb_invalidate_phys_page_fast(target_phys_addr_t start, int len)
+{
+    PageDesc *p;
+    int offset, b;
+#if 0
+    if (1) {
+        if (loglevel) {
+            fprintf(logfile, "modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
+                   cpu_single_env->mem_io_vaddr, len,
+                   cpu_single_env->eip,
+                   cpu_single_env->eip + (long)cpu_single_env->segs[R_CS].base);
+        }
+    }
+#endif
+    p = page_find(start >> TARGET_PAGE_BITS);
+    if (!p)
+        return;
+    if (p->code_bitmap) {
+        offset = start & ~TARGET_PAGE_MASK;
+        b = p->code_bitmap[offset >> 3] >> (offset & 7);
+        if (b & ((1 << len) - 1))
+            goto do_invalidate;
+    } else {
+    do_invalidate:
+        tb_invalidate_phys_page_range(start, start + len, 1);
+    }
+}
+
+#if !defined(CONFIG_SOFTMMU)
+static void tb_invalidate_phys_page(target_phys_addr_t addr,
+                                    unsigned long pc, void *puc)
+{
+    int n, current_flags, current_tb_modified;
+    target_ulong current_pc, current_cs_base;
+    PageDesc *p;
+    TranslationBlock *tb, *current_tb;
+#ifdef TARGET_HAS_PRECISE_SMC
+    CPUState *env = cpu_single_env;
+#endif
+
+    addr &= TARGET_PAGE_MASK;
+    p = page_find(addr >> TARGET_PAGE_BITS);
+    if (!p)
+        return;
+    tb = p->first_tb;
+    current_tb_modified = 0;
+    current_tb = NULL;
+    current_pc = 0; /* avoid warning */
+    current_cs_base = 0; /* avoid warning */
+    current_flags = 0; /* avoid warning */
+#ifdef TARGET_HAS_PRECISE_SMC
+    if (tb && pc != 0) {
+        current_tb = tb_find_pc(pc);
+    }
+#endif
+    while (tb != NULL) {
+        n = (long)tb & 3;
+        tb = (TranslationBlock *)((long)tb & ~3);
+#ifdef TARGET_HAS_PRECISE_SMC
+        if (current_tb == tb &&
+            (current_tb->cflags & CF_COUNT_MASK) != 1) {
+                /* If we are modifying the current TB, we must stop
+                   its execution. We could be more precise by checking
+                   that the modification is after the current PC, but it
+                   would require a specialized function to partially
+                   restore the CPU state */
+
+            current_tb_modified = 1;
+            cpu_restore_state(current_tb, env, pc, puc);
+#if defined(TARGET_I386)
+            current_flags = env->hflags;
+            current_flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
+            current_cs_base = (target_ulong)env->segs[R_CS].base;
+            current_pc = current_cs_base + env->eip;
+#else
+#error unsupported CPU
+#endif
+        }
+#endif /* TARGET_HAS_PRECISE_SMC */
+        tb_phys_invalidate(tb, addr);
+        tb = tb->page_next[n];
+    }
+    p->first_tb = NULL;
+#ifdef TARGET_HAS_PRECISE_SMC
+    if (current_tb_modified) {
+        /* we generate a block containing just the instruction
+           modifying the memory. It will ensure that it cannot modify
+           itself */
+        env->current_tb = NULL;
+        tb_gen_code(env, current_pc, current_cs_base, current_flags, 1);
+        cpu_resume_from_signal(env, puc);
+    }
+#endif
+}
+#endif
+
+/* add the tb in the target page and protect it if necessary */
+static inline void tb_alloc_page(TranslationBlock *tb,
+                                 unsigned int n, target_ulong page_addr)
+{
+    PageDesc *p;
+    TranslationBlock *last_first_tb;
+
+    tb->page_addr[n] = page_addr;
+    p = page_find_alloc(page_addr >> TARGET_PAGE_BITS);
+    tb->page_next[n] = p->first_tb;
+    last_first_tb = p->first_tb;
+    p->first_tb = (TranslationBlock *)((long)tb | n);
+    invalidate_page_bitmap(p);
+
+#if defined(TARGET_HAS_SMC) || 1
+
+#if defined(CONFIG_USER_ONLY)
+    if (p->flags & PAGE_WRITE) {
+        target_ulong addr;
+        PageDesc *p2;
+        int prot;
+
+        /* force the host page as non writable (writes will have a
+           page fault + mprotect overhead) */
+        page_addr &= qemu_host_page_mask;
+        prot = 0;
+        for(addr = page_addr; addr < page_addr + qemu_host_page_size;
+            addr += TARGET_PAGE_SIZE) {
+
+            p2 = page_find (addr >> TARGET_PAGE_BITS);
+            if (!p2)
+                continue;
+            prot |= p2->flags;
+            p2->flags &= ~PAGE_WRITE;
+            page_get_flags(addr);
+          }
+        mprotect(g2h(page_addr), qemu_host_page_size,
+                 (prot & PAGE_BITS) & ~PAGE_WRITE);
+#ifdef DEBUG_TB_INVALIDATE
+        printf("protecting code page: 0x" TARGET_FMT_lx "\n",
+               page_addr);
+#endif
+    }
+#else
+    /* if some code is already present, then the pages are already
+       protected. So we handle the case where only the first TB is
+       allocated in a physical page */
+    if (!last_first_tb) {
+        tlb_protect_code(page_addr);
+    }
+#endif
+
+#endif /* TARGET_HAS_SMC */
+}
+
+/* Allocate a new translation block. Flush the translation buffer if
+   too many translation blocks or too much generated code. */
+TranslationBlock *tb_alloc(target_ulong pc)
+{
+    TranslationBlock *tb;
+
+    if (nb_tbs >= code_gen_max_blocks ||
+        (code_gen_ptr - code_gen_buffer) >= code_gen_buffer_max_size)
+        return NULL;
+    tb = &tbs[nb_tbs++];
+    tb->pc = pc;
+    tb->cflags = 0;
+    return tb;
+}
+
+void tb_free(TranslationBlock *tb)
+{
+    /* In practice this is mostly used for single use temporary TB
+       Ignore the hard cases and just back up if this TB happens to
+       be the last one generated.  */
+    if (nb_tbs > 0 && tb == &tbs[nb_tbs - 1]) {
+        code_gen_ptr = tb->tc_ptr;
+        nb_tbs--;
+    }
+}
+
+/* add a new TB and link it to the physical page tables. phys_page2 is
+   (-1) to indicate that only one page contains the TB. */
+void tb_link_phys(TranslationBlock *tb,
+                  target_ulong phys_pc, target_ulong phys_page2)
+{
+    unsigned int h;
+    TranslationBlock **ptb;
+
+    /* Grab the mmap lock to stop another thread invalidating this TB
+       before we are done.  */
+    mmap_lock();
+    /* add in the physical hash table */
+    h = tb_phys_hash_func(phys_pc);
+    ptb = &tb_phys_hash[h];
+    tb->phys_hash_next = *ptb;
+    *ptb = tb;
+
+    /* add in the page list */
+    tb_alloc_page(tb, 0, phys_pc & TARGET_PAGE_MASK);
+    if (phys_page2 != -1)
+        tb_alloc_page(tb, 1, phys_page2);
+    else
+        tb->page_addr[1] = -1;
+
+    tb->jmp_first = (TranslationBlock *)((long)tb | 2);
+    tb->jmp_next[0] = NULL;
+    tb->jmp_next[1] = NULL;
+
+    /* init original jump addresses */
+    if (tb->tb_next_offset[0] != 0xffff)
+        tb_reset_jump(tb, 0);
+    if (tb->tb_next_offset[1] != 0xffff)
+        tb_reset_jump(tb, 1);
+
+#ifdef DEBUG_TB_CHECK
+    tb_page_check();
+#endif
+    mmap_unlock();
+}
+
+/* find the TB 'tb' such that tb[0].tc_ptr <= tc_ptr <
+   tb[1].tc_ptr. Return NULL if not found */
+TranslationBlock *tb_find_pc(unsigned long tc_ptr)
+{
+    int m_min, m_max, m;
+    unsigned long v;
+    TranslationBlock *tb;
+
+    if (nb_tbs <= 0)
+        return NULL;
+    if (tc_ptr < (unsigned long)code_gen_buffer ||
+        tc_ptr >= (unsigned long)code_gen_ptr)
+        return NULL;
+    /* binary search (cf Knuth) */
+    m_min = 0;
+    m_max = nb_tbs - 1;
+    while (m_min <= m_max) {
+        m = (m_min + m_max) >> 1;
+        tb = &tbs[m];
+        v = (unsigned long)tb->tc_ptr;
+        if (v == tc_ptr)
+            return tb;
+        else if (tc_ptr < v) {
+            m_max = m - 1;
+        } else {
+            m_min = m + 1;
+        }
+    }
+    return &tbs[m_max];
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb);
+
+static inline void tb_reset_jump_recursive2(TranslationBlock *tb, int n)
+{
+    TranslationBlock *tb1, *tb_next, **ptb;
+    unsigned int n1;
+
+    tb1 = tb->jmp_next[n];
+    if (tb1 != NULL) {
+        /* find head of list */
+        for(;;) {
+            n1 = (long)tb1 & 3;
+            tb1 = (TranslationBlock *)((long)tb1 & ~3);
+            if (n1 == 2)
+                break;
+            tb1 = tb1->jmp_next[n1];
+        }
+        /* we are now sure now that tb jumps to tb1 */
+        tb_next = tb1;
+
+        /* remove tb from the jmp_first list */
+        ptb = &tb_next->jmp_first;
+        for(;;) {
+            tb1 = *ptb;
+            n1 = (long)tb1 & 3;
+            tb1 = (TranslationBlock *)((long)tb1 & ~3);
+            if (n1 == n && tb1 == tb)
+                break;
+            ptb = &tb1->jmp_next[n1];
+        }
+        *ptb = tb->jmp_next[n];
+        tb->jmp_next[n] = NULL;
+
+        /* suppress the jump to next tb in generated code */
+        tb_reset_jump(tb, n);
+
+        /* suppress jumps in the tb on which we could have jumped */
+        tb_reset_jump_recursive(tb_next);
+    }
+}
+
+static void tb_reset_jump_recursive(TranslationBlock *tb)
+{
+    tb_reset_jump_recursive2(tb, 0);
+    tb_reset_jump_recursive2(tb, 1);
+}
+
+#if defined(TARGET_HAS_ICE)
+static void breakpoint_invalidate(CPUState *env, target_ulong pc)
+{
+    target_phys_addr_t addr;
+    target_ulong pd;
+    ram_addr_t ram_addr;
+    PhysPageDesc *p;
+
+    addr = cpu_get_phys_page_debug(env, pc);
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+    ram_addr = (pd & TARGET_PAGE_MASK) | (pc & ~TARGET_PAGE_MASK);
+    tb_invalidate_phys_page_range(ram_addr, ram_addr + 1, 0);
+}
+#endif
+
+/* Add a watchpoint.  */
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr, int type)
+{
+    int i;
+
+    for (i = 0; i < env->nb_watchpoints; i++) {
+        if (addr == env->watchpoint[i].vaddr)
+            return 0;
+    }
+    if (env->nb_watchpoints >= MAX_WATCHPOINTS)
+        return -1;
+
+    i = env->nb_watchpoints++;
+    env->watchpoint[i].vaddr = addr;
+    env->watchpoint[i].type = type;
+    tlb_flush_page(env, addr);
+    /* FIXME: This flush is needed because of the hack to make memory ops
+       terminate the TB.  It can be removed once the proper IO trap and
+       re-execute bits are in.  */
+    tb_flush(env);
+    return i;
+}
+
+/* Remove a watchpoint.  */
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr)
+{
+    int i;
+
+    for (i = 0; i < env->nb_watchpoints; i++) {
+        if (addr == env->watchpoint[i].vaddr) {
+            env->nb_watchpoints--;
+            env->watchpoint[i] = env->watchpoint[env->nb_watchpoints];
+            tlb_flush_page(env, addr);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+/* Remove all watchpoints. */
+void cpu_watchpoint_remove_all(CPUState *env) {
+    int i;
+
+    for (i = 0; i < env->nb_watchpoints; i++) {
+        tlb_flush_page(env, env->watchpoint[i].vaddr);
+    }
+    env->nb_watchpoints = 0;
+}
+
+/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a
+   breakpoint is reached */
+int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
+{
+#if defined(TARGET_HAS_ICE)
+    int i;
+
+    for(i = 0; i < env->nb_breakpoints; i++) {
+        if (env->breakpoints[i] == pc)
+            return 0;
+    }
+
+    if (env->nb_breakpoints >= MAX_BREAKPOINTS)
+        return -1;
+    env->breakpoints[env->nb_breakpoints++] = pc;
+
+    breakpoint_invalidate(env, pc);
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+/* remove all breakpoints */
+void cpu_breakpoint_remove_all(CPUState *env) {
+#if defined(TARGET_HAS_ICE)
+    int i;
+    for(i = 0; i < env->nb_breakpoints; i++) {
+        breakpoint_invalidate(env, env->breakpoints[i]);
+    }
+    env->nb_breakpoints = 0;
+#endif
+}
+
+/* remove a breakpoint */
+int cpu_breakpoint_remove(CPUState *env, target_ulong pc)
+{
+#if defined(TARGET_HAS_ICE)
+    int i;
+    for(i = 0; i < env->nb_breakpoints; i++) {
+        if (env->breakpoints[i] == pc)
+            goto found;
+    }
+    return -1;
+ found:
+    env->nb_breakpoints--;
+    if (i < env->nb_breakpoints)
+      env->breakpoints[i] = env->breakpoints[env->nb_breakpoints];
+
+    breakpoint_invalidate(env, pc);
+    return 0;
+#else
+    return -1;
+#endif
+}
+
+/* enable or disable single step mode. EXCP_DEBUG is returned by the
+   CPU loop after each instruction */
+void cpu_single_step(CPUState *env, int enabled)
+{
+#if defined(TARGET_HAS_ICE)
+    if (env->singlestep_enabled != enabled) {
+        env->singlestep_enabled = enabled;
+        /* must flush all the translated code to avoid inconsistancies */
+        /* XXX: only flush what is necessary */
+        tb_flush(env);
+    }
+#endif
+}
+
+/* enable or disable low levels log */
+void cpu_set_log(int log_flags)
+{
+    loglevel = log_flags;
+    if (loglevel && !logfile) {
+        logfile = fopen(logfilename, log_append ? "a" : "w");
+        if (!logfile) {
+            perror(logfilename);
+            _exit(1);
+        }
+#if !defined(CONFIG_SOFTMMU)
+        /* must avoid mmap() usage of glibc by setting a buffer "by hand" */
+        {
+            static uint8_t logfile_buf[4096];
+            setvbuf(logfile, logfile_buf, _IOLBF, sizeof(logfile_buf));
+        }
+#else
+        setvbuf(logfile, NULL, _IOLBF, 0);
+#endif
+        log_append = 1;
+    }
+    if (!loglevel && logfile) {
+        fclose(logfile);
+        logfile = NULL;
+    }
+}
+
+void cpu_set_log_filename(const char *filename)
+{
+    logfilename = strdup(filename);
+    if (logfile) {
+        fclose(logfile);
+        logfile = NULL;
+    }
+    cpu_set_log(loglevel);
+}
+
+/* mask must never be zero, except for A20 change call */
+void cpu_interrupt(CPUState *env, int mask)
+{
+#if !defined(USE_NPTL)
+    TranslationBlock *tb;
+    static spinlock_t interrupt_lock = SPIN_LOCK_UNLOCKED;
+#endif
+    int old_mask;
+
+    old_mask = env->interrupt_request;
+    /* FIXME: This is probably not threadsafe.  A different thread could
+       be in the middle of a read-modify-write operation.  */
+    env->interrupt_request |= mask;
+#if defined(USE_NPTL)
+    /* FIXME: TB unchaining isn't SMP safe.  For now just ignore the
+       problem and hope the cpu will stop of its own accord.  For userspace
+       emulation this often isn't actually as bad as it sounds.  Often
+       signals are used primarily to interrupt blocking syscalls.  */
+#else
+    if (use_icount) {
+        env->icount_decr.u16.high = 0xffff;
+#ifndef CONFIG_USER_ONLY
+        /* CPU_INTERRUPT_EXIT isn't a real interrupt.  It just means
+           an async event happened and we need to process it.  */
+        if (!can_do_io(env)
+            && (mask & ~(old_mask | CPU_INTERRUPT_EXIT)) != 0) {
+            cpu_abort(env, "Raised interrupt while not in I/O function");
+        }
+#endif
+    } else {
+        tb = env->current_tb;
+        /* if the cpu is currently executing code, we must unlink it and
+           all the potentially executing TB */
+        if (tb && !testandset(&interrupt_lock)) {
+            env->current_tb = NULL;
+            tb_reset_jump_recursive(tb);
+            resetlock(&interrupt_lock);
+        }
+    }
+#endif
+}
+
+void cpu_reset_interrupt(CPUState *env, int mask)
+{
+    env->interrupt_request &= ~mask;
+}
+
+CPULogItem cpu_log_items[] = {
+    { CPU_LOG_TB_OUT_ASM, "out_asm",
+      "show generated host assembly code for each compiled TB" },
+    { CPU_LOG_TB_IN_ASM, "in_asm",
+      "show target assembly code for each compiled TB" },
+    { CPU_LOG_TB_OP, "op",
+      "show micro ops for each compiled TB" },
+    { CPU_LOG_TB_OP_OPT, "op_opt",
+      "show micro ops "
+#ifdef TARGET_I386
+      "before eflags optimization and "
+#endif
+      "after liveness analysis" },
+    { CPU_LOG_INT, "int",
+      "show interrupts/exceptions in short format" },
+    { CPU_LOG_EXEC, "exec",
+      "show trace before each executed TB (lots of logs)" },
+    { CPU_LOG_TB_CPU, "cpu",
+      "show CPU state before block translation" },
+#ifdef TARGET_I386
+    { CPU_LOG_PCALL, "pcall",
+      "show protected mode far calls/returns/exceptions" },
+#endif
+#ifdef DEBUG_IOPORT
+    { CPU_LOG_IOPORT, "ioport",
+      "show all i/o ports accesses" },
+#endif
+    { 0, NULL, NULL },
+};
+
+static int cmp1(const char *s1, int n, const char *s2)
+{
+    if (strlen(s2) != n)
+        return 0;
+    return memcmp(s1, s2, n) == 0;
+}
+
+/* takes a comma separated list of log masks. Return 0 if error. */
+int cpu_str_to_log_mask(const char *str)
+{
+    CPULogItem *item;
+    int mask;
+    const char *p, *p1;
+
+    p = str;
+    mask = 0;
+    for(;;) {
+        p1 = strchr(p, ',');
+        if (!p1)
+            p1 = p + strlen(p);
+	if(cmp1(p,p1-p,"all")) {
+		for(item = cpu_log_items; item->mask != 0; item++) {
+			mask |= item->mask;
+		}
+	} else {
+        for(item = cpu_log_items; item->mask != 0; item++) {
+            if (cmp1(p, p1 - p, item->name))
+                goto found;
+        }
+        return 0;
+	}
+    found:
+        mask |= item->mask;
+        if (*p1 != ',')
+            break;
+        p = p1 + 1;
+    }
+    return mask;
+}
+
+void cpu_abort(CPUState *env, const char *fmt, ...)
+{
+    va_list ap;
+    va_list ap2;
+
+    va_start(ap, fmt);
+    va_copy(ap2, ap);
+    fprintf(stderr, "qemu: fatal: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+#ifdef TARGET_I386
+    cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
+#else
+    cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+    if (logfile) {
+        fprintf(logfile, "qemu: fatal: ");
+        vfprintf(logfile, fmt, ap2);
+        fprintf(logfile, "\n");
+#ifdef TARGET_I386
+        cpu_dump_state(env, logfile, fprintf, X86_DUMP_FPU | X86_DUMP_CCOP);
+#else
+        cpu_dump_state(env, logfile, fprintf, 0);
+#endif
+        fflush(logfile);
+        fclose(logfile);
+    }
+    va_end(ap2);
+    va_end(ap);
+    abort();
+}
+
+CPUState *cpu_copy(CPUState *env)
+{
+    CPUState *new_env = cpu_init(env->cpu_model_str);
+    /* preserve chaining and index */
+    CPUState *next_cpu = new_env->next_cpu;
+    int cpu_index = new_env->cpu_index;
+    memcpy(new_env, env, sizeof(CPUState));
+    new_env->next_cpu = next_cpu;
+    new_env->cpu_index = cpu_index;
+    return new_env;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr)
+{
+    unsigned int i;
+
+    /* Discard jump cache entries for any tb which might potentially
+       overlap the flushed page.  */
+    i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE);
+    memset (&env->tb_jmp_cache[i], 0, 
+	    TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
+
+    i = tb_jmp_cache_hash_page(addr);
+    memset (&env->tb_jmp_cache[i], 0, 
+	    TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *));
+}
+
+/* NOTE: if flush_global is true, also flush global entries (not
+   implemented yet) */
+void tlb_flush(CPUState *env, int flush_global)
+{
+    int i;
+
+#if defined(DEBUG_TLB)
+    printf("tlb_flush:\n");
+#endif
+    /* must reset current TB so that interrupts cannot modify the
+       links while we are modifying them */
+    env->current_tb = NULL;
+
+    for(i = 0; i < CPU_TLB_SIZE; i++) {
+        env->tlb_table[0][i].addr_read = -1;
+        env->tlb_table[0][i].addr_write = -1;
+        env->tlb_table[0][i].addr_code = -1;
+        env->tlb_table[1][i].addr_read = -1;
+        env->tlb_table[1][i].addr_write = -1;
+        env->tlb_table[1][i].addr_code = -1;
+#if (NB_MMU_MODES >= 3)
+        env->tlb_table[2][i].addr_read = -1;
+        env->tlb_table[2][i].addr_write = -1;
+        env->tlb_table[2][i].addr_code = -1;
+#if (NB_MMU_MODES == 4)
+        env->tlb_table[3][i].addr_read = -1;
+        env->tlb_table[3][i].addr_write = -1;
+        env->tlb_table[3][i].addr_code = -1;
+#endif
+#endif
+    }
+
+    memset (env->tb_jmp_cache, 0, TB_JMP_CACHE_SIZE * sizeof (void *));
+
+#ifdef USE_KQEMU
+    if (env->kqemu_enabled) {
+        kqemu_flush(env, flush_global);
+    }
+#endif
+    tlb_flush_count++;
+}
+
+static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
+{
+    if (addr == (tlb_entry->addr_read &
+                 (TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
+        addr == (tlb_entry->addr_write &
+                 (TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
+        addr == (tlb_entry->addr_code &
+                 (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+        tlb_entry->addr_read = -1;
+        tlb_entry->addr_write = -1;
+        tlb_entry->addr_code = -1;
+    }
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+    int i;
+
+#if defined(DEBUG_TLB)
+    printf("tlb_flush_page: " TARGET_FMT_lx "\n", addr);
+#endif
+    /* must reset current TB so that interrupts cannot modify the
+       links while we are modifying them */
+    env->current_tb = NULL;
+
+    addr &= TARGET_PAGE_MASK;
+    i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    tlb_flush_entry(&env->tlb_table[0][i], addr);
+    tlb_flush_entry(&env->tlb_table[1][i], addr);
+#if (NB_MMU_MODES >= 3)
+    tlb_flush_entry(&env->tlb_table[2][i], addr);
+#if (NB_MMU_MODES == 4)
+    tlb_flush_entry(&env->tlb_table[3][i], addr);
+#endif
+#endif
+
+    tlb_flush_jmp_cache(env, addr);
+
+#ifdef USE_KQEMU
+    if (env->kqemu_enabled) {
+        kqemu_flush_page(env, addr);
+    }
+#endif
+}
+
+/* update the TLBs so that writes to code in the virtual page 'addr'
+   can be detected */
+static void tlb_protect_code(ram_addr_t ram_addr)
+{
+    cpu_physical_memory_reset_dirty(ram_addr,
+                                    ram_addr + TARGET_PAGE_SIZE,
+                                    CODE_DIRTY_FLAG);
+}
+
+/* update the TLB so that writes in physical page 'phys_addr' are no longer
+   tested for self modifying code */
+static void tlb_unprotect_code_phys(CPUState *env, ram_addr_t ram_addr,
+                                    target_ulong vaddr)
+{
+    phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] |= CODE_DIRTY_FLAG;
+}
+
+static inline void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry,
+                                         unsigned long start, unsigned long length)
+{
+    unsigned long addr;
+    if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+        addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) + tlb_entry->addend;
+        if ((addr - start) < length) {
+            tlb_entry->addr_write = (tlb_entry->addr_write & TARGET_PAGE_MASK) | TLB_NOTDIRTY;
+        }
+    }
+}
+
+void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
+                                     int dirty_flags)
+{
+    CPUState *env;
+    unsigned long length, start1;
+    int i, mask, len;
+    uint8_t *p;
+
+    start &= TARGET_PAGE_MASK;
+    end = TARGET_PAGE_ALIGN(end);
+
+    length = end - start;
+    if (length == 0)
+        return;
+    len = length >> TARGET_PAGE_BITS;
+#ifdef USE_KQEMU
+    /* XXX: should not depend on cpu context */
+    env = first_cpu;
+    if (env->kqemu_enabled) {
+        ram_addr_t addr;
+        addr = start;
+        for(i = 0; i < len; i++) {
+            kqemu_set_notdirty(env, addr);
+            addr += TARGET_PAGE_SIZE;
+        }
+    }
+#endif
+    mask = ~dirty_flags;
+    p = phys_ram_dirty + (start >> TARGET_PAGE_BITS);
+    for(i = 0; i < len; i++)
+        p[i] &= mask;
+
+    /* we modify the TLB cache so that the dirty bit will be set again
+       when accessing the range */
+    start1 = start + (unsigned long)phys_ram_base;
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        for(i = 0; i < CPU_TLB_SIZE; i++)
+            tlb_reset_dirty_range(&env->tlb_table[0][i], start1, length);
+        for(i = 0; i < CPU_TLB_SIZE; i++)
+            tlb_reset_dirty_range(&env->tlb_table[1][i], start1, length);
+#if (NB_MMU_MODES >= 3)
+        for(i = 0; i < CPU_TLB_SIZE; i++)
+            tlb_reset_dirty_range(&env->tlb_table[2][i], start1, length);
+#if (NB_MMU_MODES == 4)
+        for(i = 0; i < CPU_TLB_SIZE; i++)
+            tlb_reset_dirty_range(&env->tlb_table[3][i], start1, length);
+#endif
+#endif
+    }
+}
+
+static inline void tlb_update_dirty(CPUTLBEntry *tlb_entry)
+{
+    ram_addr_t ram_addr;
+
+    if ((tlb_entry->addr_write & ~TARGET_PAGE_MASK) == IO_MEM_RAM) {
+        ram_addr = (tlb_entry->addr_write & TARGET_PAGE_MASK) +
+            tlb_entry->addend - (unsigned long)phys_ram_base;
+        if (!cpu_physical_memory_is_dirty(ram_addr)) {
+            tlb_entry->addr_write |= TLB_NOTDIRTY;
+        }
+    }
+}
+
+/* update the TLB according to the current state of the dirty bits */
+void cpu_tlb_update_dirty(CPUState *env)
+{
+    int i;
+    for(i = 0; i < CPU_TLB_SIZE; i++)
+        tlb_update_dirty(&env->tlb_table[0][i]);
+    for(i = 0; i < CPU_TLB_SIZE; i++)
+        tlb_update_dirty(&env->tlb_table[1][i]);
+#if (NB_MMU_MODES >= 3)
+    for(i = 0; i < CPU_TLB_SIZE; i++)
+        tlb_update_dirty(&env->tlb_table[2][i]);
+#if (NB_MMU_MODES == 4)
+    for(i = 0; i < CPU_TLB_SIZE; i++)
+        tlb_update_dirty(&env->tlb_table[3][i]);
+#endif
+#endif
+}
+
+static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
+{
+    if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY))
+        tlb_entry->addr_write = vaddr;
+}
+
+/* update the TLB corresponding to virtual page vaddr
+   so that it is no longer dirty */
+static inline void tlb_set_dirty(CPUState *env, target_ulong vaddr)
+{
+    int i;
+
+    vaddr &= TARGET_PAGE_MASK;
+    i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    tlb_set_dirty1(&env->tlb_table[0][i], vaddr);
+    tlb_set_dirty1(&env->tlb_table[1][i], vaddr);
+#if (NB_MMU_MODES >= 3)
+    tlb_set_dirty1(&env->tlb_table[2][i], vaddr);
+#if (NB_MMU_MODES == 4)
+    tlb_set_dirty1(&env->tlb_table[3][i], vaddr);
+#endif
+#endif
+}
+
+/* add a new TLB entry. At most one entry for a given virtual address
+   is permitted. Return 0 if OK or 2 if the page could not be mapped
+   (can only happen in non SOFTMMU mode for I/O pages or pages
+   conflicting with the host address space). */
+int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+                      target_phys_addr_t paddr, int prot,
+                      int mmu_idx, int is_softmmu)
+{
+    PhysPageDesc *p;
+    unsigned long pd;
+    unsigned int index;
+    target_ulong address;
+    target_ulong code_address;
+    target_phys_addr_t addend;
+    int ret;
+    CPUTLBEntry *te;
+    int i;
+    target_phys_addr_t iotlb;
+
+    p = phys_page_find(paddr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+#if defined(DEBUG_TLB)
+    printf("tlb_set_page: vaddr=" TARGET_FMT_lx " paddr=0x%08x prot=%x idx=%d smmu=%d pd=0x%08lx\n",
+           vaddr, (int)paddr, prot, mmu_idx, is_softmmu, pd);
+#endif
+
+    ret = 0;
+    address = vaddr;
+    if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM && !(pd & IO_MEM_ROMD)) {
+        /* IO memory case (romd handled later) */
+        address |= TLB_MMIO;
+    }
+    addend = (target_phys_addr_t)phys_ram_base + (pd & TARGET_PAGE_MASK);
+    if ((pd & ~TARGET_PAGE_MASK) <= IO_MEM_ROM) {
+        /* Normal RAM.  */
+        iotlb = pd & TARGET_PAGE_MASK;
+        if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM)
+            iotlb |= IO_MEM_NOTDIRTY;
+        else
+            iotlb |= IO_MEM_ROM;
+    } else {
+        /* IO handlers are currently passed a phsical address.
+           It would be nice to pass an offset from the base address
+           of that region.  This would avoid having to special case RAM,
+           and avoid full address decoding in every device.
+           We can't use the high bits of pd for this because
+           IO_MEM_ROMD uses these as a ram address.  */
+        iotlb = (pd & ~TARGET_PAGE_MASK) + paddr;
+    }
+
+    code_address = address;
+    /* Make accesses to pages with watchpoints go via the
+       watchpoint trap routines.  */
+    for (i = 0; i < env->nb_watchpoints; i++) {
+        if (vaddr == (env->watchpoint[i].vaddr & TARGET_PAGE_MASK)) {
+            iotlb = io_mem_watch + paddr;
+            /* TODO: The memory case can be optimized by not trapping
+               reads of pages with a write breakpoint.  */
+            address |= TLB_MMIO;
+        }
+    }
+
+    index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    env->iotlb[mmu_idx][index] = iotlb - vaddr;
+    te = &env->tlb_table[mmu_idx][index];
+    te->addend = addend - vaddr;
+    if (prot & PAGE_READ) {
+        te->addr_read = address;
+    } else {
+        te->addr_read = -1;
+    }
+
+    if (prot & PAGE_EXEC) {
+        te->addr_code = code_address;
+    } else {
+        te->addr_code = -1;
+    }
+    if (prot & PAGE_WRITE) {
+        if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_ROM ||
+            (pd & IO_MEM_ROMD)) {
+            /* Write access calls the I/O callback.  */
+            te->addr_write = address | TLB_MMIO;
+        } else if ((pd & ~TARGET_PAGE_MASK) == IO_MEM_RAM &&
+                   !cpu_physical_memory_is_dirty(pd)) {
+            te->addr_write = address | TLB_NOTDIRTY;
+        } else {
+            te->addr_write = address;
+        }
+    } else {
+        te->addr_write = -1;
+    }
+    return ret;
+}
+
+#else
+
+void tlb_flush(CPUState *env, int flush_global)
+{
+}
+
+void tlb_flush_page(CPUState *env, target_ulong addr)
+{
+}
+
+int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
+                      target_phys_addr_t paddr, int prot,
+                      int mmu_idx, int is_softmmu)
+{
+    return 0;
+}
+
+/* dump memory mappings */
+void page_dump(FILE *f)
+{
+    unsigned long start, end;
+    int i, j, prot, prot1;
+    PageDesc *p;
+
+    fprintf(f, "%-8s %-8s %-8s %s\n",
+            "start", "end", "size", "prot");
+    start = -1;
+    end = -1;
+    prot = 0;
+    for(i = 0; i <= L1_SIZE; i++) {
+        if (i < L1_SIZE)
+            p = l1_map[i];
+        else
+            p = NULL;
+        for(j = 0;j < L2_SIZE; j++) {
+            if (!p)
+                prot1 = 0;
+            else
+                prot1 = p[j].flags;
+            if (prot1 != prot) {
+                end = (i << (32 - L1_BITS)) | (j << TARGET_PAGE_BITS);
+                if (start != -1) {
+                    fprintf(f, "%08lx-%08lx %08lx %c%c%c\n",
+                            start, end, end - start,
+                            prot & PAGE_READ ? 'r' : '-',
+                            prot & PAGE_WRITE ? 'w' : '-',
+                            prot & PAGE_EXEC ? 'x' : '-');
+                }
+                if (prot1 != 0)
+                    start = end;
+                else
+                    start = -1;
+                prot = prot1;
+            }
+            if (!p)
+                break;
+        }
+    }
+}
+
+int page_get_flags(target_ulong address)
+{
+    PageDesc *p;
+
+    p = page_find(address >> TARGET_PAGE_BITS);
+    if (!p)
+        return 0;
+    return p->flags;
+}
+
+/* modify the flags of a page and invalidate the code if
+   necessary. The flag PAGE_WRITE_ORG is positionned automatically
+   depending on PAGE_WRITE */
+void page_set_flags(target_ulong start, target_ulong end, int flags)
+{
+    PageDesc *p;
+    target_ulong addr;
+
+    /* mmap_lock should already be held.  */
+    start = start & TARGET_PAGE_MASK;
+    end = TARGET_PAGE_ALIGN(end);
+    if (flags & PAGE_WRITE)
+        flags |= PAGE_WRITE_ORG;
+    for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+        p = page_find_alloc(addr >> TARGET_PAGE_BITS);
+        /* We may be called for host regions that are outside guest
+           address space.  */
+        if (!p)
+            return;
+        /* if the write protection is set, then we invalidate the code
+           inside */
+        if (!(p->flags & PAGE_WRITE) &&
+            (flags & PAGE_WRITE) &&
+            p->first_tb) {
+            tb_invalidate_phys_page(addr, 0, NULL);
+        }
+        p->flags = flags;
+    }
+}
+
+int page_check_range(target_ulong start, target_ulong len, int flags)
+{
+    PageDesc *p;
+    target_ulong end;
+    target_ulong addr;
+
+    end = TARGET_PAGE_ALIGN(start+len); /* must do before we loose bits in the next step */
+    start = start & TARGET_PAGE_MASK;
+
+    if( end < start )
+        /* we've wrapped around */
+        return -1;
+    for(addr = start; addr < end; addr += TARGET_PAGE_SIZE) {
+        p = page_find(addr >> TARGET_PAGE_BITS);
+        if( !p )
+            return -1;
+        if( !(p->flags & PAGE_VALID) )
+            return -1;
+
+        if ((flags & PAGE_READ) && !(p->flags & PAGE_READ))
+            return -1;
+        if (flags & PAGE_WRITE) {
+            if (!(p->flags & PAGE_WRITE_ORG))
+                return -1;
+            /* unprotect the page if it was put read-only because it
+               contains translated code */
+            if (!(p->flags & PAGE_WRITE)) {
+                if (!page_unprotect(addr, 0, NULL))
+                    return -1;
+            }
+            return 0;
+        }
+    }
+    return 0;
+}
+
+/* called from signal handler: invalidate the code and unprotect the
+   page. Return TRUE if the fault was succesfully handled. */
+int page_unprotect(target_ulong address, unsigned long pc, void *puc)
+{
+    unsigned int page_index, prot, pindex;
+    PageDesc *p, *p1;
+    target_ulong host_start, host_end, addr;
+
+    /* Technically this isn't safe inside a signal handler.  However we
+       know this only ever happens in a synchronous SEGV handler, so in
+       practice it seems to be ok.  */
+    mmap_lock();
+
+    host_start = address & qemu_host_page_mask;
+    page_index = host_start >> TARGET_PAGE_BITS;
+    p1 = page_find(page_index);
+    if (!p1) {
+        mmap_unlock();
+        return 0;
+    }
+    host_end = host_start + qemu_host_page_size;
+    p = p1;
+    prot = 0;
+    for(addr = host_start;addr < host_end; addr += TARGET_PAGE_SIZE) {
+        prot |= p->flags;
+        p++;
+    }
+    /* if the page was really writable, then we change its
+       protection back to writable */
+    if (prot & PAGE_WRITE_ORG) {
+        pindex = (address - host_start) >> TARGET_PAGE_BITS;
+        if (!(p1[pindex].flags & PAGE_WRITE)) {
+            mprotect((void *)g2h(host_start), qemu_host_page_size,
+                     (prot & PAGE_BITS) | PAGE_WRITE);
+            p1[pindex].flags |= PAGE_WRITE;
+            /* and since the content will be modified, we must invalidate
+               the corresponding translated code. */
+            tb_invalidate_phys_page(address, pc, puc);
+#ifdef DEBUG_TB_CHECK
+            tb_invalidate_check(address);
+#endif
+            mmap_unlock();
+            return 1;
+        }
+    }
+    mmap_unlock();
+    return 0;
+}
+
+static inline void tlb_set_dirty(CPUState *env,
+                                 unsigned long addr, target_ulong vaddr)
+{
+}
+#endif /* defined(CONFIG_USER_ONLY) */
+
+#if !defined(CONFIG_USER_ONLY)
+static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
+                             ram_addr_t memory);
+static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys,
+                           ram_addr_t orig_memory);
+#define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \
+                      need_subpage)                                     \
+    do {                                                                \
+        if (addr > start_addr)                                          \
+            start_addr2 = 0;                                            \
+        else {                                                          \
+            start_addr2 = start_addr & ~TARGET_PAGE_MASK;               \
+            if (start_addr2 > 0)                                        \
+                need_subpage = 1;                                       \
+        }                                                               \
+                                                                        \
+        if ((start_addr + orig_size) - addr >= TARGET_PAGE_SIZE)        \
+            end_addr2 = TARGET_PAGE_SIZE - 1;                           \
+        else {                                                          \
+            end_addr2 = (start_addr + orig_size - 1) & ~TARGET_PAGE_MASK; \
+            if (end_addr2 < TARGET_PAGE_SIZE - 1)                       \
+                need_subpage = 1;                                       \
+        }                                                               \
+    } while (0)
+
+/* register physical memory. 'size' must be a multiple of the target
+   page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
+   io memory page */
+void cpu_register_physical_memory(target_phys_addr_t start_addr,
+                                  ram_addr_t size,
+                                  ram_addr_t phys_offset)
+{
+    target_phys_addr_t addr, end_addr;
+    PhysPageDesc *p;
+    CPUState *env;
+    ram_addr_t orig_size = size;
+    void *subpage;
+
+#ifdef USE_KQEMU
+    /* XXX: should not depend on cpu context */
+    env = first_cpu;
+    if (env->kqemu_enabled) {
+        kqemu_set_phys_mem(start_addr, size, phys_offset);
+    }
+#endif
+    size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+    end_addr = start_addr + (target_phys_addr_t)size;
+    for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {
+        p = phys_page_find(addr >> TARGET_PAGE_BITS);
+        if (p && p->phys_offset != IO_MEM_UNASSIGNED) {
+            ram_addr_t orig_memory = p->phys_offset;
+            target_phys_addr_t start_addr2, end_addr2;
+            int need_subpage = 0;
+
+            CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2,
+                          need_subpage);
+            if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) {
+                if (!(orig_memory & IO_MEM_SUBPAGE)) {
+                    subpage = subpage_init((addr & TARGET_PAGE_MASK),
+                                           &p->phys_offset, orig_memory);
+                } else {
+                    subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK)
+                                            >> IO_MEM_SHIFT];
+                }
+                subpage_register(subpage, start_addr2, end_addr2, phys_offset);
+            } else {
+                p->phys_offset = phys_offset;
+                if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
+                    (phys_offset & IO_MEM_ROMD))
+                    phys_offset += TARGET_PAGE_SIZE;
+            }
+        } else {
+            p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
+            p->phys_offset = phys_offset;
+            if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
+                (phys_offset & IO_MEM_ROMD))
+                phys_offset += TARGET_PAGE_SIZE;
+            else {
+                target_phys_addr_t start_addr2, end_addr2;
+                int need_subpage = 0;
+
+                CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr,
+                              end_addr2, need_subpage);
+
+                if (need_subpage || phys_offset & IO_MEM_SUBWIDTH) {
+                    subpage = subpage_init((addr & TARGET_PAGE_MASK),
+                                           &p->phys_offset, IO_MEM_UNASSIGNED);
+                    subpage_register(subpage, start_addr2, end_addr2,
+                                     phys_offset);
+                }
+            }
+        }
+    }
+
+    /* since each CPU stores ram addresses in its TLB cache, we must
+       reset the modified entries */
+    /* XXX: slow ! */
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        tlb_flush(env, 1);
+    }
+}
+
+/* XXX: temporary until new memory mapping API */
+ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr)
+{
+    PhysPageDesc *p;
+
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p)
+        return IO_MEM_UNASSIGNED;
+    return p->phys_offset;
+}
+
+/* XXX: better than nothing */
+ram_addr_t qemu_ram_alloc(ram_addr_t size)
+{
+    ram_addr_t addr;
+    if ((phys_ram_alloc_offset + size) > phys_ram_size) {
+        fprintf(stderr, "Not enough memory (requested_size = %" PRIu64 ", max memory = %" PRIu64 "\n",
+                (uint64_t)size, (uint64_t)phys_ram_size);
+        abort();
+    }
+    addr = phys_ram_alloc_offset;
+    phys_ram_alloc_offset = TARGET_PAGE_ALIGN(phys_ram_alloc_offset + size);
+    return addr;
+}
+
+void qemu_ram_free(ram_addr_t addr)
+{
+}
+
+static uint32_t unassigned_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+#ifdef DEBUG_UNASSIGNED
+    printf("Unassigned mem read " TARGET_FMT_plx "\n", addr);
+#endif
+#ifdef TARGET_SPARC
+    do_unassigned_access(addr, 0, 0, 0);
+#elif defined(TARGET_CRIS)
+    do_unassigned_access(addr, 0, 0, 0);
+#endif
+    return 0;
+}
+
+static void unassigned_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+#ifdef DEBUG_UNASSIGNED
+    printf("Unassigned mem write " TARGET_FMT_plx " = 0x%x\n", addr, val);
+#endif
+#ifdef TARGET_SPARC
+    do_unassigned_access(addr, 1, 0, 0);
+#elif defined(TARGET_CRIS)
+    do_unassigned_access(addr, 1, 0, 0);
+#endif
+}
+
+static CPUReadMemoryFunc *unassigned_mem_read[3] = {
+    unassigned_mem_readb,
+    unassigned_mem_readb,
+    unassigned_mem_readb,
+};
+
+static CPUWriteMemoryFunc *unassigned_mem_write[3] = {
+    unassigned_mem_writeb,
+    unassigned_mem_writeb,
+    unassigned_mem_writeb,
+};
+
+static void notdirty_mem_writeb(void *opaque, target_phys_addr_t ram_addr,
+                                uint32_t val)
+{
+    int dirty_flags;
+    dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+    if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+        tb_invalidate_phys_page_fast(ram_addr, 1);
+        dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+    }
+    stb_p(phys_ram_base + ram_addr, val);
+#ifdef USE_KQEMU
+    if (cpu_single_env->kqemu_enabled &&
+        (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK)
+        kqemu_modify_page(cpu_single_env, ram_addr);
+#endif
+    dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+    phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags;
+    /* we remove the notdirty callback only if the code has been
+       flushed */
+    if (dirty_flags == 0xff)
+        tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
+}
+
+static void notdirty_mem_writew(void *opaque, target_phys_addr_t ram_addr,
+                                uint32_t val)
+{
+    int dirty_flags;
+    dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+    if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+        tb_invalidate_phys_page_fast(ram_addr, 2);
+        dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+    }
+    stw_p(phys_ram_base + ram_addr, val);
+#ifdef USE_KQEMU
+    if (cpu_single_env->kqemu_enabled &&
+        (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK)
+        kqemu_modify_page(cpu_single_env, ram_addr);
+#endif
+    dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+    phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags;
+    /* we remove the notdirty callback only if the code has been
+       flushed */
+    if (dirty_flags == 0xff)
+        tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
+}
+
+static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr,
+                                uint32_t val)
+{
+    int dirty_flags;
+    dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+    if (!(dirty_flags & CODE_DIRTY_FLAG)) {
+#if !defined(CONFIG_USER_ONLY)
+        tb_invalidate_phys_page_fast(ram_addr, 4);
+        dirty_flags = phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS];
+#endif
+    }
+    stl_p(phys_ram_base + ram_addr, val);
+#ifdef USE_KQEMU
+    if (cpu_single_env->kqemu_enabled &&
+        (dirty_flags & KQEMU_MODIFY_PAGE_MASK) != KQEMU_MODIFY_PAGE_MASK)
+        kqemu_modify_page(cpu_single_env, ram_addr);
+#endif
+    dirty_flags |= (0xff & ~CODE_DIRTY_FLAG);
+    phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] = dirty_flags;
+    /* we remove the notdirty callback only if the code has been
+       flushed */
+    if (dirty_flags == 0xff)
+        tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr);
+}
+
+static CPUReadMemoryFunc *error_mem_read[3] = {
+    NULL, /* never used */
+    NULL, /* never used */
+    NULL, /* never used */
+};
+
+static CPUWriteMemoryFunc *notdirty_mem_write[3] = {
+    notdirty_mem_writeb,
+    notdirty_mem_writew,
+    notdirty_mem_writel,
+};
+
+/* Generate a debug exception if a watchpoint has been hit.  */
+static void check_watchpoint(int offset, int flags)
+{
+    CPUState *env = cpu_single_env;
+    target_ulong vaddr;
+    int i;
+
+    vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset;
+    for (i = 0; i < env->nb_watchpoints; i++) {
+        if (vaddr == env->watchpoint[i].vaddr
+                && (env->watchpoint[i].type & flags)) {
+            env->watchpoint_hit = i + 1;
+            cpu_interrupt(env, CPU_INTERRUPT_DEBUG);
+            break;
+        }
+    }
+}
+
+/* Watchpoint access routines.  Watchpoints are inserted using TLB tricks,
+   so these check for a hit then pass through to the normal out-of-line
+   phys routines.  */
+static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ);
+    return ldub_phys(addr);
+}
+
+static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+    check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ);
+    return lduw_phys(addr);
+}
+
+static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ);
+    return ldl_phys(addr);
+}
+
+static void watch_mem_writeb(void *opaque, target_phys_addr_t addr,
+                             uint32_t val)
+{
+    check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE);
+    stb_phys(addr, val);
+}
+
+static void watch_mem_writew(void *opaque, target_phys_addr_t addr,
+                             uint32_t val)
+{
+    check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE);
+    stw_phys(addr, val);
+}
+
+static void watch_mem_writel(void *opaque, target_phys_addr_t addr,
+                             uint32_t val)
+{
+    check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE);
+    stl_phys(addr, val);
+}
+
+static CPUReadMemoryFunc *watch_mem_read[3] = {
+    watch_mem_readb,
+    watch_mem_readw,
+    watch_mem_readl,
+};
+
+static CPUWriteMemoryFunc *watch_mem_write[3] = {
+    watch_mem_writeb,
+    watch_mem_writew,
+    watch_mem_writel,
+};
+
+static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr,
+                                 unsigned int len)
+{
+    uint32_t ret;
+    unsigned int idx;
+
+    idx = SUBPAGE_IDX(addr - mmio->base);
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__,
+           mmio, len, addr, idx);
+#endif
+    ret = (**mmio->mem_read[idx][len])(mmio->opaque[idx][0][len], addr);
+
+    return ret;
+}
+
+static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr,
+                              uint32_t value, unsigned int len)
+{
+    unsigned int idx;
+
+    idx = SUBPAGE_IDX(addr - mmio->base);
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n", __func__,
+           mmio, len, addr, idx, value);
+#endif
+    (**mmio->mem_write[idx][len])(mmio->opaque[idx][1][len], addr, value);
+}
+
+static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr)
+{
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+
+    return subpage_readlen(opaque, addr, 0);
+}
+
+static void subpage_writeb (void *opaque, target_phys_addr_t addr,
+                            uint32_t value)
+{
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value);
+#endif
+    subpage_writelen(opaque, addr, value, 0);
+}
+
+static uint32_t subpage_readw (void *opaque, target_phys_addr_t addr)
+{
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+
+    return subpage_readlen(opaque, addr, 1);
+}
+
+static void subpage_writew (void *opaque, target_phys_addr_t addr,
+                            uint32_t value)
+{
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value);
+#endif
+    subpage_writelen(opaque, addr, value, 1);
+}
+
+static uint32_t subpage_readl (void *opaque, target_phys_addr_t addr)
+{
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
+#endif
+
+    return subpage_readlen(opaque, addr, 2);
+}
+
+static void subpage_writel (void *opaque,
+                         target_phys_addr_t addr, uint32_t value)
+{
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value);
+#endif
+    subpage_writelen(opaque, addr, value, 2);
+}
+
+static CPUReadMemoryFunc *subpage_read[] = {
+    &subpage_readb,
+    &subpage_readw,
+    &subpage_readl,
+};
+
+static CPUWriteMemoryFunc *subpage_write[] = {
+    &subpage_writeb,
+    &subpage_writew,
+    &subpage_writel,
+};
+
+static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
+                             ram_addr_t memory)
+{
+    int idx, eidx;
+    unsigned int i;
+
+    if (start >= TARGET_PAGE_SIZE || end >= TARGET_PAGE_SIZE)
+        return -1;
+    idx = SUBPAGE_IDX(start);
+    eidx = SUBPAGE_IDX(end);
+#if defined(DEBUG_SUBPAGE)
+    printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %d\n", __func__,
+           mmio, start, end, idx, eidx, memory);
+#endif
+    memory >>= IO_MEM_SHIFT;
+    for (; idx <= eidx; idx++) {
+        for (i = 0; i < 4; i++) {
+            if (io_mem_read[memory][i]) {
+                mmio->mem_read[idx][i] = &io_mem_read[memory][i];
+                mmio->opaque[idx][0][i] = io_mem_opaque[memory];
+            }
+            if (io_mem_write[memory][i]) {
+                mmio->mem_write[idx][i] = &io_mem_write[memory][i];
+                mmio->opaque[idx][1][i] = io_mem_opaque[memory];
+            }
+        }
+    }
+
+    return 0;
+}
+
+static void *subpage_init (target_phys_addr_t base, ram_addr_t *phys,
+                           ram_addr_t orig_memory)
+{
+    subpage_t *mmio;
+    int subpage_memory;
+
+    mmio = qemu_mallocz(sizeof(subpage_t));
+    if (mmio != NULL) {
+        mmio->base = base;
+        subpage_memory = cpu_register_io_memory(0, subpage_read, subpage_write, mmio);
+#if defined(DEBUG_SUBPAGE)
+        printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__,
+               mmio, base, TARGET_PAGE_SIZE, subpage_memory);
+#endif
+        *phys = subpage_memory | IO_MEM_SUBPAGE;
+        subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory);
+    }
+
+    return mmio;
+}
+
+static void io_mem_init(void)
+{
+    cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL);
+    cpu_register_io_memory(IO_MEM_UNASSIGNED >> IO_MEM_SHIFT, unassigned_mem_read, unassigned_mem_write, NULL);
+    cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL);
+    io_mem_nb = 5;
+
+    io_mem_watch = cpu_register_io_memory(0, watch_mem_read,
+                                          watch_mem_write, NULL);
+    /* alloc dirty bits array */
+    phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS);
+    memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS);
+}
+
+/* mem_read and mem_write are arrays of functions containing the
+   function to access byte (index 0), word (index 1) and dword (index
+   2). Functions can be omitted with a NULL function pointer. The
+   registered functions may be modified dynamically later.
+   If io_index is non zero, the corresponding io zone is
+   modified. If it is zero, a new io zone is allocated. The return
+   value can be used with cpu_register_physical_memory(). (-1) is
+   returned if error. */
+int cpu_register_io_memory(int io_index,
+                           CPUReadMemoryFunc **mem_read,
+                           CPUWriteMemoryFunc **mem_write,
+                           void *opaque)
+{
+    int i, subwidth = 0;
+
+    if (io_index <= 0) {
+        if (io_mem_nb >= IO_MEM_NB_ENTRIES)
+            return -1;
+        io_index = io_mem_nb++;
+    } else {
+        if (io_index >= IO_MEM_NB_ENTRIES)
+            return -1;
+    }
+
+    for(i = 0;i < 3; i++) {
+        if (!mem_read[i] || !mem_write[i])
+            subwidth = IO_MEM_SUBWIDTH;
+        io_mem_read[io_index][i] = mem_read[i];
+        io_mem_write[io_index][i] = mem_write[i];
+    }
+    io_mem_opaque[io_index] = opaque;
+    return (io_index << IO_MEM_SHIFT) | subwidth;
+}
+
+CPUWriteMemoryFunc **cpu_get_io_memory_write(int io_index)
+{
+    return io_mem_write[io_index >> IO_MEM_SHIFT];
+}
+
+CPUReadMemoryFunc **cpu_get_io_memory_read(int io_index)
+{
+    return io_mem_read[io_index >> IO_MEM_SHIFT];
+}
+
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+/* physical memory access (slow version, mainly for debug) */
+#if defined(CONFIG_USER_ONLY)
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+                            int len, int is_write)
+{
+    int l, flags;
+    target_ulong page;
+    void * p;
+
+    while (len > 0) {
+        page = addr & TARGET_PAGE_MASK;
+        l = (page + TARGET_PAGE_SIZE) - addr;
+        if (l > len)
+            l = len;
+        flags = page_get_flags(page);
+        if (!(flags & PAGE_VALID))
+            return;
+        if (is_write) {
+            if (!(flags & PAGE_WRITE))
+                return;
+            /* XXX: this code should not depend on lock_user */
+            if (!(p = lock_user(VERIFY_WRITE, addr, l, 0)))
+                /* FIXME - should this return an error rather than just fail? */
+                return;
+            memcpy(p, buf, l);
+            unlock_user(p, addr, l);
+        } else {
+            if (!(flags & PAGE_READ))
+                return;
+            /* XXX: this code should not depend on lock_user */
+            if (!(p = lock_user(VERIFY_READ, addr, l, 1)))
+                /* FIXME - should this return an error rather than just fail? */
+                return;
+            memcpy(buf, p, l);
+            unlock_user(p, addr, 0);
+        }
+        len -= l;
+        buf += l;
+        addr += l;
+    }
+}
+
+#else
+void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
+                            int len, int is_write)
+{
+    int l, io_index;
+    uint8_t *ptr;
+    uint32_t val;
+    target_phys_addr_t page;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    while (len > 0) {
+        page = addr & TARGET_PAGE_MASK;
+        l = (page + TARGET_PAGE_SIZE) - addr;
+        if (l > len)
+            l = len;
+        p = phys_page_find(page >> TARGET_PAGE_BITS);
+        if (!p) {
+            pd = IO_MEM_UNASSIGNED;
+        } else {
+            pd = p->phys_offset;
+        }
+
+        if (is_write) {
+            if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+                io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+                /* XXX: could force cpu_single_env to NULL to avoid
+                   potential bugs */
+                if (l >= 4 && ((addr & 3) == 0)) {
+                    /* 32 bit write access */
+                    val = ldl_p(buf);
+                    io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+                    l = 4;
+                } else if (l >= 2 && ((addr & 1) == 0)) {
+                    /* 16 bit write access */
+                    val = lduw_p(buf);
+                    io_mem_write[io_index][1](io_mem_opaque[io_index], addr, val);
+                    l = 2;
+                } else {
+                    /* 8 bit write access */
+                    val = ldub_p(buf);
+                    io_mem_write[io_index][0](io_mem_opaque[io_index], addr, val);
+                    l = 1;
+                }
+            } else {
+                unsigned long addr1;
+                addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+                /* RAM case */
+                ptr = phys_ram_base + addr1;
+                memcpy(ptr, buf, l);
+                if (!cpu_physical_memory_is_dirty(addr1)) {
+                    /* invalidate code */
+                    tb_invalidate_phys_page_range(addr1, addr1 + l, 0);
+                    /* set dirty bit */
+                    phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
+                        (0xff & ~CODE_DIRTY_FLAG);
+                }
+            }
+        } else {
+            if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+                !(pd & IO_MEM_ROMD)) {
+                /* I/O case */
+                io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+                if (l >= 4 && ((addr & 3) == 0)) {
+                    /* 32 bit read access */
+                    val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+                    stl_p(buf, val);
+                    l = 4;
+                } else if (l >= 2 && ((addr & 1) == 0)) {
+                    /* 16 bit read access */
+                    val = io_mem_read[io_index][1](io_mem_opaque[io_index], addr);
+                    stw_p(buf, val);
+                    l = 2;
+                } else {
+                    /* 8 bit read access */
+                    val = io_mem_read[io_index][0](io_mem_opaque[io_index], addr);
+                    stb_p(buf, val);
+                    l = 1;
+                }
+            } else {
+                /* RAM case */
+                ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+                    (addr & ~TARGET_PAGE_MASK);
+                memcpy(buf, ptr, l);
+            }
+        }
+        len -= l;
+        buf += l;
+        addr += l;
+    }
+}
+
+/* used for ROM loading : can write in RAM and ROM */
+void cpu_physical_memory_write_rom(target_phys_addr_t addr,
+                                   const uint8_t *buf, int len)
+{
+    int l;
+    uint8_t *ptr;
+    target_phys_addr_t page;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    while (len > 0) {
+        page = addr & TARGET_PAGE_MASK;
+        l = (page + TARGET_PAGE_SIZE) - addr;
+        if (l > len)
+            l = len;
+        p = phys_page_find(page >> TARGET_PAGE_BITS);
+        if (!p) {
+            pd = IO_MEM_UNASSIGNED;
+        } else {
+            pd = p->phys_offset;
+        }
+
+        if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM &&
+            (pd & ~TARGET_PAGE_MASK) != IO_MEM_ROM &&
+            !(pd & IO_MEM_ROMD)) {
+            /* do nothing */
+        } else {
+            unsigned long addr1;
+            addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+            /* ROM/RAM case */
+            ptr = phys_ram_base + addr1;
+            memcpy(ptr, buf, l);
+        }
+        len -= l;
+        buf += l;
+        addr += l;
+    }
+}
+
+
+/* warning: addr must be aligned */
+uint32_t ldl_phys(target_phys_addr_t addr)
+{
+    int io_index;
+    uint8_t *ptr;
+    uint32_t val;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+
+    if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+        !(pd & IO_MEM_ROMD)) {
+        /* I/O case */
+        io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+        val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+    } else {
+        /* RAM case */
+        ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+            (addr & ~TARGET_PAGE_MASK);
+        val = ldl_p(ptr);
+    }
+    return val;
+}
+
+/* warning: addr must be aligned */
+uint64_t ldq_phys(target_phys_addr_t addr)
+{
+    int io_index;
+    uint8_t *ptr;
+    uint64_t val;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+
+    if ((pd & ~TARGET_PAGE_MASK) > IO_MEM_ROM &&
+        !(pd & IO_MEM_ROMD)) {
+        /* I/O case */
+        io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+#ifdef TARGET_WORDS_BIGENDIAN
+        val = (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr) << 32;
+        val |= io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4);
+#else
+        val = io_mem_read[io_index][2](io_mem_opaque[io_index], addr);
+        val |= (uint64_t)io_mem_read[io_index][2](io_mem_opaque[io_index], addr + 4) << 32;
+#endif
+    } else {
+        /* RAM case */
+        ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+            (addr & ~TARGET_PAGE_MASK);
+        val = ldq_p(ptr);
+    }
+    return val;
+}
+
+/* XXX: optimize */
+uint32_t ldub_phys(target_phys_addr_t addr)
+{
+    uint8_t val;
+    cpu_physical_memory_read(addr, &val, 1);
+    return val;
+}
+
+/* XXX: optimize */
+uint32_t lduw_phys(target_phys_addr_t addr)
+{
+    uint16_t val;
+    cpu_physical_memory_read(addr, (uint8_t *)&val, 2);
+    return tswap16(val);
+}
+
+/* warning: addr must be aligned. The ram page is not masked as dirty
+   and the code inside is not invalidated. It is useful if the dirty
+   bits are used to track modified PTEs */
+void stl_phys_notdirty(target_phys_addr_t addr, uint32_t val)
+{
+    int io_index;
+    uint8_t *ptr;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+
+    if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+        io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+        io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+    } else {
+        ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+            (addr & ~TARGET_PAGE_MASK);
+        stl_p(ptr, val);
+    }
+}
+
+void stq_phys_notdirty(target_phys_addr_t addr, uint64_t val)
+{
+    int io_index;
+    uint8_t *ptr;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+
+    if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+        io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+#ifdef TARGET_WORDS_BIGENDIAN
+        io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val >> 32);
+        io_mem_write[io_index][2](io_mem_opaque[io_index], addr + 4, val);
+#else
+        io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+        io_mem_write[io_index][2](io_mem_opaque[io_index], addr + 4, val >> 32);
+#endif
+    } else {
+        ptr = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+            (addr & ~TARGET_PAGE_MASK);
+        stq_p(ptr, val);
+    }
+}
+
+/* warning: addr must be aligned */
+void stl_phys(target_phys_addr_t addr, uint32_t val)
+{
+    int io_index;
+    uint8_t *ptr;
+    unsigned long pd;
+    PhysPageDesc *p;
+
+    p = phys_page_find(addr >> TARGET_PAGE_BITS);
+    if (!p) {
+        pd = IO_MEM_UNASSIGNED;
+    } else {
+        pd = p->phys_offset;
+    }
+
+    if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) {
+        io_index = (pd >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+        io_mem_write[io_index][2](io_mem_opaque[io_index], addr, val);
+    } else {
+        unsigned long addr1;
+        addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK);
+        /* RAM case */
+        ptr = phys_ram_base + addr1;
+        stl_p(ptr, val);
+        if (!cpu_physical_memory_is_dirty(addr1)) {
+            /* invalidate code */
+            tb_invalidate_phys_page_range(addr1, addr1 + 4, 0);
+            /* set dirty bit */
+            phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |=
+                (0xff & ~CODE_DIRTY_FLAG);
+        }
+    }
+}
+
+/* XXX: optimize */
+void stb_phys(target_phys_addr_t addr, uint32_t val)
+{
+    uint8_t v = val;
+    cpu_physical_memory_write(addr, &v, 1);
+}
+
+/* XXX: optimize */
+void stw_phys(target_phys_addr_t addr, uint32_t val)
+{
+    uint16_t v = tswap16(val);
+    cpu_physical_memory_write(addr, (const uint8_t *)&v, 2);
+}
+
+/* XXX: optimize */
+void stq_phys(target_phys_addr_t addr, uint64_t val)
+{
+    val = tswap64(val);
+    cpu_physical_memory_write(addr, (const uint8_t *)&val, 8);
+}
+
+#endif
+
+/* virtual memory access for debug */
+int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
+                        uint8_t *buf, int len, int is_write)
+{
+    int l;
+    target_phys_addr_t phys_addr;
+    target_ulong page;
+
+    while (len > 0) {
+        page = addr & TARGET_PAGE_MASK;
+        phys_addr = cpu_get_phys_page_debug(env, page);
+        /* if no physical page mapped, return an error */
+        if (phys_addr == -1)
+            return -1;
+        l = (page + TARGET_PAGE_SIZE) - addr;
+        if (l > len)
+            l = len;
+        cpu_physical_memory_rw(phys_addr + (addr & ~TARGET_PAGE_MASK),
+                               buf, l, is_write);
+        len -= l;
+        buf += l;
+        addr += l;
+    }
+    return 0;
+}
+
+/* in deterministic execution mode, instructions doing device I/Os
+   must be at the end of the TB */
+void cpu_io_recompile(CPUState *env, void *retaddr)
+{
+    TranslationBlock *tb;
+    uint32_t n, cflags;
+    target_ulong pc, cs_base;
+    uint64_t flags;
+
+    tb = tb_find_pc((unsigned long)retaddr);
+    if (!tb) {
+        cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", 
+                  retaddr);
+    }
+    n = env->icount_decr.u16.low + tb->icount;
+    cpu_restore_state(tb, env, (unsigned long)retaddr, NULL);
+    /* Calculate how many instructions had been executed before the fault
+       occurred.  */
+    n = n - env->icount_decr.u16.low;
+    /* Generate a new TB ending on the I/O insn.  */
+    n++;
+    /* On MIPS and SH, delay slot instructions can only be restarted if
+       they were already the first instruction in the TB.  If this is not
+       the first instruction in a TB then re-execute the preceding
+       branch.  */
+#if defined(TARGET_MIPS)
+    if ((env->hflags & MIPS_HFLAG_BMASK) != 0 && n > 1) {
+        env->active_tc.PC -= 4;
+        env->icount_decr.u16.low++;
+        env->hflags &= ~MIPS_HFLAG_BMASK;
+    }
+#elif defined(TARGET_SH4)
+    if ((env->flags & ((DELAY_SLOT | DELAY_SLOT_CONDITIONAL))) != 0
+            && n > 1) {
+        env->pc -= 2;
+        env->icount_decr.u16.low++;
+        env->flags &= ~(DELAY_SLOT | DELAY_SLOT_CONDITIONAL);
+    }
+#endif
+    /* This should never happen.  */
+    if (n > CF_COUNT_MASK)
+        cpu_abort(env, "TB too big during recompile");
+
+    cflags = n | CF_LAST_IO;
+    pc = tb->pc;
+    cs_base = tb->cs_base;
+    flags = tb->flags;
+    tb_phys_invalidate(tb, -1);
+    /* FIXME: In theory this could raise an exception.  In practice
+       we have already translated the block once so it's probably ok.  */
+    tb_gen_code(env, pc, cs_base, flags, cflags);
+    /* TODO: If env->pc != tb->pc (i.e. the faulting instruction was not
+       the first in the TB) then we end up generating a whole new TB and
+       repeating the fault, which is horribly inefficient.
+       Better would be to execute just this insn uncached, or generate a
+       second new TB.  */
+    cpu_resume_from_signal(env, NULL);
+}
+
+void dump_exec_info(FILE *f,
+                    int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+    int i, target_code_size, max_target_code_size;
+    int direct_jmp_count, direct_jmp2_count, cross_page;
+    TranslationBlock *tb;
+
+    target_code_size = 0;
+    max_target_code_size = 0;
+    cross_page = 0;
+    direct_jmp_count = 0;
+    direct_jmp2_count = 0;
+    for(i = 0; i < nb_tbs; i++) {
+        tb = &tbs[i];
+        target_code_size += tb->size;
+        if (tb->size > max_target_code_size)
+            max_target_code_size = tb->size;
+        if (tb->page_addr[1] != -1)
+            cross_page++;
+        if (tb->tb_next_offset[0] != 0xffff) {
+            direct_jmp_count++;
+            if (tb->tb_next_offset[1] != 0xffff) {
+                direct_jmp2_count++;
+            }
+        }
+    }
+    /* XXX: avoid using doubles ? */
+    cpu_fprintf(f, "Translation buffer state:\n");
+    cpu_fprintf(f, "gen code size       %ld/%ld\n",
+                code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size);
+    cpu_fprintf(f, "TB count            %d/%d\n", 
+                nb_tbs, code_gen_max_blocks);
+    cpu_fprintf(f, "TB avg target size  %d max=%d bytes\n",
+                nb_tbs ? target_code_size / nb_tbs : 0,
+                max_target_code_size);
+    cpu_fprintf(f, "TB avg host size    %d bytes (expansion ratio: %0.1f)\n",
+                nb_tbs ? (code_gen_ptr - code_gen_buffer) / nb_tbs : 0,
+                target_code_size ? (double) (code_gen_ptr - code_gen_buffer) / target_code_size : 0);
+    cpu_fprintf(f, "cross page TB count %d (%d%%)\n",
+            cross_page,
+            nb_tbs ? (cross_page * 100) / nb_tbs : 0);
+    cpu_fprintf(f, "direct jump count   %d (%d%%) (2 jumps=%d %d%%)\n",
+                direct_jmp_count,
+                nb_tbs ? (direct_jmp_count * 100) / nb_tbs : 0,
+                direct_jmp2_count,
+                nb_tbs ? (direct_jmp2_count * 100) / nb_tbs : 0);
+    cpu_fprintf(f, "\nStatistics:\n");
+    cpu_fprintf(f, "TB flush count      %d\n", tb_flush_count);
+    cpu_fprintf(f, "TB invalidate count %d\n", tb_phys_invalidate_count);
+    cpu_fprintf(f, "TLB flush count     %d\n", tlb_flush_count);
+    tcg_dump_info(f, cpu_fprintf);
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _cmmu
+#define GETPC() NULL
+#define env cpu_single_env
+#define SOFTMMU_CODE_ACCESS
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+#undef env
+
+#endif
diff --git a/fpu/softfloat-macros.h b/fpu/softfloat-macros.h
new file mode 100644
index 0000000..2c8f18b
--- /dev/null
+++ b/fpu/softfloat-macros.h
@@ -0,0 +1,720 @@
+
+/*============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2b.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal notice) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+/*----------------------------------------------------------------------------
+| Shifts `a' right by the number of bits given in `count'.  If any nonzero
+| bits are shifted off, they are ``jammed'' into the least significant bit of
+| the result by setting the least significant bit to 1.  The value of `count'
+| can be arbitrarily large; in particular, if `count' is greater than 32, the
+| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+| The result is stored in the location pointed to by `zPtr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void shift32RightJamming( bits32 a, int16 count, bits32 *zPtr )
+{
+    bits32 z;
+
+    if ( count == 0 ) {
+        z = a;
+    }
+    else if ( count < 32 ) {
+        z = ( a>>count ) | ( ( a<<( ( - count ) & 31 ) ) != 0 );
+    }
+    else {
+        z = ( a != 0 );
+    }
+    *zPtr = z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts `a' right by the number of bits given in `count'.  If any nonzero
+| bits are shifted off, they are ``jammed'' into the least significant bit of
+| the result by setting the least significant bit to 1.  The value of `count'
+| can be arbitrarily large; in particular, if `count' is greater than 64, the
+| result will be either 0 or 1, depending on whether `a' is zero or nonzero.
+| The result is stored in the location pointed to by `zPtr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void shift64RightJamming( bits64 a, int16 count, bits64 *zPtr )
+{
+    bits64 z;
+
+    if ( count == 0 ) {
+        z = a;
+    }
+    else if ( count < 64 ) {
+        z = ( a>>count ) | ( ( a<<( ( - count ) & 63 ) ) != 0 );
+    }
+    else {
+        z = ( a != 0 );
+    }
+    *zPtr = z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by 64
+| _plus_ the number of bits given in `count'.  The shifted result is at most
+| 64 nonzero bits; this is stored at the location pointed to by `z0Ptr'.  The
+| bits shifted off form a second 64-bit result as follows:  The _last_ bit
+| shifted off is the most-significant bit of the extra result, and the other
+| 63 bits of the extra result are all zero if and only if _all_but_the_last_
+| bits shifted off were all zero.  This extra result is stored in the location
+| pointed to by `z1Ptr'.  The value of `count' can be arbitrarily large.
+|     (This routine makes more sense if `a0' and `a1' are considered to form
+| a fixed-point value with binary point between `a0' and `a1'.  This fixed-
+| point value is shifted right by the number of bits given in `count', and
+| the integer part of the result is returned at the location pointed to by
+| `z0Ptr'.  The fractional part of the result may be slightly corrupted as
+| described above, and is returned at the location pointed to by `z1Ptr'.)
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift64ExtraRightJamming(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1 != 0 );
+        z0 = a0>>count;
+    }
+    else {
+        if ( count == 64 ) {
+            z1 = a0 | ( a1 != 0 );
+        }
+        else {
+            z1 = ( ( a0 | a1 ) != 0 );
+        }
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+| number of bits given in `count'.  Any bits shifted off are lost.  The value
+| of `count' can be arbitrarily large; in particular, if `count' is greater
+| than 128, the result will be 0.  The result is broken into two 64-bit pieces
+| which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128Right(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1>>count );
+        z0 = a0>>count;
+    }
+    else {
+        z1 = ( count < 64 ) ? ( a0>>( count & 63 ) ) : 0;
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' right by the
+| number of bits given in `count'.  If any nonzero bits are shifted off, they
+| are ``jammed'' into the least significant bit of the result by setting the
+| least significant bit to 1.  The value of `count' can be arbitrarily large;
+| in particular, if `count' is greater than 128, the result will be either
+| 0 or 1, depending on whether the concatenation of `a0' and `a1' is zero or
+| nonzero.  The result is broken into two 64-bit pieces which are stored at
+| the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128RightJamming(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z0, z1;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z1 = a1;
+        z0 = a0;
+    }
+    else if ( count < 64 ) {
+        z1 = ( a0<<negCount ) | ( a1>>count ) | ( ( a1<<negCount ) != 0 );
+        z0 = a0>>count;
+    }
+    else {
+        if ( count == 64 ) {
+            z1 = a0 | ( a1 != 0 );
+        }
+        else if ( count < 128 ) {
+            z1 = ( a0>>( count & 63 ) ) | ( ( ( a0<<negCount ) | a1 ) != 0 );
+        }
+        else {
+            z1 = ( ( a0 | a1 ) != 0 );
+        }
+        z0 = 0;
+    }
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' right
+| by 64 _plus_ the number of bits given in `count'.  The shifted result is
+| at most 128 nonzero bits; these are broken into two 64-bit pieces which are
+| stored at the locations pointed to by `z0Ptr' and `z1Ptr'.  The bits shifted
+| off form a third 64-bit result as follows:  The _last_ bit shifted off is
+| the most-significant bit of the extra result, and the other 63 bits of the
+| extra result are all zero if and only if _all_but_the_last_ bits shifted off
+| were all zero.  This extra result is stored in the location pointed to by
+| `z2Ptr'.  The value of `count' can be arbitrarily large.
+|     (This routine makes more sense if `a0', `a1', and `a2' are considered
+| to form a fixed-point value with binary point between `a1' and `a2'.  This
+| fixed-point value is shifted right by the number of bits given in `count',
+| and the integer part of the result is returned at the locations pointed to
+| by `z0Ptr' and `z1Ptr'.  The fractional part of the result may be slightly
+| corrupted as described above, and is returned at the location pointed to by
+| `z2Ptr'.)
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shift128ExtraRightJamming(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     int16 count,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 negCount = ( - count ) & 63;
+
+    if ( count == 0 ) {
+        z2 = a2;
+        z1 = a1;
+        z0 = a0;
+    }
+    else {
+        if ( count < 64 ) {
+            z2 = a1<<negCount;
+            z1 = ( a0<<negCount ) | ( a1>>count );
+            z0 = a0>>count;
+        }
+        else {
+            if ( count == 64 ) {
+                z2 = a1;
+                z1 = a0;
+            }
+            else {
+                a2 |= a1;
+                if ( count < 128 ) {
+                    z2 = a0<<negCount;
+                    z1 = a0>>( count & 63 );
+                }
+                else {
+                    z2 = ( count == 128 ) ? a0 : ( a0 != 0 );
+                    z1 = 0;
+                }
+            }
+            z0 = 0;
+        }
+        z2 |= ( a2 != 0 );
+    }
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the
+| number of bits given in `count'.  Any bits shifted off are lost.  The value
+| of `count' must be less than 64.  The result is broken into two 64-bit
+| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shortShift128Left(
+     bits64 a0, bits64 a1, int16 count, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+    *z1Ptr = a1<<count;
+    *z0Ptr =
+        ( count == 0 ) ? a0 : ( a0<<count ) | ( a1>>( ( - count ) & 63 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Shifts the 192-bit value formed by concatenating `a0', `a1', and `a2' left
+| by the number of bits given in `count'.  Any bits shifted off are lost.
+| The value of `count' must be less than 64.  The result is broken into three
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+| `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ shortShift192Left(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     int16 count,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 negCount;
+
+    z2 = a2<<count;
+    z1 = a1<<count;
+    z0 = a0<<count;
+    if ( 0 < count ) {
+        negCount = ( ( - count ) & 63 );
+        z1 |= a2>>negCount;
+        z0 |= a1>>negCount;
+    }
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Adds the 128-bit value formed by concatenating `a0' and `a1' to the 128-bit
+| value formed by concatenating `b0' and `b1'.  Addition is modulo 2^128, so
+| any carry out is lost.  The result is broken into two 64-bit pieces which
+| are stored at the locations pointed to by `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ add128(
+     bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits64 z1;
+
+    z1 = a1 + b1;
+    *z1Ptr = z1;
+    *z0Ptr = a0 + b0 + ( z1 < a1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Adds the 192-bit value formed by concatenating `a0', `a1', and `a2' to the
+| 192-bit value formed by concatenating `b0', `b1', and `b2'.  Addition is
+| modulo 2^192, so any carry out is lost.  The result is broken into three
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr',
+| `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ add192(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     bits64 b0,
+     bits64 b1,
+     bits64 b2,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 carry0, carry1;
+
+    z2 = a2 + b2;
+    carry1 = ( z2 < a2 );
+    z1 = a1 + b1;
+    carry0 = ( z1 < a1 );
+    z0 = a0 + b0;
+    z1 += carry1;
+    z0 += ( z1 < carry1 );
+    z0 += carry0;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Subtracts the 128-bit value formed by concatenating `b0' and `b1' from the
+| 128-bit value formed by concatenating `a0' and `a1'.  Subtraction is modulo
+| 2^128, so any borrow out (carry out) is lost.  The result is broken into two
+| 64-bit pieces which are stored at the locations pointed to by `z0Ptr' and
+| `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ sub128(
+     bits64 a0, bits64 a1, bits64 b0, bits64 b1, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+
+    *z1Ptr = a1 - b1;
+    *z0Ptr = a0 - b0 - ( a1 < b1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Subtracts the 192-bit value formed by concatenating `b0', `b1', and `b2'
+| from the 192-bit value formed by concatenating `a0', `a1', and `a2'.
+| Subtraction is modulo 2^192, so any borrow out (carry out) is lost.  The
+| result is broken into three 64-bit pieces which are stored at the locations
+| pointed to by `z0Ptr', `z1Ptr', and `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ sub192(
+     bits64 a0,
+     bits64 a1,
+     bits64 a2,
+     bits64 b0,
+     bits64 b1,
+     bits64 b2,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2;
+    int8 borrow0, borrow1;
+
+    z2 = a2 - b2;
+    borrow1 = ( a2 < b2 );
+    z1 = a1 - b1;
+    borrow0 = ( a1 < b1 );
+    z0 = a0 - b0;
+    z0 -= ( z1 < borrow1 );
+    z1 -= borrow1;
+    z0 -= borrow0;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies `a' by `b' to obtain a 128-bit product.  The product is broken
+| into two 64-bit pieces which are stored at the locations pointed to by
+| `z0Ptr' and `z1Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void mul64To128( bits64 a, bits64 b, bits64 *z0Ptr, bits64 *z1Ptr )
+{
+    bits32 aHigh, aLow, bHigh, bLow;
+    bits64 z0, zMiddleA, zMiddleB, z1;
+
+    aLow = a;
+    aHigh = a>>32;
+    bLow = b;
+    bHigh = b>>32;
+    z1 = ( (bits64) aLow ) * bLow;
+    zMiddleA = ( (bits64) aLow ) * bHigh;
+    zMiddleB = ( (bits64) aHigh ) * bLow;
+    z0 = ( (bits64) aHigh ) * bHigh;
+    zMiddleA += zMiddleB;
+    z0 += ( ( (bits64) ( zMiddleA < zMiddleB ) )<<32 ) + ( zMiddleA>>32 );
+    zMiddleA <<= 32;
+    z1 += zMiddleA;
+    z0 += ( z1 < zMiddleA );
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies the 128-bit value formed by concatenating `a0' and `a1' by
+| `b' to obtain a 192-bit product.  The product is broken into three 64-bit
+| pieces which are stored at the locations pointed to by `z0Ptr', `z1Ptr', and
+| `z2Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ mul128By64To192(
+     bits64 a0,
+     bits64 a1,
+     bits64 b,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr
+ )
+{
+    bits64 z0, z1, z2, more1;
+
+    mul64To128( a1, b, &z1, &z2 );
+    mul64To128( a0, b, &z0, &more1 );
+    add128( z0, more1, 0, z1, &z0, &z1 );
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Multiplies the 128-bit value formed by concatenating `a0' and `a1' to the
+| 128-bit value formed by concatenating `b0' and `b1' to obtain a 256-bit
+| product.  The product is broken into four 64-bit pieces which are stored at
+| the locations pointed to by `z0Ptr', `z1Ptr', `z2Ptr', and `z3Ptr'.
+*----------------------------------------------------------------------------*/
+
+INLINE void
+ mul128To256(
+     bits64 a0,
+     bits64 a1,
+     bits64 b0,
+     bits64 b1,
+     bits64 *z0Ptr,
+     bits64 *z1Ptr,
+     bits64 *z2Ptr,
+     bits64 *z3Ptr
+ )
+{
+    bits64 z0, z1, z2, z3;
+    bits64 more1, more2;
+
+    mul64To128( a1, b1, &z2, &z3 );
+    mul64To128( a1, b0, &z1, &more2 );
+    add128( z1, more2, 0, z2, &z1, &z2 );
+    mul64To128( a0, b0, &z0, &more1 );
+    add128( z0, more1, 0, z1, &z0, &z1 );
+    mul64To128( a0, b1, &more1, &more2 );
+    add128( more1, more2, 0, z2, &more1, &z2 );
+    add128( z0, z1, 0, more1, &z0, &z1 );
+    *z3Ptr = z3;
+    *z2Ptr = z2;
+    *z1Ptr = z1;
+    *z0Ptr = z0;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns an approximation to the 64-bit integer quotient obtained by dividing
+| `b' into the 128-bit value formed by concatenating `a0' and `a1'.  The
+| divisor `b' must be at least 2^63.  If q is the exact quotient truncated
+| toward zero, the approximation returned lies between q and q + 2 inclusive.
+| If the exact quotient q is larger than 64 bits, the maximum positive 64-bit
+| unsigned integer is returned.
+*----------------------------------------------------------------------------*/
+
+static bits64 estimateDiv128To64( bits64 a0, bits64 a1, bits64 b )
+{
+    bits64 b0, b1;
+    bits64 rem0, rem1, term0, term1;
+    bits64 z;
+
+    if ( b <= a0 ) return LIT64( 0xFFFFFFFFFFFFFFFF );
+    b0 = b>>32;
+    z = ( b0<<32 <= a0 ) ? LIT64( 0xFFFFFFFF00000000 ) : ( a0 / b0 )<<32;
+    mul64To128( b, z, &term0, &term1 );
+    sub128( a0, a1, term0, term1, &rem0, &rem1 );
+    while ( ( (sbits64) rem0 ) < 0 ) {
+        z -= LIT64( 0x100000000 );
+        b1 = b<<32;
+        add128( rem0, rem1, b0, b1, &rem0, &rem1 );
+    }
+    rem0 = ( rem0<<32 ) | ( rem1>>32 );
+    z |= ( b0<<32 <= rem0 ) ? 0xFFFFFFFF : rem0 / b0;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns an approximation to the square root of the 32-bit significand given
+| by `a'.  Considered as an integer, `a' must be at least 2^31.  If bit 0 of
+| `aExp' (the least significant bit) is 1, the integer returned approximates
+| 2^31*sqrt(`a'/2^31), where `a' is considered an integer.  If bit 0 of `aExp'
+| is 0, the integer returned approximates 2^31*sqrt(`a'/2^30).  In either
+| case, the approximation returned lies strictly within +/-2 of the exact
+| value.
+*----------------------------------------------------------------------------*/
+
+static bits32 estimateSqrt32( int16 aExp, bits32 a )
+{
+    static const bits16 sqrtOddAdjustments[] = {
+        0x0004, 0x0022, 0x005D, 0x00B1, 0x011D, 0x019F, 0x0236, 0x02E0,
+        0x039C, 0x0468, 0x0545, 0x0631, 0x072B, 0x0832, 0x0946, 0x0A67
+    };
+    static const bits16 sqrtEvenAdjustments[] = {
+        0x0A2D, 0x08AF, 0x075A, 0x0629, 0x051A, 0x0429, 0x0356, 0x029E,
+        0x0200, 0x0179, 0x0109, 0x00AF, 0x0068, 0x0034, 0x0012, 0x0002
+    };
+    int8 index;
+    bits32 z;
+
+    index = ( a>>27 ) & 15;
+    if ( aExp & 1 ) {
+        z = 0x4000 + ( a>>17 ) - sqrtOddAdjustments[ index ];
+        z = ( ( a / z )<<14 ) + ( z<<15 );
+        a >>= 1;
+    }
+    else {
+        z = 0x8000 + ( a>>17 ) - sqrtEvenAdjustments[ index ];
+        z = a / z + z;
+        z = ( 0x20000 <= z ) ? 0xFFFF8000 : ( z<<15 );
+        if ( z <= a ) return (bits32) ( ( (sbits32) a )>>1 );
+    }
+    return ( (bits32) ( ( ( (bits64) a )<<31 ) / z ) ) + ( z>>1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the number of leading 0 bits before the most-significant 1 bit of
+| `a'.  If `a' is zero, 32 is returned.
+*----------------------------------------------------------------------------*/
+
+static int8 countLeadingZeros32( bits32 a )
+{
+    static const int8 countLeadingZerosHigh[] = {
+        8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+        3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    int8 shiftCount;
+
+    shiftCount = 0;
+    if ( a < 0x10000 ) {
+        shiftCount += 16;
+        a <<= 16;
+    }
+    if ( a < 0x1000000 ) {
+        shiftCount += 8;
+        a <<= 8;
+    }
+    shiftCount += countLeadingZerosHigh[ a>>24 ];
+    return shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the number of leading 0 bits before the most-significant 1 bit of
+| `a'.  If `a' is zero, 64 is returned.
+*----------------------------------------------------------------------------*/
+
+static int8 countLeadingZeros64( bits64 a )
+{
+    int8 shiftCount;
+
+    shiftCount = 0;
+    if ( a < ( (bits64) 1 )<<32 ) {
+        shiftCount += 32;
+    }
+    else {
+        a >>= 32;
+    }
+    shiftCount += countLeadingZeros32( a );
+    return shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1'
+| is equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag eq128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 == b0 ) && ( a1 == b1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+| than or equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag le128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 <= b1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is less
+| than the 128-bit value formed by concatenating `b0' and `b1'.  Otherwise,
+| returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag lt128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 < b0 ) || ( ( a0 == b0 ) && ( a1 < b1 ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' is
+| not equal to the 128-bit value formed by concatenating `b0' and `b1'.
+| Otherwise, returns 0.
+*----------------------------------------------------------------------------*/
+
+INLINE flag ne128( bits64 a0, bits64 a1, bits64 b0, bits64 b1 )
+{
+
+    return ( a0 != b0 ) || ( a1 != b1 );
+
+}
+
diff --git a/fpu/softfloat-native.c b/fpu/softfloat-native.c
new file mode 100644
index 0000000..e58551f
--- /dev/null
+++ b/fpu/softfloat-native.c
@@ -0,0 +1,505 @@
+/* Native implementation of soft float functions. Only a single status
+   context is supported */
+#include "softfloat.h"
+#include <math.h>
+
+void set_float_rounding_mode(int val STATUS_PARAM)
+{
+    STATUS(float_rounding_mode) = val;
+#if defined(_BSD) && !defined(__APPLE__) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
+    fpsetround(val);
+#elif defined(__arm__)
+    /* nothing to do */
+#else
+    fesetround(val);
+#endif
+}
+
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM)
+{
+    STATUS(floatx80_rounding_precision) = val;
+}
+#endif
+
+#if defined(_BSD) || (defined(HOST_SOLARIS) && HOST_SOLARIS < 10)
+#define lrint(d)		((int32_t)rint(d))
+#define llrint(d)		((int64_t)rint(d))
+#define lrintf(f)		((int32_t)rint(f))
+#define llrintf(f)		((int64_t)rint(f))
+#define sqrtf(f)		((float)sqrt(f))
+#define remainderf(fa, fb)	((float)remainder(fa, fb))
+#define rintf(f)		((float)rint(f))
+#if !defined(__sparc__) && defined(HOST_SOLARIS) && HOST_SOLARIS < 10
+extern long double rintl(long double);
+extern long double scalbnl(long double, int);
+
+long long
+llrintl(long double x) {
+	return ((long long) rintl(x));
+}
+
+long
+lrintl(long double x) {
+	return ((long) rintl(x));
+}
+
+long double
+ldexpl(long double x, int n) {
+	return (scalbnl(x, n));
+}
+#endif
+#endif
+
+#if defined(__powerpc__)
+
+/* correct (but slow) PowerPC rint() (glibc version is incorrect) */
+double qemu_rint(double x)
+{
+    double y = 4503599627370496.0;
+    if (fabs(x) >= y)
+        return x;
+    if (x < 0)
+        y = -y;
+    y = (x + y) - y;
+    if (y == 0.0)
+        y = copysign(y, x);
+    return y;
+}
+
+#define rint qemu_rint
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32(int v STATUS_PARAM)
+{
+    return (float32)v;
+}
+
+float32 uint32_to_float32(unsigned int v STATUS_PARAM)
+{
+    return (float32)v;
+}
+
+float64 int32_to_float64(int v STATUS_PARAM)
+{
+    return (float64)v;
+}
+
+float64 uint32_to_float64(unsigned int v STATUS_PARAM)
+{
+    return (float64)v;
+}
+
+#ifdef FLOATX80
+floatx80 int32_to_floatx80(int v STATUS_PARAM)
+{
+    return (floatx80)v;
+}
+#endif
+float32 int64_to_float32( int64_t v STATUS_PARAM)
+{
+    return (float32)v;
+}
+float32 uint64_to_float32( uint64_t v STATUS_PARAM)
+{
+    return (float32)v;
+}
+float64 int64_to_float64( int64_t v STATUS_PARAM)
+{
+    return (float64)v;
+}
+float64 uint64_to_float64( uint64_t v STATUS_PARAM)
+{
+    return (float64)v;
+}
+#ifdef FLOATX80
+floatx80 int64_to_floatx80( int64_t v STATUS_PARAM)
+{
+    return (floatx80)v;
+}
+#endif
+
+/* XXX: this code implements the x86 behaviour, not the IEEE one.  */
+#if HOST_LONG_BITS == 32
+static inline int long_to_int32(long a)
+{
+    return a;
+}
+#else
+static inline int long_to_int32(long a)
+{
+    if (a != (int32_t)a)
+        a = 0x80000000;
+    return a;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int32( float32 a STATUS_PARAM)
+{
+    return long_to_int32(lrintf(a));
+}
+int float32_to_int32_round_to_zero( float32 a STATUS_PARAM)
+{
+    return (int)a;
+}
+int64_t float32_to_int64( float32 a STATUS_PARAM)
+{
+    return llrintf(a);
+}
+
+int64_t float32_to_int64_round_to_zero( float32 a STATUS_PARAM)
+{
+    return (int64_t)a;
+}
+
+float64 float32_to_float64( float32 a STATUS_PARAM)
+{
+    return a;
+}
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 a STATUS_PARAM)
+{
+    return a;
+}
+#endif
+
+unsigned int float32_to_uint32( float32 a STATUS_PARAM)
+{
+    int64_t v;
+    unsigned int res;
+
+    v = llrintf(a);
+    if (v < 0) {
+        res = 0;
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+    } else {
+        res = v;
+    }
+    return res;
+}
+unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM)
+{
+    int64_t v;
+    unsigned int res;
+
+    v = (int64_t)a;
+    if (v < 0) {
+        res = 0;
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+    } else {
+        res = v;
+    }
+    return res;
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision operations.
+*----------------------------------------------------------------------------*/
+float32 float32_round_to_int( float32 a STATUS_PARAM)
+{
+    return rintf(a);
+}
+
+float32 float32_rem( float32 a, float32 b STATUS_PARAM)
+{
+    return remainderf(a, b);
+}
+
+float32 float32_sqrt( float32 a STATUS_PARAM)
+{
+    return sqrtf(a);
+}
+int float32_compare( float32 a, float32 b STATUS_PARAM )
+{
+    if (a < b) {
+        return -1;
+    } else if (a == b) {
+        return 0;
+    } else if (a > b) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+int float32_compare_quiet( float32 a, float32 b STATUS_PARAM )
+{
+    if (isless(a, b)) {
+        return -1;
+    } else if (a == b) {
+        return 0;
+    } else if (isgreater(a, b)) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+int float32_is_signaling_nan( float32 a1)
+{
+    float32u u;
+    uint32_t a;
+    u.f = a1;
+    a = u.i;
+    return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int32( float64 a STATUS_PARAM)
+{
+    return long_to_int32(lrint(a));
+}
+int float64_to_int32_round_to_zero( float64 a STATUS_PARAM)
+{
+    return (int)a;
+}
+int64_t float64_to_int64( float64 a STATUS_PARAM)
+{
+    return llrint(a);
+}
+int64_t float64_to_int64_round_to_zero( float64 a STATUS_PARAM)
+{
+    return (int64_t)a;
+}
+float32 float64_to_float32( float64 a STATUS_PARAM)
+{
+    return a;
+}
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 a STATUS_PARAM)
+{
+    return a;
+}
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 a STATUS_PARAM)
+{
+    return a;
+}
+#endif
+
+unsigned int float64_to_uint32( float64 a STATUS_PARAM)
+{
+    int64_t v;
+    unsigned int res;
+
+    v = llrint(a);
+    if (v < 0) {
+        res = 0;
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+    } else {
+        res = v;
+    }
+    return res;
+}
+unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM)
+{
+    int64_t v;
+    unsigned int res;
+
+    v = (int64_t)a;
+    if (v < 0) {
+        res = 0;
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+    } else {
+        res = v;
+    }
+    return res;
+}
+uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
+{
+    int64_t v;
+
+    v = llrint(a + (float64)INT64_MIN);
+
+    return v - INT64_MIN;
+}
+uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
+{
+    int64_t v;
+
+    v = (int64_t)(a + (float64)INT64_MIN);
+
+    return v - INT64_MIN;
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision operations.
+*----------------------------------------------------------------------------*/
+#if defined(__sun__) && defined(HOST_SOLARIS) && HOST_SOLARIS < 10
+static inline float64 trunc(float64 x)
+{
+    return x < 0 ? -floor(-x) : floor(x);
+}
+#endif
+float64 float64_trunc_to_int( float64 a STATUS_PARAM )
+{
+    return trunc(a);
+}
+
+float64 float64_round_to_int( float64 a STATUS_PARAM )
+{
+#if defined(__arm__)
+    switch(STATUS(float_rounding_mode)) {
+    default:
+    case float_round_nearest_even:
+        asm("rndd %0, %1" : "=f" (a) : "f"(a));
+        break;
+    case float_round_down:
+        asm("rnddm %0, %1" : "=f" (a) : "f"(a));
+        break;
+    case float_round_up:
+        asm("rnddp %0, %1" : "=f" (a) : "f"(a));
+        break;
+    case float_round_to_zero:
+        asm("rnddz %0, %1" : "=f" (a) : "f"(a));
+        break;
+    }
+#else
+    return rint(a);
+#endif
+}
+
+float64 float64_rem( float64 a, float64 b STATUS_PARAM)
+{
+    return remainder(a, b);
+}
+
+float64 float64_sqrt( float64 a STATUS_PARAM)
+{
+    return sqrt(a);
+}
+int float64_compare( float64 a, float64 b STATUS_PARAM )
+{
+    if (a < b) {
+        return -1;
+    } else if (a == b) {
+        return 0;
+    } else if (a > b) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+int float64_compare_quiet( float64 a, float64 b STATUS_PARAM )
+{
+    if (isless(a, b)) {
+        return -1;
+    } else if (a == b) {
+        return 0;
+    } else if (isgreater(a, b)) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+int float64_is_signaling_nan( float64 a1)
+{
+    float64u u;
+    uint64_t a;
+    u.f = a1;
+    a = u.i;
+    return
+           ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+        && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+
+}
+
+int float64_is_nan( float64 a1 )
+{
+    float64u u;
+    uint64_t a;
+    u.f = a1;
+    a = u.i;
+
+    return ( LIT64( 0xFFE0000000000000 ) < (bits64) ( a<<1 ) );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int floatx80_to_int32( floatx80 a STATUS_PARAM)
+{
+    return long_to_int32(lrintl(a));
+}
+int floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM)
+{
+    return (int)a;
+}
+int64_t floatx80_to_int64( floatx80 a STATUS_PARAM)
+{
+    return llrintl(a);
+}
+int64_t floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM)
+{
+    return (int64_t)a;
+}
+float32 floatx80_to_float32( floatx80 a STATUS_PARAM)
+{
+    return a;
+}
+float64 floatx80_to_float64( floatx80 a STATUS_PARAM)
+{
+    return a;
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision operations.
+*----------------------------------------------------------------------------*/
+floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM)
+{
+    return rintl(a);
+}
+floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return remainderl(a, b);
+}
+floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM)
+{
+    return sqrtl(a);
+}
+int floatx80_compare( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    if (a < b) {
+        return -1;
+    } else if (a == b) {
+        return 0;
+    } else if (a > b) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+int floatx80_compare_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    if (isless(a, b)) {
+        return -1;
+    } else if (a == b) {
+        return 0;
+    } else if (isgreater(a, b)) {
+        return 1;
+    } else {
+        return 2;
+    }
+}
+int floatx80_is_signaling_nan( floatx80 a1)
+{
+    floatx80u u;
+    u.f = a1;
+    return ( ( u.i.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( u.i.low<<1 );
+}
+
+#endif
diff --git a/fpu/softfloat-native.h b/fpu/softfloat-native.h
new file mode 100644
index 0000000..379d49d
--- /dev/null
+++ b/fpu/softfloat-native.h
@@ -0,0 +1,425 @@
+/* Native implementation of soft float functions */
+#include <math.h>
+
+#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
+#include <ieeefp.h>
+#define fabsf(f) ((float)fabs(f))
+#else
+#include <fenv.h>
+#endif
+
+#ifdef __OpenBSD__
+/* Get OpenBSD version number */
+#include <sys/param.h>
+#endif
+
+/*
+ * Define some C99-7.12.3 classification macros and
+ *        some C99-.12.4 for Solaris systems OS less than 10,
+ *        or Solaris 10 systems running GCC 3.x or less.
+ *   Solaris 10 with GCC4 does not need these macros as they
+ *   are defined in <iso/math_c99.h> with a compiler directive
+ */
+#if defined(HOST_SOLARIS) && (( HOST_SOLARIS <= 9 ) || ((HOST_SOLARIS >= 10) \
+                                                        && (__GNUC__ <= 4))) \
+    || (defined(__OpenBSD__) && (OpenBSD < 200811))
+/*
+ * C99 7.12.3 classification macros
+ * and
+ * C99 7.12.14 comparison macros
+ *
+ * ... do not work on Solaris 10 using GNU CC 3.4.x.
+ * Try to workaround the missing / broken C99 math macros.
+ */
+#if defined(__OpenBSD__)
+#define unordered(x, y) (isnan(x) || isnan(y))
+#endif
+
+#define isnormal(x)             (fpclass(x) >= FP_NZERO)
+#define isgreater(x, y)         ((!unordered(x, y)) && ((x) > (y)))
+#define isgreaterequal(x, y)    ((!unordered(x, y)) && ((x) >= (y)))
+#define isless(x, y)            ((!unordered(x, y)) && ((x) < (y)))
+#define islessequal(x, y)       ((!unordered(x, y)) && ((x) <= (y)))
+#define isunordered(x,y)        unordered(x, y)
+#endif
+
+#if defined(__sun__) && !defined(NEED_LIBSUNMATH)
+
+#ifndef isnan
+# define isnan(x) \
+    (sizeof (x) == sizeof (long double) ? isnan_ld (x) \
+     : sizeof (x) == sizeof (double) ? isnan_d (x) \
+     : isnan_f (x))
+static inline int isnan_f  (float       x) { return x != x; }
+static inline int isnan_d  (double      x) { return x != x; }
+static inline int isnan_ld (long double x) { return x != x; }
+#endif
+
+#ifndef isinf
+# define isinf(x) \
+    (sizeof (x) == sizeof (long double) ? isinf_ld (x) \
+     : sizeof (x) == sizeof (double) ? isinf_d (x) \
+     : isinf_f (x))
+static inline int isinf_f  (float       x) { return isnan (x - x); }
+static inline int isinf_d  (double      x) { return isnan (x - x); }
+static inline int isinf_ld (long double x) { return isnan (x - x); }
+#endif
+#endif
+
+typedef float float32;
+typedef double float64;
+#ifdef FLOATX80
+typedef long double floatx80;
+#endif
+
+typedef union {
+    float32 f;
+    uint32_t i;
+} float32u;
+typedef union {
+    float64 f;
+    uint64_t i;
+} float64u;
+#ifdef FLOATX80
+typedef union {
+    floatx80 f;
+    struct {
+        uint64_t low;
+        uint16_t high;
+    } i;
+} floatx80u;
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point rounding mode.
+*----------------------------------------------------------------------------*/
+#if (defined(_BSD) && !defined(__APPLE__)) || defined(HOST_SOLARIS)
+#if defined(__OpenBSD__)
+#define FE_RM FP_RM
+#define FE_RP FP_RP
+#define FE_RZ FP_RZ
+#endif
+enum {
+    float_round_nearest_even = FP_RN,
+    float_round_down         = FP_RM,
+    float_round_up           = FP_RP,
+    float_round_to_zero      = FP_RZ
+};
+#elif defined(__arm__)
+enum {
+    float_round_nearest_even = 0,
+    float_round_down         = 1,
+    float_round_up           = 2,
+    float_round_to_zero      = 3
+};
+#else
+enum {
+    float_round_nearest_even = FE_TONEAREST,
+    float_round_down         = FE_DOWNWARD,
+    float_round_up           = FE_UPWARD,
+    float_round_to_zero      = FE_TOWARDZERO
+};
+#endif
+
+typedef struct float_status {
+    signed char float_rounding_mode;
+#ifdef FLOATX80
+    signed char floatx80_rounding_precision;
+#endif
+} float_status;
+
+void set_float_rounding_mode(int val STATUS_PARAM);
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32( int STATUS_PARAM);
+float32 uint32_to_float32( unsigned int STATUS_PARAM);
+float64 int32_to_float64( int STATUS_PARAM);
+float64 uint32_to_float64( unsigned int STATUS_PARAM);
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( int STATUS_PARAM);
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( int STATUS_PARAM);
+#endif
+float32 int64_to_float32( int64_t STATUS_PARAM);
+float32 uint64_to_float32( uint64_t STATUS_PARAM);
+float64 int64_to_float64( int64_t STATUS_PARAM);
+float64 uint64_to_float64( uint64_t v STATUS_PARAM);
+#ifdef FLOATX80
+floatx80 int64_to_floatx80( int64_t STATUS_PARAM);
+#endif
+#ifdef FLOAT128
+float128 int64_to_float128( int64_t STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int32( float32  STATUS_PARAM);
+int float32_to_int32_round_to_zero( float32  STATUS_PARAM);
+unsigned int float32_to_uint32( float32 a STATUS_PARAM);
+unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM);
+int64_t float32_to_int64( float32  STATUS_PARAM);
+int64_t float32_to_int64_round_to_zero( float32  STATUS_PARAM);
+float64 float32_to_float64( float32  STATUS_PARAM);
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32  STATUS_PARAM);
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32  STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision operations.
+*----------------------------------------------------------------------------*/
+float32 float32_round_to_int( float32  STATUS_PARAM);
+INLINE float32 float32_add( float32 a, float32 b STATUS_PARAM)
+{
+    return a + b;
+}
+INLINE float32 float32_sub( float32 a, float32 b STATUS_PARAM)
+{
+    return a - b;
+}
+INLINE float32 float32_mul( float32 a, float32 b STATUS_PARAM)
+{
+    return a * b;
+}
+INLINE float32 float32_div( float32 a, float32 b STATUS_PARAM)
+{
+    return a / b;
+}
+float32 float32_rem( float32, float32  STATUS_PARAM);
+float32 float32_sqrt( float32  STATUS_PARAM);
+INLINE int float32_eq( float32 a, float32 b STATUS_PARAM)
+{
+    return a == b;
+}
+INLINE int float32_le( float32 a, float32 b STATUS_PARAM)
+{
+    return a <= b;
+}
+INLINE int float32_lt( float32 a, float32 b STATUS_PARAM)
+{
+    return a < b;
+}
+INLINE int float32_eq_signaling( float32 a, float32 b STATUS_PARAM)
+{
+    return a <= b && a >= b;
+}
+INLINE int float32_le_quiet( float32 a, float32 b STATUS_PARAM)
+{
+    return islessequal(a, b);
+}
+INLINE int float32_lt_quiet( float32 a, float32 b STATUS_PARAM)
+{
+    return isless(a, b);
+}
+INLINE int float32_unordered( float32 a, float32 b STATUS_PARAM)
+{
+    return isunordered(a, b);
+
+}
+int float32_compare( float32, float32 STATUS_PARAM );
+int float32_compare_quiet( float32, float32 STATUS_PARAM );
+int float32_is_signaling_nan( float32 );
+
+INLINE float32 float32_abs(float32 a)
+{
+    return fabsf(a);
+}
+
+INLINE float32 float32_chs(float32 a)
+{
+    return -a;
+}
+
+INLINE float32 float32_scalbn(float32 a, int n)
+{
+    return scalbnf(a, n);
+}
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int32( float64 STATUS_PARAM );
+int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
+unsigned int float64_to_uint32( float64 STATUS_PARAM );
+unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM );
+int64_t float64_to_int64( float64 STATUS_PARAM );
+int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
+uint64_t float64_to_uint64( float64 STATUS_PARAM );
+uint64_t float64_to_uint64_round_to_zero( float64 STATUS_PARAM );
+float32 float64_to_float32( float64 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision operations.
+*----------------------------------------------------------------------------*/
+float64 float64_round_to_int( float64 STATUS_PARAM );
+float64 float64_trunc_to_int( float64 STATUS_PARAM );
+INLINE float64 float64_add( float64 a, float64 b STATUS_PARAM)
+{
+    return a + b;
+}
+INLINE float64 float64_sub( float64 a, float64 b STATUS_PARAM)
+{
+    return a - b;
+}
+INLINE float64 float64_mul( float64 a, float64 b STATUS_PARAM)
+{
+    return a * b;
+}
+INLINE float64 float64_div( float64 a, float64 b STATUS_PARAM)
+{
+    return a / b;
+}
+float64 float64_rem( float64, float64 STATUS_PARAM );
+float64 float64_sqrt( float64 STATUS_PARAM );
+INLINE int float64_eq( float64 a, float64 b STATUS_PARAM)
+{
+    return a == b;
+}
+INLINE int float64_le( float64 a, float64 b STATUS_PARAM)
+{
+    return a <= b;
+}
+INLINE int float64_lt( float64 a, float64 b STATUS_PARAM)
+{
+    return a < b;
+}
+INLINE int float64_eq_signaling( float64 a, float64 b STATUS_PARAM)
+{
+    return a <= b && a >= b;
+}
+INLINE int float64_le_quiet( float64 a, float64 b STATUS_PARAM)
+{
+    return islessequal(a, b);
+}
+INLINE int float64_lt_quiet( float64 a, float64 b STATUS_PARAM)
+{
+    return isless(a, b);
+
+}
+INLINE int float64_unordered( float64 a, float64 b STATUS_PARAM)
+{
+    return isunordered(a, b);
+
+}
+int float64_compare( float64, float64 STATUS_PARAM );
+int float64_compare_quiet( float64, float64 STATUS_PARAM );
+int float64_is_signaling_nan( float64 );
+int float64_is_nan( float64 );
+
+INLINE float64 float64_abs(float64 a)
+{
+    return fabs(a);
+}
+
+INLINE float64 float64_chs(float64 a)
+{
+    return -a;
+}
+
+INLINE float64 float64_scalbn(float64 a, int n)
+{
+    return scalbn(a, n);
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int floatx80_to_int32( floatx80 STATUS_PARAM );
+int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
+int64_t floatx80_to_int64( floatx80 STATUS_PARAM);
+int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM);
+float32 floatx80_to_float32( floatx80 STATUS_PARAM );
+float64 floatx80_to_float64( floatx80 STATUS_PARAM );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision operations.
+*----------------------------------------------------------------------------*/
+floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
+INLINE floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a + b;
+}
+INLINE floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a - b;
+}
+INLINE floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a * b;
+}
+INLINE floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a / b;
+}
+floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
+INLINE int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a == b;
+}
+INLINE int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a <= b;
+}
+INLINE int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a < b;
+}
+INLINE int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return a <= b && a >= b;
+}
+INLINE int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return islessequal(a, b);
+}
+INLINE int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return isless(a, b);
+
+}
+INLINE int floatx80_unordered( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    return isunordered(a, b);
+
+}
+int floatx80_compare( floatx80, floatx80 STATUS_PARAM );
+int floatx80_compare_quiet( floatx80, floatx80 STATUS_PARAM );
+int floatx80_is_signaling_nan( floatx80 );
+
+INLINE floatx80 floatx80_abs(floatx80 a)
+{
+    return fabsl(a);
+}
+
+INLINE floatx80 floatx80_chs(floatx80 a)
+{
+    return -a;
+}
+
+INLINE floatx80 floatx80_scalbn(floatx80 a, int n)
+{
+    return scalbnl(a, n);
+}
+
+#endif
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h
new file mode 100644
index 0000000..93fe06e
--- /dev/null
+++ b/fpu/softfloat-specialize.h
@@ -0,0 +1,569 @@
+
+/*============================================================================
+
+This C source fragment is part of the SoftFloat IEC/IEEE Floating-point
+Arithmetic Package, Release 2b.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#if defined(TARGET_MIPS) || defined(TARGET_HPPA)
+#define SNAN_BIT_IS_ONE		1
+#else
+#define SNAN_BIT_IS_ONE		0
+#endif
+
+/*----------------------------------------------------------------------------
+| Underflow tininess-detection mode, statically initialized to default value.
+| (The declaration in `softfloat.h' must match the `int8' type here.)
+*----------------------------------------------------------------------------*/
+int8 float_detect_tininess = float_tininess_after_rounding;
+
+/*----------------------------------------------------------------------------
+| Raises the exceptions specified by `flags'.  Floating-point traps can be
+| defined here if desired.  It is currently not possible for such a trap
+| to substitute a result value.  If traps are not implemented, this routine
+| should be simply `float_exception_flags |= flags;'.
+*----------------------------------------------------------------------------*/
+
+void float_raise( int8 flags STATUS_PARAM )
+{
+    STATUS(float_exception_flags) |= flags;
+}
+
+/*----------------------------------------------------------------------------
+| Internal canonical NaN format.
+*----------------------------------------------------------------------------*/
+typedef struct {
+    flag sign;
+    bits64 high, low;
+} commonNaNT;
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated single-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_SPARC)
+#define float32_default_nan make_float32(0x7FFFFFFF)
+#elif defined(TARGET_POWERPC)
+#define float32_default_nan make_float32(0x7FC00000)
+#elif defined(TARGET_HPPA)
+#define float32_default_nan make_float32(0x7FA00000)
+#elif SNAN_BIT_IS_ONE
+#define float32_default_nan make_float32(0x7FBFFFFF)
+#else
+#define float32_default_nan make_float32(0xFFC00000)
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is a quiet
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float32_is_nan( float32 a_ )
+{
+    uint32_t a = float32_val(a_);
+#if SNAN_BIT_IS_ONE
+    return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+#else
+    return ( 0xFF800000 <= (bits32) ( a<<1 ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is a signaling
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float32_is_signaling_nan( float32 a_ )
+{
+    uint32_t a = float32_val(a_);
+#if SNAN_BIT_IS_ONE
+    return ( 0xFF800000 <= (bits32) ( a<<1 ) );
+#else
+    return ( ( ( a>>22 ) & 0x1FF ) == 0x1FE ) && ( a & 0x003FFFFF );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point NaN
+| `a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM )
+{
+    commonNaNT z;
+
+    if ( float32_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR );
+    z.sign = float32_val(a)>>31;
+    z.low = 0;
+    z.high = ( (bits64) float32_val(a) )<<41;
+    return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the single-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float32 commonNaNToFloat32( commonNaNT a )
+{
+    bits32 mantissa = a.high>>41;
+    if ( mantissa )
+        return make_float32(
+            ( ( (bits32) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) );
+    else
+        return float32_default_nan;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two single-precision floating-point values `a' and `b', one of which
+| is a NaN, and returns the appropriate NaN result.  If either `a' or `b' is a
+| signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float32 propagateFloat32NaN( float32 a, float32 b STATUS_PARAM)
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+    bits32 av, bv, res;
+
+    aIsNaN = float32_is_nan( a );
+    aIsSignalingNaN = float32_is_signaling_nan( a );
+    bIsNaN = float32_is_nan( b );
+    bIsSignalingNaN = float32_is_signaling_nan( b );
+    av = float32_val(a);
+    bv = float32_val(b);
+#if SNAN_BIT_IS_ONE
+    av &= ~0x00400000;
+    bv &= ~0x00400000;
+#else
+    av |= 0x00400000;
+    bv |= 0x00400000;
+#endif
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+    if ( aIsSignalingNaN ) {
+        if ( bIsSignalingNaN ) goto returnLargerSignificand;
+        res = bIsNaN ? bv : av;
+    }
+    else if ( aIsNaN ) {
+        if ( bIsSignalingNaN | ! bIsNaN )
+            res = av;
+        else {
+ returnLargerSignificand:
+            if ( (bits32) ( av<<1 ) < (bits32) ( bv<<1 ) )
+                res = bv;
+            else if ( (bits32) ( bv<<1 ) < (bits32) ( av<<1 ) )
+                res = av;
+            else
+                res = ( av < bv ) ? av : bv;
+        }
+    }
+    else {
+        res = bv;
+    }
+    return make_float32(res);
+}
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated double-precision NaN.
+*----------------------------------------------------------------------------*/
+#if defined(TARGET_SPARC)
+#define float64_default_nan make_float64(LIT64( 0x7FFFFFFFFFFFFFFF ))
+#elif defined(TARGET_POWERPC)
+#define float64_default_nan make_float64(LIT64( 0x7FF8000000000000 ))
+#elif defined(TARGET_HPPA)
+#define float64_default_nan make_float64(LIT64( 0x7FF4000000000000 ))
+#elif SNAN_BIT_IS_ONE
+#define float64_default_nan make_float64(LIT64( 0x7FF7FFFFFFFFFFFF ))
+#else
+#define float64_default_nan make_float64(LIT64( 0xFFF8000000000000 ))
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is a quiet
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float64_is_nan( float64 a_ )
+{
+    bits64 a = float64_val(a_);
+#if SNAN_BIT_IS_ONE
+    return
+           ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+        && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+#else
+    return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is a signaling
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float64_is_signaling_nan( float64 a_ )
+{
+    bits64 a = float64_val(a_);
+#if SNAN_BIT_IS_ONE
+    return ( LIT64( 0xFFF0000000000000 ) <= (bits64) ( a<<1 ) );
+#else
+    return
+           ( ( ( a>>51 ) & 0xFFF ) == 0xFFE )
+        && ( a & LIT64( 0x0007FFFFFFFFFFFF ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point NaN
+| `a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM)
+{
+    commonNaNT z;
+
+    if ( float64_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+    z.sign = float64_val(a)>>63;
+    z.low = 0;
+    z.high = float64_val(a)<<12;
+    return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the double-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float64 commonNaNToFloat64( commonNaNT a )
+{
+    bits64 mantissa = a.high>>12;
+
+    if ( mantissa )
+        return make_float64(
+              ( ( (bits64) a.sign )<<63 )
+            | LIT64( 0x7FF0000000000000 )
+            | ( a.high>>12 ));
+    else
+        return float64_default_nan;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two double-precision floating-point values `a' and `b', one of which
+| is a NaN, and returns the appropriate NaN result.  If either `a' or `b' is a
+| signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float64 propagateFloat64NaN( float64 a, float64 b STATUS_PARAM)
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+    bits64 av, bv, res;
+
+    aIsNaN = float64_is_nan( a );
+    aIsSignalingNaN = float64_is_signaling_nan( a );
+    bIsNaN = float64_is_nan( b );
+    bIsSignalingNaN = float64_is_signaling_nan( b );
+    av = float64_val(a);
+    bv = float64_val(b);
+#if SNAN_BIT_IS_ONE
+    av &= ~LIT64( 0x0008000000000000 );
+    bv &= ~LIT64( 0x0008000000000000 );
+#else
+    av |= LIT64( 0x0008000000000000 );
+    bv |= LIT64( 0x0008000000000000 );
+#endif
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+    if ( aIsSignalingNaN ) {
+        if ( bIsSignalingNaN ) goto returnLargerSignificand;
+        res = bIsNaN ? bv : av;
+    }
+    else if ( aIsNaN ) {
+        if ( bIsSignalingNaN | ! bIsNaN )
+            res = av;
+        else {
+ returnLargerSignificand:
+            if ( (bits64) ( av<<1 ) < (bits64) ( bv<<1 ) )
+                res = bv;
+            else if ( (bits64) ( bv<<1 ) < (bits64) ( av<<1 ) )
+                res = av;
+            else
+                res = ( av < bv ) ? av : bv;
+        }
+    }
+    else {
+        res = bv;
+    }
+    return make_float64(res);
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated extended double-precision NaN.  The
+| `high' and `low' values hold the most- and least-significant bits,
+| respectively.
+*----------------------------------------------------------------------------*/
+#if SNAN_BIT_IS_ONE
+#define floatx80_default_nan_high 0x7FFF
+#define floatx80_default_nan_low  LIT64( 0xBFFFFFFFFFFFFFFF )
+#else
+#define floatx80_default_nan_high 0xFFFF
+#define floatx80_default_nan_low  LIT64( 0xC000000000000000 )
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| quiet NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int floatx80_is_nan( floatx80 a )
+{
+#if SNAN_BIT_IS_ONE
+    bits64 aLow;
+
+    aLow = a.low & ~ LIT64( 0x4000000000000000 );
+    return
+           ( ( a.high & 0x7FFF ) == 0x7FFF )
+        && (bits64) ( aLow<<1 )
+        && ( a.low == aLow );
+#else
+    return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int floatx80_is_signaling_nan( floatx80 a )
+{
+#if SNAN_BIT_IS_ONE
+    return ( ( a.high & 0x7FFF ) == 0x7FFF ) && (bits64) ( a.low<<1 );
+#else
+    bits64 aLow;
+
+    aLow = a.low & ~ LIT64( 0x4000000000000000 );
+    return
+           ( ( a.high & 0x7FFF ) == 0x7FFF )
+        && (bits64) ( aLow<<1 )
+        && ( a.low == aLow );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point NaN `a' to the canonical NaN format.  If `a' is a signaling NaN, the
+| invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM)
+{
+    commonNaNT z;
+
+    if ( floatx80_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+    z.sign = a.high>>15;
+    z.low = 0;
+    z.high = a.low;
+    return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the extended
+| double-precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static floatx80 commonNaNToFloatx80( commonNaNT a )
+{
+    floatx80 z;
+
+    if (a.high)
+        z.low = a.high;
+    else
+        z.low = floatx80_default_nan_low;
+    z.high = ( ( (bits16) a.sign )<<15 ) | 0x7FFF;
+    return z;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two extended double-precision floating-point values `a' and `b', one
+| of which is a NaN, and returns the appropriate NaN result.  If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b STATUS_PARAM)
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = floatx80_is_nan( a );
+    aIsSignalingNaN = floatx80_is_signaling_nan( a );
+    bIsNaN = floatx80_is_nan( b );
+    bIsSignalingNaN = floatx80_is_signaling_nan( b );
+#if SNAN_BIT_IS_ONE
+    a.low &= ~LIT64( 0xC000000000000000 );
+    b.low &= ~LIT64( 0xC000000000000000 );
+#else
+    a.low |= LIT64( 0xC000000000000000 );
+    b.low |= LIT64( 0xC000000000000000 );
+#endif
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+    if ( aIsSignalingNaN ) {
+        if ( bIsSignalingNaN ) goto returnLargerSignificand;
+        return bIsNaN ? b : a;
+    }
+    else if ( aIsNaN ) {
+        if ( bIsSignalingNaN | ! bIsNaN ) return a;
+ returnLargerSignificand:
+        if ( a.low < b.low ) return b;
+        if ( b.low < a.low ) return a;
+        return ( a.high < b.high ) ? a : b;
+    }
+    else {
+        return b;
+    }
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| The pattern for a default generated quadruple-precision NaN.  The `high' and
+| `low' values hold the most- and least-significant bits, respectively.
+*----------------------------------------------------------------------------*/
+#if SNAN_BIT_IS_ONE
+#define float128_default_nan_high LIT64( 0x7FFF7FFFFFFFFFFF )
+#define float128_default_nan_low  LIT64( 0xFFFFFFFFFFFFFFFF )
+#else
+#define float128_default_nan_high LIT64( 0xFFFF800000000000 )
+#define float128_default_nan_low  LIT64( 0x0000000000000000 )
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is a quiet
+| NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float128_is_nan( float128 a )
+{
+#if SNAN_BIT_IS_ONE
+    return
+           ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+        && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+#else
+    return
+           ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+        && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is a
+| signaling NaN; otherwise returns 0.
+*----------------------------------------------------------------------------*/
+
+int float128_is_signaling_nan( float128 a )
+{
+#if SNAN_BIT_IS_ONE
+    return
+           ( LIT64( 0xFFFE000000000000 ) <= (bits64) ( a.high<<1 ) )
+        && ( a.low || ( a.high & LIT64( 0x0000FFFFFFFFFFFF ) ) );
+#else
+    return
+           ( ( ( a.high>>47 ) & 0xFFFF ) == 0xFFFE )
+        && ( a.low || ( a.high & LIT64( 0x00007FFFFFFFFFFF ) ) );
+#endif
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point NaN
+| `a' to the canonical NaN format.  If `a' is a signaling NaN, the invalid
+| exception is raised.
+*----------------------------------------------------------------------------*/
+
+static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM)
+{
+    commonNaNT z;
+
+    if ( float128_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR);
+    z.sign = a.high>>63;
+    shortShift128Left( a.high, a.low, 16, &z.high, &z.low );
+    return z;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the canonical NaN `a' to the quadruple-
+| precision floating-point format.
+*----------------------------------------------------------------------------*/
+
+static float128 commonNaNToFloat128( commonNaNT a )
+{
+    float128 z;
+
+    shift128Right( a.high, a.low, 16, &z.high, &z.low );
+    z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 );
+    return z;
+}
+
+/*----------------------------------------------------------------------------
+| Takes two quadruple-precision floating-point values `a' and `b', one of
+| which is a NaN, and returns the appropriate NaN result.  If either `a' or
+| `b' is a signaling NaN, the invalid exception is raised.
+*----------------------------------------------------------------------------*/
+
+static float128 propagateFloat128NaN( float128 a, float128 b STATUS_PARAM)
+{
+    flag aIsNaN, aIsSignalingNaN, bIsNaN, bIsSignalingNaN;
+
+    aIsNaN = float128_is_nan( a );
+    aIsSignalingNaN = float128_is_signaling_nan( a );
+    bIsNaN = float128_is_nan( b );
+    bIsSignalingNaN = float128_is_signaling_nan( b );
+#if SNAN_BIT_IS_ONE
+    a.high &= ~LIT64( 0x0000800000000000 );
+    b.high &= ~LIT64( 0x0000800000000000 );
+#else
+    a.high |= LIT64( 0x0000800000000000 );
+    b.high |= LIT64( 0x0000800000000000 );
+#endif
+    if ( aIsSignalingNaN | bIsSignalingNaN ) float_raise( float_flag_invalid STATUS_VAR);
+    if ( aIsSignalingNaN ) {
+        if ( bIsSignalingNaN ) goto returnLargerSignificand;
+        return bIsNaN ? b : a;
+    }
+    else if ( aIsNaN ) {
+        if ( bIsSignalingNaN | ! bIsNaN ) return a;
+ returnLargerSignificand:
+        if ( lt128( a.high<<1, a.low, b.high<<1, b.low ) ) return b;
+        if ( lt128( b.high<<1, b.low, a.high<<1, a.low ) ) return a;
+        return ( a.high < b.high ) ? a : b;
+    }
+    else {
+        return b;
+    }
+}
+
+#endif
diff --git a/fpu/softfloat.c b/fpu/softfloat.c
new file mode 100644
index 0000000..3ec1e0d
--- /dev/null
+++ b/fpu/softfloat.c
@@ -0,0 +1,5541 @@
+
+/*============================================================================
+
+This C source file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
+Package, Release 2b.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#include "softfloat.h"
+
+/*----------------------------------------------------------------------------
+| Primitive arithmetic functions, including multi-word arithmetic, and
+| division and square root approximations.  (Can be specialized to target if
+| desired.)
+*----------------------------------------------------------------------------*/
+#include "softfloat-macros.h"
+
+/*----------------------------------------------------------------------------
+| Functions and definitions to determine:  (1) whether tininess for underflow
+| is detected before or after rounding by default, (2) what (if anything)
+| happens when exceptions are raised, (3) how signaling NaNs are distinguished
+| from quiet NaNs, (4) the default generated quiet NaNs, and (5) how NaNs
+| are propagated from function inputs to output.  These details are target-
+| specific.
+*----------------------------------------------------------------------------*/
+#include "softfloat-specialize.h"
+
+void set_float_rounding_mode(int val STATUS_PARAM)
+{
+    STATUS(float_rounding_mode) = val;
+}
+
+void set_float_exception_flags(int val STATUS_PARAM)
+{
+    STATUS(float_exception_flags) = val;
+}
+
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM)
+{
+    STATUS(floatx80_rounding_precision) = val;
+}
+#endif
+
+/*----------------------------------------------------------------------------
+| Takes a 64-bit fixed-point value `absZ' with binary point between bits 6
+| and 7, and returns the properly rounded 32-bit integer corresponding to the
+| input.  If `zSign' is 1, the input is negated before being converted to an
+| integer.  Bit 63 of `absZ' must be zero.  Ordinarily, the fixed-point input
+| is simply rounded to an integer, with the inexact exception raised if the
+| input cannot be represented exactly as an integer.  However, if the fixed-
+| point input is too large, the invalid exception is raised and the largest
+| positive or negative integer is returned.
+*----------------------------------------------------------------------------*/
+
+static int32 roundAndPackInt32( flag zSign, bits64 absZ STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int8 roundIncrement, roundBits;
+    int32 z;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x40;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x7F;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = absZ & 0x7F;
+    absZ = ( absZ + roundIncrement )>>7;
+    absZ &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+    z = absZ;
+    if ( zSign ) z = - z;
+    if ( ( absZ>>32 ) || ( z && ( ( z < 0 ) ^ zSign ) ) ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return zSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
+| `absZ1', with binary point between bits 63 and 64 (between the input words),
+| and returns the properly rounded 64-bit integer corresponding to the input.
+| If `zSign' is 1, the input is negated before being converted to an integer.
+| Ordinarily, the fixed-point input is simply rounded to an integer, with
+| the inexact exception raised if the input cannot be represented exactly as
+| an integer.  However, if the fixed-point input is too large, the invalid
+| exception is raised and the largest positive or negative integer is
+| returned.
+*----------------------------------------------------------------------------*/
+
+static int64 roundAndPackInt64( flag zSign, bits64 absZ0, bits64 absZ1 STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment;
+    int64 z;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    increment = ( (sbits64) absZ1 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && absZ1;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && absZ1;
+            }
+        }
+    }
+    if ( increment ) {
+        ++absZ0;
+        if ( absZ0 == 0 ) goto overflow;
+        absZ0 &= ~ ( ( (bits64) ( absZ1<<1 ) == 0 ) & roundNearestEven );
+    }
+    z = absZ0;
+    if ( zSign ) z = - z;
+    if ( z && ( ( z < 0 ) ^ zSign ) ) {
+ overflow:
+        float_raise( float_flag_invalid STATUS_VAR);
+        return
+              zSign ? (sbits64) LIT64( 0x8000000000000000 )
+            : LIT64( 0x7FFFFFFFFFFFFFFF );
+    }
+    if ( absZ1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits32 extractFloat32Frac( float32 a )
+{
+
+    return float32_val(a) & 0x007FFFFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int16 extractFloat32Exp( float32 a )
+{
+
+    return ( float32_val(a)>>23 ) & 0xFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the single-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat32Sign( float32 a )
+{
+
+    return float32_val(a)>>31;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal single-precision floating-point value represented
+| by the denormalized significand `aSig'.  The normalized exponent and
+| significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat32Subnormal( bits32 aSig, int16 *zExpPtr, bits32 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros32( aSig ) - 8;
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| single-precision floating-point value, returning the result.  After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result.  This means that any integer portion of `zSig'
+| will be added into the exponent.  Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float32 packFloat32( flag zSign, int16 zExp, bits32 zSig )
+{
+
+    return make_float32(
+          ( ( (bits32) zSign )<<31 ) + ( ( (bits32) zExp )<<23 ) + zSig);
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper single-precision floating-
+| point value corresponding to the abstract input.  Ordinarily, the abstract
+| value is simply rounded and packed into the single-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly.  However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned.  If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal single-
+| precision floating-point number.
+|     The input significand `zSig' has its binary point between bits 30
+| and 29, which is 7 bits to the left of the usual location.  This shifted
+| significand must be normalized or smaller.  If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding.  In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 roundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int8 roundIncrement, roundBits;
+    flag isTiny;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x40;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x7F;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig & 0x7F;
+    if ( 0xFD <= (bits16) zExp ) {
+        if (    ( 0xFD < zExp )
+             || (    ( zExp == 0xFD )
+                  && ( (sbits32) ( zSig + roundIncrement ) < 0 ) )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+            return packFloat32( zSign, 0xFF, - ( roundIncrement == 0 ));
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ( zSig + roundIncrement < 0x80000000 );
+            shift32RightJamming( zSig, - zExp, &zSig );
+            zExp = 0;
+            roundBits = zSig & 0x7F;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+        }
+    }
+    if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+    zSig = ( zSig + roundIncrement )>>7;
+    zSig &= ~ ( ( ( roundBits ^ 0x40 ) == 0 ) & roundNearestEven );
+    if ( zSig == 0 ) zExp = 0;
+    return packFloat32( zSign, zExp, zSig );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper single-precision floating-
+| point value corresponding to the abstract input.  This routine is just like
+| `roundAndPackFloat32' except that `zSig' does not have to be normalized.
+| Bit 31 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
+| floating-point exponent.
+*----------------------------------------------------------------------------*/
+
+static float32
+ normalizeRoundAndPackFloat32( flag zSign, int16 zExp, bits32 zSig STATUS_PARAM)
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros32( zSig ) - 1;
+    return roundAndPackFloat32( zSign, zExp - shiftCount, zSig<<shiftCount STATUS_VAR);
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat64Frac( float64 a )
+{
+
+    return float64_val(a) & LIT64( 0x000FFFFFFFFFFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int16 extractFloat64Exp( float64 a )
+{
+
+    return ( float64_val(a)>>52 ) & 0x7FF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the double-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat64Sign( float64 a )
+{
+
+    return float64_val(a)>>63;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal double-precision floating-point value represented
+| by the denormalized significand `aSig'.  The normalized exponent and
+| significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat64Subnormal( bits64 aSig, int16 *zExpPtr, bits64 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( aSig ) - 11;
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into a
+| double-precision floating-point value, returning the result.  After being
+| shifted into the proper positions, the three fields are simply added
+| together to form the result.  This means that any integer portion of `zSig'
+| will be added into the exponent.  Since a properly normalized significand
+| will have an integer portion equal to 1, the `zExp' input should be 1 less
+| than the desired result exponent whenever `zSig' is a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float64 packFloat64( flag zSign, int16 zExp, bits64 zSig )
+{
+
+    return make_float64(
+        ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<52 ) + zSig);
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper double-precision floating-
+| point value corresponding to the abstract input.  Ordinarily, the abstract
+| value is simply rounded and packed into the double-precision format, with
+| the inexact exception raised if the abstract input cannot be represented
+| exactly.  However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned.  If the abstract value is too small, the input value is rounded
+| to a subnormal number, and the underflow and inexact exceptions are raised
+| if the abstract input cannot be represented exactly as a subnormal double-
+| precision floating-point number.
+|     The input significand `zSig' has its binary point between bits 62
+| and 61, which is 10 bits to the left of the usual location.  This shifted
+| significand must be normalized or smaller.  If `zSig' is not normalized,
+| `zExp' must be 0; in that case, the result returned is a subnormal number,
+| and it must not require rounding.  In the usual case that `zSig' is
+| normalized, `zExp' must be 1 less than the ``true'' floating-point exponent.
+| The handling of underflow and overflow follows the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 roundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven;
+    int16 roundIncrement, roundBits;
+    flag isTiny;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    roundIncrement = 0x200;
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = 0x3FF;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig & 0x3FF;
+    if ( 0x7FD <= (bits16) zExp ) {
+        if (    ( 0x7FD < zExp )
+             || (    ( zExp == 0x7FD )
+                  && ( (sbits64) ( zSig + roundIncrement ) < 0 ) )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+            return packFloat64( zSign, 0x7FF, - ( roundIncrement == 0 ));
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ( zSig + roundIncrement < LIT64( 0x8000000000000000 ) );
+            shift64RightJamming( zSig, - zExp, &zSig );
+            zExp = 0;
+            roundBits = zSig & 0x3FF;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+        }
+    }
+    if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+    zSig = ( zSig + roundIncrement )>>10;
+    zSig &= ~ ( ( ( roundBits ^ 0x200 ) == 0 ) & roundNearestEven );
+    if ( zSig == 0 ) zExp = 0;
+    return packFloat64( zSign, zExp, zSig );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand `zSig', and returns the proper double-precision floating-
+| point value corresponding to the abstract input.  This routine is just like
+| `roundAndPackFloat64' except that `zSig' does not have to be normalized.
+| Bit 63 of `zSig' must be zero, and `zExp' must be 1 less than the ``true''
+| floating-point exponent.
+*----------------------------------------------------------------------------*/
+
+static float64
+ normalizeRoundAndPackFloat64( flag zSign, int16 zExp, bits64 zSig STATUS_PARAM)
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( zSig ) - 1;
+    return roundAndPackFloat64( zSign, zExp - shiftCount, zSig<<shiftCount STATUS_VAR);
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the fraction bits of the extended double-precision floating-point
+| value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloatx80Frac( floatx80 a )
+{
+
+    return a.low;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the extended double-precision floating-point
+| value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int32 extractFloatx80Exp( floatx80 a )
+{
+
+    return a.high & 0x7FFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the extended double-precision floating-point value
+| `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloatx80Sign( floatx80 a )
+{
+
+    return a.high>>15;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal extended double-precision floating-point value
+| represented by the denormalized significand `aSig'.  The normalized exponent
+| and significand are stored at the locations pointed to by `zExpPtr' and
+| `zSigPtr', respectively.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloatx80Subnormal( bits64 aSig, int32 *zExpPtr, bits64 *zSigPtr )
+{
+    int8 shiftCount;
+
+    shiftCount = countLeadingZeros64( aSig );
+    *zSigPtr = aSig<<shiftCount;
+    *zExpPtr = 1 - shiftCount;
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', exponent `zExp', and significand `zSig' into an
+| extended double-precision floating-point value, returning the result.
+*----------------------------------------------------------------------------*/
+
+INLINE floatx80 packFloatx80( flag zSign, int32 zExp, bits64 zSig )
+{
+    floatx80 z;
+
+    z.low = zSig;
+    z.high = ( ( (bits16) zSign )<<15 ) + zExp;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and extended significand formed by the concatenation of `zSig0' and `zSig1',
+| and returns the proper extended double-precision floating-point value
+| corresponding to the abstract input.  Ordinarily, the abstract value is
+| rounded and packed into the extended double-precision format, with the
+| inexact exception raised if the abstract input cannot be represented
+| exactly.  However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned.  If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal extended
+| double-precision floating-point number.
+|     If `roundingPrecision' is 32 or 64, the result is rounded to the same
+| number of bits as single or double precision, respectively.  Otherwise, the
+| result is rounded to the full precision of the extended double-precision
+| format.
+|     The input significand must be normalized or smaller.  If the input
+| significand is not normalized, `zExp' must be 0; in that case, the result
+| returned is a subnormal number, and it must not require rounding.  The
+| handling of underflow and overflow follows the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80
+ roundAndPackFloatx80(
+     int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment, isTiny;
+    int64 roundIncrement, roundMask, roundBits;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    if ( roundingPrecision == 80 ) goto precision80;
+    if ( roundingPrecision == 64 ) {
+        roundIncrement = LIT64( 0x0000000000000400 );
+        roundMask = LIT64( 0x00000000000007FF );
+    }
+    else if ( roundingPrecision == 32 ) {
+        roundIncrement = LIT64( 0x0000008000000000 );
+        roundMask = LIT64( 0x000000FFFFFFFFFF );
+    }
+    else {
+        goto precision80;
+    }
+    zSig0 |= ( zSig1 != 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            roundIncrement = 0;
+        }
+        else {
+            roundIncrement = roundMask;
+            if ( zSign ) {
+                if ( roundingMode == float_round_up ) roundIncrement = 0;
+            }
+            else {
+                if ( roundingMode == float_round_down ) roundIncrement = 0;
+            }
+        }
+    }
+    roundBits = zSig0 & roundMask;
+    if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+        if (    ( 0x7FFE < zExp )
+             || ( ( zExp == 0x7FFE ) && ( zSig0 + roundIncrement < zSig0 ) )
+           ) {
+            goto overflow;
+        }
+        if ( zExp <= 0 ) {
+            isTiny =
+                   ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+                || ( zExp < 0 )
+                || ( zSig0 <= zSig0 + roundIncrement );
+            shift64RightJamming( zSig0, 1 - zExp, &zSig0 );
+            zExp = 0;
+            roundBits = zSig0 & roundMask;
+            if ( isTiny && roundBits ) float_raise( float_flag_underflow STATUS_VAR);
+            if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+            zSig0 += roundIncrement;
+            if ( (sbits64) zSig0 < 0 ) zExp = 1;
+            roundIncrement = roundMask + 1;
+            if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+                roundMask |= roundIncrement;
+            }
+            zSig0 &= ~ roundMask;
+            return packFloatx80( zSign, zExp, zSig0 );
+        }
+    }
+    if ( roundBits ) STATUS(float_exception_flags) |= float_flag_inexact;
+    zSig0 += roundIncrement;
+    if ( zSig0 < roundIncrement ) {
+        ++zExp;
+        zSig0 = LIT64( 0x8000000000000000 );
+    }
+    roundIncrement = roundMask + 1;
+    if ( roundNearestEven && ( roundBits<<1 == roundIncrement ) ) {
+        roundMask |= roundIncrement;
+    }
+    zSig0 &= ~ roundMask;
+    if ( zSig0 == 0 ) zExp = 0;
+    return packFloatx80( zSign, zExp, zSig0 );
+ precision80:
+    increment = ( (sbits64) zSig1 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && zSig1;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && zSig1;
+            }
+        }
+    }
+    if ( 0x7FFD <= (bits32) ( zExp - 1 ) ) {
+        if (    ( 0x7FFE < zExp )
+             || (    ( zExp == 0x7FFE )
+                  && ( zSig0 == LIT64( 0xFFFFFFFFFFFFFFFF ) )
+                  && increment
+                )
+           ) {
+            roundMask = 0;
+ overflow:
+            float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+            if (    ( roundingMode == float_round_to_zero )
+                 || ( zSign && ( roundingMode == float_round_up ) )
+                 || ( ! zSign && ( roundingMode == float_round_down ) )
+               ) {
+                return packFloatx80( zSign, 0x7FFE, ~ roundMask );
+            }
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        if ( zExp <= 0 ) {
+            isTiny =
+                   ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+                || ( zExp < 0 )
+                || ! increment
+                || ( zSig0 < LIT64( 0xFFFFFFFFFFFFFFFF ) );
+            shift64ExtraRightJamming( zSig0, zSig1, 1 - zExp, &zSig0, &zSig1 );
+            zExp = 0;
+            if ( isTiny && zSig1 ) float_raise( float_flag_underflow STATUS_VAR);
+            if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+            if ( roundNearestEven ) {
+                increment = ( (sbits64) zSig1 < 0 );
+            }
+            else {
+                if ( zSign ) {
+                    increment = ( roundingMode == float_round_down ) && zSig1;
+                }
+                else {
+                    increment = ( roundingMode == float_round_up ) && zSig1;
+                }
+            }
+            if ( increment ) {
+                ++zSig0;
+                zSig0 &=
+                    ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven );
+                if ( (sbits64) zSig0 < 0 ) zExp = 1;
+            }
+            return packFloatx80( zSign, zExp, zSig0 );
+        }
+    }
+    if ( zSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+    if ( increment ) {
+        ++zSig0;
+        if ( zSig0 == 0 ) {
+            ++zExp;
+            zSig0 = LIT64( 0x8000000000000000 );
+        }
+        else {
+            zSig0 &= ~ ( ( (bits64) ( zSig1<<1 ) == 0 ) & roundNearestEven );
+        }
+    }
+    else {
+        if ( zSig0 == 0 ) zExp = 0;
+    }
+    return packFloatx80( zSign, zExp, zSig0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent
+| `zExp', and significand formed by the concatenation of `zSig0' and `zSig1',
+| and returns the proper extended double-precision floating-point value
+| corresponding to the abstract input.  This routine is just like
+| `roundAndPackFloatx80' except that the input significand does not have to be
+| normalized.
+*----------------------------------------------------------------------------*/
+
+static floatx80
+ normalizeRoundAndPackFloatx80(
+     int8 roundingPrecision, flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1
+ STATUS_PARAM)
+{
+    int8 shiftCount;
+
+    if ( zSig0 == 0 ) {
+        zSig0 = zSig1;
+        zSig1 = 0;
+        zExp -= 64;
+    }
+    shiftCount = countLeadingZeros64( zSig0 );
+    shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    zExp -= shiftCount;
+    return
+        roundAndPackFloatx80( roundingPrecision, zSign, zExp, zSig0, zSig1 STATUS_VAR);
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the least-significant 64 fraction bits of the quadruple-precision
+| floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat128Frac1( float128 a )
+{
+
+    return a.low;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the most-significant 48 fraction bits of the quadruple-precision
+| floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE bits64 extractFloat128Frac0( float128 a )
+{
+
+    return a.high & LIT64( 0x0000FFFFFFFFFFFF );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the exponent bits of the quadruple-precision floating-point value
+| `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE int32 extractFloat128Exp( float128 a )
+{
+
+    return ( a.high>>48 ) & 0x7FFF;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the sign bit of the quadruple-precision floating-point value `a'.
+*----------------------------------------------------------------------------*/
+
+INLINE flag extractFloat128Sign( float128 a )
+{
+
+    return a.high>>63;
+
+}
+
+/*----------------------------------------------------------------------------
+| Normalizes the subnormal quadruple-precision floating-point value
+| represented by the denormalized significand formed by the concatenation of
+| `aSig0' and `aSig1'.  The normalized exponent is stored at the location
+| pointed to by `zExpPtr'.  The most significant 49 bits of the normalized
+| significand are stored at the location pointed to by `zSig0Ptr', and the
+| least significant 64 bits of the normalized significand are stored at the
+| location pointed to by `zSig1Ptr'.
+*----------------------------------------------------------------------------*/
+
+static void
+ normalizeFloat128Subnormal(
+     bits64 aSig0,
+     bits64 aSig1,
+     int32 *zExpPtr,
+     bits64 *zSig0Ptr,
+     bits64 *zSig1Ptr
+ )
+{
+    int8 shiftCount;
+
+    if ( aSig0 == 0 ) {
+        shiftCount = countLeadingZeros64( aSig1 ) - 15;
+        if ( shiftCount < 0 ) {
+            *zSig0Ptr = aSig1>>( - shiftCount );
+            *zSig1Ptr = aSig1<<( shiftCount & 63 );
+        }
+        else {
+            *zSig0Ptr = aSig1<<shiftCount;
+            *zSig1Ptr = 0;
+        }
+        *zExpPtr = - shiftCount - 63;
+    }
+    else {
+        shiftCount = countLeadingZeros64( aSig0 ) - 15;
+        shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr );
+        *zExpPtr = 1 - shiftCount;
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Packs the sign `zSign', the exponent `zExp', and the significand formed
+| by the concatenation of `zSig0' and `zSig1' into a quadruple-precision
+| floating-point value, returning the result.  After being shifted into the
+| proper positions, the three fields `zSign', `zExp', and `zSig0' are simply
+| added together to form the most significant 32 bits of the result.  This
+| means that any integer portion of `zSig0' will be added into the exponent.
+| Since a properly normalized significand will have an integer portion equal
+| to 1, the `zExp' input should be 1 less than the desired result exponent
+| whenever `zSig0' and `zSig1' concatenated form a complete, normalized
+| significand.
+*----------------------------------------------------------------------------*/
+
+INLINE float128
+ packFloat128( flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 )
+{
+    float128 z;
+
+    z.low = zSig1;
+    z.high = ( ( (bits64) zSign )<<63 ) + ( ( (bits64) zExp )<<48 ) + zSig0;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and extended significand formed by the concatenation of `zSig0', `zSig1',
+| and `zSig2', and returns the proper quadruple-precision floating-point value
+| corresponding to the abstract input.  Ordinarily, the abstract value is
+| simply rounded and packed into the quadruple-precision format, with the
+| inexact exception raised if the abstract input cannot be represented
+| exactly.  However, if the abstract value is too large, the overflow and
+| inexact exceptions are raised and an infinity or maximal finite value is
+| returned.  If the abstract value is too small, the input value is rounded to
+| a subnormal number, and the underflow and inexact exceptions are raised if
+| the abstract input cannot be represented exactly as a subnormal quadruple-
+| precision floating-point number.
+|     The input significand must be normalized or smaller.  If the input
+| significand is not normalized, `zExp' must be 0; in that case, the result
+| returned is a subnormal number, and it must not require rounding.  In the
+| usual case that the input significand is normalized, `zExp' must be 1 less
+| than the ``true'' floating-point exponent.  The handling of underflow and
+| overflow follows the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128
+ roundAndPackFloat128(
+     flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1, bits64 zSig2 STATUS_PARAM)
+{
+    int8 roundingMode;
+    flag roundNearestEven, increment, isTiny;
+
+    roundingMode = STATUS(float_rounding_mode);
+    roundNearestEven = ( roundingMode == float_round_nearest_even );
+    increment = ( (sbits64) zSig2 < 0 );
+    if ( ! roundNearestEven ) {
+        if ( roundingMode == float_round_to_zero ) {
+            increment = 0;
+        }
+        else {
+            if ( zSign ) {
+                increment = ( roundingMode == float_round_down ) && zSig2;
+            }
+            else {
+                increment = ( roundingMode == float_round_up ) && zSig2;
+            }
+        }
+    }
+    if ( 0x7FFD <= (bits32) zExp ) {
+        if (    ( 0x7FFD < zExp )
+             || (    ( zExp == 0x7FFD )
+                  && eq128(
+                         LIT64( 0x0001FFFFFFFFFFFF ),
+                         LIT64( 0xFFFFFFFFFFFFFFFF ),
+                         zSig0,
+                         zSig1
+                     )
+                  && increment
+                )
+           ) {
+            float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
+            if (    ( roundingMode == float_round_to_zero )
+                 || ( zSign && ( roundingMode == float_round_up ) )
+                 || ( ! zSign && ( roundingMode == float_round_down ) )
+               ) {
+                return
+                    packFloat128(
+                        zSign,
+                        0x7FFE,
+                        LIT64( 0x0000FFFFFFFFFFFF ),
+                        LIT64( 0xFFFFFFFFFFFFFFFF )
+                    );
+            }
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        if ( zExp < 0 ) {
+            isTiny =
+                   ( STATUS(float_detect_tininess) == float_tininess_before_rounding )
+                || ( zExp < -1 )
+                || ! increment
+                || lt128(
+                       zSig0,
+                       zSig1,
+                       LIT64( 0x0001FFFFFFFFFFFF ),
+                       LIT64( 0xFFFFFFFFFFFFFFFF )
+                   );
+            shift128ExtraRightJamming(
+                zSig0, zSig1, zSig2, - zExp, &zSig0, &zSig1, &zSig2 );
+            zExp = 0;
+            if ( isTiny && zSig2 ) float_raise( float_flag_underflow STATUS_VAR);
+            if ( roundNearestEven ) {
+                increment = ( (sbits64) zSig2 < 0 );
+            }
+            else {
+                if ( zSign ) {
+                    increment = ( roundingMode == float_round_down ) && zSig2;
+                }
+                else {
+                    increment = ( roundingMode == float_round_up ) && zSig2;
+                }
+            }
+        }
+    }
+    if ( zSig2 ) STATUS(float_exception_flags) |= float_flag_inexact;
+    if ( increment ) {
+        add128( zSig0, zSig1, 0, 1, &zSig0, &zSig1 );
+        zSig1 &= ~ ( ( zSig2 + zSig2 == 0 ) & roundNearestEven );
+    }
+    else {
+        if ( ( zSig0 | zSig1 ) == 0 ) zExp = 0;
+    }
+    return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Takes an abstract floating-point value having sign `zSign', exponent `zExp',
+| and significand formed by the concatenation of `zSig0' and `zSig1', and
+| returns the proper quadruple-precision floating-point value corresponding
+| to the abstract input.  This routine is just like `roundAndPackFloat128'
+| except that the input significand has fewer bits and does not have to be
+| normalized.  In all cases, `zExp' must be 1 less than the ``true'' floating-
+| point exponent.
+*----------------------------------------------------------------------------*/
+
+static float128
+ normalizeRoundAndPackFloat128(
+     flag zSign, int32 zExp, bits64 zSig0, bits64 zSig1 STATUS_PARAM)
+{
+    int8 shiftCount;
+    bits64 zSig2;
+
+    if ( zSig0 == 0 ) {
+        zSig0 = zSig1;
+        zSig1 = 0;
+        zExp -= 64;
+    }
+    shiftCount = countLeadingZeros64( zSig0 ) - 15;
+    if ( 0 <= shiftCount ) {
+        zSig2 = 0;
+        shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    }
+    else {
+        shift128ExtraRightJamming(
+            zSig0, zSig1, 0, - shiftCount, &zSig0, &zSig1, &zSig2 );
+    }
+    zExp -= shiftCount;
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR);
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the single-precision floating-point format.  The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 int32_to_float32( int32 a STATUS_PARAM )
+{
+    flag zSign;
+
+    if ( a == 0 ) return float32_zero;
+    if ( a == (sbits32) 0x80000000 ) return packFloat32( 1, 0x9E, 0 );
+    zSign = ( a < 0 );
+    return normalizeRoundAndPackFloat32( zSign, 0x9C, zSign ? - a : a STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the double-precision floating-point format.  The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 int32_to_float64( int32 a STATUS_PARAM )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig;
+
+    if ( a == 0 ) return float64_zero;
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 21;
+    zSig = absA;
+    return packFloat64( zSign, 0x432 - shiftCount, zSig<<shiftCount );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a'
+| to the extended double-precision floating-point format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 int32_to_floatx80( int32 a STATUS_PARAM )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig;
+
+    if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 32;
+    zSig = absA;
+    return packFloatx80( zSign, 0x403E - shiftCount, zSig<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 32-bit two's complement integer `a' to
+| the quadruple-precision floating-point format.  The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 int32_to_float128( int32 a STATUS_PARAM )
+{
+    flag zSign;
+    uint32 absA;
+    int8 shiftCount;
+    bits64 zSig0;
+
+    if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros32( absA ) + 17;
+    zSig0 = absA;
+    return packFloat128( zSign, 0x402E - shiftCount, zSig0<<shiftCount, 0 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the single-precision floating-point format.  The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 int64_to_float32( int64 a STATUS_PARAM )
+{
+    flag zSign;
+    uint64 absA;
+    int8 shiftCount;
+
+    if ( a == 0 ) return float32_zero;
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros64( absA ) - 40;
+    if ( 0 <= shiftCount ) {
+        return packFloat32( zSign, 0x95 - shiftCount, absA<<shiftCount );
+    }
+    else {
+        shiftCount += 7;
+        if ( shiftCount < 0 ) {
+            shift64RightJamming( absA, - shiftCount, &absA );
+        }
+        else {
+            absA <<= shiftCount;
+        }
+        return roundAndPackFloat32( zSign, 0x9C - shiftCount, absA STATUS_VAR );
+    }
+
+}
+
+float32 uint64_to_float32( uint64 a STATUS_PARAM )
+{
+    int8 shiftCount;
+
+    if ( a == 0 ) return float32_zero;
+    shiftCount = countLeadingZeros64( a ) - 40;
+    if ( 0 <= shiftCount ) {
+        return packFloat32( 1 > 0, 0x95 - shiftCount, a<<shiftCount );
+    }
+    else {
+        shiftCount += 7;
+        if ( shiftCount < 0 ) {
+            shift64RightJamming( a, - shiftCount, &a );
+        }
+        else {
+            a <<= shiftCount;
+        }
+        return roundAndPackFloat32( 1 > 0, 0x9C - shiftCount, a STATUS_VAR );
+    }
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the double-precision floating-point format.  The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 int64_to_float64( int64 a STATUS_PARAM )
+{
+    flag zSign;
+
+    if ( a == 0 ) return float64_zero;
+    if ( a == (sbits64) LIT64( 0x8000000000000000 ) ) {
+        return packFloat64( 1, 0x43E, 0 );
+    }
+    zSign = ( a < 0 );
+    return normalizeRoundAndPackFloat64( zSign, 0x43C, zSign ? - a : a STATUS_VAR );
+
+}
+
+float64 uint64_to_float64( uint64 a STATUS_PARAM )
+{
+    if ( a == 0 ) return float64_zero;
+    return normalizeRoundAndPackFloat64( 0, 0x43C, a STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a'
+| to the extended double-precision floating-point format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 int64_to_floatx80( int64 a STATUS_PARAM )
+{
+    flag zSign;
+    uint64 absA;
+    int8 shiftCount;
+
+    if ( a == 0 ) return packFloatx80( 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros64( absA );
+    return packFloatx80( zSign, 0x403E - shiftCount, absA<<shiftCount );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the 64-bit two's complement integer `a' to
+| the quadruple-precision floating-point format.  The conversion is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 int64_to_float128( int64 a STATUS_PARAM )
+{
+    flag zSign;
+    uint64 absA;
+    int8 shiftCount;
+    int32 zExp;
+    bits64 zSig0, zSig1;
+
+    if ( a == 0 ) return packFloat128( 0, 0, 0, 0 );
+    zSign = ( a < 0 );
+    absA = zSign ? - a : a;
+    shiftCount = countLeadingZeros64( absA ) + 49;
+    zExp = 0x406E - shiftCount;
+    if ( 64 <= shiftCount ) {
+        zSig1 = 0;
+        zSig0 = absA;
+        shiftCount -= 64;
+    }
+    else {
+        zSig1 = absA;
+        zSig0 = 0;
+    }
+    shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 );
+    return packFloat128( zSign, zExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 32-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float32_to_int32( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    bits64 aSig64;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( ( aExp == 0xFF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= 0x00800000;
+    shiftCount = 0xAF - aExp;
+    aSig64 = aSig;
+    aSig64 <<= 32;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig64, shiftCount, &aSig64 );
+    return roundAndPackInt32( aSign, aSig64 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 32-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned.  Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float32_to_int32_round_to_zero( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    int32 z;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    shiftCount = aExp - 0x9E;
+    if ( 0 <= shiftCount ) {
+        if ( float32_val(a) != 0xCF000000 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) return 0x7FFFFFFF;
+        }
+        return (sbits32) 0x80000000;
+    }
+    else if ( aExp <= 0x7E ) {
+        if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+        return 0;
+    }
+    aSig = ( aSig | 0x00800000 )<<8;
+    z = aSig>>( - shiftCount );
+    if ( (bits32) ( aSig<<( shiftCount & 31 ) ) ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    if ( aSign ) z = - z;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float32_to_int64( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    bits64 aSig64, aSigExtra;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    shiftCount = 0xBE - aExp;
+    if ( shiftCount < 0 ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+            return LIT64( 0x7FFFFFFFFFFFFFFF );
+        }
+        return (sbits64) LIT64( 0x8000000000000000 );
+    }
+    if ( aExp ) aSig |= 0x00800000;
+    aSig64 = aSig;
+    aSig64 <<= 40;
+    shift64ExtraRightJamming( aSig64, 0, shiftCount, &aSig64, &aSigExtra );
+    return roundAndPackInt64( aSign, aSig64, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the 64-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.  If
+| `a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+| conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float32_to_int64_round_to_zero( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits32 aSig;
+    bits64 aSig64;
+    int64 z;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    shiftCount = aExp - 0xBE;
+    if ( 0 <= shiftCount ) {
+        if ( float32_val(a) != 0xDF000000 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            if ( ! aSign || ( ( aExp == 0xFF ) && aSig ) ) {
+                return LIT64( 0x7FFFFFFFFFFFFFFF );
+            }
+        }
+        return (sbits64) LIT64( 0x8000000000000000 );
+    }
+    else if ( aExp <= 0x7E ) {
+        if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+        return 0;
+    }
+    aSig64 = aSig | 0x00800000;
+    aSig64 <<= 40;
+    z = aSig64>>( - shiftCount );
+    if ( (bits64) ( aSig64<<( shiftCount & 63 ) ) ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    if ( aSign ) z = - z;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the double-precision floating-point format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float32_to_float64( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ));
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( aSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    return packFloat64( aSign, aExp + 0x380, ( (bits64) aSig )<<29 );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the extended double-precision floating-point format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float32_to_floatx80( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) );
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    aSig |= 0x00800000;
+    return packFloatx80( aSign, aExp + 0x3F80, ( (bits64) aSig )<<40 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the single-precision floating-point value
+| `a' to the double-precision floating-point format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float32_to_float128( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) );
+        return packFloat128( aSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    return packFloat128( aSign, aExp + 0x3F80, ( (bits64) aSig )<<25, 0 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the single-precision floating-point value `a' to an integer, and
+| returns the result as a single-precision floating-point value.  The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_round_to_int( float32 a STATUS_PARAM)
+{
+    flag aSign;
+    int16 aExp;
+    bits32 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    bits32 z;
+
+    aExp = extractFloat32Exp( a );
+    if ( 0x96 <= aExp ) {
+        if ( ( aExp == 0xFF ) && extractFloat32Frac( a ) ) {
+            return propagateFloat32NaN( a, a STATUS_VAR );
+        }
+        return a;
+    }
+    if ( aExp <= 0x7E ) {
+        if ( (bits32) ( float32_val(a)<<1 ) == 0 ) return a;
+        STATUS(float_exception_flags) |= float_flag_inexact;
+        aSign = extractFloat32Sign( a );
+        switch ( STATUS(float_rounding_mode) ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x7E ) && extractFloat32Frac( a ) ) {
+                return packFloat32( aSign, 0x7F, 0 );
+            }
+            break;
+         case float_round_down:
+            return make_float32(aSign ? 0xBF800000 : 0);
+         case float_round_up:
+            return make_float32(aSign ? 0x80000000 : 0x3F800000);
+        }
+        return packFloat32( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x96 - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = float32_val(a);
+    roundingMode = STATUS(float_rounding_mode);
+    if ( roundingMode == float_round_nearest_even ) {
+        z += lastBitMask>>1;
+        if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloat32Sign( make_float32(z) ) ^ ( roundingMode == float_round_up ) ) {
+            z += roundBitsMask;
+        }
+    }
+    z &= ~ roundBitsMask;
+    if ( z != float32_val(a) ) STATUS(float_exception_flags) |= float_flag_inexact;
+    return make_float32(z);
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the single-precision
+| floating-point values `a' and `b'.  If `zSign' is 1, the sum is negated
+| before being returned.  `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 addFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
+{
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 6;
+    bSig <<= 6;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0xFF ) {
+            if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig |= 0x20000000;
+        }
+        shift32RightJamming( bSig, expDiff, &bSig );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0xFF ) {
+            if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+            return packFloat32( zSign, 0xFF, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig |= 0x20000000;
+        }
+        shift32RightJamming( aSig, - expDiff, &aSig );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0xFF ) {
+            if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+            return a;
+        }
+        if ( aExp == 0 ) return packFloat32( zSign, 0, ( aSig + bSig )>>6 );
+        zSig = 0x40000000 + aSig + bSig;
+        zExp = aExp;
+        goto roundAndPack;
+    }
+    aSig |= 0x20000000;
+    zSig = ( aSig + bSig )<<1;
+    --zExp;
+    if ( (sbits32) zSig < 0 ) {
+        zSig = aSig + bSig;
+        ++zExp;
+    }
+ roundAndPack:
+    return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the single-
+| precision floating-point values `a' and `b'.  If `zSign' is 1, the
+| difference is negated before being returned.  `zSign' is ignored if the
+| result is a NaN.  The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float32 subFloat32Sigs( float32 a, float32 b, flag zSign STATUS_PARAM)
+{
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 7;
+    bSig <<= 7;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0xFF ) {
+        if ( aSig | bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float32_default_nan;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloat32( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        return packFloat32( zSign ^ 1, 0xFF, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig |= 0x40000000;
+    }
+    shift32RightJamming( aSig, - expDiff, &aSig );
+    bSig |= 0x40000000;
+ bBigger:
+    zSig = bSig - aSig;
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig |= 0x40000000;
+    }
+    shift32RightJamming( bSig, expDiff, &bSig );
+    aSig |= 0x40000000;
+ aBigger:
+    zSig = aSig - bSig;
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the single-precision floating-point values `a'
+| and `b'.  The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_add( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat32Sigs( a, b, aSign STATUS_VAR);
+    }
+    else {
+        return subFloat32Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the single-precision floating-point values
+| `a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_sub( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat32Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return addFloat32Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the single-precision floating-point values
+| `a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_mul( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig;
+    bits64 zSig64;
+    bits32 zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0xFF ) {
+        if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+            return propagateFloat32NaN( a, b STATUS_VAR );
+        }
+        if ( ( bExp | bSig ) == 0 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        if ( ( aExp | aSig ) == 0 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x7F;
+    aSig = ( aSig | 0x00800000 )<<7;
+    bSig = ( bSig | 0x00800000 )<<8;
+    shift64RightJamming( ( (bits64) aSig ) * bSig, 32, &zSig64 );
+    zSig = zSig64;
+    if ( 0 <= (sbits32) ( zSig<<1 ) ) {
+        zSig <<= 1;
+        --zExp;
+    }
+    return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the single-precision floating-point value `a'
+| by the corresponding value `b'.  The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_div( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits32 aSig, bSig, zSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        if ( bExp == 0xFF ) {
+            if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float32_default_nan;
+        }
+        return packFloat32( zSign, 0xFF, 0 );
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        return packFloat32( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+                float_raise( float_flag_invalid STATUS_VAR);
+                return float32_default_nan;
+            }
+            float_raise( float_flag_divbyzero STATUS_VAR);
+            return packFloat32( zSign, 0xFF, 0 );
+        }
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat32( zSign, 0, 0 );
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x7D;
+    aSig = ( aSig | 0x00800000 )<<7;
+    bSig = ( bSig | 0x00800000 )<<8;
+    if ( bSig <= ( aSig + aSig ) ) {
+        aSig >>= 1;
+        ++zExp;
+    }
+    zSig = ( ( (bits64) aSig )<<32 ) / bSig;
+    if ( ( zSig & 0x3F ) == 0 ) {
+        zSig |= ( (bits64) bSig * zSig != ( (bits64) aSig )<<32 );
+    }
+    return roundAndPackFloat32( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the single-precision floating-point value `a'
+| with respect to the corresponding value `b'.  The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_rem( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, expDiff;
+    bits32 aSig, bSig;
+    bits32 q;
+    bits64 aSig64, bSig64, q64;
+    bits32 alternateASig;
+    sbits32 sigMean;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    bSig = extractFloat32Frac( b );
+    bExp = extractFloat32Exp( b );
+    bSign = extractFloat32Sign( b );
+    if ( aExp == 0xFF ) {
+        if ( aSig || ( ( bExp == 0xFF ) && bSig ) ) {
+            return propagateFloat32NaN( a, b STATUS_VAR );
+        }
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float32_default_nan;
+    }
+    if ( bExp == 0xFF ) {
+        if ( bSig ) return propagateFloat32NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float32_default_nan;
+        }
+        normalizeFloat32Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return a;
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    expDiff = aExp - bExp;
+    aSig |= 0x00800000;
+    bSig |= 0x00800000;
+    if ( expDiff < 32 ) {
+        aSig <<= 8;
+        bSig <<= 8;
+        if ( expDiff < 0 ) {
+            if ( expDiff < -1 ) return a;
+            aSig >>= 1;
+        }
+        q = ( bSig <= aSig );
+        if ( q ) aSig -= bSig;
+        if ( 0 < expDiff ) {
+            q = ( ( (bits64) aSig )<<32 ) / bSig;
+            q >>= 32 - expDiff;
+            bSig >>= 2;
+            aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+        }
+        else {
+            aSig >>= 2;
+            bSig >>= 2;
+        }
+    }
+    else {
+        if ( bSig <= aSig ) aSig -= bSig;
+        aSig64 = ( (bits64) aSig )<<40;
+        bSig64 = ( (bits64) bSig )<<40;
+        expDiff -= 64;
+        while ( 0 < expDiff ) {
+            q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+            q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+            aSig64 = - ( ( bSig * q64 )<<38 );
+            expDiff -= 62;
+        }
+        expDiff += 64;
+        q64 = estimateDiv128To64( aSig64, 0, bSig64 );
+        q64 = ( 2 < q64 ) ? q64 - 2 : 0;
+        q = q64>>( 64 - expDiff );
+        bSig <<= 6;
+        aSig = ( ( aSig64>>33 )<<( expDiff - 1 ) ) - bSig * q;
+    }
+    do {
+        alternateASig = aSig;
+        ++q;
+        aSig -= bSig;
+    } while ( 0 <= (sbits32) aSig );
+    sigMean = aSig + alternateASig;
+    if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+        aSig = alternateASig;
+    }
+    zSign = ( (sbits32) aSig < 0 );
+    if ( zSign ) aSig = - aSig;
+    return normalizeRoundAndPackFloat32( aSign ^ zSign, bExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the single-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float32_sqrt( float32 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, zExp;
+    bits32 aSig, zSig;
+    bits64 rem, term;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+    if ( aExp == 0xFF ) {
+        if ( aSig ) return propagateFloat32NaN( a, float32_zero STATUS_VAR );
+        if ( ! aSign ) return a;
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float32_default_nan;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig ) == 0 ) return a;
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float32_default_nan;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return float32_zero;
+        normalizeFloat32Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = ( ( aExp - 0x7F )>>1 ) + 0x7E;
+    aSig = ( aSig | 0x00800000 )<<8;
+    zSig = estimateSqrt32( aExp, aSig ) + 2;
+    if ( ( zSig & 0x7F ) <= 5 ) {
+        if ( zSig < 2 ) {
+            zSig = 0x7FFFFFFF;
+            goto roundAndPack;
+        }
+        aSig >>= aExp & 1;
+        term = ( (bits64) zSig ) * zSig;
+        rem = ( ( (bits64) aSig )<<32 ) - term;
+        while ( (sbits64) rem < 0 ) {
+            --zSig;
+            rem += ( ( (bits64) zSig )<<1 ) | 1;
+        }
+        zSig |= ( rem != 0 );
+    }
+    shift32RightJamming( zSig, 1, &zSig );
+ roundAndPack:
+    return roundAndPackFloat32( 0, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise.  The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_eq( float32 a, float32 b STATUS_PARAM )
+{
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    return ( float32_val(a) == float32_val(b) ) ||
+            ( (bits32) ( ( float32_val(a) | float32_val(b) )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise.  The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_le( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits32 av, bv;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    av = float32_val(a);
+    bv = float32_val(b);
+    if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 );
+    return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise.  The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_lt( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits32 av, bv;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    av = float32_val(a);
+    bv = float32_val(b);
+    if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 );
+    return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise.  The invalid exception is
+| raised if either operand is a NaN.  Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_eq_signaling( float32 a, float32 b STATUS_PARAM )
+{
+    bits32 av, bv;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    av = float32_val(a);
+    bv = float32_val(b);
+    return ( av == bv ) || ( (bits32) ( ( av | bv )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+| cause an exception.  Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_le_quiet( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits32 av, bv;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    av = float32_val(a);
+    bv = float32_val(b);
+    if ( aSign != bSign ) return aSign || ( (bits32) ( ( av | bv )<<1 ) == 0 );
+    return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the single-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+| exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float32_lt_quiet( float32 a, float32 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits32 av, bv;
+
+    if (    ( ( extractFloat32Exp( a ) == 0xFF ) && extractFloat32Frac( a ) )
+         || ( ( extractFloat32Exp( b ) == 0xFF ) && extractFloat32Frac( b ) )
+       ) {
+        if ( float32_is_signaling_nan( a ) || float32_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloat32Sign( a );
+    bSign = extractFloat32Sign( b );
+    av = float32_val(a);
+    bv = float32_val(b);
+    if ( aSign != bSign ) return aSign && ( (bits32) ( ( av | bv )<<1 ) != 0 );
+    return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 32-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float64_to_int32( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x42C - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 32-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned.  Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float64_to_int32_round_to_zero( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( 0x41E < aExp ) {
+        if ( ( aExp == 0x7FF ) && aSig ) aSign = 0;
+        goto invalid;
+    }
+    else if ( aExp < 0x3FF ) {
+        if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+        return 0;
+    }
+    aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x433 - aExp;
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_raise( float_flag_invalid STATUS_VAR);
+        return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float64_to_int64( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig, aSigExtra;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = 0x433 - aExp;
+    if ( shiftCount <= 0 ) {
+        if ( 0x43E < aExp ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            if (    ! aSign
+                 || (    ( aExp == 0x7FF )
+                      && ( aSig != LIT64( 0x0010000000000000 ) ) )
+               ) {
+                return LIT64( 0x7FFFFFFFFFFFFFFF );
+            }
+            return (sbits64) LIT64( 0x8000000000000000 );
+        }
+        aSigExtra = 0;
+        aSig <<= - shiftCount;
+    }
+    else {
+        shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra );
+    }
+    return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the 64-bit two's complement integer format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned.  Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float64_to_int64_round_to_zero( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, shiftCount;
+    bits64 aSig;
+    int64 z;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp ) aSig |= LIT64( 0x0010000000000000 );
+    shiftCount = aExp - 0x433;
+    if ( 0 <= shiftCount ) {
+        if ( 0x43E <= aExp ) {
+            if ( float64_val(a) != LIT64( 0xC3E0000000000000 ) ) {
+                float_raise( float_flag_invalid STATUS_VAR);
+                if (    ! aSign
+                     || (    ( aExp == 0x7FF )
+                          && ( aSig != LIT64( 0x0010000000000000 ) ) )
+                   ) {
+                    return LIT64( 0x7FFFFFFFFFFFFFFF );
+                }
+            }
+            return (sbits64) LIT64( 0x8000000000000000 );
+        }
+        z = aSig<<shiftCount;
+    }
+    else {
+        if ( aExp < 0x3FE ) {
+            if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+            return 0;
+        }
+        z = aSig>>( - shiftCount );
+        if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) {
+            STATUS(float_exception_flags) |= float_flag_inexact;
+        }
+    }
+    if ( aSign ) z = - z;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the single-precision floating-point format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float64_to_float32( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+    bits32 zSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) );
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    shift64RightJamming( aSig, 22, &aSig );
+    zSig = aSig;
+    if ( aExp || zSig ) {
+        zSig |= 0x40000000;
+        aExp -= 0x381;
+    }
+    return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the extended double-precision floating-point format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float64_to_floatx80( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) );
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    return
+        packFloatx80(
+            aSign, aExp + 0x3C00, ( aSig | LIT64( 0x0010000000000000 ) )<<11 );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the double-precision floating-point value
+| `a' to the quadruple-precision floating-point format.  The conversion is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float64_to_float128( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig, zSig0, zSig1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) );
+        return packFloat128( aSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat128( aSign, 0, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+        --aExp;
+    }
+    shift128Right( aSig, 0, 4, &zSig0, &zSig1 );
+    return packFloat128( aSign, aExp + 0x3C00, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the double-precision floating-point value `a' to an integer, and
+| returns the result as a double-precision floating-point value.  The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_round_to_int( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    bits64 z;
+
+    aExp = extractFloat64Exp( a );
+    if ( 0x433 <= aExp ) {
+        if ( ( aExp == 0x7FF ) && extractFloat64Frac( a ) ) {
+            return propagateFloat64NaN( a, a STATUS_VAR );
+        }
+        return a;
+    }
+    if ( aExp < 0x3FF ) {
+        if ( (bits64) ( float64_val(a)<<1 ) == 0 ) return a;
+        STATUS(float_exception_flags) |= float_flag_inexact;
+        aSign = extractFloat64Sign( a );
+        switch ( STATUS(float_rounding_mode) ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x3FE ) && extractFloat64Frac( a ) ) {
+                return packFloat64( aSign, 0x3FF, 0 );
+            }
+            break;
+         case float_round_down:
+            return make_float64(aSign ? LIT64( 0xBFF0000000000000 ) : 0);
+         case float_round_up:
+            return make_float64(
+            aSign ? LIT64( 0x8000000000000000 ) : LIT64( 0x3FF0000000000000 ));
+        }
+        return packFloat64( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x433 - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = float64_val(a);
+    roundingMode = STATUS(float_rounding_mode);
+    if ( roundingMode == float_round_nearest_even ) {
+        z += lastBitMask>>1;
+        if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloat64Sign( make_float64(z) ) ^ ( roundingMode == float_round_up ) ) {
+            z += roundBitsMask;
+        }
+    }
+    z &= ~ roundBitsMask;
+    if ( z != float64_val(a) )
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    return make_float64(z);
+
+}
+
+float64 float64_trunc_to_int( float64 a STATUS_PARAM)
+{
+    int oldmode;
+    float64 res;
+    oldmode = STATUS(float_rounding_mode);
+    STATUS(float_rounding_mode) = float_round_to_zero;
+    res = float64_round_to_int(a STATUS_VAR);
+    STATUS(float_rounding_mode) = oldmode;
+    return res;
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the double-precision
+| floating-point values `a' and `b'.  If `zSign' is 1, the sum is negated
+| before being returned.  `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 addFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
+{
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 9;
+    bSig <<= 9;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FF ) {
+            if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig |= LIT64( 0x2000000000000000 );
+        }
+        shift64RightJamming( bSig, expDiff, &bSig );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FF ) {
+            if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+            return packFloat64( zSign, 0x7FF, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig |= LIT64( 0x2000000000000000 );
+        }
+        shift64RightJamming( aSig, - expDiff, &aSig );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FF ) {
+            if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+            return a;
+        }
+        if ( aExp == 0 ) return packFloat64( zSign, 0, ( aSig + bSig )>>9 );
+        zSig = LIT64( 0x4000000000000000 ) + aSig + bSig;
+        zExp = aExp;
+        goto roundAndPack;
+    }
+    aSig |= LIT64( 0x2000000000000000 );
+    zSig = ( aSig + bSig )<<1;
+    --zExp;
+    if ( (sbits64) zSig < 0 ) {
+        zSig = aSig + bSig;
+        ++zExp;
+    }
+ roundAndPack:
+    return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the double-
+| precision floating-point values `a' and `b'.  If `zSign' is 1, the
+| difference is negated before being returned.  `zSign' is ignored if the
+| result is a NaN.  The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float64 subFloat64Sigs( float64 a, float64 b, flag zSign STATUS_PARAM )
+{
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    int16 expDiff;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    expDiff = aExp - bExp;
+    aSig <<= 10;
+    bSig <<= 10;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FF ) {
+        if ( aSig | bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float64_default_nan;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloat64( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        return packFloat64( zSign ^ 1, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig |= LIT64( 0x4000000000000000 );
+    }
+    shift64RightJamming( aSig, - expDiff, &aSig );
+    bSig |= LIT64( 0x4000000000000000 );
+ bBigger:
+    zSig = bSig - aSig;
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig |= LIT64( 0x4000000000000000 );
+    }
+    shift64RightJamming( bSig, expDiff, &bSig );
+    aSig |= LIT64( 0x4000000000000000 );
+ aBigger:
+    zSig = aSig - bSig;
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the double-precision floating-point values `a'
+| and `b'.  The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_add( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat64Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return subFloat64Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the double-precision floating-point values
+| `a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_sub( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat64Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return addFloat64Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the double-precision floating-point values
+| `a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_mul( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FF ) {
+        if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+            return propagateFloat64NaN( a, b STATUS_VAR );
+        }
+        if ( ( bExp | bSig ) == 0 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        if ( ( aExp | aSig ) == 0 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x3FF;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    mul64To128( aSig, bSig, &zSig0, &zSig1 );
+    zSig0 |= ( zSig1 != 0 );
+    if ( 0 <= (sbits64) ( zSig0<<1 ) ) {
+        zSig0 <<= 1;
+        --zExp;
+    }
+    return roundAndPackFloat64( zSign, zExp, zSig0 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the double-precision floating-point value `a'
+| by the corresponding value `b'.  The operation is performed according to
+| the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_div( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig;
+    bits64 rem0, rem1;
+    bits64 term0, term1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        if ( bExp == 0x7FF ) {
+            if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float64_default_nan;
+        }
+        return packFloat64( zSign, 0x7FF, 0 );
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        return packFloat64( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+                float_raise( float_flag_invalid STATUS_VAR);
+                return float64_default_nan;
+            }
+            float_raise( float_flag_divbyzero STATUS_VAR);
+            return packFloat64( zSign, 0x7FF, 0 );
+        }
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloat64( zSign, 0, 0 );
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x3FD;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<10;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    if ( bSig <= ( aSig + aSig ) ) {
+        aSig >>= 1;
+        ++zExp;
+    }
+    zSig = estimateDiv128To64( aSig, 0, bSig );
+    if ( ( zSig & 0x1FF ) <= 2 ) {
+        mul64To128( bSig, zSig, &term0, &term1 );
+        sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+        while ( (sbits64) rem0 < 0 ) {
+            --zSig;
+            add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+        }
+        zSig |= ( rem1 != 0 );
+    }
+    return roundAndPackFloat64( zSign, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the double-precision floating-point value `a'
+| with respect to the corresponding value `b'.  The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_rem( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int16 aExp, bExp, expDiff;
+    bits64 aSig, bSig;
+    bits64 q, alternateASig;
+    sbits64 sigMean;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    bSig = extractFloat64Frac( b );
+    bExp = extractFloat64Exp( b );
+    bSign = extractFloat64Sign( b );
+    if ( aExp == 0x7FF ) {
+        if ( aSig || ( ( bExp == 0x7FF ) && bSig ) ) {
+            return propagateFloat64NaN( a, b STATUS_VAR );
+        }
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float64_default_nan;
+    }
+    if ( bExp == 0x7FF ) {
+        if ( bSig ) return propagateFloat64NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            return float64_default_nan;
+        }
+        normalizeFloat64Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return a;
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    expDiff = aExp - bExp;
+    aSig = ( aSig | LIT64( 0x0010000000000000 ) )<<11;
+    bSig = ( bSig | LIT64( 0x0010000000000000 ) )<<11;
+    if ( expDiff < 0 ) {
+        if ( expDiff < -1 ) return a;
+        aSig >>= 1;
+    }
+    q = ( bSig <= aSig );
+    if ( q ) aSig -= bSig;
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig, 0, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        aSig = - ( ( bSig>>2 ) * q );
+        expDiff -= 62;
+    }
+    expDiff += 64;
+    if ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig, 0, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        q >>= 64 - expDiff;
+        bSig >>= 2;
+        aSig = ( ( aSig>>1 )<<( expDiff - 1 ) ) - bSig * q;
+    }
+    else {
+        aSig >>= 2;
+        bSig >>= 2;
+    }
+    do {
+        alternateASig = aSig;
+        ++q;
+        aSig -= bSig;
+    } while ( 0 <= (sbits64) aSig );
+    sigMean = aSig + alternateASig;
+    if ( ( sigMean < 0 ) || ( ( sigMean == 0 ) && ( q & 1 ) ) ) {
+        aSig = alternateASig;
+    }
+    zSign = ( (sbits64) aSig < 0 );
+    if ( zSign ) aSig = - aSig;
+    return normalizeRoundAndPackFloat64( aSign ^ zSign, bExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the double-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float64_sqrt( float64 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp, zExp;
+    bits64 aSig, zSig, doubleZSig;
+    bits64 rem0, rem1, term0, term1;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+    if ( aExp == 0x7FF ) {
+        if ( aSig ) return propagateFloat64NaN( a, a STATUS_VAR );
+        if ( ! aSign ) return a;
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float64_default_nan;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig ) == 0 ) return a;
+        float_raise( float_flag_invalid STATUS_VAR);
+        return float64_default_nan;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return float64_zero;
+        normalizeFloat64Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = ( ( aExp - 0x3FF )>>1 ) + 0x3FE;
+    aSig |= LIT64( 0x0010000000000000 );
+    zSig = estimateSqrt32( aExp, aSig>>21 );
+    aSig <<= 9 - ( aExp & 1 );
+    zSig = estimateDiv128To64( aSig, 0, zSig<<32 ) + ( zSig<<30 );
+    if ( ( zSig & 0x1FF ) <= 5 ) {
+        doubleZSig = zSig<<1;
+        mul64To128( zSig, zSig, &term0, &term1 );
+        sub128( aSig, 0, term0, term1, &rem0, &rem1 );
+        while ( (sbits64) rem0 < 0 ) {
+            --zSig;
+            doubleZSig -= 2;
+            add128( rem0, rem1, zSig>>63, doubleZSig | 1, &rem0, &rem1 );
+        }
+        zSig |= ( ( rem0 | rem1 ) != 0 );
+    }
+    return roundAndPackFloat64( 0, zExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is equal to the
+| corresponding value `b', and 0 otherwise.  The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_eq( float64 a, float64 b STATUS_PARAM )
+{
+    bits64 av, bv;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    av = float64_val(a);
+    bv = float64_val(b);
+    return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise.  The comparison is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_le( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits64 av, bv;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    av = float64_val(a);
+    bv = float64_val(b);
+    if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+    return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise.  The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_lt( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits64 av, bv;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    av = float64_val(a);
+    bv = float64_val(b);
+    if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 );
+    return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is equal to the
+| corresponding value `b', and 0 otherwise.  The invalid exception is raised
+| if either operand is a NaN.  Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_eq_signaling( float64 a, float64 b STATUS_PARAM )
+{
+    bits64 av, bv;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    av = float64_val(a);
+    bv = float64_val(b);
+    return ( av == bv ) || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than or
+| equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+| cause an exception.  Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_le_quiet( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits64 av, bv;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    av = float64_val(a);
+    bv = float64_val(b);
+    if ( aSign != bSign ) return aSign || ( (bits64) ( ( av | bv )<<1 ) == 0 );
+    return ( av == bv ) || ( aSign ^ ( av < bv ) );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the double-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+| exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float64_lt_quiet( float64 a, float64 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+    bits64 av, bv;
+
+    if (    ( ( extractFloat64Exp( a ) == 0x7FF ) && extractFloat64Frac( a ) )
+         || ( ( extractFloat64Exp( b ) == 0x7FF ) && extractFloat64Frac( b ) )
+       ) {
+        if ( float64_is_signaling_nan( a ) || float64_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloat64Sign( a );
+    bSign = extractFloat64Sign( b );
+    av = float64_val(a);
+    bv = float64_val(b);
+    if ( aSign != bSign ) return aSign && ( (bits64) ( ( av | bv )<<1 ) != 0 );
+    return ( av != bv ) && ( aSign ^ ( av < bv ) );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 32-bit two's complement integer format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic---which means in particular that the conversion
+| is rounded according to the current rounding mode.  If `a' is a NaN, the
+| largest positive integer is returned.  Otherwise, if the conversion
+| overflows, the largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 floatx80_to_int32( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+    shiftCount = 0x4037 - aExp;
+    if ( shiftCount <= 0 ) shiftCount = 1;
+    shift64RightJamming( aSig, shiftCount, &aSig );
+    return roundAndPackInt32( aSign, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 32-bit two's complement integer format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic, except that the conversion is always rounded
+| toward zero.  If `a' is a NaN, the largest positive integer is returned.
+| Otherwise, if the conversion overflows, the largest integer with the same
+| sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 floatx80_to_int32_round_to_zero( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig, savedASig;
+    int32 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( 0x401E < aExp ) {
+        if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) aSign = 0;
+        goto invalid;
+    }
+    else if ( aExp < 0x3FFF ) {
+        if ( aExp || aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+        return 0;
+    }
+    shiftCount = 0x403E - aExp;
+    savedASig = aSig;
+    aSig >>= shiftCount;
+    z = aSig;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_raise( float_flag_invalid STATUS_VAR);
+        return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig<<shiftCount ) != savedASig ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 64-bit two's complement integer format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic---which means in particular that the conversion
+| is rounded according to the current rounding mode.  If `a' is a NaN,
+| the largest positive integer is returned.  Otherwise, if the conversion
+| overflows, the largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 floatx80_to_int64( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig, aSigExtra;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    shiftCount = 0x403E - aExp;
+    if ( shiftCount <= 0 ) {
+        if ( shiftCount ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            if (    ! aSign
+                 || (    ( aExp == 0x7FFF )
+                      && ( aSig != LIT64( 0x8000000000000000 ) ) )
+               ) {
+                return LIT64( 0x7FFFFFFFFFFFFFFF );
+            }
+            return (sbits64) LIT64( 0x8000000000000000 );
+        }
+        aSigExtra = 0;
+    }
+    else {
+        shift64ExtraRightJamming( aSig, 0, shiftCount, &aSig, &aSigExtra );
+    }
+    return roundAndPackInt64( aSign, aSig, aSigExtra STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the 64-bit two's complement integer format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic, except that the conversion is always rounded
+| toward zero.  If `a' is a NaN, the largest positive integer is returned.
+| Otherwise, if the conversion overflows, the largest integer with the same
+| sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 floatx80_to_int64_round_to_zero( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig;
+    int64 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    shiftCount = aExp - 0x403E;
+    if ( 0 <= shiftCount ) {
+        aSig &= LIT64( 0x7FFFFFFFFFFFFFFF );
+        if ( ( a.high != 0xC03E ) || aSig ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            if ( ! aSign || ( ( aExp == 0x7FFF ) && aSig ) ) {
+                return LIT64( 0x7FFFFFFFFFFFFFFF );
+            }
+        }
+        return (sbits64) LIT64( 0x8000000000000000 );
+    }
+    else if ( aExp < 0x3FFF ) {
+        if ( aExp | aSig ) STATUS(float_exception_flags) |= float_flag_inexact;
+        return 0;
+    }
+    z = aSig>>( - shiftCount );
+    if ( (bits64) ( aSig<<( shiftCount & 63 ) ) ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    if ( aSign ) z = - z;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the single-precision floating-point format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 floatx80_to_float32( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) {
+            return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) );
+        }
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    shift64RightJamming( aSig, 33, &aSig );
+    if ( aExp || aSig ) aExp -= 0x3F81;
+    return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the double-precision floating-point format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 floatx80_to_float64( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig, zSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) {
+            return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) );
+        }
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    shift64RightJamming( aSig, 1, &zSig );
+    if ( aExp || aSig ) aExp -= 0x3C01;
+    return roundAndPackFloat64( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the extended double-precision floating-
+| point value `a' to the quadruple-precision floating-point format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 floatx80_to_float128( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig, zSig0, zSig1;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) {
+        return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) );
+    }
+    shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 );
+    return packFloat128( aSign, aExp, zSig0, zSig1 );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the extended double-precision floating-point value `a' to an integer,
+| and returns the result as an extended quadruple-precision floating-point
+| value.  The operation is performed according to the IEC/IEEE Standard for
+| Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    floatx80 z;
+
+    aExp = extractFloatx80Exp( a );
+    if ( 0x403E <= aExp ) {
+        if ( ( aExp == 0x7FFF ) && (bits64) ( extractFloatx80Frac( a )<<1 ) ) {
+            return propagateFloatx80NaN( a, a STATUS_VAR );
+        }
+        return a;
+    }
+    if ( aExp < 0x3FFF ) {
+        if (    ( aExp == 0 )
+             && ( (bits64) ( extractFloatx80Frac( a )<<1 ) == 0 ) ) {
+            return a;
+        }
+        STATUS(float_exception_flags) |= float_flag_inexact;
+        aSign = extractFloatx80Sign( a );
+        switch ( STATUS(float_rounding_mode) ) {
+         case float_round_nearest_even:
+            if ( ( aExp == 0x3FFE ) && (bits64) ( extractFloatx80Frac( a )<<1 )
+               ) {
+                return
+                    packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
+            }
+            break;
+         case float_round_down:
+            return
+                  aSign ?
+                      packFloatx80( 1, 0x3FFF, LIT64( 0x8000000000000000 ) )
+                : packFloatx80( 0, 0, 0 );
+         case float_round_up:
+            return
+                  aSign ? packFloatx80( 1, 0, 0 )
+                : packFloatx80( 0, 0x3FFF, LIT64( 0x8000000000000000 ) );
+        }
+        return packFloatx80( aSign, 0, 0 );
+    }
+    lastBitMask = 1;
+    lastBitMask <<= 0x403E - aExp;
+    roundBitsMask = lastBitMask - 1;
+    z = a;
+    roundingMode = STATUS(float_rounding_mode);
+    if ( roundingMode == float_round_nearest_even ) {
+        z.low += lastBitMask>>1;
+        if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+    }
+    else if ( roundingMode != float_round_to_zero ) {
+        if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
+            z.low += roundBitsMask;
+        }
+    }
+    z.low &= ~ roundBitsMask;
+    if ( z.low == 0 ) {
+        ++z.high;
+        z.low = LIT64( 0x8000000000000000 );
+    }
+    if ( z.low != a.low ) STATUS(float_exception_flags) |= float_flag_inexact;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the extended double-
+| precision floating-point values `a' and `b'.  If `zSign' is 1, the sum is
+| negated before being returned.  `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80 addFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM)
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    int32 expDiff;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FFF ) {
+            if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+            return a;
+        }
+        if ( bExp == 0 ) --expDiff;
+        shift64ExtraRightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FFF ) {
+            if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        if ( aExp == 0 ) ++expDiff;
+        shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FFF ) {
+            if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+                return propagateFloatx80NaN( a, b STATUS_VAR );
+            }
+            return a;
+        }
+        zSig1 = 0;
+        zSig0 = aSig + bSig;
+        if ( aExp == 0 ) {
+            normalizeFloatx80Subnormal( zSig0, &zExp, &zSig0 );
+            goto roundAndPack;
+        }
+        zExp = aExp;
+        goto shiftRight1;
+    }
+    zSig0 = aSig + bSig;
+    if ( (sbits64) zSig0 < 0 ) goto roundAndPack;
+ shiftRight1:
+    shift64ExtraRightJamming( zSig0, zSig1, 1, &zSig0, &zSig1 );
+    zSig0 |= LIT64( 0x8000000000000000 );
+    ++zExp;
+ roundAndPack:
+    return
+        roundAndPackFloatx80(
+            STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the extended
+| double-precision floating-point values `a' and `b'.  If `zSign' is 1, the
+| difference is negated before being returned.  `zSign' is ignored if the
+| result is a NaN.  The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static floatx80 subFloatx80Sigs( floatx80 a, floatx80 b, flag zSign STATUS_PARAM )
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    int32 expDiff;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( ( aSig | bSig )<<1 ) ) {
+            return propagateFloatx80NaN( a, b STATUS_VAR );
+        }
+        float_raise( float_flag_invalid STATUS_VAR);
+        z.low = floatx80_default_nan_low;
+        z.high = floatx80_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    zSig1 = 0;
+    if ( bSig < aSig ) goto aBigger;
+    if ( aSig < bSig ) goto bBigger;
+    return packFloatx80( STATUS(float_rounding_mode) == float_round_down, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+        return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) ++expDiff;
+    shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 );
+ bBigger:
+    sub128( bSig, 0, aSig, zSig1, &zSig0, &zSig1 );
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) --expDiff;
+    shift128RightJamming( bSig, 0, expDiff, &bSig, &zSig1 );
+ aBigger:
+    sub128( aSig, 0, bSig, zSig1, &zSig0, &zSig1 );
+    zExp = aExp;
+ normalizeRoundAndPack:
+    return
+        normalizeRoundAndPackFloatx80(
+            STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the extended double-precision floating-point
+| values `a' and `b'.  The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_add( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign == bSign ) {
+        return addFloatx80Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return subFloatx80Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the extended double-precision floating-
+| point values `a' and `b'.  The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sub( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign == bSign ) {
+        return subFloatx80Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return addFloatx80Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the extended double-precision floating-
+| point values `a' and `b'.  The operation is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_mul( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if (    (bits64) ( aSig<<1 )
+             || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+            return propagateFloatx80NaN( a, b STATUS_VAR );
+        }
+        if ( ( bExp | bSig ) == 0 ) goto invalid;
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+        if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid STATUS_VAR);
+            z.low = floatx80_default_nan_low;
+            z.high = floatx80_default_nan_high;
+            return z;
+        }
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    zExp = aExp + bExp - 0x3FFE;
+    mul64To128( aSig, bSig, &zSig0, &zSig1 );
+    if ( 0 < (sbits64) zSig0 ) {
+        shortShift128Left( zSig0, zSig1, 1, &zSig0, &zSig1 );
+        --zExp;
+    }
+    return
+        roundAndPackFloatx80(
+            STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the extended double-precision floating-point
+| value `a' by the corresponding value `b'.  The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_div( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig, bSig, zSig0, zSig1;
+    bits64 rem0, rem1, rem2, term0, term1, term2;
+    floatx80 z;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+        if ( bExp == 0x7FFF ) {
+            if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+            goto invalid;
+        }
+        return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+        return packFloatx80( zSign, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+            if ( ( aExp | aSig ) == 0 ) {
+ invalid:
+                float_raise( float_flag_invalid STATUS_VAR);
+                z.low = floatx80_default_nan_low;
+                z.high = floatx80_default_nan_high;
+                return z;
+            }
+            float_raise( float_flag_divbyzero STATUS_VAR);
+            return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+        }
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 );
+        normalizeFloatx80Subnormal( aSig, &aExp, &aSig );
+    }
+    zExp = aExp - bExp + 0x3FFE;
+    rem1 = 0;
+    if ( bSig <= aSig ) {
+        shift128Right( aSig, 0, 1, &aSig, &rem1 );
+        ++zExp;
+    }
+    zSig0 = estimateDiv128To64( aSig, rem1, bSig );
+    mul64To128( bSig, zSig0, &term0, &term1 );
+    sub128( aSig, rem1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        add128( rem0, rem1, 0, bSig, &rem0, &rem1 );
+    }
+    zSig1 = estimateDiv128To64( rem1, 0, bSig );
+    if ( (bits64) ( zSig1<<1 ) <= 8 ) {
+        mul64To128( bSig, zSig1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            add128( rem1, rem2, 0, bSig, &rem1, &rem2 );
+        }
+        zSig1 |= ( ( rem1 | rem2 ) != 0 );
+    }
+    return
+        roundAndPackFloatx80(
+            STATUS(floatx80_rounding_precision), zSign, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the extended double-precision floating-point value
+| `a' with respect to the corresponding value `b'.  The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_rem( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, expDiff;
+    bits64 aSig0, aSig1, bSig;
+    bits64 q, term0, term1, alternateASig0, alternateASig1;
+    floatx80 z;
+
+    aSig0 = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    bSig = extractFloatx80Frac( b );
+    bExp = extractFloatx80Exp( b );
+    bSign = extractFloatx80Sign( b );
+    if ( aExp == 0x7FFF ) {
+        if (    (bits64) ( aSig0<<1 )
+             || ( ( bExp == 0x7FFF ) && (bits64) ( bSig<<1 ) ) ) {
+            return propagateFloatx80NaN( a, b STATUS_VAR );
+        }
+        goto invalid;
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( (bits64) ( bSig<<1 ) ) return propagateFloatx80NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( bSig == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid STATUS_VAR);
+            z.low = floatx80_default_nan_low;
+            z.high = floatx80_default_nan_high;
+            return z;
+        }
+        normalizeFloatx80Subnormal( bSig, &bExp, &bSig );
+    }
+    if ( aExp == 0 ) {
+        if ( (bits64) ( aSig0<<1 ) == 0 ) return a;
+        normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+    }
+    bSig |= LIT64( 0x8000000000000000 );
+    zSign = aSign;
+    expDiff = aExp - bExp;
+    aSig1 = 0;
+    if ( expDiff < 0 ) {
+        if ( expDiff < -1 ) return a;
+        shift128Right( aSig0, 0, 1, &aSig0, &aSig1 );
+        expDiff = 0;
+    }
+    q = ( bSig <= aSig0 );
+    if ( q ) aSig0 -= bSig;
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        mul64To128( bSig, q, &term0, &term1 );
+        sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        shortShift128Left( aSig0, aSig1, 62, &aSig0, &aSig1 );
+        expDiff -= 62;
+    }
+    expDiff += 64;
+    if ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig );
+        q = ( 2 < q ) ? q - 2 : 0;
+        q >>= 64 - expDiff;
+        mul64To128( bSig, q<<( 64 - expDiff ), &term0, &term1 );
+        sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        shortShift128Left( 0, bSig, 64 - expDiff, &term0, &term1 );
+        while ( le128( term0, term1, aSig0, aSig1 ) ) {
+            ++q;
+            sub128( aSig0, aSig1, term0, term1, &aSig0, &aSig1 );
+        }
+    }
+    else {
+        term1 = 0;
+        term0 = bSig;
+    }
+    sub128( term0, term1, aSig0, aSig1, &alternateASig0, &alternateASig1 );
+    if (    lt128( alternateASig0, alternateASig1, aSig0, aSig1 )
+         || (    eq128( alternateASig0, alternateASig1, aSig0, aSig1 )
+              && ( q & 1 ) )
+       ) {
+        aSig0 = alternateASig0;
+        aSig1 = alternateASig1;
+        zSign = ! zSign;
+    }
+    return
+        normalizeRoundAndPackFloatx80(
+            80, zSign, bExp + expDiff, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the extended double-precision floating-point
+| value `a'.  The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 floatx80_sqrt( floatx80 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, zExp;
+    bits64 aSig0, aSig1, zSig0, zSig1, doubleZSig0;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    floatx80 z;
+
+    aSig0 = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( (bits64) ( aSig0<<1 ) ) return propagateFloatx80NaN( a, a STATUS_VAR );
+        if ( ! aSign ) return a;
+        goto invalid;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig0 ) == 0 ) return a;
+ invalid:
+        float_raise( float_flag_invalid STATUS_VAR);
+        z.low = floatx80_default_nan_low;
+        z.high = floatx80_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        if ( aSig0 == 0 ) return packFloatx80( 0, 0, 0 );
+        normalizeFloatx80Subnormal( aSig0, &aExp, &aSig0 );
+    }
+    zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFF;
+    zSig0 = estimateSqrt32( aExp, aSig0>>32 );
+    shift128Right( aSig0, 0, 2 + ( aExp & 1 ), &aSig0, &aSig1 );
+    zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 );
+    doubleZSig0 = zSig0<<1;
+    mul64To128( zSig0, zSig0, &term0, &term1 );
+    sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        doubleZSig0 -= 2;
+        add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 );
+    }
+    zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 );
+    if ( ( zSig1 & LIT64( 0x3FFFFFFFFFFFFFFF ) ) <= 5 ) {
+        if ( zSig1 == 0 ) zSig1 = 1;
+        mul64To128( doubleZSig0, zSig1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        mul64To128( zSig1, zSig1, &term2, &term3 );
+        sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            shortShift128Left( 0, zSig1, 1, &term2, &term3 );
+            term3 |= 1;
+            term2 |= doubleZSig0;
+            add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shortShift128Left( 0, zSig1, 1, &zSig0, &zSig1 );
+    zSig0 |= doubleZSig0;
+    return
+        roundAndPackFloatx80(
+            STATUS(floatx80_rounding_precision), 0, zExp, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| equal to the corresponding value `b', and 0 otherwise.  The comparison is
+| performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_eq( floatx80 a, floatx80 b STATUS_PARAM )
+{
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| less than or equal to the corresponding value `b', and 0 otherwise.  The
+| comparison is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_le( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is
+| less than the corresponding value `b', and 0 otherwise.  The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_lt( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is equal
+| to the corresponding value `b', and 0 otherwise.  The invalid exception is
+| raised if either operand is a NaN.  Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_eq_signaling( floatx80 a, floatx80 b STATUS_PARAM )
+{
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits16) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is less
+| than or equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs
+| do not cause an exception.  Otherwise, the comparison is performed according
+| to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_le_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the extended double-precision floating-point value `a' is less
+| than the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause
+| an exception.  Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int floatx80_lt_quiet( floatx80 a, floatx80 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloatx80Exp( a ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( a )<<1 ) )
+         || (    ( extractFloatx80Exp( b ) == 0x7FFF )
+              && (bits64) ( extractFloatx80Frac( b )<<1 ) )
+       ) {
+        if (    floatx80_is_signaling_nan( a )
+             || floatx80_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloatx80Sign( a );
+    bSign = extractFloatx80Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits16) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 32-bit two's complement integer format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int32 float128_to_int32( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) aSign = 0;
+    if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+    aSig0 |= ( aSig1 != 0 );
+    shiftCount = 0x4028 - aExp;
+    if ( 0 < shiftCount ) shift64RightJamming( aSig0, shiftCount, &aSig0 );
+    return roundAndPackInt32( aSign, aSig0 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 32-bit two's complement integer format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.  If
+| `a' is a NaN, the largest positive integer is returned.  Otherwise, if the
+| conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int32 float128_to_int32_round_to_zero( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1, savedASig;
+    int32 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    aSig0 |= ( aSig1 != 0 );
+    if ( 0x401E < aExp ) {
+        if ( ( aExp == 0x7FFF ) && aSig0 ) aSign = 0;
+        goto invalid;
+    }
+    else if ( aExp < 0x3FFF ) {
+        if ( aExp || aSig0 ) STATUS(float_exception_flags) |= float_flag_inexact;
+        return 0;
+    }
+    aSig0 |= LIT64( 0x0001000000000000 );
+    shiftCount = 0x402F - aExp;
+    savedASig = aSig0;
+    aSig0 >>= shiftCount;
+    z = aSig0;
+    if ( aSign ) z = - z;
+    if ( ( z < 0 ) ^ aSign ) {
+ invalid:
+        float_raise( float_flag_invalid STATUS_VAR);
+        return aSign ? (sbits32) 0x80000000 : 0x7FFFFFFF;
+    }
+    if ( ( aSig0<<shiftCount ) != savedASig ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 64-bit two's complement integer format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic---which means in particular that the conversion is rounded
+| according to the current rounding mode.  If `a' is a NaN, the largest
+| positive integer is returned.  Otherwise, if the conversion overflows, the
+| largest integer with the same sign as `a' is returned.
+*----------------------------------------------------------------------------*/
+
+int64 float128_to_int64( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+    shiftCount = 0x402F - aExp;
+    if ( shiftCount <= 0 ) {
+        if ( 0x403E < aExp ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+            if (    ! aSign
+                 || (    ( aExp == 0x7FFF )
+                      && ( aSig1 || ( aSig0 != LIT64( 0x0001000000000000 ) ) )
+                    )
+               ) {
+                return LIT64( 0x7FFFFFFFFFFFFFFF );
+            }
+            return (sbits64) LIT64( 0x8000000000000000 );
+        }
+        shortShift128Left( aSig0, aSig1, - shiftCount, &aSig0, &aSig1 );
+    }
+    else {
+        shift64ExtraRightJamming( aSig0, aSig1, shiftCount, &aSig0, &aSig1 );
+    }
+    return roundAndPackInt64( aSign, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the 64-bit two's complement integer format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic, except that the conversion is always rounded toward zero.
+| If `a' is a NaN, the largest positive integer is returned.  Otherwise, if
+| the conversion overflows, the largest integer with the same sign as `a' is
+| returned.
+*----------------------------------------------------------------------------*/
+
+int64 float128_to_int64_round_to_zero( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, shiftCount;
+    bits64 aSig0, aSig1;
+    int64 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp ) aSig0 |= LIT64( 0x0001000000000000 );
+    shiftCount = aExp - 0x402F;
+    if ( 0 < shiftCount ) {
+        if ( 0x403E <= aExp ) {
+            aSig0 &= LIT64( 0x0000FFFFFFFFFFFF );
+            if (    ( a.high == LIT64( 0xC03E000000000000 ) )
+                 && ( aSig1 < LIT64( 0x0002000000000000 ) ) ) {
+                if ( aSig1 ) STATUS(float_exception_flags) |= float_flag_inexact;
+            }
+            else {
+                float_raise( float_flag_invalid STATUS_VAR);
+                if ( ! aSign || ( ( aExp == 0x7FFF ) && ( aSig0 | aSig1 ) ) ) {
+                    return LIT64( 0x7FFFFFFFFFFFFFFF );
+                }
+            }
+            return (sbits64) LIT64( 0x8000000000000000 );
+        }
+        z = ( aSig0<<shiftCount ) | ( aSig1>>( ( - shiftCount ) & 63 ) );
+        if ( (bits64) ( aSig1<<shiftCount ) ) {
+            STATUS(float_exception_flags) |= float_flag_inexact;
+        }
+    }
+    else {
+        if ( aExp < 0x3FFF ) {
+            if ( aExp | aSig0 | aSig1 ) {
+                STATUS(float_exception_flags) |= float_flag_inexact;
+            }
+            return 0;
+        }
+        z = aSig0>>( - shiftCount );
+        if (    aSig1
+             || ( shiftCount && (bits64) ( aSig0<<( shiftCount & 63 ) ) ) ) {
+            STATUS(float_exception_flags) |= float_flag_inexact;
+        }
+    }
+    if ( aSign ) z = - z;
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the single-precision floating-point format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float32 float128_to_float32( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+    bits32 zSig;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) );
+        }
+        return packFloat32( aSign, 0xFF, 0 );
+    }
+    aSig0 |= ( aSig1 != 0 );
+    shift64RightJamming( aSig0, 18, &aSig0 );
+    zSig = aSig0;
+    if ( aExp || zSig ) {
+        zSig |= 0x40000000;
+        aExp -= 0x3F81;
+    }
+    return roundAndPackFloat32( aSign, aExp, zSig STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the double-precision floating-point format.  The conversion
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float64 float128_to_float64( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) );
+        }
+        return packFloat64( aSign, 0x7FF, 0 );
+    }
+    shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+    aSig0 |= ( aSig1 != 0 );
+    if ( aExp || aSig0 ) {
+        aSig0 |= LIT64( 0x4000000000000000 );
+        aExp -= 0x3C01;
+    }
+    return roundAndPackFloat64( aSign, aExp, aSig0 STATUS_VAR );
+
+}
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Returns the result of converting the quadruple-precision floating-point
+| value `a' to the extended double-precision floating-point format.  The
+| conversion is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+floatx80 float128_to_floatx80( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) {
+            return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) );
+        }
+        return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    else {
+        aSig0 |= LIT64( 0x0001000000000000 );
+    }
+    shortShift128Left( aSig0, aSig1, 15, &aSig0, &aSig1 );
+    return roundAndPackFloatx80( 80, aSign, aExp, aSig0, aSig1 STATUS_VAR );
+
+}
+
+#endif
+
+/*----------------------------------------------------------------------------
+| Rounds the quadruple-precision floating-point value `a' to an integer, and
+| returns the result as a quadruple-precision floating-point value.  The
+| operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_round_to_int( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 lastBitMask, roundBitsMask;
+    int8 roundingMode;
+    float128 z;
+
+    aExp = extractFloat128Exp( a );
+    if ( 0x402F <= aExp ) {
+        if ( 0x406F <= aExp ) {
+            if (    ( aExp == 0x7FFF )
+                 && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) )
+               ) {
+                return propagateFloat128NaN( a, a STATUS_VAR );
+            }
+            return a;
+        }
+        lastBitMask = 1;
+        lastBitMask = ( lastBitMask<<( 0x406E - aExp ) )<<1;
+        roundBitsMask = lastBitMask - 1;
+        z = a;
+        roundingMode = STATUS(float_rounding_mode);
+        if ( roundingMode == float_round_nearest_even ) {
+            if ( lastBitMask ) {
+                add128( z.high, z.low, 0, lastBitMask>>1, &z.high, &z.low );
+                if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
+            }
+            else {
+                if ( (sbits64) z.low < 0 ) {
+                    ++z.high;
+                    if ( (bits64) ( z.low<<1 ) == 0 ) z.high &= ~1;
+                }
+            }
+        }
+        else if ( roundingMode != float_round_to_zero ) {
+            if (   extractFloat128Sign( z )
+                 ^ ( roundingMode == float_round_up ) ) {
+                add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
+            }
+        }
+        z.low &= ~ roundBitsMask;
+    }
+    else {
+        if ( aExp < 0x3FFF ) {
+            if ( ( ( (bits64) ( a.high<<1 ) ) | a.low ) == 0 ) return a;
+            STATUS(float_exception_flags) |= float_flag_inexact;
+            aSign = extractFloat128Sign( a );
+            switch ( STATUS(float_rounding_mode) ) {
+             case float_round_nearest_even:
+                if (    ( aExp == 0x3FFE )
+                     && (   extractFloat128Frac0( a )
+                          | extractFloat128Frac1( a ) )
+                   ) {
+                    return packFloat128( aSign, 0x3FFF, 0, 0 );
+                }
+                break;
+             case float_round_down:
+                return
+                      aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
+                    : packFloat128( 0, 0, 0, 0 );
+             case float_round_up:
+                return
+                      aSign ? packFloat128( 1, 0, 0, 0 )
+                    : packFloat128( 0, 0x3FFF, 0, 0 );
+            }
+            return packFloat128( aSign, 0, 0, 0 );
+        }
+        lastBitMask = 1;
+        lastBitMask <<= 0x402F - aExp;
+        roundBitsMask = lastBitMask - 1;
+        z.low = 0;
+        z.high = a.high;
+        roundingMode = STATUS(float_rounding_mode);
+        if ( roundingMode == float_round_nearest_even ) {
+            z.high += lastBitMask>>1;
+            if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
+                z.high &= ~ lastBitMask;
+            }
+        }
+        else if ( roundingMode != float_round_to_zero ) {
+            if (   extractFloat128Sign( z )
+                 ^ ( roundingMode == float_round_up ) ) {
+                z.high |= ( a.low != 0 );
+                z.high += roundBitsMask;
+            }
+        }
+        z.high &= ~ roundBitsMask;
+    }
+    if ( ( z.low != a.low ) || ( z.high != a.high ) ) {
+        STATUS(float_exception_flags) |= float_flag_inexact;
+    }
+    return z;
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the absolute values of the quadruple-precision
+| floating-point values `a' and `b'.  If `zSign' is 1, the sum is negated
+| before being returned.  `zSign' is ignored if the result is a NaN.
+| The addition is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128 addFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM)
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+    int32 expDiff;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    expDiff = aExp - bExp;
+    if ( 0 < expDiff ) {
+        if ( aExp == 0x7FFF ) {
+            if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+            return a;
+        }
+        if ( bExp == 0 ) {
+            --expDiff;
+        }
+        else {
+            bSig0 |= LIT64( 0x0001000000000000 );
+        }
+        shift128ExtraRightJamming(
+            bSig0, bSig1, 0, expDiff, &bSig0, &bSig1, &zSig2 );
+        zExp = aExp;
+    }
+    else if ( expDiff < 0 ) {
+        if ( bExp == 0x7FFF ) {
+            if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        if ( aExp == 0 ) {
+            ++expDiff;
+        }
+        else {
+            aSig0 |= LIT64( 0x0001000000000000 );
+        }
+        shift128ExtraRightJamming(
+            aSig0, aSig1, 0, - expDiff, &aSig0, &aSig1, &zSig2 );
+        zExp = bExp;
+    }
+    else {
+        if ( aExp == 0x7FFF ) {
+            if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+                return propagateFloat128NaN( a, b STATUS_VAR );
+            }
+            return a;
+        }
+        add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+        if ( aExp == 0 ) return packFloat128( zSign, 0, zSig0, zSig1 );
+        zSig2 = 0;
+        zSig0 |= LIT64( 0x0002000000000000 );
+        zExp = aExp;
+        goto shiftRight1;
+    }
+    aSig0 |= LIT64( 0x0001000000000000 );
+    add128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+    --zExp;
+    if ( zSig0 < LIT64( 0x0002000000000000 ) ) goto roundAndPack;
+    ++zExp;
+ shiftRight1:
+    shift128ExtraRightJamming(
+        zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+ roundAndPack:
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the absolute values of the quadruple-
+| precision floating-point values `a' and `b'.  If `zSign' is 1, the
+| difference is negated before being returned.  `zSign' is ignored if the
+| result is a NaN.  The subtraction is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+static float128 subFloat128Sigs( float128 a, float128 b, flag zSign STATUS_PARAM)
+{
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1;
+    int32 expDiff;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    expDiff = aExp - bExp;
+    shortShift128Left( aSig0, aSig1, 14, &aSig0, &aSig1 );
+    shortShift128Left( bSig0, bSig1, 14, &bSig0, &bSig1 );
+    if ( 0 < expDiff ) goto aExpBigger;
+    if ( expDiff < 0 ) goto bExpBigger;
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 | bSig0 | bSig1 ) {
+            return propagateFloat128NaN( a, b STATUS_VAR );
+        }
+        float_raise( float_flag_invalid STATUS_VAR);
+        z.low = float128_default_nan_low;
+        z.high = float128_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        aExp = 1;
+        bExp = 1;
+    }
+    if ( bSig0 < aSig0 ) goto aBigger;
+    if ( aSig0 < bSig0 ) goto bBigger;
+    if ( bSig1 < aSig1 ) goto aBigger;
+    if ( aSig1 < bSig1 ) goto bBigger;
+    return packFloat128( STATUS(float_rounding_mode) == float_round_down, 0, 0, 0 );
+ bExpBigger:
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+        return packFloat128( zSign ^ 1, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        ++expDiff;
+    }
+    else {
+        aSig0 |= LIT64( 0x4000000000000000 );
+    }
+    shift128RightJamming( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+    bSig0 |= LIT64( 0x4000000000000000 );
+ bBigger:
+    sub128( bSig0, bSig1, aSig0, aSig1, &zSig0, &zSig1 );
+    zExp = bExp;
+    zSign ^= 1;
+    goto normalizeRoundAndPack;
+ aExpBigger:
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        --expDiff;
+    }
+    else {
+        bSig0 |= LIT64( 0x4000000000000000 );
+    }
+    shift128RightJamming( bSig0, bSig1, expDiff, &bSig0, &bSig1 );
+    aSig0 |= LIT64( 0x4000000000000000 );
+ aBigger:
+    sub128( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1 );
+    zExp = aExp;
+ normalizeRoundAndPack:
+    --zExp;
+    return normalizeRoundAndPackFloat128( zSign, zExp - 14, zSig0, zSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of adding the quadruple-precision floating-point values
+| `a' and `b'.  The operation is performed according to the IEC/IEEE Standard
+| for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_add( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign == bSign ) {
+        return addFloat128Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return subFloat128Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of subtracting the quadruple-precision floating-point
+| values `a' and `b'.  The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_sub( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign == bSign ) {
+        return subFloat128Sigs( a, b, aSign STATUS_VAR );
+    }
+    else {
+        return addFloat128Sigs( a, b, aSign STATUS_VAR );
+    }
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of multiplying the quadruple-precision floating-point
+| values `a' and `b'.  The operation is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_mul( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2, zSig3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if (    ( aSig0 | aSig1 )
+             || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+            return propagateFloat128NaN( a, b STATUS_VAR );
+        }
+        if ( ( bExp | bSig0 | bSig1 ) == 0 ) goto invalid;
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+        if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid STATUS_VAR);
+            z.low = float128_default_nan_low;
+            z.high = float128_default_nan_high;
+            return z;
+        }
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    zExp = aExp + bExp - 0x4000;
+    aSig0 |= LIT64( 0x0001000000000000 );
+    shortShift128Left( bSig0, bSig1, 16, &bSig0, &bSig1 );
+    mul128To256( aSig0, aSig1, bSig0, bSig1, &zSig0, &zSig1, &zSig2, &zSig3 );
+    add128( zSig0, zSig1, aSig0, aSig1, &zSig0, &zSig1 );
+    zSig2 |= ( zSig3 != 0 );
+    if ( LIT64( 0x0002000000000000 ) <= zSig0 ) {
+        shift128ExtraRightJamming(
+            zSig0, zSig1, zSig2, 1, &zSig0, &zSig1, &zSig2 );
+        ++zExp;
+    }
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the result of dividing the quadruple-precision floating-point value
+| `a' by the corresponding value `b'.  The operation is performed according to
+| the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_div( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, zExp;
+    bits64 aSig0, aSig1, bSig0, bSig1, zSig0, zSig1, zSig2;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    zSign = aSign ^ bSign;
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+        if ( bExp == 0x7FFF ) {
+            if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+            goto invalid;
+        }
+        return packFloat128( zSign, 0x7FFF, 0, 0 );
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+        return packFloat128( zSign, 0, 0, 0 );
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) {
+            if ( ( aExp | aSig0 | aSig1 ) == 0 ) {
+ invalid:
+                float_raise( float_flag_invalid STATUS_VAR);
+                z.low = float128_default_nan_low;
+                z.high = float128_default_nan_high;
+                return z;
+            }
+            float_raise( float_flag_divbyzero STATUS_VAR);
+            return packFloat128( zSign, 0x7FFF, 0, 0 );
+        }
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( zSign, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    zExp = aExp - bExp + 0x3FFD;
+    shortShift128Left(
+        aSig0 | LIT64( 0x0001000000000000 ), aSig1, 15, &aSig0, &aSig1 );
+    shortShift128Left(
+        bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+    if ( le128( bSig0, bSig1, aSig0, aSig1 ) ) {
+        shift128Right( aSig0, aSig1, 1, &aSig0, &aSig1 );
+        ++zExp;
+    }
+    zSig0 = estimateDiv128To64( aSig0, aSig1, bSig0 );
+    mul128By64To192( bSig0, bSig1, zSig0, &term0, &term1, &term2 );
+    sub192( aSig0, aSig1, 0, term0, term1, term2, &rem0, &rem1, &rem2 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        add192( rem0, rem1, rem2, 0, bSig0, bSig1, &rem0, &rem1, &rem2 );
+    }
+    zSig1 = estimateDiv128To64( rem1, rem2, bSig0 );
+    if ( ( zSig1 & 0x3FFF ) <= 4 ) {
+        mul128By64To192( bSig0, bSig1, zSig1, &term1, &term2, &term3 );
+        sub192( rem1, rem2, 0, term1, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            add192( rem1, rem2, rem3, 0, bSig0, bSig1, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shift128ExtraRightJamming( zSig0, zSig1, 0, 15, &zSig0, &zSig1, &zSig2 );
+    return roundAndPackFloat128( zSign, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the remainder of the quadruple-precision floating-point value `a'
+| with respect to the corresponding value `b'.  The operation is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_rem( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign, zSign;
+    int32 aExp, bExp, expDiff;
+    bits64 aSig0, aSig1, bSig0, bSig1, q, term0, term1, term2;
+    bits64 allZero, alternateASig0, alternateASig1, sigMean1;
+    sbits64 sigMean0;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    bSig1 = extractFloat128Frac1( b );
+    bSig0 = extractFloat128Frac0( b );
+    bExp = extractFloat128Exp( b );
+    bSign = extractFloat128Sign( b );
+    if ( aExp == 0x7FFF ) {
+        if (    ( aSig0 | aSig1 )
+             || ( ( bExp == 0x7FFF ) && ( bSig0 | bSig1 ) ) ) {
+            return propagateFloat128NaN( a, b STATUS_VAR );
+        }
+        goto invalid;
+    }
+    if ( bExp == 0x7FFF ) {
+        if ( bSig0 | bSig1 ) return propagateFloat128NaN( a, b STATUS_VAR );
+        return a;
+    }
+    if ( bExp == 0 ) {
+        if ( ( bSig0 | bSig1 ) == 0 ) {
+ invalid:
+            float_raise( float_flag_invalid STATUS_VAR);
+            z.low = float128_default_nan_low;
+            z.high = float128_default_nan_high;
+            return z;
+        }
+        normalizeFloat128Subnormal( bSig0, bSig1, &bExp, &bSig0, &bSig1 );
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return a;
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    expDiff = aExp - bExp;
+    if ( expDiff < -1 ) return a;
+    shortShift128Left(
+        aSig0 | LIT64( 0x0001000000000000 ),
+        aSig1,
+        15 - ( expDiff < 0 ),
+        &aSig0,
+        &aSig1
+    );
+    shortShift128Left(
+        bSig0 | LIT64( 0x0001000000000000 ), bSig1, 15, &bSig0, &bSig1 );
+    q = le128( bSig0, bSig1, aSig0, aSig1 );
+    if ( q ) sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+    expDiff -= 64;
+    while ( 0 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+        q = ( 4 < q ) ? q - 4 : 0;
+        mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+        shortShift192Left( term0, term1, term2, 61, &term1, &term2, &allZero );
+        shortShift128Left( aSig0, aSig1, 61, &aSig0, &allZero );
+        sub128( aSig0, 0, term1, term2, &aSig0, &aSig1 );
+        expDiff -= 61;
+    }
+    if ( -64 < expDiff ) {
+        q = estimateDiv128To64( aSig0, aSig1, bSig0 );
+        q = ( 4 < q ) ? q - 4 : 0;
+        q >>= - expDiff;
+        shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+        expDiff += 52;
+        if ( expDiff < 0 ) {
+            shift128Right( aSig0, aSig1, - expDiff, &aSig0, &aSig1 );
+        }
+        else {
+            shortShift128Left( aSig0, aSig1, expDiff, &aSig0, &aSig1 );
+        }
+        mul128By64To192( bSig0, bSig1, q, &term0, &term1, &term2 );
+        sub128( aSig0, aSig1, term1, term2, &aSig0, &aSig1 );
+    }
+    else {
+        shift128Right( aSig0, aSig1, 12, &aSig0, &aSig1 );
+        shift128Right( bSig0, bSig1, 12, &bSig0, &bSig1 );
+    }
+    do {
+        alternateASig0 = aSig0;
+        alternateASig1 = aSig1;
+        ++q;
+        sub128( aSig0, aSig1, bSig0, bSig1, &aSig0, &aSig1 );
+    } while ( 0 <= (sbits64) aSig0 );
+    add128(
+        aSig0, aSig1, alternateASig0, alternateASig1, &sigMean0, &sigMean1 );
+    if (    ( sigMean0 < 0 )
+         || ( ( ( sigMean0 | sigMean1 ) == 0 ) && ( q & 1 ) ) ) {
+        aSig0 = alternateASig0;
+        aSig1 = alternateASig1;
+    }
+    zSign = ( (sbits64) aSig0 < 0 );
+    if ( zSign ) sub128( 0, 0, aSig0, aSig1, &aSig0, &aSig1 );
+    return
+        normalizeRoundAndPackFloat128( aSign ^ zSign, bExp - 4, aSig0, aSig1 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns the square root of the quadruple-precision floating-point value `a'.
+| The operation is performed according to the IEC/IEEE Standard for Binary
+| Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+float128 float128_sqrt( float128 a STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp, zExp;
+    bits64 aSig0, aSig1, zSig0, zSig1, zSig2, doubleZSig0;
+    bits64 rem0, rem1, rem2, rem3, term0, term1, term2, term3;
+    float128 z;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        if ( aSig0 | aSig1 ) return propagateFloat128NaN( a, a STATUS_VAR );
+        if ( ! aSign ) return a;
+        goto invalid;
+    }
+    if ( aSign ) {
+        if ( ( aExp | aSig0 | aSig1 ) == 0 ) return a;
+ invalid:
+        float_raise( float_flag_invalid STATUS_VAR);
+        z.low = float128_default_nan_low;
+        z.high = float128_default_nan_high;
+        return z;
+    }
+    if ( aExp == 0 ) {
+        if ( ( aSig0 | aSig1 ) == 0 ) return packFloat128( 0, 0, 0, 0 );
+        normalizeFloat128Subnormal( aSig0, aSig1, &aExp, &aSig0, &aSig1 );
+    }
+    zExp = ( ( aExp - 0x3FFF )>>1 ) + 0x3FFE;
+    aSig0 |= LIT64( 0x0001000000000000 );
+    zSig0 = estimateSqrt32( aExp, aSig0>>17 );
+    shortShift128Left( aSig0, aSig1, 13 - ( aExp & 1 ), &aSig0, &aSig1 );
+    zSig0 = estimateDiv128To64( aSig0, aSig1, zSig0<<32 ) + ( zSig0<<30 );
+    doubleZSig0 = zSig0<<1;
+    mul64To128( zSig0, zSig0, &term0, &term1 );
+    sub128( aSig0, aSig1, term0, term1, &rem0, &rem1 );
+    while ( (sbits64) rem0 < 0 ) {
+        --zSig0;
+        doubleZSig0 -= 2;
+        add128( rem0, rem1, zSig0>>63, doubleZSig0 | 1, &rem0, &rem1 );
+    }
+    zSig1 = estimateDiv128To64( rem1, 0, doubleZSig0 );
+    if ( ( zSig1 & 0x1FFF ) <= 5 ) {
+        if ( zSig1 == 0 ) zSig1 = 1;
+        mul64To128( doubleZSig0, zSig1, &term1, &term2 );
+        sub128( rem1, 0, term1, term2, &rem1, &rem2 );
+        mul64To128( zSig1, zSig1, &term2, &term3 );
+        sub192( rem1, rem2, 0, 0, term2, term3, &rem1, &rem2, &rem3 );
+        while ( (sbits64) rem1 < 0 ) {
+            --zSig1;
+            shortShift128Left( 0, zSig1, 1, &term2, &term3 );
+            term3 |= 1;
+            term2 |= doubleZSig0;
+            add192( rem1, rem2, rem3, 0, term2, term3, &rem1, &rem2, &rem3 );
+        }
+        zSig1 |= ( ( rem1 | rem2 | rem3 ) != 0 );
+    }
+    shift128ExtraRightJamming( zSig0, zSig1, 0, 14, &zSig0, &zSig1, &zSig2 );
+    return roundAndPackFloat128( 0, zExp, zSig0, zSig1, zSig2 STATUS_VAR );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise.  The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_eq( float128 a, float128 b STATUS_PARAM )
+{
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise.  The comparison
+| is performed according to the IEC/IEEE Standard for Binary Floating-Point
+| Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_le( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise.  The comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_lt( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is equal to
+| the corresponding value `b', and 0 otherwise.  The invalid exception is
+| raised if either operand is a NaN.  Otherwise, the comparison is performed
+| according to the IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_eq_signaling( float128 a, float128 b STATUS_PARAM )
+{
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        float_raise( float_flag_invalid STATUS_VAR);
+        return 0;
+    }
+    return
+           ( a.low == b.low )
+        && (    ( a.high == b.high )
+             || (    ( a.low == 0 )
+                  && ( (bits64) ( ( a.high | b.high )<<1 ) == 0 ) )
+           );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| or equal to the corresponding value `b', and 0 otherwise.  Quiet NaNs do not
+| cause an exception.  Otherwise, the comparison is performed according to the
+| IEC/IEEE Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_le_quiet( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            || (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 == 0 );
+    }
+    return
+          aSign ? le128( b.high, b.low, a.high, a.low )
+        : le128( a.high, a.low, b.high, b.low );
+
+}
+
+/*----------------------------------------------------------------------------
+| Returns 1 if the quadruple-precision floating-point value `a' is less than
+| the corresponding value `b', and 0 otherwise.  Quiet NaNs do not cause an
+| exception.  Otherwise, the comparison is performed according to the IEC/IEEE
+| Standard for Binary Floating-Point Arithmetic.
+*----------------------------------------------------------------------------*/
+
+int float128_lt_quiet( float128 a, float128 b STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (    (    ( extractFloat128Exp( a ) == 0x7FFF )
+              && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) )
+         || (    ( extractFloat128Exp( b ) == 0x7FFF )
+              && ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )
+       ) {
+        if (    float128_is_signaling_nan( a )
+             || float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return 0;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        return
+               aSign
+            && (    ( ( (bits64) ( ( a.high | b.high )<<1 ) ) | a.low | b.low )
+                 != 0 );
+    }
+    return
+          aSign ? lt128( b.high, b.low, a.high, a.low )
+        : lt128( a.high, a.low, b.high, b.low );
+
+}
+
+#endif
+
+/* misc functions */
+float32 uint32_to_float32( unsigned int a STATUS_PARAM )
+{
+    return int64_to_float32(a STATUS_VAR);
+}
+
+float64 uint32_to_float64( unsigned int a STATUS_PARAM )
+{
+    return int64_to_float64(a STATUS_VAR);
+}
+
+unsigned int float32_to_uint32( float32 a STATUS_PARAM )
+{
+    int64_t v;
+    unsigned int res;
+
+    v = float32_to_int64(a STATUS_VAR);
+    if (v < 0) {
+        res = 0;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else {
+        res = v;
+    }
+    return res;
+}
+
+unsigned int float32_to_uint32_round_to_zero( float32 a STATUS_PARAM )
+{
+    int64_t v;
+    unsigned int res;
+
+    v = float32_to_int64_round_to_zero(a STATUS_VAR);
+    if (v < 0) {
+        res = 0;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else {
+        res = v;
+    }
+    return res;
+}
+
+unsigned int float64_to_uint32( float64 a STATUS_PARAM )
+{
+    int64_t v;
+    unsigned int res;
+
+    v = float64_to_int64(a STATUS_VAR);
+    if (v < 0) {
+        res = 0;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else {
+        res = v;
+    }
+    return res;
+}
+
+unsigned int float64_to_uint32_round_to_zero( float64 a STATUS_PARAM )
+{
+    int64_t v;
+    unsigned int res;
+
+    v = float64_to_int64_round_to_zero(a STATUS_VAR);
+    if (v < 0) {
+        res = 0;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else if (v > 0xffffffff) {
+        res = 0xffffffff;
+        float_raise( float_flag_invalid STATUS_VAR);
+    } else {
+        res = v;
+    }
+    return res;
+}
+
+/* FIXME: This looks broken.  */
+uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
+{
+    int64_t v;
+
+    v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
+    v += float64_val(a);
+    v = float64_to_int64(make_float64(v) STATUS_VAR);
+
+    return v - INT64_MIN;
+}
+
+uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
+{
+    int64_t v;
+
+    v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
+    v += float64_val(a);
+    v = float64_to_int64_round_to_zero(make_float64(v) STATUS_VAR);
+
+    return v - INT64_MIN;
+}
+
+#define COMPARE(s, nan_exp)                                                  \
+INLINE int float ## s ## _compare_internal( float ## s a, float ## s b,      \
+                                      int is_quiet STATUS_PARAM )            \
+{                                                                            \
+    flag aSign, bSign;                                                       \
+    bits ## s av, bv;                                                        \
+                                                                             \
+    if (( ( extractFloat ## s ## Exp( a ) == nan_exp ) &&                    \
+         extractFloat ## s ## Frac( a ) ) ||                                 \
+        ( ( extractFloat ## s ## Exp( b ) == nan_exp ) &&                    \
+          extractFloat ## s ## Frac( b ) )) {                                \
+        if (!is_quiet ||                                                     \
+            float ## s ## _is_signaling_nan( a ) ||                          \
+            float ## s ## _is_signaling_nan( b ) ) {                         \
+            float_raise( float_flag_invalid STATUS_VAR);                     \
+        }                                                                    \
+        return float_relation_unordered;                                     \
+    }                                                                        \
+    aSign = extractFloat ## s ## Sign( a );                                  \
+    bSign = extractFloat ## s ## Sign( b );                                  \
+    av = float ## s ## _val(a);                                              \
+    bv = float ## s ## _val(b);                                              \
+    if ( aSign != bSign ) {                                                  \
+        if ( (bits ## s) ( ( av | bv )<<1 ) == 0 ) {                         \
+            /* zero case */                                                  \
+            return float_relation_equal;                                     \
+        } else {                                                             \
+            return 1 - (2 * aSign);                                          \
+        }                                                                    \
+    } else {                                                                 \
+        if (av == bv) {                                                      \
+            return float_relation_equal;                                     \
+        } else {                                                             \
+            return 1 - 2 * (aSign ^ ( av < bv ));                            \
+        }                                                                    \
+    }                                                                        \
+}                                                                            \
+                                                                             \
+int float ## s ## _compare( float ## s a, float ## s b STATUS_PARAM )        \
+{                                                                            \
+    return float ## s ## _compare_internal(a, b, 0 STATUS_VAR);              \
+}                                                                            \
+                                                                             \
+int float ## s ## _compare_quiet( float ## s a, float ## s b STATUS_PARAM )  \
+{                                                                            \
+    return float ## s ## _compare_internal(a, b, 1 STATUS_VAR);              \
+}
+
+COMPARE(32, 0xff)
+COMPARE(64, 0x7ff)
+
+INLINE int float128_compare_internal( float128 a, float128 b,
+                                      int is_quiet STATUS_PARAM )
+{
+    flag aSign, bSign;
+
+    if (( ( extractFloat128Exp( a ) == 0x7fff ) &&
+          ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) ) ||
+        ( ( extractFloat128Exp( b ) == 0x7fff ) &&
+          ( extractFloat128Frac0( b ) | extractFloat128Frac1( b ) ) )) {
+        if (!is_quiet ||
+            float128_is_signaling_nan( a ) ||
+            float128_is_signaling_nan( b ) ) {
+            float_raise( float_flag_invalid STATUS_VAR);
+        }
+        return float_relation_unordered;
+    }
+    aSign = extractFloat128Sign( a );
+    bSign = extractFloat128Sign( b );
+    if ( aSign != bSign ) {
+        if ( ( ( ( a.high | b.high )<<1 ) | a.low | b.low ) == 0 ) {
+            /* zero case */
+            return float_relation_equal;
+        } else {
+            return 1 - (2 * aSign);
+        }
+    } else {
+        if (a.low == b.low && a.high == b.high) {
+            return float_relation_equal;
+        } else {
+            return 1 - 2 * (aSign ^ ( lt128( a.high, a.low, b.high, b.low ) ));
+        }
+    }
+}
+
+int float128_compare( float128 a, float128 b STATUS_PARAM )
+{
+    return float128_compare_internal(a, b, 0 STATUS_VAR);
+}
+
+int float128_compare_quiet( float128 a, float128 b STATUS_PARAM )
+{
+    return float128_compare_internal(a, b, 1 STATUS_VAR);
+}
+
+/* Multiply A by 2 raised to the power N.  */
+float32 float32_scalbn( float32 a, int n STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits32 aSig;
+
+    aSig = extractFloat32Frac( a );
+    aExp = extractFloat32Exp( a );
+    aSign = extractFloat32Sign( a );
+
+    if ( aExp == 0xFF ) {
+        return a;
+    }
+    aExp += n;
+    return roundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
+}
+
+float64 float64_scalbn( float64 a, int n STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+
+    aSig = extractFloat64Frac( a );
+    aExp = extractFloat64Exp( a );
+    aSign = extractFloat64Sign( a );
+
+    if ( aExp == 0x7FF ) {
+        return a;
+    }
+    aExp += n;
+    return roundAndPackFloat64( aSign, aExp, aSig STATUS_VAR );
+}
+
+#ifdef FLOATX80
+floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
+{
+    flag aSign;
+    int16 aExp;
+    bits64 aSig;
+
+    aSig = extractFloatx80Frac( a );
+    aExp = extractFloatx80Exp( a );
+    aSign = extractFloatx80Sign( a );
+
+    if ( aExp == 0x7FF ) {
+        return a;
+    }
+    aExp += n;
+    return roundAndPackFloatx80( STATUS(floatx80_rounding_precision),
+                                 aSign, aExp, aSig, 0 STATUS_VAR );
+}
+#endif
+
+#ifdef FLOAT128
+float128 float128_scalbn( float128 a, int n STATUS_PARAM )
+{
+    flag aSign;
+    int32 aExp;
+    bits64 aSig0, aSig1;
+
+    aSig1 = extractFloat128Frac1( a );
+    aSig0 = extractFloat128Frac0( a );
+    aExp = extractFloat128Exp( a );
+    aSign = extractFloat128Sign( a );
+    if ( aExp == 0x7FFF ) {
+        return a;
+    }
+    aExp += n;
+    return roundAndPackFloat128( aSign, aExp, aSig0, aSig1, 0 STATUS_VAR );
+
+}
+#endif
diff --git a/fpu/softfloat.h b/fpu/softfloat.h
new file mode 100644
index 0000000..5f95d06
--- /dev/null
+++ b/fpu/softfloat.h
@@ -0,0 +1,444 @@
+/*============================================================================
+
+This C header file is part of the SoftFloat IEC/IEEE Floating-point Arithmetic
+Package, Release 2b.
+
+Written by John R. Hauser.  This work was made possible in part by the
+International Computer Science Institute, located at Suite 600, 1947 Center
+Street, Berkeley, California 94704.  Funding was partially provided by the
+National Science Foundation under grant MIP-9311980.  The original version
+of this code was written as part of a project to build a fixed-point vector
+processor in collaboration with the University of California at Berkeley,
+overseen by Profs. Nelson Morgan and John Wawrzynek.  More information
+is available through the Web page `http://www.cs.berkeley.edu/~jhauser/
+arithmetic/SoftFloat.html'.
+
+THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort has
+been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
+RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
+AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
+COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
+EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
+INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
+OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.
+
+Derivative works are acceptable, even for commercial purposes, so long as
+(1) the source code for the derivative work includes prominent notice that
+the work is derivative, and (2) the source code includes prominent notice with
+these four paragraphs for those parts of this code that are retained.
+
+=============================================================================*/
+
+#ifndef SOFTFLOAT_H
+#define SOFTFLOAT_H
+
+#if defined(HOST_SOLARIS) && defined(NEEDS_LIBSUNMATH)
+#include <sunmath.h>
+#endif
+
+#include <inttypes.h>
+#include "config.h"
+
+/*----------------------------------------------------------------------------
+| Each of the following `typedef's defines the most convenient type that holds
+| integers of at least as many bits as specified.  For example, `uint8' should
+| be the most convenient type that can hold unsigned integers of as many as
+| 8 bits.  The `flag' type must be able to hold either a 0 or 1.  For most
+| implementations of C, `flag', `uint8', and `int8' should all be `typedef'ed
+| to the same as `int'.
+*----------------------------------------------------------------------------*/
+typedef uint8_t flag;
+typedef uint8_t uint8;
+typedef int8_t int8;
+typedef int uint16;
+typedef int int16;
+typedef unsigned int uint32;
+typedef signed int int32;
+typedef uint64_t uint64;
+typedef int64_t int64;
+
+/*----------------------------------------------------------------------------
+| Each of the following `typedef's defines a type that holds integers
+| of _exactly_ the number of bits specified.  For instance, for most
+| implementation of C, `bits16' and `sbits16' should be `typedef'ed to
+| `unsigned short int' and `signed short int' (or `short int'), respectively.
+*----------------------------------------------------------------------------*/
+typedef uint8_t bits8;
+typedef int8_t sbits8;
+typedef uint16_t bits16;
+typedef int16_t sbits16;
+typedef uint32_t bits32;
+typedef int32_t sbits32;
+typedef uint64_t bits64;
+typedef int64_t sbits64;
+
+#define LIT64( a ) a##LL
+#define INLINE static inline
+
+/*----------------------------------------------------------------------------
+| The macro `FLOATX80' must be defined to enable the extended double-precision
+| floating-point format `floatx80'.  If this macro is not defined, the
+| `floatx80' type will not be defined, and none of the functions that either
+| input or output the `floatx80' type will be defined.  The same applies to
+| the `FLOAT128' macro and the quadruple-precision format `float128'.
+*----------------------------------------------------------------------------*/
+#ifdef CONFIG_SOFTFLOAT
+/* bit exact soft float support */
+#define FLOATX80
+#define FLOAT128
+#else
+/* native float support */
+#if (defined(__i386__) || defined(__x86_64__)) && !defined(_BSD)
+#define FLOATX80
+#endif
+#endif /* !CONFIG_SOFTFLOAT */
+
+#define STATUS_PARAM , float_status *status
+#define STATUS(field) status->field
+#define STATUS_VAR , status
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point ordering relations
+*----------------------------------------------------------------------------*/
+enum {
+    float_relation_less      = -1,
+    float_relation_equal     =  0,
+    float_relation_greater   =  1,
+    float_relation_unordered =  2
+};
+
+#ifdef CONFIG_SOFTFLOAT
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point types.
+*----------------------------------------------------------------------------*/
+/* Use structures for soft-float types.  This prevents accidentally mixing
+   them with native int/float types.  A sufficiently clever compiler and
+   sane ABI should be able to see though these structs.  However
+   x86/gcc 3.x seems to struggle a bit, so leave them disabled by default.  */
+//#define USE_SOFTFLOAT_STRUCT_TYPES
+#ifdef USE_SOFTFLOAT_STRUCT_TYPES
+typedef struct {
+    uint32_t v;
+} float32;
+/* The cast ensures an error if the wrong type is passed.  */
+#define float32_val(x) (((float32)(x)).v)
+#define make_float32(x) __extension__ ({ float32 f32_val = {x}; f32_val; })
+typedef struct {
+    uint64_t v;
+} float64;
+#define float64_val(x) (((float64)(x)).v)
+#define make_float64(x) __extension__ ({ float64 f64_val = {x}; f64_val; })
+#else
+typedef uint32_t float32;
+typedef uint64_t float64;
+#define float32_val(x) (x)
+#define float64_val(x) (x)
+#define make_float32(x) (x)
+#define make_float64(x) (x)
+#endif
+#ifdef FLOATX80
+typedef struct {
+    uint64_t low;
+    uint16_t high;
+} floatx80;
+#endif
+#ifdef FLOAT128
+typedef struct {
+#ifdef WORDS_BIGENDIAN
+    uint64_t high, low;
+#else
+    uint64_t low, high;
+#endif
+} float128;
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point underflow tininess-detection mode.
+*----------------------------------------------------------------------------*/
+enum {
+    float_tininess_after_rounding  = 0,
+    float_tininess_before_rounding = 1
+};
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point rounding mode.
+*----------------------------------------------------------------------------*/
+enum {
+    float_round_nearest_even = 0,
+    float_round_down         = 1,
+    float_round_up           = 2,
+    float_round_to_zero      = 3
+};
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE floating-point exception flags.
+*----------------------------------------------------------------------------*/
+enum {
+    float_flag_invalid   =  1,
+    float_flag_divbyzero =  4,
+    float_flag_overflow  =  8,
+    float_flag_underflow = 16,
+    float_flag_inexact   = 32
+};
+
+typedef struct float_status {
+    signed char float_detect_tininess;
+    signed char float_rounding_mode;
+    signed char float_exception_flags;
+#ifdef FLOATX80
+    signed char floatx80_rounding_precision;
+#endif
+} float_status;
+
+void set_float_rounding_mode(int val STATUS_PARAM);
+void set_float_exception_flags(int val STATUS_PARAM);
+INLINE int get_float_exception_flags(float_status *status)
+{
+    return STATUS(float_exception_flags);
+}
+#ifdef FLOATX80
+void set_floatx80_rounding_precision(int val STATUS_PARAM);
+#endif
+
+/*----------------------------------------------------------------------------
+| Routine to raise any or all of the software IEC/IEEE floating-point
+| exception flags.
+*----------------------------------------------------------------------------*/
+void float_raise( int8 flags STATUS_PARAM);
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE integer-to-floating-point conversion routines.
+*----------------------------------------------------------------------------*/
+float32 int32_to_float32( int STATUS_PARAM );
+float64 int32_to_float64( int STATUS_PARAM );
+float32 uint32_to_float32( unsigned int STATUS_PARAM );
+float64 uint32_to_float64( unsigned int STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 int32_to_floatx80( int STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 int32_to_float128( int STATUS_PARAM );
+#endif
+float32 int64_to_float32( int64_t STATUS_PARAM );
+float32 uint64_to_float32( uint64_t STATUS_PARAM );
+float64 int64_to_float64( int64_t STATUS_PARAM );
+float64 uint64_to_float64( uint64_t STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 int64_to_floatx80( int64_t STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 int64_to_float128( int64_t STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float32_to_int32( float32 STATUS_PARAM );
+int float32_to_int32_round_to_zero( float32 STATUS_PARAM );
+unsigned int float32_to_uint32( float32 STATUS_PARAM );
+unsigned int float32_to_uint32_round_to_zero( float32 STATUS_PARAM );
+int64_t float32_to_int64( float32 STATUS_PARAM );
+int64_t float32_to_int64_round_to_zero( float32 STATUS_PARAM );
+float64 float32_to_float64( float32 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float32_to_floatx80( float32 STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 float32_to_float128( float32 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE single-precision operations.
+*----------------------------------------------------------------------------*/
+float32 float32_round_to_int( float32 STATUS_PARAM );
+float32 float32_add( float32, float32 STATUS_PARAM );
+float32 float32_sub( float32, float32 STATUS_PARAM );
+float32 float32_mul( float32, float32 STATUS_PARAM );
+float32 float32_div( float32, float32 STATUS_PARAM );
+float32 float32_rem( float32, float32 STATUS_PARAM );
+float32 float32_sqrt( float32 STATUS_PARAM );
+int float32_eq( float32, float32 STATUS_PARAM );
+int float32_le( float32, float32 STATUS_PARAM );
+int float32_lt( float32, float32 STATUS_PARAM );
+int float32_eq_signaling( float32, float32 STATUS_PARAM );
+int float32_le_quiet( float32, float32 STATUS_PARAM );
+int float32_lt_quiet( float32, float32 STATUS_PARAM );
+int float32_compare( float32, float32 STATUS_PARAM );
+int float32_compare_quiet( float32, float32 STATUS_PARAM );
+int float32_is_nan( float32 );
+int float32_is_signaling_nan( float32 );
+float32 float32_scalbn( float32, int STATUS_PARAM );
+
+INLINE float32 float32_abs(float32 a)
+{
+    return make_float32(float32_val(a) & 0x7fffffff);
+}
+
+INLINE float32 float32_chs(float32 a)
+{
+    return make_float32(float32_val(a) ^ 0x80000000);
+}
+
+#define float32_zero make_float32(0)
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float64_to_int32( float64 STATUS_PARAM );
+int float64_to_int32_round_to_zero( float64 STATUS_PARAM );
+unsigned int float64_to_uint32( float64 STATUS_PARAM );
+unsigned int float64_to_uint32_round_to_zero( float64 STATUS_PARAM );
+int64_t float64_to_int64( float64 STATUS_PARAM );
+int64_t float64_to_int64_round_to_zero( float64 STATUS_PARAM );
+uint64_t float64_to_uint64 (float64 a STATUS_PARAM);
+uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM);
+float32 float64_to_float32( float64 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float64_to_floatx80( float64 STATUS_PARAM );
+#endif
+#ifdef FLOAT128
+float128 float64_to_float128( float64 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE double-precision operations.
+*----------------------------------------------------------------------------*/
+float64 float64_round_to_int( float64 STATUS_PARAM );
+float64 float64_trunc_to_int( float64 STATUS_PARAM );
+float64 float64_add( float64, float64 STATUS_PARAM );
+float64 float64_sub( float64, float64 STATUS_PARAM );
+float64 float64_mul( float64, float64 STATUS_PARAM );
+float64 float64_div( float64, float64 STATUS_PARAM );
+float64 float64_rem( float64, float64 STATUS_PARAM );
+float64 float64_sqrt( float64 STATUS_PARAM );
+int float64_eq( float64, float64 STATUS_PARAM );
+int float64_le( float64, float64 STATUS_PARAM );
+int float64_lt( float64, float64 STATUS_PARAM );
+int float64_eq_signaling( float64, float64 STATUS_PARAM );
+int float64_le_quiet( float64, float64 STATUS_PARAM );
+int float64_lt_quiet( float64, float64 STATUS_PARAM );
+int float64_compare( float64, float64 STATUS_PARAM );
+int float64_compare_quiet( float64, float64 STATUS_PARAM );
+int float64_is_nan( float64 a );
+int float64_is_signaling_nan( float64 );
+float64 float64_scalbn( float64, int STATUS_PARAM );
+
+INLINE float64 float64_abs(float64 a)
+{
+    return make_float64(float64_val(a) & 0x7fffffffffffffffLL);
+}
+
+INLINE float64 float64_chs(float64 a)
+{
+    return make_float64(float64_val(a) ^ 0x8000000000000000LL);
+}
+
+#define float64_zero make_float64(0)
+
+#ifdef FLOATX80
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int floatx80_to_int32( floatx80 STATUS_PARAM );
+int floatx80_to_int32_round_to_zero( floatx80 STATUS_PARAM );
+int64_t floatx80_to_int64( floatx80 STATUS_PARAM );
+int64_t floatx80_to_int64_round_to_zero( floatx80 STATUS_PARAM );
+float32 floatx80_to_float32( floatx80 STATUS_PARAM );
+float64 floatx80_to_float64( floatx80 STATUS_PARAM );
+#ifdef FLOAT128
+float128 floatx80_to_float128( floatx80 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE extended double-precision operations.
+*----------------------------------------------------------------------------*/
+floatx80 floatx80_round_to_int( floatx80 STATUS_PARAM );
+floatx80 floatx80_add( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sub( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_mul( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_div( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_rem( floatx80, floatx80 STATUS_PARAM );
+floatx80 floatx80_sqrt( floatx80 STATUS_PARAM );
+int floatx80_eq( floatx80, floatx80 STATUS_PARAM );
+int floatx80_le( floatx80, floatx80 STATUS_PARAM );
+int floatx80_lt( floatx80, floatx80 STATUS_PARAM );
+int floatx80_eq_signaling( floatx80, floatx80 STATUS_PARAM );
+int floatx80_le_quiet( floatx80, floatx80 STATUS_PARAM );
+int floatx80_lt_quiet( floatx80, floatx80 STATUS_PARAM );
+int floatx80_is_nan( floatx80 );
+int floatx80_is_signaling_nan( floatx80 );
+floatx80 floatx80_scalbn( floatx80, int STATUS_PARAM );
+
+INLINE floatx80 floatx80_abs(floatx80 a)
+{
+    a.high &= 0x7fff;
+    return a;
+}
+
+INLINE floatx80 floatx80_chs(floatx80 a)
+{
+    a.high ^= 0x8000;
+    return a;
+}
+
+#endif
+
+#ifdef FLOAT128
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE quadruple-precision conversion routines.
+*----------------------------------------------------------------------------*/
+int float128_to_int32( float128 STATUS_PARAM );
+int float128_to_int32_round_to_zero( float128 STATUS_PARAM );
+int64_t float128_to_int64( float128 STATUS_PARAM );
+int64_t float128_to_int64_round_to_zero( float128 STATUS_PARAM );
+float32 float128_to_float32( float128 STATUS_PARAM );
+float64 float128_to_float64( float128 STATUS_PARAM );
+#ifdef FLOATX80
+floatx80 float128_to_floatx80( float128 STATUS_PARAM );
+#endif
+
+/*----------------------------------------------------------------------------
+| Software IEC/IEEE quadruple-precision operations.
+*----------------------------------------------------------------------------*/
+float128 float128_round_to_int( float128 STATUS_PARAM );
+float128 float128_add( float128, float128 STATUS_PARAM );
+float128 float128_sub( float128, float128 STATUS_PARAM );
+float128 float128_mul( float128, float128 STATUS_PARAM );
+float128 float128_div( float128, float128 STATUS_PARAM );
+float128 float128_rem( float128, float128 STATUS_PARAM );
+float128 float128_sqrt( float128 STATUS_PARAM );
+int float128_eq( float128, float128 STATUS_PARAM );
+int float128_le( float128, float128 STATUS_PARAM );
+int float128_lt( float128, float128 STATUS_PARAM );
+int float128_eq_signaling( float128, float128 STATUS_PARAM );
+int float128_le_quiet( float128, float128 STATUS_PARAM );
+int float128_lt_quiet( float128, float128 STATUS_PARAM );
+int float128_compare( float128, float128 STATUS_PARAM );
+int float128_compare_quiet( float128, float128 STATUS_PARAM );
+int float128_is_nan( float128 );
+int float128_is_signaling_nan( float128 );
+float128 float128_scalbn( float128, int STATUS_PARAM );
+
+INLINE float128 float128_abs(float128 a)
+{
+    a.high &= 0x7fffffffffffffffLL;
+    return a;
+}
+
+INLINE float128 float128_chs(float128 a)
+{
+    a.high ^= 0x8000000000000000LL;
+    return a;
+}
+
+#endif
+
+#else /* CONFIG_SOFTFLOAT */
+
+#include "softfloat-native.h"
+
+#endif /* !CONFIG_SOFTFLOAT */
+
+#endif /* !SOFTFLOAT_H */
diff --git a/framebuffer.c b/framebuffer.c
new file mode 100644
index 0000000..d0f9b40
--- /dev/null
+++ b/framebuffer.c
@@ -0,0 +1,243 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "framebuffer.h"
+#include <memory.h>
+#include <stdlib.h>
+
+typedef struct {
+    /* client fields, these correspond to code that waits for updates before displaying them */
+    /* at the moment, only one client is supported */
+    void*                        fb_opaque;
+    QFrameBufferUpdateFunc       fb_update;
+    QFrameBufferRotateFunc       fb_rotate;
+    QFrameBufferDoneFunc         fb_done;
+
+    void*                        pr_opaque;
+    QFrameBufferCheckUpdateFunc  pr_check;
+    QFrameBufferInvalidateFunc   pr_invalidate;
+    QFrameBufferDetachFunc       pr_detach;
+
+} QFrameBufferExtra;
+
+
+static int
+_get_pitch( int  width, QFrameBufferFormat  format )
+{
+
+    switch (format) {
+        case QFRAME_BUFFER_RGB565:
+            return width*2;
+        default:
+            return -1;
+    }
+}
+
+
+int
+qframebuffer_init( QFrameBuffer*       qfbuff,
+                   int                 width,
+                   int                 height,
+                   int                 rotation,
+                   QFrameBufferFormat  format )
+{
+    int   pitch;
+
+    rotation &= 3;
+
+    if (!qfbuff || width < 0 || height < 0)
+        return -1;
+
+    pitch = _get_pitch( width, format );
+    if (pitch < 0)
+        return -1;
+
+    memset( qfbuff, 0, sizeof(*qfbuff) );
+
+    qfbuff->extra = calloc( 1, sizeof(QFrameBufferExtra) );
+    if (qfbuff->extra == NULL)
+        return -1;
+
+    qfbuff->pixels = calloc( pitch, height );
+    if (qfbuff->pixels == NULL && (height > 0 && pitch > 0)) {
+        free( qfbuff->extra );
+        return -1;
+    }
+
+    qfbuff->width  = width;
+    qfbuff->height = height;
+    qfbuff->pitch  = pitch;
+    qfbuff->format = format;
+
+    qframebuffer_set_dpi( qfbuff, DEFAULT_FRAMEBUFFER_DPI, DEFAULT_FRAMEBUFFER_DPI );
+    return 0;
+}
+
+
+void
+qframebuffer_set_dpi( QFrameBuffer*   qfbuff,
+                      int             x_dpi,
+                      int             y_dpi )
+{
+    /* dpi = dots / inch
+    ** inch = dots / dpi
+    ** mm / 25.4 = dots / dpi
+    ** mm = (dots * 25.4)/dpi
+    */
+    qfbuff->phys_width_mm  = (int)(0.5 + 25.4 * qfbuff->width  / x_dpi);
+    qfbuff->phys_height_mm = (int)(0.5 + 25.4 * qfbuff->height / y_dpi);
+}
+
+/* alternative to qframebuffer_set_dpi where one can set the physical dimensions directly */
+/* in millimeters. for the record 1 inch = 25.4 mm */
+void
+qframebuffer_set_mm( QFrameBuffer*   qfbuff,
+                     int             width_mm,
+                     int             height_mm )
+{
+    qfbuff->phys_width_mm  = width_mm;
+    qfbuff->phys_height_mm = height_mm;
+}
+
+void
+qframebuffer_update( QFrameBuffer*  qfbuff, int  x, int  y, int  w, int  h )
+{
+    QFrameBufferExtra*  extra = qfbuff->extra;
+
+    if (extra->fb_update)
+        extra->fb_update( extra->fb_opaque, x, y, w, h );
+}
+
+
+void
+qframebuffer_add_client( QFrameBuffer*           qfbuff,
+                         void*                   fb_opaque,
+                         QFrameBufferUpdateFunc  fb_update,
+                         QFrameBufferRotateFunc  fb_rotate,
+                         QFrameBufferDoneFunc    fb_done )
+{
+    QFrameBufferExtra*  extra = qfbuff->extra;
+
+    extra->fb_opaque = fb_opaque;
+    extra->fb_update = fb_update;
+    extra->fb_rotate = fb_rotate;
+    extra->fb_done   = fb_done;
+}
+
+void
+qframebuffer_set_producer( QFrameBuffer*                qfbuff,
+                           void*                        opaque,
+                           QFrameBufferCheckUpdateFunc  pr_check,
+                           QFrameBufferInvalidateFunc   pr_invalidate,
+                           QFrameBufferDetachFunc       pr_detach )
+{
+    QFrameBufferExtra*  extra = qfbuff->extra;
+
+    extra->pr_opaque     = opaque;
+    extra->pr_check      = pr_check;
+    extra->pr_invalidate = pr_invalidate;
+    extra->pr_detach     = pr_detach;
+}
+
+
+void
+qframebuffer_rotate( QFrameBuffer*  qfbuff, int  rotation )
+{
+    QFrameBufferExtra*  extra = qfbuff->extra;
+
+    if ((rotation ^ qfbuff->rotation) & 1) {
+        /* swap width and height if new rotation requires it */
+        int  temp = qfbuff->width;
+        qfbuff->width  = qfbuff->height;
+        qfbuff->height = temp;
+        qfbuff->pitch  = _get_pitch( qfbuff->width, qfbuff->format );
+
+        temp = qfbuff->phys_width_mm;
+        qfbuff->phys_width_mm  = qfbuff->phys_height_mm;
+        qfbuff->phys_height_mm = temp;
+    }
+    qfbuff->rotation = rotation;
+
+    if (extra->fb_rotate)
+        extra->fb_rotate( extra->fb_opaque, rotation );
+}
+
+
+extern void
+qframebuffer_done( QFrameBuffer*   qfbuff )
+{
+    QFrameBufferExtra*  extra = qfbuff->extra;
+
+    if (extra) {
+        if (extra->pr_detach)
+            extra->pr_detach( extra->pr_opaque );
+
+        if (extra->fb_done)
+            extra->fb_done( extra->fb_opaque );
+    }
+
+    free( qfbuff->pixels );
+    free( qfbuff->extra );
+    memset( qfbuff, 0, sizeof(*qfbuff) );
+}
+
+
+#define  MAX_FRAME_BUFFERS  8
+
+static QFrameBuffer* framebuffer_fifo[ MAX_FRAME_BUFFERS ];
+static int           framebuffer_fifo_rpos;
+static int           framebuffer_fifo_count;
+
+void
+qframebuffer_fifo_add( QFrameBuffer*  qfbuff )
+{
+    if (framebuffer_fifo_count >= MAX_FRAME_BUFFERS)
+        return;
+
+    framebuffer_fifo[ framebuffer_fifo_count++ ] = qfbuff;
+}
+
+
+QFrameBuffer*
+qframebuffer_fifo_get( void )
+{
+    if (framebuffer_fifo_rpos >= framebuffer_fifo_count)
+        return NULL;
+
+    return framebuffer_fifo[ framebuffer_fifo_rpos++ ];
+}
+
+
+void
+qframebuffer_check_updates( void )
+{
+    int  nn;
+    for (nn = 0; nn < framebuffer_fifo_count; nn++) {
+        QFrameBuffer*       q     = framebuffer_fifo[nn];
+        QFrameBufferExtra*  extra = q->extra;
+
+        if (extra->pr_check)
+            extra->pr_check( extra->pr_opaque );
+    }
+}
+
+void
+qframebuffer_invalidate_all( void )
+{
+    int  nn;
+    for (nn = 0; nn < framebuffer_fifo_count; nn++) {
+        QFrameBuffer*       q     = framebuffer_fifo[nn];
+        QFrameBufferExtra*  extra = q->extra;
+
+        if (extra->pr_invalidate)
+            extra->pr_invalidate( extra->pr_opaque );
+    }
+}
diff --git a/framebuffer.h b/framebuffer.h
new file mode 100644
index 0000000..1dce0d9
--- /dev/null
+++ b/framebuffer.h
@@ -0,0 +1,205 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _QEMU_FRAMEBUFFER_H_
+#define _QEMU_FRAMEBUFFER_H_
+
+/* A simple abstract interface to framebuffer displays. this is used to
+ * de-couple hardware emulation from final display.
+ *
+ * Each QFrameBuffer object holds a pixel buffer that is shared between
+ * one 'Producer' and one or more 'Clients'
+ *
+ * The Producer is in charge of updating the pixel buffer from the state
+ * of the emulated VRAM. A Client listens to updates to the pixel buffer,
+ * sent from the producer through qframebuffer_update()/_rotate() and
+ * displays them.
+ *
+ * note the 'rotation' field: it can take values 0, 1, 2 or 3 and corresponds
+ * to a rotation that must be performed to the pixels stored in the framebuffer
+ * *before* displaying them a value of 1 corresponds to a rotation of
+ * 90 clockwise-degrees, when the framebuffer is rotated 90 or 270 degrees,
+ * its width/height are swapped automatically
+ *
+ * phys_width_mm and phys_height_mm are physical dimensions expressed
+ * in millimeters
+ *
+ * More about the client/producer relationships below.
+ */
+typedef struct QFrameBuffer   QFrameBuffer;
+
+
+typedef enum {
+    QFRAME_BUFFER_NONE   = 0,
+    QFRAME_BUFFER_RGB565 = 1,
+    QFRAME_BUFFER_MAX          /* do not remove */
+} QFrameBufferFormat;
+
+struct QFrameBuffer {
+    int                 width;        /* width in pixels */
+    int                 height;       /* height in pixels */
+    int                 pitch;        /* bytes per line */
+    int                 rotation;     /* rotation to be applied when displaying */
+    QFrameBufferFormat  format;
+    void*               pixels;       /* pixel buffer */
+
+    int                 phys_width_mm;
+    int                 phys_height_mm;
+
+    /* extra data that is handled by the framebuffer implementation */
+    void*               extra;
+
+};
+
+/* the default dpi resolution of a typical framebuffer. this is an average
+ * between various prototypes being used during the development of the
+ * Android system...
+ */
+#define  DEFAULT_FRAMEBUFFER_DPI   165
+
+
+/* initialize a framebuffer object and allocate its pixel buffer */
+/* this computes phys_width_mm and phys_height_mm assuming a 165 dpi screen */
+/* returns -1 in case of error, 0 otherwise */
+extern int
+qframebuffer_init( QFrameBuffer*       qfbuff,
+                   int                 width,
+                   int                 height,
+                   int                 rotation,
+                   QFrameBufferFormat  format );
+
+/* recompute phys_width_mm and phys_height_mm according to the emulated 
+ * screen DPI settings */
+extern void
+qframebuffer_set_dpi( QFrameBuffer*   qfbuff,
+                      int             x_dpi,
+                      int             y_dpi );
+
+/* alternative to qframebuffer_set_dpi where one can set the physical 
+ * dimensions directly in millimeters. for the record 1 inch = 25.4 mm */
+extern void
+qframebuffer_set_mm( QFrameBuffer*   qfbuff,
+                     int             width_mm,
+                     int             height_mm );
+
+/* the Client::Update method is called to instruct a client that a given
+ * rectangle of the framebuffer pixels was updated and needs to be
+ * redrawn.
+ */
+typedef void (*QFrameBufferUpdateFunc)( void*  opaque, int  x, int  y, 
+                                                       int  w, int  h );
+
+/* the Client::Rotate method is called to instruct the client that a
+ * framebuffer's internal rotation has changed. This is the rotation
+ * that must be applied before displaying the pixels.
+ *
+ * Note that it is assumed that all framebuffer pixels have changed too
+ * so the client should call its Update method as well.
+ */
+typedef void (*QFrameBufferRotateFunc)( void*  opaque, int  rotation );
+
+/* the Client::Done func tells a client that a framebuffer object was freed.
+ * no more reference to its pixels should be done.
+ */
+typedef void (*QFrameBufferDoneFunc)  ( void*  opaque );
+
+/* add one client to a given framebuffer.
+ * the current implementation only allows one client per frame-buffer,
+ * but we could allow more for various reasons (e.g. displaying the
+ * framebuffer + dispatching it through VNC at the same time)
+ */
+extern void
+qframebuffer_add_client( QFrameBuffer*           qfbuff,
+                         void*                   fb_opaque,
+                         QFrameBufferUpdateFunc  fb_update,
+                         QFrameBufferRotateFunc  fb_rotate,
+                         QFrameBufferDoneFunc    fb_done );
+
+/* Producer::CheckUpdate is called to let the producer check the
+ * VRAM state (e.g. VRAM dirty pages) to see if anything changed since the
+ * last call to the method. When true, the method should call either
+ * qframebuffer_update() or qframebuffer_rotate() with the appropriate values.
+ */
+typedef void (*QFrameBufferCheckUpdateFunc)( void*  opaque );
+
+/* Producer::Invalidate tells the producer that the next call to
+ * CheckUpdate should act as if the whole content of VRAM had changed.
+ * this is normally done to force client initialization/refreshes.
+ */
+typedef void (*QFrameBufferInvalidateFunc) ( void*  opaque );
+
+/* the Producer::Detach method is used to tell the producer that the
+ * underlying QFrameBuffer object is about to be de-allocated.
+ */
+typedef void (*QFrameBufferDetachFunc)     ( void*  opaque );
+
+/* set the producer of a given framebuffer */
+extern void
+qframebuffer_set_producer( QFrameBuffer*                qfbuff,
+                           void*                        opaque,
+                           QFrameBufferCheckUpdateFunc  fb_check,
+                           QFrameBufferInvalidateFunc   fb_invalidate,
+                           QFrameBufferDetachFunc       fb_detach );
+
+/* tell a client that a rectangle region has been updated in the framebuffer
+ * pixel buffer this is typically called from a Producer::CheckUpdate method
+ */
+extern void
+qframebuffer_update( QFrameBuffer*  qfbuff, int  x, int  y, int  w, int  h );
+
+/* rotate the framebuffer (may swap width/height), and tell all clients.
+ * Should be called from a Producer::CheckUpdate method
+ */
+extern void
+qframebuffer_rotate( QFrameBuffer*  qfbuff, int  rotation );
+
+/* finalize a framebuffer, release its pixel buffer. Should be called
+ * from the framebuffer object's owner
+ */
+extern void
+qframebuffer_done( QFrameBuffer*   qfbuff );
+
+
+/* this is called repeatedly by the emulator. for each registered framebuffer,
+ * call its producer's CheckUpdate method, if any.
+ */
+extern void
+qframebuffer_check_updates( void );
+
+/* this is called by the emulator. for each registered framebuffer, call
+ * its producer's Invalidate method, if any
+ */
+extern void
+qframebuffer_invalidate_all( void );
+
+/*
+ * to completely separate the implementation of clients, producers, and skins,
+ * we use a simple global FIFO list of QFrameBuffer objects.
+ *
+ * qframebuffer_fifo_add() is typically called by the emulator initialization
+ * depending on the emulated device's configuration
+ *
+ * qframebuffer_fifo_get() is typically called by a hardware framebuffer
+ * emulation.
+ */
+
+/* add a new constructed frame buffer object to our global list */
+extern void
+qframebuffer_fifo_add( QFrameBuffer*  qfbuff );
+
+/* retrieve a frame buffer object from the global FIFO list */
+extern QFrameBuffer*
+qframebuffer_fifo_get( void );
+
+/* */
+
+#endif /* _QEMU_FRAMEBUFFER_H_ */
+
diff --git a/gdbstub.c b/gdbstub.c
new file mode 100644
index 0000000..f36e504
--- /dev/null
+++ b/gdbstub.c
@@ -0,0 +1,1593 @@
+/*
+ * gdb server stub
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#ifdef CONFIG_USER_ONLY
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "qemu.h"
+#else
+#include "qemu-common.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "cpu.h"
+#include "gdbstub.h"
+#endif
+
+#include "qemu_socket.h"
+#ifdef _WIN32
+/* XXX: these constants may be independent of the host ones even for Unix */
+#ifndef SIGTRAP
+#define SIGTRAP 5
+#endif
+#ifndef SIGINT
+#define SIGINT 2
+#endif
+#else
+#include <signal.h>
+#endif
+
+//#define DEBUG_GDB
+
+enum RSState {
+    RS_IDLE,
+    RS_GETLINE,
+    RS_CHKSUM1,
+    RS_CHKSUM2,
+    RS_SYSCALL,
+};
+typedef struct GDBState {
+    CPUState *env; /* current CPU */
+    enum RSState state; /* parsing state */
+    char line_buf[4096];
+    int line_buf_index;
+    int line_csum;
+    uint8_t last_packet[4100];
+    int last_packet_len;
+    int signal;
+#ifdef CONFIG_USER_ONLY
+    int fd;
+    int running_state;
+#else
+    CharDriverState *chr;
+#endif
+} GDBState;
+
+/* By default use no IRQs and no timers while single stepping so as to
+ * make single stepping like an ICE HW step.
+ */
+static int sstep_flags = SSTEP_ENABLE|SSTEP_NOIRQ|SSTEP_NOTIMER;
+
+#ifdef CONFIG_USER_ONLY
+/* XXX: This is not thread safe.  Do we care?  */
+static int gdbserver_fd = -1;
+
+/* XXX: remove this hack.  */
+static GDBState gdbserver_state;
+
+static int get_char(GDBState *s)
+{
+    uint8_t ch;
+    int ret;
+
+    for(;;) {
+        ret = socket_recv(s->fd, &ch, 1);
+        if (ret < 0) {
+            if (errno == ECONNRESET)
+                s->fd = -1;
+            if (errno != EINTR && errno != EAGAIN)
+                return -1;
+        } else if (ret == 0) {
+            socket_close(s->fd);
+            s->fd = -1;
+            return -1;
+        } else {
+            break;
+        }
+    }
+    return ch;
+}
+#endif
+
+/* GDB stub state for use by semihosting syscalls.  */
+static GDBState *gdb_syscall_state;
+static gdb_syscall_complete_cb gdb_current_syscall_cb;
+
+enum {
+    GDB_SYS_UNKNOWN,
+    GDB_SYS_ENABLED,
+    GDB_SYS_DISABLED,
+} gdb_syscall_mode;
+
+/* If gdb is connected when the first semihosting syscall occurs then use
+   remote gdb syscalls.  Otherwise use native file IO.  */
+int use_gdb_syscalls(void)
+{
+    if (gdb_syscall_mode == GDB_SYS_UNKNOWN) {
+        gdb_syscall_mode = (gdb_syscall_state ? GDB_SYS_ENABLED
+                                              : GDB_SYS_DISABLED);
+    }
+    return gdb_syscall_mode == GDB_SYS_ENABLED;
+}
+
+/* Resume execution.  */
+static inline void gdb_continue(GDBState *s)
+{
+#ifdef CONFIG_USER_ONLY
+    s->running_state = 1;
+#else
+    vm_start();
+#endif
+}
+
+static void put_buffer(GDBState *s, const uint8_t *buf, int len)
+{
+#ifdef CONFIG_USER_ONLY
+    int ret;
+
+    while (len > 0) {
+        ret = socket_send(s->fd, buf, len);
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN)
+                return;
+        } else {
+            buf += ret;
+            len -= ret;
+        }
+    }
+#else
+    qemu_chr_write(s->chr, buf, len);
+#endif
+}
+
+static inline int fromhex(int v)
+{
+    if (v >= '0' && v <= '9')
+        return v - '0';
+    else if (v >= 'A' && v <= 'F')
+        return v - 'A' + 10;
+    else if (v >= 'a' && v <= 'f')
+        return v - 'a' + 10;
+    else
+        return 0;
+}
+
+static inline int tohex(int v)
+{
+    if (v < 10)
+        return v + '0';
+    else
+        return v - 10 + 'a';
+}
+
+static void memtohex(char *buf, const uint8_t *mem, int len)
+{
+    int i, c;
+    char *q;
+    q = buf;
+    for(i = 0; i < len; i++) {
+        c = mem[i];
+        *q++ = tohex(c >> 4);
+        *q++ = tohex(c & 0xf);
+    }
+    *q = '\0';
+}
+
+static void hextomem(uint8_t *mem, const char *buf, int len)
+{
+    int i;
+
+    for(i = 0; i < len; i++) {
+        mem[i] = (fromhex(buf[0]) << 4) | fromhex(buf[1]);
+        buf += 2;
+    }
+}
+
+/* return -1 if error, 0 if OK */
+static int put_packet(GDBState *s, const char *buf)
+{
+    int len, csum, i;
+    uint8_t *p;
+
+#ifdef DEBUG_GDB
+    printf("reply='%s'\n", buf);
+#endif
+
+    for(;;) {
+        p = s->last_packet;
+        *(p++) = '$';
+        len = strlen(buf);
+        memcpy(p, buf, len);
+        p += len;
+        csum = 0;
+        for(i = 0; i < len; i++) {
+            csum += buf[i];
+        }
+        *(p++) = '#';
+        *(p++) = tohex((csum >> 4) & 0xf);
+        *(p++) = tohex((csum) & 0xf);
+
+        s->last_packet_len = p - s->last_packet;
+        put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len);
+
+#ifdef CONFIG_USER_ONLY
+        i = get_char(s);
+        if (i < 0)
+            return -1;
+        if (i == '+')
+            break;
+#else
+        break;
+#endif
+    }
+    return 0;
+}
+
+#if defined(TARGET_I386)
+
+#ifdef TARGET_X86_64
+static const uint8_t gdb_x86_64_regs[16] = {
+    R_EAX, R_EBX, R_ECX, R_EDX, R_ESI, R_EDI, R_EBP, R_ESP,
+    8, 9, 10, 11, 12, 13, 14, 15,
+};
+#endif
+
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    int i, fpus, nb_regs;
+    uint8_t *p;
+
+    p = mem_buf;
+#ifdef TARGET_X86_64
+    if (env->hflags & HF_CS64_MASK) {
+        nb_regs = 16;
+        for(i = 0; i < 16; i++) {
+            *(uint64_t *)p = tswap64(env->regs[gdb_x86_64_regs[i]]);
+            p += 8;
+        }
+        *(uint64_t *)p = tswap64(env->eip);
+        p += 8;
+    } else
+#endif
+    {
+        nb_regs = 8;
+        for(i = 0; i < 8; i++) {
+            *(uint32_t *)p = tswap32(env->regs[i]);
+            p += 4;
+        }
+        *(uint32_t *)p = tswap32(env->eip);
+        p += 4;
+    }
+
+    *(uint32_t *)p = tswap32(env->eflags);
+    p += 4;
+    *(uint32_t *)p = tswap32(env->segs[R_CS].selector);
+    p += 4;
+    *(uint32_t *)p = tswap32(env->segs[R_SS].selector);
+    p += 4;
+    *(uint32_t *)p = tswap32(env->segs[R_DS].selector);
+    p += 4;
+    *(uint32_t *)p = tswap32(env->segs[R_ES].selector);
+    p += 4;
+    *(uint32_t *)p = tswap32(env->segs[R_FS].selector);
+    p += 4;
+    *(uint32_t *)p = tswap32(env->segs[R_GS].selector);
+    p += 4;
+    for(i = 0; i < 8; i++) {
+        /* XXX: convert floats */
+#ifdef USE_X86LDOUBLE
+        memcpy(p, &env->fpregs[i], 10);
+#else
+        memset(p, 0, 10);
+#endif
+        p += 10;
+    }
+    *(uint32_t *)p = tswap32(env->fpuc); /* fctrl */
+    p += 4;
+    fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+    *(uint32_t *)p = tswap32(fpus); /* fstat */
+    p += 4;
+    *(uint32_t *)p = 0; /* ftag */
+    p += 4;
+    *(uint32_t *)p = 0; /* fiseg */
+    p += 4;
+    *(uint32_t *)p = 0; /* fioff */
+    p += 4;
+    *(uint32_t *)p = 0; /* foseg */
+    p += 4;
+    *(uint32_t *)p = 0; /* fooff */
+    p += 4;
+    *(uint32_t *)p = 0; /* fop */
+    p += 4;
+    for(i = 0; i < nb_regs; i++) {
+        *(uint64_t *)p = tswap64(env->xmm_regs[i].XMM_Q(0));
+        p += 8;
+        *(uint64_t *)p = tswap64(env->xmm_regs[i].XMM_Q(1));
+        p += 8;
+    }
+    *(uint32_t *)p = tswap32(env->mxcsr);
+    p += 4;
+    return p - mem_buf;
+}
+
+static inline void cpu_gdb_load_seg(CPUState *env, const uint8_t **pp, 
+                                    int sreg)
+{
+    const uint8_t *p;
+    uint32_t sel;
+    p = *pp;
+    sel = tswap32(*(uint32_t *)p);
+    p += 4;
+    if (sel != env->segs[sreg].selector) {
+#if defined(CONFIG_USER_ONLY)
+        cpu_x86_load_seg(env, sreg, sel);
+#else
+        /* XXX: do it with a debug function which does not raise an
+           exception */
+#endif
+    }
+    *pp = p;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+    const uint8_t *p = mem_buf;
+    int i, nb_regs;
+    uint16_t fpus;
+
+#ifdef TARGET_X86_64
+    if (env->hflags & HF_CS64_MASK) {
+        nb_regs = 16;
+        for(i = 0; i < 16; i++) {
+            env->regs[gdb_x86_64_regs[i]] = tswap64(*(uint64_t *)p);
+            p += 8;
+        }
+        env->eip = tswap64(*(uint64_t *)p);
+        p += 8;
+    } else
+#endif
+    {
+        nb_regs = 8;
+        for(i = 0; i < 8; i++) {
+            env->regs[i] = tswap32(*(uint32_t *)p);
+            p += 4;
+        }
+        env->eip = tswap32(*(uint32_t *)p);
+        p += 4;
+    }
+    env->eflags = tswap32(*(uint32_t *)p);
+    p += 4;
+    cpu_gdb_load_seg(env, &p, R_CS);
+    cpu_gdb_load_seg(env, &p, R_SS);
+    cpu_gdb_load_seg(env, &p, R_DS);
+    cpu_gdb_load_seg(env, &p, R_ES);
+    cpu_gdb_load_seg(env, &p, R_FS);
+    cpu_gdb_load_seg(env, &p, R_GS);
+    
+    /* FPU state */
+    for(i = 0; i < 8; i++) {
+        /* XXX: convert floats */
+#ifdef USE_X86LDOUBLE
+        memcpy(&env->fpregs[i], p, 10);
+#endif
+        p += 10;
+    }
+    env->fpuc = tswap32(*(uint32_t *)p); /* fctrl */
+    p += 4;
+    fpus = tswap32(*(uint32_t *)p);
+    p += 4;
+    env->fpstt = (fpus >> 11) & 7;
+    env->fpus = fpus & ~0x3800;
+    p += 4 * 6;
+    
+    if (size >= ((p - mem_buf) + 16 * nb_regs + 4)) {
+        /* SSE state */
+        for(i = 0; i < nb_regs; i++) {
+            env->xmm_regs[i].XMM_Q(0) = tswap64(*(uint64_t *)p);
+            p += 8;
+            env->xmm_regs[i].XMM_Q(1) = tswap64(*(uint64_t *)p);
+            p += 8;
+        }
+        env->mxcsr = tswap32(*(uint32_t *)p);
+        p += 4;
+    }
+}
+
+#elif defined (TARGET_PPC)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    uint32_t *registers = (uint32_t *)mem_buf, tmp;
+    int i;
+
+    /* fill in gprs */
+    for(i = 0; i < 32; i++) {
+        registers[i] = tswapl(env->gpr[i]);
+    }
+    /* fill in fprs */
+    for (i = 0; i < 32; i++) {
+        registers[(i * 2) + 32] = tswapl(*((uint32_t *)&env->fpr[i]));
+	registers[(i * 2) + 33] = tswapl(*((uint32_t *)&env->fpr[i] + 1));
+    }
+    /* nip, msr, ccr, lnk, ctr, xer, mq */
+    registers[96] = tswapl(env->nip);
+    registers[97] = tswapl(env->msr);
+    tmp = 0;
+    for (i = 0; i < 8; i++)
+        tmp |= env->crf[i] << (32 - ((i + 1) * 4));
+    registers[98] = tswapl(tmp);
+    registers[99] = tswapl(env->lr);
+    registers[100] = tswapl(env->ctr);
+    registers[101] = tswapl(ppc_load_xer(env));
+    registers[102] = 0;
+
+    return 103 * 4;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+    uint32_t *registers = (uint32_t *)mem_buf;
+    int i;
+
+    /* fill in gprs */
+    for (i = 0; i < 32; i++) {
+        env->gpr[i] = tswapl(registers[i]);
+    }
+    /* fill in fprs */
+    for (i = 0; i < 32; i++) {
+        *((uint32_t *)&env->fpr[i]) = tswapl(registers[(i * 2) + 32]);
+	*((uint32_t *)&env->fpr[i] + 1) = tswapl(registers[(i * 2) + 33]);
+    }
+    /* nip, msr, ccr, lnk, ctr, xer, mq */
+    env->nip = tswapl(registers[96]);
+    ppc_store_msr(env, tswapl(registers[97]));
+    registers[98] = tswapl(registers[98]);
+    for (i = 0; i < 8; i++)
+        env->crf[i] = (registers[98] >> (32 - ((i + 1) * 4))) & 0xF;
+    env->lr = tswapl(registers[99]);
+    env->ctr = tswapl(registers[100]);
+    ppc_store_xer(env, tswapl(registers[101]));
+}
+#elif defined (TARGET_SPARC)
+#ifdef TARGET_ABI32
+#define tswap_abi(val) tswap32(val &0xffffffff)
+#else
+#define tswap_abi(val) tswapl(val)
+#endif
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+#ifdef TARGET_ABI32
+    abi_ulong *registers = (abi_ulong *)mem_buf;
+#else
+    target_ulong *registers = (target_ulong *)mem_buf;
+#endif
+    int i;
+
+    /* fill in g0..g7 */
+    for(i = 0; i < 8; i++) {
+        registers[i] = tswap_abi(env->gregs[i]);
+    }
+    /* fill in register window */
+    for(i = 0; i < 24; i++) {
+        registers[i + 8] = tswap_abi(env->regwptr[i]);
+    }
+#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
+    /* fill in fprs */
+    for (i = 0; i < 32; i++) {
+        registers[i + 32] = tswap_abi(*((uint32_t *)&env->fpr[i]));
+    }
+    /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+    registers[64] = tswap_abi(env->y);
+    {
+        uint32_t tmp;
+
+        tmp = GET_PSR(env);
+        registers[65] = tswap32(tmp);
+    }
+    registers[66] = tswap_abi(env->wim);
+    registers[67] = tswap_abi(env->tbr);
+    registers[68] = tswap_abi(env->pc);
+    registers[69] = tswap_abi(env->npc);
+    registers[70] = tswap_abi(env->fsr);
+    registers[71] = 0; /* csr */
+    registers[72] = 0;
+    return 73 * sizeof(uint32_t);
+#else
+    /* fill in fprs */
+    for (i = 0; i < 64; i += 2) {
+	uint64_t tmp;
+
+        tmp = ((uint64_t)*(uint32_t *)&env->fpr[i]) << 32;
+        tmp |= *(uint32_t *)&env->fpr[i + 1];
+        registers[i / 2 + 32] = tswap64(tmp);
+    }
+    registers[64] = tswapl(env->pc);
+    registers[65] = tswapl(env->npc);
+    registers[66] = tswapl(((uint64_t)GET_CCR(env) << 32) |
+                           ((env->asi & 0xff) << 24) |
+                           ((env->pstate & 0xfff) << 8) |
+                           GET_CWP64(env));
+    registers[67] = tswapl(env->fsr);
+    registers[68] = tswapl(env->fprs);
+    registers[69] = tswapl(env->y);
+    return 70 * sizeof(target_ulong);
+#endif
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+#ifdef TARGET_ABI32
+    abi_ulong *registers = (abi_ulong *)mem_buf;
+#else
+    target_ulong *registers = (target_ulong *)mem_buf;
+#endif
+    int i;
+
+    /* fill in g0..g7 */
+    for(i = 0; i < 7; i++) {
+        env->gregs[i] = tswap_abi(registers[i]);
+    }
+    /* fill in register window */
+    for(i = 0; i < 24; i++) {
+        env->regwptr[i] = tswap_abi(registers[i + 8]);
+    }
+#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
+    /* fill in fprs */
+    for (i = 0; i < 32; i++) {
+        *((uint32_t *)&env->fpr[i]) = tswap_abi(registers[i + 32]);
+    }
+    /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
+    env->y = tswap_abi(registers[64]);
+    PUT_PSR(env, tswap_abi(registers[65]));
+    env->wim = tswap_abi(registers[66]);
+    env->tbr = tswap_abi(registers[67]);
+    env->pc = tswap_abi(registers[68]);
+    env->npc = tswap_abi(registers[69]);
+    env->fsr = tswap_abi(registers[70]);
+#else
+    for (i = 0; i < 64; i += 2) {
+        uint64_t tmp;
+
+        tmp = tswap64(registers[i / 2 + 32]);
+	*((uint32_t *)&env->fpr[i]) = tmp >> 32;
+	*((uint32_t *)&env->fpr[i + 1]) = tmp & 0xffffffff;
+    }
+    env->pc = tswapl(registers[64]);
+    env->npc = tswapl(registers[65]);
+    {
+        uint64_t tmp = tswapl(registers[66]);
+
+        PUT_CCR(env, tmp >> 32);
+        env->asi = (tmp >> 24) & 0xff;
+        env->pstate = (tmp >> 8) & 0xfff;
+        PUT_CWP64(env, tmp & 0xff);
+    }
+    env->fsr = tswapl(registers[67]);
+    env->fprs = tswapl(registers[68]);
+    env->y = tswapl(registers[69]);
+#endif
+}
+#undef tswap_abi
+#elif defined (TARGET_ARM)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    int i;
+    uint8_t *ptr;
+
+    ptr = mem_buf;
+    /* 16 core integer registers (4 bytes each).  */
+    for (i = 0; i < 16; i++)
+      {
+        *(uint32_t *)ptr = tswapl(env->regs[i]);
+        ptr += 4;
+      }
+    /* 8 FPA registers (12 bytes each), FPS (4 bytes).
+       Not yet implemented.  */
+    memset (ptr, 0, 8 * 12 + 4);
+    ptr += 8 * 12 + 4;
+    /* CPSR (4 bytes).  */
+    *(uint32_t *)ptr = tswapl (cpsr_read(env));
+    ptr += 4;
+
+    return ptr - mem_buf;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+    int i;
+    uint8_t *ptr;
+
+    ptr = mem_buf;
+    /* Core integer registers.  */
+    for (i = 0; i < 16; i++)
+      {
+        env->regs[i] = tswapl(*(uint32_t *)ptr);
+        ptr += 4;
+      }
+    /* Ignore FPA regs and scr.  */
+    ptr += 8 * 12 + 4;
+    cpsr_write (env, tswapl(*(uint32_t *)ptr), 0xffffffff);
+}
+#elif defined (TARGET_M68K)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    int i;
+    uint8_t *ptr;
+    CPU_DoubleU u;
+
+    ptr = mem_buf;
+    /* D0-D7 */
+    for (i = 0; i < 8; i++) {
+        *(uint32_t *)ptr = tswapl(env->dregs[i]);
+        ptr += 4;
+    }
+    /* A0-A7 */
+    for (i = 0; i < 8; i++) {
+        *(uint32_t *)ptr = tswapl(env->aregs[i]);
+        ptr += 4;
+    }
+    *(uint32_t *)ptr = tswapl(env->sr);
+    ptr += 4;
+    *(uint32_t *)ptr = tswapl(env->pc);
+    ptr += 4;
+    /* F0-F7.  The 68881/68040 have 12-bit extended precision registers.
+       ColdFire has 8-bit double precision registers.  */
+    for (i = 0; i < 8; i++) {
+        u.d = env->fregs[i];
+        *(uint32_t *)ptr = tswap32(u.l.upper);
+        *(uint32_t *)ptr = tswap32(u.l.lower);
+    }
+    /* FP control regs (not implemented).  */
+    memset (ptr, 0, 3 * 4);
+    ptr += 3 * 4;
+
+    return ptr - mem_buf;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+    int i;
+    uint8_t *ptr;
+    CPU_DoubleU u;
+
+    ptr = mem_buf;
+    /* D0-D7 */
+    for (i = 0; i < 8; i++) {
+        env->dregs[i] = tswapl(*(uint32_t *)ptr);
+        ptr += 4;
+    }
+    /* A0-A7 */
+    for (i = 0; i < 8; i++) {
+        env->aregs[i] = tswapl(*(uint32_t *)ptr);
+        ptr += 4;
+    }
+    env->sr = tswapl(*(uint32_t *)ptr);
+    ptr += 4;
+    env->pc = tswapl(*(uint32_t *)ptr);
+    ptr += 4;
+    /* F0-F7.  The 68881/68040 have 12-bit extended precision registers.
+       ColdFire has 8-bit double precision registers.  */
+    for (i = 0; i < 8; i++) {
+        u.l.upper = tswap32(*(uint32_t *)ptr);
+        u.l.lower = tswap32(*(uint32_t *)ptr);
+        env->fregs[i] = u.d;
+    }
+    /* FP control regs (not implemented).  */
+    ptr += 3 * 4;
+}
+#elif defined (TARGET_MIPS)
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    int i;
+    uint8_t *ptr;
+
+    ptr = mem_buf;
+    for (i = 0; i < 32; i++)
+      {
+        *(target_ulong *)ptr = tswapl(env->active_tc.gpr[i]);
+        ptr += sizeof(target_ulong);
+      }
+
+    *(target_ulong *)ptr = (int32_t)tswap32(env->CP0_Status);
+    ptr += sizeof(target_ulong);
+
+    *(target_ulong *)ptr = tswapl(env->active_tc.LO[0]);
+    ptr += sizeof(target_ulong);
+
+    *(target_ulong *)ptr = tswapl(env->active_tc.HI[0]);
+    ptr += sizeof(target_ulong);
+
+    *(target_ulong *)ptr = tswapl(env->CP0_BadVAddr);
+    ptr += sizeof(target_ulong);
+
+    *(target_ulong *)ptr = (int32_t)tswap32(env->CP0_Cause);
+    ptr += sizeof(target_ulong);
+
+    *(target_ulong *)ptr = tswapl(env->active_tc.PC);
+    ptr += sizeof(target_ulong);
+
+    if (env->CP0_Config1 & (1 << CP0C1_FP))
+      {
+        for (i = 0; i < 32; i++)
+          {
+            if (env->CP0_Status & (1 << CP0St_FR))
+              *(target_ulong *)ptr = tswapl(env->fpu->fpr[i].d);
+            else
+              *(target_ulong *)ptr = tswap32(env->fpu->fpr[i].w[FP_ENDIAN_IDX]);
+            ptr += sizeof(target_ulong);
+          }
+
+        *(target_ulong *)ptr = (int32_t)tswap32(env->fpu->fcr31);
+        ptr += sizeof(target_ulong);
+
+        *(target_ulong *)ptr = (int32_t)tswap32(env->fpu->fcr0);
+        ptr += sizeof(target_ulong);
+      }
+
+    /* "fp", pseudo frame pointer. Not yet implemented in gdb. */
+    *(target_ulong *)ptr = 0;
+    ptr += sizeof(target_ulong);
+
+    /* Registers for embedded use, we just pad them. */
+    for (i = 0; i < 16; i++)
+      {
+        *(target_ulong *)ptr = 0;
+        ptr += sizeof(target_ulong);
+      }
+
+    /* Processor ID. */
+    *(target_ulong *)ptr = (int32_t)tswap32(env->CP0_PRid);
+    ptr += sizeof(target_ulong);
+
+    return ptr - mem_buf;
+}
+
+/* convert MIPS rounding mode in FCR31 to IEEE library */
+static unsigned int ieee_rm[] =
+  {
+    float_round_nearest_even,
+    float_round_to_zero,
+    float_round_up,
+    float_round_down
+  };
+#define RESTORE_ROUNDING_MODE \
+    set_float_rounding_mode(ieee_rm[env->fpu->fcr31 & 3], &env->fpu->fp_status)
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+    int i;
+    uint8_t *ptr;
+
+    ptr = mem_buf;
+    for (i = 0; i < 32; i++)
+      {
+        env->active_tc.gpr[i] = tswapl(*(target_ulong *)ptr);
+        ptr += sizeof(target_ulong);
+      }
+
+    env->CP0_Status = tswapl(*(target_ulong *)ptr);
+    ptr += sizeof(target_ulong);
+
+    env->active_tc.LO[0] = tswapl(*(target_ulong *)ptr);
+    ptr += sizeof(target_ulong);
+
+    env->active_tc.HI[0] = tswapl(*(target_ulong *)ptr);
+    ptr += sizeof(target_ulong);
+
+    env->CP0_BadVAddr = tswapl(*(target_ulong *)ptr);
+    ptr += sizeof(target_ulong);
+
+    env->CP0_Cause = tswapl(*(target_ulong *)ptr);
+    ptr += sizeof(target_ulong);
+
+    env->active_tc.PC = tswapl(*(target_ulong *)ptr);
+    ptr += sizeof(target_ulong);
+
+    if (env->CP0_Config1 & (1 << CP0C1_FP))
+      {
+        for (i = 0; i < 32; i++)
+          {
+            if (env->CP0_Status & (1 << CP0St_FR))
+              env->fpu->fpr[i].d = tswapl(*(target_ulong *)ptr);
+            else
+              env->fpu->fpr[i].w[FP_ENDIAN_IDX] = tswapl(*(target_ulong *)ptr);
+            ptr += sizeof(target_ulong);
+          }
+
+        env->fpu->fcr31 = tswapl(*(target_ulong *)ptr) & 0xFF83FFFF;
+        ptr += sizeof(target_ulong);
+
+        /* The remaining registers are assumed to be read-only. */
+
+        /* set rounding mode */
+        RESTORE_ROUNDING_MODE;
+
+#ifndef CONFIG_SOFTFLOAT
+        /* no floating point exception for native float */
+        SET_FP_ENABLE(env->fcr31, 0);
+#endif
+      }
+}
+#elif defined (TARGET_SH4)
+
+/* Hint: Use "set architecture sh4" in GDB to see fpu registers */
+
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+  uint32_t *ptr = (uint32_t *)mem_buf;
+  int i;
+
+#define SAVE(x) *ptr++=tswapl(x)
+  if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+      for (i = 0; i < 8; i++) SAVE(env->gregs[i + 16]);
+  } else {
+      for (i = 0; i < 8; i++) SAVE(env->gregs[i]);
+  }
+  for (i = 8; i < 16; i++) SAVE(env->gregs[i]);
+  SAVE (env->pc);
+  SAVE (env->pr);
+  SAVE (env->gbr);
+  SAVE (env->vbr);
+  SAVE (env->mach);
+  SAVE (env->macl);
+  SAVE (env->sr);
+  SAVE (env->fpul);
+  SAVE (env->fpscr);
+  for (i = 0; i < 16; i++)
+      SAVE(env->fregs[i + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
+  SAVE (env->ssr);
+  SAVE (env->spc);
+  for (i = 0; i < 8; i++) SAVE(env->gregs[i]);
+  for (i = 0; i < 8; i++) SAVE(env->gregs[i + 16]);
+  return ((uint8_t *)ptr - mem_buf);
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+  uint32_t *ptr = (uint32_t *)mem_buf;
+  int i;
+
+#define LOAD(x) (x)=*ptr++;
+  if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) {
+      for (i = 0; i < 8; i++) LOAD(env->gregs[i + 16]);
+  } else {
+      for (i = 0; i < 8; i++) LOAD(env->gregs[i]);
+  }
+  for (i = 8; i < 16; i++) LOAD(env->gregs[i]);
+  LOAD (env->pc);
+  LOAD (env->pr);
+  LOAD (env->gbr);
+  LOAD (env->vbr);
+  LOAD (env->mach);
+  LOAD (env->macl);
+  LOAD (env->sr);
+  LOAD (env->fpul);
+  LOAD (env->fpscr);
+  for (i = 0; i < 16; i++)
+      LOAD(env->fregs[i + ((env->fpscr & FPSCR_FR) ? 16 : 0)]);
+  LOAD (env->ssr);
+  LOAD (env->spc);
+  for (i = 0; i < 8; i++) LOAD(env->gregs[i]);
+  for (i = 0; i < 8; i++) LOAD(env->gregs[i + 16]);
+}
+#elif defined (TARGET_CRIS)
+
+static int cris_save_32 (unsigned char *d, uint32_t value)
+{
+	*d++ = (value);
+	*d++ = (value >>= 8);
+	*d++ = (value >>= 8);
+	*d++ = (value >>= 8);
+	return 4;
+}
+static int cris_save_16 (unsigned char *d, uint32_t value)
+{
+	*d++ = (value);
+	*d++ = (value >>= 8);
+	return 2;
+}
+static int cris_save_8 (unsigned char *d, uint32_t value)
+{
+	*d++ = (value);
+	return 1;
+}
+
+/* FIXME: this will bug on archs not supporting unaligned word accesses.  */
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+  uint8_t *ptr = mem_buf;
+  uint8_t srs;
+  int i;
+
+  for (i = 0; i < 16; i++)
+	  ptr += cris_save_32 (ptr, env->regs[i]);
+
+  srs = env->pregs[PR_SRS];
+
+  ptr += cris_save_8 (ptr, env->pregs[0]);
+  ptr += cris_save_8 (ptr, env->pregs[1]);
+  ptr += cris_save_32 (ptr, env->pregs[2]);
+  ptr += cris_save_8 (ptr, srs);
+  ptr += cris_save_16 (ptr, env->pregs[4]);
+
+  for (i = 5; i < 16; i++)
+	  ptr += cris_save_32 (ptr, env->pregs[i]);
+
+  ptr += cris_save_32 (ptr, env->pc);
+
+  for (i = 0; i < 16; i++)
+	  ptr += cris_save_32 (ptr, env->sregs[srs][i]);
+
+  return ((uint8_t *)ptr - mem_buf);
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+  uint32_t *ptr = (uint32_t *)mem_buf;
+  int i;
+
+#define LOAD(x) (x)=*ptr++;
+  for (i = 0; i < 16; i++) LOAD(env->regs[i]);
+  LOAD (env->pc);
+}
+#else
+static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
+{
+    return 0;
+}
+
+static void cpu_gdb_write_registers(CPUState *env, uint8_t *mem_buf, int size)
+{
+}
+
+#endif
+
+static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
+{
+    const char *p;
+    int ch, reg_size, type;
+    char buf[4096];
+    uint8_t mem_buf[4096];
+    uint32_t *registers;
+    target_ulong addr, len;
+
+#ifdef DEBUG_GDB
+    printf("command='%s'\n", line_buf);
+#endif
+    p = line_buf;
+    ch = *p++;
+    switch(ch) {
+    case '?':
+        /* TODO: Make this return the correct value for user-mode.  */
+        snprintf(buf, sizeof(buf), "S%02x", SIGTRAP);
+        put_packet(s, buf);
+        /* Remove all the breakpoints when this query is issued,
+         * because gdb is doing and initial connect and the state
+         * should be cleaned up.
+         */
+        cpu_breakpoint_remove_all(env);
+        cpu_watchpoint_remove_all(env);
+        break;
+    case 'c':
+        if (*p != '\0') {
+            addr = strtoull(p, (char **)&p, 16);
+#if defined(TARGET_I386)
+            env->eip = addr;
+#elif defined (TARGET_PPC)
+            env->nip = addr;
+#elif defined (TARGET_SPARC)
+            env->pc = addr;
+            env->npc = addr + 4;
+#elif defined (TARGET_ARM)
+            env->regs[15] = addr;
+#elif defined (TARGET_SH4)
+            env->pc = addr;
+#elif defined (TARGET_MIPS)
+            env->active_tc.PC = addr;
+#elif defined (TARGET_CRIS)
+            env->pc = addr;
+#endif
+        }
+        gdb_continue(s);
+	return RS_IDLE;
+    case 'C':
+        s->signal = strtoul(p, (char **)&p, 16);
+        gdb_continue(s);
+        return RS_IDLE;
+    case 'k':
+        /* Kill the target */
+        fprintf(stderr, "\nQEMU: Terminated via GDBstub\n");
+        exit(0);
+    case 'D':
+        /* Detach packet */
+        cpu_breakpoint_remove_all(env);
+        cpu_watchpoint_remove_all(env);
+        gdb_continue(s);
+        put_packet(s, "OK");
+        break;
+    case 's':
+        if (*p != '\0') {
+            addr = strtoull(p, (char **)&p, 16);
+#if defined(TARGET_I386)
+            env->eip = addr;
+#elif defined (TARGET_PPC)
+            env->nip = addr;
+#elif defined (TARGET_SPARC)
+            env->pc = addr;
+            env->npc = addr + 4;
+#elif defined (TARGET_ARM)
+            env->regs[15] = addr;
+#elif defined (TARGET_SH4)
+            env->pc = addr;
+#elif defined (TARGET_MIPS)
+            env->active_tc.PC = addr;
+#elif defined (TARGET_CRIS)
+            env->pc = addr;
+#endif
+        }
+        cpu_single_step(env, sstep_flags);
+        gdb_continue(s);
+	return RS_IDLE;
+    case 'F':
+        {
+            target_ulong ret;
+            target_ulong err;
+
+            ret = strtoull(p, (char **)&p, 16);
+            if (*p == ',') {
+                p++;
+                err = strtoull(p, (char **)&p, 16);
+            } else {
+                err = 0;
+            }
+            if (*p == ',')
+                p++;
+            type = *p;
+            if (gdb_current_syscall_cb)
+                gdb_current_syscall_cb(s->env, ret, err);
+            if (type == 'C') {
+                put_packet(s, "T02");
+            } else {
+                gdb_continue(s);
+            }
+        }
+        break;
+    case 'g':
+        reg_size = cpu_gdb_read_registers(env, mem_buf);
+        memtohex(buf, mem_buf, reg_size);
+        put_packet(s, buf);
+        break;
+    case 'G':
+        registers = (void *)mem_buf;
+        len = strlen(p) / 2;
+        hextomem((uint8_t *)registers, p, len);
+        cpu_gdb_write_registers(env, mem_buf, len);
+        put_packet(s, "OK");
+        break;
+    case 'm':
+        addr = strtoull(p, (char **)&p, 16);
+        if (*p == ',')
+            p++;
+        len = strtoull(p, NULL, 16);
+        if (cpu_memory_rw_debug(env, addr, mem_buf, len, 0) != 0) {
+            put_packet (s, "E14");
+        } else {
+            memtohex(buf, mem_buf, len);
+            put_packet(s, buf);
+        }
+        break;
+    case 'M':
+        addr = strtoull(p, (char **)&p, 16);
+        if (*p == ',')
+            p++;
+        len = strtoull(p, (char **)&p, 16);
+        if (*p == ':')
+            p++;
+        hextomem(mem_buf, p, len);
+        if (cpu_memory_rw_debug(env, addr, mem_buf, len, 1) != 0)
+            put_packet(s, "E14");
+        else
+            put_packet(s, "OK");
+        break;
+    case 'Z':
+        type = strtoul(p, (char **)&p, 16);
+        if (*p == ',')
+            p++;
+        addr = strtoull(p, (char **)&p, 16);
+        if (*p == ',')
+            p++;
+        len = strtoull(p, (char **)&p, 16);
+        switch (type) {
+        case 0:
+        case 1:
+            if (cpu_breakpoint_insert(env, addr) < 0)
+                goto breakpoint_error;
+            put_packet(s, "OK");
+            break;
+#ifndef CONFIG_USER_ONLY
+        case 2:
+            type = PAGE_WRITE;
+            goto insert_watchpoint;
+        case 3:
+            type = PAGE_READ;
+            goto insert_watchpoint;
+        case 4:
+            type = PAGE_READ | PAGE_WRITE;
+        insert_watchpoint:
+            if (cpu_watchpoint_insert(env, addr, type) < 0)
+                goto breakpoint_error;
+            put_packet(s, "OK");
+            break;
+#endif
+        default:
+            put_packet(s, "");
+            break;
+        }
+        break;
+    breakpoint_error:
+        put_packet(s, "E22");
+        break;
+
+    case 'z':
+        type = strtoul(p, (char **)&p, 16);
+        if (*p == ',')
+            p++;
+        addr = strtoull(p, (char **)&p, 16);
+        if (*p == ',')
+            p++;
+        len = strtoull(p, (char **)&p, 16);
+        if (type == 0 || type == 1) {
+            cpu_breakpoint_remove(env, addr);
+            put_packet(s, "OK");
+#ifndef CONFIG_USER_ONLY
+        } else if (type >= 2 || type <= 4) {
+            cpu_watchpoint_remove(env, addr);
+            put_packet(s, "OK");
+#endif
+        } else {
+            put_packet(s, "");
+        }
+        break;
+    case 'q':
+    case 'Q':
+        /* parse any 'q' packets here */
+        if (!strcmp(p,"qemu.sstepbits")) {
+            /* Query Breakpoint bit definitions */
+            snprintf(buf, sizeof(buf), "ENABLE=%x,NOIRQ=%x,NOTIMER=%x",
+                     SSTEP_ENABLE,
+                     SSTEP_NOIRQ,
+                     SSTEP_NOTIMER);
+            put_packet(s, buf);
+            break;
+        } else if (strncmp(p,"qemu.sstep",10) == 0) {
+            /* Display or change the sstep_flags */
+            p += 10;
+            if (*p != '=') {
+                /* Display current setting */
+                snprintf(buf, sizeof(buf), "0x%x", sstep_flags);
+                put_packet(s, buf);
+                break;
+            }
+            p++;
+            type = strtoul(p, (char **)&p, 16);
+            sstep_flags = type;
+            put_packet(s, "OK");
+            break;
+        }
+#ifdef CONFIG_LINUX_USER
+        else if (strncmp(p, "Offsets", 7) == 0) {
+            TaskState *ts = env->opaque;
+
+            snprintf(buf, sizeof(buf),
+                     "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx
+                     ";Bss=" TARGET_ABI_FMT_lx,
+                     ts->info->code_offset,
+                     ts->info->data_offset,
+                     ts->info->data_offset);
+            put_packet(s, buf);
+            break;
+        }
+#endif
+        /* Fall through.  */
+    default:
+        /* put empty packet */
+        buf[0] = '\0';
+        put_packet(s, buf);
+        break;
+    }
+    return RS_IDLE;
+}
+
+extern void tb_flush(CPUState *env);
+
+#ifndef CONFIG_USER_ONLY
+static void gdb_vm_stopped(void *opaque, int reason)
+{
+    GDBState *s = opaque;
+    char buf[256];
+    int ret;
+
+    if (s->state == RS_SYSCALL)
+        return;
+
+    /* disable single step if it was enable */
+    cpu_single_step(s->env, 0);
+
+    if (reason == EXCP_DEBUG) {
+        if (s->env->watchpoint_hit) {
+            snprintf(buf, sizeof(buf), "T%02xwatch:" TARGET_FMT_lx ";",
+                     SIGTRAP,
+                     s->env->watchpoint[s->env->watchpoint_hit - 1].vaddr);
+            put_packet(s, buf);
+            s->env->watchpoint_hit = 0;
+            return;
+        }
+	tb_flush(s->env);
+        ret = SIGTRAP;
+    } else if (reason == EXCP_INTERRUPT) {
+        ret = SIGINT;
+    } else {
+        ret = 0;
+    }
+    snprintf(buf, sizeof(buf), "S%02x", ret);
+    put_packet(s, buf);
+}
+#endif
+
+/* Send a gdb syscall request.
+   This accepts limited printf-style format specifiers, specifically:
+    %x  - target_ulong argument printed in hex.
+    %lx - 64-bit argument printed in hex.
+    %s  - string pointer (target_ulong) and length (int) pair.  */
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...)
+{
+    va_list va;
+    char buf[256];
+    char *p;
+    target_ulong addr;
+    uint64_t i64;
+    GDBState *s;
+
+    s = gdb_syscall_state;
+    if (!s)
+        return;
+    gdb_current_syscall_cb = cb;
+    s->state = RS_SYSCALL;
+#ifndef CONFIG_USER_ONLY
+    vm_stop(EXCP_DEBUG);
+#endif
+    s->state = RS_IDLE;
+    va_start(va, fmt);
+    p = buf;
+    *(p++) = 'F';
+    while (*fmt) {
+        if (*fmt == '%') {
+            fmt++;
+            switch (*fmt++) {
+            case 'x':
+                addr = va_arg(va, target_ulong);
+                p += snprintf(p, &buf[sizeof(buf)] - p, TARGET_FMT_lx, addr);
+                break;
+            case 'l':
+                if (*(fmt++) != 'x')
+                    goto bad_format;
+                i64 = va_arg(va, uint64_t);
+                p += snprintf(p, &buf[sizeof(buf)] - p, "%" PRIx64, i64);
+                break;
+            case 's':
+                addr = va_arg(va, target_ulong);
+                p += snprintf(p, &buf[sizeof(buf)] - p, TARGET_FMT_lx "/%x",
+                              addr, va_arg(va, int));
+                break;
+            default:
+            bad_format:
+                fprintf(stderr, "gdbstub: Bad syscall format string '%s'\n",
+                        fmt - 1);
+                break;
+            }
+        } else {
+            *(p++) = *(fmt++);
+        }
+    }
+    *p = 0;
+    va_end(va);
+    put_packet(s, buf);
+#ifdef CONFIG_USER_ONLY
+    gdb_handlesig(s->env, 0);
+#else
+    cpu_interrupt(s->env, CPU_INTERRUPT_EXIT);
+#endif
+}
+
+static void gdb_read_byte(GDBState *s, int ch)
+{
+    CPUState *env = s->env;
+    int i, csum;
+    uint8_t reply;
+
+#ifndef CONFIG_USER_ONLY
+    if (s->last_packet_len) {
+        /* Waiting for a response to the last packet.  If we see the start
+           of a new command then abandon the previous response.  */
+        if (ch == '-') {
+#ifdef DEBUG_GDB
+            printf("Got NACK, retransmitting\n");
+#endif
+            put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len);
+        }
+#ifdef DEBUG_GDB
+        else if (ch == '+')
+            printf("Got ACK\n");
+        else
+            printf("Got '%c' when expecting ACK/NACK\n", ch);
+#endif
+        if (ch == '+' || ch == '$')
+            s->last_packet_len = 0;
+        if (ch != '$')
+            return;
+    }
+    if (vm_running) {
+        /* when the CPU is running, we cannot do anything except stop
+           it when receiving a char */
+        vm_stop(EXCP_INTERRUPT);
+    } else
+#endif
+    {
+        switch(s->state) {
+        case RS_IDLE:
+            if (ch == '$') {
+                s->line_buf_index = 0;
+                s->state = RS_GETLINE;
+            }
+            break;
+        case RS_GETLINE:
+            if (ch == '#') {
+            s->state = RS_CHKSUM1;
+            } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
+                s->state = RS_IDLE;
+            } else {
+            s->line_buf[s->line_buf_index++] = ch;
+            }
+            break;
+        case RS_CHKSUM1:
+            s->line_buf[s->line_buf_index] = '\0';
+            s->line_csum = fromhex(ch) << 4;
+            s->state = RS_CHKSUM2;
+            break;
+        case RS_CHKSUM2:
+            s->line_csum |= fromhex(ch);
+            csum = 0;
+            for(i = 0; i < s->line_buf_index; i++) {
+                csum += s->line_buf[i];
+            }
+            if (s->line_csum != (csum & 0xff)) {
+                reply = '-';
+                put_buffer(s, &reply, 1);
+                s->state = RS_IDLE;
+            } else {
+                reply = '+';
+                put_buffer(s, &reply, 1);
+                s->state = gdb_handle_packet(s, env, s->line_buf);
+            }
+            break;
+        default:
+            abort();
+        }
+    }
+}
+
+#ifdef CONFIG_USER_ONLY
+int
+gdb_handlesig (CPUState *env, int sig)
+{
+  GDBState *s;
+  char buf[256];
+  int n;
+
+  s = &gdbserver_state;
+  if (gdbserver_fd < 0 || s->fd < 0)
+    return sig;
+
+  /* disable single step if it was enabled */
+  cpu_single_step(env, 0);
+  tb_flush(env);
+
+  if (sig != 0)
+    {
+      snprintf(buf, sizeof(buf), "S%02x", sig);
+      put_packet(s, buf);
+    }
+  /* put_packet() might have detected that the peer terminated the 
+     connection.  */
+  if (s->fd < 0)
+      return sig;
+
+  sig = 0;
+  s->state = RS_IDLE;
+  s->running_state = 0;
+  while (s->running_state == 0) {
+      n = read (s->fd, buf, 256);
+      if (n > 0)
+        {
+          int i;
+
+          for (i = 0; i < n; i++)
+            gdb_read_byte (s, buf[i]);
+        }
+      else if (n == 0 || errno != EAGAIN)
+        {
+          /* XXX: Connection closed.  Should probably wait for annother
+             connection before continuing.  */
+          return sig;
+        }
+  }
+  sig = s->signal;
+  s->signal = 0;
+  return sig;
+}
+
+/* Tell the remote gdb that the process has exited.  */
+void gdb_exit(CPUState *env, int code)
+{
+  GDBState *s;
+  char buf[4];
+
+  s = &gdbserver_state;
+  if (gdbserver_fd < 0 || s->fd < 0)
+    return;
+
+  snprintf(buf, sizeof(buf), "W%02x", code);
+  put_packet(s, buf);
+}
+
+
+static void gdb_accept(void *opaque)
+{
+    GDBState *s;
+    int       fd;
+
+    for(;;) {
+        fd = socket_accept(gdbserver_fd, NULL);
+        if (fd < 0) {
+            perror("accept");
+            return;
+        } else if (fd >= 0) {
+            break;
+        }
+    }
+
+    /* set short latency */
+    socket_set_lowlatency(fd);
+    
+    s = &gdbserver_state;
+    memset (s, 0, sizeof (GDBState));
+    s->env = first_cpu; /* XXX: allow to change CPU */
+    s->fd = fd;
+
+    gdb_syscall_state = s;
+
+    socket_set_nonblock(fd);
+}
+
+static int gdbserver_open(int port)
+{
+    SockAddress  sockaddr;
+    int fd, val, ret;
+
+    fd = socket_create_inet( SOCKET_STREAM );
+    if (fd < 0) {
+        perror("socket");
+        return -1;
+    }
+
+    /* allow fast reuse */
+    socket_set_xreuseaddr(fd);
+
+    sock_address_init_inet( &sockaddr, port, SOCK_ADDRESS_INET_ANY );
+    ret = socket_bind(fd, &sockaddr);
+    if (ret < 0) {
+        perror("bind");
+        return -1;
+    }
+    ret = socket_listen(fd, 0);
+    if (ret < 0) {
+        perror("listen");
+        socket_close(fd);
+        return -1;
+    }
+    return fd;
+}
+
+int gdbserver_start(int port)
+{
+    gdbserver_fd = gdbserver_open(port);
+    if (gdbserver_fd < 0)
+        return -1;
+    /* accept connections */
+    gdb_accept (NULL);
+    return 0;
+}
+#else
+static int gdb_chr_can_receive(void *opaque)
+{
+  return 1;
+}
+
+static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+    GDBState *s = opaque;
+    int i;
+
+    for (i = 0; i < size; i++) {
+        gdb_read_byte(s, buf[i]);
+    }
+}
+
+static void gdb_chr_event(void *opaque, int event)
+{
+    switch (event) {
+    case CHR_EVENT_RESET:
+        vm_stop(EXCP_INTERRUPT);
+        gdb_syscall_state = opaque;
+        break;
+    default:
+        break;
+    }
+}
+
+int gdbserver_start(const char *port)
+{
+    GDBState *s;
+    char gdbstub_port_name[128];
+    int port_num;
+    char *p;
+    CharDriverState *chr;
+
+    if (!port || !*port)
+      return -1;
+
+    port_num = strtol(port, &p, 10);
+    if (*p == 0) {
+        /* A numeric value is interpreted as a port number.  */
+        snprintf(gdbstub_port_name, sizeof(gdbstub_port_name),
+                 "tcp::%d,nowait,nodelay,server", port_num);
+        port = gdbstub_port_name;
+    }
+
+    chr = qemu_chr_open(port);
+    if (!chr)
+        return -1;
+
+    s = qemu_mallocz(sizeof(GDBState));
+    if (!s) {
+        return -1;
+    }
+    s->env = first_cpu; /* XXX: allow to change CPU */
+    s->chr = chr;
+    qemu_chr_add_handlers(chr, gdb_chr_can_receive, gdb_chr_receive,
+                          gdb_chr_event, s);
+    qemu_add_vm_stop_handler(gdb_vm_stopped, s);
+    return 0;
+}
+#endif
diff --git a/gdbstub.h b/gdbstub.h
new file mode 100644
index 0000000..ba65f93
--- /dev/null
+++ b/gdbstub.h
@@ -0,0 +1,19 @@
+#ifndef GDBSTUB_H
+#define GDBSTUB_H
+
+#define DEFAULT_GDBSTUB_PORT "1234"
+
+typedef void (*gdb_syscall_complete_cb)(CPUState *env,
+                                        target_ulong ret, target_ulong err);
+
+void gdb_do_syscall(gdb_syscall_complete_cb cb, const char *fmt, ...);
+int use_gdb_syscalls(void);
+#ifdef CONFIG_USER_ONLY
+int gdb_handlesig (CPUState *, int);
+void gdb_exit(CPUState *, int);
+int gdbserver_start(int);
+#else
+int gdbserver_start(const char *port);
+#endif
+
+#endif
diff --git a/gen-charmap.py b/gen-charmap.py
new file mode 100644
index 0000000..3c86350
--- /dev/null
+++ b/gen-charmap.py
@@ -0,0 +1,180 @@
+#!/usr/bin/python
+#
+# a python script used to generate some C constant tables from a key charmap file
+#
+# usage:
+#    progname file.kcm > charmap-tab.h
+#
+import sys, os, string, re
+
+header = """\
+#include "android_charmap.h"
+
+/* the following is automatically generated by the 'gen-charmap.py' script
+ * do not touch. the generation command was:
+ *   gen-charmap.py\
+"""
+
+header2 = """
+ */
+"""
+
+kmap_header = """\
+static const AKeyEntry  _%(name)s_keys[] =
+{
+   /* keycode                   base   caps    fn  caps+fn   number */
+"""
+
+
+kmap_footer = """\
+};
+
+static const AKeyCharmap  _%(name)s_charmap =
+{
+    _%(name)s_keys,
+    %(count)d,
+    "%(name)s"
+};
+"""
+
+
+re_mapname = re.compile( r".*/(\w+).kcm" )
+re_start = re.compile( r"(\w+)\s*(.*)" )
+re_char  = re.compile( r"('.')\s*(.*)" )
+re_hex   = re.compile( r"(0x\w+)\s*(.*)" )
+
+specials = { 'COMMA': 'Comma',
+             'PERIOD': 'Period',
+             'AT': 'At',
+             'LEFT_BRACKET': 'LeftBracket',
+             'RIGHT_BRACKET': 'RightBracket',
+             'SLASH': 'Slash',
+             'BACKSLASH': 'Backslash',
+             'GRAVE': 'Grave',
+             'MINUS': 'Minus',
+             'EQUALS': 'Equals',
+             'SEMICOLON': 'Semicolon',
+             'APOSTROPHE': 'Apostrophe',
+             'SPACE': 'Space',
+             'ENTER': 'Enter',
+             'TAB': 'Tab'
+           }
+
+entries = []
+
+def match_char_or_hex(line):
+    m = re_char.match(line)
+    if not m:
+        m = re_hex.match(line)
+    return m
+
+def quote(s):
+    if s == "'''":
+        s = "'\\''"
+    elif s == "'\\'":
+        s = "'\\\\'"
+    return s
+
+def process_line(line,result):
+    m = re_start.match(line)
+    if not m:
+        print "bad bad line: " + line
+        return -1
+    keycode = m.group(1)
+    line    = m.group(2)
+    m = match_char_or_hex(line)
+    if not m:
+        print "character expected in: " + line
+        return -1
+    base = quote(m.group(1))
+    line = m.group(2)
+    m = match_char_or_hex(line)
+    if not m:
+        print "character expected in: " + line
+        return -1
+    caps = quote(m.group(1))
+    line = m.group(2)
+    m = match_char_or_hex(line)
+    if not m:
+        print "character expected in: " + line
+        return -1
+    fn = quote(m.group(1))
+    line = m.group(2)
+    m = match_char_or_hex(line)
+    if not m:
+        print "character expected in: " + line
+        return -1
+    caps_fn = quote(m.group(1))
+    line = m.group(2)
+    m = match_char_or_hex(line)
+    if not m:
+        print "character expected in: " + line
+        return -1
+    number = quote(m.group(1))
+
+    if specials.has_key(keycode):
+        keycode = specials[keycode]
+    keycode = "kKeyCode" + keycode
+
+    result.append( (keycode,base,caps,fn,caps_fn,number) )
+    return 0
+
+def process_file( file ):
+    result = []
+    fp = open(file,"rb")
+    for line in fp.xreadlines():
+        line = line.strip()
+        if not line:   # skip empty lines
+            continue
+        if line[0] == '#' or line[0] == '[':  # skip
+            continue
+        if process_line(line,result) < 0:
+            break
+    fp.close()
+    return result
+
+class KMap:
+    def __init__(self,name,results):
+        self.name    = name
+        self.results = results
+
+    def dump(self):
+        t = { 'name': self.name, 'count':len(self.results) }
+        print kmap_header % t
+        for item in self.results:
+            print "    { %-22s, %5s, %5s, %5s, %6s, %5s }," % item
+        print kmap_footer % t
+
+kmaps = []
+
+if len(sys.argv) < 2:
+    print "usage: progname  charmap.kcm [charmap2.kcm ...] > charmap-tab.h"
+else:
+    genline = ""
+    for filepath in sys.argv[1:]:
+        m = re_mapname.match(filepath)
+        if not m:
+            print "%s is not a keyboard charmap name" % filepath
+            os.exit(1)
+
+        mapname = m.group(1)
+        genline = genline + " " + mapname + ".kcm"
+
+    for filepath in sys.argv[1:]:
+        m = re_mapname.match(filepath)
+        mapname = m.group(1)
+        result = process_file( filepath )
+        kmap = KMap(mapname,result)
+        kmaps.append(kmap)
+
+    print header + genline + header2
+    for kmap in kmaps:
+        kmap.dump()
+
+    print "const AKeyCharmap*  android_charmaps[%d] = {" % len(kmaps),
+    comma = ""
+    for kmap in kmaps:
+        print "%s&_%s_charmap" % (comma, kmap.name),
+        comma = ", "
+    print "};"
+    print "const int           android_charmap_count = %d;" % len(kmaps)
diff --git a/gen-icount.h b/gen-icount.h
new file mode 100644
index 0000000..61545f1
--- /dev/null
+++ b/gen-icount.h
@@ -0,0 +1,56 @@
+/* Helpers for instruction counting code generation.  */
+
+static TCGArg *icount_arg;
+static int icount_label;
+
+static inline void gen_icount_start(void)
+{
+    TCGv count;
+
+    if (!use_icount)
+        return;
+
+    icount_label = gen_new_label();
+    /* FIXME: This generates lousy code.  We can't use tcg_new_temp because
+       count needs to live over the conditional branch.  To workaround this
+       we allow the target to supply a convenient register temporary.  */
+#ifndef ICOUNT_TEMP
+    count = tcg_temp_local_new(TCG_TYPE_I32);
+#else
+    count = ICOUNT_TEMP;
+#endif
+    tcg_gen_ld_i32(count, cpu_env, offsetof(CPUState, icount_decr.u32));
+    /* This is a horrid hack to allow fixing up the value later.  */
+    icount_arg = gen_opparam_ptr + 1;
+    tcg_gen_subi_i32(count, count, 0xdeadbeef);
+
+    tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, icount_label);
+    tcg_gen_st16_i32(count, cpu_env, offsetof(CPUState, icount_decr.u16.low));
+#ifndef ICOUNT_TEMP
+    tcg_temp_free(count);
+#endif
+}
+
+static void gen_icount_end(TranslationBlock *tb, int num_insns)
+{
+    if (use_icount) {
+        *icount_arg = num_insns;
+        gen_set_label(icount_label);
+        tcg_gen_exit_tb((long)tb + 2);
+    }
+}
+
+static void inline gen_io_start(void)
+{
+    TCGv tmp = tcg_const_i32(1);
+    tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, can_do_io));
+    tcg_temp_free(tmp);
+}
+
+static inline void gen_io_end(void)
+{
+    TCGv tmp = tcg_const_i32(0);
+    tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, can_do_io));
+    tcg_temp_free(tmp);
+}
+
diff --git a/gen-skin.py b/gen-skin.py
new file mode 100755
index 0000000..f87bde7
--- /dev/null
+++ b/gen-skin.py
@@ -0,0 +1,78 @@
+#!/usr/bin/python
+#
+# a python script used to generate the "default-skin.h' header file
+# from a given skin directory
+#
+# usage:
+#    progname skin-directory-path > default-skin.h
+#
+import sys, os, string, re
+
+header = """\
+/* automatically generated, do not touch */
+
+"""
+
+
+footer = """\
+
+static const FileEntry  _file_entries[] =
+{
+"""
+
+footer2 = """\
+    { NULL, NULL, 0 }
+};
+"""
+
+
+entries = []
+
+def process_files( basepath, files ):
+    for file in files:
+        fp = open(basepath + "/" + file, "rb")
+        data = fp.read()
+        data_len  = len(data)
+        data_add  = 0
+        data_name = "_data_" + string.replace(file,".","_")
+
+        entries.append( (file, data_name, len(data)) )
+        print "static const unsigned char %s[%d] = {" % (data_name, data_len + data_add)
+        comma = "    "
+        do_line = 0
+        do_comma = 0
+        count = 0
+        line  = "    "
+        for b in data:
+            d = ord(b)
+
+            if do_comma:
+                line = line + ","
+                do_comma = 0
+
+            if do_line:
+                print line
+                line = "    "
+                do_line = 0
+
+            line = line + "%3d" % d
+            do_comma = 1
+            count += 1
+            if count == 16:
+                count = 0
+                do_line = 1
+
+        if len(line) > 0:
+            print line
+        print "};\n"
+
+if len(sys.argv) != 2:
+    print "usage: progname  skindirpath > default-skin.h"
+else:
+    print header
+    skindir = sys.argv[1]
+    process_files( skindir, os.listdir(skindir) )
+    print footer
+    for e in entries:
+        print "    { \"%s\", %s, %d }," % (e[0], e[1], e[2])
+    print footer2
diff --git a/host-defs.h b/host-defs.h
new file mode 100644
index 0000000..686416a
--- /dev/null
+++ b/host-defs.h
@@ -0,0 +1,18 @@
+#ifndef _HOST_DEFS_H
+#define _HOST_DEFS_H
+
+/* all host-specific definitions should go here */
+
+#include "config-host.h"
+#include <stdint.h>
+
+#if HOST_LONG_BITS == 32
+typedef int32_t   host_long;
+typedef uint32_t  host_ulong;
+#elif HOST_LONG_BITS == 64
+typedef int64_t   host_long;
+typedef uint64_t  host_ulong;
+#endif
+
+#endif /* _HOST_DEFS_H */
+
diff --git a/host-utils.c b/host-utils.c
new file mode 100644
index 0000000..f92c339
--- /dev/null
+++ b/host-utils.c
@@ -0,0 +1,104 @@
+/*
+ * Utility compute operations used by translated code.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2007 Aurelien Jarno
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "exec.h"
+#include "host-utils.h"
+
+//#define DEBUG_MULDIV
+
+/* Long integer helpers */
+#if !defined(__x86_64__)
+static void add128 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+    *plow += a;
+    /* carry test */
+    if (*plow < a)
+        (*phigh)++;
+    *phigh += b;
+}
+
+static void neg128 (uint64_t *plow, uint64_t *phigh)
+{
+    *plow = ~*plow;
+    *phigh = ~*phigh;
+    add128(plow, phigh, 1, 0);
+}
+
+static void mul64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+    uint32_t a0, a1, b0, b1;
+    uint64_t v;
+
+    a0 = a;
+    a1 = a >> 32;
+
+    b0 = b;
+    b1 = b >> 32;
+
+    v = (uint64_t)a0 * (uint64_t)b0;
+    *plow = v;
+    *phigh = 0;
+
+    v = (uint64_t)a0 * (uint64_t)b1;
+    add128(plow, phigh, v << 32, v >> 32);
+
+    v = (uint64_t)a1 * (uint64_t)b0;
+    add128(plow, phigh, v << 32, v >> 32);
+
+    v = (uint64_t)a1 * (uint64_t)b1;
+    *phigh += v;
+}
+
+/* Unsigned 64x64 -> 128 multiplication */
+void mulu64 (uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b)
+{
+    mul64(plow, phigh, a, b);
+#if defined(DEBUG_MULDIV)
+    printf("mulu64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
+           a, b, *phigh, *plow);
+#endif
+}
+
+/* Signed 64x64 -> 128 multiplication */
+void muls64 (uint64_t *plow, uint64_t *phigh, int64_t a, int64_t b)
+{
+    int sa, sb;
+
+    sa = (a < 0);
+    if (sa)
+        a = -a;
+    sb = (b < 0);
+    if (sb)
+        b = -b;
+    mul64(plow, phigh, a, b);
+    if (sa ^ sb) {
+        neg128(plow, phigh);
+    }
+#if defined(DEBUG_MULDIV)
+    printf("muls64: 0x%016llx * 0x%016llx = 0x%016llx%016llx\n",
+           a, b, *phigh, *plow);
+#endif
+}
+#endif /* !defined(__x86_64__) */
diff --git a/host-utils.h b/host-utils.h
new file mode 100644
index 0000000..b1e799e
--- /dev/null
+++ b/host-utils.h
@@ -0,0 +1,204 @@
+/*
+ * Utility compute operations used by translated code.
+ *
+ * Copyright (c) 2007 Thiemo Seufer
+ * Copyright (c) 2007 Jocelyn Mayer
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "osdep.h"
+
+#if defined(__x86_64__)
+#define __HAVE_FAST_MULU64__
+static always_inline void mulu64 (uint64_t *plow, uint64_t *phigh,
+                                  uint64_t a, uint64_t b)
+{
+    __asm__ ("mul %0\n\t"
+             : "=d" (*phigh), "=a" (*plow)
+             : "a" (a), "0" (b));
+}
+#define __HAVE_FAST_MULS64__
+static always_inline void muls64 (uint64_t *plow, uint64_t *phigh,
+                                  int64_t a, int64_t b)
+{
+    __asm__ ("imul %0\n\t"
+             : "=d" (*phigh), "=a" (*plow)
+             : "a" (a), "0" (b));
+}
+#else
+void muls64(uint64_t *phigh, uint64_t *plow, int64_t a, int64_t b);
+void mulu64(uint64_t *phigh, uint64_t *plow, uint64_t a, uint64_t b);
+#endif
+
+/* Note that some of those functions may end up calling libgcc functions,
+   depending on the host machine. It is up to the target emulation to
+   cope with that. */
+
+/* Binary search for leading zeros.  */
+
+static always_inline int clz32(uint32_t val)
+{
+    int cnt = 0;
+
+    if (!(val & 0xFFFF0000U)) {
+        cnt += 16;
+        val <<= 16;
+    }
+    if (!(val & 0xFF000000U)) {
+        cnt += 8;
+        val <<= 8;
+    }
+    if (!(val & 0xF0000000U)) {
+        cnt += 4;
+        val <<= 4;
+    }
+    if (!(val & 0xC0000000U)) {
+        cnt += 2;
+        val <<= 2;
+    }
+    if (!(val & 0x80000000U)) {
+        cnt++;
+        val <<= 1;
+    }
+    if (!(val & 0x80000000U)) {
+        cnt++;
+    }
+    return cnt;
+}
+
+static always_inline int clo32(uint32_t val)
+{
+    return clz32(~val);
+}
+
+static always_inline int clz64(uint64_t val)
+{
+    int cnt = 0;
+
+    if (!(val >> 32)) {
+        cnt += 32;
+    } else {
+        val >>= 32;
+    }
+
+    return cnt + clz32(val);
+}
+
+static always_inline int clo64(uint64_t val)
+{
+    return clz64(~val);
+}
+
+static always_inline int ctz32 (uint32_t val)
+{
+    int cnt;
+
+    cnt = 0;
+    if (!(val & 0x0000FFFFUL)) {
+         cnt += 16;
+        val >>= 16;
+     }
+    if (!(val & 0x000000FFUL)) {
+         cnt += 8;
+        val >>= 8;
+     }
+    if (!(val & 0x0000000FUL)) {
+         cnt += 4;
+        val >>= 4;
+     }
+    if (!(val & 0x00000003UL)) {
+         cnt += 2;
+        val >>= 2;
+     }
+    if (!(val & 0x00000001UL)) {
+         cnt++;
+        val >>= 1;
+     }
+    if (!(val & 0x00000001UL)) {
+         cnt++;
+     }
+
+     return cnt;
+ }
+ 
+static always_inline int cto32 (uint32_t val)
+ {
+    return ctz32(~val);
+}
+
+static always_inline int ctz64 (uint64_t val)
+{
+    int cnt;
+
+    cnt = 0;
+    if (!((uint32_t)val)) {
+        cnt += 32;
+        val >>= 32;
+    }
+
+    return cnt + ctz32(val);
+}
+
+static always_inline int cto64 (uint64_t val)
+{
+    return ctz64(~val);
+}
+
+static always_inline int ctpop8 (uint8_t val)
+{
+    val = (val & 0x55) + ((val >> 1) & 0x55);
+    val = (val & 0x33) + ((val >> 2) & 0x33);
+    val = (val & 0x0f) + ((val >> 4) & 0x0f);
+
+    return val;
+}
+
+static always_inline int ctpop16 (uint16_t val)
+{
+    val = (val & 0x5555) + ((val >> 1) & 0x5555);
+    val = (val & 0x3333) + ((val >> 2) & 0x3333);
+    val = (val & 0x0f0f) + ((val >> 4) & 0x0f0f);
+    val = (val & 0x00ff) + ((val >> 8) & 0x00ff);
+
+    return val;
+}
+
+static always_inline int ctpop32 (uint32_t val)
+{
+    val = (val & 0x55555555) + ((val >>  1) & 0x55555555);
+    val = (val & 0x33333333) + ((val >>  2) & 0x33333333);
+    val = (val & 0x0f0f0f0f) + ((val >>  4) & 0x0f0f0f0f);
+    val = (val & 0x00ff00ff) + ((val >>  8) & 0x00ff00ff);
+    val = (val & 0x0000ffff) + ((val >> 16) & 0x0000ffff);
+
+    return val;
+}
+
+static always_inline int ctpop64 (uint64_t val)
+{
+    val = (val & 0x5555555555555555ULL) + ((val >>  1) & 0x5555555555555555ULL);
+    val = (val & 0x3333333333333333ULL) + ((val >>  2) & 0x3333333333333333ULL);
+    val = (val & 0x0f0f0f0f0f0f0f0fULL) + ((val >>  4) & 0x0f0f0f0f0f0f0f0fULL);
+    val = (val & 0x00ff00ff00ff00ffULL) + ((val >>  8) & 0x00ff00ff00ff00ffULL);
+    val = (val & 0x0000ffff0000ffffULL) + ((val >> 16) & 0x0000ffff0000ffffULL);
+    val = (val & 0x00000000ffffffffULL) + ((val >> 32) & 0x00000000ffffffffULL);
+
+    return val;
+}
diff --git a/hostregs_helper.h b/hostregs_helper.h
new file mode 100644
index 0000000..4fdf8ad
--- /dev/null
+++ b/hostregs_helper.h
@@ -0,0 +1,98 @@
+/*
+ *  Save/restore host registrs.
+ *
+ *  Copyright (c) 2007 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* The GCC global register vairable extension is used to reserve some
+   host registers for use by dyngen.  However only the core parts of the
+   translation engine are compiled with these settings.  We must manually
+   save/restore these registers when called from regular code.
+   It is not sufficient to save/restore T0 et. al. as these may be declared
+   with a datatype smaller than the actual register.  */
+
+#if defined(DECLARE_HOST_REGS)
+
+#define DO_REG(REG)					\
+    register host_reg_t reg_AREG##REG asm(AREG##REG);	\
+    volatile host_reg_t saved_AREG##REG;
+
+#elif defined(SAVE_HOST_REGS)
+
+#define DO_REG(REG)					\
+    __asm__ __volatile__ ("" : "=r" (reg_AREG##REG));	\
+    saved_AREG##REG = reg_AREG##REG;
+
+#else
+
+#define DO_REG(REG)                                     \
+    reg_AREG##REG = saved_AREG##REG;		        \
+    __asm__ __volatile__ ("" : : "r" (reg_AREG##REG));
+
+#endif
+
+#ifdef AREG0
+DO_REG(0)
+#endif
+
+#ifdef AREG1
+DO_REG(1)
+#endif
+
+#ifdef AREG2
+DO_REG(2)
+#endif
+
+#ifdef AREG3
+DO_REG(3)
+#endif
+
+#ifdef AREG4
+DO_REG(4)
+#endif
+
+#ifdef AREG5
+DO_REG(5)
+#endif
+
+#ifdef AREG6
+DO_REG(6)
+#endif
+
+#ifdef AREG7
+DO_REG(7)
+#endif
+
+#ifdef AREG8
+DO_REG(8)
+#endif
+
+#ifdef AREG9
+DO_REG(9)
+#endif
+
+#ifdef AREG10
+DO_REG(10)
+#endif
+
+#ifdef AREG11
+DO_REG(11)
+#endif
+
+#undef SAVE_HOST_REGS
+#undef DECLARE_HOST_REGS
+#undef DO_REG
diff --git a/hpet.h b/hpet.h
new file mode 100644
index 0000000..754051a
--- /dev/null
+++ b/hpet.h
@@ -0,0 +1,22 @@
+#ifndef	__HPET__
+#define	__HPET__ 1
+
+
+
+struct hpet_info {
+	unsigned long hi_ireqfreq;	/* Hz */
+	unsigned long hi_flags;	/* information */
+	unsigned short hi_hpet;
+	unsigned short hi_timer;
+};
+
+#define	HPET_INFO_PERIODIC	0x0001	/* timer is periodic */
+
+#define	HPET_IE_ON	_IO('h', 0x01)	/* interrupt on */
+#define	HPET_IE_OFF	_IO('h', 0x02)	/* interrupt off */
+#define	HPET_INFO	_IOR('h', 0x03, struct hpet_info)
+#define	HPET_EPI	_IO('h', 0x04)	/* enable periodic */
+#define	HPET_DPI	_IO('h', 0x05)	/* disable periodic */
+#define	HPET_IRQFREQ	_IOW('h', 0x6, unsigned long)	/* IRQFREQ usec */
+
+#endif				/* !__HPET__ */
diff --git a/hw/android_arm.c b/hw/android_arm.c
new file mode 100644
index 0000000..efc8ba1
--- /dev/null
+++ b/hw/android_arm.c
@@ -0,0 +1,175 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "hw.h"
+#include "boards.h"
+#include "devices.h"
+#include "net.h"
+#include "arm_pic.h"
+#include "sysemu.h"
+#include "goldfish_device.h"
+#include "android/globals.h"
+#include "audio/audio.h"
+#include "arm-misc.h"
+
+#define ARM_CPU_SAVE_VERSION  1
+
+int android_audio_enabled;
+char* audio_input_source = NULL;
+
+void goldfish_memlog_init(uint32_t base);
+
+static struct goldfish_device event0_device = {
+    .name = "goldfish_events",
+    .id = 0,
+    .size = 0x1000,
+    .irq_count = 1
+};
+
+static struct goldfish_device nand_device = {
+    .name = "goldfish_nand",
+    .id = 0,
+    .size = 0x1000
+};
+
+static struct goldfish_device trace_device = {
+    .name = "qemu_trace",
+    .id = -1,
+    .size = 0x1000
+};
+
+/* Board init.  */
+
+#define TEST_SWITCH 1
+#if TEST_SWITCH
+uint32_t switch_test_write(void *opaque, uint32_t state)
+{
+    goldfish_switch_set_state(opaque, state);
+    return state;
+}
+#endif
+
+static void android_arm_init(ram_addr_t ram_size, int vga_ram_size,
+    const char *boot_device, DisplayState *ds, 
+    const char *kernel_filename, 
+    const char *kernel_cmdline,
+    const char *initrd_filename,
+    const char *cpu_model)
+{
+    CPUState *env;
+    qemu_irq *cpu_pic;
+    qemu_irq *goldfish_pic;
+    int i;
+    struct arm_boot_info  info;
+
+    if (!cpu_model)
+        cpu_model = "arm926";
+
+    env = cpu_init(cpu_model);
+
+    register_savevm( "cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load, env );
+
+    cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+    cpu_pic = arm_pic_init_cpu(env);
+    goldfish_pic = goldfish_interrupt_init(0xff000000, cpu_pic[ARM_PIC_CPU_IRQ], cpu_pic[ARM_PIC_CPU_FIQ]);
+    goldfish_device_init(goldfish_pic, 0xff010000, 0x7f0000, 10, 22);
+
+    goldfish_device_bus_init(0xff001000, 1);
+
+    goldfish_timer_and_rtc_init(0xff003000, 3);
+
+    goldfish_tty_add(serial_hds[0], 0, 0xff002000, 4);
+    for(i = 1; i < MAX_SERIAL_PORTS; i++) {
+        //printf("android_arm_init serial %d %x\n", i, serial_hds[i]);
+        if(serial_hds[i]) {
+            goldfish_tty_add(serial_hds[i], i, 0, 0);
+        }
+    }
+
+    for(i = 0; i < MAX_NICS; i++) {
+        if (nd_table[i].vlan) {
+            if (nd_table[i].model == NULL
+                || strcmp(nd_table[i].model, "smc91c111") == 0) {
+                struct goldfish_device *smc_device;
+                smc_device = qemu_mallocz(sizeof(*smc_device));
+                smc_device->name = "smc91x";
+                smc_device->id = i;
+                smc_device->size = 0x1000;
+                smc_device->irq_count = 1;
+                goldfish_add_device_no_io(smc_device);
+                smc91c111_init(&nd_table[i], smc_device->base, goldfish_pic[smc_device->irq]);
+            } else {
+                fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+                exit (1);
+            }
+        }
+    }
+
+    goldfish_fb_init(ds, 0);
+#ifdef HAS_AUDIO
+    if (android_audio_enabled) {
+        AUD_init();
+        goldfish_audio_init(0xff004000, 0, audio_input_source);
+    }
+#endif
+    {
+        int  idx = drive_get_index( IF_IDE, 0, 0 );
+        if (idx >= 0)
+            goldfish_mmc_init(0xff005000, 0, drives_table[idx].bdrv);
+    }
+
+    goldfish_memlog_init(0xff006000);
+
+    if (android_hw->hw_battery)
+        goldfish_battery_init();
+
+    goldfish_add_device_no_io(&event0_device);
+    events_dev_init(event0_device.base, goldfish_pic[event0_device.irq]);
+
+#ifdef CONFIG_NAND
+    goldfish_add_device_no_io(&nand_device);
+    nand_dev_init(nand_device.base);
+#endif
+#ifdef CONFIG_TRACE
+    extern const char *trace_filename;
+    if(trace_filename != NULL) {
+        goldfish_add_device_no_io(&trace_device);
+        trace_dev_init(trace_device.base);
+    }
+#endif
+
+#if TEST_SWITCH
+    {
+        void *sw;
+        sw = goldfish_switch_add("test", NULL, NULL, 0);
+        goldfish_switch_set_state(sw, 1);
+        goldfish_switch_add("test2", switch_test_write, sw, 1);
+    }
+#endif
+
+    memset(&info, 0, sizeof info);
+    info.ram_size        = ram_size;
+    info.kernel_filename = kernel_filename;
+    info.kernel_cmdline  = kernel_cmdline;
+    info.initrd_filename = initrd_filename;
+    info.nb_cpus         = 1;
+    info.board_id        = 1441;
+
+    arm_load_kernel(env, &info);
+}
+
+QEMUMachine android_arm_machine = {
+    "android_arm",
+    "ARM Android Emulator",
+    android_arm_init,
+    NULL
+};
diff --git a/hw/arm-misc.h b/hw/arm-misc.h
new file mode 100644
index 0000000..707e699
--- /dev/null
+++ b/hw/arm-misc.h
@@ -0,0 +1,49 @@
+/*
+ * Misc ARM declarations
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ */
+
+#ifndef ARM_MISC_H
+#define ARM_MISC_H 1
+
+#include "cpu.h"
+
+/* The CPU is also modeled as an interrupt controller.  */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+qemu_irq *arm_pic_init_cpu(CPUState *env);
+
+/* armv7m.c */
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+                      const char *kernel_filename, const char *cpu_model);
+
+/* arm_boot.c */
+struct arm_boot_info {
+    int ram_size;
+    const char *kernel_filename;
+    const char *kernel_cmdline;
+    const char *initrd_filename;
+    target_phys_addr_t loader_start;
+    int nb_cpus;
+    int board_id;
+    int (*atag_board)(struct arm_boot_info *info, void *p);
+};
+void arm_load_kernel(CPUState *env, struct arm_boot_info *info);
+
+/* armv7m_nvic.c */
+
+/* Multiplication factor to convert from system clock ticks to qemu timer
+   ticks.  */
+int system_clock_scale;
+qemu_irq *armv7m_nvic_init(CPUState *env);
+
+/* stellaris_enent.c */
+void stellaris_enet_init(NICInfo *nd, uint32_t base, qemu_irq irq);
+
+#endif /* !ARM_MISC_H */
+
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
new file mode 100644
index 0000000..5990961
--- /dev/null
+++ b/hw/arm_boot.c
@@ -0,0 +1,251 @@
+/*
+ * ARM kernel loader.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+#define KERNEL_ARGS_ADDR 0x100
+#define KERNEL_LOAD_ADDR 0x00010000
+#define INITRD_LOAD_ADDR 0x00800000
+
+/* The worlds second smallest bootloader.  Set r0-r2, then jump to kernel.  */
+static uint32_t bootloader[] = {
+  0xe3a00000, /* mov     r0, #0 */
+  0xe3a01000, /* mov     r1, #0x?? */
+  0xe3811c00, /* orr     r1, r1, #0x??00 */
+  0xe59f2000, /* ldr     r2, [pc, #0] */
+  0xe59ff000, /* ldr     pc, [pc, #0] */
+  0, /* Address of kernel args.  Set by integratorcp_init.  */
+  0  /* Kernel entry point.  Set by integratorcp_init.  */
+};
+
+/* Entry point for secondary CPUs.  Enable interrupt controller and
+   Issue WFI until start address is written to system controller.  */
+static uint32_t smpboot[] = {
+  0xe3a00201, /* mov     r0, #0x10000000 */
+  0xe3800601, /* orr     r0, r0, #0x001000000 */
+  0xe3a01001, /* mov     r1, #1 */
+  0xe5801100, /* str     r1, [r0, #0x100] */
+  0xe3a00201, /* mov     r0, #0x10000000 */
+  0xe3800030, /* orr     r0, #0x30 */
+  0xe320f003, /* wfi */
+  0xe5901000, /* ldr     r1, [r0] */
+  0xe3110003, /* tst     r1, #3 */
+  0x1afffffb, /* bne     <wfi> */
+  0xe12fff11  /* bx      r1 */
+};
+
+static void main_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+
+    cpu_reset(env);
+    if (env->boot_info)
+        arm_load_kernel(env, env->boot_info);
+
+    /* TODO:  Reset secondary CPUs.  */
+}
+
+static void set_kernel_args(struct arm_boot_info *info,
+                int initrd_size, void *base)
+{
+    uint32_t *p;
+
+    p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
+    /* ATAG_CORE */
+    stl_raw(p++, 5);
+    stl_raw(p++, 0x54410001);
+    stl_raw(p++, 1);
+    stl_raw(p++, 0x1000);
+    stl_raw(p++, 0);
+    /* ATAG_MEM */
+    /* TODO: handle multiple chips on one ATAG list */
+    stl_raw(p++, 4);
+    stl_raw(p++, 0x54410002);
+    stl_raw(p++, info->ram_size);
+    stl_raw(p++, info->loader_start);
+    if (initrd_size) {
+        /* ATAG_INITRD2 */
+        stl_raw(p++, 4);
+        stl_raw(p++, 0x54420005);
+        stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
+        stl_raw(p++, initrd_size);
+    }
+    if (info->kernel_cmdline && *info->kernel_cmdline) {
+        /* ATAG_CMDLINE */
+        int cmdline_size;
+
+        cmdline_size = strlen(info->kernel_cmdline);
+        memcpy(p + 2, info->kernel_cmdline, cmdline_size + 1);
+        cmdline_size = (cmdline_size >> 2) + 1;
+        stl_raw(p++, cmdline_size + 2);
+        stl_raw(p++, 0x54410009);
+        p += cmdline_size;
+    }
+    if (info->atag_board) {
+        /* ATAG_BOARD */
+        int atag_board_len;
+
+        atag_board_len = (info->atag_board(info, p + 2) + 3) >> 2;
+        stl_raw(p++, 2 + atag_board_len);
+        stl_raw(p++, 0x414f4d50);
+        p += atag_board_len;
+    }
+    /* ATAG_END */
+    stl_raw(p++, 0);
+    stl_raw(p++, 0);
+}
+
+static void set_kernel_args_old(struct arm_boot_info *info,
+                int initrd_size, void *base)
+{
+    uint32_t *p;
+    unsigned char *s;
+
+    /* see linux/include/asm-arm/setup.h */
+    p = (uint32_t *)(base + KERNEL_ARGS_ADDR);
+    /* page_size */
+    stl_raw(p++, 4096);
+    /* nr_pages */
+    stl_raw(p++, info->ram_size / 4096);
+    /* ramdisk_size */
+    stl_raw(p++, 0);
+#define FLAG_READONLY	1
+#define FLAG_RDLOAD	4
+#define FLAG_RDPROMPT	8
+    /* flags */
+    stl_raw(p++, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
+    /* rootdev */
+    stl_raw(p++, (31 << 8) | 0);	/* /dev/mtdblock0 */
+    /* video_num_cols */
+    stl_raw(p++, 0);
+    /* video_num_rows */
+    stl_raw(p++, 0);
+    /* video_x */
+    stl_raw(p++, 0);
+    /* video_y */
+    stl_raw(p++, 0);
+    /* memc_control_reg */
+    stl_raw(p++, 0);
+    /* unsigned char sounddefault */
+    /* unsigned char adfsdrives */
+    /* unsigned char bytes_per_char_h */
+    /* unsigned char bytes_per_char_v */
+    stl_raw(p++, 0);
+    /* pages_in_bank[4] */
+    stl_raw(p++, 0);
+    stl_raw(p++, 0);
+    stl_raw(p++, 0);
+    stl_raw(p++, 0);
+    /* pages_in_vram */
+    stl_raw(p++, 0);
+    /* initrd_start */
+    if (initrd_size)
+        stl_raw(p++, info->loader_start + INITRD_LOAD_ADDR);
+    else
+        stl_raw(p++, 0);
+    /* initrd_size */
+    stl_raw(p++, initrd_size);
+    /* rd_start */
+    stl_raw(p++, 0);
+    /* system_rev */
+    stl_raw(p++, 0);
+    /* system_serial_low */
+    stl_raw(p++, 0);
+    /* system_serial_high */
+    stl_raw(p++, 0);
+    /* mem_fclk_21285 */
+    stl_raw(p++, 0);
+    /* zero unused fields */
+    memset(p, 0, 256 + 1024 -
+           (p - ((uint32_t *)(base + KERNEL_ARGS_ADDR))));
+    s = base + KERNEL_ARGS_ADDR + 256 + 1024;
+    if (info->kernel_cmdline)
+        strcpy (s, info->kernel_cmdline);
+    else
+        stb_raw(s, 0);
+}
+
+void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
+{
+    int kernel_size;
+    int initrd_size;
+    int n;
+    int is_linux = 0;
+    uint64_t elf_entry;
+    target_ulong entry;
+    uint32_t pd;
+    void *loader_phys;
+
+    /* Load the kernel.  */
+    if (!info->kernel_filename) {
+        fprintf(stderr, "Kernel image must be specified\n");
+        exit(1);
+    }
+
+    if (!env->boot_info) {
+        if (info->nb_cpus == 0)
+            info->nb_cpus = 1;
+        env->boot_info = info;
+        qemu_register_reset(main_cpu_reset, env);
+    }
+
+    pd = cpu_get_physical_page_desc(info->loader_start);
+    loader_phys = phys_ram_base + (pd & TARGET_PAGE_MASK) +
+            (info->loader_start & ~TARGET_PAGE_MASK);
+
+    /* Assume that raw images are linux kernels, and ELF images are not.  */
+    kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL);
+    entry = elf_entry;
+    if (kernel_size < 0) {
+        kernel_size = load_uboot(info->kernel_filename, &entry, &is_linux);
+    }
+    if (kernel_size < 0) {
+        kernel_size = load_image(info->kernel_filename,
+                                 loader_phys + KERNEL_LOAD_ADDR);
+        entry = info->loader_start + KERNEL_LOAD_ADDR;
+        is_linux = 1;
+    }
+    if (kernel_size < 0) {
+        fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                info->kernel_filename);
+        exit(1);
+    }
+    if (!is_linux) {
+        /* Jump to the entry point.  */
+        env->regs[15] = entry & 0xfffffffe;
+        env->thumb = entry & 1;
+    } else {
+        if (info->initrd_filename) {
+            initrd_size = load_image(info->initrd_filename,
+                                     loader_phys + INITRD_LOAD_ADDR);
+            if (initrd_size < 0) {
+                fprintf(stderr, "qemu: could not load initrd '%s'\n",
+                        info->initrd_filename);
+                exit(1);
+            }
+        } else {
+            initrd_size = 0;
+        }
+        bootloader[1] |= info->board_id & 0xff;
+        bootloader[2] |= (info->board_id >> 8) & 0xff;
+        bootloader[5] = info->loader_start + KERNEL_ARGS_ADDR;
+        bootloader[6] = entry;
+        for (n = 0; n < sizeof(bootloader) / 4; n++)
+            stl_raw(loader_phys + (n * 4), bootloader[n]);
+        if (info->nb_cpus > 1)
+            for (n = 0; n < sizeof(smpboot) / 4; n++)
+                stl_raw(loader_phys + info->ram_size + (n * 4), smpboot[n]);
+        if (old_param)
+            set_kernel_args_old(info, initrd_size, loader_phys);
+        else
+            set_kernel_args(info, initrd_size, loader_phys);
+    }
+}
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
new file mode 100644
index 0000000..54e99f4
--- /dev/null
+++ b/hw/arm_gic.c
@@ -0,0 +1,747 @@
+/*
+ * ARM Generic/Distributed Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* This file contains implementation code for the RealView EB interrupt
+   controller, MPCore distributed interrupt controller and ARMv7-M
+   Nested Vectored Interrupt Controller.  */
+
+//#define DEBUG_GIC
+
+#ifdef DEBUG_GIC
+#define DPRINTF(fmt, args...) \
+do { printf("arm_gic: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#ifdef NVIC
+static const uint8_t gic_id[] =
+{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
+#define GIC_DIST_OFFSET 0
+/* The NVIC has 16 internal vectors.  However these are not exposed
+   through the normal GIC interface.  */
+#define GIC_BASE_IRQ    32
+#else
+static const uint8_t gic_id[] =
+{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+#define GIC_DIST_OFFSET 0x1000
+#define GIC_BASE_IRQ    0
+#endif
+
+typedef struct gic_irq_state
+{
+    /* ??? The documentation seems to imply the enable bits are global, even
+       for per-cpu interrupts.  This seems strange.  */
+    unsigned enabled:1;
+    unsigned pending:NCPU;
+    unsigned active:NCPU;
+    unsigned level:1;
+    unsigned model:1; /* 0 = N:N, 1 = 1:N */
+    unsigned trigger:1; /* nonzero = edge triggered.  */
+} gic_irq_state;
+
+#define ALL_CPU_MASK ((1 << NCPU) - 1)
+
+#define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
+#define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
+#define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
+#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
+#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
+#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
+#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
+#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
+#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
+#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
+#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
+#define GIC_TEST_MODEL(irq) s->irq_state[irq].model
+#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
+#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
+#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
+#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
+#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
+#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_GET_PRIORITY(irq, cpu) \
+  (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
+#ifdef NVIC
+#define GIC_TARGET(irq) 1
+#else
+#define GIC_TARGET(irq) s->irq_target[irq]
+#endif
+
+typedef struct gic_state
+{
+    uint32_t base;
+    qemu_irq parent_irq[NCPU];
+    int enabled;
+    int cpu_enabled[NCPU];
+
+    gic_irq_state irq_state[GIC_NIRQ];
+#ifndef NVIC
+    int irq_target[GIC_NIRQ];
+#endif
+    int priority1[32][NCPU];
+    int priority2[GIC_NIRQ - 32];
+    int last_active[GIC_NIRQ][NCPU];
+
+    int priority_mask[NCPU];
+    int running_irq[NCPU];
+    int running_priority[NCPU];
+    int current_pending[NCPU];
+
+    qemu_irq *in;
+#ifdef NVIC
+    void *nvic;
+#endif
+} gic_state;
+
+/* TODO: Many places that call this routine could be optimized.  */
+/* Update interrupt status after enabled or pending bits have been changed.  */
+static void gic_update(gic_state *s)
+{
+    int best_irq;
+    int best_prio;
+    int irq;
+    int level;
+    int cpu;
+    int cm;
+
+    for (cpu = 0; cpu < NCPU; cpu++) {
+        cm = 1 << cpu;
+        s->current_pending[cpu] = 1023;
+        if (!s->enabled || !s->cpu_enabled[cpu]) {
+	    qemu_irq_lower(s->parent_irq[cpu]);
+            return;
+        }
+        best_prio = 0x100;
+        best_irq = 1023;
+        for (irq = 0; irq < GIC_NIRQ; irq++) {
+            if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
+                if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
+                    best_prio = GIC_GET_PRIORITY(irq, cpu);
+                    best_irq = irq;
+                }
+            }
+        }
+        level = 0;
+        if (best_prio <= s->priority_mask[cpu]) {
+            s->current_pending[cpu] = best_irq;
+            if (best_prio < s->running_priority[cpu]) {
+                DPRINTF("Raised pending IRQ %d\n", best_irq);
+                level = 1;
+            }
+        }
+        qemu_set_irq(s->parent_irq[cpu], level);
+    }
+}
+
+static void __attribute__((unused))
+gic_set_pending_private(gic_state *s, int cpu, int irq)
+{
+    int cm = 1 << cpu;
+
+    if (GIC_TEST_PENDING(irq, cm))
+        return;
+
+    DPRINTF("Set %d pending cpu %d\n", irq, cpu);
+    GIC_SET_PENDING(irq, cm);
+    gic_update(s);
+}
+
+/* Process a change in an external IRQ input.  */
+static void gic_set_irq(void *opaque, int irq, int level)
+{
+    gic_state *s = (gic_state *)opaque;
+    /* The first external input line is internal interrupt 32.  */
+    irq += 32;
+    if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
+        return;
+
+    if (level) {
+        GIC_SET_LEVEL(irq, ALL_CPU_MASK);
+        if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
+            DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
+            GIC_SET_PENDING(irq, GIC_TARGET(irq));
+        }
+    } else {
+        GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
+    }
+    gic_update(s);
+}
+
+static void gic_set_running_irq(gic_state *s, int cpu, int irq)
+{
+    s->running_irq[cpu] = irq;
+    if (irq == 1023) {
+        s->running_priority[cpu] = 0x100;
+    } else {
+        s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+    }
+    gic_update(s);
+}
+
+static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
+{
+    int new_irq;
+    int cm = 1 << cpu;
+    new_irq = s->current_pending[cpu];
+    if (new_irq == 1023
+            || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
+        DPRINTF("ACK no pending IRQ\n");
+        return 1023;
+    }
+    s->last_active[new_irq][cpu] = s->running_irq[cpu];
+    /* Clear pending flags for both level and edge triggered interrupts.
+       Level triggered IRQs will be reasserted once they become inactive.  */
+    GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
+    gic_set_running_irq(s, cpu, new_irq);
+    DPRINTF("ACK %d\n", new_irq);
+    return new_irq;
+}
+
+static void gic_complete_irq(gic_state * s, int cpu, int irq)
+{
+    int update = 0;
+    int cm = 1 << cpu;
+    DPRINTF("EOI %d\n", irq);
+    if (s->running_irq[cpu] == 1023)
+        return; /* No active IRQ.  */
+    if (irq != 1023) {
+        /* Mark level triggered interrupts as pending if they are still
+           raised.  */
+        if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
+                && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+            DPRINTF("Set %d pending mask %x\n", irq, cm);
+            GIC_SET_PENDING(irq, cm);
+            update = 1;
+        }
+    }
+    if (irq != s->running_irq[cpu]) {
+        /* Complete an IRQ that is not currently running.  */
+        int tmp = s->running_irq[cpu];
+        while (s->last_active[tmp][cpu] != 1023) {
+            if (s->last_active[tmp][cpu] == irq) {
+                s->last_active[tmp][cpu] = s->last_active[irq][cpu];
+                break;
+            }
+            tmp = s->last_active[tmp][cpu];
+        }
+        if (update) {
+            gic_update(s);
+        }
+    } else {
+        /* Complete the current running IRQ.  */
+        gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
+    }
+}
+
+static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
+{
+    gic_state *s = (gic_state *)opaque;
+    uint32_t res;
+    int irq;
+    int i;
+    int cpu;
+    int cm;
+    int mask;
+
+    cpu = gic_get_current_cpu();
+    cm = 1 << cpu;
+    offset -= s->base + GIC_DIST_OFFSET;
+    if (offset < 0x100) {
+#ifndef NVIC
+        if (offset == 0)
+            return s->enabled;
+        if (offset == 4)
+            return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
+        if (offset < 0x08)
+            return 0;
+#endif
+        goto bad_reg;
+    } else if (offset < 0x200) {
+        /* Interrupt Set/Clear Enable.  */
+        if (offset < 0x180)
+            irq = (offset - 0x100) * 8;
+        else
+            irq = (offset - 0x180) * 8;
+        irq += GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        res = 0;
+        for (i = 0; i < 8; i++) {
+            if (GIC_TEST_ENABLED(irq + i)) {
+                res |= (1 << i);
+            }
+        }
+    } else if (offset < 0x300) {
+        /* Interrupt Set/Clear Pending.  */
+        if (offset < 0x280)
+            irq = (offset - 0x200) * 8;
+        else
+            irq = (offset - 0x280) * 8;
+        irq += GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        res = 0;
+        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        for (i = 0; i < 8; i++) {
+            if (GIC_TEST_PENDING(irq + i, mask)) {
+                res |= (1 << i);
+            }
+        }
+    } else if (offset < 0x400) {
+        /* Interrupt Active.  */
+        irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        res = 0;
+        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        for (i = 0; i < 8; i++) {
+            if (GIC_TEST_ACTIVE(irq + i, mask)) {
+                res |= (1 << i);
+            }
+        }
+    } else if (offset < 0x800) {
+        /* Interrupt Priority.  */
+        irq = (offset - 0x400) + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        res = GIC_GET_PRIORITY(irq, cpu);
+#ifndef NVIC
+    } else if (offset < 0xc00) {
+        /* Interrupt CPU Target.  */
+        irq = (offset - 0x800) + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq >= 29 && irq <= 31) {
+            res = cm;
+        } else {
+            res = GIC_TARGET(irq);
+        }
+    } else if (offset < 0xf00) {
+        /* Interrupt Configuration.  */
+        irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        res = 0;
+        for (i = 0; i < 4; i++) {
+            if (GIC_TEST_MODEL(irq + i))
+                res |= (1 << (i * 2));
+            if (GIC_TEST_TRIGGER(irq + i))
+                res |= (2 << (i * 2));
+        }
+#endif
+    } else if (offset < 0xfe0) {
+        goto bad_reg;
+    } else /* offset >= 0xfe0 */ {
+        if (offset & 3) {
+            res = 0;
+        } else {
+            res = gic_id[(offset - 0xfe0) >> 2];
+        }
+    }
+    return res;
+bad_reg:
+    cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
+    return 0;
+}
+
+static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t val;
+    val = gic_dist_readb(opaque, offset);
+    val |= gic_dist_readb(opaque, offset + 1) << 8;
+    return val;
+}
+
+static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t val;
+#ifdef NVIC
+    gic_state *s = (gic_state *)opaque;
+    uint32_t addr;
+    addr = offset - s->base;
+    if (addr < 0x100 || addr > 0xd00)
+        return nvic_readl(s->nvic, addr);
+#endif
+    val = gic_dist_readw(opaque, offset);
+    val |= gic_dist_readw(opaque, offset + 2) << 16;
+    return val;
+}
+
+static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
+                            uint32_t value)
+{
+    gic_state *s = (gic_state *)opaque;
+    int irq;
+    int i;
+    int cpu;
+
+    cpu = gic_get_current_cpu();
+    offset -= s->base + GIC_DIST_OFFSET;
+    if (offset < 0x100) {
+#ifdef NVIC
+        goto bad_reg;
+#else
+        if (offset == 0) {
+            s->enabled = (value & 1);
+            DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
+        } else if (offset < 4) {
+            /* ignored.  */
+        } else {
+            goto bad_reg;
+        }
+#endif
+    } else if (offset < 0x180) {
+        /* Interrupt Set Enable.  */
+        irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq < 16)
+          value = 0xff;
+        for (i = 0; i < 8; i++) {
+            if (value & (1 << i)) {
+                int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
+                if (!GIC_TEST_ENABLED(irq + i))
+                    DPRINTF("Enabled IRQ %d\n", irq + i);
+                GIC_SET_ENABLED(irq + i);
+                /* If a raised level triggered IRQ enabled then mark
+                   is as pending.  */
+                if (GIC_TEST_LEVEL(irq + i, mask)
+                        && !GIC_TEST_TRIGGER(irq + i)) {
+                    DPRINTF("Set %d pending mask %x\n", irq + i, mask);
+                    GIC_SET_PENDING(irq + i, mask);
+                }
+            }
+        }
+    } else if (offset < 0x200) {
+        /* Interrupt Clear Enable.  */
+        irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq < 16)
+          value = 0;
+        for (i = 0; i < 8; i++) {
+            if (value & (1 << i)) {
+                if (GIC_TEST_ENABLED(irq + i))
+                    DPRINTF("Disabled IRQ %d\n", irq + i);
+                GIC_CLEAR_ENABLED(irq + i);
+            }
+        }
+    } else if (offset < 0x280) {
+        /* Interrupt Set Pending.  */
+        irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq < 16)
+          irq = 0;
+
+        for (i = 0; i < 8; i++) {
+            if (value & (1 << i)) {
+                GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
+            }
+        }
+    } else if (offset < 0x300) {
+        /* Interrupt Clear Pending.  */
+        irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        for (i = 0; i < 8; i++) {
+            /* ??? This currently clears the pending bit for all CPUs, even
+               for per-CPU interrupts.  It's unclear whether this is the
+               corect behavior.  */
+            if (value & (1 << i)) {
+                GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
+            }
+        }
+    } else if (offset < 0x400) {
+        /* Interrupt Active.  */
+        goto bad_reg;
+    } else if (offset < 0x800) {
+        /* Interrupt Priority.  */
+        irq = (offset - 0x400) + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq < 32) {
+            s->priority1[irq][cpu] = value;
+        } else {
+            s->priority2[irq - 32] = value;
+        }
+#ifndef NVIC
+    } else if (offset < 0xc00) {
+        /* Interrupt CPU Target.  */
+        irq = (offset - 0x800) + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq < 29)
+            value = 0;
+        else if (irq < 32)
+            value = ALL_CPU_MASK;
+        s->irq_target[irq] = value & ALL_CPU_MASK;
+    } else if (offset < 0xf00) {
+        /* Interrupt Configuration.  */
+        irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
+        if (irq >= GIC_NIRQ)
+            goto bad_reg;
+        if (irq < 32)
+            value |= 0xaa;
+        for (i = 0; i < 4; i++) {
+            if (value & (1 << (i * 2))) {
+                GIC_SET_MODEL(irq + i);
+            } else {
+                GIC_CLEAR_MODEL(irq + i);
+            }
+            if (value & (2 << (i * 2))) {
+                GIC_SET_TRIGGER(irq + i);
+            } else {
+                GIC_CLEAR_TRIGGER(irq + i);
+            }
+        }
+#endif
+    } else {
+        /* 0xf00 is only handled for 32-bit writes.  */
+        goto bad_reg;
+    }
+    gic_update(s);
+    return;
+bad_reg:
+    cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
+}
+
+static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
+                            uint32_t value)
+{
+    gic_dist_writeb(opaque, offset, value & 0xff);
+    gic_dist_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
+                            uint32_t value)
+{
+    gic_state *s = (gic_state *)opaque;
+#ifdef NVIC
+    uint32_t addr;
+    addr = offset - s->base;
+    if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
+        nvic_writel(s->nvic, addr, value);
+        return;
+    }
+#endif
+    if (offset - s->base == GIC_DIST_OFFSET + 0xf00) {
+        int cpu;
+        int irq;
+        int mask;
+
+        cpu = gic_get_current_cpu();
+        irq = value & 0x3ff;
+        switch ((value >> 24) & 3) {
+        case 0:
+            mask = (value >> 16) & ALL_CPU_MASK;
+            break;
+        case 1:
+            mask = 1 << cpu;
+            break;
+        case 2:
+            mask = ALL_CPU_MASK ^ (1 << cpu);
+            break;
+        default:
+            DPRINTF("Bad Soft Int target filter\n");
+            mask = ALL_CPU_MASK;
+            break;
+        }
+        GIC_SET_PENDING(irq, mask);
+        gic_update(s);
+        return;
+    }
+    gic_dist_writew(opaque, offset, value & 0xffff);
+    gic_dist_writew(opaque, offset + 2, value >> 16);
+}
+
+static CPUReadMemoryFunc *gic_dist_readfn[] = {
+   gic_dist_readb,
+   gic_dist_readw,
+   gic_dist_readl
+};
+
+static CPUWriteMemoryFunc *gic_dist_writefn[] = {
+   gic_dist_writeb,
+   gic_dist_writew,
+   gic_dist_writel
+};
+
+#ifndef NVIC
+static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
+{
+    switch (offset) {
+    case 0x00: /* Control */
+        return s->cpu_enabled[cpu];
+    case 0x04: /* Priority mask */
+        return s->priority_mask[cpu];
+    case 0x08: /* Binary Point */
+        /* ??? Not implemented.  */
+        return 0;
+    case 0x0c: /* Acknowledge */
+        return gic_acknowledge_irq(s, cpu);
+    case 0x14: /* Runing Priority */
+        return s->running_priority[cpu];
+    case 0x18: /* Highest Pending Interrupt */
+        return s->current_pending[cpu];
+    default:
+        cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
+                  (int)offset);
+        return 0;
+    }
+}
+
+static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
+{
+    switch (offset) {
+    case 0x00: /* Control */
+        s->cpu_enabled[cpu] = (value & 1);
+        DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
+        break;
+    case 0x04: /* Priority mask */
+        s->priority_mask[cpu] = (value & 0xff);
+        break;
+    case 0x08: /* Binary Point */
+        /* ??? Not implemented.  */
+        break;
+    case 0x10: /* End Of Interrupt */
+        return gic_complete_irq(s, cpu, value & 0x3ff);
+    default:
+        cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
+                  (int)offset);
+        return;
+    }
+    gic_update(s);
+}
+#endif
+
+static void gic_reset(gic_state *s)
+{
+    int i;
+    memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
+    for (i = 0 ; i < NCPU; i++) {
+        s->priority_mask[i] = 0xf0;
+        s->current_pending[i] = 1023;
+        s->running_irq[i] = 1023;
+        s->running_priority[i] = 0x100;
+#ifdef NVIC
+        /* The NVIC doesn't have per-cpu interfaces, so enable by default.  */
+        s->cpu_enabled[i] = 1;
+#else
+        s->cpu_enabled[i] = 0;
+#endif
+    }
+    for (i = 0; i < 16; i++) {
+        GIC_SET_ENABLED(i);
+        GIC_SET_TRIGGER(i);
+    }
+#ifdef NVIC
+    /* The NVIC is always enabled.  */
+    s->enabled = 1;
+#else
+    s->enabled = 0;
+#endif
+}
+
+static void gic_save(QEMUFile *f, void *opaque)
+{
+    gic_state *s = (gic_state *)opaque;
+    int i;
+    int j;
+
+    qemu_put_be32(f, s->enabled);
+    for (i = 0; i < NCPU; i++) {
+        qemu_put_be32(f, s->cpu_enabled[i]);
+#ifndef NVIC
+        qemu_put_be32(f, s->irq_target[i]);
+#endif
+        for (j = 0; j < 32; j++)
+            qemu_put_be32(f, s->priority1[j][i]);
+        for (j = 0; j < GIC_NIRQ; j++)
+            qemu_put_be32(f, s->last_active[j][i]);
+        qemu_put_be32(f, s->priority_mask[i]);
+        qemu_put_be32(f, s->running_irq[i]);
+        qemu_put_be32(f, s->running_priority[i]);
+        qemu_put_be32(f, s->current_pending[i]);
+    }
+    for (i = 0; i < GIC_NIRQ - 32; i++) {
+        qemu_put_be32(f, s->priority2[i]);
+    }
+    for (i = 0; i < GIC_NIRQ; i++) {
+        qemu_put_byte(f, s->irq_state[i].enabled);
+        qemu_put_byte(f, s->irq_state[i].pending);
+        qemu_put_byte(f, s->irq_state[i].active);
+        qemu_put_byte(f, s->irq_state[i].level);
+        qemu_put_byte(f, s->irq_state[i].model);
+        qemu_put_byte(f, s->irq_state[i].trigger);
+    }
+}
+
+static int gic_load(QEMUFile *f, void *opaque, int version_id)
+{
+    gic_state *s = (gic_state *)opaque;
+    int i;
+    int j;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->enabled = qemu_get_be32(f);
+    for (i = 0; i < NCPU; i++) {
+        s->cpu_enabled[i] = qemu_get_be32(f);
+#ifndef NVIC
+        s->irq_target[i] = qemu_get_be32(f);
+#endif
+        for (j = 0; j < 32; j++)
+            s->priority1[j][i] = qemu_get_be32(f);
+        for (j = 0; j < GIC_NIRQ; j++)
+            s->last_active[j][i] = qemu_get_be32(f);
+        s->priority_mask[i] = qemu_get_be32(f);
+        s->running_irq[i] = qemu_get_be32(f);
+        s->running_priority[i] = qemu_get_be32(f);
+        s->current_pending[i] = qemu_get_be32(f);
+    }
+    for (i = 0; i < GIC_NIRQ - 32; i++) {
+        s->priority2[i] = qemu_get_be32(f);
+    }
+    for (i = 0; i < GIC_NIRQ; i++) {
+        s->irq_state[i].enabled = qemu_get_byte(f);
+        s->irq_state[i].pending = qemu_get_byte(f);
+        s->irq_state[i].active = qemu_get_byte(f);
+        s->irq_state[i].level = qemu_get_byte(f);
+        s->irq_state[i].model = qemu_get_byte(f);
+        s->irq_state[i].trigger = qemu_get_byte(f);
+    }
+
+    return 0;
+}
+
+static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
+{
+    gic_state *s;
+    int iomemtype;
+    int i;
+
+    s = (gic_state *)qemu_mallocz(sizeof(gic_state));
+    if (!s)
+        return NULL;
+    s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
+    for (i = 0; i < NCPU; i++) {
+        s->parent_irq[i] = parent_irq[i];
+    }
+    iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
+                                       gic_dist_writefn, s);
+    cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000,
+                                 iomemtype);
+    s->base = base;
+    gic_reset(s);
+    register_savevm("arm_gic", -1, 1, gic_save, gic_load, s);
+    return s;
+}
diff --git a/hw/arm_pic.c b/hw/arm_pic.c
new file mode 100644
index 0000000..1fe55b7
--- /dev/null
+++ b/hw/arm_pic.c
@@ -0,0 +1,48 @@
+/*
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+
+/* Stub functions for hardware that doesn't exist.  */
+void pic_info(void)
+{
+}
+
+void irq_info(void)
+{
+}
+
+
+/* Input 0 is IRQ and input 1 is FIQ.  */
+static void arm_pic_cpu_handler(void *opaque, int irq, int level)
+{
+    CPUState *env = (CPUState *)opaque;
+    switch (irq) {
+    case ARM_PIC_CPU_IRQ:
+        if (level)
+            cpu_interrupt(env, CPU_INTERRUPT_HARD);
+        else
+            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
+        break;
+    case ARM_PIC_CPU_FIQ:
+        if (level)
+            cpu_interrupt(env, CPU_INTERRUPT_FIQ);
+        else
+            cpu_reset_interrupt(env, CPU_INTERRUPT_FIQ);
+        break;
+    default:
+        cpu_abort(env, "arm_pic_cpu_handler: Bad interrput line %d\n", irq);
+    }
+}
+
+qemu_irq *arm_pic_init_cpu(CPUState *env)
+{
+    return qemu_allocate_irqs(arm_pic_cpu_handler, env, 2);
+}
diff --git a/hw/arm_pic.h b/hw/arm_pic.h
new file mode 100644
index 0000000..7886bcf
--- /dev/null
+++ b/hw/arm_pic.h
@@ -0,0 +1,25 @@
+/* 
+ * Generic ARM Programmable Interrupt Controller support.
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Arm hardware uses a wide variety of interrupt handling hardware.
+ * This provides a generic framework for connecting interrupt sources and
+ * inputs.
+ */
+
+#ifndef ARM_INTERRUPT_H
+#define ARM_INTERRUPT_H 1
+
+#include "irq.h"
+
+/* The CPU is also modeled as an interrupt controller.  */
+#define ARM_PIC_CPU_IRQ 0
+#define ARM_PIC_CPU_FIQ 1
+qemu_irq *arm_pic_init_cpu(CPUState *env);
+
+#endif /* !ARM_INTERRUPT_H */
+
diff --git a/hw/armv7m.c b/hw/armv7m.c
new file mode 100644
index 0000000..b2bad3c
--- /dev/null
+++ b/hw/armv7m.c
@@ -0,0 +1,206 @@
+/*
+ * ARMV7M System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "hw.h"
+#include "arm-misc.h"
+#include "sysemu.h"
+
+/* Bitbanded IO.  Each word corresponds to a single bit.  */
+
+/* Get the byte address of the real memory for a bitband acess.  */
+static inline uint32_t bitband_addr(uint32_t addr)
+{
+    uint32_t res;
+
+    res = addr & 0xe0000000;
+    res |= (addr & 0x1ffffff) >> 5;
+    return res;
+
+}
+
+static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
+{
+    uint8_t v;
+    cpu_physical_memory_read(bitband_addr(offset), &v, 1);
+    return (v & (1 << ((offset >> 2) & 7))) != 0;
+}
+
+static void bitband_writeb(void *opaque, target_phys_addr_t offset,
+                           uint32_t value)
+{
+    uint32_t addr;
+    uint8_t mask;
+    uint8_t v;
+    addr = bitband_addr(offset);
+    mask = (1 << ((offset >> 2) & 7));
+    cpu_physical_memory_read(addr, &v, 1);
+    if (value & 1)
+        v |= mask;
+    else
+        v &= ~mask;
+    cpu_physical_memory_write(addr, &v, 1);
+}
+
+static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t addr;
+    uint16_t mask;
+    uint16_t v;
+    addr = bitband_addr(offset) & ~1;
+    mask = (1 << ((offset >> 2) & 15));
+    mask = tswap16(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+    return (v & mask) != 0;
+}
+
+static void bitband_writew(void *opaque, target_phys_addr_t offset,
+                           uint32_t value)
+{
+    uint32_t addr;
+    uint16_t mask;
+    uint16_t v;
+    addr = bitband_addr(offset) & ~1;
+    mask = (1 << ((offset >> 2) & 15));
+    mask = tswap16(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+    if (value & 1)
+        v |= mask;
+    else
+        v &= ~mask;
+    cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
+}
+
+static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t addr;
+    uint32_t mask;
+    uint32_t v;
+    addr = bitband_addr(offset) & ~3;
+    mask = (1 << ((offset >> 2) & 31));
+    mask = tswap32(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+    return (v & mask) != 0;
+}
+
+static void bitband_writel(void *opaque, target_phys_addr_t offset,
+                           uint32_t value)
+{
+    uint32_t addr;
+    uint32_t mask;
+    uint32_t v;
+    addr = bitband_addr(offset) & ~3;
+    mask = (1 << ((offset >> 2) & 31));
+    mask = tswap32(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+    if (value & 1)
+        v |= mask;
+    else
+        v &= ~mask;
+    cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
+}
+
+static CPUReadMemoryFunc *bitband_readfn[] = {
+   bitband_readb,
+   bitband_readw,
+   bitband_readl
+};
+
+static CPUWriteMemoryFunc *bitband_writefn[] = {
+   bitband_writeb,
+   bitband_writew,
+   bitband_writel
+};
+
+static void armv7m_bitband_init(void)
+{
+    int iomemtype;
+
+    iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn,
+                                       NULL);
+    cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype);
+    cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype);
+}
+
+/* Board init.  */
+/* Init CPU and memory for a v7-M based board.
+   flash_size and sram_size are in kb.
+   Returns the NVIC array.  */
+
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+                      const char *kernel_filename, const char *cpu_model)
+{
+    CPUState *env;
+    qemu_irq *pic;
+    uint32_t pc;
+    int image_size;
+    uint64_t entry;
+    uint64_t lowaddr;
+
+    flash_size *= 1024;
+    sram_size *= 1024;
+
+    if (!cpu_model)
+	cpu_model = "cortex-m3";
+    env = cpu_init(cpu_model);
+    if (!env) {
+        fprintf(stderr, "Unable to find CPU definition\n");
+        exit(1);
+    }
+
+#if 0
+    /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
+       We don't have proper commandline options, so allocate half of memory
+       as SRAM, up to a maximum of 32Mb, and the rest as code.  */
+    if (ram_size > (512 + 32) * 1024 * 1024)
+        ram_size = (512 + 32) * 1024 * 1024;
+    sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
+    if (sram_size > 32 * 1024 * 1024)
+        sram_size = 32 * 1024 * 1024;
+    code_size = ram_size - sram_size;
+#endif
+
+    /* Flash programming is done via the SCU, so pretend it is ROM.  */
+    cpu_register_physical_memory(0, flash_size, IO_MEM_ROM);
+    cpu_register_physical_memory(0x20000000, sram_size,
+                                 flash_size + IO_MEM_RAM);
+    armv7m_bitband_init();
+
+    pic = armv7m_nvic_init(env);
+
+    image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
+    if (image_size < 0) {
+        image_size = load_image(kernel_filename, phys_ram_base);
+	lowaddr = 0;
+    }
+    if (image_size < 0) {
+        fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                kernel_filename);
+        exit(1);
+    }
+
+    /* If the image was loaded at address zero then assume it is a
+       regular ROM image and perform the normal CPU reset sequence.
+       Otherwise jump directly to the entry point.  */
+    if (lowaddr == 0) {
+	env->regs[13] = tswap32(*(uint32_t *)phys_ram_base);
+	pc = tswap32(*(uint32_t *)(phys_ram_base + 4));
+    } else {
+	pc = entry;
+    }
+    env->thumb = pc & 1;
+    env->regs[15] = pc & ~1;
+
+    /* Hack to map an additional page of ram at the top of the address
+       space.  This stops qemu complaining about executing code outside RAM
+       when returning from an exception.  */
+    cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size);
+
+    return pic;
+}
+
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
new file mode 100644
index 0000000..c55c958
--- /dev/null
+++ b/hw/armv7m_nvic.c
@@ -0,0 +1,407 @@
+/*
+ * ARM Nested Vectored Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ *
+ * The ARMv7M System controller is fairly tightly tied in with the
+ * NVIC.  Much of that is also implemented here.
+ */
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "arm-misc.h"
+
+/* 32 internal lines (16 used for system exceptions) plus 64 external
+   interrupt lines.  */
+#define GIC_NIRQ 96
+#define NCPU 1
+#define NVIC 1
+
+/* Only a single "CPU" interface is present.  */
+static inline int
+gic_get_current_cpu(void)
+{
+    return 0;
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset);
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
+
+#include "arm_gic.c"
+
+typedef struct {
+    struct {
+        uint32_t control;
+        uint32_t reload;
+        int64_t tick;
+        QEMUTimer *timer;
+    } systick;
+    gic_state *gic;
+} nvic_state;
+
+/* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
+#define SYSTICK_SCALE 1000ULL
+
+#define SYSTICK_ENABLE    (1 << 0)
+#define SYSTICK_TICKINT   (1 << 1)
+#define SYSTICK_CLKSOURCE (1 << 2)
+#define SYSTICK_COUNTFLAG (1 << 16)
+
+/* Conversion factor from qemu timer to SysTick frequencies.  */
+static inline int64_t systick_scale(nvic_state *s)
+{
+    if (s->systick.control & SYSTICK_CLKSOURCE)
+        return system_clock_scale;
+    else
+        return 1000;
+}
+
+static void systick_reload(nvic_state *s, int reset)
+{
+    if (reset)
+        s->systick.tick = qemu_get_clock(vm_clock);
+    s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
+    qemu_mod_timer(s->systick.timer, s->systick.tick);
+}
+
+static void systick_timer_tick(void * opaque)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    s->systick.control |= SYSTICK_COUNTFLAG;
+    if (s->systick.control & SYSTICK_TICKINT) {
+        /* Trigger the interrupt.  */
+        armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+    }
+    if (s->systick.reload == 0) {
+        s->systick.control &= ~SYSTICK_ENABLE;
+    } else {
+        systick_reload(s, 0);
+    }
+}
+
+/* The external routines use the hardware vector numbering, ie. the first
+   IRQ is #16.  The internal GIC routines use #32 as the first IRQ.  */
+void armv7m_nvic_set_pending(void *opaque, int irq)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    if (irq >= 16)
+        irq += 16;
+    gic_set_pending_private(s->gic, 0, irq);
+}
+
+/* Make pending IRQ active.  */
+int armv7m_nvic_acknowledge_irq(void *opaque)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    uint32_t irq;
+
+    irq = gic_acknowledge_irq(s->gic, 0);
+    if (irq == 1023)
+        cpu_abort(cpu_single_env, "Interrupt but no vector\n");
+    if (irq >= 32)
+        irq -= 16;
+    return irq;
+}
+
+void armv7m_nvic_complete_irq(void *opaque, int irq)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    if (irq >= 16)
+        irq += 16;
+    gic_complete_irq(s->gic, 0, irq);
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    uint32_t val;
+    int irq;
+
+    switch (offset) {
+    case 4: /* Interrupt Control Type.  */
+        return (GIC_NIRQ / 32) - 1;
+    case 0x10: /* SysTick Control and Status.  */
+        val = s->systick.control;
+        s->systick.control &= ~SYSTICK_COUNTFLAG;
+        return val;
+    case 0x14: /* SysTick Reload Value.  */
+        return s->systick.reload;
+    case 0x18: /* SysTick Current Value.  */
+        {
+            int64_t t;
+            if ((s->systick.control & SYSTICK_ENABLE) == 0)
+                return 0;
+            t = qemu_get_clock(vm_clock);
+            if (t >= s->systick.tick)
+                return 0;
+            val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
+            /* The interrupt in triggered when the timer reaches zero.
+               However the counter is not reloaded until the next clock
+               tick.  This is a hack to return zero during the first tick.  */
+            if (val > s->systick.reload)
+                val = 0;
+            return val;
+        }
+    case 0x1c: /* SysTick Calibration Value.  */
+        return 10000;
+    case 0xd00: /* CPUID Base.  */
+        return cpu_single_env->cp15.c0_cpuid;
+    case 0xd04: /* Interrypt Control State.  */
+        /* VECTACTIVE */
+        val = s->gic->running_irq[0];
+        if (val == 1023) {
+            val = 0;
+        } else if (val >= 32) {
+            val -= 16;
+        }
+        /* RETTOBASE */
+        if (s->gic->running_irq[0] == 1023
+                || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
+            val |= (1 << 11);
+        }
+        /* VECTPENDING */
+        if (s->gic->current_pending[0] != 1023)
+            val |= (s->gic->current_pending[0] << 12);
+        /* ISRPENDING */
+        for (irq = 32; irq < GIC_NIRQ; irq++) {
+            if (s->gic->irq_state[irq].pending) {
+                val |= (1 << 22);
+                break;
+            }
+        }
+        /* PENDSTSET */
+        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
+            val |= (1 << 26);
+        /* PENDSVSET */
+        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
+            val |= (1 << 28);
+        /* NMIPENDSET */
+        if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
+            val |= (1 << 31);
+        return val;
+    case 0xd08: /* Vector Table Offset.  */
+        return cpu_single_env->v7m.vecbase;
+    case 0xd0c: /* Application Interrupt/Reset Control.  */
+        return 0xfa05000;
+    case 0xd10: /* System Control.  */
+        /* TODO: Implement SLEEPONEXIT.  */
+        return 0;
+    case 0xd14: /* Configuration Control.  */
+        /* TODO: Implement Configuration Control bits.  */
+        return 0;
+    case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
+        irq = offset - 0xd14;
+        val = 0;
+        val = s->gic->priority1[irq++][0];
+        val = s->gic->priority1[irq++][0] << 8;
+        val = s->gic->priority1[irq++][0] << 16;
+        val = s->gic->priority1[irq][0] << 24;
+        return val;
+    case 0xd24: /* System Handler Status.  */
+        val = 0;
+        if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
+        if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
+        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
+        if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
+        if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
+        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
+        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
+        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
+        if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
+        if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
+        if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
+        if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
+        if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
+        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
+        return val;
+    case 0xd28: /* Configurable Fault Status.  */
+        /* TODO: Implement Fault Status.  */
+        cpu_abort(cpu_single_env,
+                  "Not implemented: Configurable Fault Status.");
+        return 0;
+    case 0xd2c: /* Hard Fault Status.  */
+    case 0xd30: /* Debug Fault Status.  */
+    case 0xd34: /* Mem Manage Address.  */
+    case 0xd38: /* Bus Fault Address.  */
+    case 0xd3c: /* Aux Fault Status.  */
+        /* TODO: Implement fault status registers.  */
+        goto bad_reg;
+    case 0xd40: /* PFR0.  */
+        return 0x00000030;
+    case 0xd44: /* PRF1.  */
+        return 0x00000200;
+    case 0xd48: /* DFR0.  */
+        return 0x00100000;
+    case 0xd4c: /* AFR0.  */
+        return 0x00000000;
+    case 0xd50: /* MMFR0.  */
+        return 0x00000030;
+    case 0xd54: /* MMFR1.  */
+        return 0x00000000;
+    case 0xd58: /* MMFR2.  */
+        return 0x00000000;
+    case 0xd5c: /* MMFR3.  */
+        return 0x00000000;
+    case 0xd60: /* ISAR0.  */
+        return 0x01141110;
+    case 0xd64: /* ISAR1.  */
+        return 0x02111000;
+    case 0xd68: /* ISAR2.  */
+        return 0x21112231;
+    case 0xd6c: /* ISAR3.  */
+        return 0x01111110;
+    case 0xd70: /* ISAR4.  */
+        return 0x01310102;
+    /* TODO: Implement debug registers.  */
+    default:
+    bad_reg:
+        cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
+    }
+}
+
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    uint32_t oldval;
+    switch (offset) {
+    case 0x10: /* SysTick Control and Status.  */
+        oldval = s->systick.control;
+        s->systick.control &= 0xfffffff8;
+        s->systick.control |= value & 7;
+        if ((oldval ^ value) & SYSTICK_ENABLE) {
+            int64_t now = qemu_get_clock(vm_clock);
+            if (value & SYSTICK_ENABLE) {
+                if (s->systick.tick) {
+                    s->systick.tick += now;
+                    qemu_mod_timer(s->systick.timer, s->systick.tick);
+                } else {
+                    systick_reload(s, 1);
+                }
+            } else {
+                qemu_del_timer(s->systick.timer);
+                s->systick.tick -= now;
+                if (s->systick.tick < 0)
+                  s->systick.tick = 0;
+            }
+        } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
+            /* This is a hack. Force the timer to be reloaded
+               when the reference clock is changed.  */
+            systick_reload(s, 1);
+        }
+        break;
+    case 0x14: /* SysTick Reload Value.  */
+        s->systick.reload = value;
+        break;
+    case 0x18: /* SysTick Current Value.  Writes reload the timer.  */
+        systick_reload(s, 1);
+        s->systick.control &= ~SYSTICK_COUNTFLAG;
+        break;
+    case 0xd04: /* Interrupt Control State.  */
+        if (value & (1 << 31)) {
+            armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
+        }
+        if (value & (1 << 28)) {
+            armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
+        } else if (value & (1 << 27)) {
+            s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
+            gic_update(s->gic);
+        }
+        if (value & (1 << 26)) {
+            armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+        } else if (value & (1 << 25)) {
+            s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
+            gic_update(s->gic);
+        }
+        break;
+    case 0xd08: /* Vector Table Offset.  */
+        cpu_single_env->v7m.vecbase = value & 0xffffff80;
+        break;
+    case 0xd0c: /* Application Interrupt/Reset Control.  */
+        if ((value >> 16) == 0x05fa) {
+            if (value & 2) {
+                cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
+            }
+            if (value & 5) {
+                cpu_abort(cpu_single_env, "System reset");
+            }
+        }
+        break;
+    case 0xd10: /* System Control.  */
+    case 0xd14: /* Configuration Control.  */
+        /* TODO: Implement control registers.  */
+        goto bad_reg;
+    case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
+        {
+            int irq;
+            irq = offset - 0xd14;
+            s->gic->priority1[irq++][0] = value & 0xff;
+            s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
+            s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
+            s->gic->priority1[irq][0] = (value >> 24) & 0xff;
+            gic_update(s->gic);
+        }
+        break;
+    case 0xd24: /* System Handler Control.  */
+        /* TODO: Real hardware allows you to set/clear the active bits
+           under some circumstances.  We don't implement this.  */
+        s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+        s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+        s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+        break;
+    case 0xd28: /* Configurable Fault Status.  */
+    case 0xd2c: /* Hard Fault Status.  */
+    case 0xd30: /* Debug Fault Status.  */
+    case 0xd34: /* Mem Manage Address.  */
+    case 0xd38: /* Bus Fault Address.  */
+    case 0xd3c: /* Aux Fault Status.  */
+        goto bad_reg;
+    default:
+    bad_reg:
+        cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
+    }
+}
+
+static void nvic_save(QEMUFile *f, void *opaque)
+{
+    nvic_state *s = (nvic_state *)opaque;
+
+    qemu_put_be32(f, s->systick.control);
+    qemu_put_be32(f, s->systick.reload);
+    qemu_put_be64(f, s->systick.tick);
+    qemu_put_timer(f, s->systick.timer);
+}
+
+static int nvic_load(QEMUFile *f, void *opaque, int version_id)
+{
+    nvic_state *s = (nvic_state *)opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    s->systick.control = qemu_get_be32(f);
+    s->systick.reload = qemu_get_be32(f);
+    s->systick.tick = qemu_get_be64(f);
+    qemu_get_timer(f, s->systick.timer);
+
+    return 0;
+}
+
+qemu_irq *armv7m_nvic_init(CPUState *env)
+{
+    nvic_state *s;
+    qemu_irq *parent;
+
+    parent = arm_pic_init_cpu(env);
+    s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
+    s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
+    s->gic->nvic = s;
+    s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
+    if (env->v7m.nvic)
+        cpu_abort(env, "CPU can only have one NVIC\n");
+    env->v7m.nvic = s;
+    register_savevm("armv7m_nvic", -1, 1, nvic_save, nvic_load, s);
+    return s->gic->in;
+}
diff --git a/hw/audiodev.h b/hw/audiodev.h
new file mode 100644
index 0000000..5f4a211
--- /dev/null
+++ b/hw/audiodev.h
@@ -0,0 +1,17 @@
+/* es1370.c */
+int es1370_init (PCIBus *bus, AudioState *s);
+
+/* sb16.c */
+int SB16_init (AudioState *s, qemu_irq *pic);
+
+/* adlib.c */
+int Adlib_init (AudioState *s, qemu_irq *pic);
+
+/* gus.c */
+int GUS_init (AudioState *s, qemu_irq *pic);
+
+/* ac97.c */
+int ac97_init (PCIBus *buf, AudioState *s);
+
+/* cs4231a.c */
+int cs4231a_init (AudioState *s, qemu_irq *pic);
diff --git a/hw/baum.h b/hw/baum.h
new file mode 100644
index 0000000..ac34b30
--- /dev/null
+++ b/hw/baum.h
@@ -0,0 +1,29 @@
+/*
+ * QEMU Baum
+ *
+ * Copyright (c) 2008 Samuel Thibault
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* usb device */
+USBDevice *usb_baum_init(void);
+
+/* char device */
+CharDriverState *chr_baum_init(void);
diff --git a/hw/boards.h b/hw/boards.h
new file mode 100644
index 0000000..cfb7c42
--- /dev/null
+++ b/hw/boards.h
@@ -0,0 +1,122 @@
+/* Declarations for use by board files for creating devices.  */
+
+#ifndef HW_BOARDS_H
+#define HW_BOARDS_H
+
+typedef void QEMUMachineInitFunc(ram_addr_t ram_size, int vga_ram_size,
+                                 const char *boot_device, DisplayState *ds,
+                                 const char *kernel_filename,
+                                 const char *kernel_cmdline,
+                                 const char *initrd_filename,
+                                 const char *cpu_model);
+
+typedef struct QEMUMachine {
+    const char *name;
+    const char *desc;
+    QEMUMachineInitFunc *init;
+#define RAMSIZE_FIXED	(1 << 0)
+    ram_addr_t ram_require;
+    int nodisk_ok;
+    struct QEMUMachine *next;
+} QEMUMachine;
+
+int qemu_register_machine(QEMUMachine *m);
+void register_machines(void);
+
+/* Axis ETRAX.  */
+extern QEMUMachine bareetraxfs_machine;
+
+/* pc.c */
+extern QEMUMachine pc_machine;
+extern QEMUMachine isapc_machine;
+
+/* ppc.c */
+extern QEMUMachine prep_machine;
+extern QEMUMachine core99_machine;
+extern QEMUMachine heathrow_machine;
+extern QEMUMachine ref405ep_machine;
+extern QEMUMachine taihu_machine;
+
+/* mips_r4k.c */
+extern QEMUMachine mips_machine;
+
+/* mips_jazz.c */
+extern QEMUMachine mips_magnum_machine;
+extern QEMUMachine mips_pica61_machine;
+
+/* mips_malta.c */
+extern QEMUMachine mips_malta_machine;
+
+/* mips_mipssim.c */
+extern QEMUMachine mips_mipssim_machine;
+
+/* shix.c */
+extern QEMUMachine shix_machine;
+
+/* r2d.c */
+extern QEMUMachine r2d_machine;
+
+/* sun4m.c */
+extern QEMUMachine ss5_machine, ss10_machine, ss600mp_machine, ss20_machine;
+extern QEMUMachine voyager_machine, ss_lx_machine, ss4_machine, scls_machine;
+extern QEMUMachine sbook_machine;
+extern QEMUMachine ss2_machine;
+extern QEMUMachine ss1000_machine, ss2000_machine;
+
+/* sun4u.c */
+extern QEMUMachine sun4u_machine;
+extern QEMUMachine sun4v_machine;
+
+/* integratorcp.c */
+extern QEMUMachine integratorcp_machine;
+
+/* versatilepb.c */
+extern QEMUMachine versatilepb_machine;
+extern QEMUMachine versatileab_machine;
+
+/* realview.c */
+extern QEMUMachine realview_machine;
+
+/* spitz.c */
+extern QEMUMachine akitapda_machine;
+extern QEMUMachine spitzpda_machine;
+extern QEMUMachine borzoipda_machine;
+extern QEMUMachine terrierpda_machine;
+
+/* palm.c */
+extern QEMUMachine palmte_machine;
+
+/* nseries.c */
+extern QEMUMachine n800_machine;
+extern QEMUMachine n810_machine;
+
+/* gumstix.c */
+extern QEMUMachine connex_machine;
+extern QEMUMachine verdex_machine;
+
+/* stellaris.c */
+extern QEMUMachine lm3s811evb_machine;
+extern QEMUMachine lm3s6965evb_machine;
+
+/* an5206.c */
+extern QEMUMachine an5206_machine;
+
+/* mcf5208.c */
+extern QEMUMachine mcf5208evb_machine;
+
+/* dummy_m68k.c */
+extern QEMUMachine dummy_m68k_machine;
+
+/* mainstone.c */
+extern QEMUMachine mainstone2_machine;
+
+/* musicpal.c */
+extern QEMUMachine musicpal_machine;
+
+/* tosa.c */
+extern QEMUMachine tosapda_machine;
+
+/* android_arm.c */
+extern QEMUMachine android_arm_machine;
+
+#endif
diff --git a/hw/cdrom.c b/hw/cdrom.c
new file mode 100644
index 0000000..2aa4d3b
--- /dev/null
+++ b/hw/cdrom.c
@@ -0,0 +1,157 @@
+/*
+ * QEMU ATAPI CD-ROM Emulator
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* ??? Most of the ATAPI emulation is still in ide.c.  It should be moved
+   here.  */
+
+#include "qemu-common.h"
+#include "scsi-disk.h"
+
+static void lba_to_msf(uint8_t *buf, int lba)
+{
+    lba += 150;
+    buf[0] = (lba / 75) / 60;
+    buf[1] = (lba / 75) % 60;
+    buf[2] = lba % 75;
+}
+
+/* same toc as bochs. Return -1 if error or the toc length */
+/* XXX: check this */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
+{
+    uint8_t *q;
+    int len;
+
+    if (start_track > 1 && start_track != 0xaa)
+        return -1;
+    q = buf + 2;
+    *q++ = 1; /* first session */
+    *q++ = 1; /* last session */
+    if (start_track <= 1) {
+        *q++ = 0; /* reserved */
+        *q++ = 0x14; /* ADR, control */
+        *q++ = 1;    /* track number */
+        *q++ = 0; /* reserved */
+        if (msf) {
+            *q++ = 0; /* reserved */
+            lba_to_msf(q, 0);
+            q += 3;
+        } else {
+            /* sector 0 */
+            cpu_to_be32wu((uint32_t *)q, 0);
+            q += 4;
+        }
+    }
+    /* lead out track */
+    *q++ = 0; /* reserved */
+    *q++ = 0x16; /* ADR, control */
+    *q++ = 0xaa; /* track number */
+    *q++ = 0; /* reserved */
+    if (msf) {
+        *q++ = 0; /* reserved */
+        lba_to_msf(q, nb_sectors);
+        q += 3;
+    } else {
+        cpu_to_be32wu((uint32_t *)q, nb_sectors);
+        q += 4;
+    }
+    len = q - buf;
+    cpu_to_be16wu((uint16_t *)buf, len - 2);
+    return len;
+}
+
+/* mostly same info as PearPc */
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
+{
+    uint8_t *q;
+    int len;
+
+    q = buf + 2;
+    *q++ = 1; /* first session */
+    *q++ = 1; /* last session */
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* data track */
+    *q++ = 0; /* track number */
+    *q++ = 0xa0; /* lead-in */
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    *q++ = 0;
+    *q++ = 1; /* first track */
+    *q++ = 0x00; /* disk type */
+    *q++ = 0x00;
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* data track */
+    *q++ = 0; /* track number */
+    *q++ = 0xa1;
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    *q++ = 0;
+    *q++ = 1; /* last track */
+    *q++ = 0x00;
+    *q++ = 0x00;
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* data track */
+    *q++ = 0; /* track number */
+    *q++ = 0xa2; /* lead-out */
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    if (msf) {
+        *q++ = 0; /* reserved */
+        lba_to_msf(q, nb_sectors);
+        q += 3;
+    } else {
+        cpu_to_be32wu((uint32_t *)q, nb_sectors);
+        q += 4;
+    }
+
+    *q++ = 1; /* session number */
+    *q++ = 0x14; /* ADR, control */
+    *q++ = 0;    /* track number */
+    *q++ = 1;    /* point */
+    *q++ = 0; /* min */
+    *q++ = 0; /* sec */
+    *q++ = 0; /* frame */
+    if (msf) {
+        *q++ = 0;
+        lba_to_msf(q, 0);
+        q += 3;
+    } else {
+        *q++ = 0;
+        *q++ = 0;
+        *q++ = 0;
+        *q++ = 0;
+    }
+
+    len = q - buf;
+    cpu_to_be16wu((uint16_t *)buf, len - 2);
+    return len;
+}
+
+
diff --git a/hw/devices.h b/hw/devices.h
new file mode 100644
index 0000000..45fead9
--- /dev/null
+++ b/hw/devices.h
@@ -0,0 +1,74 @@
+#ifndef QEMU_DEVICES_H
+#define QEMU_DEVICES_H
+
+/* Devices that have nowhere better to go.  */
+
+/* smc91c111.c */
+void smc91c111_init(NICInfo *, uint32_t, qemu_irq);
+
+/* ssd0323.c */
+int ssd0323_xfer_ssi(void *opaque, int data);
+void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p);
+
+/* ads7846.c */
+struct ads7846_state_s;
+uint32_t ads7846_read(void *opaque);
+void ads7846_write(void *opaque, uint32_t value);
+struct ads7846_state_s *ads7846_init(qemu_irq penirq);
+
+/* tsc210x.c */
+struct uwire_slave_s;
+struct mouse_transform_info_s;
+struct uwire_slave_s *tsc2102_init(qemu_irq pint, AudioState *audio);
+struct uwire_slave_s *tsc2301_init(qemu_irq penirq, qemu_irq kbirq,
+                qemu_irq dav, AudioState *audio);
+struct i2s_codec_s *tsc210x_codec(struct uwire_slave_s *chip);
+uint32_t tsc210x_txrx(void *opaque, uint32_t value, int len);
+void tsc210x_set_transform(struct uwire_slave_s *chip,
+                struct mouse_transform_info_s *info);
+void tsc210x_key_event(struct uwire_slave_s *chip, int key, int down);
+
+/* tsc2005.c */
+void *tsc2005_init(qemu_irq pintdav);
+uint32_t tsc2005_txrx(void *opaque, uint32_t value, int len);
+void tsc2005_set_transform(void *opaque, struct mouse_transform_info_s *info);
+
+/* stellaris_input.c */
+void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode);
+
+/* blizzard.c */
+void *s1d13745_init(qemu_irq gpio_int, DisplayState *ds);
+void s1d13745_write(void *opaque, int dc, uint16_t value);
+void s1d13745_write_block(void *opaque, int dc,
+                void *buf, size_t len, int pitch);
+uint16_t s1d13745_read(void *opaque, int dc);
+
+/* cbus.c */
+struct cbus_s {
+    qemu_irq clk;
+    qemu_irq dat;
+    qemu_irq sel;
+};
+struct cbus_s *cbus_init(qemu_irq dat_out);
+void cbus_attach(struct cbus_s *bus, void *slave_opaque);
+
+void *retu_init(qemu_irq irq, int vilma);
+void *tahvo_init(qemu_irq irq, int betty);
+
+void retu_key_event(void *retu, int state);
+
+/* tusb6010.c */
+struct tusb_s;
+struct tusb_s *tusb6010_init(qemu_irq intr);
+int tusb6010_sync_io(struct tusb_s *s);
+int tusb6010_async_io(struct tusb_s *s);
+void tusb6010_power(struct tusb_s *s, int on);
+
+/* tc6393xb.c */
+struct tc6393xb_s;
+struct tc6393xb_s *tc6393xb_init(uint32_t base, qemu_irq irq);
+void tc6393xb_gpio_out_set(struct tc6393xb_s *s, int line,
+                    qemu_irq handler);
+qemu_irq *tc6393xb_gpio_in_get(struct tc6393xb_s *s);
+
+#endif
diff --git a/hw/dma.c b/hw/dma.c
new file mode 100644
index 0000000..00c6332
--- /dev/null
+++ b/hw/dma.c
@@ -0,0 +1,548 @@
+/*
+ * QEMU DMA emulation
+ *
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "isa.h"
+
+/* #define DEBUG_DMA */
+
+#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#ifdef DEBUG_DMA
+#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#define ldebug(...) fprintf (stderr, "dma: " __VA_ARGS__)
+#else
+#define lwarn(...)
+#define linfo(...)
+#define ldebug(...)
+#endif
+
+#define LENOFA(a) ((int) (sizeof(a)/sizeof(a[0])))
+
+struct dma_regs {
+    int now[2];
+    uint16_t base[2];
+    uint8_t mode;
+    uint8_t page;
+    uint8_t pageh;
+    uint8_t dack;
+    uint8_t eop;
+    DMA_transfer_handler transfer_handler;
+    void *opaque;
+};
+
+#define ADDR 0
+#define COUNT 1
+
+static struct dma_cont {
+    uint8_t status;
+    uint8_t command;
+    uint8_t mask;
+    uint8_t flip_flop;
+    int dshift;
+    struct dma_regs regs[4];
+} dma_controllers[2];
+
+enum {
+    CMD_MEMORY_TO_MEMORY = 0x01,
+    CMD_FIXED_ADDRESS    = 0x02,
+    CMD_BLOCK_CONTROLLER = 0x04,
+    CMD_COMPRESSED_TIME  = 0x08,
+    CMD_CYCLIC_PRIORITY  = 0x10,
+    CMD_EXTENDED_WRITE   = 0x20,
+    CMD_LOW_DREQ         = 0x40,
+    CMD_LOW_DACK         = 0x80,
+    CMD_NOT_SUPPORTED    = CMD_MEMORY_TO_MEMORY | CMD_FIXED_ADDRESS
+    | CMD_COMPRESSED_TIME | CMD_CYCLIC_PRIORITY | CMD_EXTENDED_WRITE
+    | CMD_LOW_DREQ | CMD_LOW_DACK
+
+};
+
+static int channels[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
+
+static void write_page (void *opaque, uint32_t nport, uint32_t data)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel %#x %#x\n", nport, data);
+        return;
+    }
+    d->regs[ichan].page = data;
+}
+
+static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel %#x %#x\n", nport, data);
+        return;
+    }
+    d->regs[ichan].pageh = data;
+}
+
+static uint32_t read_page (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel read %#x\n", nport);
+        return 0;
+    }
+    return d->regs[ichan].page;
+}
+
+static uint32_t read_pageh (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+    int ichan;
+
+    ichan = channels[nport & 7];
+    if (-1 == ichan) {
+        dolog ("invalid channel read %#x\n", nport);
+        return 0;
+    }
+    return d->regs[ichan].pageh;
+}
+
+static inline void init_chan (struct dma_cont *d, int ichan)
+{
+    struct dma_regs *r;
+
+    r = d->regs + ichan;
+    r->now[ADDR] = r->base[ADDR] << d->dshift;
+    r->now[COUNT] = 0;
+}
+
+static inline int getff (struct dma_cont *d)
+{
+    int ff;
+
+    ff = d->flip_flop;
+    d->flip_flop = !ff;
+    return ff;
+}
+
+static uint32_t read_chan (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+    int ichan, nreg, iport, ff, val, dir;
+    struct dma_regs *r;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    ichan = iport >> 1;
+    nreg = iport & 1;
+    r = d->regs + ichan;
+
+    dir = ((r->mode >> 5) & 1) ? -1 : 1;
+    ff = getff (d);
+    if (nreg)
+        val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
+    else
+        val = r->now[ADDR] + r->now[COUNT] * dir;
+
+    ldebug ("read_chan %#x -> %d\n", iport, val);
+    return (val >> (d->dshift + (ff << 3))) & 0xff;
+}
+
+static void write_chan (void *opaque, uint32_t nport, uint32_t data)
+{
+    struct dma_cont *d = opaque;
+    int iport, ichan, nreg;
+    struct dma_regs *r;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    ichan = iport >> 1;
+    nreg = iport & 1;
+    r = d->regs + ichan;
+    if (getff (d)) {
+        r->base[nreg] = (r->base[nreg] & 0xff) | ((data << 8) & 0xff00);
+        init_chan (d, ichan);
+    } else {
+        r->base[nreg] = (r->base[nreg] & 0xff00) | (data & 0xff);
+    }
+}
+
+static void write_cont (void *opaque, uint32_t nport, uint32_t data)
+{
+    struct dma_cont *d = opaque;
+    int iport, ichan = 0;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    switch (iport) {
+    case 0x08:                  /* command */
+        if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
+            dolog ("command %#x not supported\n", data);
+            return;
+        }
+        d->command = data;
+        break;
+
+    case 0x09:
+        ichan = data & 3;
+        if (data & 4) {
+            d->status |= 1 << (ichan + 4);
+        }
+        else {
+            d->status &= ~(1 << (ichan + 4));
+        }
+        d->status &= ~(1 << ichan);
+        break;
+
+    case 0x0a:                  /* single mask */
+        if (data & 4)
+            d->mask |= 1 << (data & 3);
+        else
+            d->mask &= ~(1 << (data & 3));
+        break;
+
+    case 0x0b:                  /* mode */
+        {
+            ichan = data & 3;
+#ifdef DEBUG_DMA
+            {
+                int op, ai, dir, opmode;
+                op = (data >> 2) & 3;
+                ai = (data >> 4) & 1;
+                dir = (data >> 5) & 1;
+                opmode = (data >> 6) & 3;
+
+                linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
+                       ichan, op, ai, dir, opmode);
+            }
+#endif
+            d->regs[ichan].mode = data;
+            break;
+        }
+
+    case 0x0c:                  /* clear flip flop */
+        d->flip_flop = 0;
+        break;
+
+    case 0x0d:                  /* reset */
+        d->flip_flop = 0;
+        d->mask = ~0;
+        d->status = 0;
+        d->command = 0;
+        break;
+
+    case 0x0e:                  /* clear mask for all channels */
+        d->mask = 0;
+        break;
+
+    case 0x0f:                  /* write mask for all channels */
+        d->mask = data;
+        break;
+
+    default:
+        dolog ("unknown iport %#x\n", iport);
+        break;
+    }
+
+#ifdef DEBUG_DMA
+    if (0xc != iport) {
+        linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
+               nport, ichan, data);
+    }
+#endif
+}
+
+static uint32_t read_cont (void *opaque, uint32_t nport)
+{
+    struct dma_cont *d = opaque;
+    int iport, val;
+
+    iport = (nport >> d->dshift) & 0x0f;
+    switch (iport) {
+    case 0x08:                  /* status */
+        val = d->status;
+        d->status &= 0xf0;
+        break;
+    case 0x0f:                  /* mask */
+        val = d->mask;
+        break;
+    default:
+        val = 0;
+        break;
+    }
+
+    ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
+    return val;
+}
+
+int DMA_get_channel_mode (int nchan)
+{
+    return dma_controllers[nchan > 3].regs[nchan & 3].mode;
+}
+
+void DMA_hold_DREQ (int nchan)
+{
+    int ncont, ichan;
+
+    ncont = nchan > 3;
+    ichan = nchan & 3;
+    linfo ("held cont=%d chan=%d\n", ncont, ichan);
+    dma_controllers[ncont].status |= 1 << (ichan + 4);
+}
+
+void DMA_release_DREQ (int nchan)
+{
+    int ncont, ichan;
+
+    ncont = nchan > 3;
+    ichan = nchan & 3;
+    linfo ("released cont=%d chan=%d\n", ncont, ichan);
+    dma_controllers[ncont].status &= ~(1 << (ichan + 4));
+}
+
+static void channel_run (int ncont, int ichan)
+{
+    int n;
+    struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
+#ifdef DEBUG_DMA
+    int dir, opmode;
+
+    dir = (r->mode >> 5) & 1;
+    opmode = (r->mode >> 6) & 3;
+
+    if (dir) {
+        dolog ("DMA in address decrement mode\n");
+    }
+    if (opmode != 1) {
+        dolog ("DMA not in single mode select %#x\n", opmode);
+    }
+#endif
+
+    r = dma_controllers[ncont].regs + ichan;
+    n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
+                             r->now[COUNT], (r->base[COUNT] + 1) << ncont);
+    r->now[COUNT] = n;
+    ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
+}
+
+void DMA_run (void)
+{
+    struct dma_cont *d;
+    int icont, ichan;
+
+    d = dma_controllers;
+
+    for (icont = 0; icont < 2; icont++, d++) {
+        for (ichan = 0; ichan < 4; ichan++) {
+            int mask;
+
+            mask = 1 << ichan;
+
+            if ((0 == (d->mask & mask)) && (0 != (d->status & (mask << 4))))
+                channel_run (icont, ichan);
+        }
+    }
+}
+
+void DMA_register_channel (int nchan,
+                           DMA_transfer_handler transfer_handler,
+                           void *opaque)
+{
+    struct dma_regs *r;
+    int ichan, ncont;
+
+    ncont = nchan > 3;
+    ichan = nchan & 3;
+
+    r = dma_controllers[ncont].regs + ichan;
+    r->transfer_handler = transfer_handler;
+    r->opaque = opaque;
+}
+
+int DMA_read_memory (int nchan, void *buf, int pos, int len)
+{
+    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+    target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+    if (r->mode & 0x20) {
+        int i;
+        uint8_t *p = buf;
+
+        cpu_physical_memory_read (addr - pos - len, buf, len);
+        /* What about 16bit transfers? */
+        for (i = 0; i < len >> 1; i++) {
+            uint8_t b = p[len - i - 1];
+            p[i] = b;
+        }
+    }
+    else
+        cpu_physical_memory_read (addr + pos, buf, len);
+
+    return len;
+}
+
+int DMA_write_memory (int nchan, void *buf, int pos, int len)
+{
+    struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
+    target_phys_addr_t addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
+
+    if (r->mode & 0x20) {
+        int i;
+        uint8_t *p = buf;
+
+        cpu_physical_memory_write (addr - pos - len, buf, len);
+        /* What about 16bit transfers? */
+        for (i = 0; i < len; i++) {
+            uint8_t b = p[len - i - 1];
+            p[i] = b;
+        }
+    }
+    else
+        cpu_physical_memory_write (addr + pos, buf, len);
+
+    return len;
+}
+
+/* request the emulator to transfer a new DMA memory block ASAP */
+void DMA_schedule(int nchan)
+{
+    CPUState *env = cpu_single_env;
+    if (env)
+        cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+}
+
+static void dma_reset(void *opaque)
+{
+    struct dma_cont *d = opaque;
+    write_cont (d, (0x0d << d->dshift), 0);
+}
+
+static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
+           nchan, dma_pos, dma_len);
+    return dma_pos;
+}
+
+/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
+static void dma_init2(struct dma_cont *d, int base, int dshift,
+                      int page_base, int pageh_base)
+{
+    static const int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
+    int i;
+
+    d->dshift = dshift;
+    for (i = 0; i < 8; i++) {
+        register_ioport_write (base + (i << dshift), 1, 1, write_chan, d);
+        register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
+    }
+    for (i = 0; i < LENOFA (page_port_list); i++) {
+        register_ioport_write (page_base + page_port_list[i], 1, 1,
+                               write_page, d);
+        register_ioport_read (page_base + page_port_list[i], 1, 1,
+                              read_page, d);
+        if (pageh_base >= 0) {
+            register_ioport_write (pageh_base + page_port_list[i], 1, 1,
+                                   write_pageh, d);
+            register_ioport_read (pageh_base + page_port_list[i], 1, 1,
+                                  read_pageh, d);
+        }
+    }
+    for (i = 0; i < 8; i++) {
+        register_ioport_write (base + ((i + 8) << dshift), 1, 1,
+                               write_cont, d);
+        register_ioport_read (base + ((i + 8) << dshift), 1, 1,
+                              read_cont, d);
+    }
+    qemu_register_reset(dma_reset, d);
+    dma_reset(d);
+    for (i = 0; i < LENOFA (d->regs); ++i) {
+        d->regs[i].transfer_handler = dma_phony_handler;
+    }
+}
+
+static void dma_save (QEMUFile *f, void *opaque)
+{
+    struct dma_cont *d = opaque;
+    int i;
+
+    /* qemu_put_8s (f, &d->status); */
+    qemu_put_8s (f, &d->command);
+    qemu_put_8s (f, &d->mask);
+    qemu_put_8s (f, &d->flip_flop);
+    qemu_put_be32 (f, d->dshift);
+
+    for (i = 0; i < 4; ++i) {
+        struct dma_regs *r = &d->regs[i];
+        qemu_put_be32 (f, r->now[0]);
+        qemu_put_be32 (f, r->now[1]);
+        qemu_put_be16s (f, &r->base[0]);
+        qemu_put_be16s (f, &r->base[1]);
+        qemu_put_8s (f, &r->mode);
+        qemu_put_8s (f, &r->page);
+        qemu_put_8s (f, &r->pageh);
+        qemu_put_8s (f, &r->dack);
+        qemu_put_8s (f, &r->eop);
+    }
+}
+
+static int dma_load (QEMUFile *f, void *opaque, int version_id)
+{
+    struct dma_cont *d = opaque;
+    int i;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    /* qemu_get_8s (f, &d->status); */
+    qemu_get_8s (f, &d->command);
+    qemu_get_8s (f, &d->mask);
+    qemu_get_8s (f, &d->flip_flop);
+    d->dshift=qemu_get_be32 (f);
+
+    for (i = 0; i < 4; ++i) {
+        struct dma_regs *r = &d->regs[i];
+        r->now[0]=qemu_get_be32 (f);
+        r->now[1]=qemu_get_be32 (f);
+        qemu_get_be16s (f, &r->base[0]);
+        qemu_get_be16s (f, &r->base[1]);
+        qemu_get_8s (f, &r->mode);
+        qemu_get_8s (f, &r->page);
+        qemu_get_8s (f, &r->pageh);
+        qemu_get_8s (f, &r->dack);
+        qemu_get_8s (f, &r->eop);
+    }
+    return 0;
+}
+
+void DMA_init (int high_page_enable)
+{
+    dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
+              high_page_enable ? 0x480 : -1);
+    dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
+              high_page_enable ? 0x488 : -1);
+    register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]);
+    register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]);
+}
diff --git a/hw/goldfish_audio.c b/hw/goldfish_audio.c
new file mode 100644
index 0000000..d0a44b5
--- /dev/null
+++ b/hw/goldfish_audio.c
@@ -0,0 +1,533 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "audio/audio.h"
+#include "qemu_debug.h"
+#include "android/globals.h"
+
+#define  DEBUG  1
+
+#if DEBUG
+#  define  D(...)  VERBOSE_PRINT(audio,__VA_ARGS__)
+#else
+#  define  D(...)  ((void)0)
+#endif
+
+extern void  dprint(const char*  fmt, ...);
+
+/* define USE_QEMU_AUDIO_IN to 1 to use QEMU's audio subsystem to
+ * implement the audio input. if 0, this will try to read a .wav file
+ * directly...
+ */
+#define  USE_QEMU_AUDIO_IN  1
+
+enum {
+	/* audio status register */
+	AUDIO_INT_STATUS	= 0x00,
+	/* set this to enable IRQ */
+	AUDIO_INT_ENABLE	= 0x04,
+	/* set these to specify buffer addresses */
+	AUDIO_SET_WRITE_BUFFER_1 = 0x08,
+	AUDIO_SET_WRITE_BUFFER_2 = 0x0C,
+	/* set number of bytes in buffer to write */
+	AUDIO_WRITE_BUFFER_1  = 0x10,
+	AUDIO_WRITE_BUFFER_2  = 0x14,
+
+	/* true if audio input is supported */
+	AUDIO_READ_SUPPORTED = 0x18,
+	/* buffer to use for audio input */
+	AUDIO_SET_READ_BUFFER = 0x1C,
+
+	/* driver writes number of bytes to read */
+	AUDIO_START_READ  = 0x20,
+
+	/* number of bytes available in read buffer */
+	AUDIO_READ_BUFFER_AVAILABLE  = 0x24,
+
+	/* AUDIO_INT_STATUS bits */
+
+	/* this bit set when it is safe to write more bytes to the buffer */
+	AUDIO_INT_WRITE_BUFFER_1_EMPTY	= 1U << 0,
+	AUDIO_INT_WRITE_BUFFER_2_EMPTY	= 1U << 1,
+	AUDIO_INT_READ_BUFFER_FULL      = 1U << 2,
+};
+
+
+struct goldfish_audio_state {
+    struct goldfish_device dev;
+    // pointers to our two write buffers
+    uint32_t buffer_1, buffer_2;
+    uint32_t read_buffer;
+    // buffer flags
+    uint32_t int_status;
+    // irq enable mask for int_status
+    uint32_t int_enable;
+
+#if USE_QEMU_AUDIO_IN
+    uint32_t  read_pos;
+    uint32_t  read_size;
+#else
+    // path to file or device to use for input
+    const char* input_source;
+    // true if input is a wav file
+    int input_is_wav;
+    // true if we need to convert stereo -> mono
+    int input_is_stereo;
+    // file descriptor to use for input
+    int input_fd;
+#endif
+
+    // number of bytes available in the read buffer
+    int read_buffer_available;
+
+    // set to 1 or 2 to indicate which buffer we are writing from, or zero if both buffers are empty
+    int current_buffer;
+
+    // current data to write
+    uint8* data_1;
+    uint32_t data_1_length;
+    uint8* data_2;
+    uint32_t data_2_length;
+
+
+    // for QEMU sound output
+    QEMUSoundCard card;
+    SWVoiceOut *voice;
+#if USE_QEMU_AUDIO_IN
+    SWVoiceIn*  voicein;
+#endif
+};
+
+/* update this whenever you change the goldfish_audio_state structure */
+#define  AUDIO_STATE_SAVE_VERSION  1
+
+#define  QFIELD_STRUCT   struct goldfish_audio_state
+QFIELD_BEGIN(audio_state_fields)
+    QFIELD_INT32(buffer_1),
+    QFIELD_INT32(buffer_2),
+    QFIELD_INT32(read_buffer),
+    QFIELD_INT32(int_status),
+    QFIELD_INT32(int_enable),
+#if USE_QEMU_AUDIO_IN
+    QFIELD_INT32(read_pos),
+    QFIELD_INT32(read_size),
+#endif
+    QFIELD_INT32(read_buffer_available),
+    QFIELD_INT32(current_buffer),
+    QFIELD_INT32(data_1_length),
+    QFIELD_INT32(data_2_length),
+QFIELD_END
+
+static void  audio_state_save( QEMUFile*  f, void* opaque )
+{
+    struct goldfish_audio_state*  s = opaque;
+
+    qemu_put_struct(f, audio_state_fields, s);
+
+    /* we can't write data_1 and data_2 directly */
+    qemu_put_be32( f, s->data_1 - phys_ram_base );
+    qemu_put_be32( f, s->data_2 - phys_ram_base );
+}
+
+static int   audio_state_load( QEMUFile*  f, void*  opaque, int  version_id )
+{
+    struct goldfish_audio_state*  s = opaque;
+    int                           ret;
+
+    if (version_id != AUDIO_STATE_SAVE_VERSION)
+        return -1;
+
+    ret = qemu_get_struct(f, audio_state_fields, s);
+    if (!ret) {
+        s->data_1 = qemu_get_be32(f) + phys_ram_base;
+        s->data_2 = qemu_get_be32(f) + phys_ram_base;
+    }
+    return -1;
+}
+
+static void enable_audio(struct goldfish_audio_state *s, int enable)
+{
+    // enable or disable the output voice
+    if (s->voice != NULL)
+        AUD_set_active_out(s->voice,   (enable & (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY)) != 0);
+
+    if (s->voicein)
+        AUD_set_active_in (s->voicein, (enable & AUDIO_INT_READ_BUFFER_FULL) != 0);
+    // reset buffer information
+    s->data_1_length = 0;
+    s->data_2_length = 0;
+    s->current_buffer = 0;
+    s->read_pos = 0;
+}
+
+#if USE_QEMU_AUDIO_IN
+static void start_read(struct goldfish_audio_state *s, uint32_t count)
+{
+    //printf( "... goldfish audio start_read, count=%d\n", count );
+    s->read_size = count;
+    s->read_buffer_available = 0;
+    s->read_pos  = 0;
+}
+#else
+static void start_read(struct goldfish_audio_state *s, uint32_t count)
+{
+    uint8   wav_header[44];
+    int result;
+
+    if (!s->input_source) return;
+
+    if (s->input_fd < 0) {
+        s->input_fd = open(s->input_source, O_BINARY | O_RDONLY);
+
+        if (s->input_fd < 0) {
+            fprintf(stderr, "goldfish_audio could not open %s for audio input\n", s->input_source);
+            s->input_source = NULL; // set to to avoid endless retries
+            return;
+        }
+
+        // skip WAV header if we have a WAV file
+        if (s->input_is_wav) {
+            if (read(s->input_fd, wav_header, sizeof(wav_header)) != sizeof(wav_header)) {
+                fprintf(stderr, "goldfish_audio could not read WAV file header %s\n", s->input_source);
+                s->input_fd = -1;
+                s->input_source = NULL; // set to to avoid endless retries
+                return;
+            }
+
+            // is the WAV file stereo?
+            s->input_is_stereo = (wav_header[22] == 2);
+        } else {
+            // assume input from an audio device is stereo
+            s->input_is_stereo = 1;
+        }
+    }
+
+    uint8* buffer = (uint8*)phys_ram_base + s->read_buffer;
+    if (s->input_is_stereo) {
+        // need to read twice as much data
+        count *= 2;
+    }
+
+try_again:
+    result = read(s->input_fd, buffer, count);
+    if (result == 0 && s->input_is_wav) {
+        // end of file, so seek back to the beginning
+       lseek(s->input_fd, sizeof(wav_header), SEEK_SET);
+       goto try_again;
+    }
+
+    if (result > 0 && s->input_is_stereo) {
+        // we need to convert stereo to mono
+        uint8* src  = (uint8*)buffer;
+        uint8* dest = src;
+        int count = result/2;
+        while (count-- > 0) {
+            int  sample1 = src[0] | (src[1] << 8);
+            int  sample2 = src[2] | (src[3] << 8);
+            int  sample  = (sample1 + sample2) >> 1;
+            dst[0] = (uint8_t) sample;
+            dst[1] = (uint8_t)(sample >> 8);
+            src   += 4;
+            dst   += 2;
+        }
+
+        // we reduced the number of bytes by 2
+        result /= 2;
+    }
+
+    s->read_buffer_available = (result > 0 ? result : 0);
+    s->int_status |= AUDIO_INT_READ_BUFFER_FULL;
+    goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+}
+#endif
+
+static uint32_t goldfish_audio_read(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t ret;
+    struct goldfish_audio_state *s = opaque;
+    offset -= s->dev.base;
+    switch(offset) {
+        case AUDIO_INT_STATUS:
+            // return current buffer status flags
+            ret = s->int_status & s->int_enable;
+            if(ret) {
+                goldfish_device_set_irq(&s->dev, 0, 0);
+            }
+            return ret;
+
+	case AUDIO_READ_SUPPORTED:
+#if USE_QEMU_AUDIO_IN
+            D("%s: AUDIO_READ_SUPPORTED returns %d", __FUNCTION__,
+              (s->voicein != NULL));
+            return (s->voicein != NULL);
+#else
+            return (s->input_source ? 1 : 0);
+#endif
+
+	case AUDIO_READ_BUFFER_AVAILABLE:
+            D("%s: AUDIO_READ_BUFFER_AVAILABLE returns %d", __FUNCTION__,
+               s->read_buffer_available);
+	    return s->read_buffer_available;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_audio_read: Bad offset %x\n", offset);
+            return 0;
+    }
+}
+
+static void goldfish_audio_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    struct goldfish_audio_state *s = opaque;
+    offset -= s->dev.base;
+
+    switch(offset) {
+        case AUDIO_INT_ENABLE:
+            /* enable buffer empty interrupts */
+            D("%s: AUDIO_INT_ENABLE %d", __FUNCTION__, val );
+            enable_audio(s, val);
+            s->int_enable = val;
+            s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
+            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            break;
+        case AUDIO_SET_WRITE_BUFFER_1:
+            /* save pointer to buffer 1 */
+            s->buffer_1 = val;
+            break;
+        case AUDIO_SET_WRITE_BUFFER_2:
+            /* save pointer to buffer 2 */
+            s->buffer_2 = val;
+            break;
+        case AUDIO_WRITE_BUFFER_1:
+            /* record that data in buffer 1 is ready to write */
+            if (s->current_buffer == 0) s->current_buffer = 1;
+            s->data_1 = phys_ram_base + s->buffer_1;
+            s->data_1_length = val;
+            s->int_status &= ~AUDIO_INT_WRITE_BUFFER_1_EMPTY;
+            break;
+        case AUDIO_WRITE_BUFFER_2:
+            /* record that data in buffer 2 is ready to write */
+            if (s->current_buffer == 0) s->current_buffer = 2;
+            s->data_2 = phys_ram_base + s->buffer_2;
+            s->data_2_length = val;
+            s->int_status &= ~AUDIO_INT_WRITE_BUFFER_2_EMPTY;
+            break;
+
+	case AUDIO_SET_READ_BUFFER:
+            /* save pointer to the read buffer */
+            s->read_buffer = val;
+            D( "%s: AUDIO_SET_READ_BUFFER %p", __FUNCTION__, (void*)val );
+            break;
+
+        case AUDIO_START_READ:
+            D( "%s: AUDIO_START_READ %d", __FUNCTION__, val );
+            start_read(s, val);
+            s->int_status &= ~AUDIO_INT_READ_BUFFER_FULL;
+            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            break;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
+    }
+}
+
+static void goldfish_audio_callback(void *opaque, int free)
+{
+    struct goldfish_audio_state *s = opaque;
+    int new_status = 0;
+
+    /* loop until free is zero or both buffers are empty */
+    while (free && s->current_buffer) {
+
+        /* write data in buffer 1 */
+        while (free && s->current_buffer == 1) {
+            int write = s->data_1_length;
+            if (write > free) write = free;
+
+            int written = AUD_write(s->voice, s->data_1, write);
+            if (written) {
+                D("%s: sent %d bytes to audio output", __FUNCTION__, write);
+                s->data_1 += written;
+                s->data_1_length -= written;
+                free -= written;
+
+                if (s->data_1_length == 0) {
+                    new_status |= AUDIO_INT_WRITE_BUFFER_1_EMPTY;
+                    s->current_buffer = (s->data_2_length ? 2 : 0);
+                }
+            } else {
+                break;
+            }
+        }
+
+        /* write data in buffer 2 */
+        while (free && s->current_buffer == 2) {
+            int write = s->data_2_length;
+            if (write > free) write = free;
+
+            int written = AUD_write(s->voice, s->data_2, write);
+            if (written) {
+                D("%s: sent %d bytes to audio output", __FUNCTION__, write);
+                s->data_2 += written;
+                s->data_2_length -= written;
+                free -= written;
+
+                if (s->data_2_length == 0) {
+                    new_status |= AUDIO_INT_WRITE_BUFFER_2_EMPTY;
+                    s->current_buffer = (s->data_1_length ? 1 : 0);
+                }
+            } else {
+                break;
+            }
+        }
+    }
+
+    if (new_status && new_status != s->int_status) {
+        s->int_status |= new_status;
+        goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+    }
+}
+
+#if USE_QEMU_AUDIO_IN
+static void
+goldfish_audio_in_callback(void *opaque, int avail)
+{
+    struct goldfish_audio_state *s = opaque;
+    int new_status = 0;
+
+    if (s->read_pos >= s->read_size)
+        return;
+
+    if (0 && s->read_size > 0)
+        D("%s: in %d (pos=%d size=%d)", __FUNCTION__,
+           avail, s->read_pos, s->read_size );
+
+    while (avail > 0) {
+        int     pos     = s->read_pos;
+        int     missing = s->read_size - pos;
+        uint8*  buffer  = (uint8*)phys_ram_base + s->read_buffer + pos;
+        int     read;
+        int     avail2 = (avail > missing) ? missing : avail;
+
+        read = AUD_read(s->voicein, buffer, avail2);
+        if (read == 0)
+            break;
+
+        if (avail2 > 0)
+            D("%s: AUD_read(%d) returned %d", __FUNCTION__, avail2, read);
+
+        s->read_buffer_available += read;
+
+        avail -= read;
+        pos   += read;
+        if (pos == s->read_size) {
+            new_status |= AUDIO_INT_READ_BUFFER_FULL;
+            D("%s: AUDIO_INT_READ_BUFFER_FULL available=%d", __FUNCTION__, s->read_buffer_available);
+        }
+        s->read_pos = pos;
+    }
+
+    if (new_status && new_status != s->int_status) {
+        s->int_status |= new_status;
+        goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+    }
+}
+#endif /* USE_QEMU_AUDIO_IN */
+
+static CPUReadMemoryFunc *goldfish_audio_readfn[] = {
+   goldfish_audio_read,
+   goldfish_audio_read,
+   goldfish_audio_read
+};
+
+static CPUWriteMemoryFunc *goldfish_audio_writefn[] = {
+   goldfish_audio_write,
+   goldfish_audio_write,
+   goldfish_audio_write
+};
+
+void goldfish_audio_init(uint32_t base, int id, const char* input_source)
+{
+    struct goldfish_audio_state *s;
+    audsettings_t as;
+
+    /* nothing to do if no audio input and output */
+    if (!android_hw->hw_audioOutput && !android_hw->hw_audioInput)
+        return;
+
+    s = (struct goldfish_audio_state *)qemu_mallocz(sizeof(*s));
+    s->dev.name = "goldfish_audio";
+    s->dev.id = id;
+    s->dev.base = base;
+    s->dev.size = 0x1000;
+    s->dev.irq_count = 1;
+
+#ifndef USE_QEMU_AUDIO_IN
+    s->input_fd = -1;
+    if (input_source) {
+        s->input_source = input_source;
+        char* extension = strrchr(input_source, '.');
+        if (extension && strcasecmp(extension, ".wav") == 0) {
+            s->input_is_wav = 1;
+        }
+     }
+#endif
+
+    AUD_register_card( &glob_audio_state, "goldfish_audio", &s->card);
+
+    as.freq = 44100;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = AUDIO_HOST_ENDIANNESS;
+
+    if (android_hw->hw_audioOutput) {
+        s->voice = AUD_open_out (
+            &s->card,
+            s->voice,
+            "goldfish_audio",
+            s,
+            goldfish_audio_callback,
+            &as
+            );
+        if (!s->voice) {
+            dprint("warning: opening audio output failed\n");
+            return;
+        }
+    }
+
+#if USE_QEMU_AUDIO_IN
+    as.freq       = 8000;
+    as.nchannels  = 1;
+    as.fmt        = AUD_FMT_S16;
+    as.endianness = AUDIO_HOST_ENDIANNESS;
+
+    if (android_hw->hw_audioInput) {
+        s->voicein = AUD_open_in (
+            &s->card,
+            NULL,
+            "goldfish_audio_in",
+            s,
+            goldfish_audio_in_callback,
+            &as
+            );
+        if (!s->voicein) {
+            dprint("warning: opening audio input failed\n");
+        }
+    }
+#endif
+
+    goldfish_device_add(&s->dev, goldfish_audio_readfn, goldfish_audio_writefn, s);
+
+    register_savevm( "audio_state", 0, AUDIO_STATE_SAVE_VERSION,
+                     audio_state_save, audio_state_load, s );
+}
+
diff --git a/hw/goldfish_battery.c b/hw/goldfish_battery.c
new file mode 100644
index 0000000..d9ef785
--- /dev/null
+++ b/hw/goldfish_battery.c
@@ -0,0 +1,261 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "power_supply.h"
+
+
+enum {
+	/* status register */
+	BATTERY_INT_STATUS	    = 0x00,
+	/* set this to enable IRQ */
+	BATTERY_INT_ENABLE	    = 0x04,
+
+	BATTERY_AC_ONLINE       = 0x08,
+	BATTERY_STATUS          = 0x0C,
+	BATTERY_HEALTH          = 0x10,
+	BATTERY_PRESENT         = 0x14,
+	BATTERY_CAPACITY        = 0x18,
+
+	BATTERY_STATUS_CHANGED	= 1U << 0,
+	AC_STATUS_CHANGED   	= 1U << 1,
+	BATTERY_INT_MASK        = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED,
+};
+
+
+struct goldfish_battery_state {
+    struct goldfish_device dev;
+    // IRQs
+    uint32_t int_status;
+    // irq enable mask for int_status
+    uint32_t int_enable;
+
+    int ac_online;
+    int status;
+    int health;
+    int present;
+    int capacity;
+};
+
+/* update this each time you update the battery_state struct */
+#define  BATTERY_STATE_SAVE_VERSION  1
+
+#define  QFIELD_STRUCT  struct goldfish_battery_state
+QFIELD_BEGIN(goldfish_battery_fields)
+    QFIELD_INT32(int_status),
+    QFIELD_INT32(int_enable),
+    QFIELD_INT32(ac_online),
+    QFIELD_INT32(status),
+    QFIELD_INT32(health),
+    QFIELD_INT32(present),
+    QFIELD_INT32(capacity),
+QFIELD_END
+
+static void  goldfish_battery_save(QEMUFile*  f, void* opaque)
+{
+    struct goldfish_battery_state*  s = opaque;
+
+    qemu_put_struct(f, goldfish_battery_fields, s);
+}
+
+static int   goldfish_battery_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct goldfish_battery_state*  s = opaque;
+
+    if (version_id != BATTERY_STATE_SAVE_VERSION)
+        return -1;
+
+    return qemu_get_struct(f, goldfish_battery_fields, s);
+}
+
+static struct goldfish_battery_state *battery_state;
+
+static uint32_t goldfish_battery_read(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t ret;
+    struct goldfish_battery_state *s = opaque;
+    offset -= s->dev.base;
+    switch(offset) {
+        case BATTERY_INT_STATUS:
+            // return current buffer status flags
+            ret = s->int_status & s->int_enable;
+            if (ret) {
+                goldfish_device_set_irq(&s->dev, 0, 0);
+                s->int_status = 0;
+            }
+            return ret;
+
+		case BATTERY_INT_ENABLE:
+		    return s->int_enable;
+		case BATTERY_AC_ONLINE:
+		    return s->ac_online;
+		case BATTERY_STATUS:
+		    return s->status;
+		case BATTERY_HEALTH:
+		    return s->health;
+		case BATTERY_PRESENT:
+		    return s->present;
+		case BATTERY_CAPACITY:
+		    return s->capacity;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_battery_read: Bad offset %x\n", offset);
+            return 0;
+    }
+}
+
+static void goldfish_battery_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    struct goldfish_battery_state *s = opaque;
+    offset -= s->dev.base;
+
+    switch(offset) {
+        case BATTERY_INT_ENABLE:
+            /* enable interrupts */
+            s->int_enable = val;
+//            s->int_status = (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY);
+//            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            break;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_audio_write: Bad offset %x\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc *goldfish_battery_readfn[] = {
+    goldfish_battery_read,
+    goldfish_battery_read,
+    goldfish_battery_read
+};
+
+
+static CPUWriteMemoryFunc *goldfish_battery_writefn[] = {
+    goldfish_battery_write,
+    goldfish_battery_write,
+    goldfish_battery_write
+};
+
+void goldfish_battery_init()
+{
+    struct goldfish_battery_state *s;
+
+    s = (struct goldfish_battery_state *)qemu_mallocz(sizeof(*s));
+    s->dev.name = "goldfish-battery";
+    s->dev.base = 0;    // will be allocated dynamically
+    s->dev.size = 0x1000;
+    s->dev.irq_count = 1;
+
+    // default values for the battery
+    s->ac_online = 1;
+    s->status = POWER_SUPPLY_STATUS_CHARGING;
+    s->health = POWER_SUPPLY_HEALTH_GOOD;
+    s->present = 1;     // battery is present
+    s->capacity = 50;   // 50% charged
+
+    battery_state = s;
+
+    goldfish_device_add(&s->dev, goldfish_battery_readfn, goldfish_battery_writefn, s);
+
+    register_savevm( "battery_state", 0, BATTERY_STATE_SAVE_VERSION,
+                     goldfish_battery_save, goldfish_battery_load, s);
+}
+
+void goldfish_battery_set_prop(int ac, int property, int value)
+{
+    int new_status = (ac ? AC_STATUS_CHANGED : BATTERY_STATUS_CHANGED);
+
+    if (ac) {
+        switch (property) {
+            case POWER_SUPPLY_PROP_ONLINE:
+                battery_state->ac_online = value;
+                break;
+        }
+    } else {
+         switch (property) {
+            case POWER_SUPPLY_PROP_STATUS:
+                battery_state->status = value;
+                break;
+            case POWER_SUPPLY_PROP_HEALTH:
+                battery_state->health = value;
+                break;
+            case POWER_SUPPLY_PROP_PRESENT:
+                battery_state->present = value;
+                break;
+            case POWER_SUPPLY_PROP_CAPACITY:
+                battery_state->capacity = value;
+                break;
+        }
+    }
+
+    if (new_status != battery_state->int_status) {
+        battery_state->int_status |= new_status;
+        goldfish_device_set_irq(&battery_state->dev, 0, (battery_state->int_status & battery_state->int_enable));
+    }
+}
+
+void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data)
+{
+    char    buffer[100];
+    char*   value;
+
+    sprintf(buffer, "AC: %s\r\n", (battery_state->ac_online ? "online" : "offline"));
+    callback(data, buffer);
+
+    switch (battery_state->status) {
+	    case POWER_SUPPLY_STATUS_CHARGING:
+	        value = "Charging";
+	        break;
+	    case POWER_SUPPLY_STATUS_DISCHARGING:
+	        value = "Discharging";
+	        break;
+	    case POWER_SUPPLY_STATUS_NOT_CHARGING:
+	        value = "Not charging";
+	        break;
+	    case POWER_SUPPLY_STATUS_FULL:
+	        value = "Full";
+	        break;
+        default:
+	        value = "Unknown";
+	        break;
+    }
+    sprintf(buffer, "status: %s\r\n", value);
+    callback(data, buffer);
+
+    switch (battery_state->health) {
+	    case POWER_SUPPLY_HEALTH_GOOD:
+	        value = "Good";
+	        break;
+	    case POWER_SUPPLY_HEALTH_OVERHEAT:
+	        value = "Overhead";
+	        break;
+	    case POWER_SUPPLY_HEALTH_DEAD:
+	        value = "Dead";
+	        break;
+	    case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+	        value = "Overvoltage";
+	        break;
+	    case POWER_SUPPLY_HEALTH_UNSPEC_FAILURE:
+	        value = "Unspecified failure";
+	        break;
+        default:
+	        value = "Unknown";
+	        break;
+    }
+    sprintf(buffer, "health: %s\r\n", value);
+    callback(data, buffer);
+
+    sprintf(buffer, "present: %s\r\n", (battery_state->present ? "true" : "false"));
+    callback(data, buffer);
+
+    sprintf(buffer, "capacity: %d\r\n", battery_state->capacity);
+    callback(data, buffer);
+}
diff --git a/hw/goldfish_device.c b/hw/goldfish_device.c
new file mode 100644
index 0000000..2c9dd6e
--- /dev/null
+++ b/hw/goldfish_device.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "arm_pic.h"
+#include "goldfish_device.h"
+
+#define PDEV_BUS_OP_DONE        (0x00)
+#define PDEV_BUS_OP_REMOVE_DEV  (0x04)
+#define PDEV_BUS_OP_ADD_DEV     (0x08)
+
+#define PDEV_BUS_OP_INIT        (0x00)
+
+#define PDEV_BUS_OP             (0x00)
+#define PDEV_BUS_GET_NAME       (0x04)
+#define PDEV_BUS_NAME_LEN       (0x08)
+#define PDEV_BUS_ID             (0x0c)
+#define PDEV_BUS_IO_BASE        (0x10)
+#define PDEV_BUS_IO_SIZE        (0x14)
+#define PDEV_BUS_IRQ            (0x18)
+#define PDEV_BUS_IRQ_COUNT      (0x1c)
+
+struct bus_state {
+    struct goldfish_device dev;
+    struct goldfish_device *current;
+};
+
+qemu_irq *goldfish_pic;
+static struct goldfish_device *first_device;
+static struct goldfish_device *last_device;
+uint32_t goldfish_free_base;
+uint32_t goldfish_free_irq;
+
+void goldfish_device_set_irq(struct goldfish_device *dev, int irq, int level)
+{
+    if(irq >= dev->irq_count)
+        cpu_abort (cpu_single_env, "goldfish_device_set_irq: Bad irq %d >= %d\n", irq, dev->irq_count);
+    else
+        qemu_set_irq(goldfish_pic[dev->irq + irq], level);
+}
+
+int goldfish_add_device_no_io(struct goldfish_device *dev)
+{
+    if(dev->base == 0) {
+        dev->base = goldfish_free_base;
+        goldfish_free_base += dev->size;
+    }
+    if(dev->irq == 0 && dev->irq_count > 0) {
+        dev->irq = goldfish_free_irq;
+        goldfish_free_irq += dev->irq_count;
+    }
+    //printf("goldfish_add_device: %s, base %x %x, irq %d %d\n",
+    //       dev->name, dev->base, dev->size, dev->irq, dev->irq_count);
+    dev->next = NULL;
+    if(last_device) {
+        last_device->next = dev;
+    }
+    else {
+        first_device = dev;
+    }
+    last_device = dev;
+    return 0;
+}
+
+int goldfish_device_add(struct goldfish_device *dev,
+                       CPUReadMemoryFunc **mem_read,
+                       CPUWriteMemoryFunc **mem_write,
+                       void *opaque)
+{
+    int iomemtype;
+    goldfish_add_device_no_io(dev);
+    iomemtype = cpu_register_io_memory(0, mem_read,
+                                       mem_write, opaque);
+    cpu_register_physical_memory(dev->base, dev->size, iomemtype);
+    return 0;
+}
+
+static uint32_t goldfish_bus_read(void *opaque, target_phys_addr_t offset)
+{
+    struct bus_state *s = (struct bus_state *)opaque;
+    offset -= s->dev.base;
+
+    switch (offset) {
+        case PDEV_BUS_OP:
+            if(s->current) {
+                s->current->reported_state = 1;
+                s->current = s->current->next;
+            }
+            else {
+                s->current = first_device;
+            }
+            while(s->current && s->current->reported_state == 1)
+                s->current = s->current->next;
+            if(s->current)
+                return PDEV_BUS_OP_ADD_DEV;
+            else {
+                goldfish_device_set_irq(&s->dev, 0, 0);
+                return PDEV_BUS_OP_DONE;
+            }
+
+        case PDEV_BUS_NAME_LEN:
+            return s->current ? strlen(s->current->name) : 0;
+        case PDEV_BUS_ID:
+            return s->current ? s->current->id : 0;
+        case PDEV_BUS_IO_BASE:
+            return s->current ? s->current->base : 0;
+        case PDEV_BUS_IO_SIZE:
+            return s->current ? s->current->size : 0;
+        case PDEV_BUS_IRQ:
+            return s->current ? s->current->irq : 0;
+        case PDEV_BUS_IRQ_COUNT:
+            return s->current ? s->current->irq_count : 0;
+    default:
+        cpu_abort (cpu_single_env, "goldfish_bus_read: Bad offset %x\n", offset);
+        return 0;
+    }
+}
+
+static void goldfish_bus_op_init(struct bus_state *s)
+{
+    struct goldfish_device *dev = first_device;
+    while(dev) {
+        dev->reported_state = 0;
+        dev = dev->next;
+    }
+    s->current = NULL;
+    goldfish_device_set_irq(&s->dev, 0, first_device != NULL);
+}
+
+static void goldfish_bus_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    struct bus_state *s = (struct bus_state *)opaque;
+    offset -= s->dev.base;
+
+    switch(offset) {
+        case PDEV_BUS_OP:
+            switch(value) {
+                case PDEV_BUS_OP_INIT:
+                    goldfish_bus_op_init(s);
+                    break;
+                default:
+                    cpu_abort (cpu_single_env, "goldfish_bus_write: Bad PDEV_BUS_OP value %x\n", value);
+            };
+            break;
+        case PDEV_BUS_GET_NAME:
+            if(s->current)
+                pmemcpy(value, s->current->name, strlen(s->current->name));
+            break;
+        default:
+            cpu_abort (cpu_single_env, "goldfish_bus_write: Bad offset %x\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc *goldfish_bus_readfn[] = {
+    goldfish_bus_read,
+    goldfish_bus_read,
+    goldfish_bus_read
+};
+
+static CPUWriteMemoryFunc *goldfish_bus_writefn[] = {
+    goldfish_bus_write,
+    goldfish_bus_write,
+    goldfish_bus_write
+};
+
+
+static struct bus_state bus_state = {
+    .dev = {
+        .name = "goldfish_device_bus",
+        .id = -1,
+        .base = 0x10001000,
+        .size = 0x1000,
+        .irq = 1,
+        .irq_count = 1,
+    }
+};
+
+void goldfish_device_init(qemu_irq *pic, uint32_t base, uint32_t size, uint32_t irq, uint32_t irq_count)
+{
+    goldfish_pic = pic;
+    goldfish_free_base = base;
+    goldfish_free_irq = irq;
+}
+
+int goldfish_device_bus_init(uint32_t base, uint32_t irq)
+{
+    bus_state.dev.base = base;
+    bus_state.dev.irq = irq;
+
+    return goldfish_device_add(&bus_state.dev, goldfish_bus_readfn, goldfish_bus_writefn, &bus_state);
+}
+
diff --git a/hw/goldfish_device.h b/hw/goldfish_device.h
new file mode 100644
index 0000000..abe102e
--- /dev/null
+++ b/hw/goldfish_device.h
@@ -0,0 +1,58 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef GOLDFISH_DEVICE_H
+#define GOLDFISH_DEVICE_H
+
+struct goldfish_device {
+    struct goldfish_device *next;
+    struct goldfish_device *prev;
+    uint32_t reported_state;
+    void *cookie;
+    const char *name;
+    uint32_t id;
+    uint32_t base; // filled in by goldfish_device_add if 0
+    uint32_t size;
+    uint32_t irq; // filled in by goldfish_device_add if 0
+    uint32_t irq_count;
+};
+
+
+void goldfish_device_set_irq(struct goldfish_device *dev, int irq, int level);
+int goldfish_device_add(struct goldfish_device *dev,
+                       CPUReadMemoryFunc **mem_read,
+                       CPUWriteMemoryFunc **mem_write,
+                       void *opaque);
+
+int goldfish_add_device_no_io(struct goldfish_device *dev);
+
+void goldfish_device_init(qemu_irq *pic, uint32_t base, uint32_t size, uint32_t irq, uint32_t irq_count);
+int goldfish_device_bus_init(uint32_t base, uint32_t irq);
+
+// device init functions:
+qemu_irq *goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq);
+void goldfish_timer_and_rtc_init(uint32_t timerbase, int timerirq);
+int goldfish_tty_add(CharDriverState *cs, int id, uint32_t base, int irq);
+void goldfish_fb_init(DisplayState *ds, int id);
+void goldfish_audio_init(uint32_t base, int id, const char* input_source);
+void goldfish_battery_init();
+void goldfish_battery_set_prop(int ac, int property, int value);
+void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data);
+void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs);
+void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id);
+void goldfish_switch_set_state(void *opaque, uint32_t state);
+
+// these do not add a device
+void trace_dev_init(uint32_t base);
+void events_dev_init(uint32_t base, qemu_irq irq);
+void nand_dev_init(uint32_t base);
+
+#endif
diff --git a/hw/goldfish_events_device.c b/hw/goldfish_events_device.c
new file mode 100644
index 0000000..4cb2904
--- /dev/null
+++ b/hw/goldfish_events_device.c
@@ -0,0 +1,423 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "android/hw-events.h"
+#include "irq.h"
+
+#if 0
+// From kernel...
+#define EV_SYN			0x00
+#define EV_KEY			0x01
+#define EV_REL			0x02
+#define EV_ABS			0x03
+#define EV_MSC			0x04
+#define EV_SW			0x05
+#define EV_LED			0x11
+#define EV_SND			0x12
+#define EV_REP			0x14
+#define EV_FF			0x15
+#define EV_PWR			0x16
+#define EV_FF_STATUS		0x17
+#define EV_MAX			0x1f
+
+#define BTN_MISC		0x100
+#define BTN_0			0x100
+#define BTN_1			0x101
+#define BTN_2			0x102
+#define BTN_3			0x103
+#define BTN_4			0x104
+#define BTN_5			0x105
+#define BTN_6			0x106
+#define BTN_7			0x107
+#define BTN_8			0x108
+#define BTN_9			0x109
+
+#define BTN_MOUSE		0x110
+#define BTN_LEFT		0x110
+#define BTN_RIGHT		0x111
+#define BTN_MIDDLE		0x112
+#define BTN_SIDE		0x113
+#define BTN_EXTRA		0x114
+#define BTN_FORWARD		0x115
+#define BTN_BACK		0x116
+#define BTN_TASK		0x117
+
+#define BTN_JOYSTICK		0x120
+#define BTN_TRIGGER		0x120
+#define BTN_THUMB		0x121
+#define BTN_THUMB2		0x122
+#define BTN_TOP			0x123
+#define BTN_TOP2		0x124
+#define BTN_PINKIE		0x125
+#define BTN_BASE		0x126
+#define BTN_BASE2		0x127
+#define BTN_BASE3		0x128
+#define BTN_BASE4		0x129
+#define BTN_BASE5		0x12a
+#define BTN_BASE6		0x12b
+#define BTN_DEAD		0x12f
+
+#define BTN_GAMEPAD		0x130
+#define BTN_A			0x130
+#define BTN_B			0x131
+#define BTN_C			0x132
+#define BTN_X			0x133
+#define BTN_Y			0x134
+#define BTN_Z			0x135
+#define BTN_TL			0x136
+#define BTN_TR			0x137
+#define BTN_TL2			0x138
+#define BTN_TR2			0x139
+#define BTN_SELECT		0x13a
+#define BTN_START		0x13b
+#define BTN_MODE		0x13c
+#define BTN_THUMBL		0x13d
+#define BTN_THUMBR		0x13e
+
+#define BTN_DIGI		0x140
+#define BTN_TOOL_PEN		0x140
+#define BTN_TOOL_RUBBER		0x141
+#define BTN_TOOL_BRUSH		0x142
+#define BTN_TOOL_PENCIL		0x143
+#define BTN_TOOL_AIRBRUSH	0x144
+#define BTN_TOOL_FINGER		0x145
+#define BTN_TOOL_MOUSE		0x146
+#define BTN_TOOL_LENS		0x147
+#define BTN_TOUCH		0x14a
+#define BTN_STYLUS		0x14b
+#define BTN_STYLUS2		0x14c
+#define BTN_TOOL_DOUBLETAP	0x14d
+#define BTN_TOOL_TRIPLETAP	0x14e
+
+#define BTN_WHEEL		0x150
+#define BTN_GEAR_DOWN		0x150
+#define BTN_GEAR_UP		0x151
+
+#define REL_X           0x00
+#define REL_Y           0x01
+
+#define ABS_X			0x00
+#define ABS_Y			0x01
+#define ABS_Z			0x02
+#define ABS_RX			0x03
+#define ABS_RY			0x04
+#define ABS_RZ			0x05
+#define ABS_THROTTLE		0x06
+#define ABS_RUDDER		0x07
+#define ABS_WHEEL		0x08
+#define ABS_GAS			0x09
+#define ABS_BRAKE		0x0a
+#define ABS_HAT0X		0x10
+#define ABS_HAT0Y		0x11
+#define ABS_HAT1X		0x12
+#define ABS_HAT1Y		0x13
+#define ABS_HAT2X		0x14
+#define ABS_HAT2Y		0x15
+#define ABS_HAT3X		0x16
+#define ABS_HAT3Y		0x17
+#define ABS_PRESSURE		0x18
+#define ABS_DISTANCE		0x19
+#define ABS_TILT_X		0x1a
+#define ABS_TILT_Y		0x1b
+#define ABS_TOOL_WIDTH		0x1c
+#define ABS_VOLUME		0x20
+#define ABS_MISC		0x28
+#define ABS_MAX			0x3f
+#endif
+
+#define MAX_EVENTS 256*4
+
+enum {
+    REG_READ        = 0x00,
+    REG_SET_PAGE    = 0x00,
+    REG_LEN         = 0x04,
+    REG_DATA        = 0x08,
+
+    PAGE_NAME       = 0x00000,
+    PAGE_EVBITS     = 0x10000,
+    PAGE_ABSDATA    = 0x20000 | EV_ABS,
+};
+
+typedef struct
+{
+    uint32_t base;
+    qemu_irq  irq;
+    int pending;
+    int page;
+
+    unsigned events[MAX_EVENTS];
+    unsigned first;
+    unsigned last;
+
+    const char *name;
+    struct {
+        size_t len;
+        uint8_t *bits;
+    } ev_bits[EV_MAX + 1];
+    int32_t *abs_info;
+    size_t abs_info_count;
+} events_state;
+
+/* modify this each time you change the events_device structure. you
+ * will also need to upadte events_state_load and events_state_save
+ */
+#define  EVENTS_STATE_SAVE_VERSION  1
+
+#undef  QFIELD_STRUCT
+#define QFIELD_STRUCT  events_state
+
+QFIELD_BEGIN(events_state_fields)
+    QFIELD_INT32(pending),
+    QFIELD_INT32(page),
+    QFIELD_BUFFER(events),
+    QFIELD_INT32(first),
+    QFIELD_INT32(last),
+QFIELD_END
+
+static void  events_state_save(QEMUFile*  f, void*  opaque)
+{
+    events_state*  s = opaque;
+
+    qemu_put_struct(f, events_state_fields, s);
+}
+
+static int  events_state_load(QEMUFile*  f, void* opaque, int  version_id)
+{
+    events_state*  s = opaque;
+
+    if (version_id != EVENTS_STATE_SAVE_VERSION)
+        return -1;
+
+    return qemu_get_struct(f, events_state_fields, s);
+}
+
+extern const char*  android_skin_keycharmap;
+
+static void enqueue_event(events_state *s, unsigned int type, unsigned int code, int value)
+{
+    int  enqueued = s->last - s->first;
+
+    if (enqueued < 0)
+        enqueued += MAX_EVENTS;
+
+    if (enqueued + 3 >= MAX_EVENTS-1) {
+        fprintf(stderr, "##KBD: Full queue, lose event\n");
+        return;
+    }
+
+    if(s->first == s->last){
+        qemu_irq_raise(s->irq);
+    }
+
+    //fprintf(stderr, "##KBD: type=%d code=%d value=%d\n", type, code, value);
+
+    s->events[s->last] = type;
+    s->last = (s->last + 1) & (MAX_EVENTS-1);
+    s->events[s->last] = code;
+    s->last = (s->last + 1) & (MAX_EVENTS-1);
+    s->events[s->last] = value;
+    s->last = (s->last + 1) & (MAX_EVENTS-1);
+}
+
+static unsigned dequeue_event(events_state *s)
+{
+    unsigned n;
+
+    if(s->first == s->last) {
+        return 0;
+    }
+
+    n = s->events[s->first];
+
+    s->first = (s->first + 1) & (MAX_EVENTS - 1);
+
+    if(s->first == s->last) {
+        qemu_irq_lower(s->irq);
+    }
+
+    return n;
+}
+
+static int get_page_len(events_state *s)
+{
+    int page = s->page;
+    if (page == PAGE_NAME)
+        return strlen(s->name);
+    if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX)
+        return s->ev_bits[page - PAGE_EVBITS].len;
+    if (page == PAGE_ABSDATA)
+        return s->abs_info_count * sizeof(s->abs_info[0]);
+    return 0;
+}
+
+static int get_page_data(events_state *s, int offset)
+{
+    int page_len = get_page_len(s);
+    int page = s->page;
+    if (offset > page_len)
+        return 0;
+    if (page == PAGE_NAME)
+        return s->name[offset];
+    if (page >= PAGE_EVBITS && page <= PAGE_EVBITS + EV_MAX)
+        return s->ev_bits[page - PAGE_EVBITS].bits[offset];
+    if (page == PAGE_ABSDATA)
+        return s->abs_info[offset / sizeof(s->abs_info[0])];
+    return 0;
+}
+
+static uint32_t events_read(void *x, target_phys_addr_t off)
+{
+    events_state *s = (events_state *) x;
+    int offset = off - s->base;
+    if (offset == REG_READ)
+        return dequeue_event(s);
+    else if (offset == REG_LEN)
+        return get_page_len(s);
+    else if (offset >= REG_DATA)
+        return get_page_data(s, offset - REG_DATA);
+    return 0; // this shouldn't happen, if the driver does the right thing
+}
+
+static void events_write(void *x, target_phys_addr_t off, uint32_t val)
+{
+    events_state *s = (events_state *) x;
+    int offset = off - s->base;
+    if (offset == REG_SET_PAGE)
+        s->page = val;
+}
+
+static CPUReadMemoryFunc *events_readfn[] = {
+   events_read,
+   events_read,
+   events_read
+};
+
+static CPUWriteMemoryFunc *events_writefn[] = {
+   events_write,
+   events_write,
+   events_write
+};
+
+static void events_put_keycode(void *x, int keycode)
+{
+    events_state *s = (events_state *) x;
+
+    enqueue_event(s, EV_KEY, keycode&0x1ff, (keycode&0x200) ? 1 : 0);
+}
+
+static void events_put_mouse(void *opaque, int dx, int dy, int dz, int buttons_state)
+{
+    events_state *s = (events_state *) opaque;
+    if (dz == 0) {
+        enqueue_event(s, EV_ABS, ABS_X, dx);
+        enqueue_event(s, EV_ABS, ABS_Y, dy);
+        enqueue_event(s, EV_ABS, ABS_Z, dz);
+        enqueue_event(s, EV_KEY, BTN_TOUCH, buttons_state&1);
+    } else {
+        enqueue_event(s, EV_REL, REL_X, dx);
+        enqueue_event(s, EV_REL, REL_Y, dy);
+    }
+    enqueue_event(s, EV_SYN, 0, 0);
+}
+
+static void  events_put_generic(void*  opaque, int  type, int  code, int  value)
+{
+   events_state *s = (events_state *) opaque;
+
+    enqueue_event(s, type, code, value);
+}
+
+static int events_set_bits(events_state *s, int type, int bitl, int bith)
+{
+    uint8_t *bits;
+    uint8_t maskl, maskh;
+    int il, ih;
+    il = bitl / 8;
+    ih = bith / 8;
+    if (ih >= s->ev_bits[type].len) {
+        bits = qemu_mallocz(ih + 1);
+        if (bits == NULL)
+            return -ENOMEM;
+        memcpy(bits, s->ev_bits[type].bits, s->ev_bits[type].len);
+	qemu_free(s->ev_bits[type].bits);
+        s->ev_bits[type].bits = bits;
+        s->ev_bits[type].len = ih + 1;
+    }
+    else
+        bits = s->ev_bits[type].bits;
+    maskl = 0xffU << (bitl & 7);
+    maskh = 0xffU >> (7 - (bith & 7));
+    if (il >= ih)
+        maskh &= maskl;
+    else {
+        bits[il] |= maskl;
+        while (++il < ih)
+            bits[il] = 0xff;
+    }
+    bits[ih] |= maskh;
+    return 0;
+}
+
+#if 0
+static int events_set_abs_info(events_state *s, int axis, int32_t min, int32_t max, int32_t fuzz, int32_t flat)
+{
+    int32_t *info;
+    if (axis * 4 >= s->abs_info_count) {
+        info = qemu_mallocz((axis + 1) * 4 * sizeof(int32_t));
+        if (info == NULL)
+            return -ENOMEM;
+        memcpy(info, s->abs_info, s->abs_info_count);
+	qemu_free(s->abs_info);
+        s->abs_info = info;
+        s->abs_info_count = (axis + 1) * 4;
+    }
+    else
+        info = s->abs_info;
+    info += axis * 4;
+    *info++ = min;
+    *info++ = max;
+    *info++ = fuzz;
+    *info++ = flat;
+}
+#endif
+
+void events_dev_init(uint32_t base, qemu_irq irq)
+{
+    events_state *s;
+    int iomemtype;
+
+    s = (events_state *) qemu_mallocz(sizeof(events_state));
+    s->name = android_skin_keycharmap;
+    events_set_bits(s, EV_SYN, EV_SYN, EV_ABS);
+    events_set_bits(s, EV_SYN, EV_SW, EV_SW);
+    events_set_bits(s, EV_KEY, 1, 0x1ff);
+    events_set_bits(s, EV_REL, REL_X, REL_Y);
+    events_set_bits(s, EV_ABS, ABS_X, ABS_Z);
+    events_set_bits(s, EV_SW, 0, 0);
+    iomemtype = cpu_register_io_memory(0, events_readfn, events_writefn, s);
+
+    cpu_register_physical_memory(base, 0xfff, iomemtype);
+
+    qemu_add_kbd_event_handler(events_put_keycode, s);
+    qemu_add_mouse_event_handler(events_put_mouse, s, 1);
+    qemu_add_generic_event_handler(events_put_generic, s);
+
+    s->base = base;
+    s->irq = irq;
+
+    s->first = 0;
+    s->last = 0;
+
+    register_savevm( "events_state", 0, EVENTS_STATE_SAVE_VERSION,
+                      events_state_save, events_state_load, s );
+}
+
diff --git a/hw/goldfish_fb.c b/hw/goldfish_fb.c
new file mode 100644
index 0000000..71cede2
--- /dev/null
+++ b/hw/goldfish_fb.c
@@ -0,0 +1,405 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "android/android.h"
+#include "goldfish_device.h"
+#include "framebuffer.h"
+
+enum {
+    FB_GET_WIDTH        = 0x00,
+    FB_GET_HEIGHT       = 0x04,
+    FB_INT_STATUS       = 0x08,
+    FB_INT_ENABLE       = 0x0c,
+    FB_SET_BASE         = 0x10,
+    FB_SET_ROTATION     = 0x14,
+    FB_SET_BLANK        = 0x18,
+    FB_GET_PHYS_WIDTH   = 0x1c,
+    FB_GET_PHYS_HEIGHT  = 0x20,
+
+    FB_INT_VSYNC             = 1U << 0,
+    FB_INT_BASE_UPDATE_DONE  = 1U << 1
+};
+
+struct goldfish_fb_state {
+    struct goldfish_device dev;
+    QFrameBuffer*  qfbuff;
+    uint32_t fb_base;
+    uint32_t base_valid : 1;
+    uint32_t need_update : 1;
+    uint32_t need_int : 1;
+    uint32_t set_rotation : 2;
+    uint32_t blank : 1;
+    uint32_t int_status;
+    uint32_t int_enable;
+    int      rotation;   /* 0, 1, 2 or 3 */
+};
+
+#define  GOLDFISH_FB_SAVE_VERSION  1
+
+static void goldfish_fb_save(QEMUFile*  f, void*  opaque)
+{
+    struct goldfish_fb_state*  s = opaque;
+
+    QFrameBuffer*  q = s->qfbuff;
+
+    qemu_put_be32(f, q->width);
+    qemu_put_be32(f, q->height);
+    qemu_put_be32(f, q->pitch);
+    qemu_put_byte(f, q->rotation);
+
+    qemu_put_be32(f, s->fb_base);
+    qemu_put_byte(f, s->base_valid);
+    qemu_put_byte(f, s->need_update);
+    qemu_put_byte(f, s->need_int);
+    qemu_put_byte(f, s->set_rotation);
+    qemu_put_byte(f, s->blank);
+    qemu_put_be32(f, s->int_status);
+    qemu_put_be32(f, s->int_enable);
+    qemu_put_be32(f, s->rotation);
+}
+
+static int  goldfish_fb_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct goldfish_fb_state*  s   = opaque;
+
+    QFrameBuffer*              q   = s->qfbuff;
+    int                        ret = -1;
+    int                        ds_w, ds_h, ds_pitch, ds_rot;
+
+    if (version_id != GOLDFISH_FB_SAVE_VERSION)
+        goto Exit;
+
+    ds_w     = qemu_get_be32(f);
+    ds_h     = qemu_get_be32(f);
+    ds_pitch = qemu_get_be32(f);
+    ds_rot   = qemu_get_byte(f);
+
+    if (q->width != ds_w      ||
+        q->height != ds_h     ||
+        q->pitch != ds_pitch  ||
+        q->rotation != ds_rot )
+    {
+        /* XXX: We should be able to force a resize/rotation from here ? */
+        fprintf(stderr, "%s: framebuffer dimensions mismatch\n", __FUNCTION__);
+        goto Exit;
+    }
+
+    s->fb_base      = qemu_get_be32(f);
+    s->base_valid   = qemu_get_byte(f);
+    s->need_update  = qemu_get_byte(f);
+    s->need_int     = qemu_get_byte(f);
+    s->set_rotation = qemu_get_byte(f);
+    s->blank        = qemu_get_byte(f);
+    s->int_status   = qemu_get_be32(f);
+    s->int_enable   = qemu_get_be32(f);
+    s->rotation     = qemu_get_be32(f);
+
+    /* force a refresh */
+    s->need_update = 1;
+
+    ret = 0;
+Exit:
+    return ret;
+}
+
+
+#define  STATS  0
+
+#if STATS
+static int   stats_counter;
+static long  stats_total;
+static int   stats_full_updates;
+static long  stats_total_full_updates;
+#endif
+
+static void goldfish_fb_update_display(void *opaque)
+{
+    struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
+    uint32_t addr;
+    uint32_t base;
+
+    uint8_t*  dst_line;
+    uint8_t*  src_line;
+    int y_first, y_last = 0;
+    int full_update = 0;
+    int    width, height, pitch;
+
+    base = s->fb_base;
+    if(base == 0)
+        return;
+
+    if((s->int_enable & FB_INT_VSYNC) && !(s->int_status & FB_INT_VSYNC)) {
+        s->int_status |= FB_INT_VSYNC;
+        goldfish_device_set_irq(&s->dev, 0, 1);
+    }
+
+    y_first = -1;
+    addr  = base;
+    if(s->need_update) {
+        full_update = 1;
+        if(s->need_int) {
+            s->int_status |= FB_INT_BASE_UPDATE_DONE;
+            if(s->int_enable & FB_INT_BASE_UPDATE_DONE)
+                goldfish_device_set_irq(&s->dev, 0, 1);
+        }
+        s->need_int = 0;
+        s->need_update = 0;
+    }
+
+    src_line = phys_ram_base + base;
+    dst_line  = s->qfbuff->pixels;
+    pitch     = s->qfbuff->pitch;
+    width     = s->qfbuff->width;
+    height    = s->qfbuff->height;
+
+#if STATS
+    if (full_update)
+        stats_full_updates += 1;
+    if (++stats_counter == 120) {
+        stats_total               += stats_counter;
+        stats_total_full_updates  += stats_full_updates;
+
+        printf( "full update stats:  peak %.2f %%  total %.2f %%\n",
+                stats_full_updates*100.0/stats_counter,
+                stats_total_full_updates*100.0/stats_total );
+
+        stats_counter      = 0;
+        stats_full_updates = 0;
+    }
+#endif /* STATS */
+
+    if (s->blank)
+    {
+        memset( dst_line, 0, height*pitch );
+        y_first = 0;
+        y_last  = height-1;
+    }
+    else if (full_update)
+    {
+        int  yy;
+
+        for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
+        {
+            uint16_t*  src = (uint16_t*) src_line;
+            uint16_t*  dst = (uint16_t*) dst_line;
+            int        nn;
+
+            for (nn = 0; nn < width; nn++) {
+                unsigned   spix = src[nn];
+                unsigned   dpix = dst[nn];
+#if WORDS_BIGENDIAN
+                spix = ((spix << 8) | (spix >> 8)) & 0xffff;
+#else
+                if (spix != dpix)
+                    break;
+#endif
+            }
+
+            if (nn == width)
+                continue;
+
+#if WORDS_BIGENDIAN
+            for ( ; nn < width; nn++ ) {
+                unsigned   spix = src[nn];
+                dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
+            }
+#else
+            memcpy( dst+nn, src+nn, (width-nn)*2 );
+#endif
+
+            y_first = (y_first < 0) ? yy : y_first;
+            y_last  = yy;
+        }
+    }
+    else  /* not a full update, should not happen very often with Android */
+    {
+        int  yy;
+
+        for (yy = 0; yy < height; yy++, dst_line += pitch, src_line += width*2)
+        {
+            uint16_t*  src   = (uint16_t*) src_line;
+            uint16_t*  dst   = (uint16_t*) dst_line;
+            int        len   = width*2;
+#if WORDS_BIGENDIAN
+            int        nn;
+#endif
+            int        dirty = 0;
+
+            while (len > 0) {
+                int  len2 = TARGET_PAGE_SIZE - (addr & (TARGET_PAGE_SIZE-1));
+
+                if (len2 > len)
+                    len2 = len;
+
+                dirty |= cpu_physical_memory_get_dirty(addr, VGA_DIRTY_FLAG);
+                addr  += len2;
+                len   -= len2;
+            }
+
+            if (!dirty)
+                continue;
+
+#if WORDS_BIGENDIAN
+            for (nn = 0; nn < width; nn++ ) {
+                unsigned   spix = src[nn];
+                dst[nn] = (uint16_t)((spix << 8) | (spix >> 8));
+            }
+#else
+            memcpy( dst, src, width*2 );
+#endif
+
+            y_first = (y_first < 0) ? yy : y_first;
+            y_last  = yy;
+        }
+    }
+
+    if (y_first < 0)
+      return;
+
+    y_last += 1;
+    //printf("goldfish_fb_update_display %d %d, base %x\n", first, last, base);
+
+    cpu_physical_memory_reset_dirty(base + y_first * width * 2,
+                                    base + y_last * width * 2,
+                                    VGA_DIRTY_FLAG);
+
+    qframebuffer_update( s->qfbuff, 0, y_first, width, y_last-y_first );
+}
+
+static void goldfish_fb_invalidate_display(void * opaque)
+{
+    // is this called?
+    struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
+    s->need_update = 1;
+}
+
+static void  goldfish_fb_detach_display(void*  opaque)
+{
+    struct goldfish_fb_state *s = (struct goldfish_fb_state *)opaque;
+    s->qfbuff = NULL;
+}
+
+static uint32_t goldfish_fb_read(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t ret;
+    struct goldfish_fb_state *s = opaque;
+    offset -= s->dev.base;
+    switch(offset) {
+        case FB_GET_WIDTH:
+            ret = s->qfbuff->width;
+            //printf("FB_GET_WIDTH => %d\n", ret);
+            return ret;
+
+        case FB_GET_HEIGHT:
+            ret = s->qfbuff->height;
+            //printf( "FB_GET_HEIGHT = %d\n", ret);
+            return ret;
+
+        case FB_INT_STATUS:
+            ret = s->int_status & s->int_enable;
+            if(ret) {
+                s->int_status &= ~ret;
+                goldfish_device_set_irq(&s->dev, 0, 0);
+            }
+            return ret;
+
+        case FB_GET_PHYS_WIDTH:
+            ret = s->qfbuff->phys_width_mm;
+            //printf( "FB_GET_PHYS_WIDTH => %d\n", ret );
+            return ret;
+
+        case FB_GET_PHYS_HEIGHT:
+            ret = s->qfbuff->phys_height_mm;
+            //printf( "FB_GET_PHYS_HEIGHT => %d\n", ret );
+            return ret;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_fb_read: Bad offset %x\n", offset);
+            return 0;
+    }
+}
+
+static void goldfish_fb_write(void *opaque, target_phys_addr_t offset,
+                        uint32_t val)
+{
+    struct goldfish_fb_state *s = opaque;
+    offset -= s->dev.base;
+    switch(offset) {
+        case FB_INT_ENABLE:
+            s->int_enable = val;
+            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            break;
+        case FB_SET_BASE: {
+            int need_resize = !s->base_valid;
+            s->fb_base = val;
+            s->int_status &= ~FB_INT_BASE_UPDATE_DONE;
+            s->need_update = 1;
+            s->need_int = 1;
+            s->base_valid = 1;
+            if(s->set_rotation != s->rotation) {
+                //printf("FB_SET_BASE: rotation : %d => %d\n", s->rotation, s->set_rotation);
+                s->rotation = s->set_rotation;
+                need_resize = 1;
+            }
+            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            if (need_resize) {
+                //printf("FB_SET_BASE: need resize (rotation=%d)\n", s->rotation );
+                qframebuffer_rotate( s->qfbuff, s->rotation );
+            }
+            } break;
+        case FB_SET_ROTATION:
+            //printf( "FB_SET_ROTATION %d\n", val);
+            s->set_rotation = val;
+            break;
+        case FB_SET_BLANK:
+            s->blank = val;
+            s->need_update = 1;
+            break;
+        default:
+            cpu_abort (cpu_single_env, "goldfish_fb_write: Bad offset %x\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc *goldfish_fb_readfn[] = {
+   goldfish_fb_read,
+   goldfish_fb_read,
+   goldfish_fb_read
+};
+
+static CPUWriteMemoryFunc *goldfish_fb_writefn[] = {
+   goldfish_fb_write,
+   goldfish_fb_write,
+   goldfish_fb_write
+};
+
+void goldfish_fb_init(DisplayState *ds, int id)
+{
+    struct goldfish_fb_state *s;
+
+    s = (struct goldfish_fb_state *)qemu_mallocz(sizeof(*s));
+    s->dev.name = "goldfish_fb";
+    s->dev.id = id;
+    s->dev.size = 0x1000;
+    s->dev.irq_count = 1;
+
+    s->qfbuff = qframebuffer_fifo_get();
+    qframebuffer_set_producer( s->qfbuff, s,
+                               goldfish_fb_update_display,
+                               goldfish_fb_invalidate_display,
+                               goldfish_fb_detach_display );
+
+    goldfish_device_add(&s->dev, goldfish_fb_readfn, goldfish_fb_writefn, s);
+
+    register_savevm( "goldfish_fb", 0, GOLDFISH_FB_SAVE_VERSION,
+                     goldfish_fb_save, goldfish_fb_load, s);
+}
+
diff --git a/hw/goldfish_interrupt.c b/hw/goldfish_interrupt.c
new file mode 100644
index 0000000..2cba649
--- /dev/null
+++ b/hw/goldfish_interrupt.c
@@ -0,0 +1,190 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "arm_pic.h"
+#include "goldfish_device.h"
+#include "irq.h"
+
+enum {
+    INTERRUPT_STATUS        = 0x00, // number of pending interrupts
+    INTERRUPT_NUMBER        = 0x04,
+    INTERRUPT_DISABLE_ALL   = 0x08,
+    INTERRUPT_DISABLE       = 0x0c,
+    INTERRUPT_ENABLE        = 0x10
+};
+
+struct goldfish_int_state {
+    struct goldfish_device dev;
+    uint32_t level;
+    uint32_t pending_count;
+    uint32_t irq_enabled;
+    uint32_t fiq_enabled;
+    qemu_irq parent_irq;
+    qemu_irq parent_fiq;
+};
+
+#define  GOLDFISH_INT_SAVE_VERSION  1
+
+#define  QFIELD_STRUCT  struct goldfish_int_state
+QFIELD_BEGIN(goldfish_int_fields)
+    QFIELD_INT32(level),
+    QFIELD_INT32(pending_count),
+    QFIELD_INT32(irq_enabled),
+    QFIELD_INT32(fiq_enabled),
+QFIELD_END
+
+static void goldfish_int_save(QEMUFile*  f, void*  opaque)
+{
+    struct goldfish_int_state*  s = opaque;
+
+    qemu_put_struct(f, goldfish_int_fields, s);
+}
+
+static int  goldfish_int_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct goldfish_int_state*  s = opaque;
+
+    if (version_id != GOLDFISH_INT_SAVE_VERSION)
+        return -1;
+
+    return qemu_get_struct(f, goldfish_int_fields, s);
+}
+
+static void goldfish_int_update(struct goldfish_int_state *s)
+{
+    uint32_t flags;
+
+    flags = (s->level & s->irq_enabled);
+    qemu_set_irq(s->parent_irq, flags != 0);
+
+    flags = (s->level & s->fiq_enabled);
+    qemu_set_irq(s->parent_fiq, flags != 0);
+}
+
+static void goldfish_int_set_irq(void *opaque, int irq, int level)
+{
+    struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+    uint32_t mask = (1U << irq);
+
+    if(level) {
+        if(!(s->level & mask)) {
+            if(s->irq_enabled & mask)
+                s->pending_count++;
+            s->level |= mask;
+        }
+    }
+    else {
+        if(s->level & mask) {
+            if(s->irq_enabled & mask)
+                s->pending_count--;
+            s->level &= ~mask;
+        }
+    }
+    goldfish_int_update(s);
+}
+
+static uint32_t goldfish_int_read(void *opaque, target_phys_addr_t offset)
+{
+    struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+    offset -= s->dev.base;
+
+    switch (offset) {
+    case INTERRUPT_STATUS: /* IRQ_STATUS */
+        return s->pending_count;
+    case INTERRUPT_NUMBER: {
+        int i;
+        uint32_t pending = s->level & s->irq_enabled;
+        for(i = 0; i < 32; i++) {
+            if(pending & (1U << i))
+                return i;
+        }
+        return 0;
+    }
+    default:
+        cpu_abort (cpu_single_env, "goldfish_int_read: Bad offset %x\n", offset);
+        return 0;
+    }
+}
+
+static void goldfish_int_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    struct goldfish_int_state *s = (struct goldfish_int_state *)opaque;
+    uint32_t mask = (1U << value);
+    offset -= s->dev.base;
+
+    switch (offset) {
+        case INTERRUPT_DISABLE_ALL:
+            s->pending_count = 0;
+            s->level = 0;
+            break;
+
+        case INTERRUPT_DISABLE:
+            if(s->irq_enabled & mask) {
+                if(s->level & mask)
+                    s->pending_count--;
+                s->irq_enabled &= ~mask;
+            }
+            break;
+        case INTERRUPT_ENABLE:
+            if(!(s->irq_enabled & mask)) {
+                s->irq_enabled |= mask;
+                if(s->level & mask)
+                    s->pending_count++;
+            }
+            break;
+
+    default:
+        cpu_abort (cpu_single_env, "goldfish_int_write: Bad offset %x\n", offset);
+        return;
+    }
+    goldfish_int_update(s);
+}
+
+static CPUReadMemoryFunc *goldfish_int_readfn[] = {
+    goldfish_int_read,
+    goldfish_int_read,
+    goldfish_int_read
+};
+
+static CPUWriteMemoryFunc *goldfish_int_writefn[] = {
+    goldfish_int_write,
+    goldfish_int_write,
+    goldfish_int_write
+};
+
+qemu_irq*  goldfish_interrupt_init(uint32_t base, qemu_irq parent_irq, qemu_irq parent_fiq)
+{
+    int ret;
+    struct goldfish_int_state *s;
+    qemu_irq*  qi;
+
+    s = qemu_mallocz(sizeof(*s));
+    qi = qemu_allocate_irqs(goldfish_int_set_irq, s, 32);
+    s->dev.name = "goldfish_interrupt_controller";
+    s->dev.id = -1;
+    s->dev.base = base;
+    s->dev.size = 0x1000;
+    s->parent_irq = parent_irq;
+    s->parent_fiq = parent_fiq;
+
+    ret = goldfish_device_add(&s->dev, goldfish_int_readfn, goldfish_int_writefn, s);
+    if(ret) {
+        qemu_free(s);
+        return NULL;
+    }
+
+    register_savevm( "goldfish_int", 0, GOLDFISH_INT_SAVE_VERSION,
+                     goldfish_int_save, goldfish_int_load, s);
+
+    return qi;
+}
+
diff --git a/hw/goldfish_memlog.c b/hw/goldfish_memlog.c
new file mode 100644
index 0000000..98fcffc
--- /dev/null
+++ b/hw/goldfish_memlog.c
@@ -0,0 +1,78 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "audio/audio.h"
+
+extern void  dprint(const char*  fmt, ...);
+
+int fd = -1;
+
+static uint32_t memlog_read(void *opaque, target_phys_addr_t offset)
+{
+    struct goldfish_device *dev = opaque;
+    offset -= dev->base;
+
+    return 0;
+}
+
+unsigned info[8];
+
+static void memlog_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    char buf[128];
+    struct goldfish_device *dev = opaque;
+    offset -= dev->base;
+
+    info[offset / 4] = val;
+
+    if (offset == 0) {
+            /* write PID and VADDR to logfile */
+        sprintf(buf,"%08x %08x\n", info[0], info[1]);
+        write(fd, buf, strlen(buf));
+    }
+}
+
+
+static CPUReadMemoryFunc *memlog_readfn[] = {
+   memlog_read,
+   memlog_read,
+   memlog_read
+};
+
+static CPUWriteMemoryFunc *memlog_writefn[] = {
+   memlog_write,
+   memlog_write,
+   memlog_write
+};
+
+struct goldfish_device memlog_dev;
+
+void goldfish_memlog_init(uint32_t base)
+{
+    struct goldfish_device *dev = &memlog_dev;
+
+    dev->name = "goldfish_memlog";
+    dev->id = 0;
+    dev->base = base;
+    dev->size = 0x1000;
+    dev->irq_count = 0;
+
+    fd = open("mem.log", /* O_CREAT | */ O_TRUNC | O_WRONLY, 0644);
+
+    goldfish_device_add(dev, memlog_readfn, memlog_writefn, dev);
+}
+
diff --git a/hw/goldfish_mmc.c b/hw/goldfish_mmc.c
new file mode 100644
index 0000000..272f403
--- /dev/null
+++ b/hw/goldfish_mmc.c
@@ -0,0 +1,468 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+#include "mmc.h"
+#include "sd.h"
+#include "block.h"
+
+enum {
+    /* status register */
+    MMC_INT_STATUS          = 0x00,
+    /* set this to enable IRQ */
+    MMC_INT_ENABLE          = 0x04,
+    /* set this to specify buffer address */
+    MMC_SET_BUFFER          = 0x08,
+
+    /* MMC command number */
+    MMC_CMD                 = 0x0C,
+
+    /* MMC argument */
+    MMC_ARG                 = 0x10,
+
+    /* MMC response (or R2 bits 0 - 31) */
+    MMC_RESP_0              = 0x14,
+
+    /* MMC R2 response bits 32 - 63 */
+    MMC_RESP_1              = 0x18,
+
+    /* MMC R2 response bits 64 - 95 */
+    MMC_RESP_2              = 0x1C,
+
+    /* MMC R2 response bits 96 - 127 */
+    MMC_RESP_3              = 0x20,
+
+    MMC_BLOCK_LENGTH        = 0x24,
+    MMC_BLOCK_COUNT         = 0x28,
+
+    /* MMC state flags */
+    MMC_STATE               = 0x2C,
+
+    /* MMC_INT_STATUS bits */
+
+    MMC_STAT_END_OF_CMD     = 1U << 0,
+    MMC_STAT_END_OF_DATA    = 1U << 1,
+    MMC_STAT_STATE_CHANGE   = 1U << 2,
+
+    /* MMC_STATE bits */
+    MMC_STATE_INSERTED     = 1U << 0,
+    MMC_STATE_READ_ONLY     = 1U << 1,
+};
+
+
+struct goldfish_mmc_state {
+    struct goldfish_device dev;
+    BlockDriverState *bs;
+    // pointer to our buffer
+    uint8_t* buffer;
+    // offsets for read and write operations
+    uint32_t read_offset, write_offset;
+    // buffer status flags
+    uint32_t int_status;
+    // irq enable mask for int_status
+    uint32_t int_enable;
+
+    // MMC command argument
+    uint32_t arg;
+    uint32_t resp[4];
+
+    uint32_t block_length;
+    uint32_t block_count;
+    int is_SDHC;
+};
+
+#define  GOLDFISH_MMC_SAVE_VERSION  1
+#define  QFIELD_STRUCT  struct goldfish_mmc_state
+QFIELD_BEGIN(goldfish_mmc_fields)
+    QFIELD_INT32(read_offset),
+    QFIELD_INT32(write_offset),
+    QFIELD_INT32(int_status),
+    QFIELD_INT32(int_enable),
+    QFIELD_INT32(arg),
+    QFIELD_INT32(resp[0]),
+    QFIELD_INT32(resp[1]),
+    QFIELD_INT32(resp[2]),
+    QFIELD_INT32(resp[3]),
+    QFIELD_INT32(block_length),
+    QFIELD_INT32(block_count),
+    QFIELD_INT32(is_SDHC),
+QFIELD_END
+
+static void  goldfish_mmc_save(QEMUFile*  f, void*  opaque)
+{
+    struct goldfish_mmc_state*  s = opaque;
+
+    qemu_put_be32(f, s->buffer - phys_ram_base);
+    qemu_put_struct(f, goldfish_mmc_fields, s);
+}
+
+static int  goldfish_mmc_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct goldfish_mmc_state*  s = opaque;
+
+    if (version_id != GOLDFISH_MMC_SAVE_VERSION)
+        return -1;
+
+    s->buffer = qemu_get_be32(f) + phys_ram_base;
+    return qemu_get_struct(f, goldfish_mmc_fields, s);
+}
+
+struct mmc_opcode {
+    const char* name;
+    int cmd;
+} mmc_opcodes[] = {
+    { "MMC_GO_IDLE_STATE",         0  },
+    { "MMC_SEND_OP_COND",          1  },
+    { "MMC_ALL_SEND_CID",          2  },
+    { "MMC_SET_RELATIVE_ADDR",     3  },
+    { "MMC_SET_DSR",               4  },
+    { "MMC_SWITCH",                6  },
+    { "MMC_SELECT_CARD",           7  },
+    { "MMC_SEND_EXT_CSD",          8  },
+    { "MMC_SEND_CSD",              9  },
+    { "MMC_SEND_CID",             10  },
+    { "MMC_READ_DAT_UNTIL_STOP",  11  },
+    { "MMC_STOP_TRANSMISSION",    12  },
+    { "MMC_SEND_STATUS",          13  },
+    { "MMC_GO_INACTIVE_STATE",    15  },
+    { "MMC_SET_BLOCKLEN",         16  },
+    { "MMC_READ_SINGLE_BLOCK",    17  },
+    { "MMC_READ_MULTIPLE_BLOCK",  18  },
+    { "MMC_WRITE_DAT_UNTIL_STOP", 20  },
+    { "MMC_SET_BLOCK_COUNT",      23  },
+    { "MMC_WRITE_BLOCK",          24  },
+    { "MMC_WRITE_MULTIPLE_BLOCK", 25  },
+    { "MMC_PROGRAM_CID",          26  },
+    { "MMC_PROGRAM_CSD",          27  },
+    { "MMC_SET_WRITE_PROT",       28  },
+    { "MMC_CLR_WRITE_PROT",       29  },
+    { "MMC_SEND_WRITE_PROT",      30  },
+    { "MMC_ERASE_GROUP_START",    35  },
+    { "MMC_ERASE_GROUP_END",      36  },
+    { "MMC_ERASE",                38  },
+    { "MMC_FAST_IO",              39  },
+    { "MMC_GO_IRQ_STATE",         40  },
+    { "MMC_LOCK_UNLOCK",          42  },
+    { "MMC_APP_CMD",              55  },
+    { "MMC_GEN_CMD",              56  },
+    { "SD_APP_OP_COND",           41  },
+    { "SD_APP_SEND_SCR",          51  },
+    { "UNKNOWN",                  -1  }
+};
+
+#if 0
+static const char* get_command_name(int command)
+{
+    struct mmc_opcode* opcode = mmc_opcodes;
+
+    while (opcode->cmd != command && opcode->cmd != -1) opcode++;
+    return opcode->name;
+}
+#endif
+
+static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd, uint32_t arg)
+{
+    int result;
+    int new_status = MMC_STAT_END_OF_CMD;
+    int opcode = cmd & 63;
+
+// fprintf(stderr, "goldfish_mmc_do_command opcode: %s (0x%04X), arg: %d\n", get_command_name(opcode), cmd, arg);
+
+    s->resp[0] = 0;
+    s->resp[1] = 0;
+    s->resp[2] = 0;
+    s->resp[3] = 0;
+
+#define SET_R1_CURRENT_STATE(s)    ((s << 9) & 0x00001E00) /* sx, b (4 bits) */
+
+    switch (opcode) {
+        case MMC_SEND_CSD: {
+            int64_t sector_count = 0;
+            uint64_t capacity;
+            uint8_t exponent;
+            uint32_t m;
+
+            bdrv_get_geometry(s->bs, (uint64_t*)&sector_count);
+            capacity = sector_count * 512;
+            if (capacity > 2147483648U) {
+                // if storages is > 2 gig, then emulate SDHC card
+                s->is_SDHC = 1;
+
+                // CSD bits borrowed from a real SDHC card, with capacity bits zeroed out
+                s->resp[3] = 0x400E0032;
+                s->resp[2] = 0x5B590000;
+                s->resp[1] = 0x00007F80;
+                s->resp[0] = 0x0A4040DF;
+
+                // stuff in the real capacity
+                // m = UNSTUFF_BITS(resp, 48, 22);
+                m = (uint32_t)(capacity / (512*1024)) - 1;
+                // m must fit into 22 bits
+                if (m & 0xFFC00000) {
+                    fprintf(stderr, "SD card too big (%lld bytes).  Maximum SDHC card size is 128 gigabytes.\n", capacity);
+                    abort();
+                }
+
+                // low 16 bits go in high end of resp[1]
+                s->resp[1] |= ((m & 0x0000FFFF) << 16);
+                // high 6 bits go in low end of resp[2]
+                s->resp[2] |= (m >> 16);
+            } else {
+                // emulate standard SD card
+                s->is_SDHC = 0;
+
+                // CSD bits borrowed from a real SD card, with capacity bits zeroed out
+                s->resp[3] = 0x00260032;
+                s->resp[2] = 0x5F5A8000;
+                s->resp[1] = 0x3EF84FFF;
+                s->resp[0] = 0x928040CB;
+
+                // stuff in the real capacity
+                // e = UNSTUFF_BITS(resp, 47, 3);
+                // m = UNSTUFF_BITS(resp, 62, 12);
+                // csd->capacity = (1 + m) << (e + 2);
+                // need to reverse the formula and calculate e and m
+                exponent = 0;
+                capacity = sector_count * 512;
+                if (capacity > 2147483648U) {
+                    fprintf(stderr, "SD card too big (%lld bytes).  Maximum SD card size is 2 gigabytes.\n", capacity);
+                    abort();
+                }
+                capacity >>= 10; // convert to Kbytes
+                while (capacity > 4096) {
+                    // (capacity - 1) must fit into 12 bits
+                    exponent++;
+                    capacity >>= 1;
+                }
+                capacity -= 1;
+                exponent -= 2;
+                if (exponent > 7)
+                    cpu_abort(cpu_single_env, "exponent %d too big\n", exponent);
+
+                s->resp[2] |= (((uint32_t)capacity >> 2) & 0x3FF);  // high 10 bits to bottom of resp[2]
+                s->resp[1] |= (((uint32_t)capacity & 3) << 30);    // low 2 bits to top of resp[1]
+                s->resp[1] |= (exponent << (47 - 32));
+            }
+            break;
+        }
+
+        case MMC_SEND_EXT_CSD:
+            s->resp[0] = arg;
+            break;
+
+        case MMC_APP_CMD:
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
+            break;
+
+        case SD_APP_OP_COND:
+            s->resp[0] = 0x80FF8000;
+            break;
+
+        case SD_APP_SEND_SCR:
+        {
+            uint32_t* scr = (uint32_t*)s->buffer;
+            scr[0] = 0x00002502;
+            scr[1] = 0x00000000;
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
+            new_status |= MMC_STAT_END_OF_DATA;
+            break;
+        }
+        case MMC_SET_RELATIVE_ADDR:
+            s->resp[0] = -518519520;
+            break;
+
+        case MMC_ALL_SEND_CID:
+            s->resp[3] = 55788627;
+            s->resp[2] = 1429221959;
+            s->resp[1] = -2147479692;
+            s->resp[0] = -436179883;
+            break;
+
+        case MMC_SELECT_CARD:
+            s->resp[0] = SET_R1_CURRENT_STATE(3) | R1_READY_FOR_DATA; // 1792
+            break;
+
+         case MMC_SWITCH:
+            if (arg == 0x00FFFFF1 || arg == 0x80FFFFF1) {
+                uint8_t* switchbuf = s->buffer;
+                memset(switchbuf, 0, 64);
+                switchbuf[13] = 2;
+                new_status |= MMC_STAT_END_OF_DATA;
+            }
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA | R1_APP_CMD; //2336
+            break;
+
+         case MMC_SET_BLOCKLEN:
+            s->block_length = arg;
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+            break;
+
+        case MMC_READ_SINGLE_BLOCK:
+            s->block_count = 1;
+            // fall through
+        case MMC_READ_MULTIPLE_BLOCK: {
+            if (s->is_SDHC) {
+                // arg is block offset
+            } else {
+                // arg is byte offset
+                if (arg & 511) fprintf(stderr, "offset %d is not multiple of 512 when reading\n", arg);
+                arg /= s->block_length;
+            }
+            result = bdrv_read(s->bs, arg, s->buffer, s->block_count);
+            new_status |= MMC_STAT_END_OF_DATA;
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+            break;
+        }
+
+        case MMC_WRITE_BLOCK:
+            s->block_count = 1;
+            // fall through
+        case MMC_WRITE_MULTIPLE_BLOCK: {
+            if (s->is_SDHC) {
+                // arg is block offset
+            } else {
+                // arg is byte offset
+                if (arg & 511) fprintf(stderr, "offset %d is not multiple of 512 when writing\n", arg);
+                arg /= s->block_length;
+            }
+            // arg is byte offset
+            result = bdrv_write(s->bs, arg, s->buffer, s->block_count);
+//            bdrv_flush(s->bs);
+            new_status |= MMC_STAT_END_OF_DATA;
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+            break;
+        }
+
+        case MMC_STOP_TRANSMISSION:
+            s->resp[0] = SET_R1_CURRENT_STATE(5) | R1_READY_FOR_DATA; // 2816
+            break;
+
+        case MMC_SEND_STATUS:
+            s->resp[0] = SET_R1_CURRENT_STATE(4) | R1_READY_FOR_DATA; // 2304
+            break;
+     }
+
+    s->int_status |= new_status;
+
+    if ((s->int_status & s->int_enable)) {
+        goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+    }
+}
+
+static uint32_t goldfish_mmc_read(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t ret;
+    struct goldfish_mmc_state *s = opaque;
+
+    offset -= s->dev.base;
+    switch(offset) {
+        case MMC_INT_STATUS:
+            // return current buffer status flags
+            return s->int_status & s->int_enable;
+        case MMC_RESP_0:
+            return s->resp[0];
+        case MMC_RESP_1:
+            return s->resp[1];
+        case MMC_RESP_2:
+            return s->resp[2];
+        case MMC_RESP_3:
+            return s->resp[3];
+        case MMC_STATE: {
+            ret = MMC_STATE_INSERTED;
+            if (bdrv_is_read_only(s->bs)) {
+                ret |= MMC_STATE_READ_ONLY;
+            }
+            return ret;
+        }
+        default:
+            cpu_abort(cpu_single_env, "goldfish_mmc_read: Bad offset %x\n", offset);
+            return 0;
+    }
+}
+
+static void goldfish_mmc_write(void *opaque, target_phys_addr_t offset, uint32_t val)
+{
+    struct goldfish_mmc_state *s = opaque;
+    int status, old_status;
+
+    offset -= s->dev.base;
+
+    switch(offset) {
+
+        case MMC_INT_STATUS:
+            status = s->int_status;
+            old_status = status;
+            status &= ~val;
+            s->int_status = status;
+            if(status != old_status) {
+                goldfish_device_set_irq(&s->dev, 0, status);
+            }
+            break;
+
+        case MMC_INT_ENABLE:
+            /* enable buffer interrupts */
+            s->int_enable = val;
+            s->int_status = 0;
+            goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+            break;
+        case MMC_SET_BUFFER:
+            /* save pointer to buffer 1 */
+            s->buffer = phys_ram_base + val;
+            break;
+        case MMC_CMD:
+            goldfish_mmc_do_command(s, val, s->arg);
+            break;
+        case MMC_ARG:
+            s->arg = val;
+            break;
+        case MMC_BLOCK_LENGTH:
+            s->block_length = val + 1;
+            break;
+        case MMC_BLOCK_COUNT:
+            s->block_count = val + 1;
+            break;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_mmc_write: Bad offset %x\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc *goldfish_mmc_readfn[] = {
+   goldfish_mmc_read,
+   goldfish_mmc_read,
+   goldfish_mmc_read
+};
+
+static CPUWriteMemoryFunc *goldfish_mmc_writefn[] = {
+   goldfish_mmc_write,
+   goldfish_mmc_write,
+   goldfish_mmc_write
+};
+
+void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs)
+{
+    struct goldfish_mmc_state *s;
+
+    s = (struct goldfish_mmc_state *)qemu_mallocz(sizeof(*s));
+    s->dev.name = "goldfish_mmc";
+    s->dev.id = id;
+    s->dev.base = base;
+    s->dev.size = 0x1000;
+    s->dev.irq_count = 1;
+    s->bs = bs;
+
+    goldfish_device_add(&s->dev, goldfish_mmc_readfn, goldfish_mmc_writefn, s);
+
+    register_savevm( "goldfish_mmc", 0, GOLDFISH_MMC_SAVE_VERSION,
+                     goldfish_mmc_save, goldfish_mmc_load, s);
+}
+
diff --git a/hw/goldfish_nand.c b/hw/goldfish_nand.c
new file mode 100644
index 0000000..61b075e
--- /dev/null
+++ b/hw/goldfish_nand.c
@@ -0,0 +1,636 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "goldfish_nand_reg.h"
+#include "goldfish_nand.h"
+#include "android/utils/tempfile.h"
+#include "qemu_debug.h"
+#include "android/android.h"
+
+#define  DEBUG  1
+#if DEBUG
+#  define  D(...)    VERBOSE_PRINT(nand,__VA_ARGS__)
+#  define  D_ACTIVE  VERBOSE_CHECK(nand)
+#  define  T(...)    VERBOSE_PRINT(nand_limits,__VA_ARGS__)
+#  define  T_ACTIVE  VERBOSE_CHECK(nand_limits)
+#else
+#  define  D(...)    ((void)0)
+#  define  D_ACTIVE  0
+#  define  T(...)    ((void)0)
+#  define  T_ACTIVE  0
+#endif
+
+/* lseek uses 64-bit offsets on Darwin. */
+/* prefer lseek64 on Linux              */
+#ifdef __APPLE__
+#  define  llseek  lseek
+#elif defined(__linux__)
+#  define  llseek  lseek64
+#endif
+
+#define  XLOG  xlog
+
+static void
+xlog( const char*  format, ... )
+{
+    va_list  args;
+    va_start(args, format);
+    fprintf(stderr, "NAND: ");
+    vfprintf(stderr, format, args);
+    va_end(args);
+}
+
+typedef struct {
+    char*      devname;
+    size_t     devname_len;
+    char*      data;
+    int        fd;
+    uint32_t   flags;
+    uint32_t   page_size;
+    uint32_t   extra_size;
+    uint32_t   erase_size;
+    uint64_t   size;
+} nand_dev;
+
+nand_threshold    android_nand_write_threshold;
+nand_threshold    android_nand_read_threshold;
+
+#ifdef CONFIG_NAND_THRESHOLD
+
+/* update a threshold, return 1 if limit is hit, 0 otherwise */
+static void
+nand_threshold_update( nand_threshold*  t, uint32_t  len )
+{
+    if (t->counter < t->limit) {
+        uint64_t  avail = t->limit - t->counter;
+        if (avail > len)
+            avail = len;
+
+        if (t->counter == 0) {
+            T("%s: starting threshold counting to %lld", 
+              __FUNCTION__, t->limit);
+        }
+        t->counter += avail;
+        if (t->counter >= t->limit) {
+            /* threshold reach, send a signal to an external process */
+            T( "%s: sending signal %d to pid %d !", 
+               __FUNCTION__, t->signal, t->pid );
+
+            kill( t->pid, t->signal );
+        }
+    }
+    return;
+}
+
+#define  NAND_UPDATE_READ_THRESHOLD(len)  \
+    nand_threshold_update( &android_nand_read_threshold, (uint32_t)(len) )
+
+#define  NAND_UPDATE_WRITE_THRESHOLD(len)  \
+    nand_threshold_update( &android_nand_write_threshold, (uint32_t)(len) )
+
+#else /* !NAND_THRESHOLD */
+
+#define  NAND_UPDATE_READ_THRESHOLD(len)  \
+    do {} while (0)
+
+#define  NAND_UPDATE_WRITE_THRESHOLD(len)  \
+    do {} while (0)
+
+#endif /* !NAND_THRESHOLD */
+
+static nand_dev *nand_devs = NULL;
+static uint32_t nand_dev_count = 0;
+
+typedef struct {
+    uint32_t base;
+
+    // register state
+    uint32_t dev;
+    uint32_t addr_low;
+    uint32_t addr_high;
+    uint32_t transfer_size;
+    uint32_t data;
+    uint32_t result;
+} nand_dev_state;
+
+/* update this everytime you change the nand_dev_state structure */
+#define  NAND_DEV_STATE_SAVE_VERSION  1
+
+#define  QFIELD_STRUCT  nand_dev_state
+QFIELD_BEGIN(nand_dev_state_fields)
+    QFIELD_INT32(dev),
+    QFIELD_INT32(addr_low),
+    QFIELD_INT32(addr_high),
+    QFIELD_INT32(transfer_size),
+    QFIELD_INT32(data),
+    QFIELD_INT32(result),
+QFIELD_END
+
+static void  nand_dev_state_save(QEMUFile*  f, void*  opaque)
+{
+    nand_dev_state*  s = opaque;
+
+    qemu_put_struct(f, nand_dev_state_fields, s);
+}
+
+static int   nand_dev_state_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    nand_dev_state*  s = opaque;
+
+    if (version_id != NAND_DEV_STATE_SAVE_VERSION)
+        return -1;
+
+    return qemu_get_struct(f, nand_dev_state_fields, s);
+}
+
+
+static int  do_read(int  fd, void*  buf, size_t  size)
+{
+    int  ret;
+    do {
+        ret = read(fd, buf, size);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+static int  do_write(int  fd, const void*  buf, size_t  size)
+{
+    int  ret;
+    do {
+        ret = write(fd, buf, size);
+    } while (ret < 0 && errno == EINTR);
+
+    return ret;
+}
+
+static uint32_t nand_dev_read_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
+{
+    uint32_t len = total_len;
+    size_t read_len = dev->erase_size;
+    int eof = 0;
+
+    NAND_UPDATE_READ_THRESHOLD(total_len);
+
+    lseek(dev->fd, addr, SEEK_SET);
+    while(len > 0) {
+        if(read_len < dev->erase_size) {
+            memset(dev->data, 0xff, dev->erase_size);
+            read_len = dev->erase_size;
+            eof = 1;
+        }
+        if(len < read_len)
+            read_len = len;
+        if(!eof) {
+            read_len = do_read(dev->fd, dev->data, read_len);
+        }
+        pmemcpy(data, dev->data, read_len);
+        data += read_len;
+        len -= read_len;
+    }
+    return total_len;
+}
+
+static uint32_t nand_dev_write_file(nand_dev *dev, uint32_t data, uint64_t addr, uint32_t total_len)
+{
+    uint32_t len = total_len;
+    size_t write_len = dev->erase_size;
+    int ret;
+
+    NAND_UPDATE_WRITE_THRESHOLD(total_len);
+
+    lseek(dev->fd, addr, SEEK_SET);
+    while(len > 0) {
+        if(len < write_len)
+            write_len = len;
+        vmemcpy(data, dev->data, write_len);
+        ret = do_write(dev->fd, dev->data, write_len);
+        if(ret < write_len) {
+            XLOG("nand_dev_write_file, write failed: %s\n", strerror(errno));
+            break;
+        }
+        data += write_len;
+        len -= write_len;
+    }
+    return total_len - len;
+}
+
+static uint32_t nand_dev_erase_file(nand_dev *dev, uint64_t addr, uint32_t total_len)
+{
+    uint32_t len = total_len;
+    size_t write_len = dev->erase_size;
+    int ret;
+
+    lseek(dev->fd, addr, SEEK_SET);
+    memset(dev->data, 0xff, dev->erase_size);
+    while(len > 0) {
+        if(len < write_len)
+            write_len = len;
+        ret = do_write(dev->fd, dev->data, write_len);
+        if(ret < write_len) {
+            XLOG( "nand_dev_write_file, write failed: %s\n", strerror(errno));
+            break;
+        }
+        len -= write_len;
+    }
+    return total_len - len;
+}
+
+/* this is a huge hack required to make the PowerPC emulator binary usable
+ * on Mac OS X. If you define this function as 'static', the emulated kernel
+ * will panic when attempting to mount the /data partition.
+ *
+ * worse, if you do *not* define the function as static on Linux-x86, the
+ * emulated kernel will also panic !?
+ *
+ * I still wonder if this is a compiler bug, or due to some nasty thing the
+ * emulator does with CPU registers during execution of the translated code.
+ */
+#if !(defined __APPLE__ && defined __powerpc__)
+static
+#endif
+uint32_t nand_dev_do_cmd(nand_dev_state *s, uint32_t cmd)
+{
+    uint32_t size;
+    uint64_t addr;
+    nand_dev *dev;
+
+    addr = s->addr_low | ((uint64_t)s->addr_high << 32);
+    size = s->transfer_size;
+    if(s->dev >= nand_dev_count)
+        return 0;
+    dev = nand_devs + s->dev;
+
+    switch(cmd) {
+    case NAND_CMD_GET_DEV_NAME:
+        if(size > dev->devname_len)
+            size = dev->devname_len;
+        pmemcpy(s->data, dev->devname, size);
+        return size;
+    case NAND_CMD_READ:
+        if(addr >= dev->size)
+            return 0;
+        if(size + addr > dev->size)
+            size = dev->size - addr;
+        if(dev->fd >= 0)
+            return nand_dev_read_file(dev, s->data, addr, size);
+        pmemcpy(s->data, &dev->data[addr], size);
+        return size;
+    case NAND_CMD_WRITE:
+        if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+            return 0;
+        if(addr >= dev->size)
+            return 0;
+        if(size + addr > dev->size)
+            size = dev->size - addr;
+        if(dev->fd >= 0)
+            return nand_dev_write_file(dev, s->data, addr, size);
+        vmemcpy(s->data, &dev->data[addr], size);
+        return size;
+    case NAND_CMD_ERASE:
+        if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+            return 0;
+        if(addr >= dev->size)
+            return 0;
+        if(size + addr > dev->size)
+            size = dev->size - addr;
+        if(dev->fd >= 0)
+            return nand_dev_erase_file(dev, addr, size);
+        memset(&dev->data[addr], 0xff, size);
+        return size;
+    case NAND_CMD_BLOCK_BAD_GET: // no bad block support
+        return 0;
+    case NAND_CMD_BLOCK_BAD_SET:
+        if(dev->flags & NAND_DEV_FLAG_READ_ONLY)
+            return 0;
+        return 0;
+    default:
+        cpu_abort(cpu_single_env, "nand_dev_do_cmd: Bad command %x\n", cmd);
+        return 0;
+    }
+}
+
+/* I/O write */
+static void nand_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    nand_dev_state *s = (nand_dev_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case NAND_DEV:
+        s->dev = value;
+        if(s->dev >= nand_dev_count) {
+            cpu_abort(cpu_single_env, "nand_dev_write: Bad dev %x\n", value);
+        }
+        break;
+    case NAND_ADDR_HIGH:
+        s->addr_high = value;
+        break;
+    case NAND_ADDR_LOW:
+        s->addr_low = value;
+        break;
+    case NAND_TRANSFER_SIZE:
+        s->transfer_size = value;
+        break;
+    case NAND_DATA:
+        s->data = value;
+        break;
+    case NAND_COMMAND:
+        s->result = nand_dev_do_cmd(s, value);
+        break;
+    default:
+        cpu_abort(cpu_single_env, "nand_dev_write: Bad offset %x\n", offset);
+        break;
+    }
+}
+
+/* I/O read */
+static uint32_t nand_dev_read(void *opaque, target_phys_addr_t offset)
+{
+    nand_dev_state *s = (nand_dev_state *)opaque;
+    nand_dev *dev;
+
+    offset -= s->base;
+    switch (offset) {
+    case NAND_VERSION:
+        return NAND_VERSION_CURRENT;
+    case NAND_NUM_DEV:
+        return nand_dev_count;
+    case NAND_RESULT:
+        return s->result;
+    }
+
+    if(s->dev >= nand_dev_count)
+        return 0;
+
+    dev = nand_devs + s->dev;
+
+    switch (offset) {
+    case NAND_DEV_FLAGS:
+        return dev->flags;
+
+    case NAND_DEV_NAME_LEN:
+        return dev->devname_len;
+
+    case NAND_DEV_PAGE_SIZE:
+        return dev->page_size;
+
+    case NAND_DEV_EXTRA_SIZE:
+        return dev->extra_size;
+
+    case NAND_DEV_ERASE_SIZE:
+        return dev->erase_size;
+
+    case NAND_DEV_SIZE_LOW:
+        return (uint32_t)dev->size;
+
+    case NAND_DEV_SIZE_HIGH:
+        return (uint32_t)(dev->size >> 32);
+
+    default:
+        cpu_abort(cpu_single_env, "nand_dev_read: Bad offset %x\n", offset);
+        return 0;
+    }
+}
+
+static CPUReadMemoryFunc *nand_dev_readfn[] = {
+   nand_dev_read,
+   nand_dev_read,
+   nand_dev_read
+};
+
+static CPUWriteMemoryFunc *nand_dev_writefn[] = {
+   nand_dev_write,
+   nand_dev_write,
+   nand_dev_write
+};
+
+/* initialize the QFB device */
+void nand_dev_init(uint32_t base)
+{
+    int iomemtype;
+    static int  instance_id = 0;
+    nand_dev_state *s;
+
+    s = (nand_dev_state *)qemu_mallocz(sizeof(nand_dev_state));
+    iomemtype = cpu_register_io_memory(0, nand_dev_readfn, nand_dev_writefn, s);
+    cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+    s->base = base;
+
+    register_savevm( "nand_dev", instance_id++, NAND_DEV_STATE_SAVE_VERSION,
+                      nand_dev_state_save, nand_dev_state_load, s);
+}
+
+static int arg_match(const char *a, const char *b, size_t b_len)
+{
+    while(*a && b_len--) {
+        if(*a++ != *b++)
+            return 0;
+    }
+    return b_len == 0;
+}
+
+void nand_add_dev(const char *arg)
+{
+    uint64_t dev_size = 0;
+    const char *next_arg;
+    const char *value;
+    size_t arg_len, value_len;
+    nand_dev *new_devs, *dev;
+    char *devname = NULL;
+    size_t devname_len = 0;
+    char *initfilename = NULL;
+    char *rwfilename = NULL;
+    int initfd = -1;
+    int rwfd = -1;
+    int read_only = 0;
+    int pad;
+    ssize_t read_size;
+    uint32_t page_size = 2048;
+    uint32_t extra_size = 64;
+    uint32_t erase_pages = 64;
+
+    while(arg) {
+        next_arg = strchr(arg, ',');
+        value = strchr(arg, '=');
+        if(next_arg != NULL) {
+            arg_len = next_arg - arg;
+            next_arg++;
+            if(value >= next_arg)
+                value = NULL;
+        }
+        else
+            arg_len = strlen(arg);
+        if(value != NULL) {
+            size_t new_arg_len = value - arg;
+            value_len = arg_len - new_arg_len - 1;
+            arg_len = new_arg_len;
+            value++;
+        }
+        else
+            value_len = 0;
+
+        if(devname == NULL) {
+            if(value != NULL)
+                goto bad_arg_and_value;
+            devname_len = arg_len;
+            devname = malloc(arg_len);
+            if(devname == NULL)
+                goto out_of_memory;
+            memcpy(devname, arg, arg_len);
+        }
+        else if(value == NULL) {
+            if(arg_match("readonly", arg, arg_len)) {
+                read_only = 1;
+            }
+            else {
+                XLOG("bad arg: %.*s\n", arg_len, arg);
+                exit(1);
+            }
+        }
+        else {
+            if(arg_match("size", arg, arg_len)) {
+                char *ep;
+                dev_size = strtoull(value, &ep, 0);
+                if(ep != value + value_len)
+                    goto bad_arg_and_value;
+            }
+            else if(arg_match("pagesize", arg, arg_len)) {
+                char *ep;
+                page_size = strtoul(value, &ep, 0);
+                if(ep != value + value_len)
+                    goto bad_arg_and_value;
+            }
+            else if(arg_match("extrasize", arg, arg_len)) {
+                char *ep;
+                extra_size = strtoul(value, &ep, 0);
+                if(ep != value + value_len)
+                    goto bad_arg_and_value;
+            }
+            else if(arg_match("erasepages", arg, arg_len)) {
+                char *ep;
+                erase_pages = strtoul(value, &ep, 0);
+                if(ep != value + value_len)
+                    goto bad_arg_and_value;
+            }
+            else if(arg_match("initfile", arg, arg_len)) {
+                initfilename = malloc(value_len + 1);
+                if(initfilename == NULL)
+                    goto out_of_memory;
+                memcpy(initfilename, value, value_len);
+                initfilename[value_len] = '\0';
+            }
+            else if(arg_match("file", arg, arg_len)) {
+                rwfilename = malloc(value_len + 1);
+                if(rwfilename == NULL)
+                    goto out_of_memory;
+                memcpy(rwfilename, value, value_len);
+                rwfilename[value_len] = '\0';
+            }
+            else {
+                goto bad_arg_and_value;
+            }
+        }
+
+        arg = next_arg;
+    }
+
+    if (rwfilename == NULL) {
+        /* we create a temporary file to store everything */
+        TempFile*    tmp = tempfile_create();
+
+        if (tmp == NULL) {
+            XLOG("could not create temp file for %.*s NAND disk image: %s",
+                  devname_len, devname, strerror(errno));
+            exit(1);
+        }
+        rwfilename = (char*) tempfile_path(tmp);
+        if (VERBOSE_CHECK(init))
+            dprint( "mapping '%.*s' NAND image to %s", devname_len, devname, rwfilename);
+    }
+
+    if(rwfilename) {
+        rwfd = open(rwfilename, O_BINARY | (read_only ? O_RDONLY : O_RDWR));
+        if(rwfd < 0 && read_only) {
+            XLOG("could not open file %s, %s\n", rwfilename, strerror(errno));
+            exit(1);
+        }
+        /* this could be a writable temporary file. use atexit_close_fd to ensure
+         * that it is properly cleaned up at exit on Win32
+         */
+        if (!read_only)
+            atexit_close_fd(rwfd);
+    }
+
+    if(initfilename) {
+        initfd = open(initfilename, O_BINARY | O_RDONLY);
+        if(initfd < 0) {
+            XLOG("could not open file %s, %s\n", initfilename, strerror(errno));
+            exit(1);
+        }
+        if(dev_size == 0) {
+            dev_size = lseek(initfd, 0, SEEK_END);
+            lseek(initfd, 0, SEEK_SET);
+        }
+    }
+
+    new_devs = realloc(nand_devs, sizeof(nand_devs[0]) * (nand_dev_count + 1));
+    if(new_devs == NULL)
+        goto out_of_memory;
+    nand_devs = new_devs;
+    dev = &new_devs[nand_dev_count];
+
+    dev->page_size = page_size;
+    dev->extra_size = extra_size;
+    dev->erase_size = erase_pages * (page_size + extra_size);
+    pad = dev_size % dev->erase_size;
+    if (pad != 0) {
+        dev_size += (dev->erase_size - pad);
+        XLOG("rounding devsize up to a full eraseunit, now %llx\n", dev_size);
+    }
+    dev->devname = devname;
+    dev->devname_len = devname_len;
+    dev->size = dev_size;
+    dev->data = malloc(dev->erase_size);
+    if(dev->data == NULL)
+        goto out_of_memory;
+    dev->flags = read_only ? NAND_DEV_FLAG_READ_ONLY : 0;
+
+    if (initfd >= 0) {
+        do {
+            read_size = do_read(initfd, dev->data, dev->erase_size);
+            if(read_size < 0) {
+                XLOG("could not read file %s, %s\n", initfilename, strerror(errno));
+                exit(1);
+            }
+            if(do_write(rwfd, dev->data, read_size) != read_size) {
+                XLOG("could not write file %s, %s\n", initfilename, strerror(errno));
+                exit(1);
+            }
+        } while(read_size == dev->erase_size);
+        close(initfd);
+    }
+    dev->fd = rwfd;
+
+    nand_dev_count++;
+
+    return;
+
+out_of_memory:
+    XLOG("out of memory\n");
+    exit(1);
+
+bad_arg_and_value:
+    XLOG("bad arg: %.*s=%.*s\n", arg_len, arg, value_len, value);
+    exit(1);
+}
+
diff --git a/hw/goldfish_nand.h b/hw/goldfish_nand.h
new file mode 100644
index 0000000..dcc59d8
--- /dev/null
+++ b/hw/goldfish_nand.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef NAND_DEVICE_H
+#define NAND_DEVICE_H
+
+void nand_dev_init(uint32_t base);
+void nand_add_dev(const char *arg);
+
+typedef struct {
+    uint64_t     limit;
+    uint64_t     counter;
+    int          pid;
+    int          signal;
+} nand_threshold;
+
+extern nand_threshold   android_nand_read_threshold;
+extern nand_threshold   android_nand_write_threshold;
+
+#endif
diff --git a/hw/goldfish_nand_reg.h b/hw/goldfish_nand_reg.h
new file mode 100644
index 0000000..ea91461
--- /dev/null
+++ b/hw/goldfish_nand_reg.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef NAND_DEVICE_REG_H
+#define NAND_DEVICE_REG_H
+
+enum nand_cmd {
+	NAND_CMD_GET_DEV_NAME,  // Write device name for NAND_DEV to NAND_DATA (vaddr)
+	NAND_CMD_READ,
+	NAND_CMD_WRITE,
+	NAND_CMD_ERASE,
+	NAND_CMD_BLOCK_BAD_GET, // NAND_RESULT is 1 if block is bad, 0 if it is not
+	NAND_CMD_BLOCK_BAD_SET
+};
+
+enum nand_dev_flags {
+	NAND_DEV_FLAG_READ_ONLY = 0x00000001
+};
+
+#define NAND_VERSION_CURRENT (1)
+
+enum nand_reg {
+	// Global
+	NAND_VERSION        = 0x000,
+	NAND_NUM_DEV        = 0x004,
+	NAND_DEV            = 0x008,
+
+	// Dev info
+	NAND_DEV_FLAGS      = 0x010,
+	NAND_DEV_NAME_LEN   = 0x014,
+	NAND_DEV_PAGE_SIZE  = 0x018,
+	NAND_DEV_EXTRA_SIZE = 0x01c,
+	NAND_DEV_ERASE_SIZE = 0x020,
+	NAND_DEV_SIZE_LOW   = 0x028,
+	NAND_DEV_SIZE_HIGH  = 0x02c,
+
+	// Command
+	NAND_RESULT         = 0x040,
+	NAND_COMMAND        = 0x044,
+	NAND_DATA           = 0x048,
+	NAND_TRANSFER_SIZE  = 0x04c,
+	NAND_ADDR_LOW       = 0x050,
+	NAND_ADDR_HIGH      = 0x054,
+};
+
+#endif
diff --git a/hw/goldfish_switch.c b/hw/goldfish_switch.c
new file mode 100644
index 0000000..8a12d66
--- /dev/null
+++ b/hw/goldfish_switch.c
@@ -0,0 +1,172 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "goldfish_device.h"
+
+enum {
+    SW_NAME_LEN     = 0x00,
+    SW_NAME_PTR     = 0x04,
+    SW_FLAGS        = 0x08,
+    SW_STATE        = 0x0c,
+    SW_INT_STATUS   = 0x10,
+    SW_INT_ENABLE   = 0x14,
+
+    SW_FLAGS_OUTPUT = 1U << 0
+};
+
+
+struct switch_state {
+    struct goldfish_device dev;
+    char *name;
+    uint32_t state;
+    uint32_t state_changed : 1;
+    uint32_t int_enable : 1;
+    uint32_t (*writefn)(void *opaque, uint32_t state);
+    void *writeopaque;
+};
+
+#define  GOLDFISH_SWITCH_SAVE_VERSION  1
+
+static void  goldfish_switch_save(QEMUFile*  f, void*  opaque)
+{
+    struct switch_state*  s = opaque;
+
+    qemu_put_be32(f, s->state);
+    qemu_put_byte(f, s->state_changed);
+    qemu_put_byte(f, s->int_enable);
+}
+
+static int  goldfish_switch_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct switch_state*  s = opaque;
+
+    if (version_id != GOLDFISH_SWITCH_SAVE_VERSION)
+        return -1;
+
+    s->state         = qemu_get_be32(f);
+    s->state_changed = qemu_get_byte(f);
+    s->int_enable    = qemu_get_byte(f);
+
+    return 0;
+}
+
+static uint32_t goldfish_switch_read(void *opaque, target_phys_addr_t offset)
+{
+    struct switch_state *s = (struct switch_state *)opaque;
+    offset -= s->dev.base;
+
+    //printf("goldfish_switch_read %x %x\n", offset, size);
+
+    switch (offset) {
+        case SW_NAME_LEN:
+            return strlen(s->name);
+        case SW_FLAGS:
+            return s->writefn ? SW_FLAGS_OUTPUT : 0;
+        case SW_STATE:
+            return s->state;
+        case SW_INT_STATUS:
+            if(s->state_changed && s->int_enable) {
+                s->state_changed = 0;
+                goldfish_device_set_irq(&s->dev, 0, 0);
+                return 1;
+            }
+            return 0;
+    default:
+        cpu_abort (cpu_single_env, "goldfish_switch_read: Bad offset %x\n", offset);
+        return 0;
+    }
+}
+
+static void goldfish_switch_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    struct switch_state *s = (struct switch_state *)opaque;
+    offset -= s->dev.base;
+
+    //printf("goldfish_switch_read %x %x %x\n", offset, value, size);
+
+    switch(offset) {
+        case SW_NAME_PTR:
+            pmemcpy(value, s->name, strlen(s->name));
+            break;
+
+        case SW_STATE:
+            if(s->writefn) {
+                uint32_t new_state;
+                new_state = s->writefn(s->writeopaque, value);
+                if(new_state != s->state) {
+                    goldfish_switch_set_state(s, new_state);
+                }
+            }
+            else
+                cpu_abort (cpu_single_env, "goldfish_switch_write: write to SW_STATE on input\n");
+            break;
+
+        case SW_INT_ENABLE:
+            value &= 1;
+            if(s->state_changed && s->int_enable != value)
+                goldfish_device_set_irq(&s->dev, 0, value);
+            s->int_enable = value;
+            break;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_switch_write: Bad offset %x\n", offset);
+    }
+}
+
+static CPUReadMemoryFunc *goldfish_switch_readfn[] = {
+    goldfish_switch_read,
+    goldfish_switch_read,
+    goldfish_switch_read
+};
+
+static CPUWriteMemoryFunc *goldfish_switch_writefn[] = {
+    goldfish_switch_write,
+    goldfish_switch_write,
+    goldfish_switch_write
+};
+
+void goldfish_switch_set_state(void *opaque, uint32_t state)
+{
+    struct switch_state *s = opaque;
+    s->state_changed = 1;
+    s->state = state;
+    if(s->int_enable)
+        goldfish_device_set_irq(&s->dev, 0, 1);
+}
+
+void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id)
+{
+    int ret;
+    struct switch_state *s;
+
+    s = qemu_mallocz(sizeof(*s));
+    s->dev.name = "goldfish-switch";
+    s->dev.id = id;
+    s->dev.size = 0x1000;
+    s->dev.irq_count = 1;
+    s->name = name;
+    s->writefn = writefn;
+    s->writeopaque = writeopaque;
+
+
+    ret = goldfish_device_add(&s->dev, goldfish_switch_readfn, goldfish_switch_writefn, s);
+    if(ret) {
+        qemu_free(s);
+        return NULL;
+    }
+
+    register_savevm( "goldfish_switch", 0, GOLDFISH_SWITCH_SAVE_VERSION,
+                     goldfish_switch_save, goldfish_switch_load, s);
+
+    return s;
+}
+
diff --git a/hw/goldfish_timer.c b/hw/goldfish_timer.c
new file mode 100644
index 0000000..73f1455
--- /dev/null
+++ b/hw/goldfish_timer.c
@@ -0,0 +1,256 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu-timer.h"
+#include "cpu.h"
+#include "arm_pic.h"
+#include "goldfish_device.h"
+
+enum {
+    TIMER_TIME_LOW          = 0x00, // get low bits of current time and update TIMER_TIME_HIGH
+    TIMER_TIME_HIGH         = 0x04, // get high bits of time at last TIMER_TIME_LOW read
+    TIMER_ALARM_LOW         = 0x08, // set low bits of alarm and activate it
+    TIMER_ALARM_HIGH        = 0x0c, // set high bits of next alarm
+    TIMER_CLEAR_INTERRUPT   = 0x10,
+    TIMER_CLEAR_ALARM       = 0x14
+};
+
+struct timer_state {
+    struct goldfish_device dev;
+    uint32_t alarm_low;
+    int32_t alarm_high;
+    int64_t now;
+    int     armed;
+    QEMUTimer *timer;
+};
+
+#define  GOLDFISH_TIMER_SAVE_VERSION  1
+
+static void  goldfish_timer_save(QEMUFile*  f, void*  opaque)
+{
+    struct timer_state*  s   = opaque;
+
+    qemu_put_be64(f, s->now);  /* in case the kernel is in the middle of a timer read */
+    qemu_put_byte(f, s->armed);
+    if (s->armed) {
+        int64_t  now   = qemu_get_clock(vm_clock);
+        int64_t  alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, ticks_per_sec, 1000000000);
+        qemu_put_be64(f, alarm-now);
+    }
+}
+
+static int  goldfish_timer_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct timer_state*  s   = opaque;
+
+    if (version_id != GOLDFISH_TIMER_SAVE_VERSION)
+        return -1;
+
+    s->now   = qemu_get_be64(f);
+    s->armed = qemu_get_byte(f);
+    if (s->armed) {
+        int64_t  now   = qemu_get_clock(vm_clock);
+        int64_t  diff  = qemu_get_be64(f);
+        int64_t  alarm = now + diff;
+
+        if (alarm <= now) {
+            goldfish_device_set_irq(&s->dev, 0, 1);
+            s->armed = 0;
+        } else {
+            qemu_mod_timer(s->timer, alarm);
+        }
+    }
+    return 0;
+}
+
+static uint32_t goldfish_timer_read(void *opaque, target_phys_addr_t offset)
+{
+    struct timer_state *s = (struct timer_state *)opaque;
+    offset -= s->dev.base;
+    switch(offset) {
+        case TIMER_TIME_LOW:
+            s->now = muldiv64(qemu_get_clock(vm_clock), 1000000000, ticks_per_sec);
+            return s->now;
+        case TIMER_TIME_HIGH:
+            return s->now >> 32;
+        default:
+            cpu_abort (cpu_single_env, "goldfish_timer_read: Bad offset %x\n", offset);
+            return 0;
+    }
+}
+
+static void goldfish_timer_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    struct timer_state *s = (struct timer_state *)opaque;
+    int64_t alarm, now;
+    offset -= s->dev.base;
+    switch(offset) {
+        case TIMER_ALARM_LOW:
+            s->alarm_low = value;
+            alarm = muldiv64(s->alarm_low | (int64_t)s->alarm_high << 32, ticks_per_sec, 1000000000);
+            now   = qemu_get_clock(vm_clock);
+            if (alarm <= now) {
+                goldfish_device_set_irq(&s->dev, 0, 1);
+            } else {
+                qemu_mod_timer(s->timer, alarm);
+                s->armed = 1;
+            }
+            break;
+        case TIMER_ALARM_HIGH:
+            s->alarm_high = value;
+            //printf("alarm_high %d\n", s->alarm_high);
+            break;
+        case TIMER_CLEAR_ALARM:
+            qemu_del_timer(s->timer);
+            s->armed = 0;
+            /* fall through */
+        case TIMER_CLEAR_INTERRUPT:
+            goldfish_device_set_irq(&s->dev, 0, 0);
+            break;
+        default:
+            cpu_abort (cpu_single_env, "goldfish_timer_write: Bad offset %x\n", offset);
+    }
+}
+
+static void goldfish_timer_tick(void *opaque)
+{
+    struct timer_state *s = (struct timer_state *)opaque;
+
+    s->armed = 0;
+    goldfish_device_set_irq(&s->dev, 0, 1);
+}
+
+struct rtc_state {
+    struct goldfish_device dev;
+    uint32_t alarm_low;
+    int32_t alarm_high;
+    int64_t now;
+};
+
+/* we save the RTC for the case where the kernel is in the middle of a rtc_read
+ * (i.e. it has read the low 32-bit of s->now, but not the high 32-bits yet */
+#define  GOLDFISH_RTC_SAVE_VERSION  1
+
+static void  goldfish_rtc_save(QEMUFile*  f, void*  opaque)
+{
+    struct rtc_state*  s = opaque;
+
+    qemu_put_be64(f, s->now);
+}
+
+static int  goldfish_rtc_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct  rtc_state*  s = opaque;
+
+    if (version_id != GOLDFISH_RTC_SAVE_VERSION)
+        return -1;
+
+    /* this is an old value that is not correct. but that's ok anyway */
+    s->now = qemu_get_be64(f);
+    return 0;
+}
+
+static uint32_t goldfish_rtc_read(void *opaque, target_phys_addr_t offset)
+{
+    struct rtc_state *s = (struct rtc_state *)opaque;
+    offset -= s->dev.base;
+    switch(offset) {
+        case 0x0:
+            s->now = (int64_t)time(NULL) * 1000000000;
+            return s->now;
+        case 0x4:
+            return s->now >> 32;
+        default:
+            cpu_abort (cpu_single_env, "goldfish_rtc_read: Bad offset %x\n", offset);
+            return 0;
+    }
+}
+
+static void goldfish_rtc_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    struct rtc_state *s = (struct rtc_state *)opaque;
+    int64_t alarm;
+    offset -= s->dev.base;
+    switch(offset) {
+        case 0x8:
+            s->alarm_low = value;
+            alarm = s->alarm_low | (int64_t)s->alarm_high << 32;
+            //printf("next alarm at %lld, tps %lld\n", alarm, ticks_per_sec);
+            //qemu_mod_timer(s->timer, alarm);
+            break;
+        case 0xc:
+            s->alarm_high = value;
+            //printf("alarm_high %d\n", s->alarm_high);
+            break;
+        case 0x10:
+            goldfish_device_set_irq(&s->dev, 0, 0);
+            break;
+        default:
+            cpu_abort (cpu_single_env, "goldfish_rtc_write: Bad offset %x\n", offset);
+    }
+}
+
+static struct timer_state timer_state = {
+    .dev = {
+        .name = "goldfish_timer",
+        .id = -1,
+        .size = 0x1000,
+        .irq_count = 1,
+    }
+};
+
+static struct timer_state rtc_state = {
+    .dev = {
+        .name = "goldfish_rtc",
+        .id = -1,
+        .size = 0x1000,
+        .irq_count = 1,
+    }
+};
+
+static CPUReadMemoryFunc *goldfish_timer_readfn[] = {
+    goldfish_timer_read,
+    goldfish_timer_read,
+    goldfish_timer_read
+};
+
+static CPUWriteMemoryFunc *goldfish_timer_writefn[] = {
+    goldfish_timer_write,
+    goldfish_timer_write,
+    goldfish_timer_write
+};
+
+static CPUReadMemoryFunc *goldfish_rtc_readfn[] = {
+    goldfish_rtc_read,
+    goldfish_rtc_read,
+    goldfish_rtc_read
+};
+
+static CPUWriteMemoryFunc *goldfish_rtc_writefn[] = {
+    goldfish_rtc_write,
+    goldfish_rtc_write,
+    goldfish_rtc_write
+};
+
+void goldfish_timer_and_rtc_init(uint32_t timerbase, int timerirq)
+{
+    timer_state.dev.base = timerbase;
+    timer_state.dev.irq = timerirq;
+    timer_state.timer = qemu_new_timer(vm_clock, goldfish_timer_tick, &timer_state);
+    goldfish_device_add(&timer_state.dev, goldfish_timer_readfn, goldfish_timer_writefn, &timer_state);
+    register_savevm( "goldfish_timer", 0, GOLDFISH_TIMER_SAVE_VERSION,
+                     goldfish_timer_save, goldfish_timer_load, &timer_state);
+
+    goldfish_device_add(&rtc_state.dev, goldfish_rtc_readfn, goldfish_rtc_writefn, &rtc_state);
+    register_savevm( "goldfish_rtc", 0, GOLDFISH_RTC_SAVE_VERSION,
+                     goldfish_rtc_save, goldfish_rtc_load, &rtc_state);
+}
+
diff --git a/hw/goldfish_trace.c b/hw/goldfish_trace.c
new file mode 100644
index 0000000..ad0eba5
--- /dev/null
+++ b/hw/goldfish_trace.c
@@ -0,0 +1,251 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+/*
+ * Virtual hardware for bridging the FUSE kernel module
+ * in the emulated OS and outside file system
+ */
+#include "qemu_file.h"
+#include "goldfish_trace.h"
+
+//#define DEBUG   1
+
+extern void cpu_loop_exit(void);
+
+extern int tracing;
+
+/* for execve */
+static char path[CLIENT_PAGE_SIZE];
+static char arg[CLIENT_PAGE_SIZE];
+static unsigned long vstart;    // VM start
+static unsigned long vend;      // VM end
+static unsigned long eoff;      // offset in EXE file
+static unsigned cmdlen;         // cmdline length
+static unsigned pid;            // PID (really thread id)
+static unsigned tgid;           // thread group id (really process id)
+static unsigned long dsaddr;    // dynamic symbol address
+static unsigned long unmap_start; // start address to unmap
+
+/* for context switch */
+//static unsigned long cs_pid;    // context switch PID
+
+/* I/O write */
+static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    trace_dev_state *s = (trace_dev_state *)opaque;
+
+    offset -= s->base;
+    switch (offset >> 2) {
+    case TRACE_DEV_REG_SWITCH:  // context switch, switch to pid
+        trace_switch(value);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, context switch %u\n", value);
+#endif
+        break;
+    case TRACE_DEV_REG_TGID:    // save the tgid for the following fork/clone
+        tgid = value;
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, tgid %u\n", value);
+#endif
+        break;
+    case TRACE_DEV_REG_FORK:    // fork, fork new pid
+        trace_fork(tgid, value);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, fork %u\n", value);
+#endif
+        break;
+    case TRACE_DEV_REG_CLONE:    // fork, clone new pid (i.e. thread)
+        trace_clone(tgid, value);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, clone %u\n", value);
+#endif
+        break;
+    case TRACE_DEV_REG_EXECVE_VMSTART:  // execve, vstart
+        vstart = value;
+        break;
+    case TRACE_DEV_REG_EXECVE_VMEND:    // execve, vend
+        vend = value;
+        break;
+    case TRACE_DEV_REG_EXECVE_OFFSET:   // execve, offset in EXE
+        eoff = value;
+        break;
+    case TRACE_DEV_REG_EXECVE_EXEPATH:  // init exec, path of EXE
+        vstrcpy(value, path, CLIENT_PAGE_SIZE);
+        trace_init_exec(vstart, vend, eoff, path);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path);
+#endif
+        path[0] = 0;
+        break;
+    case TRACE_DEV_REG_CMDLINE_LEN:     // execve, process cmdline length
+        cmdlen = value;
+        break;
+    case TRACE_DEV_REG_CMDLINE:         // execve, process cmdline
+        vmemcpy(value, arg, cmdlen);
+        trace_execve(arg, cmdlen);
+#ifdef DEBUG
+        {
+            int i;
+            for (i = 0; i < cmdlen; i ++)
+                if (i != cmdlen - 1 && arg[i] == 0)
+                    arg[i] = ' ';
+            printf("QEMU.trace: kernel, execve %s[%d]\n", arg, cmdlen);
+        }
+#endif
+        arg[0] = 0;
+        break;
+    case TRACE_DEV_REG_EXIT:            // exit, exit current process with exit code
+        trace_exit(value);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, exit %x\n", value);
+#endif
+        break;
+    case TRACE_DEV_REG_NAME:            // record thread name
+        vstrcpy(value, path, CLIENT_PAGE_SIZE);
+
+        // Remove the trailing newline if it exists
+        int len = strlen(path);
+        if (path[len - 1] == '\n') {
+            path[len - 1] = 0;
+        }
+        trace_name(path);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, name %s\n", path);
+#endif
+        break;
+    case TRACE_DEV_REG_MMAP_EXEPATH:    // mmap, path of EXE, the others are same as execve
+        vstrcpy(value, path, CLIENT_PAGE_SIZE);
+        trace_mmap(vstart, vend, eoff, path);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path);
+#endif
+        path[0] = 0;
+        break;
+    case TRACE_DEV_REG_INIT_PID:        // init, name the pid that starts before device registered
+        pid = value;
+        break;
+    case TRACE_DEV_REG_INIT_NAME:       // init, the comm of the init pid
+        vstrcpy(value, path, CLIENT_PAGE_SIZE);
+        trace_init_name(tgid, pid, path);
+#ifdef DEBUG
+        printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path);
+#endif
+        path[0] = 0;
+        break;
+
+    case TRACE_DEV_REG_DYN_SYM_ADDR:    // dynamic symbol address
+        dsaddr = value;
+        break;
+    case TRACE_DEV_REG_DYN_SYM:         // add dynamic symbol
+        vstrcpy(value, arg, CLIENT_PAGE_SIZE);
+        trace_dynamic_symbol_add(dsaddr, arg);
+#ifdef DEBUG
+        printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg);
+#endif
+        arg[0] = 0;
+        break;
+    case TRACE_DEV_REG_REMOVE_ADDR:         // remove dynamic symbol addr
+        trace_dynamic_symbol_remove(value);
+#ifdef DEBUG
+        printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
+#endif
+        arg[0] = 0;
+        break;
+
+    case TRACE_DEV_REG_PRINT_STR:       // print string
+        vstrcpy(value, arg, CLIENT_PAGE_SIZE);
+        printf("%s", arg);
+        arg[0] = 0;
+        break;
+    case TRACE_DEV_REG_PRINT_NUM_DEC:   // print number in decimal
+        printf("%d", value);
+        break;
+    case TRACE_DEV_REG_PRINT_NUM_HEX:   // print number in hexical
+        printf("%x", value);
+        break;
+
+    case TRACE_DEV_REG_STOP_EMU:        // stop the VM execution
+        // To ensure that the number of instructions executed in this
+        // block is correct, we pretend that there was an exception.
+        trace_exception(0);
+
+        cpu_single_env->exception_index = EXCP_HLT;
+        cpu_single_env->halted = 1;
+        qemu_system_shutdown_request();
+        cpu_loop_exit();
+        break;
+
+    case TRACE_DEV_REG_ENABLE:          // tracing enable: 0 = stop, 1 = start
+        if (value == 1)
+            start_tracing();
+        else if (value == 0) {
+            stop_tracing();
+
+            // To ensure that the number of instructions executed in this
+            // block is correct, we pretend that there was an exception.
+            trace_exception(0);
+        }
+        break;
+
+    case TRACE_DEV_REG_UNMAP_START:
+        unmap_start = value;
+        break;
+    case TRACE_DEV_REG_UNMAP_END:
+        trace_munmap(unmap_start, value);
+        break;
+
+    default:
+        cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
+        break;
+    }
+}
+
+/* I/O read */
+static uint32_t trace_dev_read(void *opaque, target_phys_addr_t offset)
+{
+    trace_dev_state *s = (trace_dev_state *)opaque;
+
+    offset -= s->base;
+    switch (offset >> 2) {
+    case TRACE_DEV_REG_ENABLE:          // tracing enable
+        return tracing;
+    default:
+        cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
+        return 0;
+    }
+    return 0;
+}
+
+static CPUReadMemoryFunc *trace_dev_readfn[] = {
+   trace_dev_read,
+   trace_dev_read,
+   trace_dev_read
+};
+
+static CPUWriteMemoryFunc *trace_dev_writefn[] = {
+   trace_dev_write,
+   trace_dev_write,
+   trace_dev_write
+};
+
+/* initialize the trace device */
+void trace_dev_init(uint32_t base)
+{
+    int iomemtype;
+    trace_dev_state *s;
+
+    s = (trace_dev_state *)qemu_mallocz(sizeof(trace_dev_state));
+    iomemtype = cpu_register_io_memory(0, trace_dev_readfn, trace_dev_writefn, s);
+    cpu_register_physical_memory(base, 0x00000fff, iomemtype);
+    s->base = base;
+
+    path[0] = arg[0] = '\0';
+}
diff --git a/hw/goldfish_trace.h b/hw/goldfish_trace.h
new file mode 100644
index 0000000..44190ee
--- /dev/null
+++ b/hw/goldfish_trace.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _TRACE_DEV_H_
+#define _TRACE_DEV_H_
+
+#define CLIENT_PAGE_SIZE        4096
+
+/* trace device registers */
+#define TRACE_DEV_REG_SWITCH            0
+#define TRACE_DEV_REG_FORK              1
+#define TRACE_DEV_REG_EXECVE_PID        2
+#define TRACE_DEV_REG_EXECVE_VMSTART    3
+#define TRACE_DEV_REG_EXECVE_VMEND      4
+#define TRACE_DEV_REG_EXECVE_OFFSET     5
+#define TRACE_DEV_REG_EXECVE_EXEPATH    6
+#define TRACE_DEV_REG_EXIT              7
+#define TRACE_DEV_REG_CMDLINE           8
+#define TRACE_DEV_REG_CMDLINE_LEN       9
+#define TRACE_DEV_REG_MMAP_EXEPATH      10
+#define TRACE_DEV_REG_INIT_PID          11
+#define TRACE_DEV_REG_INIT_NAME         12
+#define TRACE_DEV_REG_CLONE             13
+#define TRACE_DEV_REG_UNMAP_START       14
+#define TRACE_DEV_REG_UNMAP_END         15
+#define TRACE_DEV_REG_NAME              16
+#define TRACE_DEV_REG_TGID              17
+#define TRACE_DEV_REG_DYN_SYM           50
+#define TRACE_DEV_REG_DYN_SYM_ADDR      51
+#define TRACE_DEV_REG_REMOVE_ADDR       52
+#define TRACE_DEV_REG_PRINT_STR         60
+#define TRACE_DEV_REG_PRINT_NUM_DEC     61
+#define TRACE_DEV_REG_PRINT_NUM_HEX     62
+#define TRACE_DEV_REG_STOP_EMU          90
+#define TRACE_DEV_REG_ENABLE            100
+
+/* the virtual trace device state */
+typedef struct {
+    uint32_t base;
+} trace_dev_state;
+
+/*
+ * interfaces for copy from virtual space
+ * from target-arm/op_helper.c
+ */
+extern target_phys_addr_t v2p(target_ulong ptr, int is_user);
+extern void vmemcpy(target_ulong ptr, char *buf, int size);
+extern void pmemcpy(target_ulong ptr, const char* buf, int size);
+extern void vstrcpy(target_ulong ptr, char *buf, int max);
+
+/*
+ * interfaces to trace module to signal kernel events
+ */
+extern void trace_switch(int pid);
+extern void trace_fork(int tgid, int pid);
+extern void trace_clone(int tgid, int pid);
+extern void trace_execve(const char *arg, int len);
+extern void trace_exit(int exitcode);
+extern void trace_mmap(unsigned long vstart, unsigned long vend,
+                       unsigned long offset, const char *path);
+extern void trace_munmap(unsigned long vstart, unsigned long vend);
+extern void trace_dynamic_symbol_add(unsigned long vaddr, const char *name);
+extern void trace_dynamic_symbol_remove(unsigned long vaddr);
+extern void trace_init_name(int tgid, int pid, const char *name);
+extern void trace_init_exec(unsigned long start, unsigned long end,
+                            unsigned long offset, const char *exe);
+extern void start_tracing(void);
+extern void stop_tracing(void);
+extern void trace_exception(uint32 target_pc);
+
+#endif
diff --git a/hw/goldfish_tty.c b/hw/goldfish_tty.c
new file mode 100644
index 0000000..aa62d75
--- /dev/null
+++ b/hw/goldfish_tty.c
@@ -0,0 +1,226 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "qemu_file.h"
+#include "qemu-char.h"
+#include "goldfish_device.h"
+
+enum {
+    TTY_PUT_CHAR       = 0x00,
+    TTY_BYTES_READY    = 0x04,
+    TTY_CMD            = 0x08,
+
+    TTY_DATA_PTR       = 0x10,
+    TTY_DATA_LEN       = 0x14,
+
+    TTY_CMD_INT_DISABLE    = 0,
+    TTY_CMD_INT_ENABLE     = 1,
+    TTY_CMD_WRITE_BUFFER   = 2,
+    TTY_CMD_READ_BUFFER    = 3,
+};
+
+struct tty_state {
+    struct goldfish_device dev;
+    CharDriverState *cs;
+    uint32_t ptr;
+    uint32_t ptr_len;
+    uint32_t ready;
+    uint8_t data[128];
+    uint32_t data_count;
+};
+
+#define  GOLDFISH_TTY_SAVE_VERSION  1
+
+static void  goldfish_tty_save(QEMUFile*  f, void*  opaque)
+{
+    struct tty_state*  s = opaque;
+
+    qemu_put_be32( f, s->ptr );
+    qemu_put_be32( f, s->ptr_len );
+    qemu_put_byte( f, s->ready );
+    qemu_put_byte( f, s->data_count );
+    qemu_put_buffer( f, s->data, s->data_count );
+}
+
+static int  goldfish_tty_load(QEMUFile*  f, void*  opaque, int  version_id)
+{
+    struct tty_state*  s = opaque;
+
+    if (version_id != GOLDFISH_TTY_SAVE_VERSION)
+        return -1;
+
+    s->ptr        = qemu_get_be32(f);
+    s->ptr_len    = qemu_get_be32(f);
+    s->ready      = qemu_get_byte(f);
+    s->data_count = qemu_get_byte(f);
+    qemu_get_buffer(f, s->data, s->data_count);
+
+    return 0;
+}
+
+static uint32_t goldfish_tty_read(void *opaque, target_phys_addr_t offset)
+{
+    struct tty_state *s = (struct tty_state *)opaque;
+    offset -= s->dev.base;
+
+    //printf("goldfish_tty_read %x %x\n", offset, size);
+
+    switch (offset) {
+        case TTY_BYTES_READY:
+            return s->data_count;
+    default:
+        cpu_abort (cpu_single_env, "goldfish_tty_read: Bad offset %x\n", offset);
+        return 0;
+    }
+}
+
+static void goldfish_tty_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    struct tty_state *s = (struct tty_state *)opaque;
+    offset -= s->dev.base;
+
+    //printf("goldfish_tty_read %x %x %x\n", offset, value, size);
+
+    switch(offset) {
+        case TTY_PUT_CHAR: {
+            uint8_t ch = value;
+            if(s->cs)
+                qemu_chr_write(s->cs, &ch, 1);
+        } break;
+
+        case TTY_CMD:
+            switch(value) {
+                case TTY_CMD_INT_DISABLE:
+                    if(s->ready) {
+                        if(s->data_count > 0)
+                            goldfish_device_set_irq(&s->dev, 0, 0);
+                        s->ready = 0;
+                    }
+                    break;
+
+                case TTY_CMD_INT_ENABLE:
+                    if(!s->ready) {
+                        if(s->data_count > 0)
+                            goldfish_device_set_irq(&s->dev, 0, 1);
+                        s->ready = 1;
+                    }
+                    break;
+
+                case TTY_CMD_WRITE_BUFFER:
+                    if(s->cs) {
+                        int len;
+                        target_ulong buf;
+
+                        buf = s->ptr;
+                        len = s->ptr_len;
+
+                        while(len) {
+                            int page_remain = TARGET_PAGE_SIZE - (buf & ~TARGET_PAGE_MASK);
+                            int to_write = len;
+                            uint8_t *phys = (uint8_t *)v2p(buf, 0);
+                            if(to_write > page_remain)
+                                to_write = page_remain;
+                            qemu_chr_write(s->cs, phys, to_write);
+                            buf += to_write;
+                            len -= to_write;
+                        }
+                        //printf("goldfish_tty_write: got %d bytes from %x\n", s->ptr_len, s->ptr);
+                    }
+                    break;
+
+                case TTY_CMD_READ_BUFFER:
+                    if(s->ptr_len > s->data_count)
+                        cpu_abort (cpu_single_env, "goldfish_tty_write: reading more data than available %d %d\n", s->ptr_len, s->data_count);
+                    pmemcpy(s->ptr, s->data, s->ptr_len);
+                    //printf("goldfish_tty_write: read %d bytes to %x\n", s->ptr_len, s->ptr);
+                    if(s->data_count > s->ptr_len)
+                        memmove(s->data, s->data + s->ptr_len, s->data_count - s->ptr_len);
+                    s->data_count -= s->ptr_len;
+                    if(s->data_count == 0 && s->ready)
+                        goldfish_device_set_irq(&s->dev, 0, 0);
+                    break;
+
+                default:
+                    cpu_abort (cpu_single_env, "goldfish_tty_write: Bad command %x\n", value);
+            };
+            break;
+
+        case TTY_DATA_PTR:
+            s->ptr = value;
+            break;
+
+        case TTY_DATA_LEN:
+            s->ptr_len = value;
+            break;
+
+        default:
+            cpu_abort (cpu_single_env, "goldfish_tty_write: Bad offset %x\n", offset);
+    }
+}
+
+static int tty_can_receive(void *opaque)
+{
+    struct tty_state *s = opaque;
+
+    return (sizeof(s->data) - s->data_count);
+}
+
+static void tty_receive(void *opaque, const uint8_t *buf, int size)
+{
+    struct tty_state *s = opaque;
+
+    memcpy(s->data + s->data_count, buf, size);
+    s->data_count += size;
+    if(s->data_count > 0 && s->ready)
+        goldfish_device_set_irq(&s->dev, 0, 1);
+}
+
+static CPUReadMemoryFunc *goldfish_tty_readfn[] = {
+    goldfish_tty_read,
+    goldfish_tty_read,
+    goldfish_tty_read
+};
+
+static CPUWriteMemoryFunc *goldfish_tty_writefn[] = {
+    goldfish_tty_write,
+    goldfish_tty_write,
+    goldfish_tty_write
+};
+
+int goldfish_tty_add(CharDriverState *cs, int id, uint32_t base, int irq)
+{
+    int ret;
+    struct tty_state *s;
+    static int  instance_id = 0;
+
+    s = qemu_mallocz(sizeof(*s));
+    s->dev.name = "goldfish_tty";
+    s->dev.id = id;
+    s->dev.base = base;
+    s->dev.size = 0x1000;
+    s->dev.irq = irq;
+    s->dev.irq_count = 1;
+    s->cs = cs;
+
+    if(cs) {
+        qemu_chr_add_handlers(cs, tty_can_receive, tty_receive, NULL, s);
+    }
+
+    ret = goldfish_device_add(&s->dev, goldfish_tty_readfn, goldfish_tty_writefn, s);
+    if(ret) {
+        qemu_free(s);
+    } else {
+        register_savevm( "goldfish_tty", instance_id++, GOLDFISH_TTY_SAVE_VERSION,
+                         goldfish_tty_save, goldfish_tty_load, s);
+    }
+    return ret;
+}
+
diff --git a/hw/hw.h b/hw/hw.h
new file mode 100644
index 0000000..06e24cb
--- /dev/null
+++ b/hw/hw.h
@@ -0,0 +1,110 @@
+/* Declarations for use by hardware emulation.  */
+#ifndef QEMU_HW_H
+#define QEMU_HW_H
+
+#include "qemu-common.h"
+#include "irq.h"
+#include "cpu.h"
+
+/* VM Load/Save */
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode);
+void qemu_fflush(QEMUFile *f);
+void qemu_fclose(QEMUFile *f);
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size);
+void qemu_put_byte(QEMUFile *f, int v);
+void qemu_put_be16(QEMUFile *f, unsigned int v);
+void qemu_put_be32(QEMUFile *f, unsigned int v);
+void qemu_put_be64(QEMUFile *f, uint64_t v);
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size);
+int qemu_get_byte(QEMUFile *f);
+unsigned int qemu_get_be16(QEMUFile *f);
+unsigned int qemu_get_be32(QEMUFile *f);
+uint64_t qemu_get_be64(QEMUFile *f);
+
+static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv)
+{
+    qemu_put_be64(f, *pv);
+}
+
+static inline void qemu_put_be32s(QEMUFile *f, const uint32_t *pv)
+{
+    qemu_put_be32(f, *pv);
+}
+
+static inline void qemu_put_be16s(QEMUFile *f, const uint16_t *pv)
+{
+    qemu_put_be16(f, *pv);
+}
+
+static inline void qemu_put_8s(QEMUFile *f, const uint8_t *pv)
+{
+    qemu_put_byte(f, *pv);
+}
+
+static inline void qemu_get_be64s(QEMUFile *f, uint64_t *pv)
+{
+    *pv = qemu_get_be64(f);
+}
+
+static inline void qemu_get_be32s(QEMUFile *f, uint32_t *pv)
+{
+    *pv = qemu_get_be32(f);
+}
+
+static inline void qemu_get_be16s(QEMUFile *f, uint16_t *pv)
+{
+    *pv = qemu_get_be16(f);
+}
+
+static inline void qemu_get_8s(QEMUFile *f, uint8_t *pv)
+{
+    *pv = qemu_get_byte(f);
+}
+
+#ifdef NEED_CPU_H
+#if TARGET_LONG_BITS == 64
+#define qemu_put_betl qemu_put_be64
+#define qemu_get_betl qemu_get_be64
+#define qemu_put_betls qemu_put_be64s
+#define qemu_get_betls qemu_get_be64s
+#else
+#define qemu_put_betl qemu_put_be32
+#define qemu_get_betl qemu_get_be32
+#define qemu_put_betls qemu_put_be32s
+#define qemu_get_betls qemu_get_be32s
+#endif
+#endif
+
+int64_t qemu_ftell(QEMUFile *f);
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence);
+
+typedef void SaveStateHandler(QEMUFile *f, void *opaque);
+typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
+
+int register_savevm(const char *idstr,
+                    int instance_id,
+                    int version_id,
+                    SaveStateHandler *save_state,
+                    LoadStateHandler *load_state,
+                    void *opaque);
+
+typedef void QEMUResetHandler(void *opaque);
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque);
+
+/* handler to set the boot_device for a specific type of QEMUMachine */
+/* return 0 if success */
+typedef int QEMUBootSetHandler(const char *boot_device);
+extern QEMUBootSetHandler *qemu_boot_set_handler;
+void qemu_register_boot_set(QEMUBootSetHandler *func);
+
+/* These should really be in isa.h, but are here to make pc.h happy.  */
+typedef void (IOPortWriteFunc)(void *opaque, uint32_t address, uint32_t data);
+typedef uint32_t (IOPortReadFunc)(void *opaque, uint32_t address);
+
+
+/* ANDROID: copy memory from the QEMU buffer to simulated virtual space */
+extern void pmemcpy(target_ulong ptr, const char *buf, int size);
+
+#endif
diff --git a/hw/irq.c b/hw/irq.c
new file mode 100644
index 0000000..eca707d
--- /dev/null
+++ b/hw/irq.c
@@ -0,0 +1,71 @@
+/*
+ * QEMU IRQ/GPIO common code.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "irq.h"
+
+struct IRQState {
+    qemu_irq_handler handler;
+    void *opaque;
+    int n;
+};
+
+void qemu_set_irq(qemu_irq irq, int level)
+{
+    if (!irq)
+        return;
+
+    irq->handler(irq->opaque, irq->n, level);
+}
+
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n)
+{
+    qemu_irq *s;
+    struct IRQState *p;
+    int i;
+
+    s = (qemu_irq *)qemu_mallocz(sizeof(qemu_irq) * n);
+    p = (struct IRQState *)qemu_mallocz(sizeof(struct IRQState) * n);
+    for (i = 0; i < n; i++) {
+        p->handler = handler;
+        p->opaque = opaque;
+        p->n = i;
+        s[i] = p;
+        p++;
+    }
+    return s;
+}
+
+static void qemu_notirq(void *opaque, int line, int level)
+{
+    struct IRQState *irq = opaque;
+
+    irq->handler(irq->opaque, irq->n, !level);
+}
+
+qemu_irq qemu_irq_invert(qemu_irq irq)
+{
+    /* The default state for IRQs is low, so raise the output now.  */
+    qemu_irq_raise(irq);
+    return qemu_allocate_irqs(qemu_notirq, irq, 1)[0];
+}
diff --git a/hw/irq.h b/hw/irq.h
new file mode 100644
index 0000000..0880ad2
--- /dev/null
+++ b/hw/irq.h
@@ -0,0 +1,34 @@
+#ifndef QEMU_IRQ_H
+#define QEMU_IRQ_H
+
+/* Generic IRQ/GPIO pin infrastructure.  */
+
+/* FIXME: Rmove one of these.  */
+typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
+typedef void SetIRQFunc(void *opaque, int irq_num, int level);
+
+void qemu_set_irq(qemu_irq irq, int level);
+
+static inline void qemu_irq_raise(qemu_irq irq)
+{
+    qemu_set_irq(irq, 1);
+}
+
+static inline void qemu_irq_lower(qemu_irq irq)
+{
+    qemu_set_irq(irq, 0);
+}
+
+static inline void qemu_irq_pulse(qemu_irq irq)
+{
+    qemu_set_irq(irq, 1);
+    qemu_set_irq(irq, 0);
+}
+
+/* Returns an array of N IRQs.  */
+qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n);
+
+/* Returns a new IRQ with opposite polarity.  */
+qemu_irq qemu_irq_invert(qemu_irq irq);
+
+#endif
diff --git a/hw/isa.h b/hw/isa.h
new file mode 100644
index 0000000..222e4f3
--- /dev/null
+++ b/hw/isa.h
@@ -0,0 +1,27 @@
+#ifndef HW_ISA_H
+#define HW_ISA_H
+/* ISA bus */
+
+extern target_phys_addr_t isa_mem_base;
+
+int register_ioport_read(int start, int length, int size,
+                         IOPortReadFunc *func, void *opaque);
+int register_ioport_write(int start, int length, int size,
+                          IOPortWriteFunc *func, void *opaque);
+void isa_unassign_ioport(int start, int length);
+
+void isa_mmio_init(target_phys_addr_t base, target_phys_addr_t size);
+
+/* dma.c */
+int DMA_get_channel_mode (int nchan);
+int DMA_read_memory (int nchan, void *buf, int pos, int size);
+int DMA_write_memory (int nchan, void *buf, int pos, int size);
+void DMA_hold_DREQ (int nchan);
+void DMA_release_DREQ (int nchan);
+void DMA_schedule(int nchan);
+void DMA_run (void);
+void DMA_init (int high_page_enable);
+void DMA_register_channel (int nchan,
+                           DMA_transfer_handler transfer_handler,
+                           void *opaque);
+#endif
diff --git a/hw/mmc.h b/hw/mmc.h
new file mode 100644
index 0000000..3ae3ea9
--- /dev/null
+++ b/hw/mmc.h
@@ -0,0 +1,214 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 12:37:30 $
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_H
+#define MMC_MMC_H
+
+/* Standard MMC commands (4.1)           type  argument     response */
+   /* class 1 */
+#define	MMC_GO_IDLE_STATE         0   /* bc                          */
+#define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
+#define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
+#define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
+#define MMC_SET_DSR               4   /* bc   [31:16] RCA            */
+#define MMC_SWITCH                6   /* ac   [31:0] See below   R1b */
+#define MMC_SELECT_CARD           7   /* ac   [31:16] RCA        R1  */
+#define MMC_SEND_EXT_CSD          8   /* adtc                    R1  */
+#define MMC_SEND_CSD              9   /* ac   [31:16] RCA        R2  */
+#define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
+#define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
+#define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
+#define MMC_SEND_STATUS	         13   /* ac   [31:16] RCA        R1  */
+#define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+
+  /* class 2 */
+#define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
+#define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
+#define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
+
+  /* class 3 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
+
+  /* class 4 */
+#define MMC_SET_BLOCK_COUNT      23   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_BLOCK          24   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_MULTIPLE_BLOCK 25   /* adtc                    R1  */
+#define MMC_PROGRAM_CID          26   /* adtc                    R1  */
+#define MMC_PROGRAM_CSD          27   /* adtc                    R1  */
+
+  /* class 6 */
+#define MMC_SET_WRITE_PROT       28   /* ac   [31:0] data addr   R1b */
+#define MMC_CLR_WRITE_PROT       29   /* ac   [31:0] data addr   R1b */
+#define MMC_SEND_WRITE_PROT      30   /* adtc [31:0] wpdata addr R1  */
+
+  /* class 5 */
+#define MMC_ERASE_GROUP_START    35   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE_GROUP_END      36   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE                38   /* ac                      R1b */
+
+  /* class 9 */
+#define MMC_FAST_IO              39   /* ac   <Complex>          R4  */
+#define MMC_GO_IRQ_STATE         40   /* bcr                     R5  */
+
+  /* class 7 */
+#define MMC_LOCK_UNLOCK          42   /* adtc                    R1b */
+
+  /* class 8 */
+#define MMC_APP_CMD              55   /* ac   [31:16] RCA        R1  */
+#define MMC_GEN_CMD              56   /* adtc [0] RD/WR          R1  */
+
+/*
+ * MMC_SWITCH argument format:
+ *
+ *	[31:26] Always 0
+ *	[25:24] Access Mode
+ *	[23:16] Location of target Byte in EXT_CSD
+ *	[15:08] Value Byte
+ *	[07:03] Always 0
+ *	[02:00] Command Set
+ */
+
+/*
+  MMC status in R1
+  Type
+  	e : error bit
+	s : status bit
+	r : detected and set for the actual command response
+	x : detected and set during command execution. the host must poll
+            the card by sending status command in order to read these bits.
+  Clear condition
+  	a : according to the card state
+	b : always related to the previous command. Reception of
+            a valid command will clear it (with a delay of one command)
+	c : clear by read
+ */
+
+#define R1_OUT_OF_RANGE		(1 << 31)	/* er, c */
+#define R1_ADDRESS_ERROR	(1 << 30)	/* erx, c */
+#define R1_BLOCK_LEN_ERROR	(1 << 29)	/* er, c */
+#define R1_ERASE_SEQ_ERROR      (1 << 28)	/* er, c */
+#define R1_ERASE_PARAM		(1 << 27)	/* ex, c */
+#define R1_WP_VIOLATION		(1 << 26)	/* erx, c */
+#define R1_CARD_IS_LOCKED	(1 << 25)	/* sx, a */
+#define R1_LOCK_UNLOCK_FAILED	(1 << 24)	/* erx, c */
+#define R1_COM_CRC_ERROR	(1 << 23)	/* er, b */
+#define R1_ILLEGAL_COMMAND	(1 << 22)	/* er, b */
+#define R1_CARD_ECC_FAILED	(1 << 21)	/* ex, c */
+#define R1_CC_ERROR		(1 << 20)	/* erx, c */
+#define R1_ERROR		(1 << 19)	/* erx, c */
+#define R1_UNDERRUN		(1 << 18)	/* ex, c */
+#define R1_OVERRUN		(1 << 17)	/* ex, c */
+#define R1_CID_CSD_OVERWRITE	(1 << 16)	/* erx, c, CID/CSD overwrite */
+#define R1_WP_ERASE_SKIP	(1 << 15)	/* sx, c */
+#define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
+#define R1_ERASE_RESET		(1 << 13)	/* sr, c */
+#define R1_STATUS(x)            (x & 0xFFFFE000)
+#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
+#define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
+#define R1_APP_CMD		(1 << 5)	/* sr, c */
+
+
+/*
+ * OCR bits are mostly in host.h
+ */
+#define MMC_CARD_BUSY	0x80000000	/* Card Power up status bit */
+
+/*
+ * Card Command Classes (CCC)
+ */
+#define CCC_BASIC		(1<<0)	/* (0) Basic protocol functions */
+					/* (CMD0,1,2,3,4,7,9,10,12,13,15) */
+#define CCC_STREAM_READ		(1<<1)	/* (1) Stream read commands */
+					/* (CMD11) */
+#define CCC_BLOCK_READ		(1<<2)	/* (2) Block read commands */
+					/* (CMD16,17,18) */
+#define CCC_STREAM_WRITE	(1<<3)	/* (3) Stream write commands */
+					/* (CMD20) */
+#define CCC_BLOCK_WRITE		(1<<4)	/* (4) Block write commands */
+					/* (CMD16,24,25,26,27) */
+#define CCC_ERASE		(1<<5)	/* (5) Ability to erase blocks */
+					/* (CMD32,33,34,35,36,37,38,39) */
+#define CCC_WRITE_PROT		(1<<6)	/* (6) Able to write protect blocks */
+					/* (CMD28,29,30) */
+#define CCC_LOCK_CARD		(1<<7)	/* (7) Able to lock down card */
+					/* (CMD16,CMD42) */
+#define CCC_APP_SPEC		(1<<8)	/* (8) Application specific */
+					/* (CMD55,56,57,ACMD*) */
+#define CCC_IO_MODE		(1<<9)	/* (9) I/O mode */
+					/* (CMD5,39,40,52,53) */
+#define CCC_SWITCH		(1<<10)	/* (10) High speed switch */
+					/* (CMD6,34,35,36,37,50) */
+					/* (11) Reserved */
+					/* (CMD?) */
+
+/*
+ * CSD field definitions
+ */
+
+#define CSD_STRUCT_VER_1_0  0           /* Valid for system specification 1.0 - 1.2 */
+#define CSD_STRUCT_VER_1_1  1           /* Valid for system specification 1.4 - 2.2 */
+#define CSD_STRUCT_VER_1_2  2           /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */
+#define CSD_STRUCT_EXT_CSD  3           /* Version is coded in CSD_STRUCTURE in EXT_CSD */
+
+#define CSD_SPEC_VER_0      0           /* Implements system specification 1.0 - 1.2 */
+#define CSD_SPEC_VER_1      1           /* Implements system specification 1.4 */
+#define CSD_SPEC_VER_2      2           /* Implements system specification 2.0 - 2.2 */
+#define CSD_SPEC_VER_3      3           /* Implements system specification 3.1 - 3.2 - 3.31 */
+#define CSD_SPEC_VER_4      4           /* Implements system specification 4.0 - 4.1 */
+
+/*
+ * EXT_CSD fields
+ */
+
+#define EXT_CSD_BUS_WIDTH	183	/* R/W */
+#define EXT_CSD_HS_TIMING	185	/* R/W */
+#define EXT_CSD_CARD_TYPE	196	/* RO */
+#define EXT_CSD_SEC_CNT		212	/* RO, 4 bytes */
+
+/*
+ * EXT_CSD field definitions
+ */
+
+#define EXT_CSD_CMD_SET_NORMAL		(1<<0)
+#define EXT_CSD_CMD_SET_SECURE		(1<<1)
+#define EXT_CSD_CMD_SET_CPSECURE	(1<<2)
+
+#define EXT_CSD_CARD_TYPE_26	(1<<0)	/* Card can run at 26MHz */
+#define EXT_CSD_CARD_TYPE_52	(1<<1)	/* Card can run at 52MHz */
+
+#define EXT_CSD_BUS_WIDTH_1	0	/* Card is in 1 bit mode */
+#define EXT_CSD_BUS_WIDTH_4	1	/* Card is in 4 bit mode */
+#define EXT_CSD_BUS_WIDTH_8	2	/* Card is in 8 bit mode */
+
+/*
+ * MMC_SWITCH access modes
+ */
+
+#define MMC_SWITCH_MODE_CMD_SET		0x00	/* Change the command set */
+#define MMC_SWITCH_MODE_SET_BITS	0x01	/* Set bits which are 1 in value */
+#define MMC_SWITCH_MODE_CLEAR_BITS	0x02	/* Clear bits which are 1 in value */
+#define MMC_SWITCH_MODE_WRITE_BYTE	0x03	/* Set target to value */
+
+#endif  /* MMC_MMC_PROTOCOL_H */
+
diff --git a/hw/pc.h b/hw/pc.h
new file mode 100644
index 0000000..2862849
--- /dev/null
+++ b/hw/pc.h
@@ -0,0 +1,148 @@
+#ifndef HW_PC_H
+#define HW_PC_H
+/* PC-style peripherals (also used by other machines).  */
+
+/* serial.c */
+
+SerialState *serial_init(int base, qemu_irq irq, int baudbase,
+                         CharDriverState *chr);
+SerialState *serial_mm_init (target_phys_addr_t base, int it_shift,
+                             qemu_irq irq, int baudbase,
+                             CharDriverState *chr, int ioregister);
+uint32_t serial_mm_readb (void *opaque, target_phys_addr_t addr);
+void serial_mm_writeb (void *opaque, target_phys_addr_t addr, uint32_t value);
+uint32_t serial_mm_readw (void *opaque, target_phys_addr_t addr);
+void serial_mm_writew (void *opaque, target_phys_addr_t addr, uint32_t value);
+uint32_t serial_mm_readl (void *opaque, target_phys_addr_t addr);
+void serial_mm_writel (void *opaque, target_phys_addr_t addr, uint32_t value);
+
+/* parallel.c */
+
+typedef struct ParallelState ParallelState;
+ParallelState *parallel_init(int base, qemu_irq irq, CharDriverState *chr);
+ParallelState *parallel_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq, CharDriverState *chr);
+
+/* i8259.c */
+
+typedef struct PicState2 PicState2;
+extern PicState2 *isa_pic;
+void pic_set_irq(int irq, int level);
+void pic_set_irq_new(void *opaque, int irq, int level);
+qemu_irq *i8259_init(qemu_irq parent_irq);
+void pic_set_alt_irq_func(PicState2 *s, SetIRQFunc *alt_irq_func,
+                          void *alt_irq_opaque);
+int pic_read_irq(PicState2 *s);
+void pic_update_irq(PicState2 *s);
+uint32_t pic_intack_read(PicState2 *s);
+void pic_info(void);
+void irq_info(void);
+
+/* APIC */
+typedef struct IOAPICState IOAPICState;
+
+int apic_init(CPUState *env);
+int apic_accept_pic_intr(CPUState *env);
+void apic_deliver_pic_intr(CPUState *env, int level);
+int apic_get_interrupt(CPUState *env);
+IOAPICState *ioapic_init(void);
+void ioapic_set_irq(void *opaque, int vector, int level);
+
+/* i8254.c */
+
+#define PIT_FREQ 1193182
+
+typedef struct PITState PITState;
+
+PITState *pit_init(int base, qemu_irq irq);
+void pit_set_gate(PITState *pit, int channel, int val);
+int pit_get_gate(PITState *pit, int channel);
+int pit_get_initial_count(PITState *pit, int channel);
+int pit_get_mode(PITState *pit, int channel);
+int pit_get_out(PITState *pit, int channel, int64_t current_time);
+
+/* vmport.c */
+void vmport_init(void);
+void vmport_register(unsigned char command, IOPortReadFunc *func, void *opaque);
+
+/* vmmouse.c */
+void *vmmouse_init(void *m);
+
+/* pckbd.c */
+
+void i8042_init(qemu_irq kbd_irq, qemu_irq mouse_irq, uint32_t io_base);
+void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+                   target_phys_addr_t base, int it_shift);
+
+/* mc146818rtc.c */
+
+typedef struct RTCState RTCState;
+
+RTCState *rtc_init(int base, qemu_irq irq);
+RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq);
+void rtc_set_memory(RTCState *s, int addr, int val);
+void rtc_set_date(RTCState *s, const struct tm *tm);
+
+/* pc.c */
+extern int fd_bootchk;
+
+void ioport_set_a20(int enable);
+int ioport_get_a20(void);
+
+/* acpi.c */
+extern int acpi_enabled;
+i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base,
+                       qemu_irq sci_irq);
+void piix4_smbus_register_device(SMBusDevice *dev, uint8_t addr);
+void acpi_bios_init(void);
+
+/* pcspk.c */
+void pcspk_init(PITState *);
+int pcspk_audio_init(AudioState *, qemu_irq *pic);
+
+/* piix_pci.c */
+PCIBus *i440fx_init(PCIDevice **pi440fx_state, qemu_irq *pic);
+void i440fx_set_smm(PCIDevice *d, int val);
+int piix3_init(PCIBus *bus, int devfn);
+void i440fx_init_memory_mappings(PCIDevice *d);
+
+int piix4_init(PCIBus *bus, int devfn);
+
+/* vga.c */
+
+#ifndef TARGET_SPARC
+#define VGA_RAM_SIZE (8192 * 1024)
+#else
+#define VGA_RAM_SIZE (9 * 1024 * 1024)
+#endif
+
+int isa_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
+                 unsigned long vga_ram_offset, int vga_ram_size);
+int pci_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+                 unsigned long vga_ram_offset, int vga_ram_size,
+                 unsigned long vga_bios_offset, int vga_bios_size);
+int isa_vga_mm_init(DisplayState *ds, uint8_t *vga_ram_base,
+                    unsigned long vga_ram_offset, int vga_ram_size,
+                    target_phys_addr_t vram_base, target_phys_addr_t ctrl_base,
+                    int it_shift);
+
+/* cirrus_vga.c */
+void pci_cirrus_vga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+                         unsigned long vga_ram_offset, int vga_ram_size);
+void isa_cirrus_vga_init(DisplayState *ds, uint8_t *vga_ram_base,
+                         unsigned long vga_ram_offset, int vga_ram_size);
+
+/* ide.c */
+void isa_ide_init(int iobase, int iobase2, qemu_irq irq,
+                  BlockDriverState *hd0, BlockDriverState *hd1);
+void pci_cmd646_ide_init(PCIBus *bus, BlockDriverState **hd_table,
+                         int secondary_ide_enabled);
+void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
+                        qemu_irq *pic);
+void pci_piix4_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn,
+                        qemu_irq *pic);
+
+/* ne2000.c */
+
+void isa_ne2000_init(int base, qemu_irq irq, NICInfo *nd);
+
+#endif
diff --git a/hw/pci.c b/hw/pci.c
new file mode 100644
index 0000000..5f7004a
--- /dev/null
+++ b/hw/pci.c
@@ -0,0 +1,701 @@
+/*
+ * QEMU PCI bus manager
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "pci.h"
+#include "console.h"
+#include "net.h"
+
+//#define DEBUG_PCI
+
+struct PCIBus {
+    int bus_num;
+    int devfn_min;
+    pci_set_irq_fn set_irq;
+    pci_map_irq_fn map_irq;
+    uint32_t config_reg; /* XXX: suppress */
+    /* low level pic */
+    SetIRQFunc *low_set_irq;
+    qemu_irq *irq_opaque;
+    PCIDevice *devices[256];
+    PCIDevice *parent_dev;
+    PCIBus *next;
+    /* The bus IRQ state is the logical OR of the connected devices.
+       Keep a count of the number of devices with raised IRQs.  */
+    int nirq;
+    int irq_count[];
+};
+
+static void pci_update_mappings(PCIDevice *d);
+static void pci_set_irq(void *opaque, int irq_num, int level);
+
+target_phys_addr_t pci_mem_base;
+static int pci_irq_index;
+static PCIBus *first_bus;
+
+static void pcibus_save(QEMUFile *f, void *opaque)
+{
+    PCIBus *bus = (PCIBus *)opaque;
+    int i;
+
+    qemu_put_be32(f, bus->nirq);
+    for (i = 0; i < bus->nirq; i++)
+        qemu_put_be32(f, bus->irq_count[i]);
+}
+
+static int  pcibus_load(QEMUFile *f, void *opaque, int version_id)
+{
+    PCIBus *bus = (PCIBus *)opaque;
+    int i, nirq;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    nirq = qemu_get_be32(f);
+    if (bus->nirq != nirq) {
+        fprintf(stderr, "pcibus_load: nirq mismatch: src=%d dst=%d\n",
+                nirq, bus->nirq);
+        return -EINVAL;
+    }
+
+    for (i = 0; i < nirq; i++)
+        bus->irq_count[i] = qemu_get_be32(f);
+
+    return 0;
+}
+
+PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+                         qemu_irq *pic, int devfn_min, int nirq)
+{
+    PCIBus *bus;
+    static int nbus = 0;
+
+    bus = qemu_mallocz(sizeof(PCIBus) + (nirq * sizeof(int)));
+    bus->set_irq = set_irq;
+    bus->map_irq = map_irq;
+    bus->irq_opaque = pic;
+    bus->devfn_min = devfn_min;
+    bus->nirq = nirq;
+    first_bus = bus;
+    register_savevm("PCIBUS", nbus++, 1, pcibus_save, pcibus_load, bus);
+    return bus;
+}
+
+static PCIBus *pci_register_secondary_bus(PCIDevice *dev, pci_map_irq_fn map_irq)
+{
+    PCIBus *bus;
+    bus = qemu_mallocz(sizeof(PCIBus));
+    bus->map_irq = map_irq;
+    bus->parent_dev = dev;
+    bus->next = dev->bus->next;
+    dev->bus->next = bus;
+    return bus;
+}
+
+int pci_bus_num(PCIBus *s)
+{
+    return s->bus_num;
+}
+
+void pci_device_save(PCIDevice *s, QEMUFile *f)
+{
+    int i;
+
+    qemu_put_be32(f, 2); /* PCI device version */
+    qemu_put_buffer(f, s->config, 256);
+    for (i = 0; i < 4; i++)
+        qemu_put_be32(f, s->irq_state[i]);
+}
+
+int pci_device_load(PCIDevice *s, QEMUFile *f)
+{
+    uint32_t version_id;
+    int i;
+
+    version_id = qemu_get_be32(f);
+    if (version_id > 2)
+        return -EINVAL;
+    qemu_get_buffer(f, s->config, 256);
+    pci_update_mappings(s);
+
+    if (version_id >= 2)
+        for (i = 0; i < 4; i ++)
+            s->irq_state[i] = qemu_get_be32(f);
+
+    return 0;
+}
+
+/* -1 for devfn means auto assign */
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+                               int instance_size, int devfn,
+                               PCIConfigReadFunc *config_read,
+                               PCIConfigWriteFunc *config_write)
+{
+    PCIDevice *pci_dev;
+
+    if (pci_irq_index >= PCI_DEVICES_MAX)
+        return NULL;
+
+    if (devfn < 0) {
+        for(devfn = bus->devfn_min ; devfn < 256; devfn += 8) {
+            if (!bus->devices[devfn])
+                goto found;
+        }
+        return NULL;
+    found: ;
+    }
+    pci_dev = qemu_mallocz(instance_size);
+    if (!pci_dev)
+        return NULL;
+    pci_dev->bus = bus;
+    pci_dev->devfn = devfn;
+    pstrcpy(pci_dev->name, sizeof(pci_dev->name), name);
+    memset(pci_dev->irq_state, 0, sizeof(pci_dev->irq_state));
+
+    if (!config_read)
+        config_read = pci_default_read_config;
+    if (!config_write)
+        config_write = pci_default_write_config;
+    pci_dev->config_read = config_read;
+    pci_dev->config_write = config_write;
+    pci_dev->irq_index = pci_irq_index++;
+    bus->devices[devfn] = pci_dev;
+    pci_dev->irq = qemu_allocate_irqs(pci_set_irq, pci_dev, 4);
+    return pci_dev;
+}
+
+void pci_register_io_region(PCIDevice *pci_dev, int region_num,
+                            uint32_t size, int type,
+                            PCIMapIORegionFunc *map_func)
+{
+    PCIIORegion *r;
+    uint32_t addr;
+
+    if ((unsigned int)region_num >= PCI_NUM_REGIONS)
+        return;
+    r = &pci_dev->io_regions[region_num];
+    r->addr = -1;
+    r->size = size;
+    r->type = type;
+    r->map_func = map_func;
+    if (region_num == PCI_ROM_SLOT) {
+        addr = 0x30;
+    } else {
+        addr = 0x10 + region_num * 4;
+    }
+    *(uint32_t *)(pci_dev->config + addr) = cpu_to_le32(type);
+}
+
+static target_phys_addr_t pci_to_cpu_addr(target_phys_addr_t addr)
+{
+    return addr + pci_mem_base;
+}
+
+static void pci_update_mappings(PCIDevice *d)
+{
+    PCIIORegion *r;
+    int cmd, i;
+    uint32_t last_addr, new_addr, config_ofs;
+
+    cmd = le16_to_cpu(*(uint16_t *)(d->config + PCI_COMMAND));
+    for(i = 0; i < PCI_NUM_REGIONS; i++) {
+        r = &d->io_regions[i];
+        if (i == PCI_ROM_SLOT) {
+            config_ofs = 0x30;
+        } else {
+            config_ofs = 0x10 + i * 4;
+        }
+        if (r->size != 0) {
+            if (r->type & PCI_ADDRESS_SPACE_IO) {
+                if (cmd & PCI_COMMAND_IO) {
+                    new_addr = le32_to_cpu(*(uint32_t *)(d->config +
+                                                         config_ofs));
+                    new_addr = new_addr & ~(r->size - 1);
+                    last_addr = new_addr + r->size - 1;
+                    /* NOTE: we have only 64K ioports on PC */
+                    if (last_addr <= new_addr || new_addr == 0 ||
+                        last_addr >= 0x10000) {
+                        new_addr = -1;
+                    }
+                } else {
+                    new_addr = -1;
+                }
+            } else {
+                if (cmd & PCI_COMMAND_MEMORY) {
+                    new_addr = le32_to_cpu(*(uint32_t *)(d->config +
+                                                         config_ofs));
+                    /* the ROM slot has a specific enable bit */
+                    if (i == PCI_ROM_SLOT && !(new_addr & 1))
+                        goto no_mem_map;
+                    new_addr = new_addr & ~(r->size - 1);
+                    last_addr = new_addr + r->size - 1;
+                    /* NOTE: we do not support wrapping */
+                    /* XXX: as we cannot support really dynamic
+                       mappings, we handle specific values as invalid
+                       mappings. */
+                    if (last_addr <= new_addr || new_addr == 0 ||
+                        last_addr == -1) {
+                        new_addr = -1;
+                    }
+                } else {
+                no_mem_map:
+                    new_addr = -1;
+                }
+            }
+            /* now do the real mapping */
+            if (new_addr != r->addr) {
+                if (r->addr != -1) {
+                    if (r->type & PCI_ADDRESS_SPACE_IO) {
+                        int class;
+                        /* NOTE: specific hack for IDE in PC case:
+                           only one byte must be mapped. */
+                        class = d->config[0x0a] | (d->config[0x0b] << 8);
+                        if (class == 0x0101 && r->size == 4) {
+                            isa_unassign_ioport(r->addr + 2, 1);
+                        } else {
+                            isa_unassign_ioport(r->addr, r->size);
+                        }
+                    } else {
+                        cpu_register_physical_memory(pci_to_cpu_addr(r->addr),
+                                                     r->size,
+                                                     IO_MEM_UNASSIGNED);
+                    }
+                }
+                r->addr = new_addr;
+                if (r->addr != -1) {
+                    r->map_func(d, i, r->addr, r->size, r->type);
+                }
+            }
+        }
+    }
+}
+
+uint32_t pci_default_read_config(PCIDevice *d,
+                                 uint32_t address, int len)
+{
+    uint32_t val;
+
+    switch(len) {
+    default:
+    case 4:
+	if (address <= 0xfc) {
+	    val = le32_to_cpu(*(uint32_t *)(d->config + address));
+	    break;
+	}
+	/* fall through */
+    case 2:
+        if (address <= 0xfe) {
+	    val = le16_to_cpu(*(uint16_t *)(d->config + address));
+	    break;
+	}
+	/* fall through */
+    case 1:
+        val = d->config[address];
+        break;
+    }
+    return val;
+}
+
+void pci_default_write_config(PCIDevice *d,
+                              uint32_t address, uint32_t val, int len)
+{
+    int can_write, i;
+    uint32_t end, addr;
+
+    if (len == 4 && ((address >= 0x10 && address < 0x10 + 4 * 6) ||
+                     (address >= 0x30 && address < 0x34))) {
+        PCIIORegion *r;
+        int reg;
+
+        if ( address >= 0x30 ) {
+            reg = PCI_ROM_SLOT;
+        }else{
+            reg = (address - 0x10) >> 2;
+        }
+        r = &d->io_regions[reg];
+        if (r->size == 0)
+            goto default_config;
+        /* compute the stored value */
+        if (reg == PCI_ROM_SLOT) {
+            /* keep ROM enable bit */
+            val &= (~(r->size - 1)) | 1;
+        } else {
+            val &= ~(r->size - 1);
+            val |= r->type;
+        }
+        *(uint32_t *)(d->config + address) = cpu_to_le32(val);
+        pci_update_mappings(d);
+        return;
+    }
+ default_config:
+    /* not efficient, but simple */
+    addr = address;
+    for(i = 0; i < len; i++) {
+        /* default read/write accesses */
+        switch(d->config[0x0e]) {
+        case 0x00:
+        case 0x80:
+            switch(addr) {
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x08:
+            case 0x09:
+            case 0x0a:
+            case 0x0b:
+            case 0x0e:
+            case 0x10 ... 0x27: /* base */
+            case 0x30 ... 0x33: /* rom */
+            case 0x3d:
+                can_write = 0;
+                break;
+            default:
+                can_write = 1;
+                break;
+            }
+            break;
+        default:
+        case 0x01:
+            switch(addr) {
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x08:
+            case 0x09:
+            case 0x0a:
+            case 0x0b:
+            case 0x0e:
+            case 0x38 ... 0x3b: /* rom */
+            case 0x3d:
+                can_write = 0;
+                break;
+            default:
+                can_write = 1;
+                break;
+            }
+            break;
+        }
+        if (can_write) {
+            d->config[addr] = val;
+        }
+        if (++addr > 0xff)
+        	break;
+        val >>= 8;
+    }
+
+    end = address + len;
+    if (end > PCI_COMMAND && address < (PCI_COMMAND + 2)) {
+        /* if the command register is modified, we must modify the mappings */
+        pci_update_mappings(d);
+    }
+}
+
+void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len)
+{
+    PCIBus *s = opaque;
+    PCIDevice *pci_dev;
+    int config_addr, bus_num;
+
+#if defined(DEBUG_PCI) && 0
+    printf("pci_data_write: addr=%08x val=%08x len=%d\n",
+           addr, val, len);
+#endif
+    bus_num = (addr >> 16) & 0xff;
+    while (s && s->bus_num != bus_num)
+        s = s->next;
+    if (!s)
+        return;
+    pci_dev = s->devices[(addr >> 8) & 0xff];
+    if (!pci_dev)
+        return;
+    config_addr = addr & 0xff;
+#if defined(DEBUG_PCI)
+    printf("pci_config_write: %s: addr=%02x val=%08x len=%d\n",
+           pci_dev->name, config_addr, val, len);
+#endif
+    pci_dev->config_write(pci_dev, config_addr, val, len);
+}
+
+uint32_t pci_data_read(void *opaque, uint32_t addr, int len)
+{
+    PCIBus *s = opaque;
+    PCIDevice *pci_dev;
+    int config_addr, bus_num;
+    uint32_t val;
+
+    bus_num = (addr >> 16) & 0xff;
+    while (s && s->bus_num != bus_num)
+        s= s->next;
+    if (!s)
+        goto fail;
+    pci_dev = s->devices[(addr >> 8) & 0xff];
+    if (!pci_dev) {
+    fail:
+        switch(len) {
+        case 1:
+            val = 0xff;
+            break;
+        case 2:
+            val = 0xffff;
+            break;
+        default:
+        case 4:
+            val = 0xffffffff;
+            break;
+        }
+        goto the_end;
+    }
+    config_addr = addr & 0xff;
+    val = pci_dev->config_read(pci_dev, config_addr, len);
+#if defined(DEBUG_PCI)
+    printf("pci_config_read: %s: addr=%02x val=%08x len=%d\n",
+           pci_dev->name, config_addr, val, len);
+#endif
+ the_end:
+#if defined(DEBUG_PCI) && 0
+    printf("pci_data_read: addr=%08x val=%08x len=%d\n",
+           addr, val, len);
+#endif
+    return val;
+}
+
+/***********************************************************/
+/* generic PCI irq support */
+
+/* 0 <= irq_num <= 3. level must be 0 or 1 */
+static void pci_set_irq(void *opaque, int irq_num, int level)
+{
+    PCIDevice *pci_dev = (PCIDevice *)opaque;
+    PCIBus *bus;
+    int change;
+
+    change = level - pci_dev->irq_state[irq_num];
+    if (!change)
+        return;
+
+    pci_dev->irq_state[irq_num] = level;
+    for (;;) {
+        bus = pci_dev->bus;
+        irq_num = bus->map_irq(pci_dev, irq_num);
+        if (bus->set_irq)
+            break;
+        pci_dev = bus->parent_dev;
+    }
+    bus->irq_count[irq_num] += change;
+    bus->set_irq(bus->irq_opaque, irq_num, bus->irq_count[irq_num] != 0);
+}
+
+/***********************************************************/
+/* monitor info on PCI */
+
+typedef struct {
+    uint16_t class;
+    const char *desc;
+} pci_class_desc;
+
+static pci_class_desc pci_class_descriptions[] =
+{
+    { 0x0100, "SCSI controller"},
+    { 0x0101, "IDE controller"},
+    { 0x0102, "Floppy controller"},
+    { 0x0103, "IPI controller"},
+    { 0x0104, "RAID controller"},
+    { 0x0106, "SATA controller"},
+    { 0x0107, "SAS controller"},
+    { 0x0180, "Storage controller"},
+    { 0x0200, "Ethernet controller"},
+    { 0x0201, "Token Ring controller"},
+    { 0x0202, "FDDI controller"},
+    { 0x0203, "ATM controller"},
+    { 0x0280, "Network controller"},
+    { 0x0300, "VGA controller"},
+    { 0x0301, "XGA controller"},
+    { 0x0302, "3D controller"},
+    { 0x0380, "Display controller"},
+    { 0x0400, "Video controller"},
+    { 0x0401, "Audio controller"},
+    { 0x0402, "Phone"},
+    { 0x0480, "Multimedia controller"},
+    { 0x0500, "RAM controller"},
+    { 0x0501, "Flash controller"},
+    { 0x0580, "Memory controller"},
+    { 0x0600, "Host bridge"},
+    { 0x0601, "ISA bridge"},
+    { 0x0602, "EISA bridge"},
+    { 0x0603, "MC bridge"},
+    { 0x0604, "PCI bridge"},
+    { 0x0605, "PCMCIA bridge"},
+    { 0x0606, "NUBUS bridge"},
+    { 0x0607, "CARDBUS bridge"},
+    { 0x0608, "RACEWAY bridge"},
+    { 0x0680, "Bridge"},
+    { 0x0c03, "USB controller"},
+    { 0, NULL}
+};
+
+static void pci_info_device(PCIDevice *d)
+{
+    int i, class;
+    PCIIORegion *r;
+    pci_class_desc *desc;
+
+    term_printf("  Bus %2d, device %3d, function %d:\n",
+           d->bus->bus_num, d->devfn >> 3, d->devfn & 7);
+    class = le16_to_cpu(*((uint16_t *)(d->config + PCI_CLASS_DEVICE)));
+    term_printf("    ");
+    desc = pci_class_descriptions;
+    while (desc->desc && class != desc->class)
+        desc++;
+    if (desc->desc) {
+        term_printf("%s", desc->desc);
+    } else {
+        term_printf("Class %04x", class);
+    }
+    term_printf(": PCI device %04x:%04x\n",
+           le16_to_cpu(*((uint16_t *)(d->config + PCI_VENDOR_ID))),
+           le16_to_cpu(*((uint16_t *)(d->config + PCI_DEVICE_ID))));
+
+    if (d->config[PCI_INTERRUPT_PIN] != 0) {
+        term_printf("      IRQ %d.\n", d->config[PCI_INTERRUPT_LINE]);
+    }
+    if (class == 0x0604) {
+        term_printf("      BUS %d.\n", d->config[0x19]);
+    }
+    for(i = 0;i < PCI_NUM_REGIONS; i++) {
+        r = &d->io_regions[i];
+        if (r->size != 0) {
+            term_printf("      BAR%d: ", i);
+            if (r->type & PCI_ADDRESS_SPACE_IO) {
+                term_printf("I/O at 0x%04x [0x%04x].\n",
+                       r->addr, r->addr + r->size - 1);
+            } else {
+                term_printf("32 bit memory at 0x%08x [0x%08x].\n",
+                       r->addr, r->addr + r->size - 1);
+            }
+        }
+    }
+    if (class == 0x0604 && d->config[0x19] != 0) {
+        pci_for_each_device(d->config[0x19], pci_info_device);
+    }
+}
+
+void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d))
+{
+    PCIBus *bus = first_bus;
+    PCIDevice *d;
+    int devfn;
+
+    while (bus && bus->bus_num != bus_num)
+        bus = bus->next;
+    if (bus) {
+        for(devfn = 0; devfn < 256; devfn++) {
+            d = bus->devices[devfn];
+            if (d)
+                fn(d);
+        }
+    }
+}
+
+void pci_info(void)
+{
+    pci_for_each_device(0, pci_info_device);
+}
+
+/* Initialize a PCI NIC.  */
+void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn)
+{
+#if 0
+    if (strcmp(nd->model, "ne2k_pci") == 0) {
+        pci_ne2000_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "i82551") == 0) {
+        pci_i82551_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "i82557b") == 0) {
+        pci_i82557b_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "i82559er") == 0) {
+        pci_i82559er_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "rtl8139") == 0) {
+        pci_rtl8139_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "e1000") == 0) {
+        pci_e1000_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "pcnet") == 0) {
+        pci_pcnet_init(bus, nd, devfn);
+    } else if (strcmp(nd->model, "?") == 0) {
+        fprintf(stderr, "qemu: Supported PCI NICs: i82551 i82557b i82559er"
+                        " ne2k_pci pcnet rtl8139 e1000\n");
+        exit (1);
+    } else {
+        fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd->model);
+        exit (1);
+    }
+#endif
+}
+
+typedef struct {
+    PCIDevice dev;
+    PCIBus *bus;
+} PCIBridge;
+
+static void pci_bridge_write_config(PCIDevice *d,
+                             uint32_t address, uint32_t val, int len)
+{
+    PCIBridge *s = (PCIBridge *)d;
+
+    if (address == 0x19 || (address == 0x18 && len > 1)) {
+        if (address == 0x19)
+            s->bus->bus_num = val & 0xff;
+        else
+            s->bus->bus_num = (val >> 8) & 0xff;
+#if defined(DEBUG_PCI)
+        printf ("pci-bridge: %s: Assigned bus %d\n", d->name, s->bus->bus_num);
+#endif
+    }
+    pci_default_write_config(d, address, val, len);
+}
+
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
+                        pci_map_irq_fn map_irq, const char *name)
+{
+    PCIBridge *s;
+    s = (PCIBridge *)pci_register_device(bus, name, sizeof(PCIBridge),
+                                         devfn, NULL, pci_bridge_write_config);
+    s->dev.config[0x00] = id >> 16;
+    s->dev.config[0x01] = id >> 24;
+    s->dev.config[0x02] = id; // device_id
+    s->dev.config[0x03] = id >> 8;
+    s->dev.config[0x04] = 0x06; // command = bus master, pci mem
+    s->dev.config[0x05] = 0x00;
+    s->dev.config[0x06] = 0xa0; // status = fast back-to-back, 66MHz, no error
+    s->dev.config[0x07] = 0x00; // status = fast devsel
+    s->dev.config[0x08] = 0x00; // revision
+    s->dev.config[0x09] = 0x00; // programming i/f
+    s->dev.config[0x0A] = 0x04; // class_sub = PCI to PCI bridge
+    s->dev.config[0x0B] = 0x06; // class_base = PCI_bridge
+    s->dev.config[0x0D] = 0x10; // latency_timer
+    s->dev.config[0x0E] = 0x81; // header_type
+    s->dev.config[0x1E] = 0xa0; // secondary status
+
+    s->bus = pci_register_secondary_bus(&s->dev, map_irq);
+    return s->bus;
+}
diff --git a/hw/pci.h b/hw/pci.h
new file mode 100644
index 0000000..e870987
--- /dev/null
+++ b/hw/pci.h
@@ -0,0 +1,142 @@
+#ifndef QEMU_PCI_H
+#define QEMU_PCI_H
+
+/* PCI includes legacy ISA access.  */
+#include "isa.h"
+
+/* PCI bus */
+
+extern target_phys_addr_t pci_mem_base;
+
+typedef void PCIConfigWriteFunc(PCIDevice *pci_dev,
+                                uint32_t address, uint32_t data, int len);
+typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev,
+                                   uint32_t address, int len);
+typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num,
+                                uint32_t addr, uint32_t size, int type);
+
+#define PCI_ADDRESS_SPACE_MEM		0x00
+#define PCI_ADDRESS_SPACE_IO		0x01
+#define PCI_ADDRESS_SPACE_MEM_PREFETCH	0x08
+
+typedef struct PCIIORegion {
+    uint32_t addr; /* current PCI mapping address. -1 means not mapped */
+    uint32_t size;
+    uint8_t type;
+    PCIMapIORegionFunc *map_func;
+} PCIIORegion;
+
+#define PCI_ROM_SLOT 6
+#define PCI_NUM_REGIONS 7
+
+#define PCI_DEVICES_MAX 64
+
+#define PCI_VENDOR_ID		0x00	/* 16 bits */
+#define PCI_DEVICE_ID		0x02	/* 16 bits */
+#define PCI_COMMAND		0x04	/* 16 bits */
+#define  PCI_COMMAND_IO		0x1	/* Enable response in I/O space */
+#define  PCI_COMMAND_MEMORY	0x2	/* Enable response in Memory space */
+#define PCI_CLASS_DEVICE        0x0a    /* Device class */
+#define PCI_INTERRUPT_LINE	0x3c	/* 8 bits */
+#define PCI_INTERRUPT_PIN	0x3d	/* 8 bits */
+#define PCI_MIN_GNT		0x3e	/* 8 bits */
+#define PCI_MAX_LAT		0x3f	/* 8 bits */
+
+struct PCIDevice {
+    /* PCI config space */
+    uint8_t config[256];
+
+    /* the following fields are read only */
+    PCIBus *bus;
+    int devfn;
+    char name[64];
+    PCIIORegion io_regions[PCI_NUM_REGIONS];
+
+    /* do not access the following fields */
+    PCIConfigReadFunc *config_read;
+    PCIConfigWriteFunc *config_write;
+    /* ??? This is a PC-specific hack, and should be removed.  */
+    int irq_index;
+
+    /* IRQ objects for the INTA-INTD pins.  */
+    qemu_irq *irq;
+
+    /* Current IRQ levels.  Used internally by the generic PCI code.  */
+    int irq_state[4];
+};
+
+PCIDevice *pci_register_device(PCIBus *bus, const char *name,
+                               int instance_size, int devfn,
+                               PCIConfigReadFunc *config_read,
+                               PCIConfigWriteFunc *config_write);
+
+void pci_register_io_region(PCIDevice *pci_dev, int region_num,
+                            uint32_t size, int type,
+                            PCIMapIORegionFunc *map_func);
+
+uint32_t pci_default_read_config(PCIDevice *d,
+                                 uint32_t address, int len);
+void pci_default_write_config(PCIDevice *d,
+                              uint32_t address, uint32_t val, int len);
+void pci_device_save(PCIDevice *s, QEMUFile *f);
+int pci_device_load(PCIDevice *s, QEMUFile *f);
+
+typedef void (*pci_set_irq_fn)(qemu_irq *pic, int irq_num, int level);
+typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num);
+PCIBus *pci_register_bus(pci_set_irq_fn set_irq, pci_map_irq_fn map_irq,
+                         qemu_irq *pic, int devfn_min, int nirq);
+
+void pci_nic_init(PCIBus *bus, NICInfo *nd, int devfn);
+void pci_data_write(void *opaque, uint32_t addr, uint32_t val, int len);
+uint32_t pci_data_read(void *opaque, uint32_t addr, int len);
+int pci_bus_num(PCIBus *s);
+void pci_for_each_device(int bus_num, void (*fn)(PCIDevice *d));
+
+void pci_info(void);
+PCIBus *pci_bridge_init(PCIBus *bus, int devfn, uint32_t id,
+                        pci_map_irq_fn map_irq, const char *name);
+
+/* lsi53c895a.c */
+#define LSI_MAX_DEVS 7
+void lsi_scsi_attach(void *opaque, BlockDriverState *bd, int id);
+void *lsi_scsi_init(PCIBus *bus, int devfn);
+
+/* vmware_vga.c */
+void pci_vmsvga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
+                     unsigned long vga_ram_offset, int vga_ram_size);
+
+/* usb-uhci.c */
+void usb_uhci_piix3_init(PCIBus *bus, int devfn);
+void usb_uhci_piix4_init(PCIBus *bus, int devfn);
+
+/* usb-ohci.c */
+void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn);
+
+/* eepro100.c */
+
+void pci_i82551_init(PCIBus *bus, NICInfo *nd, int devfn);
+void pci_i82557b_init(PCIBus *bus, NICInfo *nd, int devfn);
+void pci_i82559er_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* ne2000.c */
+
+void pci_ne2000_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* rtl8139.c */
+
+void pci_rtl8139_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* e1000.c */
+void pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* pcnet.c */
+void pci_pcnet_init(PCIBus *bus, NICInfo *nd, int devfn);
+
+/* prep_pci.c */
+PCIBus *pci_prep_init(qemu_irq *pic);
+
+/* apb_pci.c */
+PCIBus *pci_apb_init(target_phys_addr_t special_base, target_phys_addr_t mem_base,
+                     qemu_irq *pic);
+
+#endif
diff --git a/hw/pci_host.h b/hw/pci_host.h
new file mode 100644
index 0000000..49a0c59
--- /dev/null
+++ b/hw/pci_host.h
@@ -0,0 +1,93 @@
+/*
+ * QEMU Common PCI Host bridge configuration data space access routines.
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* Worker routines for a PCI host controller that uses an {address,data}
+   register pair to access PCI configuration space.  */
+
+typedef struct {
+    uint32_t config_reg;
+    PCIBus *bus;
+} PCIHostState;
+
+static void pci_host_data_writeb(void* opaque, pci_addr_t addr, uint32_t val)
+{
+    PCIHostState *s = opaque;
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg | (addr & 3), val, 1);
+}
+
+static void pci_host_data_writew(void* opaque, pci_addr_t addr, uint32_t val)
+{
+    PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap16(val);
+#endif
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg | (addr & 3), val, 2);
+}
+
+static void pci_host_data_writel(void* opaque, pci_addr_t addr, uint32_t val)
+{
+    PCIHostState *s = opaque;
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap32(val);
+#endif
+    if (s->config_reg & (1u << 31))
+        pci_data_write(s->bus, s->config_reg, val, 4);
+}
+
+static uint32_t pci_host_data_readb(void* opaque, pci_addr_t addr)
+{
+    PCIHostState *s = opaque;
+    if (!(s->config_reg & (1 << 31)))
+        return 0xff;
+    return pci_data_read(s->bus, s->config_reg | (addr & 3), 1);
+}
+
+static uint32_t pci_host_data_readw(void* opaque, pci_addr_t addr)
+{
+    PCIHostState *s = opaque;
+    uint32_t val;
+    if (!(s->config_reg & (1 << 31)))
+        return 0xffff;
+    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 2);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap16(val);
+#endif
+    return val;
+}
+
+static uint32_t pci_host_data_readl(void* opaque, pci_addr_t addr)
+{
+    PCIHostState *s = opaque;
+    uint32_t val;
+    if (!(s->config_reg & (1 << 31)))
+        return 0xffffffff;
+    val = pci_data_read(s->bus, s->config_reg | (addr & 3), 4);
+#ifdef TARGET_WORDS_BIGENDIAN
+    val = bswap32(val);
+#endif
+    return val;
+}
+
diff --git a/hw/pcmcia.h b/hw/pcmcia.h
new file mode 100644
index 0000000..bfa23ba
--- /dev/null
+++ b/hw/pcmcia.h
@@ -0,0 +1,50 @@
+/* PCMCIA/Cardbus */
+
+struct pcmcia_socket_s {
+    qemu_irq irq;
+    int attached;
+    const char *slot_string;
+    const char *card_string;
+};
+
+void pcmcia_socket_register(struct pcmcia_socket_s *socket);
+void pcmcia_socket_unregister(struct pcmcia_socket_s *socket);
+void pcmcia_info(void);
+
+struct pcmcia_card_s {
+    void *state;
+    struct pcmcia_socket_s *slot;
+    int (*attach)(void *state);
+    int (*detach)(void *state);
+    const uint8_t *cis;
+    int cis_len;
+
+    /* Only valid if attached */
+    uint8_t (*attr_read)(void *state, uint32_t address);
+    void (*attr_write)(void *state, uint32_t address, uint8_t value);
+    uint16_t (*common_read)(void *state, uint32_t address);
+    void (*common_write)(void *state, uint32_t address, uint16_t value);
+    uint16_t (*io_read)(void *state, uint32_t address);
+    void (*io_write)(void *state, uint32_t address, uint16_t value);
+};
+
+#define CISTPL_DEVICE		0x01	/* 5V Device Information Tuple */
+#define CISTPL_NO_LINK		0x14	/* No Link Tuple */
+#define CISTPL_VERS_1		0x15	/* Level 1 Version Tuple */
+#define CISTPL_JEDEC_C		0x18	/* JEDEC ID Tuple */
+#define CISTPL_JEDEC_A		0x19	/* JEDEC ID Tuple */
+#define CISTPL_CONFIG		0x1a	/* Configuration Tuple */
+#define CISTPL_CFTABLE_ENTRY	0x1b	/* 16-bit PCCard Configuration */
+#define CISTPL_DEVICE_OC	0x1c	/* Additional Device Information */
+#define CISTPL_DEVICE_OA	0x1d	/* Additional Device Information */
+#define CISTPL_DEVICE_GEO	0x1e	/* Additional Device Information */
+#define CISTPL_DEVICE_GEO_A	0x1f	/* Additional Device Information */
+#define CISTPL_MANFID		0x20	/* Manufacture ID Tuple */
+#define CISTPL_FUNCID		0x21	/* Function ID Tuple */
+#define CISTPL_FUNCE		0x22	/* Function Extension Tuple */
+#define CISTPL_END		0xff	/* Tuple End */
+#define CISTPL_ENDMARK		0xff
+
+/* dscm1xxxx.c */
+struct pcmcia_card_s *dscm1xxxx_init(BlockDriverState *bdrv);
+
diff --git a/hw/power_supply.h b/hw/power_supply.h
new file mode 100644
index 0000000..b85edc7
--- /dev/null
+++ b/hw/power_supply.h
@@ -0,0 +1,109 @@
+/*
+ *  Universal power supply monitor class
+ *
+ *  Copyright © 2007  Anton Vorontsov <cbou@mail.ru>
+ *  Copyright © 2004  Szabolcs Gyurko
+ *  Copyright © 2003  Ian Molton <spyro@f2s.com>
+ *
+ *  Modified: 2004, Oct     Szabolcs Gyurko
+ *
+ *  You may use this code as per GPL version 2
+ */
+
+#ifndef __LINUX_POWER_SUPPLY_H__
+#define __LINUX_POWER_SUPPLY_H__
+
+/*
+ * All voltages, currents, charges, energies, time and temperatures in uV,
+ * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
+ * stated. It's driver's job to convert its raw values to units in which
+ * this class operates.
+ */
+
+/*
+ * For systems where the charger determines the maximum battery capacity
+ * the min and max fields should be used to present these values to user
+ * space. Unused/unknown fields will not appear in sysfs.
+ */
+
+enum {
+	POWER_SUPPLY_STATUS_UNKNOWN = 0,
+	POWER_SUPPLY_STATUS_CHARGING,
+	POWER_SUPPLY_STATUS_DISCHARGING,
+	POWER_SUPPLY_STATUS_NOT_CHARGING,
+	POWER_SUPPLY_STATUS_FULL,
+};
+
+enum {
+	POWER_SUPPLY_HEALTH_UNKNOWN = 0,
+	POWER_SUPPLY_HEALTH_GOOD,
+	POWER_SUPPLY_HEALTH_OVERHEAT,
+	POWER_SUPPLY_HEALTH_DEAD,
+	POWER_SUPPLY_HEALTH_OVERVOLTAGE,
+	POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
+};
+
+enum {
+	POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0,
+	POWER_SUPPLY_TECHNOLOGY_NiMH,
+	POWER_SUPPLY_TECHNOLOGY_LION,
+	POWER_SUPPLY_TECHNOLOGY_LIPO,
+	POWER_SUPPLY_TECHNOLOGY_LiFe,
+	POWER_SUPPLY_TECHNOLOGY_NiCd,
+};
+
+enum {
+	POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
+	POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
+	POWER_SUPPLY_CAPACITY_LEVEL_LOW,
+	POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
+	POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
+	POWER_SUPPLY_CAPACITY_LEVEL_FULL,
+};
+
+enum power_supply_property {
+	/* Properties of type `int' */
+	POWER_SUPPLY_PROP_STATUS = 0,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_AVG,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_EMPTY,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_CHARGE_AVG,
+	POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
+	POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
+	POWER_SUPPLY_PROP_ENERGY_FULL,
+	POWER_SUPPLY_PROP_ENERGY_EMPTY,
+	POWER_SUPPLY_PROP_ENERGY_NOW,
+	POWER_SUPPLY_PROP_ENERGY_AVG,
+	POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TEMP_AMBIENT,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
+	/* Properties of type `const char *' */
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+enum power_supply_type {
+	POWER_SUPPLY_TYPE_BATTERY = 0,
+	POWER_SUPPLY_TYPE_UPS,
+	POWER_SUPPLY_TYPE_MAINS,
+	POWER_SUPPLY_TYPE_USB,
+};
+
+#endif /* __LINUX_POWER_SUPPLY_H__ */
diff --git a/hw/pxa.h b/hw/pxa.h
new file mode 100644
index 0000000..16a68d9
--- /dev/null
+++ b/hw/pxa.h
@@ -0,0 +1,227 @@
+/*
+ * Intel XScale PXA255/270 processor support.
+ *
+ * Copyright (c) 2006 Openedhand Ltd.
+ * Written by Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#ifndef PXA_H
+# define PXA_H			"pxa.h"
+
+/* Interrupt numbers */
+# define PXA2XX_PIC_SSP3	0
+# define PXA2XX_PIC_USBH2	2
+# define PXA2XX_PIC_USBH1	3
+# define PXA2XX_PIC_KEYPAD	4
+# define PXA2XX_PIC_PWRI2C	6
+# define PXA25X_PIC_HWUART	7
+# define PXA27X_PIC_OST_4_11	7
+# define PXA2XX_PIC_GPIO_0	8
+# define PXA2XX_PIC_GPIO_1	9
+# define PXA2XX_PIC_GPIO_X	10
+# define PXA2XX_PIC_I2S 	13
+# define PXA26X_PIC_ASSP	15
+# define PXA25X_PIC_NSSP	16
+# define PXA27X_PIC_SSP2	16
+# define PXA2XX_PIC_LCD		17
+# define PXA2XX_PIC_I2C		18
+# define PXA2XX_PIC_ICP		19
+# define PXA2XX_PIC_STUART	20
+# define PXA2XX_PIC_BTUART	21
+# define PXA2XX_PIC_FFUART	22
+# define PXA2XX_PIC_MMC		23
+# define PXA2XX_PIC_SSP		24
+# define PXA2XX_PIC_DMA		25
+# define PXA2XX_PIC_OST_0	26
+# define PXA2XX_PIC_RTC1HZ	30
+# define PXA2XX_PIC_RTCALARM	31
+
+/* DMA requests */
+# define PXA2XX_RX_RQ_I2S	2
+# define PXA2XX_TX_RQ_I2S	3
+# define PXA2XX_RX_RQ_BTUART	4
+# define PXA2XX_TX_RQ_BTUART	5
+# define PXA2XX_RX_RQ_FFUART	6
+# define PXA2XX_TX_RQ_FFUART	7
+# define PXA2XX_RX_RQ_SSP1	13
+# define PXA2XX_TX_RQ_SSP1	14
+# define PXA2XX_RX_RQ_SSP2	15
+# define PXA2XX_TX_RQ_SSP2	16
+# define PXA2XX_RX_RQ_ICP	17
+# define PXA2XX_TX_RQ_ICP	18
+# define PXA2XX_RX_RQ_STUART	19
+# define PXA2XX_TX_RQ_STUART	20
+# define PXA2XX_RX_RQ_MMCI	21
+# define PXA2XX_TX_RQ_MMCI	22
+# define PXA2XX_USB_RQ(x)	((x) + 24)
+# define PXA2XX_RX_RQ_SSP3	66
+# define PXA2XX_TX_RQ_SSP3	67
+
+# define PXA2XX_SDRAM_BASE	0xa0000000
+# define PXA2XX_INTERNAL_BASE	0x5c000000
+# define PXA2XX_INTERNAL_SIZE	0x40000
+
+/* pxa2xx_pic.c */
+qemu_irq *pxa2xx_pic_init(target_phys_addr_t base, CPUState *env);
+
+/* pxa2xx_timer.c */
+void pxa25x_timer_init(target_phys_addr_t base, qemu_irq *irqs);
+void pxa27x_timer_init(target_phys_addr_t base, qemu_irq *irqs, qemu_irq irq4);
+
+/* pxa2xx_gpio.c */
+struct pxa2xx_gpio_info_s;
+struct pxa2xx_gpio_info_s *pxa2xx_gpio_init(target_phys_addr_t base,
+                CPUState *env, qemu_irq *pic, int lines);
+qemu_irq *pxa2xx_gpio_in_get(struct pxa2xx_gpio_info_s *s);
+void pxa2xx_gpio_out_set(struct pxa2xx_gpio_info_s *s,
+                int line, qemu_irq handler);
+void pxa2xx_gpio_read_notifier(struct pxa2xx_gpio_info_s *s, qemu_irq handler);
+
+/* pxa2xx_dma.c */
+struct pxa2xx_dma_state_s;
+struct pxa2xx_dma_state_s *pxa255_dma_init(target_phys_addr_t base,
+                qemu_irq irq);
+struct pxa2xx_dma_state_s *pxa27x_dma_init(target_phys_addr_t base,
+                qemu_irq irq);
+void pxa2xx_dma_request(struct pxa2xx_dma_state_s *s, int req_num, int on);
+
+/* pxa2xx_lcd.c */
+struct pxa2xx_lcdc_s;
+struct pxa2xx_lcdc_s *pxa2xx_lcdc_init(target_phys_addr_t base,
+                qemu_irq irq, DisplayState *ds);
+void pxa2xx_lcd_vsync_notifier(struct pxa2xx_lcdc_s *s, qemu_irq handler);
+void pxa2xx_lcdc_oritentation(void *opaque, int angle);
+
+/* pxa2xx_mmci.c */
+struct pxa2xx_mmci_s;
+struct pxa2xx_mmci_s *pxa2xx_mmci_init(target_phys_addr_t base,
+                BlockDriverState *bd, qemu_irq irq, void *dma);
+void pxa2xx_mmci_handlers(struct pxa2xx_mmci_s *s, qemu_irq readonly,
+                qemu_irq coverswitch);
+
+/* pxa2xx_pcmcia.c */
+struct pxa2xx_pcmcia_s;
+struct pxa2xx_pcmcia_s *pxa2xx_pcmcia_init(target_phys_addr_t base);
+int pxa2xx_pcmcia_attach(void *opaque, struct pcmcia_card_s *card);
+int pxa2xx_pcmcia_dettach(void *opaque);
+void pxa2xx_pcmcia_set_irq_cb(void *opaque, qemu_irq irq, qemu_irq cd_irq);
+
+/* pxa2xx_keypad.c */
+struct  keymap {
+    int column;
+    int row;
+};
+struct pxa2xx_keypad_s;
+struct pxa2xx_keypad_s *pxa27x_keypad_init(target_phys_addr_t base,
+                qemu_irq irq);
+void pxa27x_register_keypad(struct pxa2xx_keypad_s *kp, struct keymap *map,
+                int size);
+
+/* pxa2xx.c */
+struct pxa2xx_ssp_s;
+void pxa2xx_ssp_attach(struct pxa2xx_ssp_s *port,
+                uint32_t (*readfn)(void *opaque),
+                void (*writefn)(void *opaque, uint32_t value), void *opaque);
+
+struct pxa2xx_i2c_s;
+struct pxa2xx_i2c_s *pxa2xx_i2c_init(target_phys_addr_t base,
+                qemu_irq irq, uint32_t page_size);
+i2c_bus *pxa2xx_i2c_bus(struct pxa2xx_i2c_s *s);
+
+struct pxa2xx_i2s_s;
+struct pxa2xx_fir_s;
+
+struct pxa2xx_state_s {
+    CPUState *env;
+    qemu_irq *pic;
+    qemu_irq reset;
+    struct pxa2xx_dma_state_s *dma;
+    struct pxa2xx_gpio_info_s *gpio;
+    struct pxa2xx_lcdc_s *lcd;
+    struct pxa2xx_ssp_s **ssp;
+    struct pxa2xx_i2c_s *i2c[2];
+    struct pxa2xx_mmci_s *mmc;
+    struct pxa2xx_pcmcia_s *pcmcia[2];
+    struct pxa2xx_i2s_s *i2s;
+    struct pxa2xx_fir_s *fir;
+    struct pxa2xx_keypad_s *kp;
+
+    /* Power management */
+    target_phys_addr_t pm_base;
+    uint32_t pm_regs[0x40];
+
+    /* Clock management */
+    target_phys_addr_t cm_base;
+    uint32_t cm_regs[4];
+    uint32_t clkcfg;
+
+    /* Memory management */
+    target_phys_addr_t mm_base;
+    uint32_t mm_regs[0x1a];
+
+    /* Performance monitoring */
+    uint32_t pmnc;
+
+    /* Real-Time clock */
+    target_phys_addr_t rtc_base;
+    uint32_t rttr;
+    uint32_t rtsr;
+    uint32_t rtar;
+    uint32_t rdar1;
+    uint32_t rdar2;
+    uint32_t ryar1;
+    uint32_t ryar2;
+    uint32_t swar1;
+    uint32_t swar2;
+    uint32_t piar;
+    uint32_t last_rcnr;
+    uint32_t last_rdcr;
+    uint32_t last_rycr;
+    uint32_t last_swcr;
+    uint32_t last_rtcpicr;
+    int64_t last_hz;
+    int64_t last_sw;
+    int64_t last_pi;
+    QEMUTimer *rtc_hz;
+    QEMUTimer *rtc_rdal1;
+    QEMUTimer *rtc_rdal2;
+    QEMUTimer *rtc_swal1;
+    QEMUTimer *rtc_swal2;
+    QEMUTimer *rtc_pi;
+};
+
+struct pxa2xx_i2s_s {
+    target_phys_addr_t base;
+    qemu_irq irq;
+    struct pxa2xx_dma_state_s *dma;
+    void (*data_req)(void *, int, int);
+
+    uint32_t control[2];
+    uint32_t status;
+    uint32_t mask;
+    uint32_t clk;
+
+    int enable;
+    int rx_len;
+    int tx_len;
+    void (*codec_out)(void *, uint32_t);
+    uint32_t (*codec_in)(void *);
+    void *opaque;
+
+    int fifo_len;
+    uint32_t fifo[16];
+};
+
+# define PA_FMT			"0x%08lx"
+# define REG_FMT		"0x" TARGET_FMT_plx
+
+struct pxa2xx_state_s *pxa270_init(unsigned int sdram_size, DisplayState *ds,
+                const char *revision);
+struct pxa2xx_state_s *pxa255_init(unsigned int sdram_size, DisplayState *ds);
+
+/* usb-ohci.c */
+void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn,
+                       qemu_irq irq);
+
+#endif	/* PXA_H */
diff --git a/hw/scsi-disk.c b/hw/scsi-disk.c
new file mode 100644
index 0000000..16b3215
--- /dev/null
+++ b/hw/scsi-disk.c
@@ -0,0 +1,809 @@
+/*
+ * SCSI Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Based on code by Fabrice Bellard
+ *
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ *
+ * Note that this file only handles the SCSI architecture model and device
+ * commands.  Emulation of interface/link layer protocols is handled by
+ * the host adapter emulator.
+ */
+
+//#define DEBUG_SCSI
+
+#ifdef DEBUG_SCSI
+#define DPRINTF(fmt, args...) \
+do { printf("scsi-disk: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
+
+#include "qemu-common.h"
+#include "block.h"
+#include "scsi-disk.h"
+
+#define SENSE_NO_SENSE        0
+#define SENSE_NOT_READY       2
+#define SENSE_HARDWARE_ERROR  4
+#define SENSE_ILLEGAL_REQUEST 5
+
+#define SCSI_DMA_BUF_SIZE    65536
+
+typedef struct SCSIRequest {
+    SCSIDeviceState *dev;
+    uint32_t tag;
+    /* ??? We should probably keep track of whether the data trasfer is
+       a read or a write.  Currently we rely on the host getting it right.  */
+    /* Both sector and sector_count are in terms of qemu 512 byte blocks.  */
+    int sector;
+    int sector_count;
+    /* The amounnt of data in the buffer.  */
+    int buf_len;
+    uint8_t *dma_buf;
+    BlockDriverAIOCB *aiocb;
+    struct SCSIRequest *next;
+} SCSIRequest;
+
+struct SCSIDeviceState
+{
+    BlockDriverState *bdrv;
+    SCSIRequest *requests;
+    /* The qemu block layer uses a fixed 512 byte sector size.
+       This is the number of 512 byte blocks in a single scsi sector.  */
+    int cluster_size;
+    int sense;
+    int tcq;
+    /* Completion functions may be called from either scsi_{read,write}_data
+       or from the AIO completion routines.  */
+    scsi_completionfn completion;
+    void *opaque;
+};
+
+/* Global pool of SCSIRequest structures.  */
+static SCSIRequest *free_requests = NULL;
+
+static SCSIRequest *scsi_new_request(SCSIDeviceState *s, uint32_t tag)
+{
+    SCSIRequest *r;
+
+    if (free_requests) {
+        r = free_requests;
+        free_requests = r->next;
+    } else {
+        r = qemu_malloc(sizeof(SCSIRequest));
+        r->dma_buf = qemu_memalign(512, SCSI_DMA_BUF_SIZE);
+    }
+    r->dev = s;
+    r->tag = tag;
+    r->sector_count = 0;
+    r->buf_len = 0;
+    r->aiocb = NULL;
+
+    r->next = s->requests;
+    s->requests = r;
+    return r;
+}
+
+static void scsi_remove_request(SCSIRequest *r)
+{
+    SCSIRequest *last;
+    SCSIDeviceState *s = r->dev;
+
+    if (s->requests == r) {
+        s->requests = r->next;
+    } else {
+        last = s->requests;
+        while (last && last->next != r)
+            last = last->next;
+        if (last) {
+            last->next = r->next;
+        } else {
+            BADF("Orphaned request\n");
+        }
+    }
+    r->next = free_requests;
+    free_requests = r;
+}
+
+static SCSIRequest *scsi_find_request(SCSIDeviceState *s, uint32_t tag)
+{
+    SCSIRequest *r;
+
+    r = s->requests;
+    while (r && r->tag != tag)
+        r = r->next;
+
+    return r;
+}
+
+/* Helper function for command completion.  */
+static void scsi_command_complete(SCSIRequest *r, int sense)
+{
+    SCSIDeviceState *s = r->dev;
+    uint32_t tag;
+    DPRINTF("Command complete tag=0x%x sense=%d\n", r->tag, sense);
+    s->sense = sense;
+    tag = r->tag;
+    scsi_remove_request(r);
+    s->completion(s->opaque, SCSI_REASON_DONE, tag, sense);
+}
+
+/* Cancel a pending data transfer.  */
+static void scsi_cancel_io(SCSIDevice *d, uint32_t tag)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+    DPRINTF("Cancel tag=0x%x\n", tag);
+    r = scsi_find_request(s, tag);
+    if (r) {
+        if (r->aiocb)
+            bdrv_aio_cancel(r->aiocb);
+        r->aiocb = NULL;
+        scsi_remove_request(r);
+    }
+}
+
+static void scsi_read_complete(void * opaque, int ret)
+{
+    SCSIRequest *r = (SCSIRequest *)opaque;
+    SCSIDeviceState *s = r->dev;
+
+    if (ret) {
+        DPRINTF("IO error\n");
+        scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+        return;
+    }
+    DPRINTF("Data ready tag=0x%x len=%d\n", r->tag, r->buf_len);
+
+    s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
+}
+
+/* Read more data from scsi device into buffer.  */
+static void scsi_read_data(SCSIDevice *d, uint32_t tag)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+    uint32_t n;
+
+    r = scsi_find_request(s, tag);
+    if (!r) {
+        BADF("Bad read tag 0x%x\n", tag);
+        /* ??? This is the wrong error.  */
+        scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+        return;
+    }
+    if (r->sector_count == (uint32_t)-1) {
+        DPRINTF("Read buf_len=%d\n", r->buf_len);
+        r->sector_count = 0;
+        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, r->buf_len);
+        return;
+    }
+    DPRINTF("Read sector_count=%d\n", r->sector_count);
+    if (r->sector_count == 0) {
+        scsi_command_complete(r, SENSE_NO_SENSE);
+        return;
+    }
+
+    n = r->sector_count;
+    if (n > SCSI_DMA_BUF_SIZE / 512)
+        n = SCSI_DMA_BUF_SIZE / 512;
+
+    r->buf_len = n * 512;
+    r->aiocb = bdrv_aio_read(s->bdrv, r->sector, r->dma_buf, n,
+                             scsi_read_complete, r);
+    if (r->aiocb == NULL)
+        scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+    r->sector += n;
+    r->sector_count -= n;
+}
+
+static void scsi_write_complete(void * opaque, int ret)
+{
+    SCSIRequest *r = (SCSIRequest *)opaque;
+    SCSIDeviceState *s = r->dev;
+    uint32_t len;
+
+    if (ret) {
+        fprintf(stderr, "scsi-disc: IO write error\n");
+        exit(1);
+    }
+
+    r->aiocb = NULL;
+    if (r->sector_count == 0) {
+        scsi_command_complete(r, SENSE_NO_SENSE);
+    } else {
+        len = r->sector_count * 512;
+        if (len > SCSI_DMA_BUF_SIZE) {
+            len = SCSI_DMA_BUF_SIZE;
+        }
+        r->buf_len = len;
+        DPRINTF("Write complete tag=0x%x more=%d\n", r->tag, len);
+        s->completion(s->opaque, SCSI_REASON_DATA, r->tag, len);
+    }
+}
+
+/* Write data to a scsi device.  Returns nonzero on failure.
+   The transfer may complete asynchronously.  */
+static int scsi_write_data(SCSIDevice *d, uint32_t tag)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+    uint32_t n;
+
+    DPRINTF("Write data tag=0x%x\n", tag);
+    r = scsi_find_request(s, tag);
+    if (!r) {
+        BADF("Bad write tag 0x%x\n", tag);
+        scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+        return 1;
+    }
+    if (r->aiocb)
+        BADF("Data transfer already in progress\n");
+    n = r->buf_len / 512;
+    if (n) {
+        r->aiocb = bdrv_aio_write(s->bdrv, r->sector, r->dma_buf, n,
+                                  scsi_write_complete, r);
+        if (r->aiocb == NULL)
+            scsi_command_complete(r, SENSE_HARDWARE_ERROR);
+        r->sector += n;
+        r->sector_count -= n;
+    } else {
+        /* Invoke completion routine to fetch data from host.  */
+        scsi_write_complete(r, 0);
+    }
+
+    return 0;
+}
+
+/* Return a pointer to the data buffer.  */
+static uint8_t *scsi_get_buf(SCSIDevice *d, uint32_t tag)
+{
+    SCSIDeviceState *s = d->state;
+    SCSIRequest *r;
+
+    r = scsi_find_request(s, tag);
+    if (!r) {
+        BADF("Bad buffer tag 0x%x\n", tag);
+        return NULL;
+    }
+    return r->dma_buf;
+}
+
+/* Execute a scsi command.  Returns the length of the data expected by the
+   command.  This will be Positive for data transfers from the device
+   (eg. disk reads), negative for transfers to the device (eg. disk writes),
+   and zero if the command does not transfer any data.  */
+
+static int32_t scsi_send_command(SCSIDevice *d, uint32_t tag,
+                                 uint8_t *buf, int lun)
+{
+    SCSIDeviceState *s = d->state;
+    uint64_t nb_sectors;
+    uint32_t lba;
+    uint32_t len;
+    int cmdlen;
+    int is_write;
+    uint8_t command;
+    uint8_t *outbuf;
+    SCSIRequest *r;
+
+    command = buf[0];
+    r = scsi_find_request(s, tag);
+    if (r) {
+        BADF("Tag 0x%x already in use\n", tag);
+        scsi_cancel_io(d, tag);
+    }
+    /* ??? Tags are not unique for different luns.  We only implement a
+       single lun, so this should not matter.  */
+    r = scsi_new_request(s, tag);
+    outbuf = r->dma_buf;
+    is_write = 0;
+    DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]);
+    switch (command >> 5) {
+    case 0:
+        lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
+        len = buf[4];
+        cmdlen = 6;
+        break;
+    case 1:
+    case 2:
+        lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+        len = buf[8] | (buf[7] << 8);
+        cmdlen = 10;
+        break;
+    case 4:
+        lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+        len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
+        cmdlen = 16;
+        break;
+    case 5:
+        lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
+        len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
+        cmdlen = 12;
+        break;
+    default:
+        BADF("Unsupported command length, command %x\n", command);
+        goto fail;
+    }
+#ifdef DEBUG_SCSI
+    {
+        int i;
+        for (i = 1; i < cmdlen; i++) {
+            printf(" 0x%02x", buf[i]);
+        }
+        printf("\n");
+    }
+#endif
+    if (lun || buf[1] >> 5) {
+        /* Only LUN 0 supported.  */
+        DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
+        goto fail;
+    }
+    switch (command) {
+    case 0x0:
+	DPRINTF("Test Unit Ready\n");
+	break;
+    case 0x03:
+        DPRINTF("Request Sense (len %d)\n", len);
+        if (len < 4)
+            goto fail;
+        memset(outbuf, 0, 4);
+        outbuf[0] = 0xf0;
+        outbuf[1] = 0;
+        outbuf[2] = s->sense;
+        r->buf_len = 4;
+        break;
+    case 0x12:
+        DPRINTF("Inquiry (len %d)\n", len);
+        if (buf[1] & 0x2) {
+            /* Command support data - optional, not implemented */
+            BADF("optional INQUIRY command support request not implemented\n");
+            goto fail;
+        }
+        else if (buf[1] & 0x1) {
+            /* Vital product data */
+            uint8_t page_code = buf[2];
+            if (len < 4) {
+                BADF("Error: Inquiry (EVPD[%02X]) buffer size %d is "
+                     "less than 4\n", page_code, len);
+                goto fail;
+            }
+
+            switch (page_code) {
+                case 0x00:
+                    {
+                        /* Supported page codes, mandatory */
+                        DPRINTF("Inquiry EVPD[Supported pages] "
+                                "buffer size %d\n", len);
+
+                        r->buf_len = 0;
+
+                        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                            outbuf[r->buf_len++] = 5;
+                        } else {
+                            outbuf[r->buf_len++] = 0;
+                        }
+
+                        outbuf[r->buf_len++] = 0x00; // this page
+                        outbuf[r->buf_len++] = 0x00;
+                        outbuf[r->buf_len++] = 3;    // number of pages
+                        outbuf[r->buf_len++] = 0x00; // list of supported pages (this page)
+                        outbuf[r->buf_len++] = 0x80; // unit serial number
+                        outbuf[r->buf_len++] = 0x83; // device identification
+                    }
+                    break;
+                case 0x80:
+                    {
+                        /* Device serial number, optional */
+                        if (len < 4) {
+                            BADF("Error: EVPD[Serial number] Inquiry buffer "
+                                 "size %d too small, %d needed\n", len, 4);
+                            goto fail;
+                        }
+
+                        DPRINTF("Inquiry EVPD[Serial number] buffer size %d\n", len);
+
+                        r->buf_len = 0;
+
+                        /* Supported page codes */
+                        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                            outbuf[r->buf_len++] = 5;
+                        } else {
+                            outbuf[r->buf_len++] = 0;
+                        }
+
+                        outbuf[r->buf_len++] = 0x80; // this page
+                        outbuf[r->buf_len++] = 0x00;
+                        outbuf[r->buf_len++] = 0x01; // 1 byte data follow
+
+                        outbuf[r->buf_len++] = '0';  // 1 byte data follow 
+                    }
+
+                    break;
+                case 0x83:
+                    {
+                        /* Device identification page, mandatory */
+                        int max_len = 255 - 8;
+                        int id_len = strlen(bdrv_get_device_name(s->bdrv));
+                        if (id_len > max_len)
+                            id_len = max_len;
+
+                        DPRINTF("Inquiry EVPD[Device identification] "
+                                "buffer size %d\n", len);
+                        r->buf_len = 0;
+                        if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                            outbuf[r->buf_len++] = 5;
+                        } else {
+                            outbuf[r->buf_len++] = 0;
+                        }
+
+                        outbuf[r->buf_len++] = 0x83; // this page
+                        outbuf[r->buf_len++] = 0x00;
+                        outbuf[r->buf_len++] = 3 + id_len;
+
+                        outbuf[r->buf_len++] = 0x2; // ASCII
+                        outbuf[r->buf_len++] = 0;   // not officially assigned
+                        outbuf[r->buf_len++] = 0;   // reserved
+                        outbuf[r->buf_len++] = id_len; // length of data following
+
+                        memcpy(&outbuf[r->buf_len],
+                               bdrv_get_device_name(s->bdrv), id_len);
+                        r->buf_len += id_len;
+                    }
+                    break;
+                default:
+                    BADF("Error: unsupported Inquiry (EVPD[%02X]) "
+                         "buffer size %d\n", page_code, len);
+                    goto fail;
+            }
+            /* done with EVPD */
+            break;
+        }
+        else {
+            /* Standard INQUIRY data */
+            if (buf[2] != 0) {
+                BADF("Error: Inquiry (STANDARD) page or code "
+                     "is non-zero [%02X]\n", buf[2]);
+                goto fail;
+            }
+
+            /* PAGE CODE == 0 */
+            if (len < 5) {
+                BADF("Error: Inquiry (STANDARD) buffer size %d "
+                     "is less than 5\n", len);
+                goto fail;
+            }
+
+            if (len < 36) {
+                BADF("Error: Inquiry (STANDARD) buffer size %d "
+                     "is less than 36 (TODO: only 5 required)\n", len);
+            }
+        }
+	memset(outbuf, 0, 36);
+	if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+	    outbuf[0] = 5;
+            outbuf[1] = 0x80;
+	    memcpy(&outbuf[16], "QEMU CD-ROM    ", 16);
+	} else {
+	    outbuf[0] = 0;
+	    memcpy(&outbuf[16], "QEMU HARDDISK  ", 16);
+	}
+	memcpy(&outbuf[8], "QEMU   ", 8);
+        memcpy(&outbuf[32], QEMU_VERSION, 4);
+        /* Identify device as SCSI-3 rev 1.
+           Some later commands are also implemented. */
+	outbuf[2] = 3;
+	outbuf[3] = 2; /* Format 2 */
+	outbuf[4] = 31;
+        /* Sync data transfer and TCQ.  */
+        outbuf[7] = 0x10 | (s->tcq ? 0x02 : 0);
+	r->buf_len = 36;
+	break;
+    case 0x16:
+        DPRINTF("Reserve(6)\n");
+        if (buf[1] & 1)
+            goto fail;
+        break;
+    case 0x17:
+        DPRINTF("Release(6)\n");
+        if (buf[1] & 1)
+            goto fail;
+        break;
+    case 0x1a:
+    case 0x5a:
+        {
+            uint8_t *p;
+            int page;
+
+            page = buf[2] & 0x3f;
+            DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
+            p = outbuf;
+            memset(p, 0, 4);
+            outbuf[1] = 0; /* Default media type.  */
+            outbuf[3] = 0; /* Block descriptor length.  */
+            if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+                outbuf[2] = 0x80; /* Readonly.  */
+            }
+            p += 4;
+            if (page == 4) {
+                int cylinders, heads, secs;
+
+                /* Rigid disk device geometry page. */
+                p[0] = 4;
+                p[1] = 0x16;
+                /* if a geometry hint is available, use it */
+                bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+                p[2] = (cylinders >> 16) & 0xff;
+                p[3] = (cylinders >> 8) & 0xff;
+                p[4] = cylinders & 0xff;
+                p[5] = heads & 0xff;
+                /* Write precomp start cylinder, disabled */
+                p[6] = (cylinders >> 16) & 0xff;
+                p[7] = (cylinders >> 8) & 0xff;
+                p[8] = cylinders & 0xff;
+                /* Reduced current start cylinder, disabled */
+                p[9] = (cylinders >> 16) & 0xff;
+                p[10] = (cylinders >> 8) & 0xff;
+                p[11] = cylinders & 0xff;
+                /* Device step rate [ns], 200ns */
+                p[12] = 0;
+                p[13] = 200;
+                /* Landing zone cylinder */
+                p[14] = 0xff;
+                p[15] =  0xff;
+                p[16] = 0xff;
+                /* Medium rotation rate [rpm], 5400 rpm */
+                p[20] = (5400 >> 8) & 0xff;
+                p[21] = 5400 & 0xff;
+                p += 0x16;
+            } else if (page == 5) {
+                int cylinders, heads, secs;
+
+                /* Flexible disk device geometry page. */
+                p[0] = 5;
+                p[1] = 0x1e;
+                /* Transfer rate [kbit/s], 5Mbit/s */
+                p[2] = 5000 >> 8;
+                p[3] = 5000 & 0xff;
+                /* if a geometry hint is available, use it */
+                bdrv_get_geometry_hint(s->bdrv, &cylinders, &heads, &secs);
+                p[4] = heads & 0xff;
+                p[5] = secs & 0xff;
+                p[6] = s->cluster_size * 2;
+                p[8] = (cylinders >> 8) & 0xff;
+                p[9] = cylinders & 0xff;
+                /* Write precomp start cylinder, disabled */
+                p[10] = (cylinders >> 8) & 0xff;
+                p[11] = cylinders & 0xff;
+                /* Reduced current start cylinder, disabled */
+                p[12] = (cylinders >> 8) & 0xff;
+                p[13] = cylinders & 0xff;
+                /* Device step rate [100us], 100us */
+                p[14] = 0;
+                p[15] = 1;
+                /* Device step pulse width [us], 1us */
+                p[16] = 1;
+                /* Device head settle delay [100us], 100us */
+                p[17] = 0;
+                p[18] = 1;
+                /* Motor on delay [0.1s], 0.1s */
+                p[19] = 1;
+                /* Motor off delay [0.1s], 0.1s */
+                p[20] = 1;
+                /* Medium rotation rate [rpm], 5400 rpm */
+                p[28] = (5400 >> 8) & 0xff;
+                p[29] = 5400 & 0xff;
+                p += 0x1e;
+            } else if ((page == 8 || page == 0x3f)) {
+                /* Caching page.  */
+                memset(p,0,20);
+                p[0] = 8;
+                p[1] = 0x12;
+                p[2] = 4; /* WCE */
+                p += 20;
+            }
+            if ((page == 0x3f || page == 0x2a)
+                    && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
+                /* CD Capabilities and Mechanical Status page. */
+                p[0] = 0x2a;
+                p[1] = 0x14;
+                p[2] = 3; // CD-R & CD-RW read
+                p[3] = 0; // Writing not supported
+                p[4] = 0x7f; /* Audio, composite, digital out,
+                                         mode 2 form 1&2, multi session */
+                p[5] = 0xff; /* CD DA, DA accurate, RW supported,
+                                         RW corrected, C2 errors, ISRC,
+                                         UPC, Bar code */
+                p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
+                /* Locking supported, jumper present, eject, tray */
+                p[7] = 0; /* no volume & mute control, no
+                                      changer */
+                p[8] = (50 * 176) >> 8; // 50x read speed
+                p[9] = (50 * 176) & 0xff;
+                p[10] = 0 >> 8; // No volume
+                p[11] = 0 & 0xff;
+                p[12] = 2048 >> 8; // 2M buffer
+                p[13] = 2048 & 0xff;
+                p[14] = (16 * 176) >> 8; // 16x read speed current
+                p[15] = (16 * 176) & 0xff;
+                p[18] = (16 * 176) >> 8; // 16x write speed
+                p[19] = (16 * 176) & 0xff;
+                p[20] = (16 * 176) >> 8; // 16x write speed current
+                p[21] = (16 * 176) & 0xff;
+                p += 22;
+            }
+            r->buf_len = p - outbuf;
+            outbuf[0] = r->buf_len - 4;
+            if (r->buf_len > len)
+                r->buf_len = len;
+        }
+        break;
+    case 0x1b:
+        DPRINTF("Start Stop Unit\n");
+	break;
+    case 0x1e:
+        DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
+        bdrv_set_locked(s->bdrv, buf[4] & 1);
+	break;
+    case 0x25:
+	DPRINTF("Read Capacity\n");
+        /* The normal LEN field for this command is zero.  */
+	memset(outbuf, 0, 8);
+	bdrv_get_geometry(s->bdrv, &nb_sectors);
+        /* Returned value is the address of the last sector.  */
+        if (nb_sectors) {
+            nb_sectors--;
+            outbuf[0] = (nb_sectors >> 24) & 0xff;
+            outbuf[1] = (nb_sectors >> 16) & 0xff;
+            outbuf[2] = (nb_sectors >> 8) & 0xff;
+            outbuf[3] = nb_sectors & 0xff;
+            outbuf[4] = 0;
+            outbuf[5] = 0;
+            outbuf[6] = s->cluster_size * 2;
+            outbuf[7] = 0;
+            r->buf_len = 8;
+        } else {
+            scsi_command_complete(r, SENSE_NOT_READY);
+            return 0;
+        }
+	break;
+    case 0x08:
+    case 0x28:
+        DPRINTF("Read (sector %d, count %d)\n", lba, len);
+        r->sector = lba * s->cluster_size;
+        r->sector_count = len * s->cluster_size;
+        break;
+    case 0x0a:
+    case 0x2a:
+        DPRINTF("Write (sector %d, count %d)\n", lba, len);
+        r->sector = lba * s->cluster_size;
+        r->sector_count = len * s->cluster_size;
+        is_write = 1;
+        break;
+    case 0x35:
+        DPRINTF("Synchronise cache (sector %d, count %d)\n", lba, len);
+        bdrv_flush(s->bdrv);
+        break;
+    case 0x43:
+        {
+            int start_track, format, msf, toclen;
+
+            msf = buf[1] & 2;
+            format = buf[2] & 0xf;
+            start_track = buf[6];
+            bdrv_get_geometry(s->bdrv, &nb_sectors);
+            DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
+            switch(format) {
+            case 0:
+                toclen = cdrom_read_toc(nb_sectors, outbuf, msf, start_track);
+                break;
+            case 1:
+                /* multi session : only a single session defined */
+                toclen = 12;
+                memset(outbuf, 0, 12);
+                outbuf[1] = 0x0a;
+                outbuf[2] = 0x01;
+                outbuf[3] = 0x01;
+                break;
+            case 2:
+                toclen = cdrom_read_toc_raw(nb_sectors, outbuf, msf, start_track);
+                break;
+            default:
+                goto error_cmd;
+            }
+            if (toclen > 0) {
+                if (len > toclen)
+                  len = toclen;
+                r->buf_len = len;
+                break;
+            }
+        error_cmd:
+            DPRINTF("Read TOC error\n");
+            goto fail;
+        }
+    case 0x46:
+        DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
+        memset(outbuf, 0, 8);
+        /* ??? This should probably return much more information.  For now
+           just return the basic header indicating the CD-ROM profile.  */
+        outbuf[7] = 8; // CD-ROM
+        r->buf_len = 8;
+        break;
+    case 0x56:
+        DPRINTF("Reserve(10)\n");
+        if (buf[1] & 3)
+            goto fail;
+        break;
+    case 0x57:
+        DPRINTF("Release(10)\n");
+        if (buf[1] & 3)
+            goto fail;
+        break;
+    case 0xa0:
+        DPRINTF("Report LUNs (len %d)\n", len);
+        if (len < 16)
+            goto fail;
+        memset(outbuf, 0, 16);
+        outbuf[3] = 8;
+        r->buf_len = 16;
+        break;
+    default:
+	DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
+    fail:
+        scsi_command_complete(r, SENSE_ILLEGAL_REQUEST);
+	return 0;
+    }
+    if (r->sector_count == 0 && r->buf_len == 0) {
+        scsi_command_complete(r, SENSE_NO_SENSE);
+    }
+    len = r->sector_count * 512 + r->buf_len;
+    if (is_write) {
+        return -len;
+    } else {
+        if (!r->sector_count)
+            r->sector_count = -1;
+        return len;
+    }
+}
+
+static void scsi_destroy(SCSIDevice *d)
+{
+    qemu_free(d->state);
+    qemu_free(d);
+}
+
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
+                           scsi_completionfn completion, void *opaque)
+{
+    SCSIDevice *d;
+    SCSIDeviceState *s;
+
+    s = (SCSIDeviceState *)qemu_mallocz(sizeof(SCSIDeviceState));
+    s->bdrv = bdrv;
+    s->tcq = tcq;
+    s->completion = completion;
+    s->opaque = opaque;
+    if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
+        s->cluster_size = 4;
+    } else {
+        s->cluster_size = 1;
+    }
+
+    d = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
+    d->state = s;
+    d->destroy = scsi_destroy;
+    d->send_command = scsi_send_command;
+    d->read_data = scsi_read_data;
+    d->write_data = scsi_write_data;
+    d->cancel_io = scsi_cancel_io;
+    d->get_buf = scsi_get_buf;
+
+    return d;
+}
diff --git a/hw/scsi-disk.h b/hw/scsi-disk.h
new file mode 100644
index 0000000..f42212b
--- /dev/null
+++ b/hw/scsi-disk.h
@@ -0,0 +1,36 @@
+#ifndef SCSI_DISK_H
+#define SCSI_DISK_H
+
+/* scsi-disk.c */
+enum scsi_reason {
+    SCSI_REASON_DONE, /* Command complete.  */
+    SCSI_REASON_DATA  /* Transfer complete, more data required.  */
+};
+
+typedef struct SCSIDeviceState SCSIDeviceState;
+typedef struct SCSIDevice SCSIDevice;
+typedef void (*scsi_completionfn)(void *opaque, int reason, uint32_t tag,
+                                  uint32_t arg);
+
+struct SCSIDevice
+{
+    SCSIDeviceState *state;
+    void (*destroy)(SCSIDevice *s);
+    int32_t (*send_command)(SCSIDevice *s, uint32_t tag, uint8_t *buf,
+                            int lun);
+    void (*read_data)(SCSIDevice *s, uint32_t tag);
+    int (*write_data)(SCSIDevice *s, uint32_t tag);
+    void (*cancel_io)(SCSIDevice *s, uint32_t tag);
+    uint8_t *(*get_buf)(SCSIDevice *s, uint32_t tag);
+};
+
+SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, int tcq,
+                           scsi_completionfn completion, void *opaque);
+SCSIDevice *scsi_generic_init(BlockDriverState *bdrv, int tcq,
+                           scsi_completionfn completion, void *opaque);
+
+/* cdrom.c */
+int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
+int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
+
+#endif
diff --git a/hw/sd.h b/hw/sd.h
new file mode 100644
index 0000000..f310062
--- /dev/null
+++ b/hw/sd.h
@@ -0,0 +1,83 @@
+/*
+ *  include/linux/mmc/sd.h
+ *
+ *  Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef MMC_SD_H
+#define MMC_SD_H
+
+/* SD commands                           type  argument     response */
+  /* class 0 */
+/* This is basically the same command as for MMC with some quirks. */
+#define SD_SEND_RELATIVE_ADDR     3   /* bcr                     R6  */
+#define SD_SEND_IF_COND           8   /* bcr  [11:0] See below   R7  */
+
+  /* class 10 */
+#define SD_SWITCH                 6   /* adtc [31:0] See below   R1  */
+
+  /* Application commands */
+#define SD_APP_SET_BUS_WIDTH      6   /* ac   [1:0] bus width    R1  */
+#define SD_APP_SEND_NUM_WR_BLKS  22   /* adtc                    R1  */
+#define SD_APP_OP_COND           41   /* bcr  [31:0] OCR         R3  */
+#define SD_APP_SEND_SCR          51   /* adtc                    R1  */
+
+/*
+ * SD_SWITCH argument format:
+ *
+ *      [31] Check (0) or switch (1)
+ *      [30:24] Reserved (0)
+ *      [23:20] Function group 6
+ *      [19:16] Function group 5
+ *      [15:12] Function group 4
+ *      [11:8] Function group 3
+ *      [7:4] Function group 2
+ *      [3:0] Function group 1
+ */
+
+/*
+ * SD_SEND_IF_COND argument format:
+ *
+ *	[31:12] Reserved (0)
+ *	[11:8] Host Voltage Supply Flags
+ *	[7:0] Check Pattern (0xAA)
+ */
+
+/*
+ * SCR field definitions
+ */
+
+#define SCR_SPEC_VER_0		0	/* Implements system specification 1.0 - 1.01 */
+#define SCR_SPEC_VER_1		1	/* Implements system specification 1.10 */
+#define SCR_SPEC_VER_2		2	/* Implements system specification 2.00 */
+
+/*
+ * SD bus widths
+ */
+#define SD_BUS_WIDTH_1		0
+#define SD_BUS_WIDTH_4		2
+
+/*
+ * SD_SWITCH mode
+ */
+#define SD_SWITCH_CHECK		0
+#define SD_SWITCH_SET		1
+
+/*
+ * SD_SWITCH function groups
+ */
+#define SD_SWITCH_GRP_ACCESS	0
+
+/*
+ * SD_SWITCH access modes
+ */
+#define SD_SWITCH_ACCESS_DEF	0
+#define SD_SWITCH_ACCESS_HS	1
+
+#endif
+
diff --git a/hw/smc91c111.c b/hw/smc91c111.c
new file mode 100644
index 0000000..410051d
--- /dev/null
+++ b/hw/smc91c111.c
@@ -0,0 +1,715 @@
+/*
+ * SMSC 91C111 Ethernet interface emulation
+ *
+ * Copyright (c) 2005 CodeSourcery, LLC.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL
+ */
+
+#include "hw.h"
+#include "net.h"
+#include "devices.h"
+/* For crc32 */
+#include <zlib.h>
+
+/* Number of 2k memory pages available.  */
+#define NUM_PACKETS 4
+
+typedef struct {
+    uint32_t base;
+    VLANClientState *vc;
+    uint16_t tcr;
+    uint16_t rcr;
+    uint16_t cr;
+    uint16_t ctr;
+    uint16_t gpr;
+    uint16_t ptr;
+    uint16_t ercv;
+    qemu_irq irq;
+    int bank;
+    int packet_num;
+    int tx_alloc;
+    /* Bitmask of allocated packets.  */
+    int allocated;
+    int tx_fifo_len;
+    int tx_fifo[NUM_PACKETS];
+    int rx_fifo_len;
+    int rx_fifo[NUM_PACKETS];
+    int tx_fifo_done_len;
+    int tx_fifo_done[NUM_PACKETS];
+    /* Packet buffer memory.  */
+    uint8_t data[NUM_PACKETS][2048];
+    uint8_t int_level;
+    uint8_t int_mask;
+    uint8_t macaddr[6];
+} smc91c111_state;
+
+#define RCR_SOFT_RST  0x8000
+#define RCR_STRIP_CRC 0x0200
+#define RCR_RXEN      0x0100
+
+#define TCR_EPH_LOOP  0x2000
+#define TCR_NOCRC     0x0100
+#define TCR_PAD_EN    0x0080
+#define TCR_FORCOL    0x0004
+#define TCR_LOOP      0x0002
+#define TCR_TXEN      0x0001
+
+#define INT_MD        0x80
+#define INT_ERCV      0x40
+#define INT_EPH       0x20
+#define INT_RX_OVRN   0x10
+#define INT_ALLOC     0x08
+#define INT_TX_EMPTY  0x04
+#define INT_TX        0x02
+#define INT_RCV       0x01
+
+#define CTR_AUTO_RELEASE  0x0800
+#define CTR_RELOAD        0x0002
+#define CTR_STORE         0x0001
+
+#define RS_ALGNERR      0x8000
+#define RS_BRODCAST     0x4000
+#define RS_BADCRC       0x2000
+#define RS_ODDFRAME     0x1000
+#define RS_TOOLONG      0x0800
+#define RS_TOOSHORT     0x0400
+#define RS_MULTICAST    0x0001
+
+/* Update interrupt status.  */
+static void smc91c111_update(smc91c111_state *s)
+{
+    int level;
+
+    if (s->tx_fifo_len == 0)
+        s->int_level |= INT_TX_EMPTY;
+    if (s->tx_fifo_done_len != 0)
+        s->int_level |= INT_TX;
+    level = (s->int_level & s->int_mask) != 0;
+    qemu_set_irq(s->irq, level);
+}
+
+/* Try to allocate a packet.  Returns 0x80 on failure.  */
+static int smc91c111_allocate_packet(smc91c111_state *s)
+{
+    int i;
+    if (s->allocated == (1 << NUM_PACKETS) - 1) {
+        return 0x80;
+    }
+
+    for (i = 0; i < NUM_PACKETS; i++) {
+        if ((s->allocated & (1 << i)) == 0)
+            break;
+    }
+    s->allocated |= 1 << i;
+    return i;
+}
+
+
+/* Process a pending TX allocate.  */
+static void smc91c111_tx_alloc(smc91c111_state *s)
+{
+    s->tx_alloc = smc91c111_allocate_packet(s);
+    if (s->tx_alloc == 0x80)
+        return;
+    s->int_level |= INT_ALLOC;
+    smc91c111_update(s);
+}
+
+/* Remove and item from the RX FIFO.  */
+static void smc91c111_pop_rx_fifo(smc91c111_state *s)
+{
+    int i;
+
+    s->rx_fifo_len--;
+    if (s->rx_fifo_len) {
+        for (i = 0; i < s->rx_fifo_len; i++)
+            s->rx_fifo[i] = s->rx_fifo[i + 1];
+        s->int_level |= INT_RCV;
+    } else {
+        s->int_level &= ~INT_RCV;
+    }
+    smc91c111_update(s);
+}
+
+/* Remove an item from the TX completion FIFO.  */
+static void smc91c111_pop_tx_fifo_done(smc91c111_state *s)
+{
+    int i;
+
+    if (s->tx_fifo_done_len == 0)
+        return;
+    s->tx_fifo_done_len--;
+    for (i = 0; i < s->tx_fifo_done_len; i++)
+        s->tx_fifo_done[i] = s->tx_fifo_done[i + 1];
+}
+
+/* Release the memory allocated to a packet.  */
+static void smc91c111_release_packet(smc91c111_state *s, int packet)
+{
+    s->allocated &= ~(1 << packet);
+    if (s->tx_alloc == 0x80)
+        smc91c111_tx_alloc(s);
+}
+
+/* Flush the TX FIFO.  */
+static void smc91c111_do_tx(smc91c111_state *s)
+{
+    int i;
+    int len;
+    int control;
+    int add_crc;
+    int packetnum;
+    uint8_t *p;
+
+    if ((s->tcr & TCR_TXEN) == 0)
+        return;
+    if (s->tx_fifo_len == 0)
+        return;
+    for (i = 0; i < s->tx_fifo_len; i++) {
+        packetnum = s->tx_fifo[i];
+        p = &s->data[packetnum][0];
+        /* Set status word.  */
+        *(p++) = 0x01;
+        *(p++) = 0x40;
+        len = *(p++);
+        len |= ((int)*(p++)) << 8;
+        len -= 6;
+        control = p[len + 1];
+        if (control & 0x20)
+            len++;
+        /* ??? This overwrites the data following the buffer.
+           Don't know what real hardware does.  */
+        if (len < 64 && (s->tcr & TCR_PAD_EN)) {
+            memset(p + len, 0, 64 - len);
+            len = 64;
+        }
+#if 0
+        /* The card is supposed to append the CRC to the frame.  However
+           none of the other network traffic has the CRC appended.
+           Suspect this is low level ethernet detail we don't need to worry
+           about.  */
+        add_crc = (control & 0x10) || (s->tcr & TCR_NOCRC) == 0;
+        if (add_crc) {
+            uint32_t crc;
+
+            crc = crc32(~0, p, len);
+            memcpy(p + len, &crc, 4);
+            len += 4;
+        }
+#else
+        add_crc = 0;
+#endif
+        if (s->ctr & CTR_AUTO_RELEASE)
+            /* Race?  */
+            smc91c111_release_packet(s, packetnum);
+        else if (s->tx_fifo_done_len < NUM_PACKETS)
+            s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum;
+        qemu_send_packet(s->vc, p, len);
+    }
+    s->tx_fifo_len = 0;
+    smc91c111_update(s);
+}
+
+/* Add a packet to the TX FIFO.  */
+static void smc91c111_queue_tx(smc91c111_state *s, int packet)
+{
+    if (s->tx_fifo_len == NUM_PACKETS)
+        return;
+    s->tx_fifo[s->tx_fifo_len++] = packet;
+    smc91c111_do_tx(s);
+}
+
+static void smc91c111_reset(smc91c111_state *s)
+{
+    s->bank = 0;
+    s->tx_fifo_len = 0;
+    s->tx_fifo_done_len = 0;
+    s->rx_fifo_len = 0;
+    s->allocated = 0;
+    s->packet_num = 0;
+    s->tx_alloc = 0;
+    s->tcr = 0;
+    s->rcr = 0;
+    s->cr = 0xa0b1;
+    s->ctr = 0x1210;
+    s->ptr = 0;
+    s->ercv = 0x1f;
+    s->int_level = INT_TX_EMPTY;
+    s->int_mask = 0;
+    smc91c111_update(s);
+}
+
+#define SET_LOW(name, val) s->name = (s->name & 0xff00) | val
+#define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8)
+
+static void smc91c111_writeb(void *opaque, target_phys_addr_t offset,
+                             uint32_t value)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+
+    offset -= s->base;
+    if (offset == 14) {
+        s->bank = value;
+        return;
+    }
+    if (offset == 15)
+        return;
+    switch (s->bank) {
+    case 0:
+        switch (offset) {
+        case 0: /* TCR */
+            SET_LOW(tcr, value);
+            return;
+        case 1:
+            SET_HIGH(tcr, value);
+            return;
+        case 4: /* RCR */
+            SET_LOW(rcr, value);
+            return;
+        case 5:
+            SET_HIGH(rcr, value);
+            if (s->rcr & RCR_SOFT_RST)
+                smc91c111_reset(s);
+            return;
+        case 10: case 11: /* RPCR */
+            /* Ignored */
+            return;
+        }
+        break;
+
+    case 1:
+        switch (offset) {
+        case 0: /* CONFIG */
+            SET_LOW(cr, value);
+            return;
+        case 1:
+            SET_HIGH(cr,value);
+            return;
+        case 2: case 3: /* BASE */
+        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+            /* Not implemented.  */
+            return;
+        case 10: /* Genral Purpose */
+            SET_LOW(gpr, value);
+            return;
+        case 11:
+            SET_HIGH(gpr, value);
+            return;
+        case 12: /* Control */
+            if (value & 1)
+                fprintf(stderr, "smc91c111:EEPROM store not implemented\n");
+            if (value & 2)
+                fprintf(stderr, "smc91c111:EEPROM reload not implemented\n");
+            value &= ~3;
+            SET_LOW(ctr, value);
+            return;
+        case 13:
+            SET_HIGH(ctr, value);
+            return;
+        }
+        break;
+
+    case 2:
+        switch (offset) {
+        case 0: /* MMU Command */
+            switch (value >> 5) {
+            case 0: /* no-op */
+                break;
+            case 1: /* Allocate for TX.  */
+                s->tx_alloc = 0x80;
+                s->int_level &= ~INT_ALLOC;
+                smc91c111_update(s);
+                smc91c111_tx_alloc(s);
+                break;
+            case 2: /* Reset MMU.  */
+                s->allocated = 0;
+                s->tx_fifo_len = 0;
+                s->tx_fifo_done_len = 0;
+                s->rx_fifo_len = 0;
+                s->tx_alloc = 0;
+                break;
+            case 3: /* Remove from RX FIFO.  */
+                smc91c111_pop_rx_fifo(s);
+                break;
+            case 4: /* Remove from RX FIFO and release.  */
+                if (s->rx_fifo_len > 0) {
+                    smc91c111_release_packet(s, s->rx_fifo[0]);
+                }
+                smc91c111_pop_rx_fifo(s);
+                break;
+            case 5: /* Release.  */
+                smc91c111_release_packet(s, s->packet_num);
+                break;
+            case 6: /* Add to TX FIFO.  */
+                smc91c111_queue_tx(s, s->packet_num);
+                break;
+            case 7: /* Reset TX FIFO.  */
+                s->tx_fifo_len = 0;
+                s->tx_fifo_done_len = 0;
+                break;
+            }
+            return;
+        case 1:
+            /* Ignore.  */
+            return;
+        case 2: /* Packet Number Register */
+            s->packet_num = value;
+            return;
+        case 3: case 4: case 5:
+            /* Should be readonly, but linux writes to them anyway. Ignore.  */
+            return;
+        case 6: /* Pointer */
+            SET_LOW(ptr, value);
+            return;
+        case 7:
+            SET_HIGH(ptr, value);
+            return;
+        case 8: case 9: case 10: case 11: /* Data */
+            {
+                int p;
+                int n;
+
+                if (s->ptr & 0x8000)
+                    n = s->rx_fifo[0];
+                else
+                    n = s->packet_num;
+                p = s->ptr & 0x07ff;
+                if (s->ptr & 0x4000) {
+                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff);
+                } else {
+                    p += (offset & 3);
+                }
+                s->data[n][p] = value;
+            }
+            return;
+        case 12: /* Interrupt ACK.  */
+            s->int_level &= ~(value & 0xd6);
+            if (value & INT_TX)
+                smc91c111_pop_tx_fifo_done(s);
+            smc91c111_update(s);
+            return;
+        case 13: /* Interrupt mask.  */
+            s->int_mask = value;
+            smc91c111_update(s);
+            return;
+        }
+        break;;
+
+    case 3:
+        switch (offset) {
+        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+            /* Multicast table.  */
+            /* Not implemented.  */
+            return;
+        case 8: case 9: /* Management Interface.  */
+            /* Not implemented.  */
+            return;
+        case 12: /* Early receive.  */
+            s->ercv = value & 0x1f;
+        case 13:
+            /* Ignore.  */
+            return;
+        }
+        break;
+    }
+    cpu_abort (cpu_single_env, "smc91c111_write: Bad reg %d:%x\n",
+               s->bank, (int)offset);
+}
+
+static uint32_t smc91c111_readb(void *opaque, target_phys_addr_t offset)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+
+    offset -= s->base;
+    if (offset == 14) {
+        return s->bank;
+    }
+    if (offset == 15)
+        return 0x33;
+    switch (s->bank) {
+    case 0:
+        switch (offset) {
+        case 0: /* TCR */
+            return s->tcr & 0xff;
+        case 1:
+            return s->tcr >> 8;
+        case 2: /* EPH Status */
+            return 0;
+        case 3:
+            return 0x40;
+        case 4: /* RCR */
+            return s->rcr & 0xff;
+        case 5:
+            return s->rcr >> 8;
+        case 6: /* Counter */
+        case 7:
+            /* Not implemented.  */
+            return 0;
+        case 8: /* Memory size.  */
+            return NUM_PACKETS;
+        case 9: /* Free memory available.  */
+            {
+                int i;
+                int n;
+                n = 0;
+                for (i = 0; i < NUM_PACKETS; i++) {
+                    if (s->allocated & (1 << i))
+                        n++;
+                }
+                return n;
+            }
+        case 10: case 11: /* RPCR */
+            /* Not implemented.  */
+            return 0;
+        }
+        break;
+
+    case 1:
+        switch (offset) {
+        case 0: /* CONFIG */
+            return s->cr & 0xff;
+        case 1:
+            return s->cr >> 8;
+        case 2: case 3: /* BASE */
+            /* Not implemented.  */
+            return 0;
+        case 4: case 5: case 6: case 7: case 8: case 9: /* IA */
+            return s->macaddr[offset - 4];
+        case 10: /* General Purpose */
+            return s->gpr & 0xff;
+        case 11:
+            return s->gpr >> 8;
+        case 12: /* Control */
+            return s->ctr & 0xff;
+        case 13:
+            return s->ctr >> 8;
+        }
+        break;
+
+    case 2:
+        switch (offset) {
+        case 0: case 1: /* MMUCR Busy bit.  */
+            return 0;
+        case 2: /* Packet Number.  */
+            return s->packet_num;
+        case 3: /* Allocation Result.  */
+            return s->tx_alloc;
+        case 4: /* TX FIFO */
+            if (s->tx_fifo_done_len == 0)
+                return 0x80;
+            else
+                return s->tx_fifo_done[0];
+        case 5: /* RX FIFO */
+            if (s->rx_fifo_len == 0)
+                return 0x80;
+            else
+                return s->rx_fifo[0];
+        case 6: /* Pointer */
+            return s->ptr & 0xff;
+        case 7:
+            return (s->ptr >> 8) & 0xf7;
+        case 8: case 9: case 10: case 11: /* Data */
+            {
+                int p;
+                int n;
+
+                if (s->ptr & 0x8000)
+                    n = s->rx_fifo[0];
+                else
+                    n = s->packet_num;
+                p = s->ptr & 0x07ff;
+                if (s->ptr & 0x4000) {
+                    s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff);
+                } else {
+                    p += (offset & 3);
+                }
+                return s->data[n][p];
+            }
+        case 12: /* Interrupt status.  */
+            return s->int_level;
+        case 13: /* Interrupt mask.  */
+            return s->int_mask;
+        }
+        break;
+
+    case 3:
+        switch (offset) {
+        case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+            /* Multicast table.  */
+            /* Not implemented.  */
+            return 0;
+        case 8: /* Management Interface.  */
+            /* Not implemented.  */
+            return 0x30;
+        case 9:
+            return 0x33;
+        case 10: /* Revision.  */
+            return 0x91;
+        case 11:
+            return 0x33;
+        case 12:
+            return s->ercv;
+        case 13:
+            return 0;
+        }
+        break;
+    }
+    cpu_abort (cpu_single_env, "smc91c111_read: Bad reg %d:%x\n",
+               s->bank, (int)offset);
+    return 0;
+}
+
+static void smc91c111_writew(void *opaque, target_phys_addr_t offset,
+                             uint32_t value)
+{
+    smc91c111_writeb(opaque, offset, value & 0xff);
+    smc91c111_writeb(opaque, offset + 1, value >> 8);
+}
+
+static void smc91c111_writel(void *opaque, target_phys_addr_t offset,
+                             uint32_t value)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+    /* 32-bit writes to offset 0xc only actually write to the bank select
+       register (offset 0xe)  */
+    if (offset != s->base + 0xc)
+        smc91c111_writew(opaque, offset, value & 0xffff);
+    smc91c111_writew(opaque, offset + 2, value >> 16);
+}
+
+static uint32_t smc91c111_readw(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t val;
+    val = smc91c111_readb(opaque, offset);
+    val |= smc91c111_readb(opaque, offset + 1) << 8;
+    return val;
+}
+
+static uint32_t smc91c111_readl(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t val;
+    val = smc91c111_readw(opaque, offset);
+    val |= smc91c111_readw(opaque, offset + 2) << 16;
+    return val;
+}
+
+static int smc91c111_can_receive(void *opaque)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+
+    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+        return 1;
+    if (s->allocated == (1 << NUM_PACKETS) - 1)
+        return 0;
+    return 1;
+}
+
+static void smc91c111_receive(void *opaque, const uint8_t *buf, int size)
+{
+    smc91c111_state *s = (smc91c111_state *)opaque;
+    int status;
+    int packetsize;
+    uint32_t crc;
+    int packetnum;
+    uint8_t *p;
+
+    if ((s->rcr & RCR_RXEN) == 0 || (s->rcr & RCR_SOFT_RST))
+        return;
+    /* Short packets are padded with zeros.  Receiving a packet
+       < 64 bytes long is considered an error condition.  */
+    if (size < 64)
+        packetsize = 64;
+    else
+        packetsize = (size & ~1);
+    packetsize += 6;
+    crc = (s->rcr & RCR_STRIP_CRC) == 0;
+    if (crc)
+        packetsize += 4;
+    /* TODO: Flag overrun and receive errors.  */
+    if (packetsize > 2048)
+        return;
+    packetnum = smc91c111_allocate_packet(s);
+    if (packetnum == 0x80)
+        return;
+    s->rx_fifo[s->rx_fifo_len++] = packetnum;
+
+    p = &s->data[packetnum][0];
+    /* ??? Multicast packets?  */
+    status = 0;
+    if (size > 1518)
+        status |= RS_TOOLONG;
+    if (size & 1)
+        status |= RS_ODDFRAME;
+    *(p++) = status & 0xff;
+    *(p++) = status >> 8;
+    *(p++) = packetsize & 0xff;
+    *(p++) = packetsize >> 8;
+    memcpy(p, buf, size & ~1);
+    p += (size & ~1);
+    /* Pad short packets.  */
+    if (size < 64) {
+        int pad;
+
+        if (size & 1)
+            *(p++) = buf[size - 1];
+        pad = 64 - size;
+        memset(p, 0, pad);
+        p += pad;
+        size = 64;
+    }
+    /* It's not clear if the CRC should go before or after the last byte in
+       odd sized packets.  Linux disables the CRC, so that's no help.
+       The pictures in the documentation show the CRC aligned on a 16-bit
+       boundary before the last odd byte, so that's what we do.  */
+    if (crc) {
+        crc = crc32(~0, buf, size);
+        *(p++) = crc & 0xff; crc >>= 8;
+        *(p++) = crc & 0xff; crc >>= 8;
+        *(p++) = crc & 0xff; crc >>= 8;
+        *(p++) = crc & 0xff; crc >>= 8;
+    }
+    if (size & 1) {
+        *(p++) = buf[size - 1];
+        *(p++) = 0x60;
+    } else {
+        *(p++) = 0;
+        *(p++) = 0x40;
+    }
+    /* TODO: Raise early RX interrupt?  */
+    s->int_level |= INT_RCV;
+    smc91c111_update(s);
+}
+
+static CPUReadMemoryFunc *smc91c111_readfn[] = {
+    smc91c111_readb,
+    smc91c111_readw,
+    smc91c111_readl
+};
+
+static CPUWriteMemoryFunc *smc91c111_writefn[] = {
+    smc91c111_writeb,
+    smc91c111_writew,
+    smc91c111_writel
+};
+
+void smc91c111_init(NICInfo *nd, uint32_t base, qemu_irq irq)
+{
+    smc91c111_state *s;
+    int iomemtype;
+
+    s = (smc91c111_state *)qemu_mallocz(sizeof(smc91c111_state));
+    iomemtype = cpu_register_io_memory(0, smc91c111_readfn,
+                                       smc91c111_writefn, s);
+    cpu_register_physical_memory(base, 16, iomemtype);
+    s->base = base;
+    s->irq = irq;
+    memcpy(s->macaddr, nd->macaddr, 6);
+
+    smc91c111_reset(s);
+
+    s->vc = qemu_new_vlan_client(nd->vlan, smc91c111_receive,
+                                 smc91c111_can_receive, s);
+    /* ??? Save/restore.  */
+}
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
new file mode 100644
index 0000000..406c9ab
--- /dev/null
+++ b/hw/usb-hid.c
@@ -0,0 +1,896 @@
+/*
+ * QEMU USB HID devices
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ * Copyright (c) 2007 OpenMoko, Inc.  (andrew@openedhand.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "console.h"
+#include "usb.h"
+
+/* HID interface requests */
+#define GET_REPORT   0xa101
+#define GET_IDLE     0xa102
+#define GET_PROTOCOL 0xa103
+#define SET_REPORT   0x2109
+#define SET_IDLE     0x210a
+#define SET_PROTOCOL 0x210b
+
+/* HID descriptor types */
+#define USB_DT_HID    0x21
+#define USB_DT_REPORT 0x22
+#define USB_DT_PHY    0x23
+
+#define USB_MOUSE     1
+#define USB_TABLET    2
+#define USB_KEYBOARD  3
+
+typedef struct USBMouseState {
+    int dx, dy, dz, buttons_state;
+    int x, y;
+    int mouse_grabbed;
+    QEMUPutMouseEntry *eh_entry;
+} USBMouseState;
+
+typedef struct USBKeyboardState {
+    uint16_t modifiers;
+    uint8_t leds;
+    uint8_t key[16];
+    int keys;
+} USBKeyboardState;
+
+typedef struct USBHIDState {
+    USBDevice dev;
+    union {
+        USBMouseState ptr;
+        USBKeyboardState kbd;
+    };
+    int kind;
+    int protocol;
+    int idle;
+    int changed;
+} USBHIDState;
+
+/* mostly the same values as the Bochs USB Mouse device */
+static const uint8_t qemu_mouse_dev_descriptor[] = {
+	0x12,       /*  u8 bLength; */
+	0x01,       /*  u8 bDescriptorType; Device */
+	0x00, 0x01, /*  u16 bcdUSB; v1.0 */
+
+	0x00,	    /*  u8  bDeviceClass; */
+	0x00,	    /*  u8  bDeviceSubClass; */
+	0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+	0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
+
+	0x27, 0x06, /*  u16 idVendor; */
+ 	0x01, 0x00, /*  u16 idProduct; */
+	0x00, 0x00, /*  u16 bcdDevice */
+
+	0x03,       /*  u8  iManufacturer; */
+	0x02,       /*  u8  iProduct; */
+	0x01,       /*  u8  iSerialNumber; */
+	0x01        /*  u8  bNumConfigurations; */
+};
+
+static const uint8_t qemu_mouse_config_descriptor[] = {
+	/* one configuration */
+	0x09,       /*  u8  bLength; */
+	0x02,       /*  u8  bDescriptorType; Configuration */
+	0x22, 0x00, /*  u16 wTotalLength; */
+	0x01,       /*  u8  bNumInterfaces; (1) */
+	0x01,       /*  u8  bConfigurationValue; */
+	0x04,       /*  u8  iConfiguration; */
+	0xa0,       /*  u8  bmAttributes;
+				 Bit 7: must be set,
+				     6: Self-powered,
+				     5: Remote wakeup,
+				     4..0: resvd */
+	50,         /*  u8  MaxPower; */
+
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  u8  if_bLength; */
+	0x04,       /*  u8  if_bDescriptorType; Interface */
+	0x00,       /*  u8  if_bInterfaceNumber; */
+	0x00,       /*  u8  if_bAlternateSetting; */
+	0x01,       /*  u8  if_bNumEndpoints; */
+	0x03,       /*  u8  if_bInterfaceClass; */
+	0x01,       /*  u8  if_bInterfaceSubClass; */
+	0x02,       /*  u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x07,       /*  u8  if_iInterface; */
+
+        /* HID descriptor */
+        0x09,        /*  u8  bLength; */
+        0x21,        /*  u8 bDescriptorType; */
+        0x01, 0x00,  /*  u16 HID_class */
+        0x00,        /*  u8 country_code */
+        0x01,        /*  u8 num_descriptors */
+        0x22,        /*  u8 type; Report */
+        52, 0,       /*  u16 len */
+
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  u8  ep_bLength; */
+	0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  u8  ep_bmAttributes; Interrupt */
+ 	0x04, 0x00, /*  u16 ep_wMaxPacketSize; */
+	0x0a,       /*  u8  ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_tablet_config_descriptor[] = {
+	/* one configuration */
+	0x09,       /*  u8  bLength; */
+	0x02,       /*  u8  bDescriptorType; Configuration */
+	0x22, 0x00, /*  u16 wTotalLength; */
+	0x01,       /*  u8  bNumInterfaces; (1) */
+	0x01,       /*  u8  bConfigurationValue; */
+	0x05,       /*  u8  iConfiguration; */
+	0xa0,       /*  u8  bmAttributes;
+				 Bit 7: must be set,
+				     6: Self-powered,
+				     5: Remote wakeup,
+				     4..0: resvd */
+	50,         /*  u8  MaxPower; */
+
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  u8  if_bLength; */
+	0x04,       /*  u8  if_bDescriptorType; Interface */
+	0x00,       /*  u8  if_bInterfaceNumber; */
+	0x00,       /*  u8  if_bAlternateSetting; */
+	0x01,       /*  u8  if_bNumEndpoints; */
+	0x03,       /*  u8  if_bInterfaceClass; */
+	0x01,       /*  u8  if_bInterfaceSubClass; */
+	0x02,       /*  u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x07,       /*  u8  if_iInterface; */
+
+        /* HID descriptor */
+        0x09,        /*  u8  bLength; */
+        0x21,        /*  u8 bDescriptorType; */
+        0x01, 0x00,  /*  u16 HID_class */
+        0x00,        /*  u8 country_code */
+        0x01,        /*  u8 num_descriptors */
+        0x22,        /*  u8 type; Report */
+        74, 0,       /*  u16 len */
+
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  u8  ep_bLength; */
+	0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  u8  ep_bmAttributes; Interrupt */
+ 	0x08, 0x00, /*  u16 ep_wMaxPacketSize; */
+	0x0a,       /*  u8  ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_keyboard_config_descriptor[] = {
+    /* one configuration */
+    0x09,		/*  u8  bLength; */
+    USB_DT_CONFIG,	/*  u8  bDescriptorType; Configuration */
+    0x22, 0x00,		/*  u16 wTotalLength; */
+    0x01,		/*  u8  bNumInterfaces; (1) */
+    0x01,		/*  u8  bConfigurationValue; */
+    0x06,		/*  u8  iConfiguration; */
+    0xa0,		/*  u8  bmAttributes;
+				Bit 7: must be set,
+				    6: Self-powered,
+				    5: Remote wakeup,
+				    4..0: resvd */
+    0x32,		/*  u8  MaxPower; */
+
+    /* USB 1.1:
+     * USB 2.0, single TT organization (mandatory):
+     *	one interface, protocol 0
+     *
+     * USB 2.0, multiple TT organization (optional):
+     *	two interfaces, protocols 1 (like single TT)
+     *	and 2 (multiple TT mode) ... config is
+     *	sometimes settable
+     *	NOT IMPLEMENTED
+     */
+
+    /* one interface */
+    0x09,		/*  u8  if_bLength; */
+    USB_DT_INTERFACE,	/*  u8  if_bDescriptorType; Interface */
+    0x00,		/*  u8  if_bInterfaceNumber; */
+    0x00,		/*  u8  if_bAlternateSetting; */
+    0x01,		/*  u8  if_bNumEndpoints; */
+    0x03,		/*  u8  if_bInterfaceClass; HID */
+    0x01,		/*  u8  if_bInterfaceSubClass; Boot */
+    0x01,		/*  u8  if_bInterfaceProtocol; Keyboard */
+    0x07,		/*  u8  if_iInterface; */
+
+    /* HID descriptor */
+    0x09,		/*  u8  bLength; */
+    USB_DT_HID,		/*  u8  bDescriptorType; */
+    0x11, 0x01,		/*  u16 HID_class */
+    0x00,		/*  u8  country_code */
+    0x01,		/*  u8  num_descriptors */
+    USB_DT_REPORT,	/*  u8  type; Report */
+    0x3f, 0x00,		/*  u16 len */
+
+    /* one endpoint (status change endpoint) */
+    0x07,		/*  u8  ep_bLength; */
+    USB_DT_ENDPOINT,	/*  u8  ep_bDescriptorType; Endpoint */
+    USB_DIR_IN | 0x01,	/*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+    0x03,		/*  u8  ep_bmAttributes; Interrupt */
+    0x08, 0x00,		/*  u16 ep_wMaxPacketSize; */
+    0x0a,		/*  u8  ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_mouse_hid_report_descriptor[] = {
+    0x05, 0x01,		/* Usage Page (Generic Desktop) */
+    0x09, 0x02,		/* Usage (Mouse) */
+    0xa1, 0x01,		/* Collection (Application) */
+    0x09, 0x01,		/*   Usage (Pointer) */
+    0xa1, 0x00,		/*   Collection (Physical) */
+    0x05, 0x09,		/*     Usage Page (Button) */
+    0x19, 0x01,		/*     Usage Minimum (1) */
+    0x29, 0x03,		/*     Usage Maximum (3) */
+    0x15, 0x00,		/*     Logical Minimum (0) */
+    0x25, 0x01,		/*     Logical Maximum (1) */
+    0x95, 0x03,		/*     Report Count (3) */
+    0x75, 0x01,		/*     Report Size (1) */
+    0x81, 0x02,		/*     Input (Data, Variable, Absolute) */
+    0x95, 0x01,		/*     Report Count (1) */
+    0x75, 0x05,		/*     Report Size (5) */
+    0x81, 0x01,		/*     Input (Constant) */
+    0x05, 0x01,		/*     Usage Page (Generic Desktop) */
+    0x09, 0x30,		/*     Usage (X) */
+    0x09, 0x31,		/*     Usage (Y) */
+    0x09, 0x38,		/*     Usage (Wheel) */
+    0x15, 0x81,		/*     Logical Minimum (-0x7f) */
+    0x25, 0x7f,		/*     Logical Maximum (0x7f) */
+    0x75, 0x08,		/*     Report Size (8) */
+    0x95, 0x03,		/*     Report Count (3) */
+    0x81, 0x06,		/*     Input (Data, Variable, Relative) */
+    0xc0,		/*   End Collection */
+    0xc0,		/* End Collection */
+};
+
+static const uint8_t qemu_tablet_hid_report_descriptor[] = {
+    0x05, 0x01,		/* Usage Page (Generic Desktop) */
+    0x09, 0x01,		/* Usage (Pointer) */
+    0xa1, 0x01,		/* Collection (Application) */
+    0x09, 0x01,		/*   Usage (Pointer) */
+    0xa1, 0x00,		/*   Collection (Physical) */
+    0x05, 0x09,		/*     Usage Page (Button) */
+    0x19, 0x01,		/*     Usage Minimum (1) */
+    0x29, 0x03,		/*     Usage Maximum (3) */
+    0x15, 0x00,		/*     Logical Minimum (0) */
+    0x25, 0x01,		/*     Logical Maximum (1) */
+    0x95, 0x03,		/*     Report Count (3) */
+    0x75, 0x01,		/*     Report Size (1) */
+    0x81, 0x02,		/*     Input (Data, Variable, Absolute) */
+    0x95, 0x01,		/*     Report Count (1) */
+    0x75, 0x05,		/*     Report Size (5) */
+    0x81, 0x01,		/*     Input (Constant) */
+    0x05, 0x01,		/*     Usage Page (Generic Desktop) */
+    0x09, 0x30,		/*     Usage (X) */
+    0x09, 0x31,		/*     Usage (Y) */
+    0x15, 0x00,		/*     Logical Minimum (0) */
+    0x26, 0xff, 0x7f,	/*     Logical Maximum (0x7fff) */
+    0x35, 0x00,		/*     Physical Minimum (0) */
+    0x46, 0xff, 0x7f,	/*     Physical Maximum (0x7fff) */
+    0x75, 0x10,		/*     Report Size (16) */
+    0x95, 0x02,		/*     Report Count (2) */
+    0x81, 0x02,		/*     Input (Data, Variable, Absolute) */
+    0x05, 0x01,		/*     Usage Page (Generic Desktop) */
+    0x09, 0x38,		/*     Usage (Wheel) */
+    0x15, 0x81,		/*     Logical Minimum (-0x7f) */
+    0x25, 0x7f,		/*     Logical Maximum (0x7f) */
+    0x35, 0x00,		/*     Physical Minimum (same as logical) */
+    0x45, 0x00,		/*     Physical Maximum (same as logical) */
+    0x75, 0x08,		/*     Report Size (8) */
+    0x95, 0x01,		/*     Report Count (1) */
+    0x81, 0x06,		/*     Input (Data, Variable, Relative) */
+    0xc0,		/*   End Collection */
+    0xc0,		/* End Collection */
+};
+
+static const uint8_t qemu_keyboard_hid_report_descriptor[] = {
+    0x05, 0x01,		/* Usage Page (Generic Desktop) */
+    0x09, 0x06,		/* Usage (Keyboard) */
+    0xa1, 0x01,		/* Collection (Application) */
+    0x75, 0x01,		/*   Report Size (1) */
+    0x95, 0x08,		/*   Report Count (8) */
+    0x05, 0x07,		/*   Usage Page (Key Codes) */
+    0x19, 0xe0,		/*   Usage Minimum (224) */
+    0x29, 0xe7,		/*   Usage Maximum (231) */
+    0x15, 0x00,		/*   Logical Minimum (0) */
+    0x25, 0x01,		/*   Logical Maximum (1) */
+    0x81, 0x02,		/*   Input (Data, Variable, Absolute) */
+    0x95, 0x01,		/*   Report Count (1) */
+    0x75, 0x08,		/*   Report Size (8) */
+    0x81, 0x01,		/*   Input (Constant) */
+    0x95, 0x05,		/*   Report Count (5) */
+    0x75, 0x01,		/*   Report Size (1) */
+    0x05, 0x08,		/*   Usage Page (LEDs) */
+    0x19, 0x01,		/*   Usage Minimum (1) */
+    0x29, 0x05,		/*   Usage Maximum (5) */
+    0x91, 0x02,		/*   Output (Data, Variable, Absolute) */
+    0x95, 0x01,		/*   Report Count (1) */
+    0x75, 0x03,		/*   Report Size (3) */
+    0x91, 0x01,		/*   Output (Constant) */
+    0x95, 0x06,		/*   Report Count (6) */
+    0x75, 0x08,		/*   Report Size (8) */
+    0x15, 0x00,		/*   Logical Minimum (0) */
+    0x25, 0xff,		/*   Logical Maximum (255) */
+    0x05, 0x07,		/*   Usage Page (Key Codes) */
+    0x19, 0x00,		/*   Usage Minimum (0) */
+    0x29, 0xff,		/*   Usage Maximum (255) */
+    0x81, 0x00,		/*   Input (Data, Array) */
+    0xc0,		/* End Collection */
+};
+
+#define USB_HID_USAGE_ERROR_ROLLOVER	0x01
+#define USB_HID_USAGE_POSTFAIL		0x02
+#define USB_HID_USAGE_ERROR_UNDEFINED	0x03
+
+/* Indices are QEMU keycodes, values are from HID Usage Table.  Indices
+ * above 0x80 are for keys that come after 0xe0 or 0xe1+0x1d or 0xe1+0x9d.  */
+static const uint8_t usb_hid_usage_keys[0x100] = {
+    0x00, 0x29, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+    0x24, 0x25, 0x26, 0x27, 0x2d, 0x2e, 0x2a, 0x2b,
+    0x14, 0x1a, 0x08, 0x15, 0x17, 0x1c, 0x18, 0x0c,
+    0x12, 0x13, 0x2f, 0x30, 0x28, 0xe0, 0x04, 0x16,
+    0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x0f, 0x33,
+    0x34, 0x35, 0xe1, 0x31, 0x1d, 0x1b, 0x06, 0x19,
+    0x05, 0x11, 0x10, 0x36, 0x37, 0x38, 0xe5, 0x55,
+    0xe2, 0x2c, 0x32, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
+    0x3f, 0x40, 0x41, 0x42, 0x43, 0x53, 0x47, 0x5f,
+    0x60, 0x61, 0x56, 0x5c, 0x5d, 0x5e, 0x57, 0x59,
+    0x5a, 0x5b, 0x62, 0x63, 0x00, 0x00, 0x00, 0x44,
+    0x45, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e,
+    0xe8, 0xe9, 0x71, 0x72, 0x73, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0xe7, 0x65,
+
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x58, 0xe4, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x46,
+    0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x4a,
+    0x52, 0x4b, 0x00, 0x50, 0x00, 0x4f, 0x00, 0x4d,
+    0x51, 0x4e, 0x49, 0x4c, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void usb_mouse_event(void *opaque,
+                            int dx1, int dy1, int dz1, int buttons_state)
+{
+    USBHIDState *hs = opaque;
+    USBMouseState *s = &hs->ptr;
+
+    s->dx += dx1;
+    s->dy += dy1;
+    s->dz += dz1;
+    s->buttons_state = buttons_state;
+    hs->changed = 1;
+}
+
+static void usb_tablet_event(void *opaque,
+			     int x, int y, int dz, int buttons_state)
+{
+    USBHIDState *hs = opaque;
+    USBMouseState *s = &hs->ptr;
+
+    s->x = x;
+    s->y = y;
+    s->dz += dz;
+    s->buttons_state = buttons_state;
+    hs->changed = 1;
+}
+
+static void usb_keyboard_event(void *opaque, int keycode)
+{
+    USBHIDState *hs = opaque;
+    USBKeyboardState *s = &hs->kbd;
+    uint8_t hid_code, key;
+    int i;
+
+    key = keycode & 0x7f;
+    hid_code = usb_hid_usage_keys[key | ((s->modifiers >> 1) & (1 << 7))];
+    s->modifiers &= ~(1 << 8);
+
+    hs->changed = 1;
+
+    switch (hid_code) {
+    case 0x00:
+        return;
+
+    case 0xe0:
+        if (s->modifiers & (1 << 9)) {
+            s->modifiers ^= 3 << 8;
+            return;
+        }
+    case 0xe1 ... 0xe7:
+        if (keycode & (1 << 7)) {
+            s->modifiers &= ~(1 << (hid_code & 0x0f));
+            return;
+        }
+    case 0xe8 ... 0xef:
+        s->modifiers |= 1 << (hid_code & 0x0f);
+        return;
+    }
+
+    if (keycode & (1 << 7)) {
+        for (i = s->keys - 1; i >= 0; i --)
+            if (s->key[i] == hid_code) {
+                s->key[i] = s->key[-- s->keys];
+                s->key[s->keys] = 0x00;
+                return;
+            }
+    } else {
+        for (i = s->keys - 1; i >= 0; i --)
+            if (s->key[i] == hid_code)
+                return;
+        if (s->keys < sizeof(s->key))
+            s->key[s->keys ++] = hid_code;
+    }
+}
+
+static inline int int_clamp(int val, int vmin, int vmax)
+{
+    if (val < vmin)
+        return vmin;
+    else if (val > vmax)
+        return vmax;
+    else
+        return val;
+}
+
+static int usb_mouse_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+    int dx, dy, dz, b, l;
+    USBMouseState *s = &hs->ptr;
+
+    if (!s->mouse_grabbed) {
+	s->eh_entry = qemu_add_mouse_event_handler(usb_mouse_event, hs,
+                                                  0, "QEMU USB Mouse");
+	s->mouse_grabbed = 1;
+    }
+
+    dx = int_clamp(s->dx, -127, 127);
+    dy = int_clamp(s->dy, -127, 127);
+    dz = int_clamp(s->dz, -127, 127);
+
+    s->dx -= dx;
+    s->dy -= dy;
+    s->dz -= dz;
+
+    /* Appears we have to invert the wheel direction */
+    dz = 0 - dz;
+
+    b = 0;
+    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+        b |= 0x01;
+    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+        b |= 0x02;
+    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+        b |= 0x04;
+
+    l = 0;
+    if (len > l)
+        buf[l ++] = b;
+    if (len > l)
+        buf[l ++] = dx;
+    if (len > l)
+        buf[l ++] = dy;
+    if (len > l)
+        buf[l ++] = dz;
+    return l;
+}
+
+static int usb_tablet_poll(USBHIDState *hs, uint8_t *buf, int len)
+{
+    int dz, b, l;
+    USBMouseState *s = &hs->ptr;
+
+    if (!s->mouse_grabbed) {
+	s->eh_entry = qemu_add_mouse_event_handler(usb_tablet_event, hs,
+                                                  1, "QEMU USB Tablet");
+	s->mouse_grabbed = 1;
+    }
+
+    dz = int_clamp(s->dz, -127, 127);
+    s->dz -= dz;
+
+    /* Appears we have to invert the wheel direction */
+    dz = 0 - dz;
+    b = 0;
+    if (s->buttons_state & MOUSE_EVENT_LBUTTON)
+        b |= 0x01;
+    if (s->buttons_state & MOUSE_EVENT_RBUTTON)
+        b |= 0x02;
+    if (s->buttons_state & MOUSE_EVENT_MBUTTON)
+        b |= 0x04;
+
+    buf[0] = b;
+    buf[1] = s->x & 0xff;
+    buf[2] = s->x >> 8;
+    buf[3] = s->y & 0xff;
+    buf[4] = s->y >> 8;
+    buf[5] = dz;
+    l = 6;
+
+    return l;
+}
+
+static int usb_keyboard_poll(USBKeyboardState *s, uint8_t *buf, int len)
+{
+    if (len < 2)
+        return 0;
+
+    buf[0] = s->modifiers & 0xff;
+    buf[1] = 0;
+    if (s->keys > 6)
+        memset(buf + 2, USB_HID_USAGE_ERROR_ROLLOVER, MIN(8, len) - 2);
+    else
+        memcpy(buf + 2, s->key, MIN(8, len) - 2);
+
+    return MIN(8, len);
+}
+
+static int usb_keyboard_write(USBKeyboardState *s, uint8_t *buf, int len)
+{
+    if (len > 0) {
+        /* 0x01: Num Lock LED
+         * 0x02: Caps Lock LED
+         * 0x04: Scroll Lock LED
+         * 0x08: Compose LED
+         * 0x10: Kana LED */
+        s->leds = buf[0];
+    }
+    return 0;
+}
+
+static void usb_mouse_handle_reset(USBDevice *dev)
+{
+    USBHIDState *s = (USBHIDState *)dev;
+
+    s->ptr.dx = 0;
+    s->ptr.dy = 0;
+    s->ptr.dz = 0;
+    s->ptr.x = 0;
+    s->ptr.y = 0;
+    s->ptr.buttons_state = 0;
+    s->protocol = 1;
+}
+
+static void usb_keyboard_handle_reset(USBDevice *dev)
+{
+    USBHIDState *s = (USBHIDState *)dev;
+
+    qemu_add_kbd_event_handler(usb_keyboard_event, s);
+    s->protocol = 1;
+}
+
+static int usb_hid_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    USBHIDState *s = (USBHIDState *)dev;
+    int ret = 0;
+
+    switch(request) {
+    case DeviceRequest | USB_REQ_GET_STATUS:
+        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case USB_DT_DEVICE:
+            memcpy(data, qemu_mouse_dev_descriptor,
+                   sizeof(qemu_mouse_dev_descriptor));
+            ret = sizeof(qemu_mouse_dev_descriptor);
+            break;
+        case USB_DT_CONFIG:
+	    if (s->kind == USB_MOUSE) {
+		memcpy(data, qemu_mouse_config_descriptor,
+		       sizeof(qemu_mouse_config_descriptor));
+		ret = sizeof(qemu_mouse_config_descriptor);
+	    } else if (s->kind == USB_TABLET) {
+		memcpy(data, qemu_tablet_config_descriptor,
+		       sizeof(qemu_tablet_config_descriptor));
+		ret = sizeof(qemu_tablet_config_descriptor);
+            } else if (s->kind == USB_KEYBOARD) {
+                memcpy(data, qemu_keyboard_config_descriptor,
+                       sizeof(qemu_keyboard_config_descriptor));
+                ret = sizeof(qemu_keyboard_config_descriptor);
+            }
+            break;
+        case USB_DT_STRING:
+            switch(value & 0xff) {
+            case 0:
+                /* language ids */
+                data[0] = 4;
+                data[1] = 3;
+                data[2] = 0x09;
+                data[3] = 0x04;
+                ret = 4;
+                break;
+            case 1:
+                /* serial number */
+                ret = set_usb_string(data, "1");
+                break;
+            case 2:
+                /* product description */
+                ret = set_usb_string(data, s->dev.devname);
+                break;
+            case 3:
+                /* vendor description */
+                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+                break;
+            case 4:
+                ret = set_usb_string(data, "HID Mouse");
+                break;
+            case 5:
+                ret = set_usb_string(data, "HID Tablet");
+                break;
+            case 6:
+                ret = set_usb_string(data, "HID Keyboard");
+                break;
+            case 7:
+                ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        data[0] = 1;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_INTERFACE:
+        data[0] = 0;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = 0;
+        break;
+        /* hid specific requests */
+    case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case 0x22:
+	    if (s->kind == USB_MOUSE) {
+		memcpy(data, qemu_mouse_hid_report_descriptor,
+		       sizeof(qemu_mouse_hid_report_descriptor));
+		ret = sizeof(qemu_mouse_hid_report_descriptor);
+	    } else if (s->kind == USB_TABLET) {
+		memcpy(data, qemu_tablet_hid_report_descriptor,
+		       sizeof(qemu_tablet_hid_report_descriptor));
+		ret = sizeof(qemu_tablet_hid_report_descriptor);
+            } else if (s->kind == USB_KEYBOARD) {
+                memcpy(data, qemu_keyboard_hid_report_descriptor,
+                       sizeof(qemu_keyboard_hid_report_descriptor));
+                ret = sizeof(qemu_keyboard_hid_report_descriptor);
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case GET_REPORT:
+	if (s->kind == USB_MOUSE)
+            ret = usb_mouse_poll(s, data, length);
+	else if (s->kind == USB_TABLET)
+            ret = usb_tablet_poll(s, data, length);
+        else if (s->kind == USB_KEYBOARD)
+            ret = usb_keyboard_poll(&s->kbd, data, length);
+        break;
+    case SET_REPORT:
+        if (s->kind == USB_KEYBOARD)
+            ret = usb_keyboard_write(&s->kbd, data, length);
+        else
+            goto fail;
+        break;
+    case GET_PROTOCOL:
+        if (s->kind != USB_KEYBOARD)
+            goto fail;
+        ret = 1;
+        data[0] = s->protocol;
+        break;
+    case SET_PROTOCOL:
+        if (s->kind != USB_KEYBOARD)
+            goto fail;
+        ret = 0;
+        s->protocol = value;
+        break;
+    case GET_IDLE:
+        ret = 1;
+        data[0] = s->idle;
+        break;
+    case SET_IDLE:
+        s->idle = value;
+        ret = 0;
+        break;
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_hid_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHIDState *s = (USBHIDState *)dev;
+    int ret = 0;
+
+    switch(p->pid) {
+    case USB_TOKEN_IN:
+        if (p->devep == 1) {
+            /* TODO: Implement finite idle delays.  */
+            if (!(s->changed || s->idle))
+                return USB_RET_NAK;
+            s->changed = 0;
+            if (s->kind == USB_MOUSE)
+                ret = usb_mouse_poll(s, p->data, p->len);
+            else if (s->kind == USB_TABLET)
+                ret = usb_tablet_poll(s, p->data, p->len);
+            else if (s->kind == USB_KEYBOARD)
+                ret = usb_keyboard_poll(&s->kbd, p->data, p->len);
+        } else {
+            goto fail;
+        }
+        break;
+    case USB_TOKEN_OUT:
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_hid_handle_destroy(USBDevice *dev)
+{
+    USBHIDState *s = (USBHIDState *)dev;
+
+    if (s->kind != USB_KEYBOARD)
+        qemu_remove_mouse_event_handler(s->ptr.eh_entry);
+    /* TODO: else */
+    qemu_free(s);
+}
+
+USBDevice *usb_tablet_init(void)
+{
+    USBHIDState *s;
+
+    s = qemu_mallocz(sizeof(USBHIDState));
+    if (!s)
+        return NULL;
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = usb_generic_handle_packet;
+
+    s->dev.handle_reset = usb_mouse_handle_reset;
+    s->dev.handle_control = usb_hid_handle_control;
+    s->dev.handle_data = usb_hid_handle_data;
+    s->dev.handle_destroy = usb_hid_handle_destroy;
+    s->kind = USB_TABLET;
+    /* Force poll routine to be run and grab input the first time.  */
+    s->changed = 1;
+
+    pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Tablet");
+
+    return (USBDevice *)s;
+}
+
+USBDevice *usb_mouse_init(void)
+{
+    USBHIDState *s;
+
+    s = qemu_mallocz(sizeof(USBHIDState));
+    if (!s)
+        return NULL;
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = usb_generic_handle_packet;
+
+    s->dev.handle_reset = usb_mouse_handle_reset;
+    s->dev.handle_control = usb_hid_handle_control;
+    s->dev.handle_data = usb_hid_handle_data;
+    s->dev.handle_destroy = usb_hid_handle_destroy;
+    s->kind = USB_MOUSE;
+    /* Force poll routine to be run and grab input the first time.  */
+    s->changed = 1;
+
+    pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Mouse");
+
+    return (USBDevice *)s;
+}
+
+USBDevice *usb_keyboard_init(void)
+{
+    USBHIDState *s;
+
+    s = qemu_mallocz(sizeof(USBHIDState));
+    if (!s)
+        return NULL;
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = usb_generic_handle_packet;
+
+    s->dev.handle_reset = usb_keyboard_handle_reset;
+    s->dev.handle_control = usb_hid_handle_control;
+    s->dev.handle_data = usb_hid_handle_data;
+    s->dev.handle_destroy = usb_hid_handle_destroy;
+    s->kind = USB_KEYBOARD;
+
+    pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Keyboard");
+
+    return (USBDevice *) s;
+}
diff --git a/hw/usb-hub.c b/hw/usb-hub.c
new file mode 100644
index 0000000..97c3d05
--- /dev/null
+++ b/hw/usb-hub.c
@@ -0,0 +1,554 @@
+/*
+ * QEMU USB HUB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "usb.h"
+
+//#define DEBUG
+
+#define MAX_PORTS 8
+
+typedef struct USBHubPort {
+    USBPort port;
+    uint16_t wPortStatus;
+    uint16_t wPortChange;
+} USBHubPort;
+
+typedef struct USBHubState {
+    USBDevice dev;
+    int nb_ports;
+    USBHubPort ports[MAX_PORTS];
+} USBHubState;
+
+#define ClearHubFeature		(0x2000 | USB_REQ_CLEAR_FEATURE)
+#define ClearPortFeature	(0x2300 | USB_REQ_CLEAR_FEATURE)
+#define GetHubDescriptor	(0xa000 | USB_REQ_GET_DESCRIPTOR)
+#define GetHubStatus		(0xa000 | USB_REQ_GET_STATUS)
+#define GetPortStatus		(0xa300 | USB_REQ_GET_STATUS)
+#define SetHubFeature		(0x2000 | USB_REQ_SET_FEATURE)
+#define SetPortFeature		(0x2300 | USB_REQ_SET_FEATURE)
+
+#define PORT_STAT_CONNECTION	0x0001
+#define PORT_STAT_ENABLE	0x0002
+#define PORT_STAT_SUSPEND	0x0004
+#define PORT_STAT_OVERCURRENT	0x0008
+#define PORT_STAT_RESET		0x0010
+#define PORT_STAT_POWER		0x0100
+#define PORT_STAT_LOW_SPEED	0x0200
+#define PORT_STAT_HIGH_SPEED    0x0400
+#define PORT_STAT_TEST          0x0800
+#define PORT_STAT_INDICATOR     0x1000
+
+#define PORT_STAT_C_CONNECTION	0x0001
+#define PORT_STAT_C_ENABLE	0x0002
+#define PORT_STAT_C_SUSPEND	0x0004
+#define PORT_STAT_C_OVERCURRENT	0x0008
+#define PORT_STAT_C_RESET	0x0010
+
+#define PORT_CONNECTION	        0
+#define PORT_ENABLE		1
+#define PORT_SUSPEND		2
+#define PORT_OVERCURRENT	3
+#define PORT_RESET		4
+#define PORT_POWER		8
+#define PORT_LOWSPEED		9
+#define PORT_HIGHSPEED		10
+#define PORT_C_CONNECTION	16
+#define PORT_C_ENABLE		17
+#define PORT_C_SUSPEND		18
+#define PORT_C_OVERCURRENT	19
+#define PORT_C_RESET		20
+#define PORT_TEST               21
+#define PORT_INDICATOR          22
+
+/* same as Linux kernel root hubs */
+
+static const uint8_t qemu_hub_dev_descriptor[] = {
+	0x12,       /*  u8 bLength; */
+	0x01,       /*  u8 bDescriptorType; Device */
+	0x10, 0x01, /*  u16 bcdUSB; v1.1 */
+
+	0x09,	    /*  u8  bDeviceClass; HUB_CLASSCODE */
+	0x00,	    /*  u8  bDeviceSubClass; */
+	0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+	0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
+
+	0x00, 0x00, /*  u16 idVendor; */
+ 	0x00, 0x00, /*  u16 idProduct; */
+	0x01, 0x01, /*  u16 bcdDevice */
+
+	0x03,       /*  u8  iManufacturer; */
+	0x02,       /*  u8  iProduct; */
+	0x01,       /*  u8  iSerialNumber; */
+	0x01        /*  u8  bNumConfigurations; */
+};
+
+/* XXX: patch interrupt size */
+static const uint8_t qemu_hub_config_descriptor[] = {
+
+	/* one configuration */
+	0x09,       /*  u8  bLength; */
+	0x02,       /*  u8  bDescriptorType; Configuration */
+	0x19, 0x00, /*  u16 wTotalLength; */
+	0x01,       /*  u8  bNumInterfaces; (1) */
+	0x01,       /*  u8  bConfigurationValue; */
+	0x00,       /*  u8  iConfiguration; */
+	0xc0,       /*  u8  bmAttributes;
+				 Bit 7: must be set,
+				     6: Self-powered,
+				     5: Remote wakeup,
+				     4..0: resvd */
+	0x00,       /*  u8  MaxPower; */
+
+	/* USB 1.1:
+	 * USB 2.0, single TT organization (mandatory):
+	 *	one interface, protocol 0
+	 *
+	 * USB 2.0, multiple TT organization (optional):
+	 *	two interfaces, protocols 1 (like single TT)
+	 *	and 2 (multiple TT mode) ... config is
+	 *	sometimes settable
+	 *	NOT IMPLEMENTED
+	 */
+
+	/* one interface */
+	0x09,       /*  u8  if_bLength; */
+	0x04,       /*  u8  if_bDescriptorType; Interface */
+	0x00,       /*  u8  if_bInterfaceNumber; */
+	0x00,       /*  u8  if_bAlternateSetting; */
+	0x01,       /*  u8  if_bNumEndpoints; */
+	0x09,       /*  u8  if_bInterfaceClass; HUB_CLASSCODE */
+	0x00,       /*  u8  if_bInterfaceSubClass; */
+	0x00,       /*  u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
+	0x00,       /*  u8  if_iInterface; */
+
+	/* one endpoint (status change endpoint) */
+	0x07,       /*  u8  ep_bLength; */
+	0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x03,       /*  u8  ep_bmAttributes; Interrupt */
+ 	0x02, 0x00, /*  u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+	0xff        /*  u8  ep_bInterval; (255ms -- usb 2.0 spec) */
+};
+
+static const uint8_t qemu_hub_hub_descriptor[] =
+{
+	0x00,			/*  u8  bLength; patched in later */
+	0x29,			/*  u8  bDescriptorType; Hub-descriptor */
+	0x00,			/*  u8  bNbrPorts; (patched later) */
+	0x0a,			/* u16  wHubCharacteristics; */
+	0x00,			/*   (per-port OC, no power switching) */
+	0x01,			/*  u8  bPwrOn2pwrGood; 2ms */
+	0x00			/*  u8  bHubContrCurrent; 0 mA */
+
+        /* DeviceRemovable and PortPwrCtrlMask patched in later */
+};
+
+static void usb_hub_attach(USBPort *port1, USBDevice *dev)
+{
+    USBHubState *s = port1->opaque;
+    USBHubPort *port = &s->ports[port1->index];
+
+    if (dev) {
+        if (port->port.dev)
+            usb_attach(port1, NULL);
+
+        port->wPortStatus |= PORT_STAT_CONNECTION;
+        port->wPortChange |= PORT_STAT_C_CONNECTION;
+        if (dev->speed == USB_SPEED_LOW)
+            port->wPortStatus |= PORT_STAT_LOW_SPEED;
+        else
+            port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
+        port->port.dev = dev;
+        /* send the attach message */
+        usb_send_msg(dev, USB_MSG_ATTACH);
+    } else {
+        dev = port->port.dev;
+        if (dev) {
+            port->wPortStatus &= ~PORT_STAT_CONNECTION;
+            port->wPortChange |= PORT_STAT_C_CONNECTION;
+            if (port->wPortStatus & PORT_STAT_ENABLE) {
+                port->wPortStatus &= ~PORT_STAT_ENABLE;
+                port->wPortChange |= PORT_STAT_C_ENABLE;
+            }
+            /* send the detach message */
+            usb_send_msg(dev, USB_MSG_DETACH);
+            port->port.dev = NULL;
+        }
+    }
+}
+
+static void usb_hub_handle_reset(USBDevice *dev)
+{
+    /* XXX: do it */
+}
+
+static int usb_hub_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    USBHubState *s = (USBHubState *)dev;
+    int ret;
+
+    switch(request) {
+    case DeviceRequest | USB_REQ_GET_STATUS:
+        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == 0 && index != 0x81) { /* clear ep halt */
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case USB_DT_DEVICE:
+            memcpy(data, qemu_hub_dev_descriptor,
+                   sizeof(qemu_hub_dev_descriptor));
+            ret = sizeof(qemu_hub_dev_descriptor);
+            break;
+        case USB_DT_CONFIG:
+            memcpy(data, qemu_hub_config_descriptor,
+                   sizeof(qemu_hub_config_descriptor));
+
+            /* status change endpoint size based on number
+             * of ports */
+            data[22] = (s->nb_ports + 1 + 7) / 8;
+
+            ret = sizeof(qemu_hub_config_descriptor);
+            break;
+        case USB_DT_STRING:
+            switch(value & 0xff) {
+            case 0:
+                /* language ids */
+                data[0] = 4;
+                data[1] = 3;
+                data[2] = 0x09;
+                data[3] = 0x04;
+                ret = 4;
+                break;
+            case 1:
+                /* serial number */
+                ret = set_usb_string(data, "314159");
+                break;
+            case 2:
+                /* product description */
+                ret = set_usb_string(data, "QEMU USB Hub");
+                break;
+            case 3:
+                /* vendor description */
+                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        data[0] = 1;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_INTERFACE:
+        data[0] = 0;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = 0;
+        break;
+        /* usb specific requests */
+    case GetHubStatus:
+        data[0] = 0;
+        data[1] = 0;
+        data[2] = 0;
+        data[3] = 0;
+        ret = 4;
+        break;
+    case GetPortStatus:
+        {
+            unsigned int n = index - 1;
+            USBHubPort *port;
+            if (n >= s->nb_ports)
+                goto fail;
+            port = &s->ports[n];
+            data[0] = port->wPortStatus;
+            data[1] = port->wPortStatus >> 8;
+            data[2] = port->wPortChange;
+            data[3] = port->wPortChange >> 8;
+            ret = 4;
+        }
+        break;
+    case SetHubFeature:
+    case ClearHubFeature:
+        if (value == 0 || value == 1) {
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case SetPortFeature:
+        {
+            unsigned int n = index - 1;
+            USBHubPort *port;
+            USBDevice *dev;
+            if (n >= s->nb_ports)
+                goto fail;
+            port = &s->ports[n];
+            dev = port->port.dev;
+            switch(value) {
+            case PORT_SUSPEND:
+                port->wPortStatus |= PORT_STAT_SUSPEND;
+                break;
+            case PORT_RESET:
+                if (dev) {
+                    usb_send_msg(dev, USB_MSG_RESET);
+                    port->wPortChange |= PORT_STAT_C_RESET;
+                    /* set enable bit */
+                    port->wPortStatus |= PORT_STAT_ENABLE;
+                }
+                break;
+            case PORT_POWER:
+                break;
+            default:
+                goto fail;
+            }
+            ret = 0;
+        }
+        break;
+    case ClearPortFeature:
+        {
+            unsigned int n = index - 1;
+            USBHubPort *port;
+            USBDevice *dev;
+            if (n >= s->nb_ports)
+                goto fail;
+            port = &s->ports[n];
+            dev = port->port.dev;
+            switch(value) {
+            case PORT_ENABLE:
+                port->wPortStatus &= ~PORT_STAT_ENABLE;
+                break;
+            case PORT_C_ENABLE:
+                port->wPortChange &= ~PORT_STAT_C_ENABLE;
+                break;
+            case PORT_SUSPEND:
+                port->wPortStatus &= ~PORT_STAT_SUSPEND;
+                break;
+            case PORT_C_SUSPEND:
+                port->wPortChange &= ~PORT_STAT_C_SUSPEND;
+                break;
+            case PORT_C_CONNECTION:
+                port->wPortChange &= ~PORT_STAT_C_CONNECTION;
+                break;
+            case PORT_C_OVERCURRENT:
+                port->wPortChange &= ~PORT_STAT_C_OVERCURRENT;
+                break;
+            case PORT_C_RESET:
+                port->wPortChange &= ~PORT_STAT_C_RESET;
+                break;
+            default:
+                goto fail;
+            }
+            ret = 0;
+        }
+        break;
+    case GetHubDescriptor:
+        {
+            unsigned int n, limit, var_hub_size = 0;
+            memcpy(data, qemu_hub_hub_descriptor,
+                   sizeof(qemu_hub_hub_descriptor));
+            data[2] = s->nb_ports;
+
+            /* fill DeviceRemovable bits */
+            limit = ((s->nb_ports + 1 + 7) / 8) + 7;
+            for (n = 7; n < limit; n++) {
+                data[n] = 0x00;
+                var_hub_size++;
+            }
+
+            /* fill PortPwrCtrlMask bits */
+            limit = limit + ((s->nb_ports + 7) / 8);
+            for (;n < limit; n++) {
+                data[n] = 0xff;
+                var_hub_size++;
+            }
+
+            ret = sizeof(qemu_hub_hub_descriptor) + var_hub_size;
+            data[0] = ret;
+            break;
+        }
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBHubState *s = (USBHubState *)dev;
+    int ret;
+
+    switch(p->pid) {
+    case USB_TOKEN_IN:
+        if (p->devep == 1) {
+            USBHubPort *port;
+            unsigned int status;
+            int i, n;
+            n = (s->nb_ports + 1 + 7) / 8;
+            if (p->len == 1) { /* FreeBSD workaround */
+                n = 1;
+            } else if (n > p->len) {
+                return USB_RET_BABBLE;
+            }
+            status = 0;
+            for(i = 0; i < s->nb_ports; i++) {
+                port = &s->ports[i];
+                if (port->wPortChange)
+                    status |= (1 << (i + 1));
+            }
+            if (status != 0) {
+                for(i = 0; i < n; i++) {
+                    p->data[i] = status >> (8 * i);
+                }
+                ret = n;
+            } else {
+                ret = USB_RET_NAK; /* usb11 11.13.1 */
+            }
+        } else {
+            goto fail;
+        }
+        break;
+    case USB_TOKEN_OUT:
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
+{
+    USBHubPort *port;
+    USBDevice *dev;
+    int i, ret;
+
+    for(i = 0; i < s->nb_ports; i++) {
+        port = &s->ports[i];
+        dev = port->port.dev;
+        if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
+            ret = dev->handle_packet(dev, p);
+            if (ret != USB_RET_NODEV) {
+                return ret;
+            }
+        }
+    }
+    return USB_RET_NODEV;
+}
+
+static int usb_hub_handle_packet(USBDevice *dev, USBPacket *p)
+{
+    USBHubState *s = (USBHubState *)dev;
+
+#if defined(DEBUG) && 0
+    printf("usb_hub: pid=0x%x\n", pid);
+#endif
+    if (dev->state == USB_STATE_DEFAULT &&
+        dev->addr != 0 &&
+        p->devaddr != dev->addr &&
+        (p->pid == USB_TOKEN_SETUP ||
+         p->pid == USB_TOKEN_OUT ||
+         p->pid == USB_TOKEN_IN)) {
+        /* broadcast the packet to the devices */
+        return usb_hub_broadcast_packet(s, p);
+    }
+    return usb_generic_handle_packet(dev, p);
+}
+
+static void usb_hub_handle_destroy(USBDevice *dev)
+{
+    USBHubState *s = (USBHubState *)dev;
+
+    qemu_free(s);
+}
+
+USBDevice *usb_hub_init(int nb_ports)
+{
+    USBHubState *s;
+    USBHubPort *port;
+    int i;
+
+    if (nb_ports > MAX_PORTS)
+        return NULL;
+    s = qemu_mallocz(sizeof(USBHubState));
+    if (!s)
+        return NULL;
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = usb_hub_handle_packet;
+
+    /* generic USB device init */
+    s->dev.handle_reset = usb_hub_handle_reset;
+    s->dev.handle_control = usb_hub_handle_control;
+    s->dev.handle_data = usb_hub_handle_data;
+    s->dev.handle_destroy = usb_hub_handle_destroy;
+
+    pstrcpy(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Hub");
+
+    s->nb_ports = nb_ports;
+    for(i = 0; i < s->nb_ports; i++) {
+        port = &s->ports[i];
+        qemu_register_usb_port(&port->port, s, i, usb_hub_attach);
+        port->wPortStatus = PORT_STAT_POWER;
+        port->wPortChange = 0;
+    }
+    return (USBDevice *)s;
+}
diff --git a/hw/usb-msd.c b/hw/usb-msd.c
new file mode 100644
index 0000000..f7ad25e
--- /dev/null
+++ b/hw/usb-msd.c
@@ -0,0 +1,578 @@
+/*
+ * USB Mass Storage Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "block.h"
+#include "scsi-disk.h"
+
+//#define DEBUG_MSD
+
+#ifdef DEBUG_MSD
+#define DPRINTF(fmt, args...) \
+do { printf("usb-msd: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+/* USB requests.  */
+#define MassStorageReset  0xff
+#define GetMaxLun         0xfe
+
+enum USBMSDMode {
+    USB_MSDM_CBW, /* Command Block.  */
+    USB_MSDM_DATAOUT, /* Tranfer data to device.  */
+    USB_MSDM_DATAIN, /* Transfer data from device.  */
+    USB_MSDM_CSW /* Command Status.  */
+};
+
+typedef struct {
+    USBDevice dev;
+    enum USBMSDMode mode;
+    uint32_t scsi_len;
+    uint8_t *scsi_buf;
+    uint32_t usb_len;
+    uint8_t *usb_buf;
+    uint32_t data_len;
+    uint32_t residue;
+    uint32_t tag;
+    BlockDriverState *bs;
+    SCSIDevice *scsi_dev;
+    int result;
+    /* For async completion.  */
+    USBPacket *packet;
+} MSDState;
+
+struct usb_msd_cbw {
+    uint32_t sig;
+    uint32_t tag;
+    uint32_t data_len;
+    uint8_t flags;
+    uint8_t lun;
+    uint8_t cmd_len;
+    uint8_t cmd[16];
+};
+
+struct usb_msd_csw {
+    uint32_t sig;
+    uint32_t tag;
+    uint32_t residue;
+    uint8_t status;
+};
+
+static const uint8_t qemu_msd_dev_descriptor[] = {
+	0x12,       /*  u8 bLength; */
+	0x01,       /*  u8 bDescriptorType; Device */
+	0x00, 0x01, /*  u16 bcdUSB; v1.0 */
+
+	0x00,	    /*  u8  bDeviceClass; */
+	0x00,	    /*  u8  bDeviceSubClass; */
+	0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+	0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
+
+        /* Vendor and product id are arbitrary.  */
+	0x00, 0x00, /*  u16 idVendor; */
+ 	0x00, 0x00, /*  u16 idProduct; */
+	0x00, 0x00, /*  u16 bcdDevice */
+
+	0x01,       /*  u8  iManufacturer; */
+	0x02,       /*  u8  iProduct; */
+	0x03,       /*  u8  iSerialNumber; */
+	0x01        /*  u8  bNumConfigurations; */
+};
+
+static const uint8_t qemu_msd_config_descriptor[] = {
+
+	/* one configuration */
+	0x09,       /*  u8  bLength; */
+	0x02,       /*  u8  bDescriptorType; Configuration */
+	0x20, 0x00, /*  u16 wTotalLength; */
+	0x01,       /*  u8  bNumInterfaces; (1) */
+	0x01,       /*  u8  bConfigurationValue; */
+	0x00,       /*  u8  iConfiguration; */
+	0xc0,       /*  u8  bmAttributes;
+				 Bit 7: must be set,
+				     6: Self-powered,
+				     5: Remote wakeup,
+				     4..0: resvd */
+	0x00,       /*  u8  MaxPower; */
+
+	/* one interface */
+	0x09,       /*  u8  if_bLength; */
+	0x04,       /*  u8  if_bDescriptorType; Interface */
+	0x00,       /*  u8  if_bInterfaceNumber; */
+	0x00,       /*  u8  if_bAlternateSetting; */
+	0x02,       /*  u8  if_bNumEndpoints; */
+	0x08,       /*  u8  if_bInterfaceClass; MASS STORAGE */
+	0x06,       /*  u8  if_bInterfaceSubClass; SCSI */
+	0x50,       /*  u8  if_bInterfaceProtocol; Bulk Only */
+	0x00,       /*  u8  if_iInterface; */
+
+	/* Bulk-In endpoint */
+	0x07,       /*  u8  ep_bLength; */
+	0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+	0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+ 	0x02,       /*  u8  ep_bmAttributes; Bulk */
+ 	0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+	0x00,       /*  u8  ep_bInterval; */
+
+	/* Bulk-Out endpoint */
+	0x07,       /*  u8  ep_bLength; */
+	0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+	0x02,       /*  u8  ep_bEndpointAddress; OUT Endpoint 2 */
+ 	0x02,       /*  u8  ep_bmAttributes; Bulk */
+ 	0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+	0x00        /*  u8  ep_bInterval; */
+};
+
+static void usb_msd_copy_data(MSDState *s)
+{
+    uint32_t len;
+    len = s->usb_len;
+    if (len > s->scsi_len)
+        len = s->scsi_len;
+    if (s->mode == USB_MSDM_DATAIN) {
+        memcpy(s->usb_buf, s->scsi_buf, len);
+    } else {
+        memcpy(s->scsi_buf, s->usb_buf, len);
+    }
+    s->usb_len -= len;
+    s->scsi_len -= len;
+    s->usb_buf += len;
+    s->scsi_buf += len;
+    s->data_len -= len;
+    if (s->scsi_len == 0) {
+        if (s->mode == USB_MSDM_DATAIN) {
+            s->scsi_dev->read_data(s->scsi_dev, s->tag);
+        } else if (s->mode == USB_MSDM_DATAOUT) {
+            s->scsi_dev->write_data(s->scsi_dev, s->tag);
+        }
+    }
+}
+
+static void usb_msd_send_status(MSDState *s)
+{
+    struct usb_msd_csw csw;
+
+    csw.sig = cpu_to_le32(0x53425355);
+    csw.tag = cpu_to_le32(s->tag);
+    csw.residue = s->residue;
+    csw.status = s->result;
+    memcpy(s->usb_buf, &csw, 13);
+}
+
+static void usb_msd_command_complete(void *opaque, int reason, uint32_t tag,
+                                     uint32_t arg)
+{
+    MSDState *s = (MSDState *)opaque;
+    USBPacket *p = s->packet;
+
+    if (tag != s->tag) {
+        fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
+    }
+    if (reason == SCSI_REASON_DONE) {
+        DPRINTF("Command complete %d\n", arg);
+        s->residue = s->data_len;
+        s->result = arg != 0;
+        if (s->packet) {
+            if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
+                /* A deferred packet with no write data remaining must be
+                   the status read packet.  */
+                usb_msd_send_status(s);
+                s->mode = USB_MSDM_CBW;
+            } else {
+                if (s->data_len) {
+                    s->data_len -= s->usb_len;
+                    if (s->mode == USB_MSDM_DATAIN)
+                        memset(s->usb_buf, 0, s->usb_len);
+                    s->usb_len = 0;
+                }
+                if (s->data_len == 0)
+                    s->mode = USB_MSDM_CSW;
+            }
+            s->packet = NULL;
+            usb_packet_complete(p);
+        } else if (s->data_len == 0) {
+            s->mode = USB_MSDM_CSW;
+        }
+        return;
+    }
+    s->scsi_len = arg;
+    s->scsi_buf = s->scsi_dev->get_buf(s->scsi_dev, tag);
+    if (p) {
+        usb_msd_copy_data(s);
+        if (s->usb_len == 0) {
+            /* Set s->packet to NULL before calling usb_packet_complete
+               because annother request may be issued before
+               usb_packet_complete returns.  */
+            DPRINTF("Packet complete %p\n", p);
+            s->packet = NULL;
+            usb_packet_complete(p);
+        }
+    }
+}
+
+static void usb_msd_handle_reset(USBDevice *dev)
+{
+    MSDState *s = (MSDState *)dev;
+
+    DPRINTF("Reset\n");
+    s->mode = USB_MSDM_CBW;
+}
+
+static int usb_msd_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    MSDState *s = (MSDState *)dev;
+    int ret = 0;
+
+    switch (request) {
+    case DeviceRequest | USB_REQ_GET_STATUS:
+        data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case USB_DT_DEVICE:
+            memcpy(data, qemu_msd_dev_descriptor,
+                   sizeof(qemu_msd_dev_descriptor));
+            ret = sizeof(qemu_msd_dev_descriptor);
+            break;
+        case USB_DT_CONFIG:
+            memcpy(data, qemu_msd_config_descriptor,
+                   sizeof(qemu_msd_config_descriptor));
+            ret = sizeof(qemu_msd_config_descriptor);
+            break;
+        case USB_DT_STRING:
+            switch(value & 0xff) {
+            case 0:
+                /* language ids */
+                data[0] = 4;
+                data[1] = 3;
+                data[2] = 0x09;
+                data[3] = 0x04;
+                ret = 4;
+                break;
+            case 1:
+                /* vendor description */
+                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+                break;
+            case 2:
+                /* product description */
+                ret = set_usb_string(data, "QEMU USB HARDDRIVE");
+                break;
+            case 3:
+                /* serial number */
+                ret = set_usb_string(data, "1");
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        data[0] = 1;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_INTERFACE:
+        data[0] = 0;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = 0;
+        break;
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == 0 && index != 0x81) { /* clear ep halt */
+            goto fail;
+        }
+        ret = 0;
+        break;
+        /* Class specific requests.  */
+    case MassStorageReset:
+        /* Reset state ready for the next CBW.  */
+        s->mode = USB_MSDM_CBW;
+        ret = 0;
+        break;
+    case GetMaxLun:
+        data[0] = 0;
+        ret = 1;
+        break;
+    default:
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void usb_msd_cancel_io(USBPacket *p, void *opaque)
+{
+    MSDState *s = opaque;
+    s->scsi_dev->cancel_io(s->scsi_dev, s->tag);
+    s->packet = NULL;
+    s->scsi_len = 0;
+}
+
+static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
+{
+    MSDState *s = (MSDState *)dev;
+    int ret = 0;
+    struct usb_msd_cbw cbw;
+    uint8_t devep = p->devep;
+    uint8_t *data = p->data;
+    int len = p->len;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        if (devep != 2)
+            goto fail;
+
+        switch (s->mode) {
+        case USB_MSDM_CBW:
+            if (len != 31) {
+                fprintf(stderr, "usb-msd: Bad CBW size");
+                goto fail;
+            }
+            memcpy(&cbw, data, 31);
+            if (le32_to_cpu(cbw.sig) != 0x43425355) {
+                fprintf(stderr, "usb-msd: Bad signature %08x\n",
+                        le32_to_cpu(cbw.sig));
+                goto fail;
+            }
+            DPRINTF("Command on LUN %d\n", cbw.lun);
+            if (cbw.lun != 0) {
+                fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
+                goto fail;
+            }
+            s->tag = le32_to_cpu(cbw.tag);
+            s->data_len = le32_to_cpu(cbw.data_len);
+            if (s->data_len == 0) {
+                s->mode = USB_MSDM_CSW;
+            } else if (cbw.flags & 0x80) {
+                s->mode = USB_MSDM_DATAIN;
+            } else {
+                s->mode = USB_MSDM_DATAOUT;
+            }
+            DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
+                    s->tag, cbw.flags, cbw.cmd_len, s->data_len);
+            s->residue = 0;
+            s->scsi_dev->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
+            /* ??? Should check that USB and SCSI data transfer
+               directions match.  */
+            if (s->residue == 0) {
+                if (s->mode == USB_MSDM_DATAIN) {
+                    s->scsi_dev->read_data(s->scsi_dev, s->tag);
+                } else if (s->mode == USB_MSDM_DATAOUT) {
+                    s->scsi_dev->write_data(s->scsi_dev, s->tag);
+                }
+            }
+            ret = len;
+            break;
+
+        case USB_MSDM_DATAOUT:
+            DPRINTF("Data out %d/%d\n", len, s->data_len);
+            if (len > s->data_len)
+                goto fail;
+
+            s->usb_buf = data;
+            s->usb_len = len;
+            if (s->scsi_len) {
+                usb_msd_copy_data(s);
+            }
+            if (s->residue && s->usb_len) {
+                s->data_len -= s->usb_len;
+                if (s->data_len == 0)
+                    s->mode = USB_MSDM_CSW;
+                s->usb_len = 0;
+            }
+            if (s->usb_len) {
+                DPRINTF("Deferring packet %p\n", p);
+                usb_defer_packet(p, usb_msd_cancel_io, s);
+                s->packet = p;
+                ret = USB_RET_ASYNC;
+            } else {
+                ret = len;
+            }
+            break;
+
+        default:
+            DPRINTF("Unexpected write (len %d)\n", len);
+            goto fail;
+        }
+        break;
+
+    case USB_TOKEN_IN:
+        if (devep != 1)
+            goto fail;
+
+        switch (s->mode) {
+        case USB_MSDM_DATAOUT:
+            if (s->data_len != 0 || len < 13)
+                goto fail;
+            /* Waiting for SCSI write to complete.  */
+            usb_defer_packet(p, usb_msd_cancel_io, s);
+            s->packet = p;
+            ret = USB_RET_ASYNC;
+            break;
+
+        case USB_MSDM_CSW:
+            DPRINTF("Command status %d tag 0x%x, len %d\n",
+                    s->result, s->tag, len);
+            if (len < 13)
+                goto fail;
+
+            s->usb_len = len;
+            s->usb_buf = data;
+            usb_msd_send_status(s);
+            s->mode = USB_MSDM_CBW;
+            ret = 13;
+            break;
+
+        case USB_MSDM_DATAIN:
+            DPRINTF("Data in %d/%d\n", len, s->data_len);
+            if (len > s->data_len)
+                len = s->data_len;
+            s->usb_buf = data;
+            s->usb_len = len;
+            if (s->scsi_len) {
+                usb_msd_copy_data(s);
+            }
+            if (s->residue && s->usb_len) {
+                s->data_len -= s->usb_len;
+                memset(s->usb_buf, 0, s->usb_len);
+                if (s->data_len == 0)
+                    s->mode = USB_MSDM_CSW;
+                s->usb_len = 0;
+            }
+            if (s->usb_len) {
+                DPRINTF("Deferring packet %p\n", p);
+                usb_defer_packet(p, usb_msd_cancel_io, s);
+                s->packet = p;
+                ret = USB_RET_ASYNC;
+            } else {
+                ret = len;
+            }
+            break;
+
+        default:
+            DPRINTF("Unexpected read (len %d)\n", len);
+            goto fail;
+        }
+        break;
+
+    default:
+        DPRINTF("Bad token\n");
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void usb_msd_handle_destroy(USBDevice *dev)
+{
+    MSDState *s = (MSDState *)dev;
+
+    s->scsi_dev->destroy(s->scsi_dev);
+    bdrv_delete(s->bs);
+    qemu_free(s);
+}
+
+USBDevice *usb_msd_init(const char *filename)
+{
+    MSDState *s;
+    BlockDriverState *bdrv;
+    BlockDriver *drv = NULL;
+    const char *p1;
+    char fmt[32];
+
+    p1 = strchr(filename, ':');
+    if (p1++) {
+        const char *p2;
+
+        if (strstart(filename, "format=", &p2)) {
+            int len = MIN(p1 - p2, sizeof(fmt));
+            pstrcpy(fmt, len, p2);
+
+            drv = bdrv_find_format(fmt);
+            if (!drv) {
+                printf("invalid format %s\n", fmt);
+                return NULL;
+            }
+        } else if (*filename != ':') {
+            printf("unrecognized USB mass-storage option %s\n", filename);
+            return NULL;
+        }
+
+        filename = p1;
+    }
+
+    if (!*filename) {
+        printf("block device specification needed\n");
+        return NULL;
+    }
+
+    s = qemu_mallocz(sizeof(MSDState));
+    if (!s)
+        return NULL;
+
+    bdrv = bdrv_new("usb");
+    if (bdrv_open2(bdrv, filename, 0, drv) < 0)
+        goto fail;
+    if (qemu_key_check(bdrv, filename))
+        goto fail;
+    s->bs = bdrv;
+
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = usb_generic_handle_packet;
+
+    s->dev.handle_reset = usb_msd_handle_reset;
+    s->dev.handle_control = usb_msd_handle_control;
+    s->dev.handle_data = usb_msd_handle_data;
+    s->dev.handle_destroy = usb_msd_handle_destroy;
+
+    snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB MSD(%.16s)",
+             filename);
+
+    s->scsi_dev = scsi_disk_init(bdrv, 0, usb_msd_command_complete, s);
+    usb_msd_handle_reset((USBDevice *)s);
+    return (USBDevice *)s;
+ fail:
+    qemu_free(s);
+    return NULL;
+}
diff --git a/hw/usb-ohci.c b/hw/usb-ohci.c
new file mode 100644
index 0000000..55cb77b
--- /dev/null
+++ b/hw/usb-ohci.c
@@ -0,0 +1,1684 @@
+/*
+ * QEMU USB OHCI Emulation
+ * Copyright (c) 2004 Gianni Tedesco
+ * Copyright (c) 2006 CodeSourcery
+ * Copyright (c) 2006 Openedhand Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * TODO:
+ *  o Isochronous transfers
+ *  o Allocate bandwidth in frames properly
+ *  o Disable timers when nothing needs to be done, or remove timer usage
+ *    all together.
+ *  o Handle unrecoverable errors properly
+ *  o BIOS work to boot from USB storage
+*/
+
+#include "hw.h"
+#include "qemu-timer.h"
+#include "usb.h"
+#include "pci.h"
+#include "pxa.h"
+
+//#define DEBUG_OHCI
+/* Dump packet contents.  */
+//#define DEBUG_PACKET
+//#define DEBUG_ISOCH
+/* This causes frames to occur 1000x slower */
+//#define OHCI_TIME_WARP 1
+
+#ifdef DEBUG_OHCI
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+/* Number of Downstream Ports on the root hub.  */
+
+#define OHCI_MAX_PORTS 15
+
+static int64_t usb_frame_time;
+static int64_t usb_bit_time;
+
+typedef struct OHCIPort {
+    USBPort port;
+    uint32_t ctrl;
+} OHCIPort;
+
+enum ohci_type {
+    OHCI_TYPE_PCI,
+    OHCI_TYPE_PXA
+};
+
+typedef struct {
+    qemu_irq irq;
+    enum ohci_type type;
+    target_phys_addr_t mem_base;
+    int mem;
+    int num_ports;
+    const char *name;
+
+    QEMUTimer *eof_timer;
+    int64_t sof_time;
+
+    /* OHCI state */
+    /* Control partition */
+    uint32_t ctl, status;
+    uint32_t intr_status;
+    uint32_t intr;
+
+    /* memory pointer partition */
+    uint32_t hcca;
+    uint32_t ctrl_head, ctrl_cur;
+    uint32_t bulk_head, bulk_cur;
+    uint32_t per_cur;
+    uint32_t done;
+    int done_count;
+
+    /* Frame counter partition */
+    uint32_t fsmps:15;
+    uint32_t fit:1;
+    uint32_t fi:14;
+    uint32_t frt:1;
+    uint16_t frame_number;
+    uint16_t padding;
+    uint32_t pstart;
+    uint32_t lst;
+
+    /* Root Hub partition */
+    uint32_t rhdesc_a, rhdesc_b;
+    uint32_t rhstatus;
+    OHCIPort rhport[OHCI_MAX_PORTS];
+
+    /* PXA27x Non-OHCI events */
+    uint32_t hstatus;
+    uint32_t hmask;
+    uint32_t hreset;
+    uint32_t htest;
+
+    /* Active packets.  */
+    uint32_t old_ctl;
+    USBPacket usb_packet;
+    uint8_t usb_buf[8192];
+    uint32_t async_td;
+    int async_complete;
+
+} OHCIState;
+
+/* Host Controller Communications Area */
+struct ohci_hcca {
+    uint32_t intr[32];
+    uint16_t frame, pad;
+    uint32_t done;
+};
+
+static void ohci_bus_stop(OHCIState *ohci);
+
+/* Bitfields for the first word of an Endpoint Desciptor.  */
+#define OHCI_ED_FA_SHIFT  0
+#define OHCI_ED_FA_MASK   (0x7f<<OHCI_ED_FA_SHIFT)
+#define OHCI_ED_EN_SHIFT  7
+#define OHCI_ED_EN_MASK   (0xf<<OHCI_ED_EN_SHIFT)
+#define OHCI_ED_D_SHIFT   11
+#define OHCI_ED_D_MASK    (3<<OHCI_ED_D_SHIFT)
+#define OHCI_ED_S         (1<<13)
+#define OHCI_ED_K         (1<<14)
+#define OHCI_ED_F         (1<<15)
+#define OHCI_ED_MPS_SHIFT 16
+#define OHCI_ED_MPS_MASK  (0x7ff<<OHCI_ED_MPS_SHIFT)
+
+/* Flags in the head field of an Endpoint Desciptor.  */
+#define OHCI_ED_H         1
+#define OHCI_ED_C         2
+
+/* Bitfields for the first word of a Transfer Desciptor.  */
+#define OHCI_TD_R         (1<<18)
+#define OHCI_TD_DP_SHIFT  19
+#define OHCI_TD_DP_MASK   (3<<OHCI_TD_DP_SHIFT)
+#define OHCI_TD_DI_SHIFT  21
+#define OHCI_TD_DI_MASK   (7<<OHCI_TD_DI_SHIFT)
+#define OHCI_TD_T0        (1<<24)
+#define OHCI_TD_T1        (1<<24)
+#define OHCI_TD_EC_SHIFT  26
+#define OHCI_TD_EC_MASK   (3<<OHCI_TD_EC_SHIFT)
+#define OHCI_TD_CC_SHIFT  28
+#define OHCI_TD_CC_MASK   (0xf<<OHCI_TD_CC_SHIFT)
+
+/* Bitfields for the first word of an Isochronous Transfer Desciptor.  */
+/* CC & DI - same as in the General Transfer Desciptor */
+#define OHCI_TD_SF_SHIFT  0
+#define OHCI_TD_SF_MASK   (0xffff<<OHCI_TD_SF_SHIFT)
+#define OHCI_TD_FC_SHIFT  24
+#define OHCI_TD_FC_MASK   (7<<OHCI_TD_FC_SHIFT)
+
+/* Isochronous Transfer Desciptor - Offset / PacketStatusWord */
+#define OHCI_TD_PSW_CC_SHIFT 12
+#define OHCI_TD_PSW_CC_MASK  (0xf<<OHCI_TD_PSW_CC_SHIFT)
+#define OHCI_TD_PSW_SIZE_SHIFT 0
+#define OHCI_TD_PSW_SIZE_MASK  (0xfff<<OHCI_TD_PSW_SIZE_SHIFT)
+
+#define OHCI_PAGE_MASK    0xfffff000
+#define OHCI_OFFSET_MASK  0xfff
+
+#define OHCI_DPTR_MASK    0xfffffff0
+
+#define OHCI_BM(val, field) \
+  (((val) & OHCI_##field##_MASK) >> OHCI_##field##_SHIFT)
+
+#define OHCI_SET_BM(val, field, newval) do { \
+    val &= ~OHCI_##field##_MASK; \
+    val |= ((newval) << OHCI_##field##_SHIFT) & OHCI_##field##_MASK; \
+    } while(0)
+
+/* endpoint descriptor */
+struct ohci_ed {
+    uint32_t flags;
+    uint32_t tail;
+    uint32_t head;
+    uint32_t next;
+};
+
+/* General transfer descriptor */
+struct ohci_td {
+    uint32_t flags;
+    uint32_t cbp;
+    uint32_t next;
+    uint32_t be;
+};
+
+/* Isochronous transfer descriptor */
+struct ohci_iso_td {
+    uint32_t flags;
+    uint32_t bp;
+    uint32_t next;
+    uint32_t be;
+    uint16_t offset[8];
+};
+
+#define USB_HZ                      12000000
+
+/* OHCI Local stuff */
+#define OHCI_CTL_CBSR         ((1<<0)|(1<<1))
+#define OHCI_CTL_PLE          (1<<2)
+#define OHCI_CTL_IE           (1<<3)
+#define OHCI_CTL_CLE          (1<<4)
+#define OHCI_CTL_BLE          (1<<5)
+#define OHCI_CTL_HCFS         ((1<<6)|(1<<7))
+#define  OHCI_USB_RESET       0x00
+#define  OHCI_USB_RESUME      0x40
+#define  OHCI_USB_OPERATIONAL 0x80
+#define  OHCI_USB_SUSPEND     0xc0
+#define OHCI_CTL_IR           (1<<8)
+#define OHCI_CTL_RWC          (1<<9)
+#define OHCI_CTL_RWE          (1<<10)
+
+#define OHCI_STATUS_HCR       (1<<0)
+#define OHCI_STATUS_CLF       (1<<1)
+#define OHCI_STATUS_BLF       (1<<2)
+#define OHCI_STATUS_OCR       (1<<3)
+#define OHCI_STATUS_SOC       ((1<<6)|(1<<7))
+
+#define OHCI_INTR_SO          (1<<0) /* Scheduling overrun */
+#define OHCI_INTR_WD          (1<<1) /* HcDoneHead writeback */
+#define OHCI_INTR_SF          (1<<2) /* Start of frame */
+#define OHCI_INTR_RD          (1<<3) /* Resume detect */
+#define OHCI_INTR_UE          (1<<4) /* Unrecoverable error */
+#define OHCI_INTR_FNO         (1<<5) /* Frame number overflow */
+#define OHCI_INTR_RHSC        (1<<6) /* Root hub status change */
+#define OHCI_INTR_OC          (1<<30) /* Ownership change */
+#define OHCI_INTR_MIE         (1<<31) /* Master Interrupt Enable */
+
+#define OHCI_HCCA_SIZE        0x100
+#define OHCI_HCCA_MASK        0xffffff00
+
+#define OHCI_EDPTR_MASK       0xfffffff0
+
+#define OHCI_FMI_FI           0x00003fff
+#define OHCI_FMI_FSMPS        0xffff0000
+#define OHCI_FMI_FIT          0x80000000
+
+#define OHCI_FR_RT            (1<<31)
+
+#define OHCI_LS_THRESH        0x628
+
+#define OHCI_RHA_RW_MASK      0x00000000 /* Mask of supported features.  */
+#define OHCI_RHA_PSM          (1<<8)
+#define OHCI_RHA_NPS          (1<<9)
+#define OHCI_RHA_DT           (1<<10)
+#define OHCI_RHA_OCPM         (1<<11)
+#define OHCI_RHA_NOCP         (1<<12)
+#define OHCI_RHA_POTPGT_MASK  0xff000000
+
+#define OHCI_RHS_LPS          (1<<0)
+#define OHCI_RHS_OCI          (1<<1)
+#define OHCI_RHS_DRWE         (1<<15)
+#define OHCI_RHS_LPSC         (1<<16)
+#define OHCI_RHS_OCIC         (1<<17)
+#define OHCI_RHS_CRWE         (1<<31)
+
+#define OHCI_PORT_CCS         (1<<0)
+#define OHCI_PORT_PES         (1<<1)
+#define OHCI_PORT_PSS         (1<<2)
+#define OHCI_PORT_POCI        (1<<3)
+#define OHCI_PORT_PRS         (1<<4)
+#define OHCI_PORT_PPS         (1<<8)
+#define OHCI_PORT_LSDA        (1<<9)
+#define OHCI_PORT_CSC         (1<<16)
+#define OHCI_PORT_PESC        (1<<17)
+#define OHCI_PORT_PSSC        (1<<18)
+#define OHCI_PORT_OCIC        (1<<19)
+#define OHCI_PORT_PRSC        (1<<20)
+#define OHCI_PORT_WTC         (OHCI_PORT_CSC|OHCI_PORT_PESC|OHCI_PORT_PSSC \
+                               |OHCI_PORT_OCIC|OHCI_PORT_PRSC)
+
+#define OHCI_TD_DIR_SETUP     0x0
+#define OHCI_TD_DIR_OUT       0x1
+#define OHCI_TD_DIR_IN        0x2
+#define OHCI_TD_DIR_RESERVED  0x3
+
+#define OHCI_CC_NOERROR             0x0
+#define OHCI_CC_CRC                 0x1
+#define OHCI_CC_BITSTUFFING         0x2
+#define OHCI_CC_DATATOGGLEMISMATCH  0x3
+#define OHCI_CC_STALL               0x4
+#define OHCI_CC_DEVICENOTRESPONDING 0x5
+#define OHCI_CC_PIDCHECKFAILURE     0x6
+#define OHCI_CC_UNDEXPETEDPID       0x7
+#define OHCI_CC_DATAOVERRUN         0x8
+#define OHCI_CC_DATAUNDERRUN        0x9
+#define OHCI_CC_BUFFEROVERRUN       0xc
+#define OHCI_CC_BUFFERUNDERRUN      0xd
+
+#define OHCI_HRESET_FSBIR       (1 << 0)
+
+/* Update IRQ levels */
+static inline void ohci_intr_update(OHCIState *ohci)
+{
+    int level = 0;
+
+    if ((ohci->intr & OHCI_INTR_MIE) &&
+        (ohci->intr_status & ohci->intr))
+        level = 1;
+
+    qemu_set_irq(ohci->irq, level);
+}
+
+/* Set an interrupt */
+static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
+{
+    ohci->intr_status |= intr;
+    ohci_intr_update(ohci);
+}
+
+/* Attach or detach a device on a root hub port.  */
+static void ohci_attach(USBPort *port1, USBDevice *dev)
+{
+    OHCIState *s = port1->opaque;
+    OHCIPort *port = &s->rhport[port1->index];
+    uint32_t old_state = port->ctrl;
+
+    if (dev) {
+        if (port->port.dev) {
+            usb_attach(port1, NULL);
+        }
+        /* set connect status */
+        port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
+
+        /* update speed */
+        if (dev->speed == USB_SPEED_LOW)
+            port->ctrl |= OHCI_PORT_LSDA;
+        else
+            port->ctrl &= ~OHCI_PORT_LSDA;
+        port->port.dev = dev;
+
+        /* notify of remote-wakeup */
+        if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND)
+            ohci_set_interrupt(s, OHCI_INTR_RD);
+
+        /* send the attach message */
+        usb_send_msg(dev, USB_MSG_ATTACH);
+        dprintf("usb-ohci: Attached port %d\n", port1->index);
+    } else {
+        /* set connect status */
+        if (port->ctrl & OHCI_PORT_CCS) {
+            port->ctrl &= ~OHCI_PORT_CCS;
+            port->ctrl |= OHCI_PORT_CSC;
+        }
+        /* disable port */
+        if (port->ctrl & OHCI_PORT_PES) {
+            port->ctrl &= ~OHCI_PORT_PES;
+            port->ctrl |= OHCI_PORT_PESC;
+        }
+        dev = port->port.dev;
+        if (dev) {
+            /* send the detach message */
+            usb_send_msg(dev, USB_MSG_DETACH);
+        }
+        port->port.dev = NULL;
+        dprintf("usb-ohci: Detached port %d\n", port1->index);
+    }
+
+    if (old_state != port->ctrl)
+        ohci_set_interrupt(s, OHCI_INTR_RHSC);
+}
+
+/* Reset the controller */
+static void ohci_reset(void *opaque)
+{
+    OHCIState *ohci = opaque;
+    OHCIPort *port;
+    int i;
+
+    ohci_bus_stop(ohci);
+    ohci->ctl = 0;
+    ohci->old_ctl = 0;
+    ohci->status = 0;
+    ohci->intr_status = 0;
+    ohci->intr = OHCI_INTR_MIE;
+
+    ohci->hcca = 0;
+    ohci->ctrl_head = ohci->ctrl_cur = 0;
+    ohci->bulk_head = ohci->bulk_cur = 0;
+    ohci->per_cur = 0;
+    ohci->done = 0;
+    ohci->done_count = 7;
+
+    /* FSMPS is marked TBD in OCHI 1.0, what gives ffs?
+     * I took the value linux sets ...
+     */
+    ohci->fsmps = 0x2778;
+    ohci->fi = 0x2edf;
+    ohci->fit = 0;
+    ohci->frt = 0;
+    ohci->frame_number = 0;
+    ohci->pstart = 0;
+    ohci->lst = OHCI_LS_THRESH;
+
+    ohci->rhdesc_a = OHCI_RHA_NPS | ohci->num_ports;
+    ohci->rhdesc_b = 0x0; /* Impl. specific */
+    ohci->rhstatus = 0;
+
+    for (i = 0; i < ohci->num_ports; i++)
+      {
+        port = &ohci->rhport[i];
+        port->ctrl = 0;
+        if (port->port.dev)
+            ohci_attach(&port->port, port->port.dev);
+      }
+    if (ohci->async_td) {
+        usb_cancel_packet(&ohci->usb_packet);
+        ohci->async_td = 0;
+    }
+    dprintf("usb-ohci: Reset %s\n", ohci->name);
+}
+
+/* Get an array of dwords from main memory */
+static inline int get_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+        *buf = le32_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of dwords in to main memory */
+static inline int put_dwords(uint32_t addr, uint32_t *buf, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint32_t tmp = cpu_to_le32(*buf);
+        cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+    }
+
+    return 1;
+}
+
+/* Get an array of words from main memory */
+static inline int get_words(uint32_t addr, uint16_t *buf, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
+        *buf = le16_to_cpu(*buf);
+    }
+
+    return 1;
+}
+
+/* Put an array of words in to main memory */
+static inline int put_words(uint32_t addr, uint16_t *buf, int num)
+{
+    int i;
+
+    for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
+        uint16_t tmp = cpu_to_le16(*buf);
+        cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
+    }
+
+    return 1;
+}
+
+static inline int ohci_read_ed(uint32_t addr, struct ohci_ed *ed)
+{
+    return get_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_read_td(uint32_t addr, struct ohci_td *td)
+{
+    return get_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_read_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+    return (get_dwords(addr, (uint32_t *)td, 4) &&
+            get_words(addr + 16, td->offset, 8));
+}
+
+static inline int ohci_put_ed(uint32_t addr, struct ohci_ed *ed)
+{
+    return put_dwords(addr, (uint32_t *)ed, sizeof(*ed) >> 2);
+}
+
+static inline int ohci_put_td(uint32_t addr, struct ohci_td *td)
+{
+    return put_dwords(addr, (uint32_t *)td, sizeof(*td) >> 2);
+}
+
+static inline int ohci_put_iso_td(uint32_t addr, struct ohci_iso_td *td)
+{
+    return (put_dwords(addr, (uint32_t *)td, 4) &&
+            put_words(addr + 16, td->offset, 8));
+}
+
+/* Read/Write the contents of a TD from/to main memory.  */
+static void ohci_copy_td(struct ohci_td *td, uint8_t *buf, int len, int write)
+{
+    uint32_t ptr;
+    uint32_t n;
+
+    ptr = td->cbp;
+    n = 0x1000 - (ptr & 0xfff);
+    if (n > len)
+        n = len;
+    cpu_physical_memory_rw(ptr, buf, n, write);
+    if (n == len)
+        return;
+    ptr = td->be & ~0xfffu;
+    buf += n;
+    cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+/* Read/Write the contents of an ISO TD from/to main memory.  */
+static void ohci_copy_iso_td(uint32_t start_addr, uint32_t end_addr,
+                             uint8_t *buf, int len, int write)
+{
+    uint32_t ptr;
+    uint32_t n;
+
+    ptr = start_addr;
+    n = 0x1000 - (ptr & 0xfff);
+    if (n > len)
+        n = len;
+    cpu_physical_memory_rw(ptr, buf, n, write);
+    if (n == len)
+        return;
+    ptr = end_addr & ~0xfffu;
+    buf += n;
+    cpu_physical_memory_rw(ptr, buf, len - n, write);
+}
+
+static void ohci_process_lists(OHCIState *ohci, int completion);
+
+static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
+{
+    OHCIState *ohci = opaque;
+#ifdef DEBUG_PACKET
+    dprintf("Async packet complete\n");
+#endif
+    ohci->async_complete = 1;
+    ohci_process_lists(ohci, 1);
+}
+
+#define USUB(a, b) ((int16_t)((uint16_t)(a) - (uint16_t)(b)))
+
+static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
+                               int completion)
+{
+    int dir;
+    size_t len = 0;
+    const char *str = NULL;
+    int pid;
+    int ret;
+    int i;
+    USBDevice *dev;
+    struct ohci_iso_td iso_td;
+    uint32_t addr;
+    uint16_t starting_frame;
+    int16_t relative_frame_number;
+    int frame_count;
+    uint32_t start_offset, next_offset, end_offset = 0;
+    uint32_t start_addr, end_addr;
+
+    addr = ed->head & OHCI_DPTR_MASK;
+
+    if (!ohci_read_iso_td(addr, &iso_td)) {
+        printf("usb-ohci: ISO_TD read error at %x\n", addr);
+        return 0;
+    }
+
+    starting_frame = OHCI_BM(iso_td.flags, TD_SF);
+    frame_count = OHCI_BM(iso_td.flags, TD_FC);
+    relative_frame_number = USUB(ohci->frame_number, starting_frame); 
+
+#ifdef DEBUG_ISOCH
+    printf("--- ISO_TD ED head 0x%.8x tailp 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
+           "frame_number 0x%.8x starting_frame 0x%.8x\n"
+           "frame_count  0x%.8x relative %d\n"
+           "di 0x%.8x cc 0x%.8x\n",
+           ed->head & OHCI_DPTR_MASK, ed->tail & OHCI_DPTR_MASK,
+           iso_td.flags, iso_td.bp, iso_td.next, iso_td.be,
+           iso_td.offset[0], iso_td.offset[1], iso_td.offset[2], iso_td.offset[3],
+           iso_td.offset[4], iso_td.offset[5], iso_td.offset[6], iso_td.offset[7],
+           ohci->frame_number, starting_frame, 
+           frame_count, relative_frame_number,         
+           OHCI_BM(iso_td.flags, TD_DI), OHCI_BM(iso_td.flags, TD_CC));
+#endif
+
+    if (relative_frame_number < 0) {
+        dprintf("usb-ohci: ISO_TD R=%d < 0\n", relative_frame_number);
+        return 1;
+    } else if (relative_frame_number > frame_count) {
+        /* ISO TD expired - retire the TD to the Done Queue and continue with
+           the next ISO TD of the same ED */
+        dprintf("usb-ohci: ISO_TD R=%d > FC=%d\n", relative_frame_number, 
+               frame_count);
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+        ohci_put_iso_td(addr, &iso_td);        
+        return 0;
+    }
+
+    dir = OHCI_BM(ed->flags, ED_D);
+    switch (dir) {
+    case OHCI_TD_DIR_IN:
+        str = "in";
+        pid = USB_TOKEN_IN;
+        break;
+    case OHCI_TD_DIR_OUT:
+        str = "out";
+        pid = USB_TOKEN_OUT;
+        break;
+    case OHCI_TD_DIR_SETUP:
+        str = "setup";
+        pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        printf("usb-ohci: Bad direction %d\n", dir);
+        return 1;
+    }
+
+    if (!iso_td.bp || !iso_td.be) {
+        printf("usb-ohci: ISO_TD bp 0x%.8x be 0x%.8x\n", iso_td.bp, iso_td.be);
+        return 1;
+    }
+
+    start_offset = iso_td.offset[relative_frame_number];
+    next_offset = iso_td.offset[relative_frame_number + 1];
+
+    if (!(OHCI_BM(start_offset, TD_PSW_CC) & 0xe) || 
+        ((relative_frame_number < frame_count) && 
+         !(OHCI_BM(next_offset, TD_PSW_CC) & 0xe))) {
+        printf("usb-ohci: ISO_TD cc != not accessed 0x%.8x 0x%.8x\n",
+               start_offset, next_offset);
+        return 1;
+    }
+
+    if ((relative_frame_number < frame_count) && (start_offset > next_offset)) {
+        printf("usb-ohci: ISO_TD start_offset=0x%.8x > next_offset=0x%.8x\n",
+                start_offset, next_offset);
+        return 1;
+    }
+
+    if ((start_offset & 0x1000) == 0) {
+        start_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    } else {
+        start_addr = (iso_td.be & OHCI_PAGE_MASK) |
+            (start_offset & OHCI_OFFSET_MASK);
+    }
+
+    if (relative_frame_number < frame_count) {
+        end_offset = next_offset - 1;
+        if ((end_offset & 0x1000) == 0) {
+            end_addr = (iso_td.bp & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        } else {
+            end_addr = (iso_td.be & OHCI_PAGE_MASK) |
+                (end_offset & OHCI_OFFSET_MASK);
+        }
+    } else {
+        /* Last packet in the ISO TD */
+        end_addr = iso_td.be;
+    }
+
+    if ((start_addr & OHCI_PAGE_MASK) != (end_addr & OHCI_PAGE_MASK)) {
+        len = (end_addr & OHCI_OFFSET_MASK) + 0x1001
+            - (start_addr & OHCI_OFFSET_MASK);
+    } else {
+        len = end_addr - start_addr + 1;
+    }
+
+    if (len && dir != OHCI_TD_DIR_IN) {
+        ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, len, 0);
+    }
+
+    if (completion) {
+        ret = ohci->usb_packet.len;
+    } else {
+        ret = USB_RET_NODEV;
+        for (i = 0; i < ohci->num_ports; i++) {
+            dev = ohci->rhport[i].port.dev;
+            if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+                continue;
+            ohci->usb_packet.pid = pid;
+            ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+            ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+            ohci->usb_packet.data = ohci->usb_buf;
+            ohci->usb_packet.len = len;
+            ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+            ohci->usb_packet.complete_opaque = ohci;
+            ret = dev->handle_packet(dev, &ohci->usb_packet);
+            if (ret != USB_RET_NODEV)
+                break;
+        }
+    
+        if (ret == USB_RET_ASYNC) {
+            return 1;
+        }
+    }
+
+#ifdef DEBUG_ISOCH
+    printf("so 0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d\n",
+           start_offset, end_offset, start_addr, end_addr, str, len, ret);
+#endif
+
+    /* Writeback */
+    if (dir == OHCI_TD_DIR_IN && ret >= 0 && ret <= len) {
+        /* IN transfer succeeded */
+        ohci_copy_iso_td(start_addr, end_addr, ohci->usb_buf, ret, 1);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, ret);
+    } else if (dir == OHCI_TD_DIR_OUT && ret == len) {
+        /* OUT transfer succeeded */
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                    OHCI_CC_NOERROR);
+        OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE, 0);
+    } else {
+        if (ret > (ssize_t) len) {
+            printf("usb-ohci: DataOverrun %d > %zu\n", ret, len);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAOVERRUN);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                        len);
+        } else if (ret >= 0) {
+            printf("usb-ohci: DataUnderrun %d\n", ret);
+            OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                        OHCI_CC_DATAUNDERRUN);
+        } else {
+            switch (ret) {
+            case USB_RET_NODEV:
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_DEVICENOTRESPONDING);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            case USB_RET_NAK:
+            case USB_RET_STALL:
+                printf("usb-ohci: got NAK/STALL %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_STALL);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_SIZE,
+                            0);
+                break;
+            default:
+                printf("usb-ohci: Bad device response %d\n", ret);
+                OHCI_SET_BM(iso_td.offset[relative_frame_number], TD_PSW_CC,
+                            OHCI_CC_UNDEXPETEDPID);
+                break;
+            }
+        }
+    }
+
+    if (relative_frame_number == frame_count) {
+        /* Last data packet of ISO TD - retire the TD to the Done Queue */
+        OHCI_SET_BM(iso_td.flags, TD_CC, OHCI_CC_NOERROR);
+        ed->head &= ~OHCI_DPTR_MASK;
+        ed->head |= (iso_td.next & OHCI_DPTR_MASK);
+        iso_td.next = ohci->done;
+        ohci->done = addr;
+        i = OHCI_BM(iso_td.flags, TD_DI);
+        if (i < ohci->done_count)
+            ohci->done_count = i;
+    }
+    ohci_put_iso_td(addr, &iso_td);
+    return 1;
+}
+
+/* Service a transport descriptor.
+   Returns nonzero to terminate processing of this endpoint.  */
+
+static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
+{
+    int dir;
+    size_t len = 0;
+    const char *str = NULL;
+    int pid;
+    int ret;
+    int i;
+    USBDevice *dev;
+    struct ohci_td td;
+    uint32_t addr;
+    int flag_r;
+    int completion;
+
+    addr = ed->head & OHCI_DPTR_MASK;
+    /* See if this TD has already been submitted to the device.  */
+    completion = (addr == ohci->async_td);
+    if (completion && !ohci->async_complete) {
+#ifdef DEBUG_PACKET
+        dprintf("Skipping async TD\n");
+#endif
+        return 1;
+    }
+    if (!ohci_read_td(addr, &td)) {
+        fprintf(stderr, "usb-ohci: TD read error at %x\n", addr);
+        return 0;
+    }
+
+    dir = OHCI_BM(ed->flags, ED_D);
+    switch (dir) {
+    case OHCI_TD_DIR_OUT:
+    case OHCI_TD_DIR_IN:
+        /* Same value.  */
+        break;
+    default:
+        dir = OHCI_BM(td.flags, TD_DP);
+        break;
+    }
+
+    switch (dir) {
+    case OHCI_TD_DIR_IN:
+        str = "in";
+        pid = USB_TOKEN_IN;
+        break;
+    case OHCI_TD_DIR_OUT:
+        str = "out";
+        pid = USB_TOKEN_OUT;
+        break;
+    case OHCI_TD_DIR_SETUP:
+        str = "setup";
+        pid = USB_TOKEN_SETUP;
+        break;
+    default:
+        fprintf(stderr, "usb-ohci: Bad direction\n");
+        return 1;
+    }
+    if (td.cbp && td.be) {
+        if ((td.cbp & 0xfffff000) != (td.be & 0xfffff000)) {
+            len = (td.be & 0xfff) + 0x1001 - (td.cbp & 0xfff);
+        } else {
+            len = (td.be - td.cbp) + 1;
+        }
+
+        if (len && dir != OHCI_TD_DIR_IN && !completion) {
+            ohci_copy_td(&td, ohci->usb_buf, len, 0);
+        }
+    }
+
+    flag_r = (td.flags & OHCI_TD_R) != 0;
+#ifdef DEBUG_PACKET
+    dprintf(" TD @ 0x%.8x %u bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
+            addr, len, str, flag_r, td.cbp, td.be);
+
+    if (len > 0 && dir != OHCI_TD_DIR_IN) {
+        dprintf("  data:");
+        for (i = 0; i < len; i++)
+            printf(" %.2x", ohci->usb_buf[i]);
+        dprintf("\n");
+    }
+#endif
+    if (completion) {
+        ret = ohci->usb_packet.len;
+        ohci->async_td = 0;
+        ohci->async_complete = 0;
+    } else {
+        ret = USB_RET_NODEV;
+        for (i = 0; i < ohci->num_ports; i++) {
+            dev = ohci->rhport[i].port.dev;
+            if ((ohci->rhport[i].ctrl & OHCI_PORT_PES) == 0)
+                continue;
+
+            if (ohci->async_td) {
+                /* ??? The hardware should allow one active packet per
+                   endpoint.  We only allow one active packet per controller.
+                   This should be sufficient as long as devices respond in a
+                   timely manner.
+                 */
+#ifdef DEBUG_PACKET
+                dprintf("Too many pending packets\n");
+#endif
+                return 1;
+            }
+            ohci->usb_packet.pid = pid;
+            ohci->usb_packet.devaddr = OHCI_BM(ed->flags, ED_FA);
+            ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
+            ohci->usb_packet.data = ohci->usb_buf;
+            ohci->usb_packet.len = len;
+            ohci->usb_packet.complete_cb = ohci_async_complete_packet;
+            ohci->usb_packet.complete_opaque = ohci;
+            ret = dev->handle_packet(dev, &ohci->usb_packet);
+            if (ret != USB_RET_NODEV)
+                break;
+        }
+#ifdef DEBUG_PACKET
+        dprintf("ret=%d\n", ret);
+#endif
+        if (ret == USB_RET_ASYNC) {
+            ohci->async_td = addr;
+            return 1;
+        }
+    }
+    if (ret >= 0) {
+        if (dir == OHCI_TD_DIR_IN) {
+            ohci_copy_td(&td, ohci->usb_buf, ret, 1);
+#ifdef DEBUG_PACKET
+            dprintf("  data:");
+            for (i = 0; i < ret; i++)
+                printf(" %.2x", ohci->usb_buf[i]);
+            dprintf("\n");
+#endif
+        } else {
+            ret = len;
+        }
+    }
+
+    /* Writeback */
+    if (ret == len || (dir == OHCI_TD_DIR_IN && ret >= 0 && flag_r)) {
+        /* Transmission succeeded.  */
+        if (ret == len) {
+            td.cbp = 0;
+        } else {
+            td.cbp += ret;
+            if ((td.cbp & 0xfff) + ret > 0xfff) {
+                td.cbp &= 0xfff;
+                td.cbp |= td.be & ~0xfff;
+            }
+        }
+        td.flags |= OHCI_TD_T1;
+        td.flags ^= OHCI_TD_T0;
+        OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_NOERROR);
+        OHCI_SET_BM(td.flags, TD_EC, 0);
+
+        ed->head &= ~OHCI_ED_C;
+        if (td.flags & OHCI_TD_T0)
+            ed->head |= OHCI_ED_C;
+    } else {
+        if (ret >= 0) {
+            dprintf("usb-ohci: Underrun\n");
+            OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAUNDERRUN);
+        } else {
+            switch (ret) {
+            case USB_RET_NODEV:
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
+            case USB_RET_NAK:
+                dprintf("usb-ohci: got NAK\n");
+                return 1;
+            case USB_RET_STALL:
+                dprintf("usb-ohci: got STALL\n");
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_STALL);
+                break;
+            case USB_RET_BABBLE:
+                dprintf("usb-ohci: got BABBLE\n");
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DATAOVERRUN);
+                break;
+            default:
+                fprintf(stderr, "usb-ohci: Bad device response %d\n", ret);
+                OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_UNDEXPETEDPID);
+                OHCI_SET_BM(td.flags, TD_EC, 3);
+                break;
+            }
+        }
+        ed->head |= OHCI_ED_H;
+    }
+
+    /* Retire this TD */
+    ed->head &= ~OHCI_DPTR_MASK;
+    ed->head |= td.next & OHCI_DPTR_MASK;
+    td.next = ohci->done;
+    ohci->done = addr;
+    i = OHCI_BM(td.flags, TD_DI);
+    if (i < ohci->done_count)
+        ohci->done_count = i;
+    ohci_put_td(addr, &td);
+    return OHCI_BM(td.flags, TD_CC) != OHCI_CC_NOERROR;
+}
+
+/* Service an endpoint list.  Returns nonzero if active TD were found.  */
+static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion)
+{
+    struct ohci_ed ed;
+    uint32_t next_ed;
+    uint32_t cur;
+    int active;
+
+    active = 0;
+
+    if (head == 0)
+        return 0;
+
+    for (cur = head; cur; cur = next_ed) {
+        if (!ohci_read_ed(cur, &ed)) {
+            fprintf(stderr, "usb-ohci: ED read error at %x\n", cur);
+            return 0;
+        }
+
+        next_ed = ed.next & OHCI_DPTR_MASK;
+
+        if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) {
+            uint32_t addr;
+            /* Cancel pending packets for ED that have been paused.  */
+            addr = ed.head & OHCI_DPTR_MASK;
+            if (ohci->async_td && addr == ohci->async_td) {
+                usb_cancel_packet(&ohci->usb_packet);
+                ohci->async_td = 0;
+            }
+            continue;
+        }
+
+        while ((ed.head & OHCI_DPTR_MASK) != ed.tail) {
+#ifdef DEBUG_PACKET
+            dprintf("ED @ 0x%.8x fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u "
+                    "h=%u c=%u\n  head=0x%.8x tailp=0x%.8x next=0x%.8x\n", cur,
+                    OHCI_BM(ed.flags, ED_FA), OHCI_BM(ed.flags, ED_EN),
+                    OHCI_BM(ed.flags, ED_D), (ed.flags & OHCI_ED_S)!= 0,
+                    (ed.flags & OHCI_ED_K) != 0, (ed.flags & OHCI_ED_F) != 0,
+                    OHCI_BM(ed.flags, ED_MPS), (ed.head & OHCI_ED_H) != 0,
+                    (ed.head & OHCI_ED_C) != 0, ed.head & OHCI_DPTR_MASK,
+                    ed.tail & OHCI_DPTR_MASK, ed.next & OHCI_DPTR_MASK);
+#endif
+            active = 1;
+
+            if ((ed.flags & OHCI_ED_F) == 0) {
+                if (ohci_service_td(ohci, &ed))
+                    break;
+            } else {
+                /* Handle isochronous endpoints */
+                if (ohci_service_iso_td(ohci, &ed, completion))
+                    break;
+            }
+        }
+
+        ohci_put_ed(cur, &ed);
+    }
+
+    return active;
+}
+
+/* Generate a SOF event, and set a timer for EOF */
+static void ohci_sof(OHCIState *ohci)
+{
+    ohci->sof_time = qemu_get_clock(vm_clock);
+    qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
+    ohci_set_interrupt(ohci, OHCI_INTR_SF);
+}
+
+/* Process Control and Bulk lists.  */
+static void ohci_process_lists(OHCIState *ohci, int completion)
+{
+    if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
+        if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
+          dprintf("usb-ohci: head %x, cur %x\n",
+                          ohci->ctrl_head, ohci->ctrl_cur);
+        if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
+            ohci->ctrl_cur = 0;
+            ohci->status &= ~OHCI_STATUS_CLF;
+        }
+    }
+
+    if ((ohci->ctl & OHCI_CTL_BLE) && (ohci->status & OHCI_STATUS_BLF)) {
+        if (!ohci_service_ed_list(ohci, ohci->bulk_head, completion)) {
+            ohci->bulk_cur = 0;
+            ohci->status &= ~OHCI_STATUS_BLF;
+        }
+    }
+}
+
+/* Do frame processing on frame boundary */
+static void ohci_frame_boundary(void *opaque)
+{
+    OHCIState *ohci = opaque;
+    struct ohci_hcca hcca;
+
+    cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 0);
+
+    /* Process all the lists at the end of the frame */
+    if (ohci->ctl & OHCI_CTL_PLE) {
+        int n;
+
+        n = ohci->frame_number & 0x1f;
+        ohci_service_ed_list(ohci, le32_to_cpu(hcca.intr[n]), 0);
+    }
+
+    /* Cancel all pending packets if either of the lists has been disabled.  */
+    if (ohci->async_td &&
+        ohci->old_ctl & (~ohci->ctl) & (OHCI_CTL_BLE | OHCI_CTL_CLE)) {
+        usb_cancel_packet(&ohci->usb_packet);
+        ohci->async_td = 0;
+    }
+    ohci->old_ctl = ohci->ctl;
+    ohci_process_lists(ohci, 0);
+
+    /* Frame boundary, so do EOF stuf here */
+    ohci->frt = ohci->fit;
+
+    /* XXX: endianness */
+    ohci->frame_number = (ohci->frame_number + 1) & 0xffff;
+    hcca.frame = cpu_to_le32(ohci->frame_number);
+
+    if (ohci->done_count == 0 && !(ohci->intr_status & OHCI_INTR_WD)) {
+        if (!ohci->done)
+            abort();
+        if (ohci->intr & ohci->intr_status)
+            ohci->done |= 1;
+        hcca.done = cpu_to_le32(ohci->done);
+        ohci->done = 0;
+        ohci->done_count = 7;
+        ohci_set_interrupt(ohci, OHCI_INTR_WD);
+    }
+
+    if (ohci->done_count != 7 && ohci->done_count != 0)
+        ohci->done_count--;
+
+    /* Do SOF stuff here */
+    ohci_sof(ohci);
+
+    /* Writeback HCCA */
+    cpu_physical_memory_rw(ohci->hcca, (uint8_t *)&hcca, sizeof(hcca), 1);
+}
+
+/* Start sending SOF tokens across the USB bus, lists are processed in
+ * next frame
+ */
+static int ohci_bus_start(OHCIState *ohci)
+{
+    ohci->eof_timer = qemu_new_timer(vm_clock,
+                    ohci_frame_boundary,
+                    ohci);
+
+    if (ohci->eof_timer == NULL) {
+        fprintf(stderr, "usb-ohci: %s: qemu_new_timer failed\n", ohci->name);
+        /* TODO: Signal unrecoverable error */
+        return 0;
+    }
+
+    dprintf("usb-ohci: %s: USB Operational\n", ohci->name);
+
+    ohci_sof(ohci);
+
+    return 1;
+}
+
+/* Stop sending SOF tokens on the bus */
+static void ohci_bus_stop(OHCIState *ohci)
+{
+    if (ohci->eof_timer)
+        qemu_del_timer(ohci->eof_timer);
+    ohci->eof_timer = NULL;
+}
+
+/* Sets a flag in a port status register but only set it if the port is
+ * connected, if not set ConnectStatusChange flag. If flag is enabled
+ * return 1.
+ */
+static int ohci_port_set_if_connected(OHCIState *ohci, int i, uint32_t val)
+{
+    int ret = 1;
+
+    /* writing a 0 has no effect */
+    if (val == 0)
+        return 0;
+
+    /* If CurrentConnectStatus is cleared we set
+     * ConnectStatusChange
+     */
+    if (!(ohci->rhport[i].ctrl & OHCI_PORT_CCS)) {
+        ohci->rhport[i].ctrl |= OHCI_PORT_CSC;
+        if (ohci->rhstatus & OHCI_RHS_DRWE) {
+            /* TODO: CSC is a wakeup event */
+        }
+        return 0;
+    }
+
+    if (ohci->rhport[i].ctrl & val)
+        ret = 0;
+
+    /* set the bit */
+    ohci->rhport[i].ctrl |= val;
+
+    return ret;
+}
+
+/* Set the frame interval - frame interval toggle is manipulated by the hcd only */
+static void ohci_set_frame_interval(OHCIState *ohci, uint16_t val)
+{
+    val &= OHCI_FMI_FI;
+
+    if (val != ohci->fi) {
+        dprintf("usb-ohci: %s: FrameInterval = 0x%x (%u)\n",
+            ohci->name, ohci->fi, ohci->fi);
+    }
+
+    ohci->fi = val;
+}
+
+static void ohci_port_power(OHCIState *ohci, int i, int p)
+{
+    if (p) {
+        ohci->rhport[i].ctrl |= OHCI_PORT_PPS;
+    } else {
+        ohci->rhport[i].ctrl &= ~(OHCI_PORT_PPS|
+                    OHCI_PORT_CCS|
+                    OHCI_PORT_PSS|
+                    OHCI_PORT_PRS);
+    }
+}
+
+/* Set HcControlRegister */
+static void ohci_set_ctl(OHCIState *ohci, uint32_t val)
+{
+    uint32_t old_state;
+    uint32_t new_state;
+
+    old_state = ohci->ctl & OHCI_CTL_HCFS;
+    ohci->ctl = val;
+    new_state = ohci->ctl & OHCI_CTL_HCFS;
+
+    /* no state change */
+    if (old_state == new_state)
+        return;
+
+    switch (new_state) {
+    case OHCI_USB_OPERATIONAL:
+        ohci_bus_start(ohci);
+        break;
+    case OHCI_USB_SUSPEND:
+        ohci_bus_stop(ohci);
+        dprintf("usb-ohci: %s: USB Suspended\n", ohci->name);
+        break;
+    case OHCI_USB_RESUME:
+        dprintf("usb-ohci: %s: USB Resume\n", ohci->name);
+        break;
+    case OHCI_USB_RESET:
+        ohci_reset(ohci);
+        dprintf("usb-ohci: %s: USB Reset\n", ohci->name);
+        break;
+    }
+}
+
+static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
+{
+    uint16_t fr;
+    int64_t tks;
+
+    if ((ohci->ctl & OHCI_CTL_HCFS) != OHCI_USB_OPERATIONAL)
+        return (ohci->frt << 31);
+
+    /* Being in USB operational state guarnatees sof_time was
+     * set already.
+     */
+    tks = qemu_get_clock(vm_clock) - ohci->sof_time;
+
+    /* avoid muldiv if possible */
+    if (tks >= usb_frame_time)
+        return (ohci->frt << 31);
+
+    tks = muldiv64(1, tks, usb_bit_time);
+    fr = (uint16_t)(ohci->fi - tks);
+
+    return (ohci->frt << 31) | fr;
+}
+
+
+/* Set root hub status */
+static void ohci_set_hub_status(OHCIState *ohci, uint32_t val)
+{
+    uint32_t old_state;
+
+    old_state = ohci->rhstatus;
+
+    /* write 1 to clear OCIC */
+    if (val & OHCI_RHS_OCIC)
+        ohci->rhstatus &= ~OHCI_RHS_OCIC;
+
+    if (val & OHCI_RHS_LPS) {
+        int i;
+
+        for (i = 0; i < ohci->num_ports; i++)
+            ohci_port_power(ohci, i, 0);
+        dprintf("usb-ohci: powered down all ports\n");
+    }
+
+    if (val & OHCI_RHS_LPSC) {
+        int i;
+
+        for (i = 0; i < ohci->num_ports; i++)
+            ohci_port_power(ohci, i, 1);
+        dprintf("usb-ohci: powered up all ports\n");
+    }
+
+    if (val & OHCI_RHS_DRWE)
+        ohci->rhstatus |= OHCI_RHS_DRWE;
+
+    if (val & OHCI_RHS_CRWE)
+        ohci->rhstatus &= ~OHCI_RHS_DRWE;
+
+    if (old_state != ohci->rhstatus)
+        ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+}
+
+/* Set root hub port status */
+static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
+{
+    uint32_t old_state;
+    OHCIPort *port;
+
+    port = &ohci->rhport[portnum];
+    old_state = port->ctrl;
+
+    /* Write to clear CSC, PESC, PSSC, OCIC, PRSC */
+    if (val & OHCI_PORT_WTC)
+        port->ctrl &= ~(val & OHCI_PORT_WTC);
+
+    if (val & OHCI_PORT_CCS)
+        port->ctrl &= ~OHCI_PORT_PES;
+
+    ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
+
+    if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS))
+        dprintf("usb-ohci: port %d: SUSPEND\n", portnum);
+
+    if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
+        dprintf("usb-ohci: port %d: RESET\n", portnum);
+        usb_send_msg(port->port.dev, USB_MSG_RESET);
+        port->ctrl &= ~OHCI_PORT_PRS;
+        /* ??? Should this also set OHCI_PORT_PESC.  */
+        port->ctrl |= OHCI_PORT_PES | OHCI_PORT_PRSC;
+    }
+
+    /* Invert order here to ensure in ambiguous case, device is
+     * powered up...
+     */
+    if (val & OHCI_PORT_LSDA)
+        ohci_port_power(ohci, portnum, 0);
+    if (val & OHCI_PORT_PPS)
+        ohci_port_power(ohci, portnum, 1);
+
+    if (old_state != port->ctrl)
+        ohci_set_interrupt(ohci, OHCI_INTR_RHSC);
+
+    return;
+}
+
+static uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
+{
+    OHCIState *ohci = ptr;
+
+    addr -= ohci->mem_base;
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ohci: Mis-aligned read\n");
+        return 0xffffffff;
+    }
+
+    if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+        /* HcRhPortStatus */
+        return ohci->rhport[(addr - 0x54) >> 2].ctrl | OHCI_PORT_PPS;
+    }
+
+    switch (addr >> 2) {
+    case 0: /* HcRevision */
+        return 0x10;
+
+    case 1: /* HcControl */
+        return ohci->ctl;
+
+    case 2: /* HcCommandStatus */
+        return ohci->status;
+
+    case 3: /* HcInterruptStatus */
+        return ohci->intr_status;
+
+    case 4: /* HcInterruptEnable */
+    case 5: /* HcInterruptDisable */
+        return ohci->intr;
+
+    case 6: /* HcHCCA */
+        return ohci->hcca;
+
+    case 7: /* HcPeriodCurrentED */
+        return ohci->per_cur;
+
+    case 8: /* HcControlHeadED */
+        return ohci->ctrl_head;
+
+    case 9: /* HcControlCurrentED */
+        return ohci->ctrl_cur;
+
+    case 10: /* HcBulkHeadED */
+        return ohci->bulk_head;
+
+    case 11: /* HcBulkCurrentED */
+        return ohci->bulk_cur;
+
+    case 12: /* HcDoneHead */
+        return ohci->done;
+
+    case 13: /* HcFmInterval */
+        return (ohci->fit << 31) | (ohci->fsmps << 16) | (ohci->fi);
+
+    case 14: /* HcFmRemaining */
+        return ohci_get_frame_remaining(ohci);
+
+    case 15: /* HcFmNumber */
+        return ohci->frame_number;
+
+    case 16: /* HcPeriodicStart */
+        return ohci->pstart;
+
+    case 17: /* HcLSThreshold */
+        return ohci->lst;
+
+    case 18: /* HcRhDescriptorA */
+        return ohci->rhdesc_a;
+
+    case 19: /* HcRhDescriptorB */
+        return ohci->rhdesc_b;
+
+    case 20: /* HcRhStatus */
+        return ohci->rhstatus;
+
+    /* PXA27x specific registers */
+    case 24: /* HcStatus */
+        return ohci->hstatus & ohci->hmask;
+
+    case 25: /* HcHReset */
+        return ohci->hreset;
+
+    case 26: /* HcHInterruptEnable */
+        return ohci->hmask;
+
+    case 27: /* HcHInterruptTest */
+        return ohci->htest;
+
+    default:
+        fprintf(stderr, "ohci_read: Bad offset %x\n", (int)addr);
+        return 0xffffffff;
+    }
+}
+
+static void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
+{
+    OHCIState *ohci = ptr;
+
+    addr -= ohci->mem_base;
+
+    /* Only aligned reads are allowed on OHCI */
+    if (addr & 3) {
+        fprintf(stderr, "usb-ohci: Mis-aligned write\n");
+        return;
+    }
+
+    if (addr >= 0x54 && addr < 0x54 + ohci->num_ports * 4) {
+        /* HcRhPortStatus */
+        ohci_port_set_status(ohci, (addr - 0x54) >> 2, val);
+        return;
+    }
+
+    switch (addr >> 2) {
+    case 1: /* HcControl */
+        ohci_set_ctl(ohci, val);
+        break;
+
+    case 2: /* HcCommandStatus */
+        /* SOC is read-only */
+        val = (val & ~OHCI_STATUS_SOC);
+
+        /* Bits written as '0' remain unchanged in the register */
+        ohci->status |= val;
+
+        if (ohci->status & OHCI_STATUS_HCR)
+            ohci_reset(ohci);
+        break;
+
+    case 3: /* HcInterruptStatus */
+        ohci->intr_status &= ~val;
+        ohci_intr_update(ohci);
+        break;
+
+    case 4: /* HcInterruptEnable */
+        ohci->intr |= val;
+        ohci_intr_update(ohci);
+        break;
+
+    case 5: /* HcInterruptDisable */
+        ohci->intr &= ~val;
+        ohci_intr_update(ohci);
+        break;
+
+    case 6: /* HcHCCA */
+        ohci->hcca = val & OHCI_HCCA_MASK;
+        break;
+
+    case 8: /* HcControlHeadED */
+        ohci->ctrl_head = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 9: /* HcControlCurrentED */
+        ohci->ctrl_cur = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 10: /* HcBulkHeadED */
+        ohci->bulk_head = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 11: /* HcBulkCurrentED */
+        ohci->bulk_cur = val & OHCI_EDPTR_MASK;
+        break;
+
+    case 13: /* HcFmInterval */
+        ohci->fsmps = (val & OHCI_FMI_FSMPS) >> 16;
+        ohci->fit = (val & OHCI_FMI_FIT) >> 31;
+        ohci_set_frame_interval(ohci, val);
+        break;
+
+    case 15: /* HcFmNumber */
+        break;
+
+    case 16: /* HcPeriodicStart */
+        ohci->pstart = val & 0xffff;
+        break;
+
+    case 17: /* HcLSThreshold */
+        ohci->lst = val & 0xffff;
+        break;
+
+    case 18: /* HcRhDescriptorA */
+        ohci->rhdesc_a &= ~OHCI_RHA_RW_MASK;
+        ohci->rhdesc_a |= val & OHCI_RHA_RW_MASK;
+        break;
+
+    case 19: /* HcRhDescriptorB */
+        break;
+
+    case 20: /* HcRhStatus */
+        ohci_set_hub_status(ohci, val);
+        break;
+
+    /* PXA27x specific registers */
+    case 24: /* HcStatus */
+        ohci->hstatus &= ~(val & ohci->hmask);
+
+    case 25: /* HcHReset */
+        ohci->hreset = val & ~OHCI_HRESET_FSBIR;
+        if (val & OHCI_HRESET_FSBIR)
+            ohci_reset(ohci);
+        break;
+
+    case 26: /* HcHInterruptEnable */
+        ohci->hmask = val;
+        break;
+
+    case 27: /* HcHInterruptTest */
+        ohci->htest = val;
+        break;
+
+    default:
+        fprintf(stderr, "ohci_write: Bad offset %x\n", (int)addr);
+        break;
+    }
+}
+
+/* Only dword reads are defined on OHCI register space */
+static CPUReadMemoryFunc *ohci_readfn[3]={
+    ohci_mem_read,
+    ohci_mem_read,
+    ohci_mem_read
+};
+
+/* Only dword writes are defined on OHCI register space */
+static CPUWriteMemoryFunc *ohci_writefn[3]={
+    ohci_mem_write,
+    ohci_mem_write,
+    ohci_mem_write
+};
+
+static void usb_ohci_init(OHCIState *ohci, int num_ports, int devfn,
+            qemu_irq irq, enum ohci_type type, const char *name)
+{
+    int i;
+
+    if (usb_frame_time == 0) {
+#ifdef OHCI_TIME_WARP
+        usb_frame_time = ticks_per_sec;
+        usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ/1000);
+#else
+        usb_frame_time = muldiv64(1, ticks_per_sec, 1000);
+        if (ticks_per_sec >= USB_HZ) {
+            usb_bit_time = muldiv64(1, ticks_per_sec, USB_HZ);
+        } else {
+            usb_bit_time = 1;
+        }
+#endif
+        dprintf("usb-ohci: usb_bit_time=%lli usb_frame_time=%lli\n",
+                usb_frame_time, usb_bit_time);
+    }
+
+    ohci->mem = cpu_register_io_memory(0, ohci_readfn, ohci_writefn, ohci);
+    ohci->name = name;
+
+    ohci->irq = irq;
+    ohci->type = type;
+
+    ohci->num_ports = num_ports;
+    for (i = 0; i < num_ports; i++) {
+        qemu_register_usb_port(&ohci->rhport[i].port, ohci, i, ohci_attach);
+    }
+
+    ohci->async_td = 0;
+    qemu_register_reset(ohci_reset, ohci);
+    ohci_reset(ohci);
+}
+
+typedef struct {
+    PCIDevice pci_dev;
+    OHCIState state;
+} OHCIPCIState;
+
+static void ohci_mapfunc(PCIDevice *pci_dev, int i,
+            uint32_t addr, uint32_t size, int type)
+{
+    OHCIPCIState *ohci = (OHCIPCIState *)pci_dev;
+    ohci->state.mem_base = addr;
+    cpu_register_physical_memory(addr, size, ohci->state.mem);
+}
+
+void usb_ohci_init_pci(struct PCIBus *bus, int num_ports, int devfn)
+{
+    OHCIPCIState *ohci;
+    int vid = 0x106b;
+    int did = 0x003f;
+
+    ohci = (OHCIPCIState *)pci_register_device(bus, "OHCI USB", sizeof(*ohci),
+                                               devfn, NULL, NULL);
+    if (ohci == NULL) {
+        fprintf(stderr, "usb-ohci: Failed to register PCI device\n");
+        return;
+    }
+
+    ohci->pci_dev.config[0x00] = vid & 0xff;
+    ohci->pci_dev.config[0x01] = (vid >> 8) & 0xff;
+    ohci->pci_dev.config[0x02] = did & 0xff;
+    ohci->pci_dev.config[0x03] = (did >> 8) & 0xff;
+    ohci->pci_dev.config[0x09] = 0x10; /* OHCI */
+    ohci->pci_dev.config[0x0a] = 0x3;
+    ohci->pci_dev.config[0x0b] = 0xc;
+    ohci->pci_dev.config[0x3d] = 0x01; /* interrupt pin 1 */
+
+    usb_ohci_init(&ohci->state, num_ports, devfn, ohci->pci_dev.irq[0],
+                  OHCI_TYPE_PCI, ohci->pci_dev.name);
+
+    pci_register_io_region((struct PCIDevice *)ohci, 0, 256,
+                           PCI_ADDRESS_SPACE_MEM, ohci_mapfunc);
+}
+
+void usb_ohci_init_pxa(target_phys_addr_t base, int num_ports, int devfn,
+                       qemu_irq irq)
+{
+    OHCIState *ohci = (OHCIState *)qemu_mallocz(sizeof(OHCIState));
+
+    usb_ohci_init(ohci, num_ports, devfn, irq,
+                  OHCI_TYPE_PXA, "OHCI USB");
+    ohci->mem_base = base;
+
+    cpu_register_physical_memory(ohci->mem_base, 0x1000, ohci->mem);
+}
diff --git a/hw/usb.c b/hw/usb.c
new file mode 100644
index 0000000..c17266d
--- /dev/null
+++ b/hw/usb.c
@@ -0,0 +1,231 @@
+/*
+ * QEMU USB emulation
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 2008 Generic packet handler rewrite by Max Krasnyansky
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "usb.h"
+
+void usb_attach(USBPort *port, USBDevice *dev)
+{
+    port->attach(port, dev);
+}
+
+/**********************/
+
+/* generic USB device helpers (you are not forced to use them when
+   writing your USB device driver, but they help handling the
+   protocol)
+*/
+
+#define SETUP_STATE_IDLE 0
+#define SETUP_STATE_DATA 1
+#define SETUP_STATE_ACK  2
+
+static int do_token_setup(USBDevice *s, USBPacket *p)
+{
+    int request, value, index;
+    int ret = 0;
+
+    if (p->len != 8)
+        return USB_RET_STALL;
+ 
+    memcpy(s->setup_buf, p->data, 8);
+    s->setup_len   = (s->setup_buf[7] << 8) | s->setup_buf[6];
+    s->setup_index = 0;
+
+    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
+    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
+ 
+    if (s->setup_buf[0] & USB_DIR_IN) {
+        ret = s->handle_control(s, request, value, index, 
+                                s->setup_len, s->data_buf);
+        if (ret < 0)
+            return ret;
+
+        if (ret < s->setup_len)
+            s->setup_len = ret;
+        s->setup_state = SETUP_STATE_DATA;
+    } else {
+        if (s->setup_len == 0)
+            s->setup_state = SETUP_STATE_ACK;
+        else
+            s->setup_state = SETUP_STATE_DATA;
+    }
+
+    return ret;
+}
+
+static int do_token_in(USBDevice *s, USBPacket *p)
+{
+    int request, value, index;
+    int ret = 0;
+
+    if (p->devep != 0)
+        return s->handle_data(s, p);
+
+    request = (s->setup_buf[0] << 8) | s->setup_buf[1];
+    value   = (s->setup_buf[3] << 8) | s->setup_buf[2];
+    index   = (s->setup_buf[5] << 8) | s->setup_buf[4];
+ 
+    switch(s->setup_state) {
+    case SETUP_STATE_ACK:
+        if (!(s->setup_buf[0] & USB_DIR_IN)) {
+            s->setup_state = SETUP_STATE_IDLE;
+            ret = s->handle_control(s, request, value, index,
+                                    s->setup_len, s->data_buf);
+            if (ret > 0)
+                return 0;
+            return ret;
+        }
+
+        /* return 0 byte */
+        return 0;
+
+    case SETUP_STATE_DATA:
+        if (s->setup_buf[0] & USB_DIR_IN) {
+            int len = s->setup_len - s->setup_index;
+            if (len > p->len)
+                len = p->len;
+            memcpy(p->data, s->data_buf + s->setup_index, len);
+            s->setup_index += len;
+            if (s->setup_index >= s->setup_len)
+                s->setup_state = SETUP_STATE_ACK;
+            return len;
+        }
+
+        s->setup_state = SETUP_STATE_IDLE;
+        return USB_RET_STALL;
+
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+static int do_token_out(USBDevice *s, USBPacket *p)
+{
+    if (p->devep != 0)
+        return s->handle_data(s, p);
+
+    switch(s->setup_state) {
+    case SETUP_STATE_ACK:
+        if (s->setup_buf[0] & USB_DIR_IN) {
+            s->setup_state = SETUP_STATE_IDLE;
+            /* transfer OK */
+        } else {
+            /* ignore additional output */
+        }
+        return 0;
+
+    case SETUP_STATE_DATA:
+        if (!(s->setup_buf[0] & USB_DIR_IN)) {
+            int len = s->setup_len - s->setup_index;
+            if (len > p->len)
+                len = p->len;
+            memcpy(s->data_buf + s->setup_index, p->data, len);
+            s->setup_index += len;
+            if (s->setup_index >= s->setup_len)
+                s->setup_state = SETUP_STATE_ACK;
+            return len;
+        }
+
+        s->setup_state = SETUP_STATE_IDLE;
+        return USB_RET_STALL;
+
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+/*
+ * Generic packet handler.
+ * Called by the HC (host controller).
+ *
+ * Returns length of the transaction or one of the USB_RET_XXX codes.
+ */
+int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
+{
+    switch(p->pid) {
+    case USB_MSG_ATTACH:
+        s->state = USB_STATE_ATTACHED;
+        return 0;
+
+    case USB_MSG_DETACH:
+        s->state = USB_STATE_NOTATTACHED;
+        return 0;
+
+    case USB_MSG_RESET:
+        s->remote_wakeup = 0;
+        s->addr = 0;
+        s->state = USB_STATE_DEFAULT;
+        s->handle_reset(s);
+        return 0;
+    }
+
+    /* Rest of the PIDs must match our address */
+    if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
+        return USB_RET_NODEV;
+
+    switch (p->pid) {
+    case USB_TOKEN_SETUP:
+        return do_token_setup(s, p);
+
+    case USB_TOKEN_IN:
+        return do_token_in(s, p);
+
+    case USB_TOKEN_OUT:
+        return do_token_out(s, p);
+ 
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+/* XXX: fix overflow */
+int set_usb_string(uint8_t *buf, const char *str)
+{
+    int len, i;
+    uint8_t *q;
+
+    q = buf;
+    len = strlen(str);
+    *q++ = 2 * len + 2;
+    *q++ = 3;
+    for(i = 0; i < len; i++) {
+        *q++ = str[i];
+        *q++ = 0;
+    }
+    return q - buf;
+}
+
+/* Send an internal message to a USB device.  */
+void usb_send_msg(USBDevice *dev, int msg)
+{
+    USBPacket p;
+    memset(&p, 0, sizeof(p));
+    p.pid = msg;
+    dev->handle_packet(dev, &p);
+
+    /* This _must_ be synchronous */
+}
diff --git a/hw/usb.h b/hw/usb.h
new file mode 100644
index 0000000..1a353bb
--- /dev/null
+++ b/hw/usb.h
@@ -0,0 +1,291 @@
+/*
+ * QEMU USB API
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define USB_TOKEN_SETUP 0x2d
+#define USB_TOKEN_IN    0x69 /* device -> host */
+#define USB_TOKEN_OUT   0xe1 /* host -> device */
+
+/* specific usb messages, also sent in the 'pid' parameter */
+#define USB_MSG_ATTACH   0x100
+#define USB_MSG_DETACH   0x101
+#define USB_MSG_RESET    0x102
+
+#define USB_RET_NODEV  (-1)
+#define USB_RET_NAK    (-2)
+#define USB_RET_STALL  (-3)
+#define USB_RET_BABBLE (-4)
+#define USB_RET_ASYNC  (-5)
+
+#define USB_SPEED_LOW   0
+#define USB_SPEED_FULL  1
+#define USB_SPEED_HIGH  2
+
+#define USB_STATE_NOTATTACHED 0
+#define USB_STATE_ATTACHED    1
+//#define USB_STATE_POWERED     2
+#define USB_STATE_DEFAULT     3
+//#define USB_STATE_ADDRESS     4
+//#define	USB_STATE_CONFIGURED  5
+#define USB_STATE_SUSPENDED   6
+
+#define USB_CLASS_AUDIO			1
+#define USB_CLASS_COMM			2
+#define USB_CLASS_HID			3
+#define USB_CLASS_PHYSICAL		5
+#define USB_CLASS_STILL_IMAGE		6
+#define USB_CLASS_PRINTER		7
+#define USB_CLASS_MASS_STORAGE		8
+#define USB_CLASS_HUB			9
+#define USB_CLASS_CDC_DATA		0x0a
+#define USB_CLASS_CSCID			0x0b
+#define USB_CLASS_CONTENT_SEC		0x0d
+#define USB_CLASS_APP_SPEC		0xfe
+#define USB_CLASS_VENDOR_SPEC		0xff
+
+#define USB_DIR_OUT			0
+#define USB_DIR_IN			0x80
+
+#define USB_TYPE_MASK			(0x03 << 5)
+#define USB_TYPE_STANDARD		(0x00 << 5)
+#define USB_TYPE_CLASS			(0x01 << 5)
+#define USB_TYPE_VENDOR			(0x02 << 5)
+#define USB_TYPE_RESERVED		(0x03 << 5)
+
+#define USB_RECIP_MASK			0x1f
+#define USB_RECIP_DEVICE		0x00
+#define USB_RECIP_INTERFACE		0x01
+#define USB_RECIP_ENDPOINT		0x02
+#define USB_RECIP_OTHER			0x03
+
+#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
+#define InterfaceRequest \
+        ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define InterfaceOutRequest \
+        ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
+#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+#define EndpointOutRequest \
+        ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
+
+#define USB_REQ_GET_STATUS		0x00
+#define USB_REQ_CLEAR_FEATURE		0x01
+#define USB_REQ_SET_FEATURE		0x03
+#define USB_REQ_SET_ADDRESS		0x05
+#define USB_REQ_GET_DESCRIPTOR		0x06
+#define USB_REQ_SET_DESCRIPTOR		0x07
+#define USB_REQ_GET_CONFIGURATION	0x08
+#define USB_REQ_SET_CONFIGURATION	0x09
+#define USB_REQ_GET_INTERFACE		0x0A
+#define USB_REQ_SET_INTERFACE		0x0B
+#define USB_REQ_SYNCH_FRAME		0x0C
+
+#define USB_DEVICE_SELF_POWERED		0
+#define USB_DEVICE_REMOTE_WAKEUP	1
+
+#define USB_DT_DEVICE			0x01
+#define USB_DT_CONFIG			0x02
+#define USB_DT_STRING			0x03
+#define USB_DT_INTERFACE		0x04
+#define USB_DT_ENDPOINT			0x05
+
+#define USB_ENDPOINT_XFER_CONTROL	0
+#define USB_ENDPOINT_XFER_ISOC		1
+#define USB_ENDPOINT_XFER_BULK		2
+#define USB_ENDPOINT_XFER_INT		3
+
+typedef struct USBPort USBPort;
+typedef struct USBDevice USBDevice;
+typedef struct USBPacket USBPacket;
+
+/* definition of a USB device */
+struct USBDevice {
+    void *opaque;
+
+    /* 
+     * Process USB packet. 
+     * Called by the HC (Host Controller).
+     *
+     * Returns length of the transaction 
+     * or one of the USB_RET_XXX codes.
+     */ 
+    int (*handle_packet)(USBDevice *dev, USBPacket *p);
+
+    /* 
+     * Called when device is destroyed.
+     */
+    void (*handle_destroy)(USBDevice *dev);
+
+    int speed;
+
+    /* The following fields are used by the generic USB device
+       layer. They are here just to avoid creating a new structure 
+       for them. */
+
+    /*
+     * Reset the device
+     */  
+    void (*handle_reset)(USBDevice *dev);
+
+    /*
+     * Process control request.
+     * Called from handle_packet().
+     *
+     * Returns length or one of the USB_RET_ codes.
+     */
+    int (*handle_control)(USBDevice *dev, int request, int value,
+                          int index, int length, uint8_t *data);
+
+    /*
+     * Process data transfers (both BULK and ISOC).
+     * Called from handle_packet().
+     *
+     * Returns length or one of the USB_RET_ codes.
+     */
+    int (*handle_data)(USBDevice *dev, USBPacket *p);
+
+    uint8_t addr;
+    char devname[32];
+
+    int state;
+    uint8_t setup_buf[8];
+    uint8_t data_buf[1024];
+    int remote_wakeup;
+    int setup_state;
+    int setup_len;
+    int setup_index;
+};
+
+typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev);
+
+/* USB port on which a device can be connected */
+struct USBPort {
+    USBDevice *dev;
+    usb_attachfn attach;
+    void *opaque;
+    int index; /* internal port index, may be used with the opaque */
+    struct USBPort *next; /* Used internally by qemu.  */
+};
+
+typedef void USBCallback(USBPacket * packet, void *opaque);
+
+/* Structure used to hold information about an active USB packet.  */
+struct USBPacket {
+    /* Data fields for use by the driver.  */
+    int pid;
+    uint8_t devaddr;
+    uint8_t devep;
+    uint8_t *data;
+    int len;
+    /* Internal use by the USB layer.  */
+    USBCallback *complete_cb;
+    void *complete_opaque;
+    USBCallback *cancel_cb;
+    void *cancel_opaque;
+};
+
+/* Defer completion of a USB packet.  The hadle_packet routine should then
+   return USB_RET_ASYNC.  Packets that complete immediately (before
+   handle_packet returns) should not call this method.  */
+static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
+                                    void * opaque)
+{
+    p->cancel_cb = cancel;
+    p->cancel_opaque = opaque;
+}
+
+/* Notify the controller that an async packet is complete.  This should only
+   be called for packets previously deferred with usb_defer_packet, and
+   should never be called from within handle_packet.  */
+static inline void usb_packet_complete(USBPacket *p)
+{
+    p->complete_cb(p, p->complete_opaque);
+}
+
+/* Cancel an active packet.  The packed must have been deferred with
+   usb_defer_packet,  and not yet completed.  */
+static inline void usb_cancel_packet(USBPacket * p)
+{
+    p->cancel_cb(p, p->cancel_opaque);
+}
+
+int usb_device_add_dev(USBDevice *dev);
+int usb_device_del_addr(int bus_num, int addr);
+void usb_attach(USBPort *port, USBDevice *dev);
+int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
+int set_usb_string(uint8_t *buf, const char *str);
+void usb_send_msg(USBDevice *dev, int msg);
+
+/* usb hub */
+USBDevice *usb_hub_init(int nb_ports);
+
+/* usb-linux.c */
+USBDevice *usb_host_device_open(const char *devname);
+int usb_host_device_close(const char *devname);
+void usb_host_info(void);
+
+/* usb-hid.c */
+USBDevice *usb_mouse_init(void);
+USBDevice *usb_tablet_init(void);
+USBDevice *usb_keyboard_init(void);
+
+/* usb-msd.c */
+USBDevice *usb_msd_init(const char *filename);
+
+/* usb-net.c */
+USBDevice *usb_net_init(NICInfo *nd);
+
+/* usb-wacom.c */
+USBDevice *usb_wacom_init(void);
+
+/* usb-serial.c */
+USBDevice *usb_serial_init(const char *filename);
+
+/* usb ports of the VM */
+
+void qemu_register_usb_port(USBPort *port, void *opaque, int index,
+                            usb_attachfn attach);
+
+#define VM_USB_HUB_SIZE 8
+
+/* usb-musb.c */
+enum musb_irq_source_e {
+    musb_irq_suspend = 0,
+    musb_irq_resume,
+    musb_irq_rst_babble,
+    musb_irq_sof,
+    musb_irq_connect,
+    musb_irq_disconnect,
+    musb_irq_vbus_request,
+    musb_irq_vbus_error,
+    musb_irq_rx,
+    musb_irq_tx,
+    musb_set_vbus,
+    musb_set_session,
+    __musb_irq_max,
+};
+
+struct musb_s;
+struct musb_s *musb_init(qemu_irq *irqs);
+uint32_t musb_core_intr_get(struct musb_s *s);
+void musb_core_intr_clear(struct musb_s *s, uint32_t mask);
+void musb_set_size(struct musb_s *s, int epnum, int size, int is_tx);
diff --git a/i386-dis.c b/i386-dis.c
new file mode 100644
index 0000000..7b44179
--- /dev/null
+++ b/i386-dis.c
@@ -0,0 +1,4120 @@
+/* Print i386 instructions for GDB, the GNU debugger.
+   Copyright 1988, 1989, 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+   2001
+   Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/*
+ * 80386 instruction printer by Pace Willisson (pace@prep.ai.mit.edu)
+ * July 1988
+ *  modified by John Hassey (hassey@dg-rtp.dg.com)
+ *  x86-64 support added by Jan Hubicka (jh@suse.cz)
+ */
+
+/*
+ * The main tables describing the instructions is essentially a copy
+ * of the "Opcode Map" chapter (Appendix A) of the Intel 80386
+ * Programmers Manual.  Usually, there is a capital letter, followed
+ * by a small letter.  The capital letter tell the addressing mode,
+ * and the small letter tells about the operand size.  Refer to
+ * the Intel manual for details.
+ */
+
+#include <stdlib.h>
+#include "dis-asm.h"
+#include "qemu-common.h"
+
+#define MAXLEN 20
+
+#include <setjmp.h>
+
+#ifndef UNIXWARE_COMPAT
+/* Set non-zero for broken, compatible instructions.  Set to zero for
+   non-broken opcodes.  */
+#define UNIXWARE_COMPAT 1
+#endif
+
+static int fetch_data PARAMS ((struct disassemble_info *, bfd_byte *));
+static void ckprefix PARAMS ((void));
+static const char *prefix_name PARAMS ((int, int));
+static int print_insn PARAMS ((bfd_vma, disassemble_info *));
+static void dofloat PARAMS ((int));
+static void OP_ST PARAMS ((int, int));
+static void OP_STi  PARAMS ((int, int));
+static int putop PARAMS ((const char *, int));
+static void oappend PARAMS ((const char *));
+static void append_seg PARAMS ((void));
+static void OP_indirE PARAMS ((int, int));
+static void print_operand_value (char *buf, size_t bufsize, int hex,
+                                 bfd_vma disp);
+static void OP_E PARAMS ((int, int));
+static void OP_G PARAMS ((int, int));
+static bfd_vma get64 PARAMS ((void));
+static bfd_signed_vma get32 PARAMS ((void));
+static bfd_signed_vma get32s PARAMS ((void));
+static int get16 PARAMS ((void));
+static void set_op PARAMS ((bfd_vma, int));
+static void OP_REG PARAMS ((int, int));
+static void OP_IMREG PARAMS ((int, int));
+static void OP_I PARAMS ((int, int));
+static void OP_I64 PARAMS ((int, int));
+static void OP_sI PARAMS ((int, int));
+static void OP_J PARAMS ((int, int));
+static void OP_SEG PARAMS ((int, int));
+static void OP_DIR PARAMS ((int, int));
+static void OP_OFF PARAMS ((int, int));
+static void OP_OFF64 PARAMS ((int, int));
+static void ptr_reg PARAMS ((int, int));
+static void OP_ESreg PARAMS ((int, int));
+static void OP_DSreg PARAMS ((int, int));
+static void OP_C PARAMS ((int, int));
+static void OP_D PARAMS ((int, int));
+static void OP_T PARAMS ((int, int));
+static void OP_Rd PARAMS ((int, int));
+static void OP_MMX PARAMS ((int, int));
+static void OP_XMM PARAMS ((int, int));
+static void OP_EM PARAMS ((int, int));
+static void OP_EX PARAMS ((int, int));
+static void OP_MS PARAMS ((int, int));
+static void OP_XS PARAMS ((int, int));
+static void OP_3DNowSuffix PARAMS ((int, int));
+static void OP_SIMD_Suffix PARAMS ((int, int));
+static void SIMD_Fixup PARAMS ((int, int));
+static void BadOp PARAMS ((void));
+
+struct dis_private {
+  /* Points to first byte not fetched.  */
+  bfd_byte *max_fetched;
+  bfd_byte the_buffer[MAXLEN];
+  bfd_vma insn_start;
+  int orig_sizeflag;
+  jmp_buf bailout;
+};
+
+/* The opcode for the fwait instruction, which we treat as a prefix
+   when we can.  */
+#define FWAIT_OPCODE (0x9b)
+
+/* Set to 1 for 64bit mode disassembly.  */
+static int mode_64bit;
+
+/* Flags for the prefixes for the current instruction.  See below.  */
+static int prefixes;
+
+/* REX prefix the current instruction.  See below.  */
+static int rex;
+/* Bits of REX we've already used.  */
+static int rex_used;
+#define REX_MODE64	8
+#define REX_EXTX	4
+#define REX_EXTY	2
+#define REX_EXTZ	1
+/* Mark parts used in the REX prefix.  When we are testing for
+   empty prefix (for 8bit register REX extension), just mask it
+   out.  Otherwise test for REX bit is excuse for existence of REX
+   only in case value is nonzero.  */
+#define USED_REX(value)					\
+  {							\
+    if (value)						\
+      rex_used |= (rex & value) ? (value) | 0x40 : 0;	\
+    else						\
+      rex_used |= 0x40;					\
+  }
+
+/* Flags for prefixes which we somehow handled when printing the
+   current instruction.  */
+static int used_prefixes;
+
+/* Flags stored in PREFIXES.  */
+#define PREFIX_REPZ 1
+#define PREFIX_REPNZ 2
+#define PREFIX_LOCK 4
+#define PREFIX_CS 8
+#define PREFIX_SS 0x10
+#define PREFIX_DS 0x20
+#define PREFIX_ES 0x40
+#define PREFIX_FS 0x80
+#define PREFIX_GS 0x100
+#define PREFIX_DATA 0x200
+#define PREFIX_ADDR 0x400
+#define PREFIX_FWAIT 0x800
+
+/* Make sure that bytes from INFO->PRIVATE_DATA->BUFFER (inclusive)
+   to ADDR (exclusive) are valid.  Returns 1 for success, longjmps
+   on error.  */
+#define FETCH_DATA(info, addr) \
+  ((addr) <= ((struct dis_private *) (info->private_data))->max_fetched \
+   ? 1 : fetch_data ((info), (addr)))
+
+static int
+fetch_data (info, addr)
+     struct disassemble_info *info;
+     bfd_byte *addr;
+{
+  int status;
+  struct dis_private *priv = (struct dis_private *) info->private_data;
+  bfd_vma start = priv->insn_start + (priv->max_fetched - priv->the_buffer);
+
+  status = (*info->read_memory_func) (start,
+				      priv->max_fetched,
+				      addr - priv->max_fetched,
+				      info);
+  if (status != 0)
+    {
+      /* If we did manage to read at least one byte, then
+         print_insn_i386 will do something sensible.  Otherwise, print
+         an error.  We do that here because this is where we know
+         STATUS.  */
+      if (priv->max_fetched == priv->the_buffer)
+	(*info->memory_error_func) (status, start, info);
+      longjmp (priv->bailout, 1);
+    }
+  else
+    priv->max_fetched = addr;
+  return 1;
+}
+
+#define XX NULL, 0
+
+#define Eb OP_E, b_mode
+#define Ev OP_E, v_mode
+#define Ed OP_E, d_mode
+#define indirEb OP_indirE, b_mode
+#define indirEv OP_indirE, v_mode
+#define Ew OP_E, w_mode
+#define Ma OP_E, v_mode
+#define M OP_E, 0		/* lea, lgdt, etc. */
+#define Mp OP_E, 0		/* 32 or 48 bit memory operand for LDS, LES etc */
+#define Gb OP_G, b_mode
+#define Gv OP_G, v_mode
+#define Gd OP_G, d_mode
+#define Gw OP_G, w_mode
+#define Rd OP_Rd, d_mode
+#define Rm OP_Rd, m_mode
+#define Ib OP_I, b_mode
+#define sIb OP_sI, b_mode	/* sign extened byte */
+#define Iv OP_I, v_mode
+#define Iq OP_I, q_mode
+#define Iv64 OP_I64, v_mode
+#define Iw OP_I, w_mode
+#define Jb OP_J, b_mode
+#define Jv OP_J, v_mode
+#define Cm OP_C, m_mode
+#define Dm OP_D, m_mode
+#define Td OP_T, d_mode
+
+#define RMeAX OP_REG, eAX_reg
+#define RMeBX OP_REG, eBX_reg
+#define RMeCX OP_REG, eCX_reg
+#define RMeDX OP_REG, eDX_reg
+#define RMeSP OP_REG, eSP_reg
+#define RMeBP OP_REG, eBP_reg
+#define RMeSI OP_REG, eSI_reg
+#define RMeDI OP_REG, eDI_reg
+#define RMrAX OP_REG, rAX_reg
+#define RMrBX OP_REG, rBX_reg
+#define RMrCX OP_REG, rCX_reg
+#define RMrDX OP_REG, rDX_reg
+#define RMrSP OP_REG, rSP_reg
+#define RMrBP OP_REG, rBP_reg
+#define RMrSI OP_REG, rSI_reg
+#define RMrDI OP_REG, rDI_reg
+#define RMAL OP_REG, al_reg
+#define RMAL OP_REG, al_reg
+#define RMCL OP_REG, cl_reg
+#define RMDL OP_REG, dl_reg
+#define RMBL OP_REG, bl_reg
+#define RMAH OP_REG, ah_reg
+#define RMCH OP_REG, ch_reg
+#define RMDH OP_REG, dh_reg
+#define RMBH OP_REG, bh_reg
+#define RMAX OP_REG, ax_reg
+#define RMDX OP_REG, dx_reg
+
+#define eAX OP_IMREG, eAX_reg
+#define eBX OP_IMREG, eBX_reg
+#define eCX OP_IMREG, eCX_reg
+#define eDX OP_IMREG, eDX_reg
+#define eSP OP_IMREG, eSP_reg
+#define eBP OP_IMREG, eBP_reg
+#define eSI OP_IMREG, eSI_reg
+#define eDI OP_IMREG, eDI_reg
+#define AL OP_IMREG, al_reg
+#define AL OP_IMREG, al_reg
+#define CL OP_IMREG, cl_reg
+#define DL OP_IMREG, dl_reg
+#define BL OP_IMREG, bl_reg
+#define AH OP_IMREG, ah_reg
+#define CH OP_IMREG, ch_reg
+#define DH OP_IMREG, dh_reg
+#define BH OP_IMREG, bh_reg
+#define AX OP_IMREG, ax_reg
+#define DX OP_IMREG, dx_reg
+#define indirDX OP_IMREG, indir_dx_reg
+
+#define Sw OP_SEG, w_mode
+#define Ap OP_DIR, 0
+#define Ob OP_OFF, b_mode
+#define Ob64 OP_OFF64, b_mode
+#define Ov OP_OFF, v_mode
+#define Ov64 OP_OFF64, v_mode
+#define Xb OP_DSreg, eSI_reg
+#define Xv OP_DSreg, eSI_reg
+#define Yb OP_ESreg, eDI_reg
+#define Yv OP_ESreg, eDI_reg
+#define DSBX OP_DSreg, eBX_reg
+
+#define es OP_REG, es_reg
+#define ss OP_REG, ss_reg
+#define cs OP_REG, cs_reg
+#define ds OP_REG, ds_reg
+#define fs OP_REG, fs_reg
+#define gs OP_REG, gs_reg
+
+#define MX OP_MMX, 0
+#define XM OP_XMM, 0
+#define EM OP_EM, v_mode
+#define EX OP_EX, v_mode
+#define MS OP_MS, v_mode
+#define XS OP_XS, v_mode
+#define None OP_E, 0
+#define OPSUF OP_3DNowSuffix, 0
+#define OPSIMD OP_SIMD_Suffix, 0
+
+#define cond_jump_flag NULL, cond_jump_mode
+#define loop_jcxz_flag NULL, loop_jcxz_mode
+
+/* bits in sizeflag */
+#define SUFFIX_ALWAYS 4
+#define AFLAG 2
+#define DFLAG 1
+
+#define b_mode 1  /* byte operand */
+#define v_mode 2  /* operand size depends on prefixes */
+#define w_mode 3  /* word operand */
+#define d_mode 4  /* double word operand  */
+#define q_mode 5  /* quad word operand */
+#define x_mode 6
+#define m_mode 7  /* d_mode in 32bit, q_mode in 64bit mode.  */
+#define cond_jump_mode 8
+#define loop_jcxz_mode 9
+
+#define es_reg 100
+#define cs_reg 101
+#define ss_reg 102
+#define ds_reg 103
+#define fs_reg 104
+#define gs_reg 105
+
+#define eAX_reg 108
+#define eCX_reg 109
+#define eDX_reg 110
+#define eBX_reg 111
+#define eSP_reg 112
+#define eBP_reg 113
+#define eSI_reg 114
+#define eDI_reg 115
+
+#define al_reg 116
+#define cl_reg 117
+#define dl_reg 118
+#define bl_reg 119
+#define ah_reg 120
+#define ch_reg 121
+#define dh_reg 122
+#define bh_reg 123
+
+#define ax_reg 124
+#define cx_reg 125
+#define dx_reg 126
+#define bx_reg 127
+#define sp_reg 128
+#define bp_reg 129
+#define si_reg 130
+#define di_reg 131
+
+#define rAX_reg 132
+#define rCX_reg 133
+#define rDX_reg 134
+#define rBX_reg 135
+#define rSP_reg 136
+#define rBP_reg 137
+#define rSI_reg 138
+#define rDI_reg 139
+
+#define indir_dx_reg 150
+
+#define FLOATCODE 1
+#define USE_GROUPS 2
+#define USE_PREFIX_USER_TABLE 3
+#define X86_64_SPECIAL 4
+
+#define FLOAT	  NULL, NULL, FLOATCODE, NULL, 0, NULL, 0
+
+#define GRP1b	  NULL, NULL, USE_GROUPS, NULL,  0, NULL, 0
+#define GRP1S	  NULL, NULL, USE_GROUPS, NULL,  1, NULL, 0
+#define GRP1Ss	  NULL, NULL, USE_GROUPS, NULL,  2, NULL, 0
+#define GRP2b	  NULL, NULL, USE_GROUPS, NULL,  3, NULL, 0
+#define GRP2S	  NULL, NULL, USE_GROUPS, NULL,  4, NULL, 0
+#define GRP2b_one NULL, NULL, USE_GROUPS, NULL,  5, NULL, 0
+#define GRP2S_one NULL, NULL, USE_GROUPS, NULL,  6, NULL, 0
+#define GRP2b_cl  NULL, NULL, USE_GROUPS, NULL,  7, NULL, 0
+#define GRP2S_cl  NULL, NULL, USE_GROUPS, NULL,  8, NULL, 0
+#define GRP3b	  NULL, NULL, USE_GROUPS, NULL,  9, NULL, 0
+#define GRP3S	  NULL, NULL, USE_GROUPS, NULL, 10, NULL, 0
+#define GRP4	  NULL, NULL, USE_GROUPS, NULL, 11, NULL, 0
+#define GRP5	  NULL, NULL, USE_GROUPS, NULL, 12, NULL, 0
+#define GRP6	  NULL, NULL, USE_GROUPS, NULL, 13, NULL, 0
+#define GRP7	  NULL, NULL, USE_GROUPS, NULL, 14, NULL, 0
+#define GRP8	  NULL, NULL, USE_GROUPS, NULL, 15, NULL, 0
+#define GRP9	  NULL, NULL, USE_GROUPS, NULL, 16, NULL, 0
+#define GRP10	  NULL, NULL, USE_GROUPS, NULL, 17, NULL, 0
+#define GRP11	  NULL, NULL, USE_GROUPS, NULL, 18, NULL, 0
+#define GRP12	  NULL, NULL, USE_GROUPS, NULL, 19, NULL, 0
+#define GRP13	  NULL, NULL, USE_GROUPS, NULL, 20, NULL, 0
+#define GRP14	  NULL, NULL, USE_GROUPS, NULL, 21, NULL, 0
+#define GRPAMD	  NULL, NULL, USE_GROUPS, NULL, 22, NULL, 0
+
+#define PREGRP0   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  0, NULL, 0
+#define PREGRP1   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  1, NULL, 0
+#define PREGRP2   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  2, NULL, 0
+#define PREGRP3   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  3, NULL, 0
+#define PREGRP4   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  4, NULL, 0
+#define PREGRP5   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  5, NULL, 0
+#define PREGRP6   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  6, NULL, 0
+#define PREGRP7   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  7, NULL, 0
+#define PREGRP8   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  8, NULL, 0
+#define PREGRP9   NULL, NULL, USE_PREFIX_USER_TABLE, NULL,  9, NULL, 0
+#define PREGRP10  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 10, NULL, 0
+#define PREGRP11  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 11, NULL, 0
+#define PREGRP12  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 12, NULL, 0
+#define PREGRP13  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 13, NULL, 0
+#define PREGRP14  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 14, NULL, 0
+#define PREGRP15  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 15, NULL, 0
+#define PREGRP16  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 16, NULL, 0
+#define PREGRP17  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 17, NULL, 0
+#define PREGRP18  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 18, NULL, 0
+#define PREGRP19  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 19, NULL, 0
+#define PREGRP20  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 20, NULL, 0
+#define PREGRP21  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 21, NULL, 0
+#define PREGRP22  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 22, NULL, 0
+#define PREGRP23  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 23, NULL, 0
+#define PREGRP24  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 24, NULL, 0
+#define PREGRP25  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 25, NULL, 0
+#define PREGRP26  NULL, NULL, USE_PREFIX_USER_TABLE, NULL, 26, NULL, 0
+
+#define X86_64_0  NULL, NULL, X86_64_SPECIAL, NULL,  0, NULL, 0
+
+typedef void (*op_rtn) PARAMS ((int bytemode, int sizeflag));
+
+struct dis386 {
+  const char *name;
+  op_rtn op1;
+  int bytemode1;
+  op_rtn op2;
+  int bytemode2;
+  op_rtn op3;
+  int bytemode3;
+};
+
+/* Upper case letters in the instruction names here are macros.
+   'A' => print 'b' if no register operands or suffix_always is true
+   'B' => print 'b' if suffix_always is true
+   'E' => print 'e' if 32-bit form of jcxz
+   'F' => print 'w' or 'l' depending on address size prefix (loop insns)
+   'H' => print ",pt" or ",pn" branch hint
+   'L' => print 'l' if suffix_always is true
+   'N' => print 'n' if instruction has no wait "prefix"
+   'O' => print 'd', or 'o'
+   'P' => print 'w', 'l' or 'q' if instruction has an operand size prefix,
+   .      or suffix_always is true.  print 'q' if rex prefix is present.
+   'Q' => print 'w', 'l' or 'q' if no register operands or suffix_always
+   .      is true
+   'R' => print 'w', 'l' or 'q' ("wd" or "dq" in intel mode)
+   'S' => print 'w', 'l' or 'q' if suffix_always is true
+   'T' => print 'q' in 64bit mode and behave as 'P' otherwise
+   'U' => print 'q' in 64bit mode and behave as 'Q' otherwise
+   'X' => print 's', 'd' depending on data16 prefix (for XMM)
+   'W' => print 'b' or 'w' ("w" or "de" in intel mode)
+   'Y' => 'q' if instruction has an REX 64bit overwrite prefix
+
+   Many of the above letters print nothing in Intel mode.  See "putop"
+   for the details.
+
+   Braces '{' and '}', and vertical bars '|', indicate alternative
+   mnemonic strings for AT&T, Intel, X86_64 AT&T, and X86_64 Intel
+   modes.  In cases where there are only two alternatives, the X86_64
+   instruction is reserved, and "(bad)" is printed.
+*/
+
+static const struct dis386 dis386[] = {
+  /* 00 */
+  { "addB",		Eb, Gb, XX },
+  { "addS",		Ev, Gv, XX },
+  { "addB",		Gb, Eb, XX },
+  { "addS",		Gv, Ev, XX },
+  { "addB",		AL, Ib, XX },
+  { "addS",		eAX, Iv, XX },
+  { "push{T|}",		es, XX, XX },
+  { "pop{T|}",		es, XX, XX },
+  /* 08 */
+  { "orB",		Eb, Gb, XX },
+  { "orS",		Ev, Gv, XX },
+  { "orB",		Gb, Eb, XX },
+  { "orS",		Gv, Ev, XX },
+  { "orB",		AL, Ib, XX },
+  { "orS",		eAX, Iv, XX },
+  { "push{T|}",		cs, XX, XX },
+  { "(bad)",		XX, XX, XX },	/* 0x0f extended opcode escape */
+  /* 10 */
+  { "adcB",		Eb, Gb, XX },
+  { "adcS",		Ev, Gv, XX },
+  { "adcB",		Gb, Eb, XX },
+  { "adcS",		Gv, Ev, XX },
+  { "adcB",		AL, Ib, XX },
+  { "adcS",		eAX, Iv, XX },
+  { "push{T|}",		ss, XX, XX },
+  { "popT|}",		ss, XX, XX },
+  /* 18 */
+  { "sbbB",		Eb, Gb, XX },
+  { "sbbS",		Ev, Gv, XX },
+  { "sbbB",		Gb, Eb, XX },
+  { "sbbS",		Gv, Ev, XX },
+  { "sbbB",		AL, Ib, XX },
+  { "sbbS",		eAX, Iv, XX },
+  { "push{T|}",		ds, XX, XX },
+  { "pop{T|}",		ds, XX, XX },
+  /* 20 */
+  { "andB",		Eb, Gb, XX },
+  { "andS",		Ev, Gv, XX },
+  { "andB",		Gb, Eb, XX },
+  { "andS",		Gv, Ev, XX },
+  { "andB",		AL, Ib, XX },
+  { "andS",		eAX, Iv, XX },
+  { "(bad)",		XX, XX, XX },	/* SEG ES prefix */
+  { "daa{|}",		XX, XX, XX },
+  /* 28 */
+  { "subB",		Eb, Gb, XX },
+  { "subS",		Ev, Gv, XX },
+  { "subB",		Gb, Eb, XX },
+  { "subS",		Gv, Ev, XX },
+  { "subB",		AL, Ib, XX },
+  { "subS",		eAX, Iv, XX },
+  { "(bad)",		XX, XX, XX },	/* SEG CS prefix */
+  { "das{|}",		XX, XX, XX },
+  /* 30 */
+  { "xorB",		Eb, Gb, XX },
+  { "xorS",		Ev, Gv, XX },
+  { "xorB",		Gb, Eb, XX },
+  { "xorS",		Gv, Ev, XX },
+  { "xorB",		AL, Ib, XX },
+  { "xorS",		eAX, Iv, XX },
+  { "(bad)",		XX, XX, XX },	/* SEG SS prefix */
+  { "aaa{|}",		XX, XX, XX },
+  /* 38 */
+  { "cmpB",		Eb, Gb, XX },
+  { "cmpS",		Ev, Gv, XX },
+  { "cmpB",		Gb, Eb, XX },
+  { "cmpS",		Gv, Ev, XX },
+  { "cmpB",		AL, Ib, XX },
+  { "cmpS",		eAX, Iv, XX },
+  { "(bad)",		XX, XX, XX },	/* SEG DS prefix */
+  { "aas{|}",		XX, XX, XX },
+  /* 40 */
+  { "inc{S|}",		RMeAX, XX, XX },
+  { "inc{S|}",		RMeCX, XX, XX },
+  { "inc{S|}",		RMeDX, XX, XX },
+  { "inc{S|}",		RMeBX, XX, XX },
+  { "inc{S|}",		RMeSP, XX, XX },
+  { "inc{S|}",		RMeBP, XX, XX },
+  { "inc{S|}",		RMeSI, XX, XX },
+  { "inc{S|}",		RMeDI, XX, XX },
+  /* 48 */
+  { "dec{S|}",		RMeAX, XX, XX },
+  { "dec{S|}",		RMeCX, XX, XX },
+  { "dec{S|}",		RMeDX, XX, XX },
+  { "dec{S|}",		RMeBX, XX, XX },
+  { "dec{S|}",		RMeSP, XX, XX },
+  { "dec{S|}",		RMeBP, XX, XX },
+  { "dec{S|}",		RMeSI, XX, XX },
+  { "dec{S|}",		RMeDI, XX, XX },
+  /* 50 */
+  { "pushS",		RMrAX, XX, XX },
+  { "pushS",		RMrCX, XX, XX },
+  { "pushS",		RMrDX, XX, XX },
+  { "pushS",		RMrBX, XX, XX },
+  { "pushS",		RMrSP, XX, XX },
+  { "pushS",		RMrBP, XX, XX },
+  { "pushS",		RMrSI, XX, XX },
+  { "pushS",		RMrDI, XX, XX },
+  /* 58 */
+  { "popS",		RMrAX, XX, XX },
+  { "popS",		RMrCX, XX, XX },
+  { "popS",		RMrDX, XX, XX },
+  { "popS",		RMrBX, XX, XX },
+  { "popS",		RMrSP, XX, XX },
+  { "popS",		RMrBP, XX, XX },
+  { "popS",		RMrSI, XX, XX },
+  { "popS",		RMrDI, XX, XX },
+  /* 60 */
+  { "pusha{P|}",	XX, XX, XX },
+  { "popa{P|}",		XX, XX, XX },
+  { "bound{S|}",	Gv, Ma, XX },
+  { X86_64_0 },
+  { "(bad)",		XX, XX, XX },	/* seg fs */
+  { "(bad)",		XX, XX, XX },	/* seg gs */
+  { "(bad)",		XX, XX, XX },	/* op size prefix */
+  { "(bad)",		XX, XX, XX },	/* adr size prefix */
+  /* 68 */
+  { "pushT",		Iq, XX, XX },
+  { "imulS",		Gv, Ev, Iv },
+  { "pushT",		sIb, XX, XX },
+  { "imulS",		Gv, Ev, sIb },
+  { "ins{b||b|}",	Yb, indirDX, XX },
+  { "ins{R||R|}",	Yv, indirDX, XX },
+  { "outs{b||b|}",	indirDX, Xb, XX },
+  { "outs{R||R|}",	indirDX, Xv, XX },
+  /* 70 */
+  { "joH",		Jb, XX, cond_jump_flag },
+  { "jnoH",		Jb, XX, cond_jump_flag },
+  { "jbH",		Jb, XX, cond_jump_flag },
+  { "jaeH",		Jb, XX, cond_jump_flag },
+  { "jeH",		Jb, XX, cond_jump_flag },
+  { "jneH",		Jb, XX, cond_jump_flag },
+  { "jbeH",		Jb, XX, cond_jump_flag },
+  { "jaH",		Jb, XX, cond_jump_flag },
+  /* 78 */
+  { "jsH",		Jb, XX, cond_jump_flag },
+  { "jnsH",		Jb, XX, cond_jump_flag },
+  { "jpH",		Jb, XX, cond_jump_flag },
+  { "jnpH",		Jb, XX, cond_jump_flag },
+  { "jlH",		Jb, XX, cond_jump_flag },
+  { "jgeH",		Jb, XX, cond_jump_flag },
+  { "jleH",		Jb, XX, cond_jump_flag },
+  { "jgH",		Jb, XX, cond_jump_flag },
+  /* 80 */
+  { GRP1b },
+  { GRP1S },
+  { "(bad)",		XX, XX, XX },
+  { GRP1Ss },
+  { "testB",		Eb, Gb, XX },
+  { "testS",		Ev, Gv, XX },
+  { "xchgB",		Eb, Gb, XX },
+  { "xchgS",		Ev, Gv, XX },
+  /* 88 */
+  { "movB",		Eb, Gb, XX },
+  { "movS",		Ev, Gv, XX },
+  { "movB",		Gb, Eb, XX },
+  { "movS",		Gv, Ev, XX },
+  { "movQ",		Ev, Sw, XX },
+  { "leaS",		Gv, M, XX },
+  { "movQ",		Sw, Ev, XX },
+  { "popU",		Ev, XX, XX },
+  /* 90 */
+  { "nop",		XX, XX, XX },
+  /* FIXME: NOP with REPz prefix is called PAUSE.  */
+  { "xchgS",		RMeCX, eAX, XX },
+  { "xchgS",		RMeDX, eAX, XX },
+  { "xchgS",		RMeBX, eAX, XX },
+  { "xchgS",		RMeSP, eAX, XX },
+  { "xchgS",		RMeBP, eAX, XX },
+  { "xchgS",		RMeSI, eAX, XX },
+  { "xchgS",		RMeDI, eAX, XX },
+  /* 98 */
+  { "cW{tR||tR|}",	XX, XX, XX },
+  { "cR{tO||tO|}",	XX, XX, XX },
+  { "lcall{T|}",	Ap, XX, XX },
+  { "(bad)",		XX, XX, XX },	/* fwait */
+  { "pushfT",		XX, XX, XX },
+  { "popfT",		XX, XX, XX },
+  { "sahf{|}",		XX, XX, XX },
+  { "lahf{|}",		XX, XX, XX },
+  /* a0 */
+  { "movB",		AL, Ob64, XX },
+  { "movS",		eAX, Ov64, XX },
+  { "movB",		Ob64, AL, XX },
+  { "movS",		Ov64, eAX, XX },
+  { "movs{b||b|}",	Yb, Xb, XX },
+  { "movs{R||R|}",	Yv, Xv, XX },
+  { "cmps{b||b|}",	Xb, Yb, XX },
+  { "cmps{R||R|}",	Xv, Yv, XX },
+  /* a8 */
+  { "testB",		AL, Ib, XX },
+  { "testS",		eAX, Iv, XX },
+  { "stosB",		Yb, AL, XX },
+  { "stosS",		Yv, eAX, XX },
+  { "lodsB",		AL, Xb, XX },
+  { "lodsS",		eAX, Xv, XX },
+  { "scasB",		AL, Yb, XX },
+  { "scasS",		eAX, Yv, XX },
+  /* b0 */
+  { "movB",		RMAL, Ib, XX },
+  { "movB",		RMCL, Ib, XX },
+  { "movB",		RMDL, Ib, XX },
+  { "movB",		RMBL, Ib, XX },
+  { "movB",		RMAH, Ib, XX },
+  { "movB",		RMCH, Ib, XX },
+  { "movB",		RMDH, Ib, XX },
+  { "movB",		RMBH, Ib, XX },
+  /* b8 */
+  { "movS",		RMeAX, Iv64, XX },
+  { "movS",		RMeCX, Iv64, XX },
+  { "movS",		RMeDX, Iv64, XX },
+  { "movS",		RMeBX, Iv64, XX },
+  { "movS",		RMeSP, Iv64, XX },
+  { "movS",		RMeBP, Iv64, XX },
+  { "movS",		RMeSI, Iv64, XX },
+  { "movS",		RMeDI, Iv64, XX },
+  /* c0 */
+  { GRP2b },
+  { GRP2S },
+  { "retT",		Iw, XX, XX },
+  { "retT",		XX, XX, XX },
+  { "les{S|}",		Gv, Mp, XX },
+  { "ldsS",		Gv, Mp, XX },
+  { "movA",		Eb, Ib, XX },
+  { "movQ",		Ev, Iv, XX },
+  /* c8 */
+  { "enterT",		Iw, Ib, XX },
+  { "leaveT",		XX, XX, XX },
+  { "lretP",		Iw, XX, XX },
+  { "lretP",		XX, XX, XX },
+  { "int3",		XX, XX, XX },
+  { "int",		Ib, XX, XX },
+  { "into{|}",		XX, XX, XX },
+  { "iretP",		XX, XX, XX },
+  /* d0 */
+  { GRP2b_one },
+  { GRP2S_one },
+  { GRP2b_cl },
+  { GRP2S_cl },
+  { "aam{|}",		sIb, XX, XX },
+  { "aad{|}",		sIb, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "xlat",		DSBX, XX, XX },
+  /* d8 */
+  { FLOAT },
+  { FLOAT },
+  { FLOAT },
+  { FLOAT },
+  { FLOAT },
+  { FLOAT },
+  { FLOAT },
+  { FLOAT },
+  /* e0 */
+  { "loopneFH",		Jb, XX, loop_jcxz_flag },
+  { "loopeFH",		Jb, XX, loop_jcxz_flag },
+  { "loopFH",		Jb, XX, loop_jcxz_flag },
+  { "jEcxzH",		Jb, XX, loop_jcxz_flag },
+  { "inB",		AL, Ib, XX },
+  { "inS",		eAX, Ib, XX },
+  { "outB",		Ib, AL, XX },
+  { "outS",		Ib, eAX, XX },
+  /* e8 */
+  { "callT",		Jv, XX, XX },
+  { "jmpT",		Jv, XX, XX },
+  { "ljmp{T|}",		Ap, XX, XX },
+  { "jmp",		Jb, XX, XX },
+  { "inB",		AL, indirDX, XX },
+  { "inS",		eAX, indirDX, XX },
+  { "outB",		indirDX, AL, XX },
+  { "outS",		indirDX, eAX, XX },
+  /* f0 */
+  { "(bad)",		XX, XX, XX },	/* lock prefix */
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },	/* repne */
+  { "(bad)",		XX, XX, XX },	/* repz */
+  { "hlt",		XX, XX, XX },
+  { "cmc",		XX, XX, XX },
+  { GRP3b },
+  { GRP3S },
+  /* f8 */
+  { "clc",		XX, XX, XX },
+  { "stc",		XX, XX, XX },
+  { "cli",		XX, XX, XX },
+  { "sti",		XX, XX, XX },
+  { "cld",		XX, XX, XX },
+  { "std",		XX, XX, XX },
+  { GRP4 },
+  { GRP5 },
+};
+
+static const struct dis386 dis386_twobyte[] = {
+  /* 00 */
+  { GRP6 },
+  { GRP7 },
+  { "larS",		Gv, Ew, XX },
+  { "lslS",		Gv, Ew, XX },
+  { "(bad)",		XX, XX, XX },
+  { "syscall",		XX, XX, XX },
+  { "clts",		XX, XX, XX },
+  { "sysretP",		XX, XX, XX },
+  /* 08 */
+  { "invd",		XX, XX, XX },
+  { "wbinvd",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "ud2a",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { GRPAMD },
+  { "femms",		XX, XX, XX },
+  { "",			MX, EM, OPSUF }, /* See OP_3DNowSuffix.  */
+  /* 10 */
+  { PREGRP8 },
+  { PREGRP9 },
+  { "movlpX",		XM, EX, SIMD_Fixup, 'h' }, /* really only 2 operands */
+  { "movlpX",		EX, XM, SIMD_Fixup, 'h' },
+  { "unpcklpX",		XM, EX, XX },
+  { "unpckhpX",		XM, EX, XX },
+  { "movhpX",		XM, EX, SIMD_Fixup, 'l' },
+  { "movhpX",		EX, XM, SIMD_Fixup, 'l' },
+  /* 18 */
+  { GRP14 },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  /* 20 */
+  { "movL",		Rm, Cm, XX },
+  { "movL",		Rm, Dm, XX },
+  { "movL",		Cm, Rm, XX },
+  { "movL",		Dm, Rm, XX },
+  { "movL",		Rd, Td, XX },
+  { "(bad)",		XX, XX, XX },
+  { "movL",		Td, Rd, XX },
+  { "(bad)",		XX, XX, XX },
+  /* 28 */
+  { "movapX",		XM, EX, XX },
+  { "movapX",		EX, XM, XX },
+  { PREGRP2 },
+  { "movntpX",		Ev, XM, XX },
+  { PREGRP4 },
+  { PREGRP3 },
+  { "ucomisX",		XM,EX, XX },
+  { "comisX",		XM,EX, XX },
+  /* 30 */
+  { "wrmsr",		XX, XX, XX },
+  { "rdtsc",		XX, XX, XX },
+  { "rdmsr",		XX, XX, XX },
+  { "rdpmc",		XX, XX, XX },
+  { "sysenter",		XX, XX, XX },
+  { "sysexit",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  /* 38 */
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  /* 40 */
+  { "cmovo",		Gv, Ev, XX },
+  { "cmovno",		Gv, Ev, XX },
+  { "cmovb",		Gv, Ev, XX },
+  { "cmovae",		Gv, Ev, XX },
+  { "cmove",		Gv, Ev, XX },
+  { "cmovne",		Gv, Ev, XX },
+  { "cmovbe",		Gv, Ev, XX },
+  { "cmova",		Gv, Ev, XX },
+  /* 48 */
+  { "cmovs",		Gv, Ev, XX },
+  { "cmovns",		Gv, Ev, XX },
+  { "cmovp",		Gv, Ev, XX },
+  { "cmovnp",		Gv, Ev, XX },
+  { "cmovl",		Gv, Ev, XX },
+  { "cmovge",		Gv, Ev, XX },
+  { "cmovle",		Gv, Ev, XX },
+  { "cmovg",		Gv, Ev, XX },
+  /* 50 */
+  { "movmskpX",		Gd, XS, XX },
+  { PREGRP13 },
+  { PREGRP12 },
+  { PREGRP11 },
+  { "andpX",		XM, EX, XX },
+  { "andnpX",		XM, EX, XX },
+  { "orpX",		XM, EX, XX },
+  { "xorpX",		XM, EX, XX },
+  /* 58 */
+  { PREGRP0 },
+  { PREGRP10 },
+  { PREGRP17 },
+  { PREGRP16 },
+  { PREGRP14 },
+  { PREGRP7 },
+  { PREGRP5 },
+  { PREGRP6 },
+  /* 60 */
+  { "punpcklbw",	MX, EM, XX },
+  { "punpcklwd",	MX, EM, XX },
+  { "punpckldq",	MX, EM, XX },
+  { "packsswb",		MX, EM, XX },
+  { "pcmpgtb",		MX, EM, XX },
+  { "pcmpgtw",		MX, EM, XX },
+  { "pcmpgtd",		MX, EM, XX },
+  { "packuswb",		MX, EM, XX },
+  /* 68 */
+  { "punpckhbw",	MX, EM, XX },
+  { "punpckhwd",	MX, EM, XX },
+  { "punpckhdq",	MX, EM, XX },
+  { "packssdw",		MX, EM, XX },
+  { PREGRP26 },
+  { PREGRP24 },
+  { "movd",		MX, Ed, XX },
+  { PREGRP19 },
+  /* 70 */
+  { PREGRP22 },
+  { GRP10 },
+  { GRP11 },
+  { GRP12 },
+  { "pcmpeqb",		MX, EM, XX },
+  { "pcmpeqw",		MX, EM, XX },
+  { "pcmpeqd",		MX, EM, XX },
+  { "emms",		XX, XX, XX },
+  /* 78 */
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  { PREGRP23 },
+  { PREGRP20 },
+  /* 80 */
+  { "joH",		Jv, XX, cond_jump_flag },
+  { "jnoH",		Jv, XX, cond_jump_flag },
+  { "jbH",		Jv, XX, cond_jump_flag },
+  { "jaeH",		Jv, XX, cond_jump_flag },
+  { "jeH",		Jv, XX, cond_jump_flag },
+  { "jneH",		Jv, XX, cond_jump_flag },
+  { "jbeH",		Jv, XX, cond_jump_flag },
+  { "jaH",		Jv, XX, cond_jump_flag },
+  /* 88 */
+  { "jsH",		Jv, XX, cond_jump_flag },
+  { "jnsH",		Jv, XX, cond_jump_flag },
+  { "jpH",		Jv, XX, cond_jump_flag },
+  { "jnpH",		Jv, XX, cond_jump_flag },
+  { "jlH",		Jv, XX, cond_jump_flag },
+  { "jgeH",		Jv, XX, cond_jump_flag },
+  { "jleH",		Jv, XX, cond_jump_flag },
+  { "jgH",		Jv, XX, cond_jump_flag },
+  /* 90 */
+  { "seto",		Eb, XX, XX },
+  { "setno",		Eb, XX, XX },
+  { "setb",		Eb, XX, XX },
+  { "setae",		Eb, XX, XX },
+  { "sete",		Eb, XX, XX },
+  { "setne",		Eb, XX, XX },
+  { "setbe",		Eb, XX, XX },
+  { "seta",		Eb, XX, XX },
+  /* 98 */
+  { "sets",		Eb, XX, XX },
+  { "setns",		Eb, XX, XX },
+  { "setp",		Eb, XX, XX },
+  { "setnp",		Eb, XX, XX },
+  { "setl",		Eb, XX, XX },
+  { "setge",		Eb, XX, XX },
+  { "setle",		Eb, XX, XX },
+  { "setg",		Eb, XX, XX },
+  /* a0 */
+  { "pushT",		fs, XX, XX },
+  { "popT",		fs, XX, XX },
+  { "cpuid",		XX, XX, XX },
+  { "btS",		Ev, Gv, XX },
+  { "shldS",		Ev, Gv, Ib },
+  { "shldS",		Ev, Gv, CL },
+  { "(bad)",		XX, XX, XX },
+  { "(bad)",		XX, XX, XX },
+  /* a8 */
+  { "pushT",		gs, XX, XX },
+  { "popT",		gs, XX, XX },
+  { "rsm",		XX, XX, XX },
+  { "btsS",		Ev, Gv, XX },
+  { "shrdS",		Ev, Gv, Ib },
+  { "shrdS",		Ev, Gv, CL },
+  { GRP13 },
+  { "imulS",		Gv, Ev, XX },
+  /* b0 */
+  { "cmpxchgB",		Eb, Gb, XX },
+  { "cmpxchgS",		Ev, Gv, XX },
+  { "lssS",		Gv, Mp, XX },
+  { "btrS",		Ev, Gv, XX },
+  { "lfsS",		Gv, Mp, XX },
+  { "lgsS",		Gv, Mp, XX },
+  { "movz{bR|x|bR|x}",	Gv, Eb, XX },
+  { "movz{wR|x|wR|x}",	Gv, Ew, XX }, /* yes, there really is movzww ! */
+  /* b8 */
+  { "(bad)",		XX, XX, XX },
+  { "ud2b",		XX, XX, XX },
+  { GRP8 },
+  { "btcS",		Ev, Gv, XX },
+  { "bsfS",		Gv, Ev, XX },
+  { "bsrS",		Gv, Ev, XX },
+  { "movs{bR|x|bR|x}",	Gv, Eb, XX },
+  { "movs{wR|x|wR|x}",	Gv, Ew, XX }, /* yes, there really is movsww ! */
+  /* c0 */
+  { "xaddB",		Eb, Gb, XX },
+  { "xaddS",		Ev, Gv, XX },
+  { PREGRP1 },
+  { "movntiS",		Ev, Gv, XX },
+  { "pinsrw",		MX, Ed, Ib },
+  { "pextrw",		Gd, MS, Ib },
+  { "shufpX",		XM, EX, Ib },
+  { GRP9 },
+  /* c8 */
+  { "bswap",		RMeAX, XX, XX },
+  { "bswap",		RMeCX, XX, XX },
+  { "bswap",		RMeDX, XX, XX },
+  { "bswap",		RMeBX, XX, XX },
+  { "bswap",		RMeSP, XX, XX },
+  { "bswap",		RMeBP, XX, XX },
+  { "bswap",		RMeSI, XX, XX },
+  { "bswap",		RMeDI, XX, XX },
+  /* d0 */
+  { "(bad)",		XX, XX, XX },
+  { "psrlw",		MX, EM, XX },
+  { "psrld",		MX, EM, XX },
+  { "psrlq",		MX, EM, XX },
+  { "paddq",		MX, EM, XX },
+  { "pmullw",		MX, EM, XX },
+  { PREGRP21 },
+  { "pmovmskb",		Gd, MS, XX },
+  /* d8 */
+  { "psubusb",		MX, EM, XX },
+  { "psubusw",		MX, EM, XX },
+  { "pminub",		MX, EM, XX },
+  { "pand",		MX, EM, XX },
+  { "paddusb",		MX, EM, XX },
+  { "paddusw",		MX, EM, XX },
+  { "pmaxub",		MX, EM, XX },
+  { "pandn",		MX, EM, XX },
+  /* e0 */
+  { "pavgb",		MX, EM, XX },
+  { "psraw",		MX, EM, XX },
+  { "psrad",		MX, EM, XX },
+  { "pavgw",		MX, EM, XX },
+  { "pmulhuw",		MX, EM, XX },
+  { "pmulhw",		MX, EM, XX },
+  { PREGRP15 },
+  { PREGRP25 },
+  /* e8 */
+  { "psubsb",		MX, EM, XX },
+  { "psubsw",		MX, EM, XX },
+  { "pminsw",		MX, EM, XX },
+  { "por",		MX, EM, XX },
+  { "paddsb",		MX, EM, XX },
+  { "paddsw",		MX, EM, XX },
+  { "pmaxsw",		MX, EM, XX },
+  { "pxor",		MX, EM, XX },
+  /* f0 */
+  { "(bad)",		XX, XX, XX },
+  { "psllw",		MX, EM, XX },
+  { "pslld",		MX, EM, XX },
+  { "psllq",		MX, EM, XX },
+  { "pmuludq",		MX, EM, XX },
+  { "pmaddwd",		MX, EM, XX },
+  { "psadbw",		MX, EM, XX },
+  { PREGRP18 },
+  /* f8 */
+  { "psubb",		MX, EM, XX },
+  { "psubw",		MX, EM, XX },
+  { "psubd",		MX, EM, XX },
+  { "psubq",		MX, EM, XX },
+  { "paddb",		MX, EM, XX },
+  { "paddw",		MX, EM, XX },
+  { "paddd",		MX, EM, XX },
+  { "(bad)",		XX, XX, XX }
+};
+
+static const unsigned char onebyte_has_modrm[256] = {
+  /*       0 1 2 3 4 5 6 7 8 9 a b c d e f        */
+  /*       -------------------------------        */
+  /* 00 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 00 */
+  /* 10 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 10 */
+  /* 20 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 20 */
+  /* 30 */ 1,1,1,1,0,0,0,0,1,1,1,1,0,0,0,0, /* 30 */
+  /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 40 */
+  /* 50 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 50 */
+  /* 60 */ 0,0,1,1,0,0,0,0,0,1,0,1,0,0,0,0, /* 60 */
+  /* 70 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 70 */
+  /* 80 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 80 */
+  /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 90 */
+  /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* a0 */
+  /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* b0 */
+  /* c0 */ 1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0, /* c0 */
+  /* d0 */ 1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* d0 */
+  /* e0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* e0 */
+  /* f0 */ 0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1  /* f0 */
+  /*       -------------------------------        */
+  /*       0 1 2 3 4 5 6 7 8 9 a b c d e f        */
+};
+
+static const unsigned char twobyte_has_modrm[256] = {
+  /*       0 1 2 3 4 5 6 7 8 9 a b c d e f        */
+  /*       -------------------------------        */
+  /* 00 */ 1,1,1,1,0,0,0,0,0,0,0,0,0,1,0,1, /* 0f */
+  /* 10 */ 1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, /* 1f */
+  /* 20 */ 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1, /* 2f */
+  /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+  /* 40 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 4f */
+  /* 50 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 5f */
+  /* 60 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 6f */
+  /* 70 */ 1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1, /* 7f */
+  /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+  /* 90 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* 9f */
+  /* a0 */ 0,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1, /* af */
+  /* b0 */ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1, /* bf */
+  /* c0 */ 1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, /* cf */
+  /* d0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* df */
+  /* e0 */ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, /* ef */
+  /* f0 */ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0  /* ff */
+  /*       -------------------------------        */
+  /*       0 1 2 3 4 5 6 7 8 9 a b c d e f        */
+};
+
+static const unsigned char twobyte_uses_SSE_prefix[256] = {
+  /*       0 1 2 3 4 5 6 7 8 9 a b c d e f        */
+  /*       -------------------------------        */
+  /* 00 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0f */
+  /* 10 */ 1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1f */
+  /* 20 */ 0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0, /* 2f */
+  /* 30 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 3f */
+  /* 40 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 4f */
+  /* 50 */ 0,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1, /* 5f */
+  /* 60 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, /* 6f */
+  /* 70 */ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, /* 7f */
+  /* 80 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 8f */
+  /* 90 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 9f */
+  /* a0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* af */
+  /* b0 */ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* bf */
+  /* c0 */ 0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0, /* cf */
+  /* d0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* df */
+  /* e0 */ 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0, /* ef */
+  /* f0 */ 0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0  /* ff */
+  /*       -------------------------------        */
+  /*       0 1 2 3 4 5 6 7 8 9 a b c d e f        */
+};
+
+static char obuf[100];
+static char *obufp;
+static char scratchbuf[100];
+static unsigned char *start_codep;
+static unsigned char *insn_codep;
+static unsigned char *codep;
+static disassemble_info *the_info;
+static int mod;
+static int rm;
+static int reg;
+static unsigned char need_modrm;
+
+/* If we are accessing mod/rm/reg without need_modrm set, then the
+   values are stale.  Hitting this abort likely indicates that you
+   need to update onebyte_has_modrm or twobyte_has_modrm.  */
+#define MODRM_CHECK  if (!need_modrm) abort ()
+
+static const char **names64;
+static const char **names32;
+static const char **names16;
+static const char **names8;
+static const char **names8rex;
+static const char **names_seg;
+static const char **index16;
+
+static const char *intel_names64[] = {
+  "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+  "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+static const char *intel_names32[] = {
+  "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+  "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d"
+};
+static const char *intel_names16[] = {
+  "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+  "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w"
+};
+static const char *intel_names8[] = {
+  "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+};
+static const char *intel_names8rex[] = {
+  "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+  "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b"
+};
+static const char *intel_names_seg[] = {
+  "es", "cs", "ss", "ds", "fs", "gs", "?", "?",
+};
+static const char *intel_index16[] = {
+  "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx"
+};
+
+static const char *att_names64[] = {
+  "%rax", "%rcx", "%rdx", "%rbx", "%rsp", "%rbp", "%rsi", "%rdi",
+  "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15"
+};
+static const char *att_names32[] = {
+  "%eax", "%ecx", "%edx", "%ebx", "%esp", "%ebp", "%esi", "%edi",
+  "%r8d", "%r9d", "%r10d", "%r11d", "%r12d", "%r13d", "%r14d", "%r15d"
+};
+static const char *att_names16[] = {
+  "%ax", "%cx", "%dx", "%bx", "%sp", "%bp", "%si", "%di",
+  "%r8w", "%r9w", "%r10w", "%r11w", "%r12w", "%r13w", "%r14w", "%r15w"
+};
+static const char *att_names8[] = {
+  "%al", "%cl", "%dl", "%bl", "%ah", "%ch", "%dh", "%bh",
+};
+static const char *att_names8rex[] = {
+  "%al", "%cl", "%dl", "%bl", "%spl", "%bpl", "%sil", "%dil",
+  "%r8b", "%r9b", "%r10b", "%r11b", "%r12b", "%r13b", "%r14b", "%r15b"
+};
+static const char *att_names_seg[] = {
+  "%es", "%cs", "%ss", "%ds", "%fs", "%gs", "%?", "%?",
+};
+static const char *att_index16[] = {
+  "%bx,%si", "%bx,%di", "%bp,%si", "%bp,%di", "%si", "%di", "%bp", "%bx"
+};
+
+static const struct dis386 grps[][8] = {
+  /* GRP1b */
+  {
+    { "addA",	Eb, Ib, XX },
+    { "orA",	Eb, Ib, XX },
+    { "adcA",	Eb, Ib, XX },
+    { "sbbA",	Eb, Ib, XX },
+    { "andA",	Eb, Ib, XX },
+    { "subA",	Eb, Ib, XX },
+    { "xorA",	Eb, Ib, XX },
+    { "cmpA",	Eb, Ib, XX }
+  },
+  /* GRP1S */
+  {
+    { "addQ",	Ev, Iv, XX },
+    { "orQ",	Ev, Iv, XX },
+    { "adcQ",	Ev, Iv, XX },
+    { "sbbQ",	Ev, Iv, XX },
+    { "andQ",	Ev, Iv, XX },
+    { "subQ",	Ev, Iv, XX },
+    { "xorQ",	Ev, Iv, XX },
+    { "cmpQ",	Ev, Iv, XX }
+  },
+  /* GRP1Ss */
+  {
+    { "addQ",	Ev, sIb, XX },
+    { "orQ",	Ev, sIb, XX },
+    { "adcQ",	Ev, sIb, XX },
+    { "sbbQ",	Ev, sIb, XX },
+    { "andQ",	Ev, sIb, XX },
+    { "subQ",	Ev, sIb, XX },
+    { "xorQ",	Ev, sIb, XX },
+    { "cmpQ",	Ev, sIb, XX }
+  },
+  /* GRP2b */
+  {
+    { "rolA",	Eb, Ib, XX },
+    { "rorA",	Eb, Ib, XX },
+    { "rclA",	Eb, Ib, XX },
+    { "rcrA",	Eb, Ib, XX },
+    { "shlA",	Eb, Ib, XX },
+    { "shrA",	Eb, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "sarA",	Eb, Ib, XX },
+  },
+  /* GRP2S */
+  {
+    { "rolQ",	Ev, Ib, XX },
+    { "rorQ",	Ev, Ib, XX },
+    { "rclQ",	Ev, Ib, XX },
+    { "rcrQ",	Ev, Ib, XX },
+    { "shlQ",	Ev, Ib, XX },
+    { "shrQ",	Ev, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "sarQ",	Ev, Ib, XX },
+  },
+  /* GRP2b_one */
+  {
+    { "rolA",	Eb, XX, XX },
+    { "rorA",	Eb, XX, XX },
+    { "rclA",	Eb, XX, XX },
+    { "rcrA",	Eb, XX, XX },
+    { "shlA",	Eb, XX, XX },
+    { "shrA",	Eb, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "sarA",	Eb, XX, XX },
+  },
+  /* GRP2S_one */
+  {
+    { "rolQ",	Ev, XX, XX },
+    { "rorQ",	Ev, XX, XX },
+    { "rclQ",	Ev, XX, XX },
+    { "rcrQ",	Ev, XX, XX },
+    { "shlQ",	Ev, XX, XX },
+    { "shrQ",	Ev, XX, XX },
+    { "(bad)",	XX, XX, XX},
+    { "sarQ",	Ev, XX, XX },
+  },
+  /* GRP2b_cl */
+  {
+    { "rolA",	Eb, CL, XX },
+    { "rorA",	Eb, CL, XX },
+    { "rclA",	Eb, CL, XX },
+    { "rcrA",	Eb, CL, XX },
+    { "shlA",	Eb, CL, XX },
+    { "shrA",	Eb, CL, XX },
+    { "(bad)",	XX, XX, XX },
+    { "sarA",	Eb, CL, XX },
+  },
+  /* GRP2S_cl */
+  {
+    { "rolQ",	Ev, CL, XX },
+    { "rorQ",	Ev, CL, XX },
+    { "rclQ",	Ev, CL, XX },
+    { "rcrQ",	Ev, CL, XX },
+    { "shlQ",	Ev, CL, XX },
+    { "shrQ",	Ev, CL, XX },
+    { "(bad)",	XX, XX, XX },
+    { "sarQ",	Ev, CL, XX }
+  },
+  /* GRP3b */
+  {
+    { "testA",	Eb, Ib, XX },
+    { "(bad)",	Eb, XX, XX },
+    { "notA",	Eb, XX, XX },
+    { "negA",	Eb, XX, XX },
+    { "mulA",	Eb, XX, XX },	/* Don't print the implicit %al register,  */
+    { "imulA",	Eb, XX, XX },	/* to distinguish these opcodes from other */
+    { "divA",	Eb, XX, XX },	/* mul/imul opcodes.  Do the same for div  */
+    { "idivA",	Eb, XX, XX }	/* and idiv for consistency.		   */
+  },
+  /* GRP3S */
+  {
+    { "testQ",	Ev, Iv, XX },
+    { "(bad)",	XX, XX, XX },
+    { "notQ",	Ev, XX, XX },
+    { "negQ",	Ev, XX, XX },
+    { "mulQ",	Ev, XX, XX },	/* Don't print the implicit register.  */
+    { "imulQ",	Ev, XX, XX },
+    { "divQ",	Ev, XX, XX },
+    { "idivQ",	Ev, XX, XX },
+  },
+  /* GRP4 */
+  {
+    { "incA",	Eb, XX, XX },
+    { "decA",	Eb, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* GRP5 */
+  {
+    { "incQ",	Ev, XX, XX },
+    { "decQ",	Ev, XX, XX },
+    { "callT",	indirEv, XX, XX },
+    { "lcallT",	indirEv, XX, XX },
+    { "jmpT",	indirEv, XX, XX },
+    { "ljmpT",	indirEv, XX, XX },
+    { "pushU",	Ev, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* GRP6 */
+  {
+    { "sldtQ",	Ev, XX, XX },
+    { "strQ",	Ev, XX, XX },
+    { "lldt",	Ew, XX, XX },
+    { "ltr",	Ew, XX, XX },
+    { "verr",	Ew, XX, XX },
+    { "verw",	Ew, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX }
+  },
+  /* GRP7 */
+  {
+    { "sgdtQ",	 M, XX, XX },
+    { "sidtQ",	 M, XX, XX },
+    { "lgdtQ",	 M, XX, XX },
+    { "lidtQ",	 M, XX, XX },
+    { "smswQ",	Ev, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "lmsw",	Ew, XX, XX },
+    { "invlpg",	Ew, XX, XX },
+  },
+  /* GRP8 */
+  {
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "btQ",	Ev, Ib, XX },
+    { "btsQ",	Ev, Ib, XX },
+    { "btrQ",	Ev, Ib, XX },
+    { "btcQ",	Ev, Ib, XX },
+  },
+  /* GRP9 */
+  {
+    { "(bad)",	XX, XX, XX },
+    { "cmpxchg8b", Ev, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* GRP10 */
+  {
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psrlw",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psraw",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psllw",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* GRP11 */
+  {
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psrld",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psrad",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "pslld",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* GRP12 */
+  {
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psrlq",	MS, Ib, XX },
+    { "psrldq",	MS, Ib, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "psllq",	MS, Ib, XX },
+    { "pslldq",	MS, Ib, XX },
+  },
+  /* GRP13 */
+  {
+    { "fxsave", Ev, XX, XX },
+    { "fxrstor", Ev, XX, XX },
+    { "ldmxcsr", Ev, XX, XX },
+    { "stmxcsr", Ev, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "lfence", None, XX, XX },
+    { "mfence", None, XX, XX },
+    { "sfence", None, XX, XX },
+    /* FIXME: the sfence with memory operand is clflush!  */
+  },
+  /* GRP14 */
+  {
+    { "prefetchnta", Ev, XX, XX },
+    { "prefetcht0", Ev, XX, XX },
+    { "prefetcht1", Ev, XX, XX },
+    { "prefetcht2", Ev, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* GRPAMD */
+  {
+    { "prefetch", Eb, XX, XX },
+    { "prefetchw", Eb, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  }
+};
+
+static const struct dis386 prefix_user_table[][4] = {
+  /* PREGRP0 */
+  {
+    { "addps", XM, EX, XX },
+    { "addss", XM, EX, XX },
+    { "addpd", XM, EX, XX },
+    { "addsd", XM, EX, XX },
+  },
+  /* PREGRP1 */
+  {
+    { "", XM, EX, OPSIMD },	/* See OP_SIMD_SUFFIX.  */
+    { "", XM, EX, OPSIMD },
+    { "", XM, EX, OPSIMD },
+    { "", XM, EX, OPSIMD },
+  },
+  /* PREGRP2 */
+  {
+    { "cvtpi2ps", XM, EM, XX },
+    { "cvtsi2ssY", XM, Ev, XX },
+    { "cvtpi2pd", XM, EM, XX },
+    { "cvtsi2sdY", XM, Ev, XX },
+  },
+  /* PREGRP3 */
+  {
+    { "cvtps2pi", MX, EX, XX },
+    { "cvtss2siY", Gv, EX, XX },
+    { "cvtpd2pi", MX, EX, XX },
+    { "cvtsd2siY", Gv, EX, XX },
+  },
+  /* PREGRP4 */
+  {
+    { "cvttps2pi", MX, EX, XX },
+    { "cvttss2siY", Gv, EX, XX },
+    { "cvttpd2pi", MX, EX, XX },
+    { "cvttsd2siY", Gv, EX, XX },
+  },
+  /* PREGRP5 */
+  {
+    { "divps", XM, EX, XX },
+    { "divss", XM, EX, XX },
+    { "divpd", XM, EX, XX },
+    { "divsd", XM, EX, XX },
+  },
+  /* PREGRP6 */
+  {
+    { "maxps", XM, EX, XX },
+    { "maxss", XM, EX, XX },
+    { "maxpd", XM, EX, XX },
+    { "maxsd", XM, EX, XX },
+  },
+  /* PREGRP7 */
+  {
+    { "minps", XM, EX, XX },
+    { "minss", XM, EX, XX },
+    { "minpd", XM, EX, XX },
+    { "minsd", XM, EX, XX },
+  },
+  /* PREGRP8 */
+  {
+    { "movups", XM, EX, XX },
+    { "movss", XM, EX, XX },
+    { "movupd", XM, EX, XX },
+    { "movsd", XM, EX, XX },
+  },
+  /* PREGRP9 */
+  {
+    { "movups", EX, XM, XX },
+    { "movss", EX, XM, XX },
+    { "movupd", EX, XM, XX },
+    { "movsd", EX, XM, XX },
+  },
+  /* PREGRP10 */
+  {
+    { "mulps", XM, EX, XX },
+    { "mulss", XM, EX, XX },
+    { "mulpd", XM, EX, XX },
+    { "mulsd", XM, EX, XX },
+  },
+  /* PREGRP11 */
+  {
+    { "rcpps", XM, EX, XX },
+    { "rcpss", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+  /* PREGRP12 */
+  {
+    { "rsqrtps", XM, EX, XX },
+    { "rsqrtss", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+  /* PREGRP13 */
+  {
+    { "sqrtps", XM, EX, XX },
+    { "sqrtss", XM, EX, XX },
+    { "sqrtpd", XM, EX, XX },
+    { "sqrtsd", XM, EX, XX },
+  },
+  /* PREGRP14 */
+  {
+    { "subps", XM, EX, XX },
+    { "subss", XM, EX, XX },
+    { "subpd", XM, EX, XX },
+    { "subsd", XM, EX, XX },
+  },
+  /* PREGRP15 */
+  {
+    { "(bad)", XM, EX, XX },
+    { "cvtdq2pd", XM, EX, XX },
+    { "cvttpd2dq", XM, EX, XX },
+    { "cvtpd2dq", XM, EX, XX },
+  },
+  /* PREGRP16 */
+  {
+    { "cvtdq2ps", XM, EX, XX },
+    { "cvttps2dq",XM, EX, XX },
+    { "cvtps2dq",XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+  /* PREGRP17 */
+  {
+    { "cvtps2pd", XM, EX, XX },
+    { "cvtss2sd", XM, EX, XX },
+    { "cvtpd2ps", XM, EX, XX },
+    { "cvtsd2ss", XM, EX, XX },
+  },
+  /* PREGRP18 */
+  {
+    { "maskmovq", MX, MS, XX },
+    { "(bad)", XM, EX, XX },
+    { "maskmovdqu", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+  /* PREGRP19 */
+  {
+    { "movq", MX, EM, XX },
+    { "movdqu", XM, EX, XX },
+    { "movdqa", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+  /* PREGRP20 */
+  {
+    { "movq", EM, MX, XX },
+    { "movdqu", EX, XM, XX },
+    { "movdqa", EX, XM, XX },
+    { "(bad)", EX, XM, XX },
+  },
+  /* PREGRP21 */
+  {
+    { "(bad)", EX, XM, XX },
+    { "movq2dq", XM, MS, XX },
+    { "movq", EX, XM, XX },
+    { "movdq2q", MX, XS, XX },
+  },
+  /* PREGRP22 */
+  {
+    { "pshufw", MX, EM, Ib },
+    { "pshufhw", XM, EX, Ib },
+    { "pshufd", XM, EX, Ib },
+    { "pshuflw", XM, EX, Ib },
+  },
+  /* PREGRP23 */
+  {
+    { "movd", Ed, MX, XX },
+    { "movq", XM, EX, XX },
+    { "movd", Ed, XM, XX },
+    { "(bad)", Ed, XM, XX },
+  },
+  /* PREGRP24 */
+  {
+    { "(bad)", MX, EX, XX },
+    { "(bad)", XM, EX, XX },
+    { "punpckhqdq", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+  /* PREGRP25 */
+  {
+  { "movntq", Ev, MX, XX },
+  { "(bad)", Ev, XM, XX },
+  { "movntdq", Ev, XM, XX },
+  { "(bad)", Ev, XM, XX },
+  },
+  /* PREGRP26 */
+  {
+    { "(bad)", MX, EX, XX },
+    { "(bad)", XM, EX, XX },
+    { "punpcklqdq", XM, EX, XX },
+    { "(bad)", XM, EX, XX },
+  },
+};
+
+static const struct dis386 x86_64_table[][2] = {
+  {
+    { "arpl", Ew, Gw, XX },
+    { "movs{||lq|xd}", Gv, Ed, XX },
+  },
+};
+
+#define INTERNAL_DISASSEMBLER_ERROR _("<internal disassembler error>")
+
+static void
+ckprefix ()
+{
+  int newrex;
+  rex = 0;
+  prefixes = 0;
+  used_prefixes = 0;
+  rex_used = 0;
+  while (1)
+    {
+      FETCH_DATA (the_info, codep + 1);
+      newrex = 0;
+      switch (*codep)
+	{
+	/* REX prefixes family.  */
+	case 0x40:
+	case 0x41:
+	case 0x42:
+	case 0x43:
+	case 0x44:
+	case 0x45:
+	case 0x46:
+	case 0x47:
+	case 0x48:
+	case 0x49:
+	case 0x4a:
+	case 0x4b:
+	case 0x4c:
+	case 0x4d:
+	case 0x4e:
+	case 0x4f:
+	    if (mode_64bit)
+	      newrex = *codep;
+	    else
+	      return;
+	  break;
+	case 0xf3:
+	  prefixes |= PREFIX_REPZ;
+	  break;
+	case 0xf2:
+	  prefixes |= PREFIX_REPNZ;
+	  break;
+	case 0xf0:
+	  prefixes |= PREFIX_LOCK;
+	  break;
+	case 0x2e:
+	  prefixes |= PREFIX_CS;
+	  break;
+	case 0x36:
+	  prefixes |= PREFIX_SS;
+	  break;
+	case 0x3e:
+	  prefixes |= PREFIX_DS;
+	  break;
+	case 0x26:
+	  prefixes |= PREFIX_ES;
+	  break;
+	case 0x64:
+	  prefixes |= PREFIX_FS;
+	  break;
+	case 0x65:
+	  prefixes |= PREFIX_GS;
+	  break;
+	case 0x66:
+	  prefixes |= PREFIX_DATA;
+	  break;
+	case 0x67:
+	  prefixes |= PREFIX_ADDR;
+	  break;
+	case FWAIT_OPCODE:
+	  /* fwait is really an instruction.  If there are prefixes
+	     before the fwait, they belong to the fwait, *not* to the
+	     following instruction.  */
+	  if (prefixes)
+	    {
+	      prefixes |= PREFIX_FWAIT;
+	      codep++;
+	      return;
+	    }
+	  prefixes = PREFIX_FWAIT;
+	  break;
+	default:
+	  return;
+	}
+      /* Rex is ignored when followed by another prefix.  */
+      if (rex)
+	{
+	  oappend (prefix_name (rex, 0));
+	  oappend (" ");
+	}
+      rex = newrex;
+      codep++;
+    }
+}
+
+/* Return the name of the prefix byte PREF, or NULL if PREF is not a
+   prefix byte.  */
+
+static const char *
+prefix_name (pref, sizeflag)
+     int pref;
+     int sizeflag;
+{
+  switch (pref)
+    {
+    /* REX prefixes family.  */
+    case 0x40:
+      return "rex";
+    case 0x41:
+      return "rexZ";
+    case 0x42:
+      return "rexY";
+    case 0x43:
+      return "rexYZ";
+    case 0x44:
+      return "rexX";
+    case 0x45:
+      return "rexXZ";
+    case 0x46:
+      return "rexXY";
+    case 0x47:
+      return "rexXYZ";
+    case 0x48:
+      return "rex64";
+    case 0x49:
+      return "rex64Z";
+    case 0x4a:
+      return "rex64Y";
+    case 0x4b:
+      return "rex64YZ";
+    case 0x4c:
+      return "rex64X";
+    case 0x4d:
+      return "rex64XZ";
+    case 0x4e:
+      return "rex64XY";
+    case 0x4f:
+      return "rex64XYZ";
+    case 0xf3:
+      return "repz";
+    case 0xf2:
+      return "repnz";
+    case 0xf0:
+      return "lock";
+    case 0x2e:
+      return "cs";
+    case 0x36:
+      return "ss";
+    case 0x3e:
+      return "ds";
+    case 0x26:
+      return "es";
+    case 0x64:
+      return "fs";
+    case 0x65:
+      return "gs";
+    case 0x66:
+      return (sizeflag & DFLAG) ? "data16" : "data32";
+    case 0x67:
+      if (mode_64bit)
+        return (sizeflag & AFLAG) ? "addr32" : "addr64";
+      else
+        return ((sizeflag & AFLAG) && !mode_64bit) ? "addr16" : "addr32";
+    case FWAIT_OPCODE:
+      return "fwait";
+    default:
+      return NULL;
+    }
+}
+
+static char op1out[100], op2out[100], op3out[100];
+static int op_ad, op_index[3];
+static bfd_vma op_address[3];
+static bfd_vma op_riprel[3];
+static bfd_vma start_pc;
+
+/*
+ *   On the 386's of 1988, the maximum length of an instruction is 15 bytes.
+ *   (see topic "Redundant prefixes" in the "Differences from 8086"
+ *   section of the "Virtual 8086 Mode" chapter.)
+ * 'pc' should be the address of this instruction, it will
+ *   be used to print the target address if this is a relative jump or call
+ * The function returns the length of this instruction in bytes.
+ */
+
+static int8_t intel_syntax;
+static char open_char;
+static char close_char;
+static char separator_char;
+static char scale_char;
+
+int
+print_insn_i386 (pc, info)
+     bfd_vma pc;
+     disassemble_info *info;
+{
+  intel_syntax = -1;
+
+  return print_insn (pc, info);
+}
+
+static int
+print_insn (pc, info)
+     bfd_vma pc;
+     disassemble_info *info;
+{
+  const struct dis386 *dp;
+  int i;
+  int two_source_ops;
+  char *first, *second, *third;
+  int needcomma;
+  unsigned char uses_SSE_prefix;
+  int sizeflag;
+  const char *p;
+  struct dis_private priv;
+
+  mode_64bit = (info->mach == bfd_mach_x86_64_intel_syntax
+		|| info->mach == bfd_mach_x86_64);
+
+  if (intel_syntax == -1)
+    intel_syntax = (info->mach == bfd_mach_i386_i386_intel_syntax
+		    || info->mach == bfd_mach_x86_64_intel_syntax);
+
+  if (info->mach == bfd_mach_i386_i386
+      || info->mach == bfd_mach_x86_64
+      || info->mach == bfd_mach_i386_i386_intel_syntax
+      || info->mach == bfd_mach_x86_64_intel_syntax)
+    priv.orig_sizeflag = AFLAG | DFLAG;
+  else if (info->mach == bfd_mach_i386_i8086)
+    priv.orig_sizeflag = 0;
+  else
+    abort ();
+
+  for (p = info->disassembler_options; p != NULL; )
+    {
+      if (strncmp (p, "x86-64", 6) == 0)
+	{
+	  mode_64bit = 1;
+	  priv.orig_sizeflag = AFLAG | DFLAG;
+	}
+      else if (strncmp (p, "i386", 4) == 0)
+	{
+	  mode_64bit = 0;
+	  priv.orig_sizeflag = AFLAG | DFLAG;
+	}
+      else if (strncmp (p, "i8086", 5) == 0)
+	{
+	  mode_64bit = 0;
+	  priv.orig_sizeflag = 0;
+	}
+      else if (strncmp (p, "intel", 5) == 0)
+	{
+	  intel_syntax = 1;
+	}
+      else if (strncmp (p, "att", 3) == 0)
+	{
+	  intel_syntax = 0;
+	}
+      else if (strncmp (p, "addr", 4) == 0)
+	{
+	  if (p[4] == '1' && p[5] == '6')
+	    priv.orig_sizeflag &= ~AFLAG;
+	  else if (p[4] == '3' && p[5] == '2')
+	    priv.orig_sizeflag |= AFLAG;
+	}
+      else if (strncmp (p, "data", 4) == 0)
+	{
+	  if (p[4] == '1' && p[5] == '6')
+	    priv.orig_sizeflag &= ~DFLAG;
+	  else if (p[4] == '3' && p[5] == '2')
+	    priv.orig_sizeflag |= DFLAG;
+	}
+      else if (strncmp (p, "suffix", 6) == 0)
+	priv.orig_sizeflag |= SUFFIX_ALWAYS;
+
+      p = strchr (p, ',');
+      if (p != NULL)
+	p++;
+    }
+
+  if (intel_syntax)
+    {
+      names64 = intel_names64;
+      names32 = intel_names32;
+      names16 = intel_names16;
+      names8 = intel_names8;
+      names8rex = intel_names8rex;
+      names_seg = intel_names_seg;
+      index16 = intel_index16;
+      open_char = '[';
+      close_char = ']';
+      separator_char = '+';
+      scale_char = '*';
+    }
+  else
+    {
+      names64 = att_names64;
+      names32 = att_names32;
+      names16 = att_names16;
+      names8 = att_names8;
+      names8rex = att_names8rex;
+      names_seg = att_names_seg;
+      index16 = att_index16;
+      open_char = '(';
+      close_char =  ')';
+      separator_char = ',';
+      scale_char = ',';
+    }
+
+  /* The output looks better if we put 7 bytes on a line, since that
+     puts most long word instructions on a single line.  */
+  info->bytes_per_line = 7;
+
+  info->private_data = (PTR) &priv;
+  priv.max_fetched = priv.the_buffer;
+  priv.insn_start = pc;
+
+  obuf[0] = 0;
+  op1out[0] = 0;
+  op2out[0] = 0;
+  op3out[0] = 0;
+
+  op_index[0] = op_index[1] = op_index[2] = -1;
+
+  the_info = info;
+  start_pc = pc;
+  start_codep = priv.the_buffer;
+  codep = priv.the_buffer;
+
+  if (setjmp (priv.bailout) != 0)
+    {
+      const char *name;
+
+      /* Getting here means we tried for data but didn't get it.  That
+	 means we have an incomplete instruction of some sort.  Just
+	 print the first byte as a prefix or a .byte pseudo-op.  */
+      if (codep > priv.the_buffer)
+	{
+	  name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+	  if (name != NULL)
+	    (*info->fprintf_func) (info->stream, "%s", name);
+	  else
+	    {
+	      /* Just print the first byte as a .byte instruction.  */
+	      (*info->fprintf_func) (info->stream, ".byte 0x%x",
+				     (unsigned int) priv.the_buffer[0]);
+	    }
+
+	  return 1;
+	}
+
+      return -1;
+    }
+
+  obufp = obuf;
+  ckprefix ();
+
+  insn_codep = codep;
+  sizeflag = priv.orig_sizeflag;
+
+  FETCH_DATA (info, codep + 1);
+  two_source_ops = (*codep == 0x62) || (*codep == 0xc8);
+
+  if ((prefixes & PREFIX_FWAIT)
+      && ((*codep < 0xd8) || (*codep > 0xdf)))
+    {
+      const char *name;
+
+      /* fwait not followed by floating point instruction.  Print the
+         first prefix, which is probably fwait itself.  */
+      name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+      if (name == NULL)
+	name = INTERNAL_DISASSEMBLER_ERROR;
+      (*info->fprintf_func) (info->stream, "%s", name);
+      return 1;
+    }
+
+  if (*codep == 0x0f)
+    {
+      FETCH_DATA (info, codep + 2);
+      dp = &dis386_twobyte[*++codep];
+      need_modrm = twobyte_has_modrm[*codep];
+      uses_SSE_prefix = twobyte_uses_SSE_prefix[*codep];
+    }
+  else
+    {
+      dp = &dis386[*codep];
+      need_modrm = onebyte_has_modrm[*codep];
+      uses_SSE_prefix = 0;
+    }
+  codep++;
+
+  if (!uses_SSE_prefix && (prefixes & PREFIX_REPZ))
+    {
+      oappend ("repz ");
+      used_prefixes |= PREFIX_REPZ;
+    }
+  if (!uses_SSE_prefix && (prefixes & PREFIX_REPNZ))
+    {
+      oappend ("repnz ");
+      used_prefixes |= PREFIX_REPNZ;
+    }
+  if (prefixes & PREFIX_LOCK)
+    {
+      oappend ("lock ");
+      used_prefixes |= PREFIX_LOCK;
+    }
+
+  if (prefixes & PREFIX_ADDR)
+    {
+      sizeflag ^= AFLAG;
+      if (dp->bytemode3 != loop_jcxz_mode || intel_syntax)
+	{
+	  if ((sizeflag & AFLAG) || mode_64bit)
+	    oappend ("addr32 ");
+	  else
+	    oappend ("addr16 ");
+	  used_prefixes |= PREFIX_ADDR;
+	}
+    }
+
+  if (!uses_SSE_prefix && (prefixes & PREFIX_DATA))
+    {
+      sizeflag ^= DFLAG;
+      if (dp->bytemode3 == cond_jump_mode
+	  && dp->bytemode1 == v_mode
+	  && !intel_syntax)
+	{
+	  if (sizeflag & DFLAG)
+	    oappend ("data32 ");
+	  else
+	    oappend ("data16 ");
+	  used_prefixes |= PREFIX_DATA;
+	}
+    }
+
+  if (need_modrm)
+    {
+      FETCH_DATA (info, codep + 1);
+      mod = (*codep >> 6) & 3;
+      reg = (*codep >> 3) & 7;
+      rm = *codep & 7;
+    }
+
+  if (dp->name == NULL && dp->bytemode1 == FLOATCODE)
+    {
+      dofloat (sizeflag);
+    }
+  else
+    {
+      int index;
+      if (dp->name == NULL)
+	{
+	  switch (dp->bytemode1)
+	    {
+	    case USE_GROUPS:
+	      dp = &grps[dp->bytemode2][reg];
+	      break;
+
+	    case USE_PREFIX_USER_TABLE:
+	      index = 0;
+	      used_prefixes |= (prefixes & PREFIX_REPZ);
+	      if (prefixes & PREFIX_REPZ)
+		index = 1;
+	      else
+		{
+		  used_prefixes |= (prefixes & PREFIX_DATA);
+		  if (prefixes & PREFIX_DATA)
+		    index = 2;
+		  else
+		    {
+		      used_prefixes |= (prefixes & PREFIX_REPNZ);
+		      if (prefixes & PREFIX_REPNZ)
+			index = 3;
+		    }
+		}
+	      dp = &prefix_user_table[dp->bytemode2][index];
+	      break;
+
+	    case X86_64_SPECIAL:
+	      dp = &x86_64_table[dp->bytemode2][mode_64bit];
+	      break;
+
+	    default:
+	      oappend (INTERNAL_DISASSEMBLER_ERROR);
+	      break;
+	    }
+	}
+
+      if (putop (dp->name, sizeflag) == 0)
+	{
+	  obufp = op1out;
+	  op_ad = 2;
+	  if (dp->op1)
+	    (*dp->op1) (dp->bytemode1, sizeflag);
+
+	  obufp = op2out;
+	  op_ad = 1;
+	  if (dp->op2)
+	    (*dp->op2) (dp->bytemode2, sizeflag);
+
+	  obufp = op3out;
+	  op_ad = 0;
+	  if (dp->op3)
+	    (*dp->op3) (dp->bytemode3, sizeflag);
+	}
+    }
+
+  /* See if any prefixes were not used.  If so, print the first one
+     separately.  If we don't do this, we'll wind up printing an
+     instruction stream which does not precisely correspond to the
+     bytes we are disassembling.  */
+  if ((prefixes & ~used_prefixes) != 0)
+    {
+      const char *name;
+
+      name = prefix_name (priv.the_buffer[0], priv.orig_sizeflag);
+      if (name == NULL)
+	name = INTERNAL_DISASSEMBLER_ERROR;
+      (*info->fprintf_func) (info->stream, "%s", name);
+      return 1;
+    }
+  if (rex & ~rex_used)
+    {
+      const char *name;
+      name = prefix_name (rex | 0x40, priv.orig_sizeflag);
+      if (name == NULL)
+	name = INTERNAL_DISASSEMBLER_ERROR;
+      (*info->fprintf_func) (info->stream, "%s ", name);
+    }
+
+  obufp = obuf + strlen (obuf);
+  for (i = strlen (obuf); i < 6; i++)
+    oappend (" ");
+  oappend (" ");
+  (*info->fprintf_func) (info->stream, "%s", obuf);
+
+  /* The enter and bound instructions are printed with operands in the same
+     order as the intel book; everything else is printed in reverse order.  */
+  if (intel_syntax || two_source_ops)
+    {
+      first = op1out;
+      second = op2out;
+      third = op3out;
+      op_ad = op_index[0];
+      op_index[0] = op_index[2];
+      op_index[2] = op_ad;
+    }
+  else
+    {
+      first = op3out;
+      second = op2out;
+      third = op1out;
+    }
+  needcomma = 0;
+  if (*first)
+    {
+      if (op_index[0] != -1 && !op_riprel[0])
+	(*info->print_address_func) ((bfd_vma) op_address[op_index[0]], info);
+      else
+	(*info->fprintf_func) (info->stream, "%s", first);
+      needcomma = 1;
+    }
+  if (*second)
+    {
+      if (needcomma)
+	(*info->fprintf_func) (info->stream, ",");
+      if (op_index[1] != -1 && !op_riprel[1])
+	(*info->print_address_func) ((bfd_vma) op_address[op_index[1]], info);
+      else
+	(*info->fprintf_func) (info->stream, "%s", second);
+      needcomma = 1;
+    }
+  if (*third)
+    {
+      if (needcomma)
+	(*info->fprintf_func) (info->stream, ",");
+      if (op_index[2] != -1 && !op_riprel[2])
+	(*info->print_address_func) ((bfd_vma) op_address[op_index[2]], info);
+      else
+	(*info->fprintf_func) (info->stream, "%s", third);
+    }
+  for (i = 0; i < 3; i++)
+    if (op_index[i] != -1 && op_riprel[i])
+      {
+	(*info->fprintf_func) (info->stream, "        # ");
+	(*info->print_address_func) ((bfd_vma) (start_pc + codep - start_codep
+						+ op_address[op_index[i]]), info);
+      }
+  return codep - priv.the_buffer;
+}
+
+static const char *float_mem[] = {
+  /* d8 */
+  "fadd{s||s|}",
+  "fmul{s||s|}",
+  "fcom{s||s|}",
+  "fcomp{s||s|}",
+  "fsub{s||s|}",
+  "fsubr{s||s|}",
+  "fdiv{s||s|}",
+  "fdivr{s||s|}",
+  /*  d9 */
+  "fld{s||s|}",
+  "(bad)",
+  "fst{s||s|}",
+  "fstp{s||s|}",
+  "fldenv",
+  "fldcw",
+  "fNstenv",
+  "fNstcw",
+  /* da */
+  "fiadd{l||l|}",
+  "fimul{l||l|}",
+  "ficom{l||l|}",
+  "ficomp{l||l|}",
+  "fisub{l||l|}",
+  "fisubr{l||l|}",
+  "fidiv{l||l|}",
+  "fidivr{l||l|}",
+  /* db */
+  "fild{l||l|}",
+  "(bad)",
+  "fist{l||l|}",
+  "fistp{l||l|}",
+  "(bad)",
+  "fld{t||t|}",
+  "(bad)",
+  "fstp{t||t|}",
+  /* dc */
+  "fadd{l||l|}",
+  "fmul{l||l|}",
+  "fcom{l||l|}",
+  "fcomp{l||l|}",
+  "fsub{l||l|}",
+  "fsubr{l||l|}",
+  "fdiv{l||l|}",
+  "fdivr{l||l|}",
+  /* dd */
+  "fld{l||l|}",
+  "(bad)",
+  "fst{l||l|}",
+  "fstp{l||l|}",
+  "frstor",
+  "(bad)",
+  "fNsave",
+  "fNstsw",
+  /* de */
+  "fiadd",
+  "fimul",
+  "ficom",
+  "ficomp",
+  "fisub",
+  "fisubr",
+  "fidiv",
+  "fidivr",
+  /* df */
+  "fild",
+  "(bad)",
+  "fist",
+  "fistp",
+  "fbld",
+  "fild{ll||ll|}",
+  "fbstp",
+  "fistpll",
+};
+
+#define ST OP_ST, 0
+#define STi OP_STi, 0
+
+#define FGRPd9_2 NULL, NULL, 0, NULL, 0, NULL, 0
+#define FGRPd9_4 NULL, NULL, 1, NULL, 0, NULL, 0
+#define FGRPd9_5 NULL, NULL, 2, NULL, 0, NULL, 0
+#define FGRPd9_6 NULL, NULL, 3, NULL, 0, NULL, 0
+#define FGRPd9_7 NULL, NULL, 4, NULL, 0, NULL, 0
+#define FGRPda_5 NULL, NULL, 5, NULL, 0, NULL, 0
+#define FGRPdb_4 NULL, NULL, 6, NULL, 0, NULL, 0
+#define FGRPde_3 NULL, NULL, 7, NULL, 0, NULL, 0
+#define FGRPdf_4 NULL, NULL, 8, NULL, 0, NULL, 0
+
+static const struct dis386 float_reg[][8] = {
+  /* d8 */
+  {
+    { "fadd",	ST, STi, XX },
+    { "fmul",	ST, STi, XX },
+    { "fcom",	STi, XX, XX },
+    { "fcomp",	STi, XX, XX },
+    { "fsub",	ST, STi, XX },
+    { "fsubr",	ST, STi, XX },
+    { "fdiv",	ST, STi, XX },
+    { "fdivr",	ST, STi, XX },
+  },
+  /* d9 */
+  {
+    { "fld",	STi, XX, XX },
+    { "fxch",	STi, XX, XX },
+    { FGRPd9_2 },
+    { "(bad)",	XX, XX, XX },
+    { FGRPd9_4 },
+    { FGRPd9_5 },
+    { FGRPd9_6 },
+    { FGRPd9_7 },
+  },
+  /* da */
+  {
+    { "fcmovb",	ST, STi, XX },
+    { "fcmove",	ST, STi, XX },
+    { "fcmovbe",ST, STi, XX },
+    { "fcmovu",	ST, STi, XX },
+    { "(bad)",	XX, XX, XX },
+    { FGRPda_5 },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* db */
+  {
+    { "fcmovnb",ST, STi, XX },
+    { "fcmovne",ST, STi, XX },
+    { "fcmovnbe",ST, STi, XX },
+    { "fcmovnu",ST, STi, XX },
+    { FGRPdb_4 },
+    { "fucomi",	ST, STi, XX },
+    { "fcomi",	ST, STi, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* dc */
+  {
+    { "fadd",	STi, ST, XX },
+    { "fmul",	STi, ST, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+#if UNIXWARE_COMPAT
+    { "fsub",	STi, ST, XX },
+    { "fsubr",	STi, ST, XX },
+    { "fdiv",	STi, ST, XX },
+    { "fdivr",	STi, ST, XX },
+#else
+    { "fsubr",	STi, ST, XX },
+    { "fsub",	STi, ST, XX },
+    { "fdivr",	STi, ST, XX },
+    { "fdiv",	STi, ST, XX },
+#endif
+  },
+  /* dd */
+  {
+    { "ffree",	STi, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "fst",	STi, XX, XX },
+    { "fstp",	STi, XX, XX },
+    { "fucom",	STi, XX, XX },
+    { "fucomp",	STi, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+  /* de */
+  {
+    { "faddp",	STi, ST, XX },
+    { "fmulp",	STi, ST, XX },
+    { "(bad)",	XX, XX, XX },
+    { FGRPde_3 },
+#if UNIXWARE_COMPAT
+    { "fsubp",	STi, ST, XX },
+    { "fsubrp",	STi, ST, XX },
+    { "fdivp",	STi, ST, XX },
+    { "fdivrp",	STi, ST, XX },
+#else
+    { "fsubrp",	STi, ST, XX },
+    { "fsubp",	STi, ST, XX },
+    { "fdivrp",	STi, ST, XX },
+    { "fdivp",	STi, ST, XX },
+#endif
+  },
+  /* df */
+  {
+    { "ffreep",	STi, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { "(bad)",	XX, XX, XX },
+    { FGRPdf_4 },
+    { "fucomip",ST, STi, XX },
+    { "fcomip", ST, STi, XX },
+    { "(bad)",	XX, XX, XX },
+  },
+};
+
+static const char *fgrps[][8] = {
+  /* d9_2  0 */
+  {
+    "fnop","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+  },
+
+  /* d9_4  1 */
+  {
+    "fchs","fabs","(bad)","(bad)","ftst","fxam","(bad)","(bad)",
+  },
+
+  /* d9_5  2 */
+  {
+    "fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz","(bad)",
+  },
+
+  /* d9_6  3 */
+  {
+    "f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp",
+  },
+
+  /* d9_7  4 */
+  {
+    "fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos",
+  },
+
+  /* da_5  5 */
+  {
+    "(bad)","fucompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+  },
+
+  /* db_4  6 */
+  {
+    "feni(287 only)","fdisi(287 only)","fNclex","fNinit",
+    "fNsetpm(287 only)","(bad)","(bad)","(bad)",
+  },
+
+  /* de_3  7 */
+  {
+    "(bad)","fcompp","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+  },
+
+  /* df_4  8 */
+  {
+    "fNstsw","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)","(bad)",
+  },
+};
+
+static void
+dofloat (sizeflag)
+     int sizeflag;
+{
+  const struct dis386 *dp;
+  unsigned char floatop;
+
+  floatop = codep[-1];
+
+  if (mod != 3)
+    {
+      putop (float_mem[(floatop - 0xd8) * 8 + reg], sizeflag);
+      obufp = op1out;
+      if (floatop == 0xdb)
+        OP_E (x_mode, sizeflag);
+      else if (floatop == 0xdd)
+        OP_E (d_mode, sizeflag);
+      else
+        OP_E (v_mode, sizeflag);
+      return;
+    }
+  /* Skip mod/rm byte.  */
+  MODRM_CHECK;
+  codep++;
+
+  dp = &float_reg[floatop - 0xd8][reg];
+  if (dp->name == NULL)
+    {
+      putop (fgrps[dp->bytemode1][rm], sizeflag);
+
+      /* Instruction fnstsw is only one with strange arg.  */
+      if (floatop == 0xdf && codep[-1] == 0xe0)
+        pstrcpy (op1out, sizeof(op1out), names16[0]);
+    }
+  else
+    {
+      putop (dp->name, sizeflag);
+
+      obufp = op1out;
+      if (dp->op1)
+	(*dp->op1) (dp->bytemode1, sizeflag);
+      obufp = op2out;
+      if (dp->op2)
+	(*dp->op2) (dp->bytemode2, sizeflag);
+    }
+}
+
+static void
+OP_ST (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  oappend ("%st");
+}
+
+static void
+OP_STi (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  snprintf (scratchbuf, sizeof(scratchbuf), "%%st(%d)", rm);
+  oappend (scratchbuf + intel_syntax);
+}
+
+/* Capital letters in template are macros.  */
+static int
+putop (template, sizeflag)
+     const char *template;
+     int sizeflag;
+{
+  const char *p;
+  int alt;
+
+  for (p = template; *p; p++)
+    {
+      switch (*p)
+	{
+	default:
+	  *obufp++ = *p;
+	  break;
+	case '{':
+	  alt = 0;
+	  if (intel_syntax)
+	    alt += 1;
+	  if (mode_64bit)
+	    alt += 2;
+	  while (alt != 0)
+	    {
+	      while (*++p != '|')
+		{
+		  if (*p == '}')
+		    {
+		      /* Alternative not valid.  */
+                      pstrcpy (obuf, sizeof(obuf), "(bad)");
+		      obufp = obuf + 5;
+		      return 1;
+		    }
+		  else if (*p == '\0')
+		    abort ();
+		}
+	      alt--;
+	    }
+	  break;
+	case '|':
+	  while (*++p != '}')
+	    {
+	      if (*p == '\0')
+		abort ();
+	    }
+	  break;
+	case '}':
+	  break;
+	case 'A':
+          if (intel_syntax)
+            break;
+	  if (mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+	    *obufp++ = 'b';
+	  break;
+	case 'B':
+          if (intel_syntax)
+            break;
+	  if (sizeflag & SUFFIX_ALWAYS)
+	    *obufp++ = 'b';
+	  break;
+	case 'E':		/* For jcxz/jecxz */
+	  if (mode_64bit)
+	    {
+	      if (sizeflag & AFLAG)
+		*obufp++ = 'r';
+	      else
+		*obufp++ = 'e';
+	    }
+	  else
+	    if (sizeflag & AFLAG)
+	      *obufp++ = 'e';
+	  used_prefixes |= (prefixes & PREFIX_ADDR);
+	  break;
+	case 'F':
+          if (intel_syntax)
+            break;
+	  if ((prefixes & PREFIX_ADDR) || (sizeflag & SUFFIX_ALWAYS))
+	    {
+	      if (sizeflag & AFLAG)
+		*obufp++ = mode_64bit ? 'q' : 'l';
+	      else
+		*obufp++ = mode_64bit ? 'l' : 'w';
+	      used_prefixes |= (prefixes & PREFIX_ADDR);
+	    }
+	  break;
+	case 'H':
+          if (intel_syntax)
+            break;
+	  if ((prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_CS
+	      || (prefixes & (PREFIX_CS | PREFIX_DS)) == PREFIX_DS)
+	    {
+	      used_prefixes |= prefixes & (PREFIX_CS | PREFIX_DS);
+	      *obufp++ = ',';
+	      *obufp++ = 'p';
+	      if (prefixes & PREFIX_DS)
+		*obufp++ = 't';
+	      else
+		*obufp++ = 'n';
+	    }
+	  break;
+	case 'L':
+          if (intel_syntax)
+            break;
+	  if (sizeflag & SUFFIX_ALWAYS)
+	    *obufp++ = 'l';
+	  break;
+	case 'N':
+	  if ((prefixes & PREFIX_FWAIT) == 0)
+	    *obufp++ = 'n';
+	  else
+	    used_prefixes |= PREFIX_FWAIT;
+	  break;
+	case 'O':
+	  USED_REX (REX_MODE64);
+	  if (rex & REX_MODE64)
+	    *obufp++ = 'o';
+	  else
+	    *obufp++ = 'd';
+	  break;
+	case 'T':
+          if (intel_syntax)
+            break;
+	  if (mode_64bit)
+	    {
+	      *obufp++ = 'q';
+	      break;
+	    }
+	  /* Fall through.  */
+	case 'P':
+          if (intel_syntax)
+            break;
+	  if ((prefixes & PREFIX_DATA)
+	      || (rex & REX_MODE64)
+	      || (sizeflag & SUFFIX_ALWAYS))
+	    {
+	      USED_REX (REX_MODE64);
+	      if (rex & REX_MODE64)
+		*obufp++ = 'q';
+	      else
+		{
+		   if (sizeflag & DFLAG)
+		      *obufp++ = 'l';
+		   else
+		     *obufp++ = 'w';
+		   used_prefixes |= (prefixes & PREFIX_DATA);
+		}
+	    }
+	  break;
+	case 'U':
+          if (intel_syntax)
+            break;
+	  if (mode_64bit)
+	    {
+	      *obufp++ = 'q';
+	      break;
+	    }
+	  /* Fall through.  */
+	case 'Q':
+          if (intel_syntax)
+            break;
+	  USED_REX (REX_MODE64);
+	  if (mod != 3 || (sizeflag & SUFFIX_ALWAYS))
+	    {
+	      if (rex & REX_MODE64)
+		*obufp++ = 'q';
+	      else
+		{
+		  if (sizeflag & DFLAG)
+		    *obufp++ = 'l';
+		  else
+		    *obufp++ = 'w';
+		  used_prefixes |= (prefixes & PREFIX_DATA);
+		}
+	    }
+	  break;
+	case 'R':
+	  USED_REX (REX_MODE64);
+          if (intel_syntax)
+	    {
+	      if (rex & REX_MODE64)
+		{
+		  *obufp++ = 'q';
+		  *obufp++ = 't';
+		}
+	      else if (sizeflag & DFLAG)
+		{
+		  *obufp++ = 'd';
+		  *obufp++ = 'q';
+		}
+	      else
+		{
+		  *obufp++ = 'w';
+		  *obufp++ = 'd';
+		}
+	    }
+	  else
+	    {
+	      if (rex & REX_MODE64)
+		*obufp++ = 'q';
+	      else if (sizeflag & DFLAG)
+		*obufp++ = 'l';
+	      else
+		*obufp++ = 'w';
+	    }
+	  if (!(rex & REX_MODE64))
+	    used_prefixes |= (prefixes & PREFIX_DATA);
+	  break;
+	case 'S':
+          if (intel_syntax)
+            break;
+	  if (sizeflag & SUFFIX_ALWAYS)
+	    {
+	      if (rex & REX_MODE64)
+		*obufp++ = 'q';
+	      else
+		{
+		  if (sizeflag & DFLAG)
+		    *obufp++ = 'l';
+		  else
+		    *obufp++ = 'w';
+		  used_prefixes |= (prefixes & PREFIX_DATA);
+		}
+	    }
+	  break;
+	case 'X':
+	  if (prefixes & PREFIX_DATA)
+	    *obufp++ = 'd';
+	  else
+	    *obufp++ = 's';
+          used_prefixes |= (prefixes & PREFIX_DATA);
+	  break;
+	case 'Y':
+          if (intel_syntax)
+            break;
+	  if (rex & REX_MODE64)
+	    {
+	      USED_REX (REX_MODE64);
+	      *obufp++ = 'q';
+	    }
+	  break;
+	  /* implicit operand size 'l' for i386 or 'q' for x86-64 */
+	case 'W':
+	  /* operand size flag for cwtl, cbtw */
+	  USED_REX (0);
+	  if (rex)
+	    *obufp++ = 'l';
+	  else if (sizeflag & DFLAG)
+	    *obufp++ = 'w';
+	  else
+	    *obufp++ = 'b';
+          if (intel_syntax)
+	    {
+	      if (rex)
+		{
+		  *obufp++ = 'q';
+		  *obufp++ = 'e';
+		}
+	      if (sizeflag & DFLAG)
+		{
+		  *obufp++ = 'd';
+		  *obufp++ = 'e';
+		}
+	      else
+		{
+		  *obufp++ = 'w';
+		}
+	    }
+	  if (!rex)
+	    used_prefixes |= (prefixes & PREFIX_DATA);
+	  break;
+	}
+    }
+  *obufp = 0;
+  return 0;
+}
+
+static void
+oappend (s)
+     const char *s;
+{
+  strcpy (obufp, s);
+  obufp += strlen (s);
+}
+
+static void
+append_seg ()
+{
+  if (prefixes & PREFIX_CS)
+    {
+      used_prefixes |= PREFIX_CS;
+      oappend ("%cs:" + intel_syntax);
+    }
+  if (prefixes & PREFIX_DS)
+    {
+      used_prefixes |= PREFIX_DS;
+      oappend ("%ds:" + intel_syntax);
+    }
+  if (prefixes & PREFIX_SS)
+    {
+      used_prefixes |= PREFIX_SS;
+      oappend ("%ss:" + intel_syntax);
+    }
+  if (prefixes & PREFIX_ES)
+    {
+      used_prefixes |= PREFIX_ES;
+      oappend ("%es:" + intel_syntax);
+    }
+  if (prefixes & PREFIX_FS)
+    {
+      used_prefixes |= PREFIX_FS;
+      oappend ("%fs:" + intel_syntax);
+    }
+  if (prefixes & PREFIX_GS)
+    {
+      used_prefixes |= PREFIX_GS;
+      oappend ("%gs:" + intel_syntax);
+    }
+}
+
+static void
+OP_indirE (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  if (!intel_syntax)
+    oappend ("*");
+  OP_E (bytemode, sizeflag);
+}
+
+static void
+print_operand_value (char *buf, size_t bufsize, int hex, bfd_vma disp)
+{
+  if (mode_64bit)
+    {
+      if (hex)
+	{
+	  char tmp[30];
+	  int i;
+	  buf[0] = '0';
+	  buf[1] = 'x';
+          snprintf_vma (tmp, sizeof(tmp), disp);
+	  for (i = 0; tmp[i] == '0' && tmp[i + 1]; i++);
+          pstrcpy (buf + 2, bufsize - 2, tmp + i);
+	}
+      else
+	{
+	  bfd_signed_vma v = disp;
+	  char tmp[30];
+	  int i;
+	  if (v < 0)
+	    {
+	      *(buf++) = '-';
+	      v = -disp;
+	      /* Check for possible overflow on 0x8000000000000000.  */
+	      if (v < 0)
+		{
+                  pstrcpy (buf, bufsize, "9223372036854775808");
+		  return;
+		}
+	    }
+	  if (!v)
+	    {
+                pstrcpy (buf, bufsize, "0");
+	      return;
+	    }
+
+	  i = 0;
+	  tmp[29] = 0;
+	  while (v)
+	    {
+	      tmp[28 - i] = (v % 10) + '0';
+	      v /= 10;
+	      i++;
+	    }
+          pstrcpy (buf, bufsize, tmp + 29 - i);
+	}
+    }
+  else
+    {
+      if (hex)
+        snprintf (buf, bufsize, "0x%x", (unsigned int) disp);
+      else
+        snprintf (buf, bufsize, "%d", (int) disp);
+    }
+}
+
+static void
+OP_E (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_vma disp;
+  int add = 0;
+  int riprel = 0;
+  USED_REX (REX_EXTZ);
+  if (rex & REX_EXTZ)
+    add += 8;
+
+  /* Skip mod/rm byte.  */
+  MODRM_CHECK;
+  codep++;
+
+  if (mod == 3)
+    {
+      switch (bytemode)
+	{
+	case b_mode:
+	  USED_REX (0);
+	  if (rex)
+	    oappend (names8rex[rm + add]);
+	  else
+	    oappend (names8[rm + add]);
+	  break;
+	case w_mode:
+	  oappend (names16[rm + add]);
+	  break;
+	case d_mode:
+	  oappend (names32[rm + add]);
+	  break;
+	case q_mode:
+	  oappend (names64[rm + add]);
+	  break;
+	case m_mode:
+	  if (mode_64bit)
+	    oappend (names64[rm + add]);
+	  else
+	    oappend (names32[rm + add]);
+	  break;
+	case v_mode:
+	  USED_REX (REX_MODE64);
+	  if (rex & REX_MODE64)
+	    oappend (names64[rm + add]);
+	  else if (sizeflag & DFLAG)
+	    oappend (names32[rm + add]);
+	  else
+	    oappend (names16[rm + add]);
+	  used_prefixes |= (prefixes & PREFIX_DATA);
+	  break;
+	case 0:
+	  if (!(codep[-2] == 0xAE && codep[-1] == 0xF8 /* sfence */)
+	      && !(codep[-2] == 0xAE && codep[-1] == 0xF0 /* mfence */)
+	      && !(codep[-2] == 0xAE && codep[-1] == 0xe8 /* lfence */))
+	    BadOp ();	/* bad sfence,lea,lds,les,lfs,lgs,lss modrm */
+	  break;
+	default:
+	  oappend (INTERNAL_DISASSEMBLER_ERROR);
+	  break;
+	}
+      return;
+    }
+
+  disp = 0;
+  append_seg ();
+
+  if ((sizeflag & AFLAG) || mode_64bit) /* 32 bit address mode */
+    {
+      int havesib;
+      int havebase;
+      int base;
+      int index = 0;
+      int scale = 0;
+
+      havesib = 0;
+      havebase = 1;
+      base = rm;
+
+      if (base == 4)
+	{
+	  havesib = 1;
+	  FETCH_DATA (the_info, codep + 1);
+	  scale = (*codep >> 6) & 3;
+	  index = (*codep >> 3) & 7;
+	  base = *codep & 7;
+	  USED_REX (REX_EXTY);
+	  USED_REX (REX_EXTZ);
+	  if (rex & REX_EXTY)
+	    index += 8;
+	  if (rex & REX_EXTZ)
+	    base += 8;
+	  codep++;
+	}
+
+      switch (mod)
+	{
+	case 0:
+	  if ((base & 7) == 5)
+	    {
+	      havebase = 0;
+	      if (mode_64bit && !havesib && (sizeflag & AFLAG))
+		riprel = 1;
+	      disp = get32s ();
+	    }
+	  break;
+	case 1:
+	  FETCH_DATA (the_info, codep + 1);
+	  disp = *codep++;
+	  if ((disp & 0x80) != 0)
+	    disp -= 0x100;
+	  break;
+	case 2:
+	  disp = get32s ();
+	  break;
+	}
+
+      if (!intel_syntax)
+        if (mod != 0 || (base & 7) == 5)
+          {
+            print_operand_value (scratchbuf, sizeof(scratchbuf), !riprel, disp);
+            oappend (scratchbuf);
+	    if (riprel)
+	      {
+		set_op (disp, 1);
+		oappend ("(%rip)");
+	      }
+          }
+
+      if (havebase || (havesib && (index != 4 || scale != 0)))
+	{
+          if (intel_syntax)
+            {
+              switch (bytemode)
+                {
+                case b_mode:
+                  oappend ("BYTE PTR ");
+                  break;
+                case w_mode:
+                  oappend ("WORD PTR ");
+                  break;
+                case v_mode:
+                  oappend ("DWORD PTR ");
+                  break;
+                case d_mode:
+                  oappend ("QWORD PTR ");
+                  break;
+                case m_mode:
+		  if (mode_64bit)
+		    oappend ("DWORD PTR ");
+		  else
+		    oappend ("QWORD PTR ");
+		  break;
+                case x_mode:
+                  oappend ("XWORD PTR ");
+                  break;
+                default:
+                  break;
+                }
+             }
+	  *obufp++ = open_char;
+	  if (intel_syntax && riprel)
+	    oappend ("rip + ");
+          *obufp = '\0';
+	  USED_REX (REX_EXTZ);
+	  if (!havesib && (rex & REX_EXTZ))
+	    base += 8;
+	  if (havebase)
+	    oappend (mode_64bit && (sizeflag & AFLAG)
+		     ? names64[base] : names32[base]);
+	  if (havesib)
+	    {
+	      if (index != 4)
+		{
+                  if (intel_syntax)
+                    {
+                      if (havebase)
+                        {
+                          *obufp++ = separator_char;
+                          *obufp = '\0';
+                        }
+                      snprintf (scratchbuf, sizeof(scratchbuf), "%s",
+                                mode_64bit && (sizeflag & AFLAG)
+                                ? names64[index] : names32[index]);
+                    }
+                  else
+                      snprintf (scratchbuf, sizeof(scratchbuf), ",%s",
+                                mode_64bit && (sizeflag & AFLAG)
+                                ? names64[index] : names32[index]);
+		  oappend (scratchbuf);
+		}
+              if (!intel_syntax
+                  || (intel_syntax
+                      && bytemode != b_mode
+                      && bytemode != w_mode
+                      && bytemode != v_mode))
+                {
+                  *obufp++ = scale_char;
+                  *obufp = '\0';
+                  snprintf (scratchbuf, sizeof(scratchbuf), "%d", 1 << scale);
+	          oappend (scratchbuf);
+                }
+	    }
+          if (intel_syntax)
+            if (mod != 0 || (base & 7) == 5)
+              {
+		/* Don't print zero displacements.  */
+                if (disp != 0)
+                  {
+		    if ((bfd_signed_vma) disp > 0)
+		      {
+			*obufp++ = '+';
+			*obufp = '\0';
+		      }
+
+                    print_operand_value (scratchbuf, sizeof(scratchbuf), 0,
+                                         disp);
+                    oappend (scratchbuf);
+                  }
+              }
+
+	  *obufp++ = close_char;
+          *obufp = '\0';
+	}
+      else if (intel_syntax)
+        {
+          if (mod != 0 || (base & 7) == 5)
+            {
+	      if (prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+			      | PREFIX_ES | PREFIX_FS | PREFIX_GS))
+		;
+	      else
+		{
+		  oappend (names_seg[ds_reg - es_reg]);
+		  oappend (":");
+		}
+              print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp);
+              oappend (scratchbuf);
+            }
+        }
+    }
+  else
+    { /* 16 bit address mode */
+      switch (mod)
+	{
+	case 0:
+	  if ((rm & 7) == 6)
+	    {
+	      disp = get16 ();
+	      if ((disp & 0x8000) != 0)
+		disp -= 0x10000;
+	    }
+	  break;
+	case 1:
+	  FETCH_DATA (the_info, codep + 1);
+	  disp = *codep++;
+	  if ((disp & 0x80) != 0)
+	    disp -= 0x100;
+	  break;
+	case 2:
+	  disp = get16 ();
+	  if ((disp & 0x8000) != 0)
+	    disp -= 0x10000;
+	  break;
+	}
+
+      if (!intel_syntax)
+        if (mod != 0 || (rm & 7) == 6)
+          {
+            print_operand_value (scratchbuf, sizeof(scratchbuf), 0, disp);
+            oappend (scratchbuf);
+          }
+
+      if (mod != 0 || (rm & 7) != 6)
+	{
+	  *obufp++ = open_char;
+          *obufp = '\0';
+	  oappend (index16[rm + add]);
+          *obufp++ = close_char;
+          *obufp = '\0';
+	}
+    }
+}
+
+static void
+OP_G (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  int add = 0;
+  USED_REX (REX_EXTX);
+  if (rex & REX_EXTX)
+    add += 8;
+  switch (bytemode)
+    {
+    case b_mode:
+      USED_REX (0);
+      if (rex)
+	oappend (names8rex[reg + add]);
+      else
+	oappend (names8[reg + add]);
+      break;
+    case w_mode:
+      oappend (names16[reg + add]);
+      break;
+    case d_mode:
+      oappend (names32[reg + add]);
+      break;
+    case q_mode:
+      oappend (names64[reg + add]);
+      break;
+    case v_mode:
+      USED_REX (REX_MODE64);
+      if (rex & REX_MODE64)
+	oappend (names64[reg + add]);
+      else if (sizeflag & DFLAG)
+	oappend (names32[reg + add]);
+      else
+	oappend (names16[reg + add]);
+      used_prefixes |= (prefixes & PREFIX_DATA);
+      break;
+    default:
+      oappend (INTERNAL_DISASSEMBLER_ERROR);
+      break;
+    }
+}
+
+static bfd_vma
+get64 ()
+{
+  bfd_vma x;
+#ifdef BFD64
+  unsigned int a;
+  unsigned int b;
+
+  FETCH_DATA (the_info, codep + 8);
+  a = *codep++ & 0xff;
+  a |= (*codep++ & 0xff) << 8;
+  a |= (*codep++ & 0xff) << 16;
+  a |= (*codep++ & 0xff) << 24;
+  b = *codep++ & 0xff;
+  b |= (*codep++ & 0xff) << 8;
+  b |= (*codep++ & 0xff) << 16;
+  b |= (*codep++ & 0xff) << 24;
+  x = a + ((bfd_vma) b << 32);
+#else
+  abort ();
+  x = 0;
+#endif
+  return x;
+}
+
+static bfd_signed_vma
+get32 ()
+{
+  bfd_signed_vma x = 0;
+
+  FETCH_DATA (the_info, codep + 4);
+  x = *codep++ & (bfd_signed_vma) 0xff;
+  x |= (*codep++ & (bfd_signed_vma) 0xff) << 8;
+  x |= (*codep++ & (bfd_signed_vma) 0xff) << 16;
+  x |= (*codep++ & (bfd_signed_vma) 0xff) << 24;
+  return x;
+}
+
+static bfd_signed_vma
+get32s ()
+{
+  bfd_signed_vma x = 0;
+
+  FETCH_DATA (the_info, codep + 4);
+  x = *codep++ & (bfd_signed_vma) 0xff;
+  x |= (*codep++ & (bfd_signed_vma) 0xff) << 8;
+  x |= (*codep++ & (bfd_signed_vma) 0xff) << 16;
+  x |= (*codep++ & (bfd_signed_vma) 0xff) << 24;
+
+  x = (x ^ ((bfd_signed_vma) 1 << 31)) - ((bfd_signed_vma) 1 << 31);
+
+  return x;
+}
+
+static int
+get16 ()
+{
+  int x = 0;
+
+  FETCH_DATA (the_info, codep + 2);
+  x = *codep++ & 0xff;
+  x |= (*codep++ & 0xff) << 8;
+  return x;
+}
+
+static void
+set_op (op, riprel)
+     bfd_vma op;
+     int riprel;
+{
+  op_index[op_ad] = op_ad;
+  if (mode_64bit)
+    {
+      op_address[op_ad] = op;
+      op_riprel[op_ad] = riprel;
+    }
+  else
+    {
+      /* Mask to get a 32-bit address.  */
+      op_address[op_ad] = op & 0xffffffff;
+      op_riprel[op_ad] = riprel & 0xffffffff;
+    }
+}
+
+static void
+OP_REG (code, sizeflag)
+     int code;
+     int sizeflag;
+{
+  const char *s;
+  int add = 0;
+  USED_REX (REX_EXTZ);
+  if (rex & REX_EXTZ)
+    add = 8;
+
+  switch (code)
+    {
+    case indir_dx_reg:
+      if (intel_syntax)
+        s = "[dx]";
+      else
+        s = "(%dx)";
+      break;
+    case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+    case sp_reg: case bp_reg: case si_reg: case di_reg:
+      s = names16[code - ax_reg + add];
+      break;
+    case es_reg: case ss_reg: case cs_reg:
+    case ds_reg: case fs_reg: case gs_reg:
+      s = names_seg[code - es_reg + add];
+      break;
+    case al_reg: case ah_reg: case cl_reg: case ch_reg:
+    case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+      USED_REX (0);
+      if (rex)
+	s = names8rex[code - al_reg + add];
+      else
+	s = names8[code - al_reg];
+      break;
+    case rAX_reg: case rCX_reg: case rDX_reg: case rBX_reg:
+    case rSP_reg: case rBP_reg: case rSI_reg: case rDI_reg:
+      if (mode_64bit)
+	{
+	  s = names64[code - rAX_reg + add];
+	  break;
+	}
+      code += eAX_reg - rAX_reg;
+      /* Fall through.  */
+    case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+    case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+      USED_REX (REX_MODE64);
+      if (rex & REX_MODE64)
+	s = names64[code - eAX_reg + add];
+      else if (sizeflag & DFLAG)
+	s = names32[code - eAX_reg + add];
+      else
+	s = names16[code - eAX_reg + add];
+      used_prefixes |= (prefixes & PREFIX_DATA);
+      break;
+    default:
+      s = INTERNAL_DISASSEMBLER_ERROR;
+      break;
+    }
+  oappend (s);
+}
+
+static void
+OP_IMREG (code, sizeflag)
+     int code;
+     int sizeflag;
+{
+  const char *s;
+
+  switch (code)
+    {
+    case indir_dx_reg:
+      if (intel_syntax)
+        s = "[dx]";
+      else
+        s = "(%dx)";
+      break;
+    case ax_reg: case cx_reg: case dx_reg: case bx_reg:
+    case sp_reg: case bp_reg: case si_reg: case di_reg:
+      s = names16[code - ax_reg];
+      break;
+    case es_reg: case ss_reg: case cs_reg:
+    case ds_reg: case fs_reg: case gs_reg:
+      s = names_seg[code - es_reg];
+      break;
+    case al_reg: case ah_reg: case cl_reg: case ch_reg:
+    case dl_reg: case dh_reg: case bl_reg: case bh_reg:
+      USED_REX (0);
+      if (rex)
+	s = names8rex[code - al_reg];
+      else
+	s = names8[code - al_reg];
+      break;
+    case eAX_reg: case eCX_reg: case eDX_reg: case eBX_reg:
+    case eSP_reg: case eBP_reg: case eSI_reg: case eDI_reg:
+      USED_REX (REX_MODE64);
+      if (rex & REX_MODE64)
+	s = names64[code - eAX_reg];
+      else if (sizeflag & DFLAG)
+	s = names32[code - eAX_reg];
+      else
+	s = names16[code - eAX_reg];
+      used_prefixes |= (prefixes & PREFIX_DATA);
+      break;
+    default:
+      s = INTERNAL_DISASSEMBLER_ERROR;
+      break;
+    }
+  oappend (s);
+}
+
+static void
+OP_I (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_signed_vma op;
+  bfd_signed_vma mask = -1;
+
+  switch (bytemode)
+    {
+    case b_mode:
+      FETCH_DATA (the_info, codep + 1);
+      op = *codep++;
+      mask = 0xff;
+      break;
+    case q_mode:
+      if (mode_64bit)
+	{
+	  op = get32s ();
+	  break;
+	}
+      /* Fall through.  */
+    case v_mode:
+      USED_REX (REX_MODE64);
+      if (rex & REX_MODE64)
+	op = get32s ();
+      else if (sizeflag & DFLAG)
+	{
+	  op = get32 ();
+	  mask = 0xffffffff;
+	}
+      else
+	{
+	  op = get16 ();
+	  mask = 0xfffff;
+	}
+      used_prefixes |= (prefixes & PREFIX_DATA);
+      break;
+    case w_mode:
+      mask = 0xfffff;
+      op = get16 ();
+      break;
+    default:
+      oappend (INTERNAL_DISASSEMBLER_ERROR);
+      return;
+    }
+
+  op &= mask;
+  scratchbuf[0] = '$';
+  print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op);
+  oappend (scratchbuf + intel_syntax);
+  scratchbuf[0] = '\0';
+}
+
+static void
+OP_I64 (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_signed_vma op;
+  bfd_signed_vma mask = -1;
+
+  if (!mode_64bit)
+    {
+      OP_I (bytemode, sizeflag);
+      return;
+    }
+
+  switch (bytemode)
+    {
+    case b_mode:
+      FETCH_DATA (the_info, codep + 1);
+      op = *codep++;
+      mask = 0xff;
+      break;
+    case v_mode:
+      USED_REX (REX_MODE64);
+      if (rex & REX_MODE64)
+	op = get64 ();
+      else if (sizeflag & DFLAG)
+	{
+	  op = get32 ();
+	  mask = 0xffffffff;
+	}
+      else
+	{
+	  op = get16 ();
+	  mask = 0xfffff;
+	}
+      used_prefixes |= (prefixes & PREFIX_DATA);
+      break;
+    case w_mode:
+      mask = 0xfffff;
+      op = get16 ();
+      break;
+    default:
+      oappend (INTERNAL_DISASSEMBLER_ERROR);
+      return;
+    }
+
+  op &= mask;
+  scratchbuf[0] = '$';
+  print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op);
+  oappend (scratchbuf + intel_syntax);
+  scratchbuf[0] = '\0';
+}
+
+static void
+OP_sI (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_signed_vma op;
+  bfd_signed_vma mask = -1;
+
+  switch (bytemode)
+    {
+    case b_mode:
+      FETCH_DATA (the_info, codep + 1);
+      op = *codep++;
+      if ((op & 0x80) != 0)
+	op -= 0x100;
+      mask = 0xffffffff;
+      break;
+    case v_mode:
+      USED_REX (REX_MODE64);
+      if (rex & REX_MODE64)
+	op = get32s ();
+      else if (sizeflag & DFLAG)
+	{
+	  op = get32s ();
+	  mask = 0xffffffff;
+	}
+      else
+	{
+	  mask = 0xffffffff;
+	  op = get16 ();
+	  if ((op & 0x8000) != 0)
+	    op -= 0x10000;
+	}
+      used_prefixes |= (prefixes & PREFIX_DATA);
+      break;
+    case w_mode:
+      op = get16 ();
+      mask = 0xffffffff;
+      if ((op & 0x8000) != 0)
+	op -= 0x10000;
+      break;
+    default:
+      oappend (INTERNAL_DISASSEMBLER_ERROR);
+      return;
+    }
+
+  scratchbuf[0] = '$';
+  print_operand_value (scratchbuf + 1, sizeof(scratchbuf) - 1, 1, op);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_J (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_vma disp;
+  bfd_vma mask = -1;
+
+  switch (bytemode)
+    {
+    case b_mode:
+      FETCH_DATA (the_info, codep + 1);
+      disp = *codep++;
+      if ((disp & 0x80) != 0)
+	disp -= 0x100;
+      break;
+    case v_mode:
+      if (sizeflag & DFLAG)
+	disp = get32s ();
+      else
+	{
+	  disp = get16 ();
+	  /* For some reason, a data16 prefix on a jump instruction
+	     means that the pc is masked to 16 bits after the
+	     displacement is added!  */
+	  mask = 0xffff;
+	}
+      break;
+    default:
+      oappend (INTERNAL_DISASSEMBLER_ERROR);
+      return;
+    }
+  disp = (start_pc + codep - start_codep + disp) & mask;
+  set_op (disp, 0);
+  print_operand_value (scratchbuf, sizeof(scratchbuf), 1, disp);
+  oappend (scratchbuf);
+}
+
+static void
+OP_SEG (dummy, sizeflag)
+     int dummy;
+     int sizeflag;
+{
+  oappend (names_seg[reg]);
+}
+
+static void
+OP_DIR (dummy, sizeflag)
+     int dummy;
+     int sizeflag;
+{
+  int seg, offset;
+
+  if (sizeflag & DFLAG)
+    {
+      offset = get32 ();
+      seg = get16 ();
+    }
+  else
+    {
+      offset = get16 ();
+      seg = get16 ();
+    }
+  used_prefixes |= (prefixes & PREFIX_DATA);
+  if (intel_syntax)
+    snprintf (scratchbuf, sizeof(scratchbuf), "0x%x,0x%x", seg, offset);
+  else
+    snprintf (scratchbuf, sizeof(scratchbuf), "$0x%x,$0x%x", seg, offset);
+  oappend (scratchbuf);
+}
+
+static void
+OP_OFF (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_vma off;
+
+  append_seg ();
+
+  if ((sizeflag & AFLAG) || mode_64bit)
+    off = get32 ();
+  else
+    off = get16 ();
+
+  if (intel_syntax)
+    {
+      if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+		        | PREFIX_ES | PREFIX_FS | PREFIX_GS)))
+	{
+	  oappend (names_seg[ds_reg - es_reg]);
+	  oappend (":");
+	}
+    }
+  print_operand_value (scratchbuf, sizeof(scratchbuf), 1, off);
+  oappend (scratchbuf);
+}
+
+static void
+OP_OFF64 (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  bfd_vma off;
+
+  if (!mode_64bit)
+    {
+      OP_OFF (bytemode, sizeflag);
+      return;
+    }
+
+  append_seg ();
+
+  off = get64 ();
+
+  if (intel_syntax)
+    {
+      if (!(prefixes & (PREFIX_CS | PREFIX_SS | PREFIX_DS
+		        | PREFIX_ES | PREFIX_FS | PREFIX_GS)))
+	{
+	  oappend (names_seg[ds_reg - es_reg]);
+	  oappend (":");
+	}
+    }
+  print_operand_value (scratchbuf, sizeof(scratchbuf), 1, off);
+  oappend (scratchbuf);
+}
+
+static void
+ptr_reg (code, sizeflag)
+     int code;
+     int sizeflag;
+{
+  const char *s;
+  if (intel_syntax)
+    oappend ("[");
+  else
+    oappend ("(");
+
+  USED_REX (REX_MODE64);
+  if (rex & REX_MODE64)
+    {
+      if (!(sizeflag & AFLAG))
+        s = names32[code - eAX_reg];
+      else
+        s = names64[code - eAX_reg];
+    }
+  else if (sizeflag & AFLAG)
+    s = names32[code - eAX_reg];
+  else
+    s = names16[code - eAX_reg];
+  oappend (s);
+  if (intel_syntax)
+    oappend ("]");
+  else
+    oappend (")");
+}
+
+static void
+OP_ESreg (code, sizeflag)
+     int code;
+     int sizeflag;
+{
+  oappend ("%es:" + intel_syntax);
+  ptr_reg (code, sizeflag);
+}
+
+static void
+OP_DSreg (code, sizeflag)
+     int code;
+     int sizeflag;
+{
+  if ((prefixes
+       & (PREFIX_CS
+	  | PREFIX_DS
+	  | PREFIX_SS
+	  | PREFIX_ES
+	  | PREFIX_FS
+	  | PREFIX_GS)) == 0)
+    prefixes |= PREFIX_DS;
+  append_seg ();
+  ptr_reg (code, sizeflag);
+}
+
+static void
+OP_C (dummy, sizeflag)
+     int dummy;
+     int sizeflag;
+{
+  int add = 0;
+  USED_REX (REX_EXTX);
+  if (rex & REX_EXTX)
+    add = 8;
+  snprintf (scratchbuf, sizeof(scratchbuf), "%%cr%d", reg + add);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_D (dummy, sizeflag)
+     int dummy;
+     int sizeflag;
+{
+  int add = 0;
+  USED_REX (REX_EXTX);
+  if (rex & REX_EXTX)
+    add = 8;
+  if (intel_syntax)
+    snprintf (scratchbuf, sizeof(scratchbuf), "db%d", reg + add);
+  else
+    snprintf (scratchbuf, sizeof(scratchbuf), "%%db%d", reg + add);
+  oappend (scratchbuf);
+}
+
+static void
+OP_T (dummy, sizeflag)
+     int dummy;
+     int sizeflag;
+{
+  snprintf (scratchbuf, sizeof(scratchbuf), "%%tr%d", reg);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_Rd (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  if (mod == 3)
+    OP_E (bytemode, sizeflag);
+  else
+    BadOp ();
+}
+
+static void
+OP_MMX (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  int add = 0;
+  USED_REX (REX_EXTX);
+  if (rex & REX_EXTX)
+    add = 8;
+  used_prefixes |= (prefixes & PREFIX_DATA);
+  if (prefixes & PREFIX_DATA)
+    snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", reg + add);
+  else
+    snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", reg + add);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_XMM (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  int add = 0;
+  USED_REX (REX_EXTX);
+  if (rex & REX_EXTX)
+    add = 8;
+  snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", reg + add);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_EM (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  int add = 0;
+  if (mod != 3)
+    {
+      OP_E (bytemode, sizeflag);
+      return;
+    }
+  USED_REX (REX_EXTZ);
+  if (rex & REX_EXTZ)
+    add = 8;
+
+  /* Skip mod/rm byte.  */
+  MODRM_CHECK;
+  codep++;
+  used_prefixes |= (prefixes & PREFIX_DATA);
+  if (prefixes & PREFIX_DATA)
+    snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", rm + add);
+  else
+    snprintf (scratchbuf, sizeof(scratchbuf), "%%mm%d", rm + add);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_EX (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  int add = 0;
+  if (mod != 3)
+    {
+      OP_E (bytemode, sizeflag);
+      return;
+    }
+  USED_REX (REX_EXTZ);
+  if (rex & REX_EXTZ)
+    add = 8;
+
+  /* Skip mod/rm byte.  */
+  MODRM_CHECK;
+  codep++;
+  snprintf (scratchbuf, sizeof(scratchbuf), "%%xmm%d", rm + add);
+  oappend (scratchbuf + intel_syntax);
+}
+
+static void
+OP_MS (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  if (mod == 3)
+    OP_EM (bytemode, sizeflag);
+  else
+    BadOp ();
+}
+
+static void
+OP_XS (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  if (mod == 3)
+    OP_EX (bytemode, sizeflag);
+  else
+    BadOp ();
+}
+
+static const char *Suffix3DNow[] = {
+/* 00 */	NULL,		NULL,		NULL,		NULL,
+/* 04 */	NULL,		NULL,		NULL,		NULL,
+/* 08 */	NULL,		NULL,		NULL,		NULL,
+/* 0C */	"pi2fw",	"pi2fd",	NULL,		NULL,
+/* 10 */	NULL,		NULL,		NULL,		NULL,
+/* 14 */	NULL,		NULL,		NULL,		NULL,
+/* 18 */	NULL,		NULL,		NULL,		NULL,
+/* 1C */	"pf2iw",	"pf2id",	NULL,		NULL,
+/* 20 */	NULL,		NULL,		NULL,		NULL,
+/* 24 */	NULL,		NULL,		NULL,		NULL,
+/* 28 */	NULL,		NULL,		NULL,		NULL,
+/* 2C */	NULL,		NULL,		NULL,		NULL,
+/* 30 */	NULL,		NULL,		NULL,		NULL,
+/* 34 */	NULL,		NULL,		NULL,		NULL,
+/* 38 */	NULL,		NULL,		NULL,		NULL,
+/* 3C */	NULL,		NULL,		NULL,		NULL,
+/* 40 */	NULL,		NULL,		NULL,		NULL,
+/* 44 */	NULL,		NULL,		NULL,		NULL,
+/* 48 */	NULL,		NULL,		NULL,		NULL,
+/* 4C */	NULL,		NULL,		NULL,		NULL,
+/* 50 */	NULL,		NULL,		NULL,		NULL,
+/* 54 */	NULL,		NULL,		NULL,		NULL,
+/* 58 */	NULL,		NULL,		NULL,		NULL,
+/* 5C */	NULL,		NULL,		NULL,		NULL,
+/* 60 */	NULL,		NULL,		NULL,		NULL,
+/* 64 */	NULL,		NULL,		NULL,		NULL,
+/* 68 */	NULL,		NULL,		NULL,		NULL,
+/* 6C */	NULL,		NULL,		NULL,		NULL,
+/* 70 */	NULL,		NULL,		NULL,		NULL,
+/* 74 */	NULL,		NULL,		NULL,		NULL,
+/* 78 */	NULL,		NULL,		NULL,		NULL,
+/* 7C */	NULL,		NULL,		NULL,		NULL,
+/* 80 */	NULL,		NULL,		NULL,		NULL,
+/* 84 */	NULL,		NULL,		NULL,		NULL,
+/* 88 */	NULL,		NULL,		"pfnacc",	NULL,
+/* 8C */	NULL,		NULL,		"pfpnacc",	NULL,
+/* 90 */	"pfcmpge",	NULL,		NULL,		NULL,
+/* 94 */	"pfmin",	NULL,		"pfrcp",	"pfrsqrt",
+/* 98 */	NULL,		NULL,		"pfsub",	NULL,
+/* 9C */	NULL,		NULL,		"pfadd",	NULL,
+/* A0 */	"pfcmpgt",	NULL,		NULL,		NULL,
+/* A4 */	"pfmax",	NULL,		"pfrcpit1",	"pfrsqit1",
+/* A8 */	NULL,		NULL,		"pfsubr",	NULL,
+/* AC */	NULL,		NULL,		"pfacc",	NULL,
+/* B0 */	"pfcmpeq",	NULL,		NULL,		NULL,
+/* B4 */	"pfmul",	NULL,		"pfrcpit2",	"pfmulhrw",
+/* B8 */	NULL,		NULL,		NULL,		"pswapd",
+/* BC */	NULL,		NULL,		NULL,		"pavgusb",
+/* C0 */	NULL,		NULL,		NULL,		NULL,
+/* C4 */	NULL,		NULL,		NULL,		NULL,
+/* C8 */	NULL,		NULL,		NULL,		NULL,
+/* CC */	NULL,		NULL,		NULL,		NULL,
+/* D0 */	NULL,		NULL,		NULL,		NULL,
+/* D4 */	NULL,		NULL,		NULL,		NULL,
+/* D8 */	NULL,		NULL,		NULL,		NULL,
+/* DC */	NULL,		NULL,		NULL,		NULL,
+/* E0 */	NULL,		NULL,		NULL,		NULL,
+/* E4 */	NULL,		NULL,		NULL,		NULL,
+/* E8 */	NULL,		NULL,		NULL,		NULL,
+/* EC */	NULL,		NULL,		NULL,		NULL,
+/* F0 */	NULL,		NULL,		NULL,		NULL,
+/* F4 */	NULL,		NULL,		NULL,		NULL,
+/* F8 */	NULL,		NULL,		NULL,		NULL,
+/* FC */	NULL,		NULL,		NULL,		NULL,
+};
+
+static void
+OP_3DNowSuffix (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  const char *mnemonic;
+
+  FETCH_DATA (the_info, codep + 1);
+  /* AMD 3DNow! instructions are specified by an opcode suffix in the
+     place where an 8-bit immediate would normally go.  ie. the last
+     byte of the instruction.  */
+  obufp = obuf + strlen (obuf);
+  mnemonic = Suffix3DNow[*codep++ & 0xff];
+  if (mnemonic)
+    oappend (mnemonic);
+  else
+    {
+      /* Since a variable sized modrm/sib chunk is between the start
+	 of the opcode (0x0f0f) and the opcode suffix, we need to do
+	 all the modrm processing first, and don't know until now that
+	 we have a bad opcode.  This necessitates some cleaning up.  */
+      op1out[0] = '\0';
+      op2out[0] = '\0';
+      BadOp ();
+    }
+}
+
+static const char *simd_cmp_op[] = {
+  "eq",
+  "lt",
+  "le",
+  "unord",
+  "neq",
+  "nlt",
+  "nle",
+  "ord"
+};
+
+static void
+OP_SIMD_Suffix (bytemode, sizeflag)
+     int bytemode;
+     int sizeflag;
+{
+  unsigned int cmp_type;
+
+  FETCH_DATA (the_info, codep + 1);
+  obufp = obuf + strlen (obuf);
+  cmp_type = *codep++ & 0xff;
+  if (cmp_type < 8)
+    {
+      char suffix1 = 'p', suffix2 = 's';
+      used_prefixes |= (prefixes & PREFIX_REPZ);
+      if (prefixes & PREFIX_REPZ)
+	suffix1 = 's';
+      else
+	{
+	  used_prefixes |= (prefixes & PREFIX_DATA);
+	  if (prefixes & PREFIX_DATA)
+	    suffix2 = 'd';
+	  else
+	    {
+	      used_prefixes |= (prefixes & PREFIX_REPNZ);
+	      if (prefixes & PREFIX_REPNZ)
+		suffix1 = 's', suffix2 = 'd';
+	    }
+	}
+      snprintf (scratchbuf, sizeof(scratchbuf), "cmp%s%c%c",
+                simd_cmp_op[cmp_type], suffix1, suffix2);
+      used_prefixes |= (prefixes & PREFIX_REPZ);
+      oappend (scratchbuf);
+    }
+  else
+    {
+      /* We have a bad extension byte.  Clean up.  */
+      op1out[0] = '\0';
+      op2out[0] = '\0';
+      BadOp ();
+    }
+}
+
+static void
+SIMD_Fixup (extrachar, sizeflag)
+     int extrachar;
+     int sizeflag;
+{
+  /* Change movlps/movhps to movhlps/movlhps for 2 register operand
+     forms of these instructions.  */
+  if (mod == 3)
+    {
+      char *p = obuf + strlen (obuf);
+      *(p + 1) = '\0';
+      *p       = *(p - 1);
+      *(p - 1) = *(p - 2);
+      *(p - 2) = *(p - 3);
+      *(p - 3) = extrachar;
+    }
+}
+
+static void
+BadOp (void)
+{
+  /* Throw away prefixes and 1st. opcode byte.  */
+  codep = insn_codep + 1;
+  oappend ("(bad)");
+}
diff --git a/i386-vl.ld b/i386-vl.ld
new file mode 100644
index 0000000..428fe83
--- /dev/null
+++ b/i386-vl.ld
@@ -0,0 +1,140 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0xa8000000 + SIZEOF_HEADERS;
+  .interp     : { *(.interp) 	}
+  .hash          : { *(.hash)		}
+  .dynsym        : { *(.dynsym)		}
+  .dynstr        : { *(.dynstr)		}
+  .gnu.version   : { *(.gnu.version)	}
+  .gnu.version_d   : { *(.gnu.version_d)	}
+  .gnu.version_r   : { *(.gnu.version_r)	}
+  .rel.text      :
+    { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+  .rela.text     :
+    { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+  .rel.data      :
+    { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+  .rela.data     :
+    { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+  .rel.rodata    :
+    { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+  .rela.rodata   :
+    { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+  .rel.got       : { *(.rel.got)		}
+  .rela.got      : { *(.rela.got)		}
+  .rel.ctors     : { *(.rel.ctors)	}
+  .rela.ctors    : { *(.rela.ctors)	}
+  .rel.dtors     : { *(.rel.dtors)	}
+  .rela.dtors    : { *(.rela.dtors)	}
+  .rel.init      : { *(.rel.init)	}
+  .rela.init     : { *(.rela.init)	}
+  .rel.fini      : { *(.rel.fini)	}
+  .rela.fini     : { *(.rela.fini)	}
+  .rel.bss       : { *(.rel.bss)		}
+  .rela.bss      : { *(.rela.bss)		}
+  .rel.plt       : { *(.rel.plt)		}
+  .rela.plt      : { *(.rela.plt)		}
+  .init          : { *(.init)	} =0x47ff041f
+  .text      :
+  {
+    *(.text)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.gnu.linkonce.t*)
+  } =0x47ff041f
+  _etext = .;
+  PROVIDE (etext = .);
+  .fini      : { *(.fini)    } =0x47ff041f
+  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
+  .rodata1   : { *(.rodata1) }
+  .reginfo : { *(.reginfo) }
+  __preinit_array_start = .;
+  .preinit_array : { *(.preinit_array) }
+  __preinit_array_end = .;
+  __init_array_start = .;
+  .init_array : { *(.init_array) }
+  __init_array_end = .;
+  __fini_array_start = .;
+  .fini_array : { *(.fini_array) }
+  __fini_array_end = .;
+
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x100000) + (. & (0x100000 - 1));
+  .data    :
+  {
+    *(.data)
+    *(.gnu.linkonce.d*)
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  .ctors         :
+  {
+    *(.ctors)
+  }
+  .dtors         :
+  {
+    *(.dtors)
+  }
+  .plt      : { *(.plt)	}
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  _edata  =  .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+  }
+  _end = . ;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* These must appear regardless of  .  */
+}
diff --git a/i386.ld b/i386.ld
new file mode 100644
index 0000000..9f4cb5b
--- /dev/null
+++ b/i386.ld
@@ -0,0 +1,142 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp     : { *(.interp) 	}
+  .hash          : { *(.hash)		}
+  .dynsym        : { *(.dynsym)		}
+  .dynstr        : { *(.dynstr)		}
+  .gnu.version   : { *(.gnu.version)	}
+  .gnu.version_d   : { *(.gnu.version_d)	}
+  .gnu.version_r   : { *(.gnu.version_r)	}
+  .rel.text      :
+    { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+  .rela.text     :
+    { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+  .rel.data      :
+    { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+  .rela.data     :
+    { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+  .rel.rodata    :
+    { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+  .rela.rodata   :
+    { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+  .rel.got       : { *(.rel.got)		}
+  .rela.got      : { *(.rela.got)		}
+  .rel.ctors     : { *(.rel.ctors)	}
+  .rela.ctors    : { *(.rela.ctors)	}
+  .rel.dtors     : { *(.rel.dtors)	}
+  .rela.dtors    : { *(.rela.dtors)	}
+  .rel.init      : { *(.rel.init)	}
+  .rela.init     : { *(.rela.init)	}
+  .rel.fini      : { *(.rel.fini)	}
+  .rela.fini     : { *(.rela.fini)	}
+  .rel.bss       : { *(.rel.bss)		}
+  .rela.bss      : { *(.rela.bss)		}
+  .rel.plt       : { *(.rel.plt)		}
+  .rela.plt      : { *(.rela.plt)		}
+  .init          : { *(.init)	} =0x47ff041f
+  .text      :
+  {
+    *(.text)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.gnu.linkonce.t*)
+  } =0x47ff041f
+  _etext = .;
+  PROVIDE (etext = .);
+  .fini      : { *(.fini)    } =0x47ff041f
+  . = ALIGN(32 / 8);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { *(.preinit_array) }
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { *(.init_array) }
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { *(.fini_array) }
+  PROVIDE (__fini_array_end = .);
+  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
+  .rodata1   : { *(.rodata1) }
+  .reginfo : { *(.reginfo) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x100000) + (. & (0x100000 - 1));
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .data    :
+  {
+    *(.data)
+    *(.gnu.linkonce.d*)
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  .ctors         :
+  {
+    *(.ctors)
+  }
+  .dtors         :
+  {
+    *(.dtors)
+  }
+  .plt      : { *(.plt)	}
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  _edata  =  .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+  }
+  _end = . ;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* These must appear regardless of  .  */
+}
diff --git a/ia64.ld b/ia64.ld
new file mode 100644
index 0000000..8d2ede2
--- /dev/null
+++ b/ia64.ld
@@ -0,0 +1,211 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf64-ia64-little", "elf64-ia64-little",
+	      "elf64-ia64-little")
+OUTPUT_ARCH(ia64)
+ENTRY(_start)
+SEARCH_DIR("/usr/ia64-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
+/* Do we need any of these for elf?
+   __DYNAMIC = 0;    */
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  PROVIDE (__executable_start = 0x60000000); . = 0x60000000 + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .hash           : { *(.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rel.init       : { *(.rel.init) }
+  .rela.init      : { *(.rela.init) }
+  .rel.text       : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+  .rela.text      : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+  .rel.fini       : { *(.rel.fini) }
+  .rela.fini      : { *(.rela.fini) }
+  .rel.rodata     : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+  .rela.rodata    : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+  .rel.data       : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+  .rela.data      : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+  .rel.tdata	  : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+  .rela.tdata	  : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+  .rel.tbss	  : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+  .rela.tbss	  : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+  .rel.ctors      : { *(.rel.ctors) }
+  .rela.ctors     : { *(.rela.ctors) }
+  .rel.dtors      : { *(.rel.dtors) }
+  .rela.dtors     : { *(.rela.dtors) }
+  .rel.got        : { *(.rel.got) }
+  .rela.got       : { *(.rela.got) }
+  .rel.sdata      : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+  .rela.sdata     : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+  .rel.sbss       : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+  .rela.sbss      : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+  .rel.sdata2     : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+  .rela.sdata2    : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+  .rel.sbss2      : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+  .rela.sbss2     : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+  .rel.bss        : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+  .rela.bss       : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .rela.IA_64.pltoff   : { *(.rela.IA_64.pltoff) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0x00300000010070000002000001000400
+  .plt            : { *(.plt) }
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0x00300000010070000002000001000400
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0x00300000010070000002000001000400
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .sdata2         : { *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) }
+  .sbss2          : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+  .opd            : { *(.opd) }
+  .IA_64.unwind_info   : { *(.IA_64.unwind_info* .gnu.linkonce.ia64unwi.*) }
+  .IA_64.unwind   : { *(.IA_64.unwind* .gnu.linkonce.ia64unw.*) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x10000) + (. & (0x10000 - 1));
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(64 / 8);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { *(.preinit_array) }
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { *(.init_array) }
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { *(.fini_array) }
+  PROVIDE (__fini_array_end = .);
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .eh_frame       : { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : { *(.gcc_except_table) }
+  .dynamic        : { *(.dynamic) }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin*.o(.ctors))
+    /* We don't want to include the .ctor section from
+       from the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin*.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  /* Ensure __gp is outside the range of any normal data.  We need to
+     do this to avoid the linker optimizing the code in op.o and getting
+     it out of sync with the relocs that we read when processing that
+     file.  A better solution might be to ensure that the dynamically
+     generated code and static qemu code share a single gp-value.  */
+  __gp = . + 0x200000;
+  .got            : { *(.got.plt) *(.got) }
+  .IA_64.pltoff   : { *(.IA_64.pltoff) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata          :
+  {
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+  }
+  _edata = .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss           :
+  {
+    PROVIDE (__sbss_start = .);
+    PROVIDE (___sbss_start = .);
+    *(.dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+    PROVIDE (__sbss_end = .);
+    PROVIDE (___sbss_end = .);
+  }
+  .bss            :
+  {
+   . += 0x400000;	/* ensure .bss stuff is out of reach of gp */
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.  */
+   . = ALIGN(64 / 8);
+  }
+  . = ALIGN(64 / 8);
+  _end = .;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/images/android_icon.ico b/images/android_icon.ico
new file mode 100644
index 0000000..bd25179
--- /dev/null
+++ b/images/android_icon.ico
Binary files differ
diff --git a/images/android_icon.rc b/images/android_icon.rc
new file mode 100644
index 0000000..df468ac
--- /dev/null
+++ b/images/android_icon.rc
@@ -0,0 +1,3 @@
+1 ICON "../images/android_icon.ico"

+

+

diff --git a/images/android_icon_16.png b/images/android_icon_16.png
new file mode 100644
index 0000000..0b0744b
--- /dev/null
+++ b/images/android_icon_16.png
Binary files differ
diff --git a/images/android_icon_256.png b/images/android_icon_256.png
new file mode 100644
index 0000000..2d1dc05
--- /dev/null
+++ b/images/android_icon_256.png
Binary files differ
diff --git a/images/android_icon_32.png b/images/android_icon_32.png
new file mode 100644
index 0000000..72aa861
--- /dev/null
+++ b/images/android_icon_32.png
Binary files differ
diff --git a/keymaps.c b/keymaps.c
new file mode 100644
index 0000000..15c40fa
--- /dev/null
+++ b/keymaps.c
@@ -0,0 +1,207 @@
+/*
+ * QEMU keysym to keycode conversion using rdesktop keymaps
+ *
+ * Copyright (c) 2004 Johannes Schindelin
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static int get_keysym(const char *name)
+{
+    name2keysym_t *p;
+    for(p = name2keysym; p->name != NULL; p++) {
+        if (!strcmp(p->name, name))
+            return p->keysym;
+    }
+    return 0;
+}
+
+struct key_range {
+    int start;
+    int end;
+    struct key_range *next;
+};
+
+#define MAX_NORMAL_KEYCODE 512
+#define MAX_EXTRA_COUNT 256
+typedef struct {
+    uint16_t keysym2keycode[MAX_NORMAL_KEYCODE];
+    struct {
+	int keysym;
+	uint16_t keycode;
+    } keysym2keycode_extra[MAX_EXTRA_COUNT];
+    int extra_count;
+    struct key_range *keypad_range;
+    struct key_range *numlock_range;
+} kbd_layout_t;
+
+static void add_to_key_range(struct key_range **krp, int code) {
+    struct key_range *kr;
+    for (kr = *krp; kr; kr = kr->next) {
+	if (code >= kr->start && code <= kr->end)
+	    break;
+	if (code == kr->start - 1) {
+	    kr->start--;
+	    break;
+	}
+	if (code == kr->end + 1) {
+	    kr->end++;
+	    break;
+	}
+    }
+    if (kr == NULL) {
+	kr = qemu_mallocz(sizeof(*kr));
+	if (kr) {
+	    kr->start = kr->end = code;
+	    kr->next = *krp;
+	    *krp = kr;
+	}
+    }
+}
+
+static kbd_layout_t *parse_keyboard_layout(const char *language,
+					   kbd_layout_t * k)
+{
+    FILE *f;
+    char file_name[1024];
+    char line[1024];
+    int len;
+
+    snprintf(file_name, sizeof(file_name),
+             "%s/keymaps/%s", bios_dir, language);
+
+    if (!k)
+	k = qemu_mallocz(sizeof(kbd_layout_t));
+    if (!k)
+        return 0;
+    if (!(f = fopen(file_name, "r"))) {
+	fprintf(stderr,
+		"Could not read keymap file: '%s'\n", file_name);
+	return 0;
+    }
+    for(;;) {
+	if (fgets(line, 1024, f) == NULL)
+            break;
+        len = strlen(line);
+        if (len > 0 && line[len - 1] == '\n')
+            line[len - 1] = '\0';
+        if (line[0] == '#')
+	    continue;
+	if (!strncmp(line, "map ", 4))
+	    continue;
+	if (!strncmp(line, "include ", 8)) {
+	    parse_keyboard_layout(line + 8, k);
+        } else {
+	    char *end_of_keysym = line;
+	    while (*end_of_keysym != 0 && *end_of_keysym != ' ')
+		end_of_keysym++;
+	    if (*end_of_keysym) {
+		int keysym;
+		*end_of_keysym = 0;
+		keysym = get_keysym(line);
+		if (keysym == 0) {
+                    //		    fprintf(stderr, "Warning: unknown keysym %s\n", line);
+		} else {
+		    const char *rest = end_of_keysym + 1;
+		    char *rest2;
+		    int keycode = strtol(rest, &rest2, 0);
+
+		    if (rest && strstr(rest, "numlock")) {
+			add_to_key_range(&k->keypad_range, keycode);
+			add_to_key_range(&k->numlock_range, keysym);
+			//fprintf(stderr, "keypad keysym %04x keycode %d\n", keysym, keycode);
+		    }
+
+		    /* if(keycode&0x80)
+		       keycode=(keycode<<8)^0x80e0; */
+		    if (keysym < MAX_NORMAL_KEYCODE) {
+			//fprintf(stderr,"Setting keysym %s (%d) to %d\n",line,keysym,keycode);
+			k->keysym2keycode[keysym] = keycode;
+		    } else {
+			if (k->extra_count >= MAX_EXTRA_COUNT) {
+			    fprintf(stderr,
+				    "Warning: Could not assign keysym %s (0x%x) because of memory constraints.\n",
+				    line, keysym);
+			} else {
+#if 0
+			    fprintf(stderr, "Setting %d: %d,%d\n",
+				    k->extra_count, keysym, keycode);
+#endif
+			    k->keysym2keycode_extra[k->extra_count].
+				keysym = keysym;
+			    k->keysym2keycode_extra[k->extra_count].
+				keycode = keycode;
+			    k->extra_count++;
+			}
+		    }
+		}
+	    }
+	}
+    }
+    fclose(f);
+    return k;
+}
+
+static void *init_keyboard_layout(const char *language)
+{
+    return parse_keyboard_layout(language, 0);
+}
+
+static int keysym2scancode(void *kbd_layout, int keysym)
+{
+    kbd_layout_t *k = kbd_layout;
+    if (keysym < MAX_NORMAL_KEYCODE) {
+	if (k->keysym2keycode[keysym] == 0)
+	    fprintf(stderr, "Warning: no scancode found for keysym %d\n",
+		    keysym);
+	return k->keysym2keycode[keysym];
+    } else {
+	int i;
+#ifdef XK_ISO_Left_Tab
+	if (keysym == XK_ISO_Left_Tab)
+	    keysym = XK_Tab;
+#endif
+	for (i = 0; i < k->extra_count; i++)
+	    if (k->keysym2keycode_extra[i].keysym == keysym)
+		return k->keysym2keycode_extra[i].keycode;
+    }
+    return 0;
+}
+
+static inline int keycode_is_keypad(void *kbd_layout, int keycode)
+{
+    kbd_layout_t *k = kbd_layout;
+    struct key_range *kr;
+
+    for (kr = k->keypad_range; kr; kr = kr->next)
+        if (keycode >= kr->start && keycode <= kr->end)
+            return 1;
+    return 0;
+}
+
+static inline int keysym_is_numlock(void *kbd_layout, int keysym)
+{
+    kbd_layout_t *k = kbd_layout;
+    struct key_range *kr;
+
+    for (kr = k->numlock_range; kr; kr = kr->next)
+        if (keysym >= kr->start && keysym <= kr->end)
+            return 1;
+    return 0;
+}
diff --git a/kqemu.c b/kqemu.c
new file mode 100644
index 0000000..4783aa2
--- /dev/null
+++ b/kqemu.c
@@ -0,0 +1,1025 @@
+/*
+ *  KQEMU support
+ *
+ *  Copyright (c) 2005-2008 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winioctl.h>
+#else
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#endif
+#ifdef HOST_SOLARIS
+#include <sys/ioccom.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "qemu-common.h"
+
+#ifdef USE_KQEMU
+
+#define DEBUG
+//#define PROFILE
+
+#include <unistd.h>
+#include <fcntl.h>
+#include "kqemu.h"
+
+#ifdef _WIN32
+#define KQEMU_DEVICE "\\\\.\\kqemu"
+#else
+#define KQEMU_DEVICE "/dev/kqemu"
+#endif
+
+static void qpi_init(void);
+
+#ifdef _WIN32
+#define KQEMU_INVALID_FD INVALID_HANDLE_VALUE
+HANDLE kqemu_fd = KQEMU_INVALID_FD;
+#define kqemu_closefd(x) CloseHandle(x)
+#else
+#define KQEMU_INVALID_FD -1
+int kqemu_fd = KQEMU_INVALID_FD;
+#define kqemu_closefd(x) close(x)
+#endif
+
+/* 0 = not allowed
+   1 = user kqemu
+   2 = kernel kqemu
+*/
+int kqemu_allowed = 1;
+uint64_t *pages_to_flush;
+unsigned int nb_pages_to_flush;
+uint64_t *ram_pages_to_update;
+unsigned int nb_ram_pages_to_update;
+uint64_t *modified_ram_pages;
+unsigned int nb_modified_ram_pages;
+uint8_t *modified_ram_pages_table;
+int qpi_io_memory;
+uint32_t kqemu_comm_base; /* physical address of the QPI communication page */
+
+#define cpuid(index, eax, ebx, ecx, edx) \
+  asm volatile ("cpuid" \
+                : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) \
+                : "0" (index))
+
+#ifdef __x86_64__
+static int is_cpuid_supported(void)
+{
+    return 1;
+}
+#else
+static int is_cpuid_supported(void)
+{
+    int v0, v1;
+    asm volatile ("pushf\n"
+                  "popl %0\n"
+                  "movl %0, %1\n"
+                  "xorl $0x00200000, %0\n"
+                  "pushl %0\n"
+                  "popf\n"
+                  "pushf\n"
+                  "popl %0\n"
+                  : "=a" (v0), "=d" (v1)
+                  :
+                  : "cc");
+    return (v0 != v1);
+}
+#endif
+
+static void kqemu_update_cpuid(CPUState *env)
+{
+    int critical_features_mask, features, ext_features, ext_features_mask;
+    uint32_t eax, ebx, ecx, edx;
+
+    /* the following features are kept identical on the host and
+       target cpus because they are important for user code. Strictly
+       speaking, only SSE really matters because the OS must support
+       it if the user code uses it. */
+    critical_features_mask =
+        CPUID_CMOV | CPUID_CX8 |
+        CPUID_FXSR | CPUID_MMX | CPUID_SSE |
+        CPUID_SSE2 | CPUID_SEP;
+    ext_features_mask = CPUID_EXT_SSE3 | CPUID_EXT_MONITOR;
+    if (!is_cpuid_supported()) {
+        features = 0;
+        ext_features = 0;
+    } else {
+        cpuid(1, eax, ebx, ecx, edx);
+        features = edx;
+        ext_features = ecx;
+    }
+#ifdef __x86_64__
+    /* NOTE: on x86_64 CPUs, SYSENTER is not supported in
+       compatibility mode, so in order to have the best performances
+       it is better not to use it */
+    features &= ~CPUID_SEP;
+#endif
+    env->cpuid_features = (env->cpuid_features & ~critical_features_mask) |
+        (features & critical_features_mask);
+    env->cpuid_ext_features = (env->cpuid_ext_features & ~ext_features_mask) |
+        (ext_features & ext_features_mask);
+    /* XXX: we could update more of the target CPUID state so that the
+       non accelerated code sees exactly the same CPU features as the
+       accelerated code */
+}
+
+int kqemu_init(CPUState *env)
+{
+    struct kqemu_init kinit;
+    int ret, version;
+#ifdef _WIN32
+    DWORD temp;
+#endif
+
+    if (!kqemu_allowed)
+        return -1;
+
+#ifdef _WIN32
+    kqemu_fd = CreateFile(KQEMU_DEVICE, GENERIC_WRITE | GENERIC_READ,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE,
+                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+                          NULL);
+    if (kqemu_fd == KQEMU_INVALID_FD) {
+        fprintf(stderr, "Could not open '%s' - QEMU acceleration layer not activated: %lu\n",
+                KQEMU_DEVICE, GetLastError());
+        return -1;
+    }
+#else
+    kqemu_fd = open(KQEMU_DEVICE, O_RDWR);
+    if (kqemu_fd == KQEMU_INVALID_FD) {
+        fprintf(stderr, "Could not open '%s' - QEMU acceleration layer not activated: %s\n",
+                KQEMU_DEVICE, strerror(errno));
+        return -1;
+    }
+#endif
+    version = 0;
+#ifdef _WIN32
+    DeviceIoControl(kqemu_fd, KQEMU_GET_VERSION, NULL, 0,
+                    &version, sizeof(version), &temp, NULL);
+#else
+    ioctl(kqemu_fd, KQEMU_GET_VERSION, &version);
+#endif
+    if (version != KQEMU_VERSION) {
+        fprintf(stderr, "Version mismatch between kqemu module and qemu (%08x %08x) - disabling kqemu use\n",
+                version, KQEMU_VERSION);
+        goto fail;
+    }
+
+    pages_to_flush = qemu_vmalloc(KQEMU_MAX_PAGES_TO_FLUSH *
+                                  sizeof(uint64_t));
+    if (!pages_to_flush)
+        goto fail;
+
+    ram_pages_to_update = qemu_vmalloc(KQEMU_MAX_RAM_PAGES_TO_UPDATE *
+                                       sizeof(uint64_t));
+    if (!ram_pages_to_update)
+        goto fail;
+
+    modified_ram_pages = qemu_vmalloc(KQEMU_MAX_MODIFIED_RAM_PAGES *
+                                      sizeof(uint64_t));
+    if (!modified_ram_pages)
+        goto fail;
+    modified_ram_pages_table = qemu_mallocz(phys_ram_size >> TARGET_PAGE_BITS);
+    if (!modified_ram_pages_table)
+        goto fail;
+
+    memset(&kinit, 0, sizeof(kinit)); /* set the paddings to zero */
+    kinit.ram_base = phys_ram_base;
+    kinit.ram_size = phys_ram_size;
+    kinit.ram_dirty = phys_ram_dirty;
+    kinit.pages_to_flush = pages_to_flush;
+    kinit.ram_pages_to_update = ram_pages_to_update;
+    kinit.modified_ram_pages = modified_ram_pages;
+#ifdef _WIN32
+    ret = DeviceIoControl(kqemu_fd, KQEMU_INIT, &kinit, sizeof(kinit),
+                          NULL, 0, &temp, NULL) == TRUE ? 0 : -1;
+#else
+    ret = ioctl(kqemu_fd, KQEMU_INIT, &kinit);
+#endif
+    if (ret < 0) {
+        fprintf(stderr, "Error %d while initializing QEMU acceleration layer - disabling it for now\n", ret);
+    fail:
+        kqemu_closefd(kqemu_fd);
+        kqemu_fd = KQEMU_INVALID_FD;
+        return -1;
+    }
+    kqemu_update_cpuid(env);
+    env->kqemu_enabled = kqemu_allowed;
+    nb_pages_to_flush = 0;
+    nb_ram_pages_to_update = 0;
+
+    qpi_init();
+    return 0;
+}
+
+void kqemu_flush_page(CPUState *env, target_ulong addr)
+{
+#if defined(DEBUG)
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "kqemu_flush_page: addr=" TARGET_FMT_lx "\n", addr);
+    }
+#endif
+    if (nb_pages_to_flush >= KQEMU_MAX_PAGES_TO_FLUSH)
+        nb_pages_to_flush = KQEMU_FLUSH_ALL;
+    else
+        pages_to_flush[nb_pages_to_flush++] = addr;
+}
+
+void kqemu_flush(CPUState *env, int global)
+{
+#ifdef DEBUG
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "kqemu_flush:\n");
+    }
+#endif
+    nb_pages_to_flush = KQEMU_FLUSH_ALL;
+}
+
+void kqemu_set_notdirty(CPUState *env, ram_addr_t ram_addr)
+{
+#ifdef DEBUG
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "kqemu_set_notdirty: addr=%08lx\n", 
+                (unsigned long)ram_addr);
+    }
+#endif
+    /* we only track transitions to dirty state */
+    if (phys_ram_dirty[ram_addr >> TARGET_PAGE_BITS] != 0xff)
+        return;
+    if (nb_ram_pages_to_update >= KQEMU_MAX_RAM_PAGES_TO_UPDATE)
+        nb_ram_pages_to_update = KQEMU_RAM_PAGES_UPDATE_ALL;
+    else
+        ram_pages_to_update[nb_ram_pages_to_update++] = ram_addr;
+}
+
+static void kqemu_reset_modified_ram_pages(void)
+{
+    int i;
+    unsigned long page_index;
+
+    for(i = 0; i < nb_modified_ram_pages; i++) {
+        page_index = modified_ram_pages[i] >> TARGET_PAGE_BITS;
+        modified_ram_pages_table[page_index] = 0;
+    }
+    nb_modified_ram_pages = 0;
+}
+
+void kqemu_modify_page(CPUState *env, ram_addr_t ram_addr)
+{
+    unsigned long page_index;
+    int ret;
+#ifdef _WIN32
+    DWORD temp;
+#endif
+
+    page_index = ram_addr >> TARGET_PAGE_BITS;
+    if (!modified_ram_pages_table[page_index]) {
+#if 0
+        printf("%d: modify_page=%08lx\n", nb_modified_ram_pages, ram_addr);
+#endif
+        modified_ram_pages_table[page_index] = 1;
+        modified_ram_pages[nb_modified_ram_pages++] = ram_addr;
+        if (nb_modified_ram_pages >= KQEMU_MAX_MODIFIED_RAM_PAGES) {
+            /* flush */
+#ifdef _WIN32
+            ret = DeviceIoControl(kqemu_fd, KQEMU_MODIFY_RAM_PAGES,
+                                  &nb_modified_ram_pages,
+                                  sizeof(nb_modified_ram_pages),
+                                  NULL, 0, &temp, NULL);
+#else
+            ret = ioctl(kqemu_fd, KQEMU_MODIFY_RAM_PAGES,
+                        &nb_modified_ram_pages);
+#endif
+            kqemu_reset_modified_ram_pages();
+        }
+    }
+}
+
+void kqemu_set_phys_mem(uint64_t start_addr, ram_addr_t size, 
+                        ram_addr_t phys_offset)
+{
+    struct kqemu_phys_mem kphys_mem1, *kphys_mem = &kphys_mem1;
+    uint64_t end;
+    int ret, io_index;
+
+    end = (start_addr + size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
+    start_addr &= TARGET_PAGE_MASK;
+    kphys_mem->phys_addr = start_addr;
+    kphys_mem->size = end - start_addr;
+    kphys_mem->ram_addr = phys_offset & TARGET_PAGE_MASK;
+    io_index = phys_offset & ~TARGET_PAGE_MASK;
+    switch(io_index) {
+    case IO_MEM_RAM:
+        kphys_mem->io_index = KQEMU_IO_MEM_RAM;
+        break;
+    case IO_MEM_ROM:
+        kphys_mem->io_index = KQEMU_IO_MEM_ROM;
+        break;
+    default:
+        if (qpi_io_memory == io_index) {
+            kphys_mem->io_index = KQEMU_IO_MEM_COMM;
+        } else {
+            kphys_mem->io_index = KQEMU_IO_MEM_UNASSIGNED;
+        }
+        break;
+    }
+#ifdef _WIN32
+    {
+        DWORD temp;
+        ret = DeviceIoControl(kqemu_fd, KQEMU_SET_PHYS_MEM, 
+                              kphys_mem, sizeof(*kphys_mem),
+                              NULL, 0, &temp, NULL) == TRUE ? 0 : -1;
+    }
+#else
+    ret = ioctl(kqemu_fd, KQEMU_SET_PHYS_MEM, kphys_mem);
+#endif
+    if (ret < 0) {
+        fprintf(stderr, "kqemu: KQEMU_SET_PHYS_PAGE error=%d: start_addr=0x%016" PRIx64 " size=0x%08lx phys_offset=0x%08lx\n",
+                ret, start_addr, 
+                (unsigned long)size, (unsigned long)phys_offset);
+    }
+}
+
+struct fpstate {
+    uint16_t fpuc;
+    uint16_t dummy1;
+    uint16_t fpus;
+    uint16_t dummy2;
+    uint16_t fptag;
+    uint16_t dummy3;
+
+    uint32_t fpip;
+    uint32_t fpcs;
+    uint32_t fpoo;
+    uint32_t fpos;
+    uint8_t fpregs1[8 * 10];
+};
+
+struct fpxstate {
+    uint16_t fpuc;
+    uint16_t fpus;
+    uint16_t fptag;
+    uint16_t fop;
+    uint32_t fpuip;
+    uint16_t cs_sel;
+    uint16_t dummy0;
+    uint32_t fpudp;
+    uint16_t ds_sel;
+    uint16_t dummy1;
+    uint32_t mxcsr;
+    uint32_t mxcsr_mask;
+    uint8_t fpregs1[8 * 16];
+    uint8_t xmm_regs[16 * 16];
+    uint8_t dummy2[96];
+};
+
+static struct fpxstate fpx1 __attribute__((aligned(16)));
+
+static void restore_native_fp_frstor(CPUState *env)
+{
+    int fptag, i, j;
+    struct fpstate fp1, *fp = &fp1;
+
+    fp->fpuc = env->fpuc;
+    fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+    fptag = 0;
+    for (i=7; i>=0; i--) {
+	fptag <<= 2;
+	if (env->fptags[i]) {
+            fptag |= 3;
+        } else {
+            /* the FPU automatically computes it */
+        }
+    }
+    fp->fptag = fptag;
+    j = env->fpstt;
+    for(i = 0;i < 8; i++) {
+        memcpy(&fp->fpregs1[i * 10], &env->fpregs[j].d, 10);
+        j = (j + 1) & 7;
+    }
+    asm volatile ("frstor %0" : "=m" (*fp));
+}
+
+static void save_native_fp_fsave(CPUState *env)
+{
+    int fptag, i, j;
+    uint16_t fpuc;
+    struct fpstate fp1, *fp = &fp1;
+
+    asm volatile ("fsave %0" : : "m" (*fp));
+    env->fpuc = fp->fpuc;
+    env->fpstt = (fp->fpus >> 11) & 7;
+    env->fpus = fp->fpus & ~0x3800;
+    fptag = fp->fptag;
+    for(i = 0;i < 8; i++) {
+        env->fptags[i] = ((fptag & 3) == 3);
+        fptag >>= 2;
+    }
+    j = env->fpstt;
+    for(i = 0;i < 8; i++) {
+        memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 10], 10);
+        j = (j + 1) & 7;
+    }
+    /* we must restore the default rounding state */
+    fpuc = 0x037f | (env->fpuc & (3 << 10));
+    asm volatile("fldcw %0" : : "m" (fpuc));
+}
+
+static void restore_native_fp_fxrstor(CPUState *env)
+{
+    struct fpxstate *fp = &fpx1;
+    int i, j, fptag;
+
+    fp->fpuc = env->fpuc;
+    fp->fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+    fptag = 0;
+    for(i = 0; i < 8; i++)
+        fptag |= (env->fptags[i] << i);
+    fp->fptag = fptag ^ 0xff;
+
+    j = env->fpstt;
+    for(i = 0;i < 8; i++) {
+        memcpy(&fp->fpregs1[i * 16], &env->fpregs[j].d, 10);
+        j = (j + 1) & 7;
+    }
+    if (env->cpuid_features & CPUID_SSE) {
+        fp->mxcsr = env->mxcsr;
+        /* XXX: check if DAZ is not available */
+        fp->mxcsr_mask = 0xffff;
+        memcpy(fp->xmm_regs, env->xmm_regs, CPU_NB_REGS * 16);
+    }
+    asm volatile ("fxrstor %0" : "=m" (*fp));
+}
+
+static void save_native_fp_fxsave(CPUState *env)
+{
+    struct fpxstate *fp = &fpx1;
+    int fptag, i, j;
+    uint16_t fpuc;
+
+    asm volatile ("fxsave %0" : : "m" (*fp));
+    env->fpuc = fp->fpuc;
+    env->fpstt = (fp->fpus >> 11) & 7;
+    env->fpus = fp->fpus & ~0x3800;
+    fptag = fp->fptag ^ 0xff;
+    for(i = 0;i < 8; i++) {
+        env->fptags[i] = (fptag >> i) & 1;
+    }
+    j = env->fpstt;
+    for(i = 0;i < 8; i++) {
+        memcpy(&env->fpregs[j].d, &fp->fpregs1[i * 16], 10);
+        j = (j + 1) & 7;
+    }
+    if (env->cpuid_features & CPUID_SSE) {
+        env->mxcsr = fp->mxcsr;
+        memcpy(env->xmm_regs, fp->xmm_regs, CPU_NB_REGS * 16);
+    }
+
+    /* we must restore the default rounding state */
+    asm volatile ("fninit");
+    fpuc = 0x037f | (env->fpuc & (3 << 10));
+    asm volatile("fldcw %0" : : "m" (fpuc));
+}
+
+static int do_syscall(CPUState *env,
+                      struct kqemu_cpu_state *kenv)
+{
+    int selector;
+
+    selector = (env->star >> 32) & 0xffff;
+#ifdef TARGET_X86_64
+    if (env->hflags & HF_LMA_MASK) {
+        int code64;
+
+        env->regs[R_ECX] = kenv->next_eip;
+        env->regs[11] = env->eflags;
+
+        code64 = env->hflags & HF_CS64_MASK;
+
+        cpu_x86_set_cpl(env, 0);
+        cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+                               0, 0xffffffff,
+                               DESC_G_MASK | DESC_P_MASK |
+                               DESC_S_MASK |
+                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | DESC_L_MASK);
+        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+                               0, 0xffffffff,
+                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+                               DESC_S_MASK |
+                               DESC_W_MASK | DESC_A_MASK);
+        env->eflags &= ~env->fmask;
+        if (code64)
+            env->eip = env->lstar;
+        else
+            env->eip = env->cstar;
+    } else
+#endif
+    {
+        env->regs[R_ECX] = (uint32_t)kenv->next_eip;
+
+        cpu_x86_set_cpl(env, 0);
+        cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc,
+                           0, 0xffffffff,
+                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+                               DESC_S_MASK |
+                               DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK);
+        cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc,
+                               0, 0xffffffff,
+                               DESC_G_MASK | DESC_B_MASK | DESC_P_MASK |
+                               DESC_S_MASK |
+                               DESC_W_MASK | DESC_A_MASK);
+        env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK);
+        env->eip = (uint32_t)env->star;
+    }
+    return 2;
+}
+
+#ifdef CONFIG_PROFILER
+
+#define PC_REC_SIZE 1
+#define PC_REC_HASH_BITS 16
+#define PC_REC_HASH_SIZE (1 << PC_REC_HASH_BITS)
+
+typedef struct PCRecord {
+    unsigned long pc;
+    int64_t count;
+    struct PCRecord *next;
+} PCRecord;
+
+static PCRecord *pc_rec_hash[PC_REC_HASH_SIZE];
+static int nb_pc_records;
+
+static void kqemu_record_pc(unsigned long pc)
+{
+    unsigned long h;
+    PCRecord **pr, *r;
+
+    h = pc / PC_REC_SIZE;
+    h = h ^ (h >> PC_REC_HASH_BITS);
+    h &= (PC_REC_HASH_SIZE - 1);
+    pr = &pc_rec_hash[h];
+    for(;;) {
+        r = *pr;
+        if (r == NULL)
+            break;
+        if (r->pc == pc) {
+            r->count++;
+            return;
+        }
+        pr = &r->next;
+    }
+    r = malloc(sizeof(PCRecord));
+    r->count = 1;
+    r->pc = pc;
+    r->next = NULL;
+    *pr = r;
+    nb_pc_records++;
+}
+
+static int pc_rec_cmp(const void *p1, const void *p2)
+{
+    PCRecord *r1 = *(PCRecord **)p1;
+    PCRecord *r2 = *(PCRecord **)p2;
+    if (r1->count < r2->count)
+        return 1;
+    else if (r1->count == r2->count)
+        return 0;
+    else
+        return -1;
+}
+
+static void kqemu_record_flush(void)
+{
+    PCRecord *r, *r_next;
+    int h;
+
+    for(h = 0; h < PC_REC_HASH_SIZE; h++) {
+        for(r = pc_rec_hash[h]; r != NULL; r = r_next) {
+            r_next = r->next;
+            free(r);
+        }
+        pc_rec_hash[h] = NULL;
+    }
+    nb_pc_records = 0;
+}
+
+void kqemu_record_dump(void)
+{
+    PCRecord **pr, *r;
+    int i, h;
+    FILE *f;
+    int64_t total, sum;
+
+    pr = malloc(sizeof(PCRecord *) * nb_pc_records);
+    i = 0;
+    total = 0;
+    for(h = 0; h < PC_REC_HASH_SIZE; h++) {
+        for(r = pc_rec_hash[h]; r != NULL; r = r->next) {
+            pr[i++] = r;
+            total += r->count;
+        }
+    }
+    qsort(pr, nb_pc_records, sizeof(PCRecord *), pc_rec_cmp);
+
+    f = fopen("/tmp/kqemu.stats", "w");
+    if (!f) {
+        perror("/tmp/kqemu.stats");
+        exit(1);
+    }
+    fprintf(f, "total: %" PRId64 "\n", total);
+    sum = 0;
+    for(i = 0; i < nb_pc_records; i++) {
+        r = pr[i];
+        sum += r->count;
+        fprintf(f, "%08lx: %" PRId64 " %0.2f%% %0.2f%%\n",
+                r->pc,
+                r->count,
+                (double)r->count / (double)total * 100.0,
+                (double)sum / (double)total * 100.0);
+    }
+    fclose(f);
+    free(pr);
+
+    kqemu_record_flush();
+}
+#endif
+
+static inline void kqemu_load_seg(struct kqemu_segment_cache *ksc,
+                                  const SegmentCache *sc)
+{
+    ksc->selector = sc->selector;
+    ksc->flags = sc->flags;
+    ksc->limit = sc->limit;
+    ksc->base = sc->base;
+}
+
+static inline void kqemu_save_seg(SegmentCache *sc,
+                                  const struct kqemu_segment_cache *ksc)
+{
+    sc->selector = ksc->selector;
+    sc->flags = ksc->flags;
+    sc->limit = ksc->limit;
+    sc->base = ksc->base;
+}
+
+int kqemu_cpu_exec(CPUState *env)
+{
+    struct kqemu_cpu_state kcpu_state, *kenv = &kcpu_state;
+    int ret, cpl, i;
+#ifdef CONFIG_PROFILER
+    int64_t ti;
+#endif
+#ifdef _WIN32
+    DWORD temp;
+#endif
+
+#ifdef CONFIG_PROFILER
+    ti = profile_getclock();
+#endif
+#ifdef DEBUG
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "kqemu: cpu_exec: enter\n");
+        cpu_dump_state(env, logfile, fprintf, 0);
+    }
+#endif
+    for(i = 0; i < CPU_NB_REGS; i++)
+        kenv->regs[i] = env->regs[i];
+    kenv->eip = env->eip;
+    kenv->eflags = env->eflags;
+    for(i = 0; i < 6; i++)
+        kqemu_load_seg(&kenv->segs[i], &env->segs[i]);
+    kqemu_load_seg(&kenv->ldt, &env->ldt);
+    kqemu_load_seg(&kenv->tr, &env->tr);
+    kqemu_load_seg(&kenv->gdt, &env->gdt);
+    kqemu_load_seg(&kenv->idt, &env->idt);
+    kenv->cr0 = env->cr[0];
+    kenv->cr2 = env->cr[2];
+    kenv->cr3 = env->cr[3];
+    kenv->cr4 = env->cr[4];
+    kenv->a20_mask = env->a20_mask;
+    kenv->efer = env->efer;
+    kenv->tsc_offset = 0;
+    kenv->star = env->star;
+    kenv->sysenter_cs = env->sysenter_cs;
+    kenv->sysenter_esp = env->sysenter_esp;
+    kenv->sysenter_eip = env->sysenter_eip;
+#ifdef TARGET_X86_64
+    kenv->lstar = env->lstar;
+    kenv->cstar = env->cstar;
+    kenv->fmask = env->fmask;
+    kenv->kernelgsbase = env->kernelgsbase;
+#endif
+    if (env->dr[7] & 0xff) {
+        kenv->dr7 = env->dr[7];
+        kenv->dr0 = env->dr[0];
+        kenv->dr1 = env->dr[1];
+        kenv->dr2 = env->dr[2];
+        kenv->dr3 = env->dr[3];
+    } else {
+        kenv->dr7 = 0;
+    }
+    kenv->dr6 = env->dr[6];
+    cpl = (env->hflags & HF_CPL_MASK);
+    kenv->cpl = cpl;
+    kenv->nb_pages_to_flush = nb_pages_to_flush;
+    kenv->user_only = (env->kqemu_enabled == 1);
+    kenv->nb_ram_pages_to_update = nb_ram_pages_to_update;
+    nb_ram_pages_to_update = 0;
+    kenv->nb_modified_ram_pages = nb_modified_ram_pages;
+
+    kqemu_reset_modified_ram_pages();
+
+    if (env->cpuid_features & CPUID_FXSR)
+        restore_native_fp_fxrstor(env);
+    else
+        restore_native_fp_frstor(env);
+
+#ifdef _WIN32
+    if (DeviceIoControl(kqemu_fd, KQEMU_EXEC,
+                        kenv, sizeof(struct kqemu_cpu_state),
+                        kenv, sizeof(struct kqemu_cpu_state),
+                        &temp, NULL)) {
+        ret = kenv->retval;
+    } else {
+        ret = -1;
+    }
+#else
+    ioctl(kqemu_fd, KQEMU_EXEC, kenv);
+    ret = kenv->retval;
+#endif
+    if (env->cpuid_features & CPUID_FXSR)
+        save_native_fp_fxsave(env);
+    else
+        save_native_fp_fsave(env);
+
+    for(i = 0; i < CPU_NB_REGS; i++)
+        env->regs[i] = kenv->regs[i];
+    env->eip = kenv->eip;
+    env->eflags = kenv->eflags;
+    for(i = 0; i < 6; i++)
+        kqemu_save_seg(&env->segs[i], &kenv->segs[i]);
+    cpu_x86_set_cpl(env, kenv->cpl);
+    kqemu_save_seg(&env->ldt, &kenv->ldt);
+    env->cr[0] = kenv->cr0;
+    env->cr[4] = kenv->cr4;
+    env->cr[3] = kenv->cr3;
+    env->cr[2] = kenv->cr2;
+    env->dr[6] = kenv->dr6;
+#ifdef TARGET_X86_64
+    env->kernelgsbase = kenv->kernelgsbase;
+#endif
+
+    /* flush pages as indicated by kqemu */
+    if (kenv->nb_pages_to_flush >= KQEMU_FLUSH_ALL) {
+        tlb_flush(env, 1);
+    } else {
+        for(i = 0; i < kenv->nb_pages_to_flush; i++) {
+            tlb_flush_page(env, pages_to_flush[i]);
+        }
+    }
+    nb_pages_to_flush = 0;
+
+#ifdef CONFIG_PROFILER
+    kqemu_time += profile_getclock() - ti;
+    kqemu_exec_count++;
+#endif
+
+    if (kenv->nb_ram_pages_to_update > 0) {
+        cpu_tlb_update_dirty(env);
+    }
+
+    if (kenv->nb_modified_ram_pages > 0) {
+        for(i = 0; i < kenv->nb_modified_ram_pages; i++) {
+            unsigned long addr;
+            addr = modified_ram_pages[i];
+            tb_invalidate_phys_page_range(addr, addr + TARGET_PAGE_SIZE, 0);
+        }
+    }
+
+    /* restore the hidden flags */
+    {
+        unsigned int new_hflags;
+#ifdef TARGET_X86_64
+        if ((env->hflags & HF_LMA_MASK) &&
+            (env->segs[R_CS].flags & DESC_L_MASK)) {
+            /* long mode */
+            new_hflags = HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK;
+        } else
+#endif
+        {
+            /* legacy / compatibility case */
+            new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
+                >> (DESC_B_SHIFT - HF_CS32_SHIFT);
+            new_hflags |= (env->segs[R_SS].flags & DESC_B_MASK)
+                >> (DESC_B_SHIFT - HF_SS32_SHIFT);
+            if (!(env->cr[0] & CR0_PE_MASK) ||
+                   (env->eflags & VM_MASK) ||
+                   !(env->hflags & HF_CS32_MASK)) {
+                /* XXX: try to avoid this test. The problem comes from the
+                   fact that is real mode or vm86 mode we only modify the
+                   'base' and 'selector' fields of the segment cache to go
+                   faster. A solution may be to force addseg to one in
+                   translate-i386.c. */
+                new_hflags |= HF_ADDSEG_MASK;
+            } else {
+                new_hflags |= ((env->segs[R_DS].base |
+                                env->segs[R_ES].base |
+                                env->segs[R_SS].base) != 0) <<
+                    HF_ADDSEG_SHIFT;
+            }
+        }
+        env->hflags = (env->hflags &
+           ~(HF_CS32_MASK | HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK)) |
+            new_hflags;
+    }
+    /* update FPU flags */
+    env->hflags = (env->hflags & ~(HF_MP_MASK | HF_EM_MASK | HF_TS_MASK)) |
+        ((env->cr[0] << (HF_MP_SHIFT - 1)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK));
+    if (env->cr[4] & CR4_OSFXSR_MASK)
+        env->hflags |= HF_OSFXSR_MASK;
+    else
+        env->hflags &= ~HF_OSFXSR_MASK;
+
+#ifdef DEBUG
+    if (loglevel & CPU_LOG_INT) {
+        fprintf(logfile, "kqemu: kqemu_cpu_exec: ret=0x%x\n", ret);
+    }
+#endif
+    if (ret == KQEMU_RET_SYSCALL) {
+        /* syscall instruction */
+        return do_syscall(env, kenv);
+    } else
+    if ((ret & 0xff00) == KQEMU_RET_INT) {
+        env->exception_index = ret & 0xff;
+        env->error_code = 0;
+        env->exception_is_int = 1;
+        env->exception_next_eip = kenv->next_eip;
+#ifdef CONFIG_PROFILER
+        kqemu_ret_int_count++;
+#endif
+#ifdef DEBUG
+        if (loglevel & CPU_LOG_INT) {
+            fprintf(logfile, "kqemu: interrupt v=%02x:\n",
+                    env->exception_index);
+            cpu_dump_state(env, logfile, fprintf, 0);
+        }
+#endif
+        return 1;
+    } else if ((ret & 0xff00) == KQEMU_RET_EXCEPTION) {
+        env->exception_index = ret & 0xff;
+        env->error_code = kenv->error_code;
+        env->exception_is_int = 0;
+        env->exception_next_eip = 0;
+#ifdef CONFIG_PROFILER
+        kqemu_ret_excp_count++;
+#endif
+#ifdef DEBUG
+        if (loglevel & CPU_LOG_INT) {
+            fprintf(logfile, "kqemu: exception v=%02x e=%04x:\n",
+                    env->exception_index, env->error_code);
+            cpu_dump_state(env, logfile, fprintf, 0);
+        }
+#endif
+        return 1;
+    } else if (ret == KQEMU_RET_INTR) {
+#ifdef CONFIG_PROFILER
+        kqemu_ret_intr_count++;
+#endif
+#ifdef DEBUG
+        if (loglevel & CPU_LOG_INT) {
+            cpu_dump_state(env, logfile, fprintf, 0);
+        }
+#endif
+        return 0;
+    } else if (ret == KQEMU_RET_SOFTMMU) {
+#ifdef CONFIG_PROFILER
+        {
+            unsigned long pc = env->eip + env->segs[R_CS].base;
+            kqemu_record_pc(pc);
+        }
+#endif
+#ifdef DEBUG
+        if (loglevel & CPU_LOG_INT) {
+            cpu_dump_state(env, logfile, fprintf, 0);
+        }
+#endif
+        return 2;
+    } else {
+        cpu_dump_state(env, stderr, fprintf, 0);
+        fprintf(stderr, "Unsupported return value: 0x%x\n", ret);
+        exit(1);
+    }
+    return 0;
+}
+
+void kqemu_cpu_interrupt(CPUState *env)
+{
+#if defined(_WIN32)
+    /* cancelling the I/O request causes KQEMU to finish executing the
+       current block and successfully returning. */
+    CancelIo(kqemu_fd);
+#endif
+}
+
+/* 
+   QEMU paravirtualization interface. The current interface only
+   allows to modify the IF and IOPL flags when running in
+   kqemu.
+
+   At this point it is not very satisfactory. I leave it for reference
+   as it adds little complexity.
+*/
+
+#define QPI_COMM_PAGE_PHYS_ADDR 0xff000000
+
+static uint32_t qpi_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    return 0;
+}
+
+static uint32_t qpi_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+    return 0;
+}
+
+static void qpi_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static void qpi_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+}
+
+static uint32_t qpi_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+    CPUState *env;
+
+    env = cpu_single_env;
+    if (!env)
+        return 0;
+    return env->eflags & (IF_MASK | IOPL_MASK);
+}
+
+/* Note: after writing to this address, the guest code must make sure
+   it is exiting the current TB. pushf/popf can be used for that
+   purpose. */
+static void qpi_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    CPUState *env;
+
+    env = cpu_single_env;
+    if (!env)
+        return;
+    env->eflags = (env->eflags & ~(IF_MASK | IOPL_MASK)) | 
+        (val & (IF_MASK | IOPL_MASK));
+}
+
+static CPUReadMemoryFunc *qpi_mem_read[3] = {
+    qpi_mem_readb,
+    qpi_mem_readw,
+    qpi_mem_readl,
+};
+
+static CPUWriteMemoryFunc *qpi_mem_write[3] = {
+    qpi_mem_writeb,
+    qpi_mem_writew,
+    qpi_mem_writel,
+};
+
+static void qpi_init(void)
+{
+    kqemu_comm_base = 0xff000000 | 1;
+    qpi_io_memory = cpu_register_io_memory(0, 
+                                           qpi_mem_read, 
+                                           qpi_mem_write, NULL);
+    cpu_register_physical_memory(kqemu_comm_base & ~0xfff, 
+                                 0x1000, qpi_io_memory);
+}
+#endif
diff --git a/kqemu.h b/kqemu.h
new file mode 100644
index 0000000..ed25c75
--- /dev/null
+++ b/kqemu.h
@@ -0,0 +1,154 @@
+/*
+ * KQEMU header
+ * 
+ * Copyright (c) 2004-2008 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef KQEMU_H
+#define KQEMU_H
+
+#if defined(__i386__)
+#define KQEMU_PAD32(x) x
+#else
+#define KQEMU_PAD32(x)
+#endif
+
+#define KQEMU_VERSION 0x010400
+
+struct kqemu_segment_cache {
+    uint16_t selector;
+    uint16_t padding1;
+    uint32_t flags;
+    uint64_t base;
+    uint32_t limit;
+    uint32_t padding2;
+};
+
+struct kqemu_cpu_state {
+    uint64_t regs[16];
+    uint64_t eip;
+    uint64_t eflags;
+
+    struct kqemu_segment_cache segs[6]; /* selector values */
+    struct kqemu_segment_cache ldt;
+    struct kqemu_segment_cache tr;
+    struct kqemu_segment_cache gdt; /* only base and limit are used */
+    struct kqemu_segment_cache idt; /* only base and limit are used */
+
+    uint64_t cr0;
+    uint64_t cr2;
+    uint64_t cr3;
+    uint64_t cr4;
+    uint64_t a20_mask;
+
+    /* sysenter registers */
+    uint64_t sysenter_cs;
+    uint64_t sysenter_esp;
+    uint64_t sysenter_eip;
+    uint64_t efer;
+    uint64_t star;
+    
+    uint64_t lstar;
+    uint64_t cstar;
+    uint64_t fmask;
+    uint64_t kernelgsbase;
+
+    uint64_t tsc_offset;
+
+    uint64_t dr0;
+    uint64_t dr1;
+    uint64_t dr2;
+    uint64_t dr3;
+    uint64_t dr6;
+    uint64_t dr7;
+
+    uint8_t cpl;
+    uint8_t user_only;
+    uint16_t padding1;
+
+    uint32_t error_code; /* error_code when exiting with an exception */
+    uint64_t next_eip; /* next eip value when exiting with an interrupt */
+    uint32_t nb_pages_to_flush; /* number of pages to flush,
+                                       KQEMU_FLUSH_ALL means full flush */
+#define KQEMU_MAX_PAGES_TO_FLUSH 512
+#define KQEMU_FLUSH_ALL (KQEMU_MAX_PAGES_TO_FLUSH + 1)
+
+    int32_t retval;
+
+    /* number of ram_dirty entries to update */
+    uint32_t nb_ram_pages_to_update; 
+#define KQEMU_MAX_RAM_PAGES_TO_UPDATE 512
+#define KQEMU_RAM_PAGES_UPDATE_ALL (KQEMU_MAX_RAM_PAGES_TO_UPDATE + 1)
+
+#define KQEMU_MAX_MODIFIED_RAM_PAGES 512
+    uint32_t nb_modified_ram_pages;
+};
+
+struct kqemu_init {
+    uint8_t *ram_base; /* must be page aligned */
+    KQEMU_PAD32(uint32_t padding1;)
+    uint64_t ram_size; /* must be multiple of 4 KB */
+    uint8_t *ram_dirty; /* must be page aligned */
+    KQEMU_PAD32(uint32_t padding2;)
+    uint64_t *pages_to_flush; /* must be page aligned */
+    KQEMU_PAD32(uint32_t padding4;)
+    uint64_t *ram_pages_to_update; /* must be page aligned */
+    KQEMU_PAD32(uint32_t padding5;)
+    uint64_t *modified_ram_pages; /* must be page aligned */
+    KQEMU_PAD32(uint32_t padding6;)
+};
+
+#define KQEMU_IO_MEM_RAM        0
+#define KQEMU_IO_MEM_ROM        1
+#define KQEMU_IO_MEM_COMM       2 /* kqemu communication page */
+#define KQEMU_IO_MEM_UNASSIGNED 3 /* any device: return to application */
+
+struct kqemu_phys_mem {
+    uint64_t phys_addr; /* physical address range: phys_addr,
+                           phys_addr + size */
+    uint64_t size;        
+    uint64_t ram_addr;  /* corresponding ram address */
+    uint32_t io_index;  /* memory type: see KQEMU_IO_MEM_xxx */
+    uint32_t padding1;
+};
+
+#define KQEMU_RET_ABORT    (-1)
+#define KQEMU_RET_EXCEPTION 0x0000 /* 8 low order bit are the exception */
+#define KQEMU_RET_INT       0x0100 /* 8 low order bit are the interrupt */
+#define KQEMU_RET_SOFTMMU   0x0200 /* emulation needed (I/O or
+                                      unsupported INSN) */
+#define KQEMU_RET_INTR      0x0201 /* interrupted by a signal */
+#define KQEMU_RET_SYSCALL   0x0300 /* syscall insn */
+
+#ifdef _WIN32
+#define KQEMU_EXEC             CTL_CODE(FILE_DEVICE_UNKNOWN, 1, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define KQEMU_INIT             CTL_CODE(FILE_DEVICE_UNKNOWN, 2, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define KQEMU_GET_VERSION      CTL_CODE(FILE_DEVICE_UNKNOWN, 3, METHOD_BUFFERED, FILE_READ_ACCESS)
+#define KQEMU_MODIFY_RAM_PAGES CTL_CODE(FILE_DEVICE_UNKNOWN, 4, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#define KQEMU_SET_PHYS_MEM     CTL_CODE(FILE_DEVICE_UNKNOWN, 5, METHOD_BUFFERED, FILE_WRITE_ACCESS)
+#else
+#define KQEMU_EXEC             _IOWR('q', 1, struct kqemu_cpu_state)
+#define KQEMU_INIT             _IOW('q', 2, struct kqemu_init)
+#define KQEMU_GET_VERSION      _IOR('q', 3, int)
+#define KQEMU_MODIFY_RAM_PAGES _IOW('q', 4, int)
+#define KQEMU_SET_PHYS_MEM     _IOW('q', 5, struct kqemu_phys_mem)
+#endif
+
+#endif /* KQEMU_H */
diff --git a/linux_keycodes.h b/linux_keycodes.h
new file mode 100644
index 0000000..3875028
--- /dev/null
+++ b/linux_keycodes.h
@@ -0,0 +1,452 @@
+#ifndef _INPUT_H
+#define _INPUT_H
+
+/*
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * Keys and buttons
+ */
+
+#define KEY_RESERVED		0
+#define KEY_ESC			1
+#define KEY_1			2
+#define KEY_2			3
+#define KEY_3			4
+#define KEY_4			5
+#define KEY_5			6
+#define KEY_6			7
+#define KEY_7			8
+#define KEY_8			9
+#define KEY_9			10
+#define KEY_0			11
+#define KEY_MINUS		12
+#define KEY_EQUAL		13
+#define KEY_BACKSPACE		14
+#define KEY_TAB			15
+#define KEY_Q			16
+#define KEY_W			17
+#define KEY_E			18
+#define KEY_R			19
+#define KEY_T			20
+#define KEY_Y			21
+#define KEY_U			22
+#define KEY_I			23
+#define KEY_O			24
+#define KEY_P			25
+#define KEY_LEFTBRACE		26
+#define KEY_RIGHTBRACE		27
+#define KEY_ENTER		28
+#define KEY_LEFTCTRL		29
+#define KEY_A			30
+#define KEY_S			31
+#define KEY_D			32
+#define KEY_F			33
+#define KEY_G			34
+#define KEY_H			35
+#define KEY_J			36
+#define KEY_K			37
+#define KEY_L			38
+#define KEY_SEMICOLON		39
+#define KEY_APOSTROPHE		40
+#define KEY_GRAVE		41
+#define KEY_LEFTSHIFT		42
+#define KEY_BACKSLASH		43
+#define KEY_Z			44
+#define KEY_X			45
+#define KEY_C			46
+#define KEY_V			47
+#define KEY_B			48
+#define KEY_N			49
+#define KEY_M			50
+#define KEY_COMMA		51
+#define KEY_DOT			52
+#define KEY_SLASH		53
+#define KEY_RIGHTSHIFT		54
+#define KEY_KPASTERISK		55
+#define KEY_LEFTALT		56
+#define KEY_SPACE		57
+#define KEY_CAPSLOCK		58
+#define KEY_F1			59
+#define KEY_F2			60
+#define KEY_F3			61
+#define KEY_F4			62
+#define KEY_F5			63
+#define KEY_F6			64
+#define KEY_F7			65
+#define KEY_F8			66
+#define KEY_F9			67
+#define KEY_F10			68
+#define KEY_NUMLOCK		69
+#define KEY_SCROLLLOCK		70
+#define KEY_KP7			71
+#define KEY_KP8			72
+#define KEY_KP9			73
+#define KEY_KPMINUS		74
+#define KEY_KP4			75
+#define KEY_KP5			76
+#define KEY_KP6			77
+#define KEY_KPPLUS		78
+#define KEY_KP1			79
+#define KEY_KP2			80
+#define KEY_KP3			81
+#define KEY_KP0			82
+#define KEY_KPDOT		83
+
+#define KEY_ZENKAKUHANKAKU	85
+#define KEY_102ND		86
+#define KEY_F11			87
+#define KEY_F12			88
+#define KEY_RO			89
+#define KEY_KATAKANA		90
+#define KEY_HIRAGANA		91
+#define KEY_HENKAN		92
+#define KEY_KATAKANAHIRAGANA	93
+#define KEY_MUHENKAN		94
+#define KEY_KPJPCOMMA		95
+#define KEY_KPENTER		96
+#define KEY_RIGHTCTRL		97
+#define KEY_KPSLASH		98
+#define KEY_SYSRQ		99
+#define KEY_RIGHTALT		100
+#define KEY_LINEFEED		101
+#define KEY_HOME		102
+#define KEY_UP			103
+#define KEY_PAGEUP		104
+#define KEY_LEFT		105
+#define KEY_RIGHT		106
+#define KEY_END			107
+#define KEY_DOWN		108
+#define KEY_PAGEDOWN		109
+#define KEY_INSERT		110
+#define KEY_DELETE		111
+#define KEY_MACRO		112
+#define KEY_MUTE		113
+#define KEY_VOLUMEDOWN		114
+#define KEY_VOLUMEUP		115
+#define KEY_POWER		116
+#define KEY_KPEQUAL		117
+#define KEY_KPPLUSMINUS		118
+#define KEY_PAUSE		119
+
+#define KEY_KPCOMMA		121
+#define KEY_HANGEUL		122
+#define KEY_HANGUEL		KEY_HANGEUL
+#define KEY_HANJA		123
+#define KEY_YEN			124
+#define KEY_LEFTMETA		125
+#define KEY_RIGHTMETA		126
+#define KEY_COMPOSE		127
+
+#define KEY_STOP		128
+#define KEY_AGAIN		129
+#define KEY_PROPS		130
+#define KEY_UNDO		131
+#define KEY_FRONT		132
+#define KEY_COPY		133
+#define KEY_OPEN		134
+#define KEY_PASTE		135
+#define KEY_FIND		136
+#define KEY_CUT			137
+#define KEY_HELP		138
+#define KEY_MENU		139
+#define KEY_CALC		140
+#define KEY_SETUP		141
+#define KEY_SLEEP		142
+#define KEY_WAKEUP		143
+#define KEY_FILE		144
+#define KEY_SENDFILE		145
+#define KEY_DELETEFILE		146
+#define KEY_XFER		147
+#define KEY_PROG1		148
+#define KEY_PROG2		149
+#define KEY_WWW			150
+#define KEY_MSDOS		151
+#define KEY_COFFEE		152
+#define KEY_DIRECTION		153
+#define KEY_CYCLEWINDOWS	154
+#define KEY_MAIL		155
+#define KEY_BOOKMARKS		156
+#define KEY_COMPUTER		157
+#define KEY_BACK		158
+#define KEY_FORWARD		159
+#define KEY_CLOSECD		160
+#define KEY_EJECTCD		161
+#define KEY_EJECTCLOSECD	162
+#define KEY_NEXTSONG		163
+#define KEY_PLAYPAUSE		164
+#define KEY_PREVIOUSSONG	165
+#define KEY_STOPCD		166
+#define KEY_RECORD		167
+#define KEY_REWIND		168
+#define KEY_PHONE		169
+#define KEY_ISO			170
+#define KEY_CONFIG		171
+#define KEY_HOMEPAGE		172
+#define KEY_REFRESH		173
+#define KEY_EXIT		174
+#define KEY_MOVE		175
+#define KEY_EDIT		176
+#define KEY_SCROLLUP		177
+#define KEY_SCROLLDOWN		178
+#define KEY_KPLEFTPAREN		179
+#define KEY_KPRIGHTPAREN	180
+#define KEY_NEW			181
+#define KEY_REDO		182
+
+#define KEY_F13			183
+#define KEY_F14			184
+#define KEY_F15			185
+#define KEY_F16			186
+#define KEY_F17			187
+#define KEY_F18			188
+#define KEY_F19			189
+#define KEY_F20			190
+#define KEY_F21			191
+#define KEY_F22			192
+#define KEY_F23			193
+#define KEY_F24			194
+
+#define KEY_PLAYCD		200
+#define KEY_PAUSECD		201
+#define KEY_PROG3		202
+#define KEY_PROG4		203
+#define KEY_SUSPEND		205
+#define KEY_CLOSE		206
+#define KEY_PLAY		207
+#define KEY_FASTFORWARD		208
+#define KEY_BASSBOOST		209
+#define KEY_PRINT		210
+#define KEY_HP			211
+#define KEY_CAMERA		212
+#define KEY_SOUND		213
+#define KEY_QUESTION		214
+#define KEY_EMAIL		215
+#define KEY_CHAT		216
+#define KEY_SEARCH		217
+#define KEY_CONNECT		218
+#define KEY_FINANCE		219
+#define KEY_SPORT		220
+#define KEY_SHOP		221
+#define KEY_ALTERASE		222
+#define KEY_CANCEL		223
+#define KEY_BRIGHTNESSDOWN	224
+#define KEY_BRIGHTNESSUP	225
+#define KEY_MEDIA		226
+
+
+/*Zeus: these keys are defined for OMAP730 Perseus2*/
+#define KEY_STAR		227
+#define KEY_SHARP		228
+#define KEY_SOFT1		229
+#define KEY_SOFT2		230
+#define KEY_SEND		231
+#define KEY_CENTER		232
+#define KEY_HEADSETHOOK		233
+#define KEY_0_5			234
+#define KEY_2_5			235
+
+#define KEY_SWITCHVIDEOMODE	236
+#define KEY_KBDILLUMTOGGLE	237
+#define KEY_KBDILLUMDOWN	238
+#define KEY_KBDILLUMUP		239
+
+#define KEY_SEND		231
+#define KEY_REPLY		232
+#define KEY_FORWARDMAIL		233
+#define KEY_SAVE		234
+#define KEY_DOCUMENTS		235
+
+#define KEY_BATTERY		236
+
+#define KEY_UNKNOWN		240
+
+#define KEY_NUM                 241
+#define KEY_FOCUS               242
+#define KEY_PLUS                243
+#define KEY_NOTIFICATION        244
+
+#define BTN_MISC		0x100
+#define BTN_0			0x100
+#define BTN_1			0x101
+#define BTN_2			0x102
+#define BTN_3			0x103
+#define BTN_4			0x104
+#define BTN_5			0x105
+#define BTN_6			0x106
+#define BTN_7			0x107
+#define BTN_8			0x108
+#define BTN_9			0x109
+
+#define BTN_MOUSE		0x110
+#define BTN_LEFT		0x110
+#define BTN_RIGHT		0x111
+#define BTN_MIDDLE		0x112
+#define BTN_SIDE		0x113
+#define BTN_EXTRA		0x114
+#define BTN_FORWARD		0x115
+#define BTN_BACK		0x116
+#define BTN_TASK		0x117
+
+#define BTN_JOYSTICK		0x120
+#define BTN_TRIGGER		0x120
+#define BTN_THUMB		0x121
+#define BTN_THUMB2		0x122
+#define BTN_TOP			0x123
+#define BTN_TOP2		0x124
+#define BTN_PINKIE		0x125
+#define BTN_BASE		0x126
+#define BTN_BASE2		0x127
+#define BTN_BASE3		0x128
+#define BTN_BASE4		0x129
+#define BTN_BASE5		0x12a
+#define BTN_BASE6		0x12b
+#define BTN_DEAD		0x12f
+
+#define BTN_GAMEPAD		0x130
+#define BTN_A			0x130
+#define BTN_B			0x131
+#define BTN_C			0x132
+#define BTN_X			0x133
+#define BTN_Y			0x134
+#define BTN_Z			0x135
+#define BTN_TL			0x136
+#define BTN_TR			0x137
+#define BTN_TL2			0x138
+#define BTN_TR2			0x139
+#define BTN_SELECT		0x13a
+#define BTN_START		0x13b
+#define BTN_MODE		0x13c
+#define BTN_THUMBL		0x13d
+#define BTN_THUMBR		0x13e
+
+#define BTN_DIGI		0x140
+#define BTN_TOOL_PEN		0x140
+#define BTN_TOOL_RUBBER		0x141
+#define BTN_TOOL_BRUSH		0x142
+#define BTN_TOOL_PENCIL		0x143
+#define BTN_TOOL_AIRBRUSH	0x144
+#define BTN_TOOL_FINGER		0x145
+#define BTN_TOOL_MOUSE		0x146
+#define BTN_TOOL_LENS		0x147
+#define BTN_TOUCH		0x14a
+#define BTN_STYLUS		0x14b
+#define BTN_STYLUS2		0x14c
+#define BTN_TOOL_DOUBLETAP	0x14d
+#define BTN_TOOL_TRIPLETAP	0x14e
+
+#define BTN_WHEEL		0x150
+#define BTN_GEAR_DOWN		0x150
+#define BTN_GEAR_UP		0x151
+
+#define KEY_OK			0x160
+#define KEY_SELECT		0x161
+#define KEY_GOTO		0x162
+#define KEY_CLEAR		0x163
+#define KEY_POWER2		0x164
+#define KEY_OPTION		0x165
+#define KEY_INFO		0x166
+#define KEY_TIME		0x167
+#define KEY_VENDOR		0x168
+#define KEY_ARCHIVE		0x169
+#define KEY_PROGRAM		0x16a
+#define KEY_CHANNEL		0x16b
+#define KEY_FAVORITES		0x16c
+#define KEY_EPG			0x16d
+#define KEY_PVR			0x16e
+#define KEY_MHP			0x16f
+#define KEY_LANGUAGE		0x170
+#define KEY_TITLE		0x171
+#define KEY_SUBTITLE		0x172
+#define KEY_ANGLE		0x173
+#define KEY_ZOOM		0x174
+#define KEY_MODE		0x175
+#define KEY_KEYBOARD		0x176
+#define KEY_SCREEN		0x177
+#define KEY_PC			0x178
+#define KEY_TV			0x179
+#define KEY_TV2			0x17a
+#define KEY_VCR			0x17b
+#define KEY_VCR2		0x17c
+#define KEY_SAT			0x17d
+#define KEY_SAT2		0x17e
+#define KEY_CD			0x17f
+#define KEY_TAPE		0x180
+#define KEY_RADIO		0x181
+#define KEY_TUNER		0x182
+#define KEY_PLAYER		0x183
+#define KEY_TEXT		0x184
+#define KEY_DVD			0x185
+#define KEY_AUX			0x186
+#define KEY_MP3			0x187
+#define KEY_AUDIO		0x188
+#define KEY_VIDEO		0x189
+#define KEY_DIRECTORY		0x18a
+#define KEY_LIST		0x18b
+#define KEY_MEMO		0x18c
+#define KEY_CALENDAR		0x18d
+#define KEY_RED			0x18e
+#define KEY_GREEN		0x18f
+#define KEY_YELLOW		0x190
+#define KEY_BLUE		0x191
+#define KEY_CHANNELUP		0x192
+#define KEY_CHANNELDOWN		0x193
+#define KEY_FIRST		0x194
+#define KEY_LAST		0x195
+#define KEY_AB			0x196
+#define KEY_NEXT		0x197
+#define KEY_RESTART		0x198
+#define KEY_SLOW		0x199
+#define KEY_SHUFFLE		0x19a
+#define KEY_BREAK		0x19b
+#define KEY_PREVIOUS		0x19c
+#define KEY_DIGITS		0x19d
+#define KEY_TEEN		0x19e
+#define KEY_TWEN		0x19f
+
+#define KEY_DEL_EOL		0x1c0
+#define KEY_DEL_EOS		0x1c1
+#define KEY_INS_LINE		0x1c2
+#define KEY_DEL_LINE		0x1c3
+
+#define KEY_FN			0x1d0
+#define KEY_FN_ESC		0x1d1
+#define KEY_FN_F1		0x1d2
+#define KEY_FN_F2		0x1d3
+#define KEY_FN_F3		0x1d4
+#define KEY_FN_F4		0x1d5
+#define KEY_FN_F5		0x1d6
+#define KEY_FN_F6		0x1d7
+#define KEY_FN_F7		0x1d8
+#define KEY_FN_F8		0x1d9
+#define KEY_FN_F9		0x1da
+#define KEY_FN_F10		0x1db
+#define KEY_FN_F11		0x1dc
+#define KEY_FN_F12		0x1dd
+#define KEY_FN_1		0x1de
+#define KEY_FN_2		0x1df
+#define KEY_FN_D		0x1e0
+#define KEY_FN_E		0x1e1
+#define KEY_FN_F		0x1e2
+#define KEY_FN_S		0x1e3
+#define KEY_FN_B		0x1e4
+
+#define KEY_BRL_DOT1		0x1f1
+#define KEY_BRL_DOT2		0x1f2
+#define KEY_BRL_DOT3		0x1f3
+#define KEY_BRL_DOT4		0x1f4
+#define KEY_BRL_DOT5		0x1f5
+#define KEY_BRL_DOT6		0x1f6
+#define KEY_BRL_DOT7		0x1f7
+#define KEY_BRL_DOT8		0x1f8
+
+/* We avoid low common keys in module aliases so they don't get huge. */
+#define KEY_MIN_INTERESTING	KEY_MUTE
+#define KEY_MAX			0x1ff
+
+#endif
diff --git a/loader.c b/loader.c
new file mode 100644
index 0000000..84ed123
--- /dev/null
+++ b/loader.c
@@ -0,0 +1,412 @@
+/*
+ * QEMU Executable loader
+ *
+ * Copyright (c) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "cpu.h"
+#include "disas.h"
+#include "sysemu.h"
+#include "uboot_image.h"
+
+/* return the size or -1 if error */
+int get_image_size(const char *filename)
+{
+    int fd, size;
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+    size = lseek(fd, 0, SEEK_END);
+    close(fd);
+    return size;
+}
+
+/* return the size or -1 if error */
+/* deprecated, because caller does not specify buffer size! */
+int load_image(const char *filename, uint8_t *addr)
+{
+    int fd, size;
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+    size = lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, SEEK_SET);
+    if (read(fd, addr, size) != size) {
+        close(fd);
+        return -1;
+    }
+    close(fd);
+    return size;
+}
+
+/* return the amount read, just like fread.  0 may mean error or eof */
+int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f)
+{
+    uint8_t buf[4096];
+    target_phys_addr_t dst_begin = dst_addr;
+    size_t want, did;
+
+    while (nbytes) {
+	want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes;
+	did = fread(buf, 1, want, f);
+	if (did != want) break;
+
+	cpu_physical_memory_write_rom(dst_addr, buf, did);
+	dst_addr += did;
+	nbytes -= did;
+    }
+    return dst_addr - dst_begin;
+}
+
+/* returns 0 on error, 1 if ok */
+int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f)
+{
+    return fread_targphys(dst_addr, nbytes, f) == nbytes;
+}
+
+/* read()-like version */
+int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes)
+{
+    uint8_t buf[4096];
+    target_phys_addr_t dst_begin = dst_addr;
+    size_t want, did;
+
+    while (nbytes) {
+	want = nbytes > sizeof(buf) ? sizeof(buf) : nbytes;
+	did = read(fd, buf, want);
+	if (did != want) break;
+
+	cpu_physical_memory_write_rom(dst_addr, buf, did);
+	dst_addr += did;
+	nbytes -= did;
+    }
+    return dst_addr - dst_begin;
+}
+
+/* return the size or -1 if error */
+int load_image_targphys(const char *filename,
+			target_phys_addr_t addr, int max_sz)
+{
+    FILE *f;
+    size_t got;
+
+    f = fopen(filename, "rb");
+    if (!f) return -1;
+
+    got = fread_targphys(addr, max_sz, f);
+    if (ferror(f)) { fclose(f); return -1; }
+    fclose(f);
+
+    return got;
+}
+
+void pstrcpy_targphys(target_phys_addr_t dest, int buf_size,
+                      const char *source)
+{
+    static const uint8_t nul_byte = 0;
+    const char *nulp;
+
+    if (buf_size <= 0) return;
+    nulp = memchr(source, 0, buf_size);
+    if (nulp) {
+	cpu_physical_memory_write_rom(dest, (uint8_t *)source,
+                                      (nulp - source) + 1);
+    } else {
+	cpu_physical_memory_write_rom(dest, (uint8_t *)source, buf_size - 1);
+	cpu_physical_memory_write_rom(dest, &nul_byte, 1);
+    }
+}
+
+/* A.OUT loader */
+
+struct exec
+{
+  uint32_t a_info;   /* Use macros N_MAGIC, etc for access */
+  uint32_t a_text;   /* length of text, in bytes */
+  uint32_t a_data;   /* length of data, in bytes */
+  uint32_t a_bss;    /* length of uninitialized data area, in bytes */
+  uint32_t a_syms;   /* length of symbol table data in file, in bytes */
+  uint32_t a_entry;  /* start address */
+  uint32_t a_trsize; /* length of relocation info for text, in bytes */
+  uint32_t a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#ifdef BSWAP_NEEDED
+static void bswap_ahdr(struct exec *e)
+{
+    bswap32s(&e->a_info);
+    bswap32s(&e->a_text);
+    bswap32s(&e->a_data);
+    bswap32s(&e->a_bss);
+    bswap32s(&e->a_syms);
+    bswap32s(&e->a_entry);
+    bswap32s(&e->a_trsize);
+    bswap32s(&e->a_drsize);
+}
+#else
+#define bswap_ahdr(x) do { } while (0)
+#endif
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define OMAGIC 0407
+#define NMAGIC 0410
+#define ZMAGIC 0413
+#define QMAGIC 0314
+#define _N_HDROFF(x) (1024 - sizeof (struct exec))
+#define N_TXTOFF(x)							\
+    (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) :	\
+     (N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
+#define N_TXTADDR(x) (N_MAGIC(x) == QMAGIC ? TARGET_PAGE_SIZE : 0)
+#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text)
+#define _N_SEGMENT_ROUND(x) (((x) + TARGET_PAGE_SIZE - 1) & ~(TARGET_PAGE_SIZE - 1))
+
+#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text)
+
+#define N_DATADDR(x) \
+    (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \
+     : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x))))
+
+
+int load_aout(const char *filename, target_phys_addr_t addr, int max_sz)
+{
+    int fd, size, ret;
+    struct exec e;
+    uint32_t magic;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+
+    size = read(fd, &e, sizeof(e));
+    if (size < 0)
+        goto fail;
+
+    bswap_ahdr(&e);
+
+    magic = N_MAGIC(e);
+    switch (magic) {
+    case ZMAGIC:
+    case QMAGIC:
+    case OMAGIC:
+        if (e.a_text + e.a_data > max_sz)
+            goto fail;
+	lseek(fd, N_TXTOFF(e), SEEK_SET);
+	size = read_targphys(fd, addr, e.a_text + e.a_data);
+	if (size < 0)
+	    goto fail;
+	break;
+    case NMAGIC:
+        if (N_DATADDR(e) + e.a_data > max_sz)
+            goto fail;
+	lseek(fd, N_TXTOFF(e), SEEK_SET);
+	size = read_targphys(fd, addr, e.a_text);
+	if (size < 0)
+	    goto fail;
+	ret = read_targphys(fd, addr + N_DATADDR(e), e.a_data);
+	if (ret < 0)
+	    goto fail;
+	size += ret;
+	break;
+    default:
+	goto fail;
+    }
+    close(fd);
+    return size;
+ fail:
+    close(fd);
+    return -1;
+}
+
+/* ELF loader */
+
+static void *load_at(int fd, int offset, int size)
+{
+    void *ptr;
+    if (lseek(fd, offset, SEEK_SET) < 0)
+        return NULL;
+    ptr = qemu_malloc(size);
+    if (!ptr)
+        return NULL;
+    if (read(fd, ptr, size) != size) {
+        qemu_free(ptr);
+        return NULL;
+    }
+    return ptr;
+}
+
+
+#define ELF_CLASS   ELFCLASS32
+#include "elf.h"
+
+#define SZ		32
+#define elf_word        uint32_t
+#define elf_sword        int32_t
+#define bswapSZs	bswap32s
+#include "elf_ops.h"
+
+#undef elfhdr
+#undef elf_phdr
+#undef elf_shdr
+#undef elf_sym
+#undef elf_note
+#undef elf_word
+#undef elf_sword
+#undef bswapSZs
+#undef SZ
+#define elfhdr		elf64_hdr
+#define elf_phdr	elf64_phdr
+#define elf_note	elf64_note
+#define elf_shdr	elf64_shdr
+#define elf_sym		elf64_sym
+#define elf_word        uint64_t
+#define elf_sword        int64_t
+#define bswapSZs	bswap64s
+#define SZ		64
+#include "elf_ops.h"
+
+/* return < 0 if error, otherwise the number of bytes loaded in memory */
+int load_elf(const char *filename, int64_t virt_to_phys_addend,
+             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr)
+{
+    int fd, data_order, host_data_order, must_swab, ret;
+    uint8_t e_ident[EI_NIDENT];
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0) {
+        perror(filename);
+        return -1;
+    }
+    if (read(fd, e_ident, sizeof(e_ident)) != sizeof(e_ident))
+        goto fail;
+    if (e_ident[0] != ELFMAG0 ||
+        e_ident[1] != ELFMAG1 ||
+        e_ident[2] != ELFMAG2 ||
+        e_ident[3] != ELFMAG3)
+        goto fail;
+#ifdef WORDS_BIGENDIAN
+    data_order = ELFDATA2MSB;
+#else
+    data_order = ELFDATA2LSB;
+#endif
+    must_swab = data_order != e_ident[EI_DATA];
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    host_data_order = ELFDATA2MSB;
+#else
+    host_data_order = ELFDATA2LSB;
+#endif
+    if (host_data_order != e_ident[EI_DATA])
+        return -1;
+
+    lseek(fd, 0, SEEK_SET);
+    if (e_ident[EI_CLASS] == ELFCLASS64) {
+        ret = load_elf64(fd, virt_to_phys_addend, must_swab, pentry,
+                         lowaddr, highaddr);
+    } else {
+        ret = load_elf32(fd, virt_to_phys_addend, must_swab, pentry,
+                         lowaddr, highaddr);
+    }
+
+    close(fd);
+    return ret;
+
+ fail:
+    close(fd);
+    return -1;
+}
+
+static void bswap_uboot_header(uboot_image_header_t *hdr)
+{
+#ifndef WORDS_BIGENDIAN
+    bswap32s(&hdr->ih_magic);
+    bswap32s(&hdr->ih_hcrc);
+    bswap32s(&hdr->ih_time);
+    bswap32s(&hdr->ih_size);
+    bswap32s(&hdr->ih_load);
+    bswap32s(&hdr->ih_ep);
+    bswap32s(&hdr->ih_dcrc);
+#endif
+}
+
+/* Load a U-Boot image.  */
+int load_uboot(const char *filename, target_ulong *ep, int *is_linux)
+{
+
+    int fd;
+    int size;
+    uboot_image_header_t h;
+    uboot_image_header_t *hdr = &h;
+    uint8_t *data = NULL;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+        return -1;
+
+    size = read(fd, hdr, sizeof(uboot_image_header_t));
+    if (size < 0)
+        goto fail;
+
+    bswap_uboot_header(hdr);
+
+    if (hdr->ih_magic != IH_MAGIC)
+        goto fail;
+
+    /* TODO: Implement Multi-File images.  */
+    if (hdr->ih_type == IH_TYPE_MULTI) {
+        fprintf(stderr, "Unable to load multi-file u-boot images\n");
+        goto fail;
+    }
+
+    /* TODO: Implement compressed images.  */
+    if (hdr->ih_comp != IH_COMP_NONE) {
+        fprintf(stderr, "Unable to load compressed u-boot images\n");
+        goto fail;
+    }
+
+    /* TODO: Check CPU type.  */
+    if (is_linux) {
+        if (hdr->ih_type == IH_TYPE_KERNEL && hdr->ih_os == IH_OS_LINUX)
+            *is_linux = 1;
+        else
+            *is_linux = 0;
+    }
+
+    *ep = hdr->ih_ep;
+    data = qemu_malloc(hdr->ih_size);
+    if (!data)
+        goto fail;
+
+    if (read(fd, data, hdr->ih_size) != hdr->ih_size) {
+        fprintf(stderr, "Error reading file\n");
+        goto fail;
+    }
+
+    cpu_physical_memory_write_rom(hdr->ih_load, data, hdr->ih_size);
+
+    return hdr->ih_size;
+
+fail:
+    if (data)
+        qemu_free(data);
+    close(fd);
+    return -1;
+}
+
diff --git a/loadpng.c b/loadpng.c
new file mode 100644
index 0000000..218ae20
--- /dev/null
+++ b/loadpng.c
@@ -0,0 +1,275 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <png.h>
+
+#if 0
+#define LOG(x...) fprintf(stderr,"error: " x)
+#else
+#define LOG(x...) do {} while (0)
+#endif
+
+void *loadpng(const char *fn, unsigned *_width, unsigned *_height)
+{
+    FILE *fp = 0;
+    unsigned char header[8];
+    unsigned char *data = 0;
+    unsigned char **rowptrs = 0;
+    png_structp p = 0;
+    png_infop pi = 0;
+    
+    png_uint_32 width, height;
+    int bitdepth, colortype, imethod, cmethod, fmethod, i;
+
+    p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+    if(p == 0) {
+        LOG("%s: failed to allocate png read struct\n", fn);
+        return 0;
+    }
+
+    pi = png_create_info_struct(p);
+    if(pi == 0) {
+        LOG("%s: failed to allocate png info struct\n", fn);
+        goto oops;
+    }
+        
+    fp = fopen(fn, "rb");
+    if(fp == 0) {
+        LOG("%s: failed to open file\n", fn);
+        return 0;
+    }
+
+    if(fread(header, 8, 1, fp) != 1) {
+        LOG("%s: failed to read header\n", fn);
+        goto oops;
+    }
+    
+    if(png_sig_cmp(header, 0, 8)) {
+        LOG("%s: header is not a PNG header\n", fn);
+        goto oops;
+    }
+
+    if(setjmp(png_jmpbuf(p))) {
+        LOG("%s: png library error\n", fn);
+    oops:
+        png_destroy_read_struct(&p, &pi, 0);
+        if(fp != 0) fclose(fp);
+        if(data != 0) free(data);
+        if(rowptrs != 0) free(rowptrs);
+        return 0;
+    }
+
+    png_init_io(p, fp);
+    png_set_sig_bytes(p, 8);
+
+    png_read_info(p, pi);
+
+    png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype,
+                 &imethod, &cmethod, &fmethod);
+//    printf("PNG: %d x %d (d=%d, c=%d)\n",
+//           width, height, bitdepth, colortype);
+
+    switch(colortype){
+    case PNG_COLOR_TYPE_PALETTE:
+        png_set_palette_to_rgb(p);
+        break;
+
+    case PNG_COLOR_TYPE_RGB:
+        if(png_get_valid(p, pi, PNG_INFO_tRNS)) {
+            png_set_tRNS_to_alpha(p);
+        } else {
+            png_set_filler(p, 0xff, PNG_FILLER_AFTER);
+        }
+        break;
+        
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+        break;
+
+    case PNG_COLOR_TYPE_GRAY:
+        if(bitdepth < 8) {
+            png_set_gray_1_2_4_to_8(p);
+        }
+        
+    default:
+        LOG("%s: unsupported (grayscale?) color type\n");
+        goto oops;
+    }
+
+    if(bitdepth == 16) {
+        png_set_strip_16(p);
+    }
+
+    data = (unsigned char*) malloc((width * 4) * height);
+    rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height);
+
+    if((data == 0) || (rowptrs == 0)){
+        LOG("could not allocate data buffer\n");
+        goto oops;
+    }
+
+    for(i = 0; i < height; i++) {
+        rowptrs[i] = data + ((width * 4) * i);
+    }
+    
+    png_read_image(p, rowptrs);
+    
+    png_destroy_read_struct(&p, &pi, 0);
+    fclose(fp);
+    if(rowptrs != 0) free(rowptrs);
+
+    *_width = width;
+    *_height = height;
+    
+    return (void*) data;    
+}
+
+
+typedef struct
+{
+    const unsigned char*  base;
+    const unsigned char*  end;
+    const unsigned char*  cursor;
+
+} PngReader;
+
+static void
+png_reader_read_data( png_structp  png_ptr,
+                      png_bytep   data,
+                      png_size_t  length )
+{
+  PngReader* reader = png_get_io_ptr(png_ptr);
+  png_size_t avail  = (png_size_t)(reader->end - reader->cursor);
+
+  if (avail > length)
+      avail = length;
+
+  memcpy( data, reader->cursor, avail );
+  reader->cursor += avail;
+}
+
+
+void *readpng(const unsigned char *base, size_t   size, unsigned *_width, unsigned *_height)
+{
+    PngReader  reader;
+    unsigned char *data = 0;
+    unsigned char **rowptrs = 0;
+    png_structp p = 0;
+    png_infop pi = 0;
+
+    png_uint_32 width, height;
+    int bitdepth, colortype, imethod, cmethod, fmethod, i;
+
+    p = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+    if(p == 0) {
+        LOG("%s: failed to allocate png read struct\n", fn);
+        return 0;
+    }
+
+    pi = png_create_info_struct(p);
+    if(pi == 0) {
+        LOG("%s: failed to allocate png info struct\n", fn);
+        goto oops;
+    }
+
+    reader.base   = base;
+    reader.end    = base + size;
+    reader.cursor = base;
+
+    if(size < 8 || png_sig_cmp((unsigned char*)base, 0, 8)) {
+        LOG("%s: header is not a PNG header\n", fn);
+        goto oops;
+    }
+
+    reader.cursor += 8;
+
+    if(setjmp(png_jmpbuf(p))) {
+        LOG("%s: png library error\n", fn);
+    oops:
+        png_destroy_read_struct(&p, &pi, 0);
+        if(data != 0) free(data);
+        if(rowptrs != 0) free(rowptrs);
+        return 0;
+    }
+
+    png_set_read_fn (p, &reader, png_reader_read_data);
+    png_set_sig_bytes(p, 8);
+
+    png_read_info(p, pi);
+
+    png_get_IHDR(p, pi, &width, &height, &bitdepth, &colortype,
+                 &imethod, &cmethod, &fmethod);
+//    printf("PNG: %d x %d (d=%d, c=%d)\n",
+//           width, height, bitdepth, colortype);
+
+    switch(colortype){
+    case PNG_COLOR_TYPE_PALETTE:
+        png_set_palette_to_rgb(p);
+        break;
+
+    case PNG_COLOR_TYPE_RGB:
+        if(png_get_valid(p, pi, PNG_INFO_tRNS)) {
+            png_set_tRNS_to_alpha(p);
+        } else {
+            png_set_filler(p, 0xff, PNG_FILLER_AFTER);
+        }
+        break;
+
+    case PNG_COLOR_TYPE_RGB_ALPHA:
+        break;
+
+    case PNG_COLOR_TYPE_GRAY:
+        if(bitdepth < 8) {
+            png_set_gray_1_2_4_to_8(p);
+        }
+
+    default:
+        LOG("%s: unsupported (grayscale?) color type\n");
+        goto oops;
+    }
+
+    if(bitdepth == 16) {
+        png_set_strip_16(p);
+    }
+
+    data    = (unsigned char*) malloc((width * 4) * height);
+    rowptrs = (unsigned char **) malloc(sizeof(unsigned char*) * height);
+
+    if((data == 0) || (rowptrs == 0)){
+        LOG("could not allocate data buffer\n");
+        goto oops;
+    }
+
+    for(i = 0; i < height; i++) {
+        rowptrs[i] = data + ((width * 4) * i);
+    }
+
+    png_read_image(p, rowptrs);
+
+    png_destroy_read_struct(&p, &pi, 0);
+    if(rowptrs != 0) free(rowptrs);
+
+    *_width = width;
+    *_height = height;
+
+    return (void*) data;    
+}
+
+
+#if 0
+int main(int argc, char **argv)
+{
+    unsigned w,h;
+    unsigned char *data;
+    
+    if(argc < 2) return 0;
+
+
+    data = loadpng(argv[1], &w, &h);
+
+    if(data != 0) {
+        printf("w: %d  h: %d\n", w, h);
+    }
+
+    return 0;
+}
+#endif
diff --git a/m68k.ld b/m68k.ld
new file mode 100644
index 0000000..28da902
--- /dev/null
+++ b/m68k.ld
@@ -0,0 +1,177 @@
+/* Script for -z combreloc: combine and sort reloc sections */
+OUTPUT_FORMAT("elf32-m68k", "elf32-m68k",
+	      "elf32-m68k")
+OUTPUT_ARCH(m68k)
+ENTRY(_start)
+SEARCH_DIR("/usr/local/m68k-linux/lib");
+/* Do we need any of these for elf?
+   __DYNAMIC = 0;    */
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .hash           : { *(.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rel.dyn        :
+    {
+      *(.rel.init)
+      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+      *(.rel.fini)
+      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+      *(.rel.ctors)
+      *(.rel.dtors)
+      *(.rel.got)
+      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+    }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+      *(.rela.ctors)
+      *(.rela.dtors)
+      *(.rela.got)
+      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+    }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0x4e754e75
+  .plt            : { *(.plt) }
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0x4e754e75
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0x4e754e75
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x2000) + (. & (0x2000 - 1));
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(32 / 8);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { *(.preinit_array) }
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { *(.init_array) }
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { *(.fini_array) }
+  PROVIDE (__fini_array_end = .);
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .eh_frame       : { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : { *(.gcc_except_table) }
+  .dynamic        : { *(.dynamic) }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    /* We don't want to include the .ctor section from
+       from the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .got            : { *(.got.plt) *(.got) }
+  _edata = .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.  */
+   . = ALIGN(32 / 8);
+  }
+  . = ALIGN(32 / 8);
+  _end = .;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+}
diff --git a/monitor.c b/monitor.c
new file mode 100644
index 0000000..f0f38a7
--- /dev/null
+++ b/monitor.c
@@ -0,0 +1,2736 @@
+/*
+ * QEMU monitor
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw/hw.h"
+#include "hw/usb.h"
+
+#include "hw/pc.h"
+#include "hw/pci.h"
+#include "gdbstub.h"
+#include "net.h"
+#include "qemu-char.h"
+#include "sysemu.h"
+#include "console.h"
+#include "block.h"
+#include "audio/audio.h"
+#include "disas.h"
+#include "cpu-defs.h"
+#include <dirent.h>
+#include "qemu-timer.h"
+
+//#define DEBUG
+//#define DEBUG_COMPLETION
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
+/*
+ * Supported types:
+ *
+ * 'F'          filename
+ * 'B'          block device name
+ * 's'          string (accept optional quote)
+ * 'i'          32 bit integer
+ * 'l'          target long (32 or 64 bit)
+ * '/'          optional gdb-like print format (like "/10x")
+ *
+ * '?'          optional type (for 'F', 's' and 'i')
+ *
+ */
+
+typedef struct term_cmd_t {
+    const char *name;
+    const char *args_type;
+    void *handler;
+    const char *params;
+    const char *help;
+} term_cmd_t;
+
+#define MAX_MON 4
+static CharDriverState *monitor_hd[MAX_MON];
+static int hide_banner;
+
+static term_cmd_t term_cmds[];
+static term_cmd_t info_cmds[];
+
+static uint8_t term_outbuf[1024];
+static int term_outbuf_index;
+
+static void monitor_start_input(void);
+
+CPUState *mon_cpu = NULL;
+
+void term_flush(void)
+{
+    int i;
+    if (term_outbuf_index > 0) {
+        for (i = 0; i < MAX_MON; i++)
+            if (monitor_hd[i] && monitor_hd[i]->focus == 0)
+                qemu_chr_write(monitor_hd[i], term_outbuf, term_outbuf_index);
+        term_outbuf_index = 0;
+    }
+}
+
+/* flush at every end of line or if the buffer is full */
+void term_puts(const char *str)
+{
+    char c;
+    for(;;) {
+        c = *str++;
+        if (c == '\0')
+            break;
+        if (c == '\n')
+            term_outbuf[term_outbuf_index++] = '\r';
+        term_outbuf[term_outbuf_index++] = c;
+        if (term_outbuf_index >= (sizeof(term_outbuf) - 1) ||
+            c == '\n')
+            term_flush();
+    }
+}
+
+void term_vprintf(const char *fmt, va_list ap)
+{
+    char buf[4096];
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    term_puts(buf);
+}
+
+void term_printf(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    term_vprintf(fmt, ap);
+    va_end(ap);
+}
+
+void term_print_filename(const char *filename)
+{
+    int i;
+
+    for (i = 0; filename[i]; i++) {
+	switch (filename[i]) {
+	case ' ':
+	case '"':
+	case '\\':
+	    term_printf("\\%c", filename[i]);
+	    break;
+	case '\t':
+	    term_printf("\\t");
+	    break;
+	case '\r':
+	    term_printf("\\r");
+	    break;
+	case '\n':
+	    term_printf("\\n");
+	    break;
+	default:
+	    term_printf("%c", filename[i]);
+	    break;
+	}
+    }
+}
+
+static int monitor_fprintf(FILE *stream, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    term_vprintf(fmt, ap);
+    va_end(ap);
+    return 0;
+}
+
+static int compare_cmd(const char *name, const char *list)
+{
+    const char *p, *pstart;
+    int len;
+    len = strlen(name);
+    p = list;
+    for(;;) {
+        pstart = p;
+        p = strchr(p, '|');
+        if (!p)
+            p = pstart + strlen(pstart);
+        if ((p - pstart) == len && !memcmp(pstart, name, len))
+            return 1;
+        if (*p == '\0')
+            break;
+        p++;
+    }
+    return 0;
+}
+
+static void help_cmd1(term_cmd_t *cmds, const char *prefix, const char *name)
+{
+    term_cmd_t *cmd;
+
+    for(cmd = cmds; cmd->name != NULL; cmd++) {
+        if (!name || !strcmp(name, cmd->name))
+            term_printf("%s%s %s -- %s\n", prefix, cmd->name, cmd->params, cmd->help);
+    }
+}
+
+static void help_cmd(const char *name)
+{
+    if (name && !strcmp(name, "info")) {
+        help_cmd1(info_cmds, "info ", NULL);
+    } else {
+        help_cmd1(term_cmds, "", name);
+        if (name && !strcmp(name, "log")) {
+            CPULogItem *item;
+            term_printf("Log items (comma separated):\n");
+            term_printf("%-10s %s\n", "none", "remove all logs");
+            for(item = cpu_log_items; item->mask != 0; item++) {
+                term_printf("%-10s %s\n", item->name, item->help);
+            }
+        }
+    }
+}
+
+static void do_help(const char *name)
+{
+    help_cmd(name);
+}
+
+static void do_commit(const char *device)
+{
+    int i, all_devices;
+
+    all_devices = !strcmp(device, "all");
+    for (i = 0; i < nb_drives; i++) {
+            if (all_devices ||
+                !strcmp(bdrv_get_device_name(drives_table[i].bdrv), device))
+                bdrv_commit(drives_table[i].bdrv);
+    }
+}
+
+static void do_info(const char *item)
+{
+    term_cmd_t *cmd;
+    void (*handler)(void);
+
+    if (!item)
+        goto help;
+    for(cmd = info_cmds; cmd->name != NULL; cmd++) {
+        if (compare_cmd(item, cmd->name))
+            goto found;
+    }
+ help:
+    help_cmd("info");
+    return;
+ found:
+    handler = cmd->handler;
+    handler();
+}
+
+static void do_info_version(void)
+{
+  term_printf("%s\n", QEMU_VERSION);
+}
+
+static void do_info_name(void)
+{
+    if (qemu_name)
+        term_printf("%s\n", qemu_name);
+}
+
+static void do_info_block(void)
+{
+    bdrv_info();
+}
+
+static void do_info_blockstats(void)
+{
+    bdrv_info_stats();
+}
+
+/* get the current CPU defined by the user */
+static int mon_set_cpu(int cpu_index)
+{
+    CPUState *env;
+
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        if (env->cpu_index == cpu_index) {
+            mon_cpu = env;
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static CPUState *mon_get_cpu(void)
+{
+    if (!mon_cpu) {
+        mon_set_cpu(0);
+    }
+    return mon_cpu;
+}
+
+static void do_info_registers(void)
+{
+    CPUState *env;
+    env = mon_get_cpu();
+    if (!env)
+        return;
+#ifdef TARGET_I386
+    cpu_dump_state(env, NULL, monitor_fprintf,
+                   X86_DUMP_FPU);
+#else
+    cpu_dump_state(env, NULL, monitor_fprintf,
+                   0);
+#endif
+}
+
+static void do_info_cpus(void)
+{
+    CPUState *env;
+
+    /* just to set the default cpu if not already done */
+    mon_get_cpu();
+
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        term_printf("%c CPU #%d:",
+                    (env == mon_cpu) ? '*' : ' ',
+                    env->cpu_index);
+#if defined(TARGET_I386)
+        term_printf(" pc=0x" TARGET_FMT_lx, env->eip + env->segs[R_CS].base);
+#elif defined(TARGET_PPC)
+        term_printf(" nip=0x" TARGET_FMT_lx, env->nip);
+#elif defined(TARGET_SPARC)
+        term_printf(" pc=0x" TARGET_FMT_lx " npc=0x" TARGET_FMT_lx, env->pc, env->npc);
+#elif defined(TARGET_MIPS)
+        term_printf(" PC=0x" TARGET_FMT_lx, env->active_tc.PC);
+#endif
+        if (env->halted)
+            term_printf(" (halted)");
+        term_printf("\n");
+    }
+}
+
+static void do_cpu_set(int index)
+{
+    if (mon_set_cpu(index) < 0)
+        term_printf("Invalid CPU index\n");
+}
+
+static void do_info_jit(void)
+{
+    dump_exec_info(NULL, monitor_fprintf);
+}
+
+static void do_info_history (void)
+{
+    int i;
+    const char *str;
+
+    i = 0;
+    for(;;) {
+        str = readline_get_history(i);
+        if (!str)
+            break;
+	term_printf("%d: '%s'\n", i, str);
+        i++;
+    }
+}
+
+#if defined(TARGET_PPC)
+/* XXX: not implemented in other targets */
+static void do_info_cpu_stats (void)
+{
+    CPUState *env;
+
+    env = mon_get_cpu();
+    cpu_dump_statistics(env, NULL, &monitor_fprintf, 0);
+}
+#endif
+
+static void do_quit(void)
+{
+    exit(0);
+}
+
+static int eject_device(BlockDriverState *bs, int force)
+{
+    if (bdrv_is_inserted(bs)) {
+        if (!force) {
+            if (!bdrv_is_removable(bs)) {
+                term_printf("device is not removable\n");
+                return -1;
+            }
+            if (bdrv_is_locked(bs)) {
+                term_printf("device is locked\n");
+                return -1;
+            }
+        }
+        bdrv_close(bs);
+    }
+    return 0;
+}
+
+static void do_eject(int force, const char *filename)
+{
+    BlockDriverState *bs;
+
+    bs = bdrv_find(filename);
+    if (!bs) {
+        term_printf("device not found\n");
+        return;
+    }
+    eject_device(bs, force);
+}
+
+static void do_change_block(const char *device, const char *filename, const char *fmt)
+{
+    BlockDriverState *bs;
+    BlockDriver *drv = NULL;
+
+    bs = bdrv_find(device);
+    if (!bs) {
+        term_printf("device not found\n");
+        return;
+    }
+    if (fmt) {
+        drv = bdrv_find_format(fmt);
+        if (!drv) {
+            term_printf("invalid format %s\n", fmt);
+            return;
+        }
+    }
+    if (eject_device(bs, 0) < 0)
+        return;
+    bdrv_open2(bs, filename, 0, drv);
+    qemu_key_check(bs, filename);
+}
+
+static void do_change_vnc(const char *target)
+{
+    if (strcmp(target, "passwd") == 0 ||
+	strcmp(target, "password") == 0) {
+	char password[9];
+	monitor_readline("Password: ", 1, password, sizeof(password)-1);
+	password[sizeof(password)-1] = '\0';
+	if (vnc_display_password(NULL, password) < 0)
+	    term_printf("could not set VNC server password\n");
+    } else {
+	if (vnc_display_open(NULL, target) < 0)
+	    term_printf("could not start VNC server on %s\n", target);
+    }
+}
+
+static void do_change(const char *device, const char *target, const char *fmt)
+{
+    if (strcmp(device, "vnc") == 0) {
+	do_change_vnc(target);
+    } else {
+	do_change_block(device, target, fmt);
+    }
+}
+
+static void do_screen_dump(const char *filename)
+{
+    vga_hw_screen_dump(filename);
+}
+
+static void do_logfile(const char *filename)
+{
+    cpu_set_log_filename(filename);
+}
+
+static void do_log(const char *items)
+{
+    int mask;
+
+    if (!strcmp(items, "none")) {
+        mask = 0;
+    } else {
+        mask = cpu_str_to_log_mask(items);
+        if (!mask) {
+            help_cmd("log");
+            return;
+        }
+    }
+    cpu_set_log(mask);
+}
+
+static void do_stop(void)
+{
+    vm_stop(EXCP_INTERRUPT);
+}
+
+static void do_cont(void)
+{
+    vm_start();
+}
+
+#ifdef CONFIG_GDBSTUB
+static void do_gdbserver(const char *port)
+{
+    if (!port)
+        port = DEFAULT_GDBSTUB_PORT;
+    if (gdbserver_start(port) < 0) {
+        qemu_printf("Could not open gdbserver socket on port '%s'\n", port);
+    } else {
+        qemu_printf("Waiting gdb connection on port '%s'\n", port);
+    }
+}
+#endif
+
+static void term_printc(int c)
+{
+    term_printf("'");
+    switch(c) {
+    case '\'':
+        term_printf("\\'");
+        break;
+    case '\\':
+        term_printf("\\\\");
+        break;
+    case '\n':
+        term_printf("\\n");
+        break;
+    case '\r':
+        term_printf("\\r");
+        break;
+    default:
+        if (c >= 32 && c <= 126) {
+            term_printf("%c", c);
+        } else {
+            term_printf("\\x%02x", c);
+        }
+        break;
+    }
+    term_printf("'");
+}
+
+static void memory_dump(int count, int format, int wsize,
+                        target_phys_addr_t addr, int is_physical)
+{
+    CPUState *env;
+    int nb_per_line, l, line_size, i, max_digits, len;
+    uint8_t buf[16];
+    uint64_t v;
+
+    if (format == 'i') {
+        int flags;
+        flags = 0;
+        env = mon_get_cpu();
+        if (!env && !is_physical)
+            return;
+#ifdef TARGET_I386
+        if (wsize == 2) {
+            flags = 1;
+        } else if (wsize == 4) {
+            flags = 0;
+        } else {
+            /* as default we use the current CS size */
+            flags = 0;
+            if (env) {
+#ifdef TARGET_X86_64
+                if ((env->efer & MSR_EFER_LMA) &&
+                    (env->segs[R_CS].flags & DESC_L_MASK))
+                    flags = 2;
+                else
+#endif
+                if (!(env->segs[R_CS].flags & DESC_B_MASK))
+                    flags = 1;
+            }
+        }
+#endif
+        monitor_disas(env, addr, count, is_physical, flags);
+        return;
+    }
+
+    len = wsize * count;
+    if (wsize == 1)
+        line_size = 8;
+    else
+        line_size = 16;
+    nb_per_line = line_size / wsize;
+    max_digits = 0;
+
+    switch(format) {
+    case 'o':
+        max_digits = (wsize * 8 + 2) / 3;
+        break;
+    default:
+    case 'x':
+        max_digits = (wsize * 8) / 4;
+        break;
+    case 'u':
+    case 'd':
+        max_digits = (wsize * 8 * 10 + 32) / 33;
+        break;
+    case 'c':
+        wsize = 1;
+        break;
+    }
+
+    while (len > 0) {
+        if (is_physical)
+            term_printf(TARGET_FMT_plx ":", addr);
+        else
+            term_printf(TARGET_FMT_lx ":", (target_ulong)addr);
+        l = len;
+        if (l > line_size)
+            l = line_size;
+        if (is_physical) {
+            cpu_physical_memory_rw(addr, buf, l, 0);
+        } else {
+            env = mon_get_cpu();
+            if (!env)
+                break;
+            if (cpu_memory_rw_debug(env, addr, buf, l, 0) < 0) {
+                term_printf(" Cannot access memory\n");
+                break;
+            }
+        }
+        i = 0;
+        while (i < l) {
+            switch(wsize) {
+            default:
+            case 1:
+                v = ldub_raw(buf + i);
+                break;
+            case 2:
+                v = lduw_raw(buf + i);
+                break;
+            case 4:
+                v = (uint32_t)ldl_raw(buf + i);
+                break;
+            case 8:
+                v = ldq_raw(buf + i);
+                break;
+            }
+            term_printf(" ");
+            switch(format) {
+            case 'o':
+                term_printf("%#*" PRIo64, max_digits, v);
+                break;
+            case 'x':
+                term_printf("0x%0*" PRIx64, max_digits, v);
+                break;
+            case 'u':
+                term_printf("%*" PRIu64, max_digits, v);
+                break;
+            case 'd':
+                term_printf("%*" PRId64, max_digits, v);
+                break;
+            case 'c':
+                term_printc(v);
+                break;
+            }
+            i += wsize;
+        }
+        term_printf("\n");
+        addr += l;
+        len -= l;
+    }
+}
+
+#if TARGET_LONG_BITS == 64
+#define GET_TLONG(h, l) (((uint64_t)(h) << 32) | (l))
+#else
+#define GET_TLONG(h, l) (l)
+#endif
+
+static void do_memory_dump(int count, int format, int size,
+                           uint32_t addrh, uint32_t addrl)
+{
+    target_long addr = GET_TLONG(addrh, addrl);
+    memory_dump(count, format, size, addr, 0);
+}
+
+#if TARGET_PHYS_ADDR_BITS > 32
+#define GET_TPHYSADDR(h, l) (((uint64_t)(h) << 32) | (l))
+#else
+#define GET_TPHYSADDR(h, l) (l)
+#endif
+
+static void do_physical_memory_dump(int count, int format, int size,
+                                    uint32_t addrh, uint32_t addrl)
+
+{
+    target_phys_addr_t addr = GET_TPHYSADDR(addrh, addrl);
+    memory_dump(count, format, size, addr, 1);
+}
+
+static void do_print(int count, int format, int size, unsigned int valh, unsigned int vall)
+{
+    target_phys_addr_t val = GET_TPHYSADDR(valh, vall);
+#if TARGET_PHYS_ADDR_BITS == 32
+    switch(format) {
+    case 'o':
+        term_printf("%#o", val);
+        break;
+    case 'x':
+        term_printf("%#x", val);
+        break;
+    case 'u':
+        term_printf("%u", val);
+        break;
+    default:
+    case 'd':
+        term_printf("%d", val);
+        break;
+    case 'c':
+        term_printc(val);
+        break;
+    }
+#else
+    switch(format) {
+    case 'o':
+        term_printf("%#" PRIo64, val);
+        break;
+    case 'x':
+        term_printf("%#" PRIx64, val);
+        break;
+    case 'u':
+        term_printf("%" PRIu64, val);
+        break;
+    default:
+    case 'd':
+        term_printf("%" PRId64, val);
+        break;
+    case 'c':
+        term_printc(val);
+        break;
+    }
+#endif
+    term_printf("\n");
+}
+
+static void do_memory_save(unsigned int valh, unsigned int vall,
+                           uint32_t size, const char *filename)
+{
+    FILE *f;
+    target_long addr = GET_TLONG(valh, vall);
+    uint32_t l;
+    CPUState *env;
+    uint8_t buf[1024];
+
+    env = mon_get_cpu();
+    if (!env)
+        return;
+
+    f = fopen(filename, "wb");
+    if (!f) {
+        term_printf("could not open '%s'\n", filename);
+        return;
+    }
+    while (size != 0) {
+        l = sizeof(buf);
+        if (l > size)
+            l = size;
+        cpu_memory_rw_debug(env, addr, buf, l, 0);
+        fwrite(buf, 1, l, f);
+        addr += l;
+        size -= l;
+    }
+    fclose(f);
+}
+
+static void do_physical_memory_save(unsigned int valh, unsigned int vall,
+                                    uint32_t size, const char *filename)
+{
+    FILE *f;
+    uint32_t l;
+    uint8_t buf[1024];
+    target_phys_addr_t addr = GET_TPHYSADDR(valh, vall); 
+
+    f = fopen(filename, "wb");
+    if (!f) {
+        term_printf("could not open '%s'\n", filename);
+        return;
+    }
+    while (size != 0) {
+        l = sizeof(buf);
+        if (l > size)
+            l = size;
+        cpu_physical_memory_rw(addr, buf, l, 0);
+        fwrite(buf, 1, l, f);
+        fflush(f);
+        addr += l;
+        size -= l;
+    }
+    fclose(f);
+}
+
+static void do_sum(uint32_t start, uint32_t size)
+{
+    uint32_t addr;
+    uint8_t buf[1];
+    uint16_t sum;
+
+    sum = 0;
+    for(addr = start; addr < (start + size); addr++) {
+        cpu_physical_memory_rw(addr, buf, 1, 0);
+        /* BSD sum algorithm ('sum' Unix command) */
+        sum = (sum >> 1) | (sum << 15);
+        sum += buf[0];
+    }
+    term_printf("%05d\n", sum);
+}
+
+typedef struct {
+    int keycode;
+    const char *name;
+} KeyDef;
+
+static const KeyDef key_defs[] = {
+    { 0x2a, "shift" },
+    { 0x36, "shift_r" },
+
+    { 0x38, "alt" },
+    { 0xb8, "alt_r" },
+    { 0x64, "altgr" },
+    { 0xe4, "altgr_r" },
+    { 0x1d, "ctrl" },
+    { 0x9d, "ctrl_r" },
+
+    { 0xdd, "menu" },
+
+    { 0x01, "esc" },
+
+    { 0x02, "1" },
+    { 0x03, "2" },
+    { 0x04, "3" },
+    { 0x05, "4" },
+    { 0x06, "5" },
+    { 0x07, "6" },
+    { 0x08, "7" },
+    { 0x09, "8" },
+    { 0x0a, "9" },
+    { 0x0b, "0" },
+    { 0x0c, "minus" },
+    { 0x0d, "equal" },
+    { 0x0e, "backspace" },
+
+    { 0x0f, "tab" },
+    { 0x10, "q" },
+    { 0x11, "w" },
+    { 0x12, "e" },
+    { 0x13, "r" },
+    { 0x14, "t" },
+    { 0x15, "y" },
+    { 0x16, "u" },
+    { 0x17, "i" },
+    { 0x18, "o" },
+    { 0x19, "p" },
+
+    { 0x1c, "ret" },
+
+    { 0x1e, "a" },
+    { 0x1f, "s" },
+    { 0x20, "d" },
+    { 0x21, "f" },
+    { 0x22, "g" },
+    { 0x23, "h" },
+    { 0x24, "j" },
+    { 0x25, "k" },
+    { 0x26, "l" },
+
+    { 0x2c, "z" },
+    { 0x2d, "x" },
+    { 0x2e, "c" },
+    { 0x2f, "v" },
+    { 0x30, "b" },
+    { 0x31, "n" },
+    { 0x32, "m" },
+
+    { 0x37, "asterisk" },
+
+    { 0x39, "spc" },
+    { 0x3a, "caps_lock" },
+    { 0x3b, "f1" },
+    { 0x3c, "f2" },
+    { 0x3d, "f3" },
+    { 0x3e, "f4" },
+    { 0x3f, "f5" },
+    { 0x40, "f6" },
+    { 0x41, "f7" },
+    { 0x42, "f8" },
+    { 0x43, "f9" },
+    { 0x44, "f10" },
+    { 0x45, "num_lock" },
+    { 0x46, "scroll_lock" },
+
+    { 0xb5, "kp_divide" },
+    { 0x37, "kp_multiply" },
+    { 0x4a, "kp_subtract" },
+    { 0x4e, "kp_add" },
+    { 0x9c, "kp_enter" },
+    { 0x53, "kp_decimal" },
+    { 0x54, "sysrq" },
+
+    { 0x52, "kp_0" },
+    { 0x4f, "kp_1" },
+    { 0x50, "kp_2" },
+    { 0x51, "kp_3" },
+    { 0x4b, "kp_4" },
+    { 0x4c, "kp_5" },
+    { 0x4d, "kp_6" },
+    { 0x47, "kp_7" },
+    { 0x48, "kp_8" },
+    { 0x49, "kp_9" },
+
+    { 0x56, "<" },
+
+    { 0x57, "f11" },
+    { 0x58, "f12" },
+
+    { 0xb7, "print" },
+
+    { 0xc7, "home" },
+    { 0xc9, "pgup" },
+    { 0xd1, "pgdn" },
+    { 0xcf, "end" },
+
+    { 0xcb, "left" },
+    { 0xc8, "up" },
+    { 0xd0, "down" },
+    { 0xcd, "right" },
+
+    { 0xd2, "insert" },
+    { 0xd3, "delete" },
+#if defined(TARGET_SPARC) && !defined(TARGET_SPARC64)
+    { 0xf0, "stop" },
+    { 0xf1, "again" },
+    { 0xf2, "props" },
+    { 0xf3, "undo" },
+    { 0xf4, "front" },
+    { 0xf5, "copy" },
+    { 0xf6, "open" },
+    { 0xf7, "paste" },
+    { 0xf8, "find" },
+    { 0xf9, "cut" },
+    { 0xfa, "lf" },
+    { 0xfb, "help" },
+    { 0xfc, "meta_l" },
+    { 0xfd, "meta_r" },
+    { 0xfe, "compose" },
+#endif
+    { 0, NULL },
+};
+
+static int get_keycode(const char *key)
+{
+    const KeyDef *p;
+    char *endp;
+    int ret;
+
+    for(p = key_defs; p->name != NULL; p++) {
+        if (!strcmp(key, p->name))
+            return p->keycode;
+    }
+    if (strstart(key, "0x", NULL)) {
+        ret = strtoul(key, &endp, 0);
+        if (*endp == '\0' && ret >= 0x01 && ret <= 0xff)
+            return ret;
+    }
+    return -1;
+}
+
+#define MAX_KEYCODES 16
+static uint8_t keycodes[MAX_KEYCODES];
+static int nb_pending_keycodes;
+static QEMUTimer *key_timer;
+
+static void release_keys(void *opaque)
+{
+    int keycode;
+
+    while (nb_pending_keycodes > 0) {
+        nb_pending_keycodes--;
+        keycode = keycodes[nb_pending_keycodes];
+        if (keycode & 0x80)
+            kbd_put_keycode(0xe0);
+        kbd_put_keycode(keycode | 0x80);
+    }
+}
+
+static void do_sendkey(const char *string, int has_hold_time, int hold_time)
+{
+    char keyname_buf[16];
+    char *separator;
+    int keyname_len, keycode, i;
+
+    if (nb_pending_keycodes > 0) {
+        qemu_del_timer(key_timer);
+        release_keys(NULL);
+    }
+    if (!has_hold_time)
+        hold_time = 100;
+    i = 0;
+    while (1) {
+        separator = strchr(string, '-');
+        keyname_len = separator ? separator - string : strlen(string);
+        if (keyname_len > 0) {
+            pstrcpy(keyname_buf, sizeof(keyname_buf), string);
+            if (keyname_len > sizeof(keyname_buf) - 1) {
+                term_printf("invalid key: '%s...'\n", keyname_buf);
+                return;
+            }
+            if (i == MAX_KEYCODES) {
+                term_printf("too many keys\n");
+                return;
+            }
+            keyname_buf[keyname_len] = 0;
+            keycode = get_keycode(keyname_buf);
+            if (keycode < 0) {
+                term_printf("unknown key: '%s'\n", keyname_buf);
+                return;
+            }
+            keycodes[i++] = keycode;
+        }
+        if (!separator)
+            break;
+        string = separator + 1;
+    }
+    nb_pending_keycodes = i;
+    /* key down events */
+    for (i = 0; i < nb_pending_keycodes; i++) {
+        keycode = keycodes[i];
+        if (keycode & 0x80)
+            kbd_put_keycode(0xe0);
+        kbd_put_keycode(keycode & 0x7f);
+    }
+    /* delayed key up events */
+    qemu_mod_timer(key_timer, qemu_get_clock(vm_clock) +
+                    muldiv64(ticks_per_sec, hold_time, 1000));
+}
+
+static int mouse_button_state;
+
+static void do_mouse_move(const char *dx_str, const char *dy_str,
+                          const char *dz_str)
+{
+    int dx, dy, dz;
+    dx = strtol(dx_str, NULL, 0);
+    dy = strtol(dy_str, NULL, 0);
+    dz = 0;
+    if (dz_str)
+        dz = strtol(dz_str, NULL, 0);
+    kbd_mouse_event(dx, dy, dz, mouse_button_state);
+}
+
+static void do_mouse_button(int button_state)
+{
+    mouse_button_state = button_state;
+    kbd_mouse_event(0, 0, 0, mouse_button_state);
+}
+
+static void do_ioport_read(int count, int format, int size, int addr, int has_index, int index)
+{
+    uint32_t val;
+    int suffix;
+
+    if (has_index) {
+        cpu_outb(NULL, addr & 0xffff, index & 0xff);
+        addr++;
+    }
+    addr &= 0xffff;
+
+    switch(size) {
+    default:
+    case 1:
+        val = cpu_inb(NULL, addr);
+        suffix = 'b';
+        break;
+    case 2:
+        val = cpu_inw(NULL, addr);
+        suffix = 'w';
+        break;
+    case 4:
+        val = cpu_inl(NULL, addr);
+        suffix = 'l';
+        break;
+    }
+    term_printf("port%c[0x%04x] = %#0*x\n",
+                suffix, addr, size * 2, val);
+}
+
+static void do_system_reset(void)
+{
+    qemu_system_reset_request();
+}
+
+static void do_system_powerdown(void)
+{
+    qemu_system_powerdown_request();
+}
+
+#if defined(TARGET_I386)
+static void print_pte(uint32_t addr, uint32_t pte, uint32_t mask)
+{
+    term_printf("%08x: %08x %c%c%c%c%c%c%c%c\n",
+                addr,
+                pte & mask,
+                pte & PG_GLOBAL_MASK ? 'G' : '-',
+                pte & PG_PSE_MASK ? 'P' : '-',
+                pte & PG_DIRTY_MASK ? 'D' : '-',
+                pte & PG_ACCESSED_MASK ? 'A' : '-',
+                pte & PG_PCD_MASK ? 'C' : '-',
+                pte & PG_PWT_MASK ? 'T' : '-',
+                pte & PG_USER_MASK ? 'U' : '-',
+                pte & PG_RW_MASK ? 'W' : '-');
+}
+
+static void tlb_info(void)
+{
+    CPUState *env;
+    int l1, l2;
+    uint32_t pgd, pde, pte;
+
+    env = mon_get_cpu();
+    if (!env)
+        return;
+
+    if (!(env->cr[0] & CR0_PG_MASK)) {
+        term_printf("PG disabled\n");
+        return;
+    }
+    pgd = env->cr[3] & ~0xfff;
+    for(l1 = 0; l1 < 1024; l1++) {
+        cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
+        pde = le32_to_cpu(pde);
+        if (pde & PG_PRESENT_MASK) {
+            if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+                print_pte((l1 << 22), pde, ~((1 << 20) - 1));
+            } else {
+                for(l2 = 0; l2 < 1024; l2++) {
+                    cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
+                                             (uint8_t *)&pte, 4);
+                    pte = le32_to_cpu(pte);
+                    if (pte & PG_PRESENT_MASK) {
+                        print_pte((l1 << 22) + (l2 << 12),
+                                  pte & ~PG_PSE_MASK,
+                                  ~0xfff);
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void mem_print(uint32_t *pstart, int *plast_prot,
+                      uint32_t end, int prot)
+{
+    int prot1;
+    prot1 = *plast_prot;
+    if (prot != prot1) {
+        if (*pstart != -1) {
+            term_printf("%08x-%08x %08x %c%c%c\n",
+                        *pstart, end, end - *pstart,
+                        prot1 & PG_USER_MASK ? 'u' : '-',
+                        'r',
+                        prot1 & PG_RW_MASK ? 'w' : '-');
+        }
+        if (prot != 0)
+            *pstart = end;
+        else
+            *pstart = -1;
+        *plast_prot = prot;
+    }
+}
+
+static void mem_info(void)
+{
+    CPUState *env;
+    int l1, l2, prot, last_prot;
+    uint32_t pgd, pde, pte, start, end;
+
+    env = mon_get_cpu();
+    if (!env)
+        return;
+
+    if (!(env->cr[0] & CR0_PG_MASK)) {
+        term_printf("PG disabled\n");
+        return;
+    }
+    pgd = env->cr[3] & ~0xfff;
+    last_prot = 0;
+    start = -1;
+    for(l1 = 0; l1 < 1024; l1++) {
+        cpu_physical_memory_read(pgd + l1 * 4, (uint8_t *)&pde, 4);
+        pde = le32_to_cpu(pde);
+        end = l1 << 22;
+        if (pde & PG_PRESENT_MASK) {
+            if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+                prot = pde & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
+                mem_print(&start, &last_prot, end, prot);
+            } else {
+                for(l2 = 0; l2 < 1024; l2++) {
+                    cpu_physical_memory_read((pde & ~0xfff) + l2 * 4,
+                                             (uint8_t *)&pte, 4);
+                    pte = le32_to_cpu(pte);
+                    end = (l1 << 22) + (l2 << 12);
+                    if (pte & PG_PRESENT_MASK) {
+                        prot = pte & (PG_USER_MASK | PG_RW_MASK | PG_PRESENT_MASK);
+                    } else {
+                        prot = 0;
+                    }
+                    mem_print(&start, &last_prot, end, prot);
+                }
+            }
+        } else {
+            prot = 0;
+            mem_print(&start, &last_prot, end, prot);
+        }
+    }
+}
+#endif
+
+static void do_info_kqemu(void)
+{
+#ifdef USE_KQEMU
+    CPUState *env;
+    int val;
+    val = 0;
+    env = mon_get_cpu();
+    if (!env) {
+        term_printf("No cpu initialized yet");
+        return;
+    }
+    val = env->kqemu_enabled;
+    term_printf("kqemu support: ");
+    switch(val) {
+    default:
+    case 0:
+        term_printf("disabled\n");
+        break;
+    case 1:
+        term_printf("enabled for user code\n");
+        break;
+    case 2:
+        term_printf("enabled for user and kernel code\n");
+        break;
+    }
+#else
+    term_printf("kqemu support: not compiled\n");
+#endif
+}
+
+#ifdef CONFIG_PROFILER
+
+int64_t kqemu_time;
+int64_t qemu_time;
+int64_t kqemu_exec_count;
+int64_t dev_time;
+int64_t kqemu_ret_int_count;
+int64_t kqemu_ret_excp_count;
+int64_t kqemu_ret_intr_count;
+
+static void do_info_profile(void)
+{
+    int64_t total;
+    total = qemu_time;
+    if (total == 0)
+        total = 1;
+    term_printf("async time  %" PRId64 " (%0.3f)\n",
+                dev_time, dev_time / (double)ticks_per_sec);
+    term_printf("qemu time   %" PRId64 " (%0.3f)\n",
+                qemu_time, qemu_time / (double)ticks_per_sec);
+    term_printf("kqemu time  %" PRId64 " (%0.3f %0.1f%%) count=%" PRId64 " int=%" PRId64 " excp=%" PRId64 " intr=%" PRId64 "\n",
+                kqemu_time, kqemu_time / (double)ticks_per_sec,
+                kqemu_time / (double)total * 100.0,
+                kqemu_exec_count,
+                kqemu_ret_int_count,
+                kqemu_ret_excp_count,
+                kqemu_ret_intr_count);
+    qemu_time = 0;
+    kqemu_time = 0;
+    kqemu_exec_count = 0;
+    dev_time = 0;
+    kqemu_ret_int_count = 0;
+    kqemu_ret_excp_count = 0;
+    kqemu_ret_intr_count = 0;
+#ifdef USE_KQEMU
+    kqemu_record_dump();
+#endif
+}
+#else
+static void do_info_profile(void)
+{
+    term_printf("Internal profiler not compiled\n");
+}
+#endif
+
+/* Capture support */
+static LIST_HEAD (capture_list_head, CaptureState) capture_head;
+
+static void do_info_capture (void)
+{
+    int i;
+    CaptureState *s;
+
+    for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+        term_printf ("[%d]: ", i);
+        s->ops.info (s->opaque);
+    }
+}
+
+static void do_stop_capture (int n)
+{
+    int i;
+    CaptureState *s;
+
+    for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) {
+        if (i == n) {
+            s->ops.destroy (s->opaque);
+            LIST_REMOVE (s, entries);
+            qemu_free (s);
+            return;
+        }
+    }
+}
+
+#ifdef HAS_AUDIO
+int wav_start_capture (CaptureState *s, const char *path, int freq,
+                       int bits, int nchannels);
+
+static void do_wav_capture (const char *path,
+                            int has_freq, int freq,
+                            int has_bits, int bits,
+                            int has_channels, int nchannels)
+{
+    CaptureState *s;
+
+    s = qemu_mallocz (sizeof (*s));
+    if (!s) {
+        term_printf ("Not enough memory to add wave capture\n");
+        return;
+    }
+
+    freq = has_freq ? freq : 44100;
+    bits = has_bits ? bits : 16;
+    nchannels = has_channels ? nchannels : 2;
+
+    if (wav_start_capture (s, path, freq, bits, nchannels)) {
+        term_printf ("Faied to add wave capture\n");
+        qemu_free (s);
+        return;
+    }
+    LIST_INSERT_HEAD (&capture_head, s, entries);
+}
+#endif
+
+static term_cmd_t term_cmds[] = {
+    { "help|?", "s?", do_help,
+      "[cmd]", "show the help" },
+    { "commit", "s", do_commit,
+      "device|all", "commit changes to the disk images (if -snapshot is used) or backing files" },
+    { "info", "s?", do_info,
+      "subcommand", "show various information about the system state" },
+    { "q|quit", "", do_quit,
+      "", "quit the emulator" },
+    { "eject", "-fB", do_eject,
+      "[-f] device", "eject a removable media (use -f to force it)" },
+    { "change", "BF", do_change,
+      "device filename", "change a removable media" },
+    { "screendump", "F", do_screen_dump,
+      "filename", "save screen into PPM image 'filename'" },
+    { "log", "s", do_log,
+      "item1[,...]", "activate logging of the specified items to '/tmp/qemu.log'" },
+#if 0
+    { "savevm", "F", do_savevm,
+      "filename", "save the whole virtual machine state to 'filename'" },
+    { "loadvm", "F", do_loadvm,
+      "filename", "restore the whole virtual machine state from 'filename'" },
+#endif
+    { "stop", "", do_stop,
+      "", "stop emulation", },
+    { "c|cont", "", do_cont,
+      "", "resume emulation", },
+#ifdef CONFIG_GDBSTUB
+    { "gdbserver", "s?", do_gdbserver,
+      "[port]", "start gdbserver session (default port=1234)", },
+#endif
+    { "x", "/l", do_memory_dump,
+      "/fmt addr", "virtual memory dump starting at 'addr'", },
+    { "xp", "/l", do_physical_memory_dump,
+      "/fmt addr", "physical memory dump starting at 'addr'", },
+    { "p|print", "/l", do_print,
+      "/fmt expr", "print expression value (use $reg for CPU register access)", },
+    { "i", "/ii.", do_ioport_read,
+      "/fmt addr", "I/O port read" },
+
+    { "sendkey", "si?", do_sendkey,
+      "keys [hold_ms]", "send keys to the VM (e.g. 'sendkey ctrl-alt-f1', default hold time=100 ms)" },
+    { "system_reset", "", do_system_reset,
+      "", "reset the system" },
+    { "system_powerdown", "", do_system_powerdown,
+      "", "send system power down event" },
+    { "sum", "ii", do_sum,
+      "addr size", "compute the checksum of a memory region" },
+    { "usb_add", "s", do_usb_add,
+      "device", "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')" },
+    { "usb_del", "s", do_usb_del,
+      "device", "remove USB device 'bus.addr'" },
+    { "cpu", "i", do_cpu_set,
+      "index", "set the default CPU" },
+    { "mouse_move", "sss?", do_mouse_move,
+      "dx dy [dz]", "send mouse move events" },
+    { "mouse_button", "i", do_mouse_button,
+      "state", "change mouse button state (1=L, 2=M, 4=R)" },
+#ifdef HAS_AUDIO
+    { "wavcapture", "si?i?i?", do_wav_capture,
+      "path [frequency bits channels]",
+      "capture audio to a wave file (default frequency=44100 bits=16 channels=2)" },
+#endif
+     { "stopcapture", "i", do_stop_capture,
+       "capture index", "stop capture" },
+    { NULL, NULL, },
+};
+
+static term_cmd_t info_cmds[] = {
+    { "version", "", do_info_version,
+      "", "show the version of qemu" },
+    { "network", "", do_info_network,
+      "", "show the network state" },
+    { "block", "", do_info_block,
+      "", "show the block devices" },
+    { "registers", "", do_info_registers,
+      "", "show the cpu registers" },
+    { "cpus", "", do_info_cpus,
+      "", "show infos for each CPU" },
+    { "history", "", do_info_history,
+      "", "show the command line history", },
+    { "irq", "", irq_info,
+      "", "show the interrupts statistics (if available)", },
+    { "pic", "", pic_info,
+      "", "show i8259 (PIC) state", },
+    { "pci", "", pci_info,
+      "", "show PCI info", },
+#if defined(TARGET_I386)
+    { "tlb", "", tlb_info,
+      "", "show virtual to physical memory mappings", },
+    { "mem", "", mem_info,
+      "", "show the active virtual memory mappings", },
+#endif
+    { "jit", "", do_info_jit,
+      "", "show dynamic compiler info", },
+    { "kqemu", "", do_info_kqemu,
+      "", "show kqemu information", },
+    { "usb", "", usb_info,
+      "", "show guest USB devices", },
+    { "usbhost", "", usb_host_info,
+      "", "show host USB devices", },
+    { "profile", "", do_info_profile,
+      "", "show profiling information", },
+    { "capture", "", do_info_capture,
+      "show capture information" },
+    { NULL, NULL, },
+};
+
+/*******************************************************************/
+
+static const char *pch;
+static jmp_buf expr_env;
+
+#define MD_TLONG 0
+#define MD_I32   1
+
+typedef struct MonitorDef {
+    const char *name;
+    int offset;
+    target_long (*get_value)(struct MonitorDef *md, int val);
+    int type;
+} MonitorDef;
+
+#if defined(TARGET_I386)
+static target_long monitor_get_pc (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return env->eip + env->segs[R_CS].base;
+}
+#endif
+
+#if defined(TARGET_PPC)
+static target_long monitor_get_ccr (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    unsigned int u;
+    int i;
+
+    if (!env)
+        return 0;
+
+    u = 0;
+    for (i = 0; i < 8; i++)
+	u |= env->crf[i] << (32 - (4 * i));
+
+    return u;
+}
+
+static target_long monitor_get_msr (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return env->msr;
+}
+
+static target_long monitor_get_xer (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return ppc_load_xer(env);
+}
+
+static target_long monitor_get_decr (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return cpu_ppc_load_decr(env);
+}
+
+static target_long monitor_get_tbu (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return cpu_ppc_load_tbu(env);
+}
+
+static target_long monitor_get_tbl (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return cpu_ppc_load_tbl(env);
+}
+#endif
+
+#if defined(TARGET_SPARC)
+#ifndef TARGET_SPARC64
+static target_long monitor_get_psr (struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return GET_PSR(env);
+}
+#endif
+
+static target_long monitor_get_reg(struct MonitorDef *md, int val)
+{
+    CPUState *env = mon_get_cpu();
+    if (!env)
+        return 0;
+    return env->regwptr[val];
+}
+#endif
+
+static MonitorDef monitor_defs[] = {
+#ifdef TARGET_I386
+
+#define SEG(name, seg) \
+    { name, offsetof(CPUState, segs[seg].selector), NULL, MD_I32 },\
+    { name ".base", offsetof(CPUState, segs[seg].base) },\
+    { name ".limit", offsetof(CPUState, segs[seg].limit), NULL, MD_I32 },
+
+    { "eax", offsetof(CPUState, regs[0]) },
+    { "ecx", offsetof(CPUState, regs[1]) },
+    { "edx", offsetof(CPUState, regs[2]) },
+    { "ebx", offsetof(CPUState, regs[3]) },
+    { "esp|sp", offsetof(CPUState, regs[4]) },
+    { "ebp|fp", offsetof(CPUState, regs[5]) },
+    { "esi", offsetof(CPUState, regs[6]) },
+    { "edi", offsetof(CPUState, regs[7]) },
+#ifdef TARGET_X86_64
+    { "r8", offsetof(CPUState, regs[8]) },
+    { "r9", offsetof(CPUState, regs[9]) },
+    { "r10", offsetof(CPUState, regs[10]) },
+    { "r11", offsetof(CPUState, regs[11]) },
+    { "r12", offsetof(CPUState, regs[12]) },
+    { "r13", offsetof(CPUState, regs[13]) },
+    { "r14", offsetof(CPUState, regs[14]) },
+    { "r15", offsetof(CPUState, regs[15]) },
+#endif
+    { "eflags", offsetof(CPUState, eflags) },
+    { "eip", offsetof(CPUState, eip) },
+    SEG("cs", R_CS)
+    SEG("ds", R_DS)
+    SEG("es", R_ES)
+    SEG("ss", R_SS)
+    SEG("fs", R_FS)
+    SEG("gs", R_GS)
+    { "pc", 0, monitor_get_pc, },
+#elif defined(TARGET_PPC)
+    /* General purpose registers */
+    { "r0", offsetof(CPUState, gpr[0]) },
+    { "r1", offsetof(CPUState, gpr[1]) },
+    { "r2", offsetof(CPUState, gpr[2]) },
+    { "r3", offsetof(CPUState, gpr[3]) },
+    { "r4", offsetof(CPUState, gpr[4]) },
+    { "r5", offsetof(CPUState, gpr[5]) },
+    { "r6", offsetof(CPUState, gpr[6]) },
+    { "r7", offsetof(CPUState, gpr[7]) },
+    { "r8", offsetof(CPUState, gpr[8]) },
+    { "r9", offsetof(CPUState, gpr[9]) },
+    { "r10", offsetof(CPUState, gpr[10]) },
+    { "r11", offsetof(CPUState, gpr[11]) },
+    { "r12", offsetof(CPUState, gpr[12]) },
+    { "r13", offsetof(CPUState, gpr[13]) },
+    { "r14", offsetof(CPUState, gpr[14]) },
+    { "r15", offsetof(CPUState, gpr[15]) },
+    { "r16", offsetof(CPUState, gpr[16]) },
+    { "r17", offsetof(CPUState, gpr[17]) },
+    { "r18", offsetof(CPUState, gpr[18]) },
+    { "r19", offsetof(CPUState, gpr[19]) },
+    { "r20", offsetof(CPUState, gpr[20]) },
+    { "r21", offsetof(CPUState, gpr[21]) },
+    { "r22", offsetof(CPUState, gpr[22]) },
+    { "r23", offsetof(CPUState, gpr[23]) },
+    { "r24", offsetof(CPUState, gpr[24]) },
+    { "r25", offsetof(CPUState, gpr[25]) },
+    { "r26", offsetof(CPUState, gpr[26]) },
+    { "r27", offsetof(CPUState, gpr[27]) },
+    { "r28", offsetof(CPUState, gpr[28]) },
+    { "r29", offsetof(CPUState, gpr[29]) },
+    { "r30", offsetof(CPUState, gpr[30]) },
+    { "r31", offsetof(CPUState, gpr[31]) },
+    /* Floating point registers */
+    { "f0", offsetof(CPUState, fpr[0]) },
+    { "f1", offsetof(CPUState, fpr[1]) },
+    { "f2", offsetof(CPUState, fpr[2]) },
+    { "f3", offsetof(CPUState, fpr[3]) },
+    { "f4", offsetof(CPUState, fpr[4]) },
+    { "f5", offsetof(CPUState, fpr[5]) },
+    { "f6", offsetof(CPUState, fpr[6]) },
+    { "f7", offsetof(CPUState, fpr[7]) },
+    { "f8", offsetof(CPUState, fpr[8]) },
+    { "f9", offsetof(CPUState, fpr[9]) },
+    { "f10", offsetof(CPUState, fpr[10]) },
+    { "f11", offsetof(CPUState, fpr[11]) },
+    { "f12", offsetof(CPUState, fpr[12]) },
+    { "f13", offsetof(CPUState, fpr[13]) },
+    { "f14", offsetof(CPUState, fpr[14]) },
+    { "f15", offsetof(CPUState, fpr[15]) },
+    { "f16", offsetof(CPUState, fpr[16]) },
+    { "f17", offsetof(CPUState, fpr[17]) },
+    { "f18", offsetof(CPUState, fpr[18]) },
+    { "f19", offsetof(CPUState, fpr[19]) },
+    { "f20", offsetof(CPUState, fpr[20]) },
+    { "f21", offsetof(CPUState, fpr[21]) },
+    { "f22", offsetof(CPUState, fpr[22]) },
+    { "f23", offsetof(CPUState, fpr[23]) },
+    { "f24", offsetof(CPUState, fpr[24]) },
+    { "f25", offsetof(CPUState, fpr[25]) },
+    { "f26", offsetof(CPUState, fpr[26]) },
+    { "f27", offsetof(CPUState, fpr[27]) },
+    { "f28", offsetof(CPUState, fpr[28]) },
+    { "f29", offsetof(CPUState, fpr[29]) },
+    { "f30", offsetof(CPUState, fpr[30]) },
+    { "f31", offsetof(CPUState, fpr[31]) },
+    { "fpscr", offsetof(CPUState, fpscr) },
+    /* Next instruction pointer */
+    { "nip|pc", offsetof(CPUState, nip) },
+    { "lr", offsetof(CPUState, lr) },
+    { "ctr", offsetof(CPUState, ctr) },
+    { "decr", 0, &monitor_get_decr, },
+    { "ccr", 0, &monitor_get_ccr, },
+    /* Machine state register */
+    { "msr", 0, &monitor_get_msr, },
+    { "xer", 0, &monitor_get_xer, },
+    { "tbu", 0, &monitor_get_tbu, },
+    { "tbl", 0, &monitor_get_tbl, },
+#if defined(TARGET_PPC64)
+    /* Address space register */
+    { "asr", offsetof(CPUState, asr) },
+#endif
+    /* Segment registers */
+    { "sdr1", offsetof(CPUState, sdr1) },
+    { "sr0", offsetof(CPUState, sr[0]) },
+    { "sr1", offsetof(CPUState, sr[1]) },
+    { "sr2", offsetof(CPUState, sr[2]) },
+    { "sr3", offsetof(CPUState, sr[3]) },
+    { "sr4", offsetof(CPUState, sr[4]) },
+    { "sr5", offsetof(CPUState, sr[5]) },
+    { "sr6", offsetof(CPUState, sr[6]) },
+    { "sr7", offsetof(CPUState, sr[7]) },
+    { "sr8", offsetof(CPUState, sr[8]) },
+    { "sr9", offsetof(CPUState, sr[9]) },
+    { "sr10", offsetof(CPUState, sr[10]) },
+    { "sr11", offsetof(CPUState, sr[11]) },
+    { "sr12", offsetof(CPUState, sr[12]) },
+    { "sr13", offsetof(CPUState, sr[13]) },
+    { "sr14", offsetof(CPUState, sr[14]) },
+    { "sr15", offsetof(CPUState, sr[15]) },
+    /* Too lazy to put BATs and SPRs ... */
+#elif defined(TARGET_SPARC)
+    { "g0", offsetof(CPUState, gregs[0]) },
+    { "g1", offsetof(CPUState, gregs[1]) },
+    { "g2", offsetof(CPUState, gregs[2]) },
+    { "g3", offsetof(CPUState, gregs[3]) },
+    { "g4", offsetof(CPUState, gregs[4]) },
+    { "g5", offsetof(CPUState, gregs[5]) },
+    { "g6", offsetof(CPUState, gregs[6]) },
+    { "g7", offsetof(CPUState, gregs[7]) },
+    { "o0", 0, monitor_get_reg },
+    { "o1", 1, monitor_get_reg },
+    { "o2", 2, monitor_get_reg },
+    { "o3", 3, monitor_get_reg },
+    { "o4", 4, monitor_get_reg },
+    { "o5", 5, monitor_get_reg },
+    { "o6", 6, monitor_get_reg },
+    { "o7", 7, monitor_get_reg },
+    { "l0", 8, monitor_get_reg },
+    { "l1", 9, monitor_get_reg },
+    { "l2", 10, monitor_get_reg },
+    { "l3", 11, monitor_get_reg },
+    { "l4", 12, monitor_get_reg },
+    { "l5", 13, monitor_get_reg },
+    { "l6", 14, monitor_get_reg },
+    { "l7", 15, monitor_get_reg },
+    { "i0", 16, monitor_get_reg },
+    { "i1", 17, monitor_get_reg },
+    { "i2", 18, monitor_get_reg },
+    { "i3", 19, monitor_get_reg },
+    { "i4", 20, monitor_get_reg },
+    { "i5", 21, monitor_get_reg },
+    { "i6", 22, monitor_get_reg },
+    { "i7", 23, monitor_get_reg },
+    { "pc", offsetof(CPUState, pc) },
+    { "npc", offsetof(CPUState, npc) },
+    { "y", offsetof(CPUState, y) },
+#ifndef TARGET_SPARC64
+    { "psr", 0, &monitor_get_psr, },
+    { "wim", offsetof(CPUState, wim) },
+#endif
+    { "tbr", offsetof(CPUState, tbr) },
+    { "fsr", offsetof(CPUState, fsr) },
+    { "f0", offsetof(CPUState, fpr[0]) },
+    { "f1", offsetof(CPUState, fpr[1]) },
+    { "f2", offsetof(CPUState, fpr[2]) },
+    { "f3", offsetof(CPUState, fpr[3]) },
+    { "f4", offsetof(CPUState, fpr[4]) },
+    { "f5", offsetof(CPUState, fpr[5]) },
+    { "f6", offsetof(CPUState, fpr[6]) },
+    { "f7", offsetof(CPUState, fpr[7]) },
+    { "f8", offsetof(CPUState, fpr[8]) },
+    { "f9", offsetof(CPUState, fpr[9]) },
+    { "f10", offsetof(CPUState, fpr[10]) },
+    { "f11", offsetof(CPUState, fpr[11]) },
+    { "f12", offsetof(CPUState, fpr[12]) },
+    { "f13", offsetof(CPUState, fpr[13]) },
+    { "f14", offsetof(CPUState, fpr[14]) },
+    { "f15", offsetof(CPUState, fpr[15]) },
+    { "f16", offsetof(CPUState, fpr[16]) },
+    { "f17", offsetof(CPUState, fpr[17]) },
+    { "f18", offsetof(CPUState, fpr[18]) },
+    { "f19", offsetof(CPUState, fpr[19]) },
+    { "f20", offsetof(CPUState, fpr[20]) },
+    { "f21", offsetof(CPUState, fpr[21]) },
+    { "f22", offsetof(CPUState, fpr[22]) },
+    { "f23", offsetof(CPUState, fpr[23]) },
+    { "f24", offsetof(CPUState, fpr[24]) },
+    { "f25", offsetof(CPUState, fpr[25]) },
+    { "f26", offsetof(CPUState, fpr[26]) },
+    { "f27", offsetof(CPUState, fpr[27]) },
+    { "f28", offsetof(CPUState, fpr[28]) },
+    { "f29", offsetof(CPUState, fpr[29]) },
+    { "f30", offsetof(CPUState, fpr[30]) },
+    { "f31", offsetof(CPUState, fpr[31]) },
+#ifdef TARGET_SPARC64
+    { "f32", offsetof(CPUState, fpr[32]) },
+    { "f34", offsetof(CPUState, fpr[34]) },
+    { "f36", offsetof(CPUState, fpr[36]) },
+    { "f38", offsetof(CPUState, fpr[38]) },
+    { "f40", offsetof(CPUState, fpr[40]) },
+    { "f42", offsetof(CPUState, fpr[42]) },
+    { "f44", offsetof(CPUState, fpr[44]) },
+    { "f46", offsetof(CPUState, fpr[46]) },
+    { "f48", offsetof(CPUState, fpr[48]) },
+    { "f50", offsetof(CPUState, fpr[50]) },
+    { "f52", offsetof(CPUState, fpr[52]) },
+    { "f54", offsetof(CPUState, fpr[54]) },
+    { "f56", offsetof(CPUState, fpr[56]) },
+    { "f58", offsetof(CPUState, fpr[58]) },
+    { "f60", offsetof(CPUState, fpr[60]) },
+    { "f62", offsetof(CPUState, fpr[62]) },
+    { "asi", offsetof(CPUState, asi) },
+    { "pstate", offsetof(CPUState, pstate) },
+    { "cansave", offsetof(CPUState, cansave) },
+    { "canrestore", offsetof(CPUState, canrestore) },
+    { "otherwin", offsetof(CPUState, otherwin) },
+    { "wstate", offsetof(CPUState, wstate) },
+    { "cleanwin", offsetof(CPUState, cleanwin) },
+    { "fprs", offsetof(CPUState, fprs) },
+#endif
+#elif defined(TARGET_ARM)
+    { "r0", offsetof(CPUState, regs[0]) },
+    { "r1", offsetof(CPUState, regs[1]) },
+    { "r2", offsetof(CPUState, regs[2]) },
+    { "r3", offsetof(CPUState, regs[3]) },
+    { "r4", offsetof(CPUState, regs[4]) },
+    { "r5", offsetof(CPUState, regs[5]) },
+    { "r6", offsetof(CPUState, regs[6]) },
+    { "r7", offsetof(CPUState, regs[7]) },
+    { "r8", offsetof(CPUState, regs[8]) },
+    { "r9", offsetof(CPUState, regs[9]) },
+    { "r10", offsetof(CPUState, regs[10]) },
+    { "r11", offsetof(CPUState, regs[11]) },
+    { "r12", offsetof(CPUState, regs[12]) },
+    { "r13", offsetof(CPUState, regs[13]) },
+    { "r14", offsetof(CPUState, regs[14]) },
+    { "r15", offsetof(CPUState, regs[15]) },
+    /* some interesting aliases */
+    { "sp", offsetof(CPUState, regs[13]) },
+    { "lr", offsetof(CPUState, regs[14]) },
+    { "pc", offsetof(CPUState, regs[15]) },
+#endif
+    { NULL },
+};
+
+static void expr_error(const char *fmt)
+{
+    term_printf(fmt);
+    term_printf("\n");
+    longjmp(expr_env, 1);
+}
+
+/* return 0 if OK, -1 if not found, -2 if no CPU defined */
+static int get_monitor_def(target_long *pval, const char *name)
+{
+    MonitorDef *md;
+    void *ptr;
+
+    for(md = monitor_defs; md->name != NULL; md++) {
+        if (compare_cmd(name, md->name)) {
+            if (md->get_value) {
+                *pval = md->get_value(md, md->offset);
+            } else {
+                CPUState *env = mon_get_cpu();
+                if (!env)
+                    return -2;
+                ptr = (uint8_t *)env + md->offset;
+                switch(md->type) {
+                case MD_I32:
+                    *pval = *(int32_t *)ptr;
+                    break;
+                case MD_TLONG:
+                    *pval = *(target_long *)ptr;
+                    break;
+                default:
+                    *pval = 0;
+                    break;
+                }
+            }
+            return 0;
+        }
+    }
+    return -1;
+}
+
+static void next(void)
+{
+    if (pch != '\0') {
+        pch++;
+        while (isspace(*pch))
+            pch++;
+    }
+}
+
+static int64_t expr_sum(void);
+
+static int64_t expr_unary(void)
+{
+    int64_t n;
+    char *p;
+    int ret;
+
+    switch(*pch) {
+    case '+':
+        next();
+        n = expr_unary();
+        break;
+    case '-':
+        next();
+        n = -expr_unary();
+        break;
+    case '~':
+        next();
+        n = ~expr_unary();
+        break;
+    case '(':
+        next();
+        n = expr_sum();
+        if (*pch != ')') {
+            expr_error("')' expected");
+        }
+        next();
+        break;
+    case '\'':
+        pch++;
+        if (*pch == '\0')
+            expr_error("character constant expected");
+        n = *pch;
+        pch++;
+        if (*pch != '\'')
+            expr_error("missing terminating \' character");
+        next();
+        break;
+    case '$':
+        {
+            char buf[128], *q;
+            target_long reg=0;
+
+            pch++;
+            q = buf;
+            while ((*pch >= 'a' && *pch <= 'z') ||
+                   (*pch >= 'A' && *pch <= 'Z') ||
+                   (*pch >= '0' && *pch <= '9') ||
+                   *pch == '_' || *pch == '.') {
+                if ((q - buf) < sizeof(buf) - 1)
+                    *q++ = *pch;
+                pch++;
+            }
+            while (isspace(*pch))
+                pch++;
+            *q = 0;
+            ret = get_monitor_def(&reg, buf);
+            if (ret == -1)
+                expr_error("unknown register");
+            else if (ret == -2)
+                expr_error("no cpu defined");
+            n = reg;
+        }
+        break;
+    case '\0':
+        expr_error("unexpected end of expression");
+        n = 0;
+        break;
+    default:
+#if TARGET_PHYS_ADDR_BITS > 32
+        n = strtoull(pch, &p, 0);
+#else
+        n = strtoul(pch, &p, 0);
+#endif
+        if (pch == p) {
+            expr_error("invalid char in expression");
+        }
+        pch = p;
+        while (isspace(*pch))
+            pch++;
+        break;
+    }
+    return n;
+}
+
+
+static int64_t expr_prod(void)
+{
+    int64_t val, val2;
+    int op;
+
+    val = expr_unary();
+    for(;;) {
+        op = *pch;
+        if (op != '*' && op != '/' && op != '%')
+            break;
+        next();
+        val2 = expr_unary();
+        switch(op) {
+        default:
+        case '*':
+            val *= val2;
+            break;
+        case '/':
+        case '%':
+            if (val2 == 0)
+                expr_error("division by zero");
+            if (op == '/')
+                val /= val2;
+            else
+                val %= val2;
+            break;
+        }
+    }
+    return val;
+}
+
+static int64_t expr_logic(void)
+{
+    int64_t val, val2;
+    int op;
+
+    val = expr_prod();
+    for(;;) {
+        op = *pch;
+        if (op != '&' && op != '|' && op != '^')
+            break;
+        next();
+        val2 = expr_prod();
+        switch(op) {
+        default:
+        case '&':
+            val &= val2;
+            break;
+        case '|':
+            val |= val2;
+            break;
+        case '^':
+            val ^= val2;
+            break;
+        }
+    }
+    return val;
+}
+
+static int64_t expr_sum(void)
+{
+    int64_t val, val2;
+    int op;
+
+    val = expr_logic();
+    for(;;) {
+        op = *pch;
+        if (op != '+' && op != '-')
+            break;
+        next();
+        val2 = expr_logic();
+        if (op == '+')
+            val += val2;
+        else
+            val -= val2;
+    }
+    return val;
+}
+
+static int get_expr(int64_t *pval, const char **pp)
+{
+    pch = *pp;
+    if (setjmp(expr_env)) {
+        *pp = pch;
+        return -1;
+    }
+    while (isspace(*pch))
+        pch++;
+    *pval = expr_sum();
+    *pp = pch;
+    return 0;
+}
+
+static int get_str(char *buf, int buf_size, const char **pp)
+{
+    const char *p;
+    char *q;
+    int c;
+
+    q = buf;
+    p = *pp;
+    while (isspace(*p))
+        p++;
+    if (*p == '\0') {
+    fail:
+        *q = '\0';
+        *pp = p;
+        return -1;
+    }
+    if (*p == '\"') {
+        p++;
+        while (*p != '\0' && *p != '\"') {
+            if (*p == '\\') {
+                p++;
+                c = *p++;
+                switch(c) {
+                case 'n':
+                    c = '\n';
+                    break;
+                case 'r':
+                    c = '\r';
+                    break;
+                case '\\':
+                case '\'':
+                case '\"':
+                    break;
+                default:
+                    qemu_printf("unsupported escape code: '\\%c'\n", c);
+                    goto fail;
+                }
+                if ((q - buf) < buf_size - 1) {
+                    *q++ = c;
+                }
+            } else {
+                if ((q - buf) < buf_size - 1) {
+                    *q++ = *p;
+                }
+                p++;
+            }
+        }
+        if (*p != '\"') {
+            qemu_printf("unterminated string\n");
+            goto fail;
+        }
+        p++;
+    } else {
+        while (*p != '\0' && !isspace(*p)) {
+            if ((q - buf) < buf_size - 1) {
+                *q++ = *p;
+            }
+            p++;
+        }
+    }
+    *q = '\0';
+    *pp = p;
+    return 0;
+}
+
+static int default_fmt_format = 'x';
+static int default_fmt_size = 4;
+
+#define MAX_ARGS 16
+
+static void monitor_handle_command(const char *cmdline)
+{
+    const char *p, *pstart, *typestr;
+    char *q;
+    int c, nb_args, len, i, has_arg;
+    term_cmd_t *cmd;
+    char cmdname[256];
+    char buf[1024];
+    void *str_allocated[MAX_ARGS];
+    void *args[MAX_ARGS];
+    void (*handler_0)(void);
+    void (*handler_1)(void *arg0);
+    void (*handler_2)(void *arg0, void *arg1);
+    void (*handler_3)(void *arg0, void *arg1, void *arg2);
+    void (*handler_4)(void *arg0, void *arg1, void *arg2, void *arg3);
+    void (*handler_5)(void *arg0, void *arg1, void *arg2, void *arg3,
+                      void *arg4);
+    void (*handler_6)(void *arg0, void *arg1, void *arg2, void *arg3,
+                      void *arg4, void *arg5);
+    void (*handler_7)(void *arg0, void *arg1, void *arg2, void *arg3,
+                      void *arg4, void *arg5, void *arg6);
+
+#ifdef DEBUG
+    term_printf("command='%s'\n", cmdline);
+#endif
+
+    /* extract the command name */
+    p = cmdline;
+    q = cmdname;
+    while (isspace(*p))
+        p++;
+    if (*p == '\0')
+        return;
+    pstart = p;
+    while (*p != '\0' && *p != '/' && !isspace(*p))
+        p++;
+    len = p - pstart;
+    if (len > sizeof(cmdname) - 1)
+        len = sizeof(cmdname) - 1;
+    memcpy(cmdname, pstart, len);
+    cmdname[len] = '\0';
+
+    /* find the command */
+    for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+        if (compare_cmd(cmdname, cmd->name))
+            goto found;
+    }
+    term_printf("unknown command: '%s'\n", cmdname);
+    return;
+ found:
+
+    for(i = 0; i < MAX_ARGS; i++)
+        str_allocated[i] = NULL;
+
+    /* parse the parameters */
+    typestr = cmd->args_type;
+    nb_args = 0;
+    for(;;) {
+        c = *typestr;
+        if (c == '\0')
+            break;
+        typestr++;
+        switch(c) {
+        case 'F':
+        case 'B':
+        case 's':
+            {
+                int ret;
+                char *str;
+
+                while (isspace(*p))
+                    p++;
+                if (*typestr == '?') {
+                    typestr++;
+                    if (*p == '\0') {
+                        /* no optional string: NULL argument */
+                        str = NULL;
+                        goto add_str;
+                    }
+                }
+                ret = get_str(buf, sizeof(buf), &p);
+                if (ret < 0) {
+                    switch(c) {
+                    case 'F':
+                        term_printf("%s: filename expected\n", cmdname);
+                        break;
+                    case 'B':
+                        term_printf("%s: block device name expected\n", cmdname);
+                        break;
+                    default:
+                        term_printf("%s: string expected\n", cmdname);
+                        break;
+                    }
+                    goto fail;
+                }
+                str = qemu_malloc(strlen(buf) + 1);
+                pstrcpy(str, sizeof(buf), buf);
+                str_allocated[nb_args] = str;
+            add_str:
+                if (nb_args >= MAX_ARGS) {
+                error_args:
+                    term_printf("%s: too many arguments\n", cmdname);
+                    goto fail;
+                }
+                args[nb_args++] = str;
+            }
+            break;
+        case '/':
+            {
+                int count, format, size;
+
+                while (isspace(*p))
+                    p++;
+                if (*p == '/') {
+                    /* format found */
+                    p++;
+                    count = 1;
+                    if (isdigit(*p)) {
+                        count = 0;
+                        while (isdigit(*p)) {
+                            count = count * 10 + (*p - '0');
+                            p++;
+                        }
+                    }
+                    size = -1;
+                    format = -1;
+                    for(;;) {
+                        switch(*p) {
+                        case 'o':
+                        case 'd':
+                        case 'u':
+                        case 'x':
+                        case 'i':
+                        case 'c':
+                            format = *p++;
+                            break;
+                        case 'b':
+                            size = 1;
+                            p++;
+                            break;
+                        case 'h':
+                            size = 2;
+                            p++;
+                            break;
+                        case 'w':
+                            size = 4;
+                            p++;
+                            break;
+                        case 'g':
+                        case 'L':
+                            size = 8;
+                            p++;
+                            break;
+                        default:
+                            goto next;
+                        }
+                    }
+                next:
+                    if (*p != '\0' && !isspace(*p)) {
+                        term_printf("invalid char in format: '%c'\n", *p);
+                        goto fail;
+                    }
+                    if (format < 0)
+                        format = default_fmt_format;
+                    if (format != 'i') {
+                        /* for 'i', not specifying a size gives -1 as size */
+                        if (size < 0)
+                            size = default_fmt_size;
+                    }
+                    default_fmt_size = size;
+                    default_fmt_format = format;
+                } else {
+                    count = 1;
+                    format = default_fmt_format;
+                    if (format != 'i') {
+                        size = default_fmt_size;
+                    } else {
+                        size = -1;
+                    }
+                }
+                if (nb_args + 3 > MAX_ARGS)
+                    goto error_args;
+                args[nb_args++] = (void*)(long)count;
+                args[nb_args++] = (void*)(long)format;
+                args[nb_args++] = (void*)(long)size;
+            }
+            break;
+        case 'i':
+        case 'l':
+            {
+                int64_t val;
+
+                while (isspace(*p))
+                    p++;
+                if (*typestr == '?' || *typestr == '.') {
+                    if (*typestr == '?') {
+                        if (*p == '\0')
+                            has_arg = 0;
+                        else
+                            has_arg = 1;
+                    } else {
+                        if (*p == '.') {
+                            p++;
+                            while (isspace(*p))
+                                p++;
+                            has_arg = 1;
+                        } else {
+                            has_arg = 0;
+                        }
+                    }
+                    typestr++;
+                    if (nb_args >= MAX_ARGS)
+                        goto error_args;
+                    args[nb_args++] = (void *)(long)has_arg;
+                    if (!has_arg) {
+                        if (nb_args >= MAX_ARGS)
+                            goto error_args;
+                        val = -1;
+                        goto add_num;
+                    }
+                }
+                if (get_expr(&val, &p))
+                    goto fail;
+            add_num:
+                if (c == 'i') {
+                    if (nb_args >= MAX_ARGS)
+                        goto error_args;
+                    args[nb_args++] = (void *)(long)val;
+                } else {
+                    if ((nb_args + 1) >= MAX_ARGS)
+                        goto error_args;
+#if TARGET_PHYS_ADDR_BITS > 32
+                    args[nb_args++] = (void *)(long)((val >> 32) & 0xffffffff);
+#else
+                    args[nb_args++] = (void *)0;
+#endif
+                    args[nb_args++] = (void *)(long)(val & 0xffffffff);
+                }
+            }
+            break;
+        case '-':
+            {
+                int has_option;
+                /* option */
+
+                c = *typestr++;
+                if (c == '\0')
+                    goto bad_type;
+                while (isspace(*p))
+                    p++;
+                has_option = 0;
+                if (*p == '-') {
+                    p++;
+                    if (*p != c) {
+                        term_printf("%s: unsupported option -%c\n",
+                                    cmdname, *p);
+                        goto fail;
+                    }
+                    p++;
+                    has_option = 1;
+                }
+                if (nb_args >= MAX_ARGS)
+                    goto error_args;
+                args[nb_args++] = (void *)(long)has_option;
+            }
+            break;
+        default:
+        bad_type:
+            term_printf("%s: unknown type '%c'\n", cmdname, c);
+            goto fail;
+        }
+    }
+    /* check that all arguments were parsed */
+    while (isspace(*p))
+        p++;
+    if (*p != '\0') {
+        term_printf("%s: extraneous characters at the end of line\n",
+                    cmdname);
+        goto fail;
+    }
+
+    switch(nb_args) {
+    case 0:
+        handler_0 = cmd->handler;
+        handler_0();
+        break;
+    case 1:
+        handler_1 = cmd->handler;
+        handler_1(args[0]);
+        break;
+    case 2:
+        handler_2 = cmd->handler;
+        handler_2(args[0], args[1]);
+        break;
+    case 3:
+        handler_3 = cmd->handler;
+        handler_3(args[0], args[1], args[2]);
+        break;
+    case 4:
+        handler_4 = cmd->handler;
+        handler_4(args[0], args[1], args[2], args[3]);
+        break;
+    case 5:
+        handler_5 = cmd->handler;
+        handler_5(args[0], args[1], args[2], args[3], args[4]);
+        break;
+    case 6:
+        handler_6 = cmd->handler;
+        handler_6(args[0], args[1], args[2], args[3], args[4], args[5]);
+        break;
+    case 7:
+        handler_7 = cmd->handler;
+        handler_7(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+        break;
+    default:
+        term_printf("unsupported number of arguments: %d\n", nb_args);
+        goto fail;
+    }
+ fail:
+    for(i = 0; i < MAX_ARGS; i++)
+        qemu_free(str_allocated[i]);
+    return;
+}
+
+static void cmd_completion(const char *name, const char *list)
+{
+    const char *p, *pstart;
+    char cmd[128];
+    int len;
+
+    p = list;
+    for(;;) {
+        pstart = p;
+        p = strchr(p, '|');
+        if (!p)
+            p = pstart + strlen(pstart);
+        len = p - pstart;
+        if (len > sizeof(cmd) - 2)
+            len = sizeof(cmd) - 2;
+        memcpy(cmd, pstart, len);
+        cmd[len] = '\0';
+        if (name[0] == '\0' || !strncmp(name, cmd, strlen(name))) {
+            add_completion(cmd);
+        }
+        if (*p == '\0')
+            break;
+        p++;
+    }
+}
+
+static void file_completion(const char *input)
+{
+    DIR *ffs;
+    struct dirent *d;
+    char path[1024];
+    char file[1024], file_prefix[1024];
+    int input_path_len;
+    const char *p;
+
+    p = strrchr(input, '/');
+    if (!p) {
+        input_path_len = 0;
+        pstrcpy(file_prefix, sizeof(file_prefix), input);
+        pstrcpy(path, sizeof(path), ".");
+    } else {
+        input_path_len = p - input + 1;
+        memcpy(path, input, input_path_len);
+        if (input_path_len > sizeof(path) - 1)
+            input_path_len = sizeof(path) - 1;
+        path[input_path_len] = '\0';
+        pstrcpy(file_prefix, sizeof(file_prefix), p + 1);
+    }
+#ifdef DEBUG_COMPLETION
+    term_printf("input='%s' path='%s' prefix='%s'\n", input, path, file_prefix);
+#endif
+    ffs = opendir(path);
+    if (!ffs)
+        return;
+    for(;;) {
+        struct stat sb;
+        d = readdir(ffs);
+        if (!d)
+            break;
+        if (strstart(d->d_name, file_prefix, NULL)) {
+            memcpy(file, input, input_path_len);
+            if (input_path_len < sizeof(file))
+                pstrcpy(file + input_path_len, sizeof(file) - input_path_len,
+                        d->d_name);
+            /* stat the file to find out if it's a directory.
+             * In that case add a slash to speed up typing long paths
+             */
+            stat(file, &sb);
+            if(S_ISDIR(sb.st_mode))
+                pstrcat(file, sizeof(file), "/");
+            add_completion(file);
+        }
+    }
+    closedir(ffs);
+}
+
+static void block_completion_it(void *opaque, const char *name)
+{
+    const char *input = opaque;
+
+    if (input[0] == '\0' ||
+        !strncmp(name, (char *)input, strlen(input))) {
+        add_completion(name);
+    }
+}
+
+/* NOTE: this parser is an approximate form of the real command parser */
+static void parse_cmdline(const char *cmdline,
+                         int *pnb_args, char **args)
+{
+    const char *p;
+    int nb_args, ret;
+    char buf[1024];
+
+    p = cmdline;
+    nb_args = 0;
+    for(;;) {
+        while (isspace(*p))
+            p++;
+        if (*p == '\0')
+            break;
+        if (nb_args >= MAX_ARGS)
+            break;
+        ret = get_str(buf, sizeof(buf), &p);
+        args[nb_args] = qemu_strdup(buf);
+        nb_args++;
+        if (ret < 0)
+            break;
+    }
+    *pnb_args = nb_args;
+}
+
+void readline_find_completion(const char *cmdline)
+{
+    const char *cmdname;
+    char *args[MAX_ARGS];
+    int nb_args, i, len;
+    const char *ptype, *str;
+    term_cmd_t *cmd;
+    const KeyDef *key;
+
+    parse_cmdline(cmdline, &nb_args, args);
+#ifdef DEBUG_COMPLETION
+    for(i = 0; i < nb_args; i++) {
+        term_printf("arg%d = '%s'\n", i, (char *)args[i]);
+    }
+#endif
+
+    /* if the line ends with a space, it means we want to complete the
+       next arg */
+    len = strlen(cmdline);
+    if (len > 0 && isspace(cmdline[len - 1])) {
+        if (nb_args >= MAX_ARGS)
+            return;
+        args[nb_args++] = qemu_strdup("");
+    }
+    if (nb_args <= 1) {
+        /* command completion */
+        if (nb_args == 0)
+            cmdname = "";
+        else
+            cmdname = args[0];
+        completion_index = strlen(cmdname);
+        for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+            cmd_completion(cmdname, cmd->name);
+        }
+    } else {
+        /* find the command */
+        for(cmd = term_cmds; cmd->name != NULL; cmd++) {
+            if (compare_cmd(args[0], cmd->name))
+                goto found;
+        }
+        return;
+    found:
+        ptype = cmd->args_type;
+        for(i = 0; i < nb_args - 2; i++) {
+            if (*ptype != '\0') {
+                ptype++;
+                while (*ptype == '?')
+                    ptype++;
+            }
+        }
+        str = args[nb_args - 1];
+        switch(*ptype) {
+        case 'F':
+            /* file completion */
+            completion_index = strlen(str);
+            file_completion(str);
+            break;
+        case 'B':
+            /* block device name completion */
+            completion_index = strlen(str);
+            bdrv_iterate(block_completion_it, (void *)str);
+            break;
+        case 's':
+            /* XXX: more generic ? */
+            if (!strcmp(cmd->name, "info")) {
+                completion_index = strlen(str);
+                for(cmd = info_cmds; cmd->name != NULL; cmd++) {
+                    cmd_completion(str, cmd->name);
+                }
+            } else if (!strcmp(cmd->name, "sendkey")) {
+                completion_index = strlen(str);
+                for(key = key_defs; key->name != NULL; key++) {
+                    cmd_completion(str, key->name);
+                }
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    for(i = 0; i < nb_args; i++)
+        qemu_free(args[i]);
+}
+
+static int term_can_read(void *opaque)
+{
+    return 128;
+}
+
+static void term_read(void *opaque, const uint8_t *buf, int size)
+{
+    int i;
+    for(i = 0; i < size; i++)
+        readline_handle_byte(buf[i]);
+}
+
+static void monitor_start_input(void);
+
+static void monitor_handle_command1(void *opaque, const char *cmdline)
+{
+    monitor_handle_command(cmdline);
+    monitor_start_input();
+}
+
+static void monitor_start_input(void)
+{
+    readline_start("(qemu) ", 0, monitor_handle_command1, NULL);
+}
+
+static void term_event(void *opaque, int event)
+{
+    if (event != CHR_EVENT_RESET)
+	return;
+
+    if (!hide_banner)
+	    term_printf("QEMU %s monitor - type 'help' for more information\n",
+			QEMU_VERSION);
+    monitor_start_input();
+}
+
+static int is_first_init = 1;
+
+void monitor_init(CharDriverState *hd, int show_banner)
+{
+    int i;
+
+    if (is_first_init) {
+        key_timer = qemu_new_timer(vm_clock, release_keys, NULL);
+        if (!key_timer)
+            return;
+        for (i = 0; i < MAX_MON; i++) {
+            monitor_hd[i] = NULL;
+        }
+        is_first_init = 0;
+    }
+    for (i = 0; i < MAX_MON; i++) {
+        if (monitor_hd[i] == NULL) {
+            monitor_hd[i] = hd;
+            break;
+        }
+    }
+
+    hide_banner = !show_banner;
+
+    qemu_chr_add_handlers(hd, term_can_read, term_read, term_event, NULL);
+
+    readline_start("", 0, monitor_handle_command1, NULL);
+}
+
+/* XXX: use threads ? */
+/* modal monitor readline */
+static int monitor_readline_started;
+static char *monitor_readline_buf;
+static int monitor_readline_buf_size;
+
+static void monitor_readline_cb(void *opaque, const char *input)
+{
+    pstrcpy(monitor_readline_buf, monitor_readline_buf_size, input);
+    monitor_readline_started = 0;
+}
+
+void monitor_readline(const char *prompt, int is_password,
+                      char *buf, int buf_size)
+{
+    int i;
+    int old_focus[MAX_MON];
+
+    if (is_password) {
+        for (i = 0; i < MAX_MON; i++) {
+            old_focus[i] = 0;
+            if (monitor_hd[i]) {
+                old_focus[i] = monitor_hd[i]->focus;
+                monitor_hd[i]->focus = 0;
+                qemu_chr_send_event(monitor_hd[i], CHR_EVENT_FOCUS);
+            }
+        }
+    }
+
+    readline_start(prompt, is_password, monitor_readline_cb, NULL);
+    monitor_readline_buf = buf;
+    monitor_readline_buf_size = buf_size;
+    monitor_readline_started = 1;
+    while (monitor_readline_started) {
+        main_loop_wait(10);
+    }
+    /* restore original focus */
+    if (is_password) {
+        for (i = 0; i < MAX_MON; i++)
+            if (old_focus[i])
+                monitor_hd[i]->focus = old_focus[i];
+    }
+}
diff --git a/net.h b/net.h
new file mode 100644
index 0000000..5212b48
--- /dev/null
+++ b/net.h
@@ -0,0 +1,58 @@
+#ifndef QEMU_NET_H
+#define QEMU_NET_H
+
+/* VLANs support */
+
+typedef struct VLANClientState VLANClientState;
+
+struct VLANClientState {
+    IOReadHandler *fd_read;
+    /* Packets may still be sent if this returns zero.  It's used to
+       rate-limit the slirp code.  */
+    IOCanRWHandler *fd_can_read;
+    void *opaque;
+    struct VLANClientState *next;
+    struct VLANState *vlan;
+    char info_str[256];
+};
+
+struct VLANState {
+    int id;
+    VLANClientState *first_client;
+    struct VLANState *next;
+    unsigned int nb_guest_devs, nb_host_devs;
+};
+
+VLANState *qemu_find_vlan(int id);
+VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+                                      IOReadHandler *fd_read,
+                                      IOCanRWHandler *fd_can_read,
+                                      void *opaque);
+void qemu_del_vlan_client(VLANClientState *vc);
+int qemu_can_send_packet(VLANClientState *vc);
+void qemu_send_packet(VLANClientState *vc, const uint8_t *buf, int size);
+void qemu_handler_true(void *opaque);
+
+void do_info_network(void);
+
+/* NIC info */
+
+#define MAX_NICS 8
+
+struct NICInfo {
+    uint8_t macaddr[6];
+    const char *model;
+    VLANState *vlan;
+};
+
+extern int nb_nics;
+extern NICInfo nd_table[MAX_NICS];
+
+/* checksumming functions (net-checksum.c) */
+uint32_t net_checksum_add(int len, uint8_t *buf);
+uint16_t net_checksum_finish(uint32_t sum);
+uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
+                             uint8_t *addrs, uint8_t *buf);
+void net_checksum_calculate(uint8_t *data, int length);
+
+#endif
diff --git a/offset_layout.py b/offset_layout.py
new file mode 100755
index 0000000..6c2f879
--- /dev/null
+++ b/offset_layout.py
@@ -0,0 +1,111 @@
+#!/usr/bin/python
+
+import re
+import sys
+import getopt
+
+DX = DY = PX = PY = KX = KY = 0
+
+_RE_LINE = re.compile("^\s*(?P<keyword>[\w-]+)\s+{\s*$")
+_RE_XY = re.compile("^(?P<start>\s*)(?P<xy>[x|y]\s+)(?P<num>\d+)(?P<end>\s*)$")
+
+def main():
+  ParseArgs()
+  ParseInput()
+
+def Usage():
+  print >>sys.stderr, """
+  Usage: %s --dx offset-x-display --dy offset-y-display --px offset-x-phone-buttons --py offset-y-phone-buttons --kx offset-x-keyboard --ky offset-y-keyboard < layout > layout2.
+
+  Unspecified offsets default to 0 (unchanged).
+  Reads from stdin, outputs to stdout.
+  Phone buttons: soft-left/top/righ/bottom, home, dpad, dial, power, etc.
+  Keyboard is the soft keyboard.
+
+  If your shell doesn't let you use negative integers, use _ for minus sign,
+  i.e. --dx _40 --dy _42 for <-40,-42).
+  """ % (sys.argv[0])
+  sys.exit(1)
+
+def ParseArgs():
+  global DX, DY, PX, PY, KX, KY
+  try:
+    options, args = getopt.getopt(sys.argv[1:], "", ["dx=", "dy=", "px=", "py=", "kx=", "ky="])
+    for opt, value in options:
+      if opt in ["--dx"]:
+        DX = int(value.replace("_", "-"))
+      elif opt in ["--dy"]:
+        DY = int(value.replace("_", "-"))
+      elif opt in ["--px"]:
+        PX = int(value.replace("_", "-"))
+      elif opt in ["--py"]:
+        PY = int(value.replace("_", "-"))
+      elif opt in ["--kx"]:
+        KX = int(value.replace("_", "-"))
+      elif opt in ["--ky"]:
+        KY = int(value.replace("_", "-"))
+      else:
+        Usage()
+  except getopt.error, msg:
+    Usage()
+
+def ParseInput():
+  global DX, DY, PX, PY, KX, KY
+
+  PHONE = [	"soft-left", "home", "back", "dpad-up", "dpad-down", "dpad-left", "dpad-right", "dpad-center", "phone-dial", "phone-hangup", "power", "volume-up", "volume-down" ]
+  KEYBOARD = [   "DEL", "CAP", "CAP2", "PERIOD", "ENTER", "ALT", "SYM", "AT", "SPACE", "SLASH", "COMMA", "ALT2" ]
+
+  mode = None
+  while True:
+    line = sys.stdin.readline()
+    if not line:
+      return
+    m_line = _RE_LINE.match(line)
+    if m_line:
+      keyword = m_line.group("keyword")
+      if keyword in ["display", "button"]:
+        mode = keyword
+        is_phone = False
+        is_keyboard = False
+        print >>sys.stderr, "Mode:", mode
+      else:
+        if mode == "button" and "{" in line:
+          is_phone = keyword in PHONE
+          is_keyboard = (len(keyword) == 1 and keyword.isalnum())
+          if not is_keyboard:
+            is_keyboard = keyword in KEYBOARD
+    elif "}" in line:
+      is_phone = False
+      is_keyboard = False
+      if mode == "display":
+        mode = None
+    else:
+      m_xy = _RE_XY.match(line)
+      if m_xy:
+        x = 0
+        y = 0
+        if mode == "display":
+          x = DX
+          y = DY
+        elif mode == "button" and is_phone:
+          x = PX
+          y = PY
+        elif mode == "button" and is_keyboard:
+          x = KX
+          y = KY
+        if x or y:
+          d = m_xy.groupdict()
+          n = int(d["num"])
+          if d["xy"].startswith("x"):
+            n += x
+          else:
+            n += y
+          d["num"] = n
+          line = "%(start)s%(xy)s%(num)s%(end)s" % d
+    sys.stdout.write(line)
+
+
+
+
+if __name__ == "__main__":
+  main()
diff --git a/osdep.c b/osdep.c
new file mode 100644
index 0000000..49193e9
--- /dev/null
+++ b/osdep.c
@@ -0,0 +1,288 @@
+/*
+ * QEMU low level functions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#ifdef HOST_SOLARIS
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#endif
+
+#include "qemu-common.h"
+#include "sysemu.h"
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#elif defined(_BSD)
+#include <stdlib.h>
+#else
+#include <malloc.h>
+#endif
+
+
+#if defined(_WIN32)
+void *qemu_memalign(size_t alignment, size_t size)
+{
+    return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+}
+
+void *qemu_vmalloc(size_t size)
+{
+    /* FIXME: this is not exactly optimal solution since VirtualAlloc
+       has 64Kb granularity, but at least it guarantees us that the
+       memory is page aligned. */
+    return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+}
+
+void qemu_vfree(void *ptr)
+{
+    VirtualFree(ptr, 0, MEM_RELEASE);
+}
+
+#else
+
+#if defined(USE_KQEMU)
+
+#ifdef __OpenBSD__
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#else
+#include <sys/vfs.h>
+#endif
+
+#include <sys/mman.h>
+#include <fcntl.h>
+
+static void *kqemu_vmalloc(size_t size)
+{
+    static int phys_ram_fd = -1;
+    static int phys_ram_size = 0;
+    void *ptr;
+
+#ifdef __OpenBSD__ /* no need (?) for a dummy file on OpenBSD */
+    int map_anon = MAP_ANON;
+#else
+    int map_anon = 0;
+    const char *tmpdir;
+    char phys_ram_file[1024];
+#ifdef HOST_SOLARIS
+    struct statvfs stfs;
+#else
+    struct statfs stfs;
+#endif
+
+    if (phys_ram_fd < 0) {
+        tmpdir = getenv("QEMU_TMPDIR");
+        if (!tmpdir)
+#ifdef HOST_SOLARIS
+            tmpdir = "/tmp";
+        if (statvfs(tmpdir, &stfs) == 0) {
+#else
+            tmpdir = "/dev/shm";
+        if (statfs(tmpdir, &stfs) == 0) {
+#endif
+            int64_t free_space;
+            int ram_mb;
+
+            free_space = (int64_t)stfs.f_bavail * stfs.f_bsize;
+            if ((ram_size + 8192 * 1024) >= free_space) {
+                ram_mb = (ram_size / (1024 * 1024));
+                fprintf(stderr,
+                        "You do not have enough space in '%s' for the %d MB of QEMU virtual RAM.\n",
+                        tmpdir, ram_mb);
+                if (strcmp(tmpdir, "/dev/shm") == 0) {
+                    fprintf(stderr, "To have more space available provided you have enough RAM and swap, do as root:\n"
+                            "mount -o remount,size=%dm /dev/shm\n",
+                            ram_mb + 16);
+                } else {
+                    fprintf(stderr,
+                            "Use the '-m' option of QEMU to diminish the amount of virtual RAM or use the\n"
+                            "QEMU_TMPDIR environment variable to set another directory where the QEMU\n"
+                            "temporary RAM file will be opened.\n");
+                }
+                fprintf(stderr, "Or disable the accelerator module with -no-kqemu\n");
+                exit(1);
+            }
+        }
+        snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX",
+                 tmpdir);
+        phys_ram_fd = mkstemp(phys_ram_file);
+        if (phys_ram_fd < 0) {
+            fprintf(stderr,
+                    "warning: could not create temporary file in '%s'.\n"
+                    "Use QEMU_TMPDIR to select a directory in a tmpfs filesystem.\n"
+                    "Using '/tmp' as fallback.\n",
+                    tmpdir);
+            snprintf(phys_ram_file, sizeof(phys_ram_file), "%s/qemuXXXXXX",
+                     "/tmp");
+            phys_ram_fd = mkstemp(phys_ram_file);
+            if (phys_ram_fd < 0) {
+                fprintf(stderr, "Could not create temporary memory file '%s'\n",
+                        phys_ram_file);
+                exit(1);
+            }
+        }
+        unlink(phys_ram_file);
+    }
+    size = (size + 4095) & ~4095;
+    ftruncate(phys_ram_fd, phys_ram_size + size);
+#endif /* !__OpenBSD__ */
+    ptr = mmap(NULL,
+               size,
+               PROT_WRITE | PROT_READ, map_anon | MAP_SHARED,
+               phys_ram_fd, phys_ram_size);
+    if (ptr == MAP_FAILED) {
+        fprintf(stderr, "Could not map physical memory\n");
+        exit(1);
+    }
+    phys_ram_size += size;
+    return ptr;
+}
+
+static void kqemu_vfree(void *ptr)
+{
+    /* may be useful some day, but currently we do not need to free */
+}
+
+#endif
+
+void *qemu_memalign(size_t alignment, size_t size)
+{
+#if defined(_POSIX_C_SOURCE)
+    int ret;
+    void *ptr;
+    ret = posix_memalign(&ptr, alignment, size);
+    if (ret != 0)
+        return NULL;
+    return ptr;
+#elif defined(_BSD)
+    return valloc(size);
+#else
+    return memalign(alignment, size);
+#endif
+}
+
+/* alloc shared memory pages */
+void *qemu_vmalloc(size_t size)
+{
+#if defined(USE_KQEMU)
+    if (kqemu_allowed)
+        return kqemu_vmalloc(size);
+#endif
+#ifdef _BSD
+    return valloc(size);
+#else
+    return memalign(4096, size);
+#endif
+}
+
+void qemu_vfree(void *ptr)
+{
+#if defined(USE_KQEMU)
+    if (kqemu_allowed)
+        kqemu_vfree(ptr);
+#endif
+    free(ptr);
+}
+
+#endif
+
+int qemu_create_pidfile(const char *filename)
+{
+    char buffer[128];
+    int len;
+#ifndef _WIN32
+    int fd;
+
+    fd = open(filename, O_RDWR | O_CREAT, 0600);
+    if (fd == -1)
+        return -1;
+
+    if (lockf(fd, F_TLOCK, 0) == -1)
+        return -1;
+
+    len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid());
+    if (write(fd, buffer, len) != len)
+        return -1;
+#else
+    HANDLE file;
+    DWORD flags;
+    OVERLAPPED overlap;
+    BOOL ret;
+
+    /* Open for writing with no sharing. */
+    file = CreateFile(filename, GENERIC_WRITE, 0, NULL,
+		      OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+    if (file == INVALID_HANDLE_VALUE)
+      return -1;
+
+    flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY;
+    overlap.hEvent = 0;
+    /* Lock 1 byte. */
+    ret = LockFileEx(file, flags, 0, 0, 1, &overlap);
+    if (ret == 0)
+      return -1;
+
+    /* Write PID to file. */
+    len = snprintf(buffer, sizeof(buffer), "%ld\n", (long)getpid());
+    ret = WriteFileEx(file, (LPCVOID)buffer, (DWORD)len,
+		      &overlap, NULL);
+    if (ret == 0)
+      return -1;
+#endif
+    return 0;
+}
+
+#ifdef _WIN32
+
+/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
+#define _W32_FT_OFFSET (116444736000000000ULL)
+
+int qemu_gettimeofday(qemu_timeval *tp)
+{
+  union {
+    unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */
+    FILETIME ft;
+  }  _now;
+
+  if(tp)
+    {
+      GetSystemTimeAsFileTime (&_now.ft);
+      tp->tv_usec=(long)((_now.ns100 / 10ULL) % 1000000ULL );
+      tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000ULL);
+    }
+  /* Always return 0 as per Open Group Base Specifications Issue 6.
+     Do not set errno on error.  */
+  return 0;
+}
+#endif /* _WIN32 */
+
+
diff --git a/osdep.h b/osdep.h
new file mode 100644
index 0000000..626cea1
--- /dev/null
+++ b/osdep.h
@@ -0,0 +1,90 @@
+#ifndef QEMU_OSDEP_H
+#define QEMU_OSDEP_H
+
+#include <stdarg.h>
+#ifdef __OpenBSD__
+#include <sys/types.h>
+#include <sys/signal.h>
+#endif
+
+#ifndef glue
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s)	tostring(s)
+#define tostring(s)	#s
+#endif
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({                      \
+        const typeof(((type *) 0)->member) *__mptr = (ptr);     \
+        (type *) ((char *) __mptr - offsetof(type, member));})
+#endif
+
+#ifndef likely
+#if __GNUC__ < 3
+#define __builtin_expect(x, n) (x)
+#endif
+
+#define likely(x)   __builtin_expect(!!(x), 1)
+#define unlikely(x)   __builtin_expect(!!(x), 0)
+#endif
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER)
+#endif
+#ifndef container_of
+#define container_of(ptr, type, member) ({                      \
+        const typeof(((type *) 0)->member) *__mptr = (ptr);     \
+        (type *) ((char *) __mptr - offsetof(type, member));})
+#endif
+
+#ifndef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#ifndef always_inline
+#if (__GNUC__ < 3) || defined(__APPLE__)
+#define always_inline inline
+#else
+#define always_inline __attribute__ (( always_inline )) __inline__
+#define inline always_inline
+#endif
+#else
+#define inline always_inline
+#endif
+
+#ifdef __i386__
+#define REGPARM __attribute((regparm(3)))
+#else
+#define REGPARM
+#endif
+
+#define qemu_printf printf
+
+void *qemu_memalign(size_t alignment, size_t size);
+void *qemu_vmalloc(size_t size);
+void qemu_vfree(void *ptr);
+
+int qemu_create_pidfile(const char *filename);
+
+#ifdef _WIN32
+int ffs(int i);
+
+typedef struct {
+    long tv_sec;
+    long tv_usec;
+} qemu_timeval;
+int qemu_gettimeofday(qemu_timeval *tp);
+#else
+typedef struct timeval qemu_timeval;
+#define qemu_gettimeofday(tp) gettimeofday(tp, NULL);
+#endif /* !_WIN32 */
+
+#endif
diff --git a/ppc-dis.c b/ppc-dis.c
new file mode 100644
index 0000000..f9ae53e
--- /dev/null
+++ b/ppc-dis.c
@@ -0,0 +1,3246 @@
+/* ppc-dis.c -- Disassemble PowerPC instructions
+   Copyright 1994 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+#include "dis-asm.h"
+
+/* ppc.h -- Header file for PowerPC opcode table
+   Copyright 1994 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+1, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* The opcode table is an array of struct powerpc_opcode.  */
+
+struct powerpc_opcode
+{
+  /* The opcode name.  */
+  const char *name;
+
+  /* The opcode itself.  Those bits which will be filled in with
+     operands are zeroes.  */
+  uint32_t opcode;
+
+  /* The opcode mask.  This is used by the disassembler.  This is a
+     mask containing ones indicating those bits which must match the
+     opcode field, and zeroes indicating those bits which need not
+     match (and are presumably filled in by operands).  */
+  uint32_t mask;
+
+  /* One bit flags for the opcode.  These are used to indicate which
+     specific processors support the instructions.  The defined values
+     are listed below.  */
+  uint32_t flags;
+
+  /* An array of operand codes.  Each code is an index into the
+     operand table.  They appear in the order which the operands must
+     appear in assembly code, and are terminated by a zero.  */
+  unsigned char operands[8];
+};
+
+/* The table itself is sorted by major opcode number, and is otherwise
+   in the order in which the disassembler should consider
+   instructions.  */
+extern const struct powerpc_opcode powerpc_opcodes[];
+extern const int powerpc_num_opcodes;
+
+/* Values defined for the flags field of a struct powerpc_opcode.  */
+
+/* Opcode is defined for the PowerPC architecture.  */
+#define PPC_OPCODE_PPC (01)
+
+/* Opcode is defined for the POWER (RS/6000) architecture.  */
+#define PPC_OPCODE_POWER (02)
+
+/* Opcode is defined for the POWER2 (Rios 2) architecture.  */
+#define PPC_OPCODE_POWER2 (04)
+
+/* Opcode is only defined on 32 bit architectures.  */
+#define PPC_OPCODE_32 (010)
+
+/* Opcode is only defined on 64 bit architectures.  */
+#define PPC_OPCODE_64 (020)
+
+/* Opcode is supported by the Motorola PowerPC 601 processor.  The 601
+   is assumed to support all PowerPC (PPC_OPCODE_PPC) instructions,
+   but it also supports many additional POWER instructions.  */
+#define PPC_OPCODE_601 (040)
+
+/* A macro to extract the major opcode from an instruction.  */
+#define PPC_OP(i) (((i) >> 26) & 0x3f)
+
+/* The operands table is an array of struct powerpc_operand.  */
+
+struct powerpc_operand
+{
+  /* The number of bits in the operand.  */
+  int bits;
+
+  /* How far the operand is left shifted in the instruction.  */
+  int shift;
+
+  /* Insertion function.  This is used by the assembler.  To insert an
+     operand value into an instruction, check this field.
+
+     If it is NULL, execute
+         i |= (op & ((1 << o->bits) - 1)) << o->shift;
+     (i is the instruction which we are filling in, o is a pointer to
+     this structure, and op is the opcode value; this assumes twos
+     complement arithmetic).
+
+     If this field is not NULL, then simply call it with the
+     instruction and the operand value.  It will return the new value
+     of the instruction.  If the ERRMSG argument is not NULL, then if
+     the operand value is illegal, *ERRMSG will be set to a warning
+     string (the operand will be inserted in any case).  If the
+     operand value is legal, *ERRMSG will be unchanged (most operands
+     can accept any value).  */
+  unsigned long (*insert)(uint32_t instruction, int32_t op,
+				   const char **errmsg);
+
+  /* Extraction function.  This is used by the disassembler.  To
+     extract this operand type from an instruction, check this field.
+
+     If it is NULL, compute
+         op = ((i) >> o->shift) & ((1 << o->bits) - 1);
+	 if ((o->flags & PPC_OPERAND_SIGNED) != 0
+	     && (op & (1 << (o->bits - 1))) != 0)
+	   op -= 1 << o->bits;
+     (i is the instruction, o is a pointer to this structure, and op
+     is the result; this assumes twos complement arithmetic).
+
+     If this field is not NULL, then simply call it with the
+     instruction value.  It will return the value of the operand.  If
+     the INVALID argument is not NULL, *INVALID will be set to
+     non-zero if this operand type can not actually be extracted from
+     this operand (i.e., the instruction does not match).  If the
+     operand is valid, *INVALID will not be changed.  */
+  long (*extract) (uint32_t instruction, int *invalid);
+
+  /* One bit syntax flags.  */
+  uint32_t flags;
+};
+
+/* Elements in the table are retrieved by indexing with values from
+   the operands field of the powerpc_opcodes table.  */
+
+extern const struct powerpc_operand powerpc_operands[];
+
+/* Values defined for the flags field of a struct powerpc_operand.  */
+
+/* This operand takes signed values.  */
+#define PPC_OPERAND_SIGNED (01)
+
+/* This operand takes signed values, but also accepts a full positive
+   range of values when running in 32 bit mode.  That is, if bits is
+   16, it takes any value from -0x8000 to 0xffff.  In 64 bit mode,
+   this flag is ignored.  */
+#define PPC_OPERAND_SIGNOPT (02)
+
+/* This operand does not actually exist in the assembler input.  This
+   is used to support extended mnemonics such as mr, for which two
+   operands fields are identical.  The assembler should call the
+   insert function with any op value.  The disassembler should call
+   the extract function, ignore the return value, and check the value
+   placed in the valid argument.  */
+#define PPC_OPERAND_FAKE (04)
+
+/* The next operand should be wrapped in parentheses rather than
+   separated from this one by a comma.  This is used for the load and
+   store instructions which want their operands to look like
+       reg,displacement(reg)
+   */
+#define PPC_OPERAND_PARENS (010)
+
+/* This operand may use the symbolic names for the CR fields, which
+   are
+       lt  0	gt  1	eq  2	so  3	un  3
+       cr0 0	cr1 1	cr2 2	cr3 3
+       cr4 4	cr5 5	cr6 6	cr7 7
+   These may be combined arithmetically, as in cr2*4+gt.  These are
+   only supported on the PowerPC, not the POWER.  */
+#define PPC_OPERAND_CR (020)
+
+/* This operand names a register.  The disassembler uses this to print
+   register names with a leading 'r'.  */
+#define PPC_OPERAND_GPR (040)
+
+/* This operand names a floating point register.  The disassembler
+   prints these with a leading 'f'.  */
+#define PPC_OPERAND_FPR (0100)
+
+/* This operand is a relative branch displacement.  The disassembler
+   prints these symbolically if possible.  */
+#define PPC_OPERAND_RELATIVE (0200)
+
+/* This operand is an absolute branch address.  The disassembler
+   prints these symbolically if possible.  */
+#define PPC_OPERAND_ABSOLUTE (0400)
+
+/* This operand is optional, and is zero if omitted.  This is used for
+   the optional BF and L fields in the comparison instructions.  The
+   assembler must count the number of operands remaining on the line,
+   and the number of operands remaining for the opcode, and decide
+   whether this operand is present or not.  The disassembler should
+   print this operand out only if it is not zero.  */
+#define PPC_OPERAND_OPTIONAL (01000)
+
+/* This flag is only used with PPC_OPERAND_OPTIONAL.  If this operand
+   is omitted, then for the next operand use this operand value plus
+   1, ignoring the next operand field for the opcode.  This wretched
+   hack is needed because the Power rotate instructions can take
+   either 4 or 5 operands.  The disassembler should print this operand
+   out regardless of the PPC_OPERAND_OPTIONAL field.  */
+#define PPC_OPERAND_NEXT (02000)
+
+/* This operand should be regarded as a negative number for the
+   purposes of overflow checking (i.e., the normal most negative
+   number is disallowed and one more than the normal most positive
+   number is allowed).  This flag will only be set for a signed
+   operand.  */
+#define PPC_OPERAND_NEGATIVE (04000)
+
+/* The POWER and PowerPC assemblers use a few macros.  We keep them
+   with the operands table for simplicity.  The macro table is an
+   array of struct powerpc_macro.  */
+
+struct powerpc_macro
+{
+  /* The macro name.  */
+  const char *name;
+
+  /* The number of operands the macro takes.  */
+  unsigned int operands;
+
+  /* One bit flags for the opcode.  These are used to indicate which
+     specific processors support the instructions.  The values are the
+     same as those for the struct powerpc_opcode flags field.  */
+  uint32_t flags;
+
+  /* A format string to turn the macro into a normal instruction.
+     Each %N in the string is replaced with operand number N (zero
+     based).  */
+  const char *format;
+};
+
+extern const struct powerpc_macro powerpc_macros[];
+extern const int powerpc_num_macros;
+
+/* ppc-opc.c -- PowerPC opcode list
+   Copyright 1994 Free Software Foundation, Inc.
+   Written by Ian Lance Taylor, Cygnus Support
+
+This file is part of GDB, GAS, and the GNU binutils.
+
+GDB, GAS, and the GNU binutils are free software; you can redistribute
+them and/or modify them under the terms of the GNU General Public
+License as published by the Free Software Foundation; either version
+2, or (at your option) any later version.
+
+GDB, GAS, and the GNU binutils are distributed in the hope that they
+will be useful, but WITHOUT ANY WARRANTY; without even the implied
+warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this file; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+/* This file holds the PowerPC opcode table.  The opcode table
+   includes almost all of the extended instruction mnemonics.  This
+   permits the disassembler to use them, and simplifies the assembler
+   logic, at the cost of increasing the table size.  The table is
+   strictly constant data, so the compiler should be able to put it in
+   the .text section.
+
+   This file also holds the operand table.  All knowledge about
+   inserting operands into instructions and vice-versa is kept in this
+   file.  */
+
+/* Local insertion and extraction functions.  */
+
+static unsigned long insert_bat (uint32_t, int32_t, const char **);
+static long extract_bat(uint32_t, int *);
+static unsigned long insert_bba(uint32_t, int32_t, const char **);
+static long extract_bba(uint32_t, int *);
+static unsigned long insert_bd(uint32_t, int32_t, const char **);
+static long extract_bd(uint32_t, int *);
+static unsigned long insert_bdm(uint32_t, int32_t, const char **);
+static long extract_bdm(uint32_t, int *);
+static unsigned long insert_bdp(uint32_t, int32_t, const char **);
+static long extract_bdp(uint32_t, int *);
+static unsigned long insert_bo(uint32_t, int32_t, const char **);
+static long extract_bo(uint32_t, int *);
+static unsigned long insert_boe(uint32_t, int32_t, const char **);
+static long extract_boe(uint32_t, int *);
+static unsigned long insert_ds(uint32_t, int32_t, const char **);
+static long extract_ds(uint32_t, int *);
+static unsigned long insert_li(uint32_t, int32_t, const char **);
+static long extract_li(uint32_t, int *);
+static unsigned long insert_mbe(uint32_t, int32_t, const char **);
+static long extract_mbe(uint32_t, int *);
+static unsigned long insert_mb6(uint32_t, int32_t, const char **);
+static long extract_mb6(uint32_t, int *);
+static unsigned long insert_nb(uint32_t, int32_t, const char **);
+static long extract_nb(uint32_t, int *);
+static unsigned long insert_nsi(uint32_t, int32_t, const char **);
+static long extract_nsi(uint32_t, int *);
+static unsigned long insert_ral(uint32_t, int32_t, const char **);
+static unsigned long insert_ram(uint32_t, int32_t, const char **);
+static unsigned long insert_ras(uint32_t, int32_t, const char **);
+static unsigned long insert_rbs(uint32_t, int32_t, const char **);
+static long extract_rbs(uint32_t, int *);
+static unsigned long insert_sh6(uint32_t, int32_t, const char **);
+static long extract_sh6(uint32_t, int *);
+static unsigned long insert_spr(uint32_t, int32_t, const char **);
+static long extract_spr(uint32_t, int *);
+static unsigned long insert_tbr(uint32_t, int32_t, const char **);
+static long extract_tbr(uint32_t, int *);
+
+/* The operands table.
+
+   The fields are bits, shift, signed, insert, extract, flags.  */
+
+const struct powerpc_operand powerpc_operands[] =
+{
+  /* The zero index is used to indicate the end of the list of
+     operands.  */
+#define UNUSED (0)
+  { 0, 0, 0, 0, 0 },
+
+  /* The BA field in an XL form instruction.  */
+#define BA (1)
+#define BA_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+  /* The BA field in an XL form instruction when it must be the same
+     as the BT field in the same instruction.  */
+#define BAT (2)
+  { 5, 16, insert_bat, extract_bat, PPC_OPERAND_FAKE },
+
+  /* The BB field in an XL form instruction.  */
+#define BB (3)
+#define BB_MASK (0x1f << 11)
+  { 5, 11, 0, 0, PPC_OPERAND_CR },
+
+  /* The BB field in an XL form instruction when it must be the same
+     as the BA field in the same instruction.  */
+#define BBA (4)
+  { 5, 11, insert_bba, extract_bba, PPC_OPERAND_FAKE },
+
+  /* The BD field in a B form instruction.  The lower two bits are
+     forced to zero.  */
+#define BD (5)
+  { 16, 0, insert_bd, extract_bd, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when absolute addressing is
+     used.  */
+#define BDA (6)
+  { 16, 0, insert_bd, extract_bd, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the - modifier is used.
+     This sets the y bit of the BO field appropriately.  */
+#define BDM (7)
+  { 16, 0, insert_bdm, extract_bdm,
+      PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the - modifier is used
+     and absolute address is used.  */
+#define BDMA (8)
+  { 16, 0, insert_bdm, extract_bdm,
+      PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the + modifier is used.
+     This sets the y bit of the BO field appropriately.  */
+#define BDP (9)
+  { 16, 0, insert_bdp, extract_bdp,
+      PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The BD field in a B form instruction when the + modifier is used
+     and absolute addressing is used.  */
+#define BDPA (10)
+  { 16, 0, insert_bdp, extract_bdp,
+      PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The BF field in an X or XL form instruction.  */
+#define BF (11)
+  { 3, 23, 0, 0, PPC_OPERAND_CR },
+
+  /* An optional BF field.  This is used for comparison instructions,
+     in which an omitted BF field is taken as zero.  */
+#define OBF (12)
+  { 3, 23, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+  /* The BFA field in an X or XL form instruction.  */
+#define BFA (13)
+  { 3, 18, 0, 0, PPC_OPERAND_CR },
+
+  /* The BI field in a B form or XL form instruction.  */
+#define BI (14)
+#define BI_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_CR },
+
+  /* The BO field in a B form instruction.  Certain values are
+     illegal.  */
+#define BO (15)
+#define BO_MASK (0x1f << 21)
+  { 5, 21, insert_bo, extract_bo, 0 },
+
+  /* The BO field in a B form instruction when the + or - modifier is
+     used.  This is like the BO field, but it must be even.  */
+#define BOE (16)
+  { 5, 21, insert_boe, extract_boe, 0 },
+
+  /* The BT field in an X or XL form instruction.  */
+#define BT (17)
+  { 5, 21, 0, 0, PPC_OPERAND_CR },
+
+  /* The condition register number portion of the BI field in a B form
+     or XL form instruction.  This is used for the extended
+     conditional branch mnemonics, which set the lower two bits of the
+     BI field.  This field is optional.  */
+#define CR (18)
+  { 3, 18, 0, 0, PPC_OPERAND_CR | PPC_OPERAND_OPTIONAL },
+
+  /* The D field in a D form instruction.  This is a displacement off
+     a register, and implies that the next operand is a register in
+     parentheses.  */
+#define D (19)
+  { 16, 0, 0, 0, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+  /* The DS field in a DS form instruction.  This is like D, but the
+     lower two bits are forced to zero.  */
+#define DS (20)
+  { 16, 0, insert_ds, extract_ds, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
+
+  /* The FL1 field in a POWER SC form instruction.  */
+#define FL1 (21)
+  { 4, 12, 0, 0, 0 },
+
+  /* The FL2 field in a POWER SC form instruction.  */
+#define FL2 (22)
+  { 3, 2, 0, 0, 0 },
+
+  /* The FLM field in an XFL form instruction.  */
+#define FLM (23)
+  { 8, 17, 0, 0, 0 },
+
+  /* The FRA field in an X or A form instruction.  */
+#define FRA (24)
+#define FRA_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FRB field in an X or A form instruction.  */
+#define FRB (25)
+#define FRB_MASK (0x1f << 11)
+  { 5, 11, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FRC field in an A form instruction.  */
+#define FRC (26)
+#define FRC_MASK (0x1f << 6)
+  { 5, 6, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FRS field in an X form instruction or the FRT field in a D, X
+     or A form instruction.  */
+#define FRS (27)
+#define FRT (FRS)
+  { 5, 21, 0, 0, PPC_OPERAND_FPR },
+
+  /* The FXM field in an XFX instruction.  */
+#define FXM (28)
+#define FXM_MASK (0xff << 12)
+  { 8, 12, 0, 0, 0 },
+
+  /* The L field in a D or X form instruction.  */
+#define L (29)
+  { 1, 21, 0, 0, PPC_OPERAND_OPTIONAL },
+
+  /* The LEV field in a POWER SC form instruction.  */
+#define LEV (30)
+  { 7, 5, 0, 0, 0 },
+
+  /* The LI field in an I form instruction.  The lower two bits are
+     forced to zero.  */
+#define LI (31)
+  { 26, 0, insert_li, extract_li, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
+
+  /* The LI field in an I form instruction when used as an absolute
+     address.  */
+#define LIA (32)
+  { 26, 0, insert_li, extract_li, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
+
+  /* The MB field in an M form instruction.  */
+#define MB (33)
+#define MB_MASK (0x1f << 6)
+  { 5, 6, 0, 0, 0 },
+
+  /* The ME field in an M form instruction.  */
+#define ME (34)
+#define ME_MASK (0x1f << 1)
+  { 5, 1, 0, 0, 0 },
+
+  /* The MB and ME fields in an M form instruction expressed a single
+     operand which is a bitmask indicating which bits to select.  This
+     is a two operand form using PPC_OPERAND_NEXT.  See the
+     description in opcode/ppc.h for what this means.  */
+#define MBE (35)
+  { 5, 6, 0, 0, PPC_OPERAND_OPTIONAL | PPC_OPERAND_NEXT },
+  { 32, 0, insert_mbe, extract_mbe, 0 },
+
+  /* The MB or ME field in an MD or MDS form instruction.  The high
+     bit is wrapped to the low end.  */
+#define MB6 (37)
+#define ME6 (MB6)
+#define MB6_MASK (0x3f << 5)
+  { 6, 5, insert_mb6, extract_mb6, 0 },
+
+  /* The NB field in an X form instruction.  The value 32 is stored as
+     0.  */
+#define NB (38)
+  { 6, 11, insert_nb, extract_nb, 0 },
+
+  /* The NSI field in a D form instruction.  This is the same as the
+     SI field, only negated.  */
+#define NSI (39)
+  { 16, 0, insert_nsi, extract_nsi,
+      PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
+
+  /* The RA field in an D, DS, X, XO, M, or MDS form instruction.  */
+#define RA (40)
+#define RA_MASK (0x1f << 16)
+  { 5, 16, 0, 0, PPC_OPERAND_GPR },
+
+  /* The RA field in a D or X form instruction which is an updating
+     load, which means that the RA field may not be zero and may not
+     equal the RT field.  */
+#define RAL (41)
+  { 5, 16, insert_ral, 0, PPC_OPERAND_GPR },
+
+  /* The RA field in an lmw instruction, which has special value
+     restrictions.  */
+#define RAM (42)
+  { 5, 16, insert_ram, 0, PPC_OPERAND_GPR },
+
+  /* The RA field in a D or X form instruction which is an updating
+     store or an updating floating point load, which means that the RA
+     field may not be zero.  */
+#define RAS (43)
+  { 5, 16, insert_ras, 0, PPC_OPERAND_GPR },
+
+  /* The RB field in an X, XO, M, or MDS form instruction.  */
+#define RB (44)
+#define RB_MASK (0x1f << 11)
+  { 5, 11, 0, 0, PPC_OPERAND_GPR },
+
+  /* The RB field in an X form instruction when it must be the same as
+     the RS field in the instruction.  This is used for extended
+     mnemonics like mr.  */
+#define RBS (45)
+  { 5, 1, insert_rbs, extract_rbs, PPC_OPERAND_FAKE },
+
+  /* The RS field in a D, DS, X, XFX, XS, M, MD or MDS form
+     instruction or the RT field in a D, DS, X, XFX or XO form
+     instruction.  */
+#define RS (46)
+#define RT (RS)
+#define RT_MASK (0x1f << 21)
+  { 5, 21, 0, 0, PPC_OPERAND_GPR },
+
+  /* The SH field in an X or M form instruction.  */
+#define SH (47)
+#define SH_MASK (0x1f << 11)
+  { 5, 11, 0, 0, 0 },
+
+  /* The SH field in an MD form instruction.  This is split.  */
+#define SH6 (48)
+#define SH6_MASK ((0x1f << 11) | (1 << 1))
+  { 6, 1, insert_sh6, extract_sh6, 0 },
+
+  /* The SI field in a D form instruction.  */
+#define SI (49)
+  { 16, 0, 0, 0, PPC_OPERAND_SIGNED },
+
+  /* The SI field in a D form instruction when we accept a wide range
+     of positive values.  */
+#define SISIGNOPT (50)
+  { 16, 0, 0, 0, PPC_OPERAND_SIGNED | PPC_OPERAND_SIGNOPT },
+
+  /* The SPR field in an XFX form instruction.  This is flipped--the
+     lower 5 bits are stored in the upper 5 and vice- versa.  */
+#define SPR (51)
+#define SPR_MASK (0x3ff << 11)
+  { 10, 11, insert_spr, extract_spr, 0 },
+
+  /* The BAT index number in an XFX form m[ft]ibat[lu] instruction.  */
+#define SPRBAT (52)
+#define SPRBAT_MASK (0x3 << 17)
+  { 2, 17, 0, 0, 0 },
+
+  /* The SPRG register number in an XFX form m[ft]sprg instruction.  */
+#define SPRG (53)
+#define SPRG_MASK (0x3 << 16)
+  { 2, 16, 0, 0, 0 },
+
+  /* The SR field in an X form instruction.  */
+#define SR (54)
+  { 4, 16, 0, 0, 0 },
+
+  /* The SV field in a POWER SC form instruction.  */
+#define SV (55)
+  { 14, 2, 0, 0, 0 },
+
+  /* The TBR field in an XFX form instruction.  This is like the SPR
+     field, but it is optional.  */
+#define TBR (56)
+  { 10, 11, insert_tbr, extract_tbr, PPC_OPERAND_OPTIONAL },
+
+  /* The TO field in a D or X form instruction.  */
+#define TO (57)
+#define TO_MASK (0x1f << 21)
+  { 5, 21, 0, 0, 0 },
+
+  /* The U field in an X form instruction.  */
+#define U (58)
+  { 4, 12, 0, 0, 0 },
+
+  /* The UI field in a D form instruction.  */
+#define UI (59)
+  { 16, 0, 0, 0, 0 },
+};
+
+/* The functions used to insert and extract complicated operands.  */
+
+/* The BA field in an XL form instruction when it must be the same as
+   the BT field in the same instruction.  This operand is marked FAKE.
+   The insertion function just copies the BT field into the BA field,
+   and the extraction function just checks that the fields are the
+   same.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bat (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | (((insn >> 21) & 0x1f) << 16);
+}
+
+static long
+extract_bat (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn >> 21) & 0x1f) != ((insn >> 16) & 0x1f))
+    *invalid = 1;
+  return 0;
+}
+
+/* The BB field in an XL form instruction when it must be the same as
+   the BA field in the same instruction.  This operand is marked FAKE.
+   The insertion function just copies the BA field into the BB field,
+   and the extraction function just checks that the fields are the
+   same.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bba (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | (((insn >> 16) & 0x1f) << 11);
+}
+
+static long
+extract_bba (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn >> 16) & 0x1f) != ((insn >> 11) & 0x1f))
+    *invalid = 1;
+  return 0;
+}
+
+/* The BD field in a B form instruction.  The lower two bits are
+   forced to zero.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bd (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_bd (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the - modifier is used.
+   This modifier means that the branch is not expected to be taken.
+   We must set the y bit of the BO field to 1 if the offset is
+   negative.  When extracting, we require that the y bit be 1 and that
+   the offset be positive, since if the y bit is 0 we just want to
+   print the normal form of the instruction.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdm (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if ((value & 0x8000) != 0)
+    insn |= 1 << 21;
+  return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdm (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn & (1 << 21)) == 0
+	  || (insn & (1 << 15)) == 0))
+    *invalid = 1;
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* The BD field in a B form instruction when the + modifier is used.
+   This is like BDM, above, except that the branch is expected to be
+   taken.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_bdp (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if ((value & 0x8000) == 0)
+    insn |= 1 << 21;
+  return insn | (value & 0xfffc);
+}
+
+static long
+extract_bdp (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn & (1 << 21)) == 0
+	  || (insn & (1 << 15)) != 0))
+    *invalid = 1;
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* Check for legal values of a BO field.  */
+
+static int
+valid_bo (int32_t value)
+{
+  /* Certain encodings have bits that are required to be zero.  These
+     are (z must be zero, y may be anything):
+         001zy
+	 011zy
+	 1z00y
+	 1z01y
+	 1z1zz
+     */
+  switch (value & 0x14)
+    {
+    default:
+    case 0:
+      return 1;
+    case 0x4:
+      return (value & 0x2) == 0;
+    case 0x10:
+      return (value & 0x8) == 0;
+    case 0x14:
+      return value == 0x14;
+    }
+}
+
+/* The BO field in a B form instruction.  Warn about attempts to set
+   the field to an illegal value.  */
+
+static unsigned long
+insert_bo (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (errmsg != (const char **) NULL
+      && ! valid_bo (value))
+    *errmsg = "invalid conditional option";
+  return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_bo (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  int32_t value;
+
+  value = (insn >> 21) & 0x1f;
+  if (invalid != (int *) NULL
+      && ! valid_bo (value))
+    *invalid = 1;
+  return value;
+}
+
+/* The BO field in a B form instruction when the + or - modifier is
+   used.  This is like the BO field, but it must be even.  When
+   extracting it, we force it to be even.  */
+
+static unsigned long
+insert_boe (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (errmsg != (const char **) NULL)
+    {
+      if (! valid_bo (value))
+	*errmsg = "invalid conditional option";
+      else if ((value & 1) != 0)
+	*errmsg = "attempt to set y bit when using + or - modifier";
+    }
+  return insn | ((value & 0x1f) << 21);
+}
+
+static long
+extract_boe (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  int32_t value;
+
+  value = (insn >> 21) & 0x1f;
+  if (invalid != (int *) NULL
+      && ! valid_bo (value))
+    *invalid = 1;
+  return value & 0x1e;
+}
+
+/* The DS field in a DS form instruction.  This is like D, but the
+   lower two bits are forced to zero.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_ds (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | (value & 0xfffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_ds (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if ((insn & 0x8000) != 0)
+    return (insn & 0xfffc) - 0x10000;
+  else
+    return insn & 0xfffc;
+}
+
+/* The LI field in an I form instruction.  The lower two bits are
+   forced to zero.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_li (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | (value & 0x3fffffc);
+}
+
+/*ARGSUSED*/
+static long
+extract_li (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if ((insn & 0x2000000) != 0)
+    return (insn & 0x3fffffc) - 0x4000000;
+  else
+    return insn & 0x3fffffc;
+}
+
+/* The MB and ME fields in an M form instruction expressed as a single
+   operand which is itself a bitmask.  The extraction function always
+   marks it as invalid, since we never want to recognize an
+   instruction which uses a field of this type.  */
+
+static unsigned long
+insert_mbe (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  uint32_t uval;
+  int mb, me;
+
+  uval = value;
+
+  if (uval == 0)
+    {
+      if (errmsg != (const char **) NULL)
+	*errmsg = "illegal bitmask";
+      return insn;
+    }
+
+  me = 31;
+  while ((uval & 1) == 0)
+    {
+      uval >>= 1;
+      --me;
+    }
+
+  mb = me;
+  uval >>= 1;
+  while ((uval & 1) != 0)
+    {
+      uval >>= 1;
+      --mb;
+    }
+
+  if (uval != 0)
+    {
+      if (errmsg != (const char **) NULL)
+	*errmsg = "illegal bitmask";
+    }
+
+  return insn | (mb << 6) | (me << 1);
+}
+
+static long
+extract_mbe (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  long ret;
+  int mb, me;
+  int i;
+
+  if (invalid != (int *) NULL)
+    *invalid = 1;
+
+  ret = 0;
+  mb = (insn >> 6) & 0x1f;
+  me = (insn >> 1) & 0x1f;
+  for (i = mb; i < me; i++)
+    ret |= 1 << (31 - i);
+  return ret;
+}
+
+/* The MB or ME field in an MD or MDS form instruction.  The high bit
+   is wrapped to the low end.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_mb6 (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | ((value & 0x1f) << 6) | (value & 0x20);
+}
+
+/*ARGSUSED*/
+static long
+extract_mb6 (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  return ((insn >> 6) & 0x1f) | (insn & 0x20);
+}
+
+/* The NB field in an X form instruction.  The value 32 is stored as
+   0.  */
+
+static unsigned long
+insert_nb (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (value < 0 || value > 32)
+    *errmsg = "value out of range";
+  if (value == 32)
+    value = 0;
+  return insn | ((value & 0x1f) << 11);
+}
+
+/*ARGSUSED*/
+static long
+extract_nb (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  long ret;
+
+  ret = (insn >> 11) & 0x1f;
+  if (ret == 0)
+    ret = 32;
+  return ret;
+}
+
+/* The NSI field in a D form instruction.  This is the same as the SI
+   field, only negated.  The extraction function always marks it as
+   invalid, since we never want to recognize an instruction which uses
+   a field of this type.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_nsi (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | ((- value) & 0xffff);
+}
+
+static long
+extract_nsi (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL)
+    *invalid = 1;
+  if ((insn & 0x8000) != 0)
+    return - ((insn & 0xffff) - 0x10000);
+  else
+    return - (insn & 0xffff);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+   load, which means that the RA field may not be zero and may not
+   equal the RT field.  */
+
+static unsigned long
+insert_ral (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (value == 0
+      || value == ((insn >> 21) & 0x1f))
+    *errmsg = "invalid register operand when updating";
+  return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in an lmw instruction, which has special value
+   restrictions.  */
+
+static unsigned long
+insert_ram (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (value >= ((insn >> 21) & 0x1f))
+    *errmsg = "index register in load range";
+  return insn | ((value & 0x1f) << 16);
+}
+
+/* The RA field in a D or X form instruction which is an updating
+   store or an updating floating point load, which means that the RA
+   field may not be zero.  */
+
+static unsigned long
+insert_ras (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (value == 0)
+    *errmsg = "invalid register operand when updating";
+  return insn | ((value & 0x1f) << 16);
+}
+
+/* The RB field in an X form instruction when it must be the same as
+   the RS field in the instruction.  This is used for extended
+   mnemonics like mr.  This operand is marked FAKE.  The insertion
+   function just copies the BT field into the BA field, and the
+   extraction function just checks that the fields are the same.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_rbs (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | (((insn >> 21) & 0x1f) << 11);
+}
+
+static long
+extract_rbs (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  if (invalid != (int *) NULL
+      && ((insn >> 21) & 0x1f) != ((insn >> 11) & 0x1f))
+    *invalid = 1;
+  return 0;
+}
+
+/* The SH field in an MD form instruction.  This is split.  */
+
+/*ARGSUSED*/
+static unsigned long
+insert_sh6 (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
+}
+
+/*ARGSUSED*/
+static long
+extract_sh6 (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20);
+}
+
+/* The SPR field in an XFX form instruction.  This is flipped--the
+   lower 5 bits are stored in the upper 5 and vice- versa.  */
+
+static unsigned long
+insert_spr (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_spr (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+}
+
+/* The TBR field in an XFX instruction.  This is just like SPR, but it
+   is optional.  When TBR is omitted, it must be inserted as 268 (the
+   magic number of the TB register).  These functions treat 0
+   (indicating an omitted optional operand) as 268.  This means that
+   ``mftb 4,0'' is not handled correctly.  This does not matter very
+   much, since the architecture manual does not define mftb as
+   accepting any values other than 268 or 269.  */
+
+#define TB (268)
+
+static unsigned long
+insert_tbr (insn, value, errmsg)
+     uint32_t insn;
+     int32_t value;
+     const char **errmsg;
+{
+  if (value == 0)
+    value = TB;
+  return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
+}
+
+static long
+extract_tbr (insn, invalid)
+     uint32_t insn;
+     int *invalid;
+{
+  long ret;
+
+  ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
+  if (ret == TB)
+    ret = 0;
+  return ret;
+}
+
+/* Macros used to form opcodes.  */
+
+/* The main opcode.  */
+#define OP(x) (((x) & 0x3f) << 26)
+#define OP_MASK OP (0x3f)
+
+/* The main opcode combined with a trap code in the TO field of a D
+   form instruction.  Used for extended mnemonics for the trap
+   instructions.  */
+#define OPTO(x,to) (OP (x) | (((to) & 0x1f) << 21))
+#define OPTO_MASK (OP_MASK | TO_MASK)
+
+/* The main opcode combined with a comparison size bit in the L field
+   of a D form or X form instruction.  Used for extended mnemonics for
+   the comparison instructions.  */
+#define OPL(x,l) (OP (x) | (((l) & 1) << 21))
+#define OPL_MASK OPL (0x3f,1)
+
+/* An A form instruction.  */
+#define A(op, xop, rc) (OP (op) | (((xop) & 0x1f) << 1) | ((rc) & 1))
+#define A_MASK A (0x3f, 0x1f, 1)
+
+/* An A_MASK with the FRB field fixed.  */
+#define AFRB_MASK (A_MASK | FRB_MASK)
+
+/* An A_MASK with the FRC field fixed.  */
+#define AFRC_MASK (A_MASK | FRC_MASK)
+
+/* An A_MASK with the FRA and FRC fields fixed.  */
+#define AFRAFRC_MASK (A_MASK | FRA_MASK | FRC_MASK)
+
+/* A B form instruction.  */
+#define B(op, aa, lk) (OP (op) | (((aa) & 1) << 1) | ((lk) & 1))
+#define B_MASK B (0x3f, 1, 1)
+
+/* A B form instruction setting the BO field.  */
+#define BBO(op, bo, aa, lk) (B ((op), (aa), (lk)) | (((bo) & 0x1f) << 21))
+#define BBO_MASK BBO (0x3f, 0x1f, 1, 1)
+
+/* A BBO_MASK with the y bit of the BO field removed.  This permits
+   matching a conditional branch regardless of the setting of the y
+   bit.  */
+#define Y_MASK (1 << 21)
+#define BBOY_MASK (BBO_MASK &~ Y_MASK)
+
+/* A B form instruction setting the BO field and the condition bits of
+   the BI field.  */
+#define BBOCB(op, bo, cb, aa, lk) \
+  (BBO ((op), (bo), (aa), (lk)) | (((cb) & 0x3) << 16))
+#define BBOCB_MASK BBOCB (0x3f, 0x1f, 0x3, 1, 1)
+
+/* A BBOCB_MASK with the y bit of the BO field removed.  */
+#define BBOYCB_MASK (BBOCB_MASK &~ Y_MASK)
+
+/* A BBOYCB_MASK in which the BI field is fixed.  */
+#define BBOYBI_MASK (BBOYCB_MASK | BI_MASK)
+
+/* The main opcode mask with the RA field clear.  */
+#define DRA_MASK (OP_MASK | RA_MASK)
+
+/* A DS form instruction.  */
+#define DSO(op, xop) (OP (op) | ((xop) & 0x3))
+#define DS_MASK DSO (0x3f, 3)
+
+/* An M form instruction.  */
+#define M(op, rc) (OP (op) | ((rc) & 1))
+#define M_MASK M (0x3f, 1)
+
+/* An M form instruction with the ME field specified.  */
+#define MME(op, me, rc) (M ((op), (rc)) | (((me) & 0x1f) << 1))
+
+/* An M_MASK with the MB and ME fields fixed.  */
+#define MMBME_MASK (M_MASK | MB_MASK | ME_MASK)
+
+/* An M_MASK with the SH and ME fields fixed.  */
+#define MSHME_MASK (M_MASK | SH_MASK | ME_MASK)
+
+/* An MD form instruction.  */
+#define MD(op, xop, rc) (OP (op) | (((xop) & 0x7) << 2) | ((rc) & 1))
+#define MD_MASK MD (0x3f, 0x7, 1)
+
+/* An MD_MASK with the MB field fixed.  */
+#define MDMB_MASK (MD_MASK | MB6_MASK)
+
+/* An MD_MASK with the SH field fixed.  */
+#define MDSH_MASK (MD_MASK | SH6_MASK)
+
+/* An MDS form instruction.  */
+#define MDS(op, xop, rc) (OP (op) | (((xop) & 0xf) << 1) | ((rc) & 1))
+#define MDS_MASK MDS (0x3f, 0xf, 1)
+
+/* An MDS_MASK with the MB field fixed.  */
+#define MDSMB_MASK (MDS_MASK | MB6_MASK)
+
+/* An SC form instruction.  */
+#define SC(op, sa, lk) (OP (op) | (((sa) & 1) << 1) | ((lk) & 1))
+#define SC_MASK (OP_MASK | (0x3ff << 16) | (1 << 1) | 1)
+
+/* An X form instruction.  */
+#define X(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An X form instruction with the RC bit specified.  */
+#define XRC(op, xop, rc) (X ((op), (xop)) | ((rc) & 1))
+
+/* The mask for an X form instruction.  */
+#define X_MASK XRC (0x3f, 0x3ff, 1)
+
+/* An X_MASK with the RA field fixed.  */
+#define XRA_MASK (X_MASK | RA_MASK)
+
+/* An X_MASK with the RB field fixed.  */
+#define XRB_MASK (X_MASK | RB_MASK)
+
+/* An X_MASK with the RT field fixed.  */
+#define XRT_MASK (X_MASK | RT_MASK)
+
+/* An X_MASK with the RA and RB fields fixed.  */
+#define XRARB_MASK (X_MASK | RA_MASK | RB_MASK)
+
+/* An X_MASK with the RT and RA fields fixed.  */
+#define XRTRA_MASK (X_MASK | RT_MASK | RA_MASK)
+
+/* An X form comparison instruction.  */
+#define XCMPL(op, xop, l) (X ((op), (xop)) | (((l) & 1) << 21))
+
+/* The mask for an X form comparison instruction.  */
+#define XCMP_MASK (X_MASK | (1 << 22))
+
+/* The mask for an X form comparison instruction with the L field
+   fixed.  */
+#define XCMPL_MASK (XCMP_MASK | (1 << 21))
+
+/* An X form trap instruction with the TO field specified.  */
+#define XTO(op, xop, to) (X ((op), (xop)) | (((to) & 0x1f) << 21))
+#define XTO_MASK (X_MASK | TO_MASK)
+
+/* An XFL form instruction.  */
+#define XFL(op, xop, rc) (OP (op) | (((xop) & 0x3ff) << 1) | ((rc) & 1))
+#define XFL_MASK (XFL (0x3f, 0x3ff, 1) | (1 << 25) | (1 << 16))
+
+/* An XL form instruction with the LK field set to 0.  */
+#define XL(op, xop) (OP (op) | (((xop) & 0x3ff) << 1))
+
+/* An XL form instruction which uses the LK field.  */
+#define XLLK(op, xop, lk) (XL ((op), (xop)) | ((lk) & 1))
+
+/* The mask for an XL form instruction.  */
+#define XL_MASK XLLK (0x3f, 0x3ff, 1)
+
+/* An XL form instruction which explicitly sets the BO field.  */
+#define XLO(op, bo, xop, lk) \
+  (XLLK ((op), (xop), (lk)) | (((bo) & 0x1f) << 21))
+#define XLO_MASK (XL_MASK | BO_MASK)
+
+/* An XL form instruction which explicitly sets the y bit of the BO
+   field.  */
+#define XLYLK(op, xop, y, lk) (XLLK ((op), (xop), (lk)) | (((y) & 1) << 21))
+#define XLYLK_MASK (XL_MASK | Y_MASK)
+
+/* An XL form instruction which sets the BO field and the condition
+   bits of the BI field.  */
+#define XLOCB(op, bo, cb, xop, lk) \
+  (XLO ((op), (bo), (xop), (lk)) | (((cb) & 3) << 16))
+#define XLOCB_MASK XLOCB (0x3f, 0x1f, 0x3, 0x3ff, 1)
+
+/* An XL_MASK or XLYLK_MASK or XLOCB_MASK with the BB field fixed.  */
+#define XLBB_MASK (XL_MASK | BB_MASK)
+#define XLYBB_MASK (XLYLK_MASK | BB_MASK)
+#define XLBOCBBB_MASK (XLOCB_MASK | BB_MASK)
+
+/* An XL_MASK with the BO and BB fields fixed.  */
+#define XLBOBB_MASK (XL_MASK | BO_MASK | BB_MASK)
+
+/* An XL_MASK with the BO, BI and BB fields fixed.  */
+#define XLBOBIBB_MASK (XL_MASK | BO_MASK | BI_MASK | BB_MASK)
+
+/* An XO form instruction.  */
+#define XO(op, xop, oe, rc) \
+  (OP (op) | (((xop) & 0x1ff) << 1) | (((oe) & 1) << 10) | ((rc) & 1))
+#define XO_MASK XO (0x3f, 0x1ff, 1, 1)
+
+/* An XO_MASK with the RB field fixed.  */
+#define XORB_MASK (XO_MASK | RB_MASK)
+
+/* An XS form instruction.  */
+#define XS(op, xop, rc) (OP (op) | (((xop) & 0x1ff) << 2) | ((rc) & 1))
+#define XS_MASK XS (0x3f, 0x1ff, 1)
+
+/* A mask for the FXM version of an XFX form instruction.  */
+#define XFXFXM_MASK (X_MASK | (1 << 20) | (1 << 11))
+
+/* An XFX form instruction with the FXM field filled in.  */
+#define XFXM(op, xop, fxm) \
+  (X ((op), (xop)) | (((fxm) & 0xff) << 12))
+
+/* An XFX form instruction with the SPR field filled in.  */
+#define XSPR(op, xop, spr) \
+  (X ((op), (xop)) | (((spr) & 0x1f) << 16) | (((spr) & 0x3e0) << 6))
+#define XSPR_MASK (X_MASK | SPR_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+   SPRBAT field.  */
+#define XSPRBAT_MASK (XSPR_MASK &~ SPRBAT_MASK)
+
+/* An XFX form instruction with the SPR field filled in except for the
+   SPRG field.  */
+#define XSPRG_MASK (XSPR_MASK &~ SPRG_MASK)
+
+/* The BO encodings used in extended conditional branch mnemonics.  */
+#define BODNZF	(0x0)
+#define BODNZFP	(0x1)
+#define BODZF	(0x2)
+#define BODZFP	(0x3)
+#define BOF	(0x4)
+#define BOFP	(0x5)
+#define BODNZT	(0x8)
+#define BODNZTP	(0x9)
+#define BODZT	(0xa)
+#define BODZTP	(0xb)
+#define BOT	(0xc)
+#define BOTP	(0xd)
+#define BODNZ	(0x10)
+#define BODNZP	(0x11)
+#define BODZ	(0x12)
+#define BODZP	(0x13)
+#define BOU	(0x14)
+
+/* The BI condition bit encodings used in extended conditional branch
+   mnemonics.  */
+#define CBLT	(0)
+#define CBGT	(1)
+#define CBEQ	(2)
+#define CBSO	(3)
+
+/* The TO encodings used in extended trap mnemonics.  */
+#define TOLGT	(0x1)
+#define TOLLT	(0x2)
+#define TOEQ	(0x4)
+#define TOLGE	(0x5)
+#define TOLNL	(0x5)
+#define TOLLE	(0x6)
+#define TOLNG	(0x6)
+#define TOGT	(0x8)
+#define TOGE	(0xc)
+#define TONL	(0xc)
+#define TOLT	(0x10)
+#define TOLE	(0x14)
+#define TONG	(0x14)
+#define TONE	(0x18)
+#define TOU	(0x1f)
+
+/* Smaller names for the flags so each entry in the opcodes table will
+   fit on a single line.  */
+#undef PPC
+#define PPC PPC_OPCODE_PPC
+#define POWER PPC_OPCODE_POWER
+#define POWER2 PPC_OPCODE_POWER2
+#define B32 PPC_OPCODE_32
+#define B64 PPC_OPCODE_64
+#define M601 PPC_OPCODE_601
+
+/* The opcode table.
+
+   The format of the opcode table is:
+
+   NAME	     OPCODE	MASK		FLAGS		{ OPERANDS }
+
+   NAME is the name of the instruction.
+   OPCODE is the instruction opcode.
+   MASK is the opcode mask; this is used to tell the disassembler
+     which bits in the actual opcode must match OPCODE.
+   FLAGS are flags indicated what processors support the instruction.
+   OPERANDS is the list of operands.
+
+   The disassembler reads the table in order and prints the first
+   instruction which matches, so this table is sorted to put more
+   specific instructions before more general instructions.  It is also
+   sorted by major opcode.  */
+
+const struct powerpc_opcode powerpc_opcodes[] = {
+{ "tdlgti",  OPTO(2,TOLGT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdllti",  OPTO(2,TOLLT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdeqi",   OPTO(2,TOEQ), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlgei",  OPTO(2,TOLGE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlnli",  OPTO(2,TOLNL), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdllei",  OPTO(2,TOLLE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlngi",  OPTO(2,TOLNG), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdgti",   OPTO(2,TOGT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdgei",   OPTO(2,TOGE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdnli",   OPTO(2,TONL), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlti",   OPTO(2,TOLT), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdlei",   OPTO(2,TOLE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdngi",   OPTO(2,TONG), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdnei",   OPTO(2,TONE), OPTO_MASK,	PPC|B64,	{ RA, SI } },
+{ "tdi",     OP(2),	OP_MASK,	PPC|B64,	{ TO, RA, SI } },
+
+{ "twlgti",  OPTO(3,TOLGT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlgti",   OPTO(3,TOLGT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twllti",  OPTO(3,TOLLT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tllti",   OPTO(3,TOLLT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "tweqi",   OPTO(3,TOEQ), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "teqi",    OPTO(3,TOEQ), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlgei",  OPTO(3,TOLGE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlgei",   OPTO(3,TOLGE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlnli",  OPTO(3,TOLNL), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlnli",   OPTO(3,TOLNL), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twllei",  OPTO(3,TOLLE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tllei",   OPTO(3,TOLLE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlngi",  OPTO(3,TOLNG), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlngi",   OPTO(3,TOLNG), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twgti",   OPTO(3,TOGT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tgti",    OPTO(3,TOGT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twgei",   OPTO(3,TOGE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tgei",    OPTO(3,TOGE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twnli",   OPTO(3,TONL), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tnli",    OPTO(3,TONL), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlti",   OPTO(3,TOLT), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlti",    OPTO(3,TOLT), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twlei",   OPTO(3,TOLE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tlei",    OPTO(3,TOLE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twngi",   OPTO(3,TONG), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tngi",    OPTO(3,TONG), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twnei",   OPTO(3,TONE), OPTO_MASK,	PPC,		{ RA, SI } },
+{ "tnei",    OPTO(3,TONE), OPTO_MASK,	POWER,		{ RA, SI } },
+{ "twi",     OP(3),	OP_MASK,	PPC,		{ TO, RA, SI } },
+{ "ti",      OP(3),	OP_MASK,	POWER,		{ TO, RA, SI } },
+
+{ "mulli",   OP(7),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "muli",    OP(7),	OP_MASK,	POWER,		{ RT, RA, SI } },
+
+{ "subfic",  OP(8),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "sfi",     OP(8),	OP_MASK,	POWER,		{ RT, RA, SI } },
+
+{ "dozi",    OP(9),	OP_MASK,	POWER|M601,	{ RT, RA, SI } },
+
+{ "cmplwi",  OPL(10,0),	OPL_MASK,	PPC,		{ OBF, RA, UI } },
+{ "cmpldi",  OPL(10,1), OPL_MASK,	PPC|B64,	{ OBF, RA, UI } },
+{ "cmpli",   OP(10),	OP_MASK,	PPC,		{ BF, L, RA, UI } },
+{ "cmpli",   OP(10),	OP_MASK,	POWER,		{ BF, RA, UI } },
+
+{ "cmpwi",   OPL(11,0),	OPL_MASK,	PPC,		{ OBF, RA, SI } },
+{ "cmpdi",   OPL(11,1),	OPL_MASK,	PPC|B64,	{ OBF, RA, SI } },
+{ "cmpi",    OP(11),	OP_MASK,	PPC,		{ BF, L, RA, SI } },
+{ "cmpi",    OP(11),	OP_MASK,	POWER,		{ BF, RA, SI } },
+
+{ "addic",   OP(12),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "ai",	     OP(12),	OP_MASK,	POWER,		{ RT, RA, SI } },
+{ "subic",   OP(12),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+
+{ "addic.",  OP(13),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "ai.",     OP(13),	OP_MASK,	POWER,		{ RT, RA, SI } },
+{ "subic.",  OP(13),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+
+{ "li",	     OP(14),	DRA_MASK,	PPC,		{ RT, SI } },
+{ "lil",     OP(14),	DRA_MASK,	POWER,		{ RT, SI } },
+{ "addi",    OP(14),	OP_MASK,	PPC,		{ RT, RA, SI } },
+{ "cal",     OP(14),	OP_MASK,	POWER,		{ RT, D, RA } },
+{ "subi",    OP(14),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+{ "la",	     OP(14),	OP_MASK,	PPC,		{ RT, D, RA } },
+
+{ "lis",     OP(15),	DRA_MASK,	PPC,		{ RT, SISIGNOPT } },
+{ "liu",     OP(15),	DRA_MASK,	POWER,		{ RT, SISIGNOPT } },
+{ "addis",   OP(15),	OP_MASK,	PPC,		{ RT,RA,SISIGNOPT } },
+{ "cau",     OP(15),	OP_MASK,	POWER,		{ RT,RA,SISIGNOPT } },
+{ "subis",   OP(15),	OP_MASK,	PPC,		{ RT, RA, NSI } },
+
+{ "bdnz-",   BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdnz+",   BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdnz",    BBO(16,BODNZ,0,0), BBOYBI_MASK, PPC,	{ BD } },
+{ "bdn",     BBO(16,BODNZ,0,0), BBOYBI_MASK, POWER,	{ BD } },
+{ "bdnzl-",  BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdnzl+",  BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdnzl",   BBO(16,BODNZ,0,1), BBOYBI_MASK, PPC,	{ BD } },
+{ "bdnl",    BBO(16,BODNZ,0,1), BBOYBI_MASK, POWER,	{ BD } },
+{ "bdnza-",  BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdnza+",  BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdnza",   BBO(16,BODNZ,1,0), BBOYBI_MASK, PPC,	{ BDA } },
+{ "bdna",    BBO(16,BODNZ,1,0), BBOYBI_MASK, POWER,	{ BDA } },
+{ "bdnzla-", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdnzla+", BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdnzla",  BBO(16,BODNZ,1,1), BBOYBI_MASK, PPC,	{ BDA } },
+{ "bdnla",   BBO(16,BODNZ,1,1), BBOYBI_MASK, POWER,	{ BDA } },
+{ "bdz-",    BBO(16,BODZ,0,0), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdz+",    BBO(16,BODZ,0,0), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdz",     BBO(16,BODZ,0,0), BBOYBI_MASK, PPC|POWER,	{ BD } },
+{ "bdzl-",   BBO(16,BODZ,0,1), BBOYBI_MASK, PPC,	{ BDM } },
+{ "bdzl+",   BBO(16,BODZ,0,1), BBOYBI_MASK, PPC,	{ BDP } },
+{ "bdzl",    BBO(16,BODZ,0,1), BBOYBI_MASK, PPC|POWER,	{ BD } },
+{ "bdza-",   BBO(16,BODZ,1,0), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdza+",   BBO(16,BODZ,1,0), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdza",    BBO(16,BODZ,1,0), BBOYBI_MASK, PPC|POWER,	{ BDA } },
+{ "bdzla-",  BBO(16,BODZ,1,1), BBOYBI_MASK, PPC,	{ BDMA } },
+{ "bdzla+",  BBO(16,BODZ,1,1), BBOYBI_MASK, PPC,	{ BDPA } },
+{ "bdzla",   BBO(16,BODZ,1,1), BBOYBI_MASK, PPC|POWER,	{ BDA } },
+{ "blt-",    BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "blt+",    BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "blt",     BBOCB(16,BOT,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bltl-",   BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bltl+",   BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bltl",    BBOCB(16,BOT,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blta-",   BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "blta+",   BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "blta",    BBOCB(16,BOT,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bltla-",  BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bltla+",  BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bltla",   BBOCB(16,BOT,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgt-",    BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bgt+",    BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bgt",     BBOCB(16,BOT,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgtl-",   BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bgtl+",   BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bgtl",    BBOCB(16,BOT,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgta-",   BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgta+",   BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgta",    BBOCB(16,BOT,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgtla-",  BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgtla+",  BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgtla",   BBOCB(16,BOT,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beq-",    BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "beq+",    BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "beq",     BBOCB(16,BOT,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beql-",   BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "beql+",   BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "beql",    BBOCB(16,BOT,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "beqa-",   BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "beqa+",   BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "beqa",    BBOCB(16,BOT,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "beqla-",  BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "beqla+",  BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "beqla",   BBOCB(16,BOT,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bso-",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bso+",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bso",     BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsol-",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bsol+",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bsol",    BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bsoa-",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bsoa+",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bsoa",    BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bsola-",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bsola+",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bsola",   BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bun-",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bun+",    BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bun",     BBOCB(16,BOT,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "bunl-",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bunl+",   BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bunl",    BBOCB(16,BOT,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "buna-",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "buna+",   BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "buna",    BBOCB(16,BOT,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bunla-",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bunla+",  BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bunla",   BBOCB(16,BOT,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bge-",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bge+",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bge",     BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgel-",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bgel+",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bgel",    BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bgea-",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgea+",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgea",    BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bgela-",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bgela+",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bgela",   BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnl-",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnl+",    BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnl",     BBOCB(16,BOF,CBLT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnll-",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnll+",   BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnll",    BBOCB(16,BOF,CBLT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnla-",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnla+",   BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnla",    BBOCB(16,BOF,CBLT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnlla-",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnlla+",  BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnlla",   BBOCB(16,BOF,CBLT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "ble-",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "ble+",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "ble",     BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blel-",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "blel+",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "blel",    BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "blea-",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "blea+",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "blea",    BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "blela-",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "blela+",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "blela",   BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bng-",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bng+",    BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bng",     BBOCB(16,BOF,CBGT,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bngl-",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bngl+",   BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bngl",    BBOCB(16,BOF,CBGT,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnga-",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnga+",   BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnga",    BBOCB(16,BOF,CBGT,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bngla-",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bngla+",  BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bngla",   BBOCB(16,BOF,CBGT,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bne-",    BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bne+",    BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bne",     BBOCB(16,BOF,CBEQ,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnel-",   BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnel+",   BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnel",    BBOCB(16,BOF,CBEQ,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnea-",   BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnea+",   BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnea",    BBOCB(16,BOF,CBEQ,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnela-",  BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnela+",  BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnela",   BBOCB(16,BOF,CBEQ,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bns-",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bns+",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bns",     BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsl-",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnsl+",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnsl",    BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC|POWER, { CR, BD } },
+{ "bnsa-",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnsa+",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnsa",    BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnsla-",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnsla+",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnsla",   BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC|POWER, { CR, BDA } },
+{ "bnu-",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnu+",    BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnu",     BBOCB(16,BOF,CBSO,0,0), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "bnul-",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDM } },
+{ "bnul+",   BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BDP } },
+{ "bnul",    BBOCB(16,BOF,CBSO,0,1), BBOYCB_MASK, PPC,	{ CR, BD } },
+{ "bnua-",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnua+",   BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnua",    BBOCB(16,BOF,CBSO,1,0), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bnula-",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDMA } },
+{ "bnula+",  BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDPA } },
+{ "bnula",   BBOCB(16,BOF,CBSO,1,1), BBOYCB_MASK, PPC,	{ CR, BDA } },
+{ "bdnzt-",  BBO(16,BODNZT,0,0), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnzt+",  BBO(16,BODNZT,0,0), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnzt",   BBO(16,BODNZT,0,0), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnztl-", BBO(16,BODNZT,0,1), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnztl+", BBO(16,BODNZT,0,1), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnztl",  BBO(16,BODNZT,0,1), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnzta-", BBO(16,BODNZT,1,0), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnzta+", BBO(16,BODNZT,1,0), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnzta",  BBO(16,BODNZT,1,0), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bdnztla-",BBO(16,BODNZT,1,1), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnztla+",BBO(16,BODNZT,1,1), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnztla", BBO(16,BODNZT,1,1), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bdnzf-",  BBO(16,BODNZF,0,0), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnzf+",  BBO(16,BODNZF,0,0), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnzf",   BBO(16,BODNZF,0,0), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnzfl-", BBO(16,BODNZF,0,1), BBOY_MASK, PPC,	{ BI, BDM } },
+{ "bdnzfl+", BBO(16,BODNZF,0,1), BBOY_MASK, PPC,	{ BI, BDP } },
+{ "bdnzfl",  BBO(16,BODNZF,0,1), BBOY_MASK, PPC,	{ BI, BD } },
+{ "bdnzfa-", BBO(16,BODNZF,1,0), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnzfa+", BBO(16,BODNZF,1,0), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnzfa",  BBO(16,BODNZF,1,0), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bdnzfla-",BBO(16,BODNZF,1,1), BBOY_MASK, PPC,	{ BI, BDMA } },
+{ "bdnzfla+",BBO(16,BODNZF,1,1), BBOY_MASK, PPC,	{ BI, BDPA } },
+{ "bdnzfla", BBO(16,BODNZF,1,1), BBOY_MASK, PPC,	{ BI, BDA } },
+{ "bt-",     BBO(16,BOT,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bt+",     BBO(16,BOT,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bt",	     BBO(16,BOT,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbt",     BBO(16,BOT,0,0), BBOY_MASK, POWER,		{ BI, BD } },
+{ "btl-",    BBO(16,BOT,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "btl+",    BBO(16,BOT,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "btl",     BBO(16,BOT,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbtl",    BBO(16,BOT,0,1), BBOY_MASK, POWER,		{ BI, BD } },
+{ "bta-",    BBO(16,BOT,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bta+",    BBO(16,BOT,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bta",     BBO(16,BOT,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbta",    BBO(16,BOT,1,0), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "btla-",   BBO(16,BOT,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "btla+",   BBO(16,BOT,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "btla",    BBO(16,BOT,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbtla",   BBO(16,BOT,1,1), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "bf-",     BBO(16,BOF,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bf+",     BBO(16,BOF,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bf",	     BBO(16,BOF,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbf",     BBO(16,BOF,0,0), BBOY_MASK, POWER,		{ BI, BD } },
+{ "bfl-",    BBO(16,BOF,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bfl+",    BBO(16,BOF,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bfl",     BBO(16,BOF,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bbfl",    BBO(16,BOF,0,1), BBOY_MASK, POWER,		{ BI, BD } },
+{ "bfa-",    BBO(16,BOF,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bfa+",    BBO(16,BOF,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bfa",     BBO(16,BOF,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbfa",    BBO(16,BOF,1,0), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "bfla-",   BBO(16,BOF,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bfla+",   BBO(16,BOF,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bfla",    BBO(16,BOF,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bbfla",   BBO(16,BOF,1,1), BBOY_MASK, POWER,		{ BI, BDA } },
+{ "bdzt-",   BBO(16,BODZT,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdzt+",   BBO(16,BODZT,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdzt",    BBO(16,BODZT,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdztl-",  BBO(16,BODZT,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdztl+",  BBO(16,BODZT,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdztl",   BBO(16,BODZT,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdzta-",  BBO(16,BODZT,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdzta+",  BBO(16,BODZT,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdzta",   BBO(16,BODZT,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bdztla-", BBO(16,BODZT,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdztla+", BBO(16,BODZT,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdztla",  BBO(16,BODZT,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bdzf-",   BBO(16,BODZF,0,0), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdzf+",   BBO(16,BODZF,0,0), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdzf",    BBO(16,BODZF,0,0), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdzfl-",  BBO(16,BODZF,0,1), BBOY_MASK, PPC,		{ BI, BDM } },
+{ "bdzfl+",  BBO(16,BODZF,0,1), BBOY_MASK, PPC,		{ BI, BDP } },
+{ "bdzfl",   BBO(16,BODZF,0,1), BBOY_MASK, PPC,		{ BI, BD } },
+{ "bdzfa-",  BBO(16,BODZF,1,0), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdzfa+",  BBO(16,BODZF,1,0), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdzfa",   BBO(16,BODZF,1,0), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bdzfla-", BBO(16,BODZF,1,1), BBOY_MASK, PPC,		{ BI, BDMA } },
+{ "bdzfla+", BBO(16,BODZF,1,1), BBOY_MASK, PPC,		{ BI, BDPA } },
+{ "bdzfla",  BBO(16,BODZF,1,1), BBOY_MASK, PPC,		{ BI, BDA } },
+{ "bc-",     B(16,0,0),	B_MASK,		PPC,		{ BOE, BI, BDM } },
+{ "bc+",     B(16,0,0),	B_MASK,		PPC,		{ BOE, BI, BDP } },
+{ "bc",	     B(16,0,0),	B_MASK,		PPC|POWER,	{ BO, BI, BD } },
+{ "bcl-",    B(16,0,1),	B_MASK,		PPC,		{ BOE, BI, BDM } },
+{ "bcl+",    B(16,0,1),	B_MASK,		PPC,		{ BOE, BI, BDP } },
+{ "bcl",     B(16,0,1),	B_MASK,		PPC|POWER,	{ BO, BI, BD } },
+{ "bca-",    B(16,1,0),	B_MASK,		PPC,		{ BOE, BI, BDMA } },
+{ "bca+",    B(16,1,0),	B_MASK,		PPC,		{ BOE, BI, BDPA } },
+{ "bca",     B(16,1,0),	B_MASK,		PPC|POWER,	{ BO, BI, BDA } },
+{ "bcla-",   B(16,1,1),	B_MASK,		PPC,		{ BOE, BI, BDMA } },
+{ "bcla+",   B(16,1,1),	B_MASK,		PPC,		{ BOE, BI, BDPA } },
+{ "bcla",    B(16,1,1),	B_MASK,		PPC|POWER,	{ BO, BI, BDA } },
+
+{ "sc",      SC(17,1,0), 0xffffffff,	PPC,		{ 0 } },
+{ "svc",     SC(17,0,0), SC_MASK,	POWER,		{ LEV, FL1, FL2 } },
+{ "svcl",    SC(17,0,1), SC_MASK,	POWER,		{ LEV, FL1, FL2 } },
+{ "svca",    SC(17,1,0), SC_MASK,	POWER,		{ SV } },
+{ "svcla",   SC(17,1,1), SC_MASK,	POWER,		{ SV } },
+
+{ "b",	     B(18,0,0),	B_MASK,		PPC|POWER,	{ LI } },
+{ "bl",      B(18,0,1),	B_MASK,		PPC|POWER,	{ LI } },
+{ "ba",      B(18,1,0),	B_MASK,		PPC|POWER,	{ LIA } },
+{ "bla",     B(18,1,1),	B_MASK,		PPC|POWER,	{ LIA } },
+
+{ "mcrf",    XL(19,0),	XLBB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "blr",     XLO(19,BOU,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "br",      XLO(19,BOU,16,0), XLBOBIBB_MASK, POWER,	{ 0 } },
+{ "blrl",    XLO(19,BOU,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "brl",     XLO(19,BOU,16,1), XLBOBIBB_MASK, POWER,	{ 0 } },
+{ "bdnzlr",  XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlr-", XLO(19,BODNZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlr+", XLO(19,BODNZP,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlrl", XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlrl-",XLO(19,BODNZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdnzlrl+",XLO(19,BODNZP,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlr",   XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlr-",  XLO(19,BODZ,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlr+",  XLO(19,BODZP,16,0), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlrl",  XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlrl-", XLO(19,BODZ,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bdzlrl+", XLO(19,BODZP,16,1), XLBOBIBB_MASK, PPC,	{ 0 } },
+{ "bltlr",   XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr-",  XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlr+",  XLOCB(19,BOTP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltr",    XLOCB(19,BOT,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bltlrl",  XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl-", XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltlrl+", XLOCB(19,BOTP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltrl",   XLOCB(19,BOT,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlr",   XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr-",  XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlr+",  XLOCB(19,BOTP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtr",    XLOCB(19,BOT,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgtlrl",  XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl-", XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtlrl+", XLOCB(19,BOTP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtrl",   XLOCB(19,BOT,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlr",   XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr-",  XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlr+",  XLOCB(19,BOTP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqr",    XLOCB(19,BOT,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "beqlrl",  XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl-", XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqlrl+", XLOCB(19,BOTP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqrl",   XLOCB(19,BOT,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolr",   XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr-",  XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolr+",  XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsor",    XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bsolrl",  XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsolrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsorl",   XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bunlr",   XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr-",  XLOCB(19,BOT,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlr+",  XLOCB(19,BOTP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl",  XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl-", XLOCB(19,BOT,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunlrl+", XLOCB(19,BOTP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr",   XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr-",  XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelr+",  XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bger",    XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bgelrl",  XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgelrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgerl",   XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllr",   XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr-",  XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllr+",  XLOCB(19,BOFP,CBLT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlr",    XLOCB(19,BOF,CBLT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnllrl",  XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl-", XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnllrl+", XLOCB(19,BOFP,CBLT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlrl",   XLOCB(19,BOF,CBLT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelr",   XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr-",  XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelr+",  XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bler",    XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "blelrl",  XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blelrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blerl",   XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglr",   XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr-",  XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglr+",  XLOCB(19,BOFP,CBGT,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngr",    XLOCB(19,BOF,CBGT,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnglrl",  XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl-", XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnglrl+", XLOCB(19,BOFP,CBGT,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngrl",   XLOCB(19,BOF,CBGT,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelr",   XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr-",  XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelr+",  XLOCB(19,BOFP,CBEQ,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bner",    XLOCB(19,BOF,CBEQ,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnelrl",  XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl-", XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnelrl+", XLOCB(19,BOFP,CBEQ,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnerl",   XLOCB(19,BOF,CBEQ,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslr",   XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr-",  XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslr+",  XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsr",    XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnslrl",  XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnslrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsrl",   XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, POWER, { CR } },
+{ "bnulr",   XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr-",  XLOCB(19,BOF,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulr+",  XLOCB(19,BOFP,CBSO,16,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl",  XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl-", XLOCB(19,BOF,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnulrl+", XLOCB(19,BOFP,CBSO,16,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btlr",    XLO(19,BOT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlr-",   XLO(19,BOT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlr+",   XLO(19,BOTP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbtr",    XLO(19,BOT,16,0), XLBOBB_MASK, POWER,	{ BI } },
+{ "btlrl",   XLO(19,BOT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlrl-",  XLO(19,BOT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btlrl+",  XLO(19,BOTP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbtrl",   XLO(19,BOT,16,1), XLBOBB_MASK, POWER,	{ BI } },
+{ "bflr",    XLO(19,BOF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflr-",   XLO(19,BOF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflr+",   XLO(19,BOFP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbfr",    XLO(19,BOF,16,0), XLBOBB_MASK, POWER,	{ BI } },
+{ "bflrl",   XLO(19,BOF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflrl-",  XLO(19,BOF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bflrl+",  XLO(19,BOFP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bbfrl",   XLO(19,BOF,16,1), XLBOBB_MASK, POWER,	{ BI } },
+{ "bdnztlr", XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlr-",XLO(19,BODNZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlr+",XLO(19,BODNZTP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlrl",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlrl-",XLO(19,BODNZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnztlrl+",XLO(19,BODNZTP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflr", XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflr-",XLO(19,BODNZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflr+",XLO(19,BODNZFP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflrl",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflrl-",XLO(19,BODNZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdnzflrl+",XLO(19,BODNZFP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlr",  XLO(19,BODZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlr-", XLO(19,BODZT,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlr+", XLO(19,BODZTP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlrl", XLO(19,BODZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlrl-",XLO(19,BODZT,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdztlrl+",XLO(19,BODZTP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflr",  XLO(19,BODZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflr-", XLO(19,BODZF,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflr+", XLO(19,BODZFP,16,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflrl", XLO(19,BODZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflrl-",XLO(19,BODZF,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bdzflrl+",XLO(19,BODZFP,16,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bclr",    XLLK(19,16,0), XLYBB_MASK,	PPC,		{ BO, BI } },
+{ "bclrl",   XLLK(19,16,1), XLYBB_MASK,	PPC,		{ BO, BI } },
+{ "bclr+",   XLYLK(19,16,1,0), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bclrl+",  XLYLK(19,16,1,1), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bclr-",   XLYLK(19,16,0,0), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bclrl-",  XLYLK(19,16,0,1), XLYBB_MASK, PPC,		{ BOE, BI } },
+{ "bcr",     XLLK(19,16,0), XLBB_MASK,	POWER,		{ BO, BI } },
+{ "bcrl",    XLLK(19,16,1), XLBB_MASK,	POWER,		{ BO, BI } },
+
+{ "crnot",   XL(19,33), XL_MASK,	PPC,		{ BT, BA, BBA } },
+{ "crnor",   XL(19,33),	XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "rfi",     XL(19,50),	0xffffffff,	PPC|POWER,	{ 0 } },
+{ "rfci",    XL(19,51),	0xffffffff,	PPC,		{ 0 } },
+
+{ "rfsvc",   XL(19,82),	0xffffffff,	POWER,		{ 0 } },
+
+{ "crandc",  XL(19,129), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "isync",   XL(19,150), 0xffffffff,	PPC,		{ 0 } },
+{ "ics",     XL(19,150), 0xffffffff,	POWER,		{ 0 } },
+
+{ "crclr",   XL(19,193), XL_MASK,	PPC,		{ BT, BAT, BBA } },
+{ "crxor",   XL(19,193), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crnand",  XL(19,225), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crand",   XL(19,257), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crset",   XL(19,289), XL_MASK,	PPC,		{ BT, BAT, BBA } },
+{ "creqv",   XL(19,289), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crorc",   XL(19,417), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "crmove",  XL(19,449), XL_MASK,	PPC,		{ BT, BA, BBA } },
+{ "cror",    XL(19,449), XL_MASK,	PPC|POWER,	{ BT, BA, BB } },
+
+{ "bctr",    XLO(19,BOU,528,0), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bctrl",   XLO(19,BOU,528,1), XLBOBIBB_MASK, PPC|POWER, { 0 } },
+{ "bltctr",  XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr-", XLOCB(19,BOT,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctr+", XLOCB(19,BOTP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl", XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl-",XLOCB(19,BOT,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bltctrl+",XLOCB(19,BOTP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr",  XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr-", XLOCB(19,BOT,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctr+", XLOCB(19,BOTP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl", XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl-",XLOCB(19,BOT,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgtctrl+",XLOCB(19,BOTP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr",  XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr-", XLOCB(19,BOT,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctr+", XLOCB(19,BOTP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl", XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl-",XLOCB(19,BOT,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "beqctrl+",XLOCB(19,BOTP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr",  XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bsoctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr",  XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr-", XLOCB(19,BOT,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctr+", XLOCB(19,BOTP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl", XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl-",XLOCB(19,BOT,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bunctrl+",XLOCB(19,BOTP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr",  XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bgectrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr",  XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr-", XLOCB(19,BOF,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctr+", XLOCB(19,BOFP,CBLT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl", XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl-",XLOCB(19,BOF,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnlctrl+",XLOCB(19,BOFP,CBLT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr",  XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "blectrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr",  XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr-", XLOCB(19,BOF,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctr+", XLOCB(19,BOFP,CBGT,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl", XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl-",XLOCB(19,BOF,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bngctrl+",XLOCB(19,BOFP,CBGT,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr",  XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr-", XLOCB(19,BOF,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectr+", XLOCB(19,BOFP,CBEQ,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl", XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl-",XLOCB(19,BOF,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnectrl+",XLOCB(19,BOFP,CBEQ,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr",  XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnsctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr",  XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr-", XLOCB(19,BOF,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctr+", XLOCB(19,BOFP,CBSO,528,0), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl", XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl-",XLOCB(19,BOF,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "bnuctrl+",XLOCB(19,BOFP,CBSO,528,1), XLBOCBBB_MASK, PPC, { CR } },
+{ "btctr",   XLO(19,BOT,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctr-",  XLO(19,BOT,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctr+",  XLO(19,BOTP,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctrl",  XLO(19,BOT,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctrl-", XLO(19,BOT,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "btctrl+", XLO(19,BOTP,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctr",   XLO(19,BOF,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctr-",  XLO(19,BOF,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctr+",  XLO(19,BOFP,528,0), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctrl",  XLO(19,BOF,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctrl-", XLO(19,BOF,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bfctrl+", XLO(19,BOFP,528,1), XLBOBB_MASK, PPC,	{ BI } },
+{ "bcctr",   XLLK(19,528,0), XLYBB_MASK, PPC,		{ BO, BI } },
+{ "bcctr-",  XLYLK(19,528,0,0), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcctr+",  XLYLK(19,528,1,0), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcctrl",  XLLK(19,528,1), XLYBB_MASK, PPC,		{ BO, BI } },
+{ "bcctrl-", XLYLK(19,528,0,1), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcctrl+", XLYLK(19,528,1,1), XLYBB_MASK, PPC,	{ BOE, BI } },
+{ "bcc",     XLLK(19,528,0), XLBB_MASK,	POWER,		{ BO, BI } },
+{ "bccl",    XLLK(19,528,1), XLBB_MASK,	POWER,		{ BO, BI } },
+
+{ "rlwimi",  M(20,0),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlimi",   M(20,0),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+
+{ "rlwimi.", M(20,1),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlimi.",  M(20,1),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+
+{ "rotlwi",  MME(21,31,0), MMBME_MASK,	PPC,		{ RA, RS, SH } },
+{ "clrlwi",  MME(21,31,0), MSHME_MASK,	PPC,		{ RA, RS, MB } },
+{ "rlwinm",  M(21,0),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlinm",   M(21,0),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+{ "rotlwi.", MME(21,31,1), MMBME_MASK,	PPC,		{ RA,RS,SH } },
+{ "clrlwi.", MME(21,31,1), MSHME_MASK,	PPC,		{ RA, RS, MB } },
+{ "rlwinm.", M(21,1),	M_MASK,		PPC,		{ RA,RS,SH,MBE,ME } },
+{ "rlinm.",  M(21,1),	M_MASK,		POWER,		{ RA,RS,SH,MBE,ME } },
+
+{ "rlmi",    M(22,0),	M_MASK,		POWER|M601,	{ RA,RS,RB,MBE,ME } },
+{ "rlmi.",   M(22,1),	M_MASK,		POWER|M601,	{ RA,RS,RB,MBE,ME } },
+
+{ "rotlw",   MME(23,31,0), MMBME_MASK,	PPC,		{ RA, RS, RB } },
+{ "rlwnm",   M(23,0),	M_MASK,		PPC,		{ RA,RS,RB,MBE,ME } },
+{ "rlnm",    M(23,0),	M_MASK,		POWER,		{ RA,RS,RB,MBE,ME } },
+{ "rotlw.",  MME(23,31,1), MMBME_MASK,	PPC,		{ RA, RS, RB } },
+{ "rlwnm.",  M(23,1),	M_MASK,		PPC,		{ RA,RS,RB,MBE,ME } },
+{ "rlnm.",   M(23,1),	M_MASK,		POWER,		{ RA,RS,RB,MBE,ME } },
+
+{ "nop",     OP(24),	0xffffffff,	PPC,		{ 0 } },
+{ "ori",     OP(24),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "oril",    OP(24),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "oris",    OP(25),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "oriu",    OP(25),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "xori",    OP(26),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "xoril",   OP(26),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "xoris",   OP(27),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "xoriu",   OP(27),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "andi.",   OP(28),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "andil.",  OP(28),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "andis.",  OP(29),	OP_MASK,	PPC,		{ RA, RS, UI } },
+{ "andiu.",  OP(29),	OP_MASK,	POWER,		{ RA, RS, UI } },
+
+{ "rotldi",  MD(30,0,0), MDMB_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+{ "clrldi",  MD(30,0,0), MDSH_MASK,	PPC|B64,	{ RA, RS, MB6 } },
+{ "rldicl",  MD(30,0,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+{ "rotldi.", MD(30,0,1), MDMB_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+{ "clrldi.", MD(30,0,1), MDSH_MASK,	PPC|B64,	{ RA, RS, MB6 } },
+{ "rldicl.", MD(30,0,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+
+{ "rldicr",  MD(30,1,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, ME6 } },
+{ "rldicr.", MD(30,1,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, ME6 } },
+
+{ "rldic",   MD(30,2,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+{ "rldic.",  MD(30,2,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+
+{ "rldimi",  MD(30,3,0), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+{ "rldimi.", MD(30,3,1), MD_MASK,	PPC|B64,	{ RA, RS, SH6, MB6 } },
+
+{ "rotld",   MDS(30,8,0), MDSMB_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "rldcl",   MDS(30,8,0), MDS_MASK,	PPC|B64,	{ RA, RS, RB, MB6 } },
+{ "rotld.",  MDS(30,8,1), MDSMB_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "rldcl.",  MDS(30,8,1), MDS_MASK,	PPC|B64,	{ RA, RS, RB, MB6 } },
+
+{ "rldcr",   MDS(30,9,0), MDS_MASK,	PPC|B64,	{ RA, RS, RB, ME6 } },
+{ "rldcr.",  MDS(30,9,1), MDS_MASK,	PPC|B64,	{ RA, RS, RB, ME6 } },
+
+{ "cmpw",    XCMPL(31,0,0), XCMPL_MASK, PPC,		{ OBF, RA, RB } },
+{ "cmpd",    XCMPL(31,0,1), XCMPL_MASK, PPC|B64,	{ OBF, RA, RB } },
+{ "cmp",     X(31,0),	XCMP_MASK,	PPC,		{ BF, L, RA, RB } },
+{ "cmp",     X(31,0),	XCMPL_MASK,	POWER,		{ BF, RA, RB } },
+
+{ "twlgt",   XTO(31,4,TOLGT), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlgt",    XTO(31,4,TOLGT), XTO_MASK, POWER,		{ RA, RB } },
+{ "twllt",   XTO(31,4,TOLLT), XTO_MASK, PPC,		{ RA, RB } },
+{ "tllt",    XTO(31,4,TOLLT), XTO_MASK, POWER,		{ RA, RB } },
+{ "tweq",    XTO(31,4,TOEQ), XTO_MASK,	PPC,		{ RA, RB } },
+{ "teq",     XTO(31,4,TOEQ), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twlge",   XTO(31,4,TOLGE), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlge",    XTO(31,4,TOLGE), XTO_MASK, POWER,		{ RA, RB } },
+{ "twlnl",   XTO(31,4,TOLNL), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlnl",    XTO(31,4,TOLNL), XTO_MASK, POWER,		{ RA, RB } },
+{ "twlle",   XTO(31,4,TOLLE), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlle",    XTO(31,4,TOLLE), XTO_MASK, POWER,		{ RA, RB } },
+{ "twlng",   XTO(31,4,TOLNG), XTO_MASK, PPC,		{ RA, RB } },
+{ "tlng",    XTO(31,4,TOLNG), XTO_MASK, POWER,		{ RA, RB } },
+{ "twgt",    XTO(31,4,TOGT), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tgt",     XTO(31,4,TOGT), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twge",    XTO(31,4,TOGE), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tge",     XTO(31,4,TOGE), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twnl",    XTO(31,4,TONL), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tnl",     XTO(31,4,TONL), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twlt",    XTO(31,4,TOLT), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tlt",     XTO(31,4,TOLT), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twle",    XTO(31,4,TOLE), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tle",     XTO(31,4,TOLE), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twng",    XTO(31,4,TONG), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tng",     XTO(31,4,TONG), XTO_MASK,	POWER,		{ RA, RB } },
+{ "twne",    XTO(31,4,TONE), XTO_MASK,	PPC,		{ RA, RB } },
+{ "tne",     XTO(31,4,TONE), XTO_MASK,	POWER,		{ RA, RB } },
+{ "trap",    XTO(31,4,TOU), 0xffffffff,	PPC,		{ 0 } },
+{ "tw",      X(31,4),	X_MASK,		PPC,		{ TO, RA, RB } },
+{ "t",       X(31,4),	X_MASK,		POWER,		{ TO, RA, RB } },
+
+{ "subfc",   XO(31,8,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sf",      XO(31,8,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subc",    XO(31,8,0,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfc.",  XO(31,8,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sf.",     XO(31,8,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subc.",   XO(31,8,0,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfco",  XO(31,8,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfo",     XO(31,8,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subco",   XO(31,8,1,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfco.", XO(31,8,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfo.",    XO(31,8,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subco.",  XO(31,8,1,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+
+{ "mulhdu",  XO(31,9,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulhdu.", XO(31,9,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "addc",    XO(31,10,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "a",       XO(31,10,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addc.",   XO(31,10,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "a.",      XO(31,10,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addco",   XO(31,10,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ao",      XO(31,10,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addco.",  XO(31,10,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ao.",     XO(31,10,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "mulhwu",  XO(31,11,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulhwu.", XO(31,11,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "mfcr",    X(31,19),	XRARB_MASK,	POWER|PPC,	{ RT } },
+
+{ "lwarx",   X(31,20),	X_MASK,		PPC,		{ RT, RA, RB } },
+
+{ "ldx",     X(31,21),	X_MASK,		PPC|B64,	{ RT, RA, RB } },
+
+{ "lwzx",    X(31,23),	X_MASK,		PPC,		{ RT, RA, RB } },
+{ "lx",      X(31,23),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "slw",     XRC(31,24,0), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sl",      XRC(31,24,0), X_MASK,	POWER,		{ RA, RS, RB } },
+{ "slw.",    XRC(31,24,1), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sl.",     XRC(31,24,1), X_MASK,	POWER,		{ RA, RS, RB } },
+
+{ "cntlzw",  XRC(31,26,0), XRB_MASK,	PPC,		{ RA, RS } },
+{ "cntlz",   XRC(31,26,0), XRB_MASK,	POWER,		{ RA, RS } },
+{ "cntlzw.", XRC(31,26,1), XRB_MASK,	PPC,		{ RA, RS } },
+{ "cntlz.",  XRC(31,26,1), XRB_MASK, 	POWER,		{ RA, RS } },
+
+{ "sld",     XRC(31,27,0), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "sld.",    XRC(31,27,1), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+
+{ "and",     XRC(31,28,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "and.",    XRC(31,28,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "maskg",   XRC(31,29,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "maskg.",  XRC(31,29,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "cmplw",   XCMPL(31,32,0), XCMPL_MASK, PPC,		{ OBF, RA, RB } },
+{ "cmpld",   XCMPL(31,32,1), XCMPL_MASK, PPC|B64,	{ OBF, RA, RB } },
+{ "cmpl",    X(31,32),	XCMP_MASK,	PPC,		{ BF, L, RA, RB } },
+{ "cmpl",    X(31,32),	XCMPL_MASK,	POWER,		{ BF, RA, RB } },
+
+{ "subf",    XO(31,40,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sub",     XO(31,40,0,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subf.",   XO(31,40,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sub.",    XO(31,40,0,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfo",   XO(31,40,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "subo",    XO(31,40,1,0), XO_MASK,	PPC,		{ RT, RB, RA } },
+{ "subfo.",  XO(31,40,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "subo.",   XO(31,40,1,1), XO_MASK,	PPC,		{ RT, RB, RA } },
+
+{ "ldux",    X(31,53),	X_MASK,		PPC|B64,	{ RT, RAL, RB } },
+
+{ "dcbst",   X(31,54),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lwzux",   X(31,55),	X_MASK,		PPC,		{ RT, RAL, RB } },
+{ "lux",     X(31,55),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "cntlzd",  XRC(31,58,0), XRB_MASK,	PPC|B64,	{ RA, RS } },
+{ "cntlzd.", XRC(31,58,1), XRB_MASK,	PPC|B64,	{ RA, RS } },
+
+{ "andc",    XRC(31,60,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "andc.",   XRC(31,60,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "tdlgt",   XTO(31,68,TOLGT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdllt",   XTO(31,68,TOLLT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdeq",    XTO(31,68,TOEQ), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlge",   XTO(31,68,TOLGE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlnl",   XTO(31,68,TOLNL), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlle",   XTO(31,68,TOLLE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlng",   XTO(31,68,TOLNG), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdgt",    XTO(31,68,TOGT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdge",    XTO(31,68,TOGE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdnl",    XTO(31,68,TONL), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdlt",    XTO(31,68,TOLT), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdle",    XTO(31,68,TOLE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdng",    XTO(31,68,TONG), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "tdne",    XTO(31,68,TONE), XTO_MASK, PPC|B64,	{ RA, RB } },
+{ "td",	     X(31,68),	X_MASK,		PPC|B64,	{ TO, RA, RB } },
+
+{ "mulhd",   XO(31,73,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulhd.",  XO(31,73,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "mulhw",   XO(31,75,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulhw.",  XO(31,75,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "mfmsr",   X(31,83),	XRARB_MASK,	PPC|POWER,	{ RT } },
+
+{ "ldarx",   X(31,84),	X_MASK,		PPC|B64,	{ RT, RA, RB } },
+
+{ "dcbf",    X(31,86),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lbzx",    X(31,87),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "neg",     XO(31,104,0,0), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+{ "neg.",    XO(31,104,0,1), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+{ "nego",    XO(31,104,1,0), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+{ "nego.",   XO(31,104,1,1), XORB_MASK,	PPC|POWER,	{ RT, RA } },
+
+{ "mul",     XO(31,107,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "mul.",    XO(31,107,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "mulo",    XO(31,107,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "mulo.",   XO(31,107,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "clf",     X(31,118), XRB_MASK,	POWER,		{ RT, RA } },
+
+{ "lbzux",   X(31,119),	X_MASK,		PPC|POWER,	{ RT, RAL, RB } },
+
+{ "not",     XRC(31,124,0), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "nor",     XRC(31,124,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "not.",    XRC(31,124,1), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "nor.",    XRC(31,124,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "subfe",   XO(31,136,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfe",     XO(31,136,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subfe.",  XO(31,136,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfe.",    XO(31,136,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subfeo",  XO(31,136,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfeo",    XO(31,136,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "subfeo.", XO(31,136,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "sfeo.",   XO(31,136,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "adde",    XO(31,138,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ae",      XO(31,138,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "adde.",   XO(31,138,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "ae.",     XO(31,138,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addeo",   XO(31,138,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "aeo",     XO(31,138,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addeo.",  XO(31,138,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "aeo.",    XO(31,138,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "mtcr",    XFXM(31,144,0xff), XFXFXM_MASK|FXM_MASK, PPC|POWER, { RS }},
+{ "mtcrf",   X(31,144),	XFXFXM_MASK,	PPC|POWER,	{ FXM, RS } },
+
+{ "mtmsr",   X(31,146),	XRARB_MASK,	PPC|POWER,	{ RS } },
+
+{ "stdx",    X(31,149), X_MASK,		PPC|B64,	{ RS, RA, RB } },
+
+{ "stwcx.",  XRC(31,150,1), X_MASK,	PPC,		{ RS, RA, RB } },
+
+{ "stwx",    X(31,151), X_MASK,		PPC,		{ RS, RA, RB } },
+{ "stx",     X(31,151), X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "slq",     XRC(31,152,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "slq.",    XRC(31,152,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sle",     XRC(31,153,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sle.",    XRC(31,153,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "stdux",   X(31,181),	X_MASK,		PPC|B64,	{ RS, RAS, RB } },
+
+{ "stwux",   X(31,183),	X_MASK,		PPC,		{ RS, RAS, RB } },
+{ "stux",    X(31,183),	X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "sliq",    XRC(31,184,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "sliq.",   XRC(31,184,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "subfze",  XO(31,200,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfze",    XO(31,200,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfze.", XO(31,200,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfze.",   XO(31,200,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfzeo", XO(31,200,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfzeo",   XO(31,200,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfzeo.",XO(31,200,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfzeo.",  XO(31,200,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "addze",   XO(31,202,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "aze",     XO(31,202,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addze.",  XO(31,202,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "aze.",    XO(31,202,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "addzeo",  XO(31,202,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "azeo",    XO(31,202,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addzeo.", XO(31,202,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "azeo.",   XO(31,202,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "mtsr",    X(31,210),	XRB_MASK|(1<<20), PPC|POWER|B32, { SR, RS } },
+
+{ "stdcx.",  XRC(31,214,1), X_MASK,	PPC|B64,	{ RS, RA, RB } },
+
+{ "stbx",    X(31,215),	X_MASK,		PPC|POWER,	{ RS, RA, RB } },
+
+{ "sllq",    XRC(31,216,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sllq.",   XRC(31,216,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sleq",    XRC(31,217,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sleq.",   XRC(31,217,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "subfme",  XO(31,232,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfme",    XO(31,232,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfme.", XO(31,232,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfme.",   XO(31,232,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfmeo", XO(31,232,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfmeo",   XO(31,232,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "subfmeo.",XO(31,232,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "sfmeo.",  XO(31,232,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "mulld",   XO(31,233,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulld.",  XO(31,233,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulldo",  XO(31,233,1,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "mulldo.", XO(31,233,1,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "addme",   XO(31,234,0,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "ame",     XO(31,234,0,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addme.",  XO(31,234,0,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "ame.",    XO(31,234,0,1), XORB_MASK, POWER,		{ RT, RA } },
+{ "addmeo",  XO(31,234,1,0), XORB_MASK, PPC,		{ RT, RA } },
+{ "ameo",    XO(31,234,1,0), XORB_MASK, POWER,		{ RT, RA } },
+{ "addmeo.", XO(31,234,1,1), XORB_MASK, PPC,		{ RT, RA } },
+{ "ameo.",   XO(31,234,1,1), XORB_MASK, POWER,		{ RT, RA } },
+
+{ "mullw",   XO(31,235,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "muls",    XO(31,235,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "mullw.",  XO(31,235,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "muls.",   XO(31,235,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "mullwo",  XO(31,235,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulso",   XO(31,235,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "mullwo.", XO(31,235,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "mulso.",  XO(31,235,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "mtsrin",  X(31,242),	XRA_MASK,	PPC|B32,	{ RS, RB } },
+{ "mtsri",   X(31,242),	XRA_MASK,	POWER|B32,	{ RS, RB } },
+
+{ "dcbtst",  X(31,246),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "stbux",   X(31,247),	X_MASK,		PPC|POWER,	{ RS, RAS, RB } },
+
+{ "slliq",   XRC(31,248,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "slliq.",  XRC(31,248,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "doz",     XO(31,264,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "doz.",    XO(31,264,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "dozo",    XO(31,264,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "dozo.",   XO(31,264,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "add",     XO(31,266,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "cax",     XO(31,266,0,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "add.",    XO(31,266,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "cax.",    XO(31,266,0,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addo",    XO(31,266,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "caxo",    XO(31,266,1,0), XO_MASK,	POWER,		{ RT, RA, RB } },
+{ "addo.",   XO(31,266,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "caxo.",   XO(31,266,1,1), XO_MASK,	POWER,		{ RT, RA, RB } },
+
+{ "lscbx",   XRC(31,277,0), X_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "lscbx.",  XRC(31,277,1), X_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "dcbt",    X(31,278),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lhzx",    X(31,279),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "icbt",    X(31,262),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "eqv",     XRC(31,284,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "eqv.",    XRC(31,284,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "tlbie",   X(31,306),	XRTRA_MASK,	PPC,		{ RB } },
+{ "tlbi",    X(31,306),	XRTRA_MASK,	POWER,		{ RB } },
+
+{ "eciwx",   X(31,310), X_MASK,		PPC,		{ RT, RA, RB } },
+
+{ "lhzux",   X(31,311),	X_MASK,		PPC|POWER,	{ RT, RAL, RB } },
+
+{ "xor",     XRC(31,316,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "xor.",    XRC(31,316,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "mfdcr",   X(31,323),	X_MASK,		PPC,		{ RT, SPR } },
+
+{ "div",     XO(31,331,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "div.",    XO(31,331,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divo",    XO(31,331,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divo.",   XO(31,331,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "mfmq",    XSPR(31,339,0), XSPR_MASK,	POWER|M601,	{ RT } },
+{ "mfxer",   XSPR(31,339,1), XSPR_MASK,	PPC|POWER,	{ RT } },
+{ "mfrtcu",  XSPR(31,339,4), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfrtcl",  XSPR(31,339,5), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfdec",   XSPR(31,339,6), XSPR_MASK, POWER|M601,	{ RT } },
+{ "mflr",    XSPR(31,339,8), XSPR_MASK,	PPC|POWER,	{ RT } },
+{ "mfctr",   XSPR(31,339,9), XSPR_MASK,	PPC|POWER,	{ RT } },
+{ "mftid",   XSPR(31,339,17), XSPR_MASK, POWER,		{ RT } },
+{ "mfdsisr", XSPR(31,339,18), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfdar",   XSPR(31,339,19), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfdec",   XSPR(31,339,22), XSPR_MASK, PPC,		{ RT } },
+{ "mfsdr0",  XSPR(31,339,24), XSPR_MASK, POWER,		{ RT } },
+{ "mfsdr1",  XSPR(31,339,25), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfsrr0",  XSPR(31,339,26), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfsrr1",  XSPR(31,339,27), XSPR_MASK, PPC|POWER,	{ RT } },
+{ "mfsprg",  XSPR(31,339,272), XSPRG_MASK, PPC,		{ RT, SPRG } },
+{ "mfasr",   XSPR(31,339,280), XSPR_MASK, PPC|B64,	{ RT } },
+{ "mfear",   XSPR(31,339,282), XSPR_MASK, PPC,		{ RT } },
+{ "mfpvr",   XSPR(31,339,287), XSPR_MASK, PPC,		{ RT } },
+{ "mfibatu", XSPR(31,339,528), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfibatl", XSPR(31,339,529), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfdbatu", XSPR(31,339,536), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfdbatl", XSPR(31,339,537), XSPRBAT_MASK, PPC,	{ RT, SPRBAT } },
+{ "mfspr",   X(31,339),	X_MASK,		PPC|POWER,	{ RT, SPR } },
+
+{ "lwax",    X(31,341),	X_MASK,		PPC|B64,	{ RT, RA, RB } },
+
+{ "lhax",    X(31,343),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "dccci",   X(31,454),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "abs",     XO(31,360,0,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "abs.",    XO(31,360,0,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "abso",    XO(31,360,1,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "abso.",   XO(31,360,1,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+
+{ "divs",    XO(31,363,0,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divs.",   XO(31,363,0,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divso",   XO(31,363,1,0), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+{ "divso.",  XO(31,363,1,1), XO_MASK,	POWER|M601,	{ RT, RA, RB } },
+
+{ "tlbia",   X(31,370),	0xffffffff,	PPC,		{ 0 } },
+
+{ "mftbu",   XSPR(31,371,269), XSPR_MASK, PPC,		{ RT } },
+{ "mftb",    X(31,371),	X_MASK,		PPC,		{ RT, TBR } },
+
+{ "lwaux",   X(31,373),	X_MASK,		PPC|B64,	{ RT, RAL, RB } },
+
+{ "lhaux",   X(31,375),	X_MASK,		PPC|POWER,	{ RT, RAL, RB } },
+
+{ "sthx",    X(31,407),	X_MASK,		PPC|POWER,	{ RS, RA, RB } },
+
+{ "lfqx",    X(31,791),	X_MASK,		POWER2,		{ FRT, RA, RB } },
+
+{ "lfqux",   X(31,823),	X_MASK,		POWER2,		{ FRT, RA, RB } },
+
+{ "stfqx",   X(31,919),	X_MASK,		POWER2,		{ FRS, RA, RB } },
+
+{ "stfqux",  X(31,951),	X_MASK,		POWER2,		{ FRS, RA, RB } },
+
+{ "orc",     XRC(31,412,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "orc.",    XRC(31,412,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "sradi",   XS(31,413,0), XS_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+{ "sradi.",  XS(31,413,1), XS_MASK,	PPC|B64,	{ RA, RS, SH6 } },
+
+{ "slbie",   X(31,434),	XRTRA_MASK,	PPC|B64,	{ RB } },
+
+{ "ecowx",   X(31,438),	X_MASK,		PPC,		{ RT, RA, RB } },
+
+{ "sthux",   X(31,439),	X_MASK,		PPC|POWER,	{ RS, RAS, RB } },
+
+{ "mr",	     XRC(31,444,0), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "or",      XRC(31,444,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "mr.",     XRC(31,444,1), X_MASK,	PPC|POWER,	{ RA, RS, RBS } },
+{ "or.",     XRC(31,444,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "mtdcr",   X(31,451),	X_MASK,		PPC,		{ SPR, RS } },
+
+{ "divdu",   XO(31,457,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divdu.",  XO(31,457,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divduo",  XO(31,457,1,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divduo.", XO(31,457,1,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "divwu",   XO(31,459,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwu.",  XO(31,459,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwuo",  XO(31,459,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwuo.", XO(31,459,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "mtmq",    XSPR(31,467,0), XSPR_MASK,	POWER|M601,	{ RS } },
+{ "mtxer",   XSPR(31,467,1), XSPR_MASK,	PPC|POWER,	{ RS } },
+{ "mtlr",    XSPR(31,467,8), XSPR_MASK,	PPC|POWER,	{ RS } },
+{ "mtctr",   XSPR(31,467,9), XSPR_MASK,	PPC|POWER,	{ RS } },
+{ "mttid",   XSPR(31,467,17), XSPR_MASK, POWER,		{ RS } },
+{ "mtdsisr", XSPR(31,467,18), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtdar",   XSPR(31,467,19), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtrtcu",  XSPR(31,467,20), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtrtcl",  XSPR(31,467,21), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtdec",   XSPR(31,467,22), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsdr0",  XSPR(31,467,24), XSPR_MASK, POWER,		{ RS } },
+{ "mtsdr1",  XSPR(31,467,25), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsrr0",  XSPR(31,467,26), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsrr1",  XSPR(31,467,27), XSPR_MASK, PPC|POWER,	{ RS } },
+{ "mtsprg",  XSPR(31,467,272), XSPRG_MASK, PPC,		{ SPRG, RS } },
+{ "mtasr",   XSPR(31,467,280), XSPR_MASK, PPC|B64,	{ RS } },
+{ "mtear",   XSPR(31,467,282), XSPR_MASK, PPC,		{ RS } },
+{ "mttbl",   XSPR(31,467,284), XSPR_MASK, PPC,		{ RS } },
+{ "mttbu",   XSPR(31,467,285), XSPR_MASK, PPC,		{ RS } },
+{ "mtibatu", XSPR(31,467,528), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtibatl", XSPR(31,467,529), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtdbatu", XSPR(31,467,536), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtdbatl", XSPR(31,467,537), XSPRBAT_MASK, PPC,	{ SPRBAT, RS } },
+{ "mtspr",   X(31,467),	X_MASK,		PPC|POWER,	{ SPR, RS } },
+
+{ "dcbi",    X(31,470),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "nand",    XRC(31,476,0), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+{ "nand.",   XRC(31,476,1), X_MASK,	PPC|POWER,	{ RA, RS, RB } },
+
+{ "nabs",    XO(31,488,0,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "nabs.",   XO(31,488,0,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "nabso",   XO(31,488,1,0), XORB_MASK, POWER|M601,	{ RT, RA } },
+{ "nabso.",  XO(31,488,1,1), XORB_MASK, POWER|M601,	{ RT, RA } },
+
+{ "divd",    XO(31,489,0,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divd.",   XO(31,489,0,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divdo",   XO(31,489,1,0), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+{ "divdo.",  XO(31,489,1,1), XO_MASK,	PPC|B64,	{ RT, RA, RB } },
+
+{ "divw",    XO(31,491,0,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divw.",   XO(31,491,0,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwo",   XO(31,491,1,0), XO_MASK,	PPC,		{ RT, RA, RB } },
+{ "divwo.",  XO(31,491,1,1), XO_MASK,	PPC,		{ RT, RA, RB } },
+
+{ "slbia",   X(31,498),	0xffffffff,	PPC|B64,	{ 0 } },
+
+{ "cli",     X(31,502), XRB_MASK,	POWER,		{ RT, RA } },
+
+{ "mcrxr",   X(31,512),	XRARB_MASK|(3<<21), PPC|POWER,	{ BF } },
+
+{ "clcs",    X(31,531), XRB_MASK,	POWER|M601,	{ RT, RA } },
+
+{ "lswx",    X(31,533),	X_MASK,		PPC,		{ RT, RA, RB } },
+{ "lsx",     X(31,533),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "lwbrx",   X(31,534),	X_MASK,		PPC,		{ RT, RA, RB } },
+{ "lbrx",    X(31,534),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "lfsx",    X(31,535),	X_MASK,		PPC|POWER,	{ FRT, RA, RB } },
+
+{ "srw",     XRC(31,536,0), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sr",      XRC(31,536,0), X_MASK,	POWER,		{ RA, RS, RB } },
+{ "srw.",    XRC(31,536,1), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sr.",     XRC(31,536,1), X_MASK,	POWER,		{ RA, RS, RB } },
+
+{ "rrib",    XRC(31,537,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "rrib.",   XRC(31,537,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "srd",     XRC(31,539,0), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "srd.",    XRC(31,539,1), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+
+{ "maskir",  XRC(31,541,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "maskir.", XRC(31,541,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "tlbsync", X(31,566),	0xffffffff,	PPC,		{ 0 } },
+
+{ "lfsux",   X(31,567),	X_MASK,		PPC|POWER,	{ FRT, RAS, RB } },
+
+{ "mfsr",    X(31,595),	XRB_MASK|(1<<20), PPC|POWER|B32, { RT, SR } },
+
+{ "lswi",    X(31,597),	X_MASK,		PPC,		{ RT, RA, NB } },
+{ "lsi",     X(31,597),	X_MASK,		POWER,		{ RT, RA, NB } },
+
+{ "sync",    X(31,598), 0xffffffff,	PPC,		{ 0 } },
+{ "dcs",     X(31,598), 0xffffffff,	POWER,		{ 0 } },
+
+{ "lfdx",    X(31,599), X_MASK,		PPC|POWER,	{ FRT, RA, RB } },
+
+{ "mfsri",   X(31,627), X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "dclst",   X(31,630), XRB_MASK,	POWER,		{ RS, RA } },
+
+{ "lfdux",   X(31,631), X_MASK,		PPC|POWER,	{ FRT, RAS, RB } },
+
+{ "mfsrin",  X(31,659), XRA_MASK,	PPC|B32,	{ RT, RB } },
+
+{ "stswx",   X(31,661), X_MASK,		PPC,		{ RS, RA, RB } },
+{ "stsx",    X(31,661), X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "stwbrx",  X(31,662), X_MASK,		PPC,		{ RS, RA, RB } },
+{ "stbrx",   X(31,662), X_MASK,		POWER,		{ RS, RA, RB } },
+
+{ "stfsx",   X(31,663), X_MASK,		PPC|POWER,	{ FRS, RA, RB } },
+
+{ "srq",     XRC(31,664,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "srq.",    XRC(31,664,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sre",     XRC(31,665,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sre.",    XRC(31,665,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "stfsux",  X(31,695),	X_MASK,		PPC|POWER,	{ FRS, RAS, RB } },
+
+{ "sriq",    XRC(31,696,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "sriq.",   XRC(31,696,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "stswi",   X(31,725),	X_MASK,		PPC,		{ RS, RA, NB } },
+{ "stsi",    X(31,725),	X_MASK,		POWER,		{ RS, RA, NB } },
+
+{ "stfdx",   X(31,727),	X_MASK,		PPC|POWER,	{ FRS, RA, RB } },
+
+{ "srlq",    XRC(31,728,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "srlq.",   XRC(31,728,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "sreq",    XRC(31,729,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sreq.",   XRC(31,729,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "stfdux",  X(31,759),	X_MASK,		PPC|POWER,	{ FRS, RAS, RB } },
+
+{ "srliq",   XRC(31,760,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "srliq.",  XRC(31,760,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "lhbrx",   X(31,790),	X_MASK,		PPC|POWER,	{ RT, RA, RB } },
+
+{ "sraw",    XRC(31,792,0), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sra",     XRC(31,792,0), X_MASK,	POWER,		{ RA, RS, RB } },
+{ "sraw.",   XRC(31,792,1), X_MASK,	PPC,		{ RA, RS, RB } },
+{ "sra.",    XRC(31,792,1), X_MASK,	POWER,		{ RA, RS, RB } },
+
+{ "srad",    XRC(31,794,0), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+{ "srad.",   XRC(31,794,1), X_MASK,	PPC|B64,	{ RA, RS, RB } },
+
+{ "rac",     X(31,818),	X_MASK,		POWER,		{ RT, RA, RB } },
+
+{ "srawi",   XRC(31,824,0), X_MASK,	PPC,		{ RA, RS, SH } },
+{ "srai",    XRC(31,824,0), X_MASK,	POWER,		{ RA, RS, SH } },
+{ "srawi.",  XRC(31,824,1), X_MASK,	PPC,		{ RA, RS, SH } },
+{ "srai.",   XRC(31,824,1), X_MASK,	POWER,		{ RA, RS, SH } },
+
+{ "eieio",   X(31,854),	0xffffffff,	PPC,		{ 0 } },
+
+{ "sthbrx",  X(31,918),	X_MASK,		PPC|POWER,	{ RS, RA, RB } },
+
+{ "sraq",    XRC(31,920,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "sraq.",   XRC(31,920,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "srea",    XRC(31,921,0), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+{ "srea.",   XRC(31,921,1), X_MASK,	POWER|M601,	{ RA, RS, RB } },
+
+{ "extsh",   XRC(31,922,0), XRB_MASK,	PPC,		{ RA, RS } },
+{ "exts",    XRC(31,922,0), XRB_MASK,	POWER,		{ RA, RS } },
+{ "extsh.",  XRC(31,922,1), XRB_MASK,	PPC,		{ RA, RS } },
+{ "exts.",   XRC(31,922,1), XRB_MASK,	POWER,		{ RA, RS } },
+
+{ "sraiq",   XRC(31,952,0), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+{ "sraiq.",  XRC(31,952,1), X_MASK,	POWER|M601,	{ RA, RS, SH } },
+
+{ "extsb",   XRC(31,954,0), XRB_MASK,	PPC,		{ RA, RS} },
+{ "extsb.",  XRC(31,954,1), XRB_MASK,	PPC,		{ RA, RS} },
+
+{ "iccci",   X(31,966),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "icbi",    X(31,982),	XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "stfiwx",  X(31,983),	X_MASK,		PPC,		{ FRS, RA, RB } },
+
+{ "extsw",   XRC(31,986,0), XRB_MASK,	PPC,		{ RA, RS } },
+{ "extsw.",  XRC(31,986,1), XRB_MASK,	PPC,		{ RA, RS } },
+
+{ "dcbz",    X(31,1014), XRT_MASK,	PPC,		{ RA, RB } },
+{ "dclz",    X(31,1014), XRT_MASK,	PPC,		{ RA, RB } },
+
+{ "lwz",     OP(32),	OP_MASK,	PPC,		{ RT, D, RA } },
+{ "l",	     OP(32),	OP_MASK,	POWER,		{ RT, D, RA } },
+
+{ "lwzu",    OP(33),	OP_MASK,	PPC,		{ RT, D, RAL } },
+{ "lu",      OP(33),	OP_MASK,	POWER,		{ RT, D, RA } },
+
+{ "lbz",     OP(34),	OP_MASK,	PPC|POWER,	{ RT, D, RA } },
+
+{ "lbzu",    OP(35),	OP_MASK,	PPC|POWER,	{ RT, D, RAL } },
+
+{ "stw",     OP(36),	OP_MASK,	PPC,		{ RS, D, RA } },
+{ "st",      OP(36),	OP_MASK,	POWER,		{ RS, D, RA } },
+
+{ "stwu",    OP(37),	OP_MASK,	PPC,		{ RS, D, RAS } },
+{ "stu",     OP(37),	OP_MASK,	POWER,		{ RS, D, RA } },
+
+{ "stb",     OP(38),	OP_MASK,	PPC|POWER,	{ RS, D, RA } },
+
+{ "stbu",    OP(39),	OP_MASK,	PPC|POWER,	{ RS, D, RAS } },
+
+{ "lhz",     OP(40),	OP_MASK,	PPC|POWER,	{ RT, D, RA } },
+
+{ "lhzu",    OP(41),	OP_MASK,	PPC|POWER,	{ RT, D, RAL } },
+
+{ "lha",     OP(42),	OP_MASK,	PPC|POWER,	{ RT, D, RA } },
+
+{ "lhau",    OP(43),	OP_MASK,	PPC|POWER,	{ RT, D, RAL } },
+
+{ "sth",     OP(44),	OP_MASK,	PPC|POWER,	{ RS, D, RA } },
+
+{ "sthu",    OP(45),	OP_MASK,	PPC|POWER,	{ RS, D, RAS } },
+
+{ "lmw",     OP(46),	OP_MASK,	PPC,		{ RT, D, RAM } },
+{ "lm",      OP(46),	OP_MASK,	POWER,		{ RT, D, RA } },
+
+{ "stmw",    OP(47),	OP_MASK,	PPC,		{ RS, D, RA } },
+{ "stm",     OP(47),	OP_MASK,	POWER,		{ RS, D, RA } },
+
+{ "lfs",     OP(48),	OP_MASK,	PPC|POWER,	{ FRT, D, RA } },
+
+{ "lfsu",    OP(49),	OP_MASK,	PPC|POWER,	{ FRT, D, RAS } },
+
+{ "lfd",     OP(50),	OP_MASK,	PPC|POWER,	{ FRT, D, RA } },
+
+{ "lfdu",    OP(51),	OP_MASK,	PPC|POWER,	{ FRT, D, RAS } },
+
+{ "stfs",    OP(52),	OP_MASK,	PPC|POWER,	{ FRS, D, RA } },
+
+{ "stfsu",   OP(53),	OP_MASK,	PPC|POWER,	{ FRS, D, RAS } },
+
+{ "stfd",    OP(54),	OP_MASK,	PPC|POWER,	{ FRS, D, RA } },
+
+{ "stfdu",   OP(55),	OP_MASK,	PPC|POWER,	{ FRS, D, RAS } },
+
+{ "lfq",     OP(56),	OP_MASK,	POWER2,		{ FRT, D, RA } },
+
+{ "lfqu",    OP(57),	OP_MASK,	POWER2,		{ FRT, D, RA } },
+
+{ "ld",      DSO(58,0),	DS_MASK,	PPC|B64,	{ RT, DS, RA } },
+
+{ "ldu",     DSO(58,1), DS_MASK,	PPC|B64,	{ RT, DS, RAL } },
+
+{ "lwa",     DSO(58,2), DS_MASK,	PPC|B64,	{ RT, DS, RA } },
+
+{ "fdivs",   A(59,18,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fdivs.",  A(59,18,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+
+{ "fsubs",   A(59,20,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fsubs.",  A(59,20,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+
+{ "fadds",   A(59,21,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fadds.",  A(59,21,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+
+{ "fsqrts",  A(59,22,0), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+{ "fsqrts.", A(59,22,1), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+
+{ "fres",    A(59,24,0), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+{ "fres.",   A(59,24,1), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+
+{ "fmuls",   A(59,25,0), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+{ "fmuls.",  A(59,25,1), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+
+{ "fmsubs",  A(59,28,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fmsubs.", A(59,28,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fmadds",  A(59,29,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fmadds.", A(59,29,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmsubs", A(59,30,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnmsubs.",A(59,30,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmadds", A(59,31,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnmadds.",A(59,31,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "stfq",    OP(60),	OP_MASK,	POWER2,		{ FRS, D, RA } },
+
+{ "stfqu",   OP(61),	OP_MASK,	POWER2,		{ FRS, D, RA } },
+
+{ "std",     DSO(62,0),	DS_MASK,	PPC|B64,	{ RS, DS, RA } },
+
+{ "stdu",    DSO(62,1),	DS_MASK,	PPC|B64,	{ RS, DS, RAS } },
+
+{ "fcmpu",   X(63,0),	X_MASK|(3<<21),	PPC|POWER,	{ BF, FRA, FRB } },
+
+{ "frsp",    XRC(63,12,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "frsp.",   XRC(63,12,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "fctiw",   XRC(63,14,0), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcir",    XRC(63,14,0), XRA_MASK,	POWER2,		{ FRT, FRB } },
+{ "fctiw.",  XRC(63,14,1), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcir.",   XRC(63,14,1), XRA_MASK,	POWER2,		{ FRT, FRB } },
+
+{ "fctiwz",  XRC(63,15,0), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcirz",   XRC(63,15,0), XRA_MASK,	POWER2,		{ FRT, FRB } },
+{ "fctiwz.", XRC(63,15,1), XRA_MASK,	PPC,		{ FRT, FRB } },
+{ "fcirz.",  XRC(63,15,1), XRA_MASK,	POWER2,		{ FRT, FRB } },
+
+{ "fdiv",    A(63,18,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fd",      A(63,18,0), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+{ "fdiv.",   A(63,18,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fd.",     A(63,18,1), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+
+{ "fsub",    A(63,20,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fs",      A(63,20,0), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+{ "fsub.",   A(63,20,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fs.",     A(63,20,1), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+
+{ "fadd",    A(63,21,0), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fa",      A(63,21,0), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+{ "fadd.",   A(63,21,1), AFRC_MASK,	PPC,		{ FRT, FRA, FRB } },
+{ "fa.",     A(63,21,1), AFRC_MASK,	POWER,		{ FRT, FRA, FRB } },
+
+{ "fsqrt",   A(63,22,0), AFRAFRC_MASK,	PPC|POWER2,	{ FRT, FRB } },
+{ "fsqrt.",  A(63,22,1), AFRAFRC_MASK,	PPC|POWER2,	{ FRT, FRB } },
+
+{ "fsel",    A(63,23,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fsel.",   A(63,23,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+
+{ "fmul",    A(63,25,0), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+{ "fm",      A(63,25,0), AFRB_MASK,	POWER,		{ FRT, FRA, FRC } },
+{ "fmul.",   A(63,25,1), AFRB_MASK,	PPC,		{ FRT, FRA, FRC } },
+{ "fm.",     A(63,25,1), AFRB_MASK,	POWER,		{ FRT, FRA, FRC } },
+
+{ "frsqrte", A(63,26,0), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+{ "frsqrte.",A(63,26,1), AFRAFRC_MASK,	PPC,		{ FRT, FRB } },
+
+{ "fmsub",   A(63,28,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fms",     A(63,28,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fmsub.",  A(63,28,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fms.",    A(63,28,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fmadd",   A(63,29,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fma",     A(63,29,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fmadd.",  A(63,29,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fma.",    A(63,29,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmsub",  A(63,30,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnms",    A(63,30,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fnmsub.", A(63,30,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnms.",   A(63,30,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fnmadd",  A(63,31,0), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnma",    A(63,31,0), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+{ "fnmadd.", A(63,31,1), A_MASK,	PPC,		{ FRT,FRA,FRC,FRB } },
+{ "fnma.",   A(63,31,1), A_MASK,	POWER,		{ FRT,FRA,FRC,FRB } },
+
+{ "fcmpo",   X(63,30),	X_MASK|(3<<21),	PPC|POWER,	{ BF, FRA, FRB } },
+
+{ "mtfsb1",  XRC(63,38,0), XRARB_MASK,	PPC|POWER,	{ BT } },
+{ "mtfsb1.", XRC(63,38,1), XRARB_MASK,	PPC|POWER,	{ BT } },
+
+{ "fneg",    XRC(63,40,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fneg.",   XRC(63,40,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "mcrfs",   X(63,64),	XRB_MASK|(3<<21)|(3<<16), PPC|POWER, { BF, BFA } },
+
+{ "mtfsb0",  XRC(63,70,0), XRARB_MASK,	PPC|POWER,	{ BT } },
+{ "mtfsb0.", XRC(63,70,1), XRARB_MASK,	PPC|POWER,	{ BT } },
+
+{ "fmr",     XRC(63,72,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fmr.",    XRC(63,72,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "mtfsfi",  XRC(63,134,0), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+{ "mtfsfi.", XRC(63,134,1), XRA_MASK|(3<<21)|(1<<11), PPC|POWER, { BF, U } },
+
+{ "fnabs",   XRC(63,136,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fnabs.",  XRC(63,136,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "fabs",    XRC(63,264,0), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+{ "fabs.",   XRC(63,264,1), XRA_MASK,	PPC|POWER,	{ FRT, FRB } },
+
+{ "mffs",    XRC(63,583,0), XRARB_MASK,	PPC|POWER,	{ FRT } },
+{ "mffs.",   XRC(63,583,1), XRARB_MASK,	PPC|POWER,	{ FRT } },
+
+{ "mtfsf",   XFL(63,711,0), XFL_MASK,	PPC|POWER,	{ FLM, FRB } },
+{ "mtfsf.",  XFL(63,711,1), XFL_MASK,	PPC|POWER,	{ FLM, FRB } },
+
+{ "fctid",   XRC(63,814,0), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+{ "fctid.",  XRC(63,814,1), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+
+{ "fctidz",  XRC(63,815,0), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+{ "fctidz.", XRC(63,815,1), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+
+{ "fcfid",   XRC(63,846,0), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+{ "fcfid.",  XRC(63,846,1), XRA_MASK,	PPC|B64,	{ FRT, FRB } },
+
+};
+
+const int powerpc_num_opcodes =
+  sizeof (powerpc_opcodes) / sizeof (powerpc_opcodes[0]);
+
+/* The macro table.  This is only used by the assembler.  */
+
+const struct powerpc_macro powerpc_macros[] = {
+{ "extldi",  4,   PPC|B64,	"rldicr %0,%1,%3,(%2)-1" },
+{ "extldi.", 4,   PPC|B64,	"rldicr. %0,%1,%3,(%2)-1" },
+{ "extrdi",  4,   PPC|B64,	"rldicl %0,%1,(%2)+(%3),64-(%2)" },
+{ "extrdi.", 4,   PPC|B64,	"rldicl. %0,%1,(%2)+(%3),64-(%2)" },
+{ "insrdi",  4,   PPC|B64,	"rldimi %0,%1,64-((%2)+(%3)),%3" },
+{ "insrdi.", 4,   PPC|B64,	"rldimi. %0,%1,64-((%2)+(%3)),%3" },
+{ "rotrdi",  3,   PPC|B64,	"rldicl %0,%1,64-(%2),0" },
+{ "rotrdi.", 3,   PPC|B64,	"rldicl. %0,%1,64-(%2),0" },
+{ "sldi",    3,   PPC|B64,	"rldicr %0,%1,%2,63-(%2)" },
+{ "sldi.",   3,   PPC|B64,	"rldicr. %0,%1,%2,63-(%2)" },
+{ "srdi",    3,   PPC|B64,	"rldicl %0,%1,64-(%2),%2" },
+{ "srdi.",   3,   PPC|B64,	"rldicl. %0,%1,64-(%2),%2" },
+{ "clrrdi",  3,   PPC|B64,	"rldicr %0,%1,0,63-(%2)" },
+{ "clrrdi.", 3,   PPC|B64,	"rldicr. %0,%1,0,63-(%2)" },
+{ "clrlsldi",4,   PPC|B64,	"rldic %0,%1,%3,(%2)-(%3)" },
+{ "clrlsldi.",4,  PPC|B64,	"rldic. %0,%1,%3,(%2)-(%3)" },
+
+{ "extlwi",  4,   PPC,		"rlwinm %0,%1,%3,0,(%2)-1" },
+{ "extlwi.", 4,   PPC,		"rlwinm. %0,%1,%3,0,(%2)-1" },
+{ "extrwi",  4,   PPC,		"rlwinm %0,%1,(%2)+(%3),32-(%2),31" },
+{ "extrwi.", 4,   PPC,		"rlwinm. %0,%1,(%2)+(%3),32-(%2),31" },
+{ "inslwi",  4,   PPC,		"rlwimi %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "inslwi.", 4,   PPC,		"rlwimi. %0,%1,32-(%3),%3,(%2)+(%3)-1" },
+{ "insrwi",  4,   PPC,		"rlwimi %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1" },
+{ "insrwi.", 4,   PPC,		"rlwimi. %0,%1,32-((%2)+(%3)),%3,(%2)+(%3)-1"},
+{ "rotrwi",  3,   PPC,		"rlwinm %0,%1,32-(%2),0,31" },
+{ "rotrwi.", 3,   PPC,		"rlwinm. %0,%1,32-(%2),0,31" },
+{ "slwi",    3,   PPC,		"rlwinm %0,%1,%2,0,31-(%2)" },
+{ "sli",     3,   POWER,	"rlinm %0,%1,%2,0,31-(%2)" },
+{ "slwi.",   3,   PPC,		"rlwinm. %0,%1,%2,0,31-(%2)" },
+{ "sli.",    3,   POWER,	"rlinm. %0,%1,%2,0,31-(%2)" },
+{ "srwi",    3,   PPC,		"rlwinm %0,%1,32-(%2),%2,31" },
+{ "sri",     3,   POWER,	"rlinm %0,%1,32-(%2),%2,31" },
+{ "srwi.",   3,   PPC,		"rlwinm. %0,%1,32-(%2),%2,31" },
+{ "sri.",    3,   POWER,	"rlinm. %0,%1,32-(%2),%2,31" },
+{ "clrrwi",  3,   PPC,		"rlwinm %0,%1,0,0,31-(%2)" },
+{ "clrrwi.", 3,   PPC,		"rlwinm. %0,%1,0,0,31-(%2)" },
+{ "clrlslwi",4,   PPC,		"rlwinm %0,%1,%3,(%2)-(%3),31-(%3)" },
+{ "clrlslwi.",4,  PPC,		"rlwinm. %0,%1,%3,(%2)-(%3),31-(%3)" },
+
+};
+
+const int powerpc_num_macros =
+  sizeof (powerpc_macros) / sizeof (powerpc_macros[0]);
+
+static int
+print_insn_powerpc (disassemble_info *info, uint32_t insn, unsigned memaddr,
+		    int dialect);
+
+/* Print a big endian PowerPC instruction.  For convenience, also
+   disassemble instructions supported by the Motorola PowerPC 601.  */
+
+int print_insn_ppc (bfd_vma pc, disassemble_info *info)
+{
+    uint32_t opc;
+    bfd_byte buf[4];
+
+    (*info->read_memory_func)(pc, buf, 4, info);
+    if (info->endian == BFD_ENDIAN_BIG)
+        opc = bfd_getb32(buf);
+    else
+        opc = bfd_getl32(buf);
+    if (info->mach == bfd_mach_ppc64) {
+        return print_insn_powerpc (info, opc, pc,
+                                   PPC | B64);
+    } else {
+        return print_insn_powerpc (info, opc, pc,
+                                   PPC | B32 | M601);
+    }
+}
+
+/* Print a PowerPC or POWER instruction.  */
+
+static int
+print_insn_powerpc (disassemble_info *info, uint32_t insn, unsigned memaddr,
+		    int dialect)
+{
+  const struct powerpc_opcode *opcode;
+  const struct powerpc_opcode *opcode_end;
+  uint32_t op;
+
+  /* Get the major opcode of the instruction.  */
+  op = PPC_OP (insn);
+
+  /* Find the first match in the opcode table.  We could speed this up
+     a bit by doing a binary search on the major opcode.  */
+  opcode_end = powerpc_opcodes + powerpc_num_opcodes;
+  for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++)
+    {
+      uint32_t table_op;
+      const unsigned char *opindex;
+      const struct powerpc_operand *operand;
+      int invalid;
+      int need_comma;
+      int need_paren;
+
+      table_op = PPC_OP (opcode->opcode);
+      if (op < table_op)
+		break;
+      if (op > table_op)
+		continue;
+
+      if ((insn & opcode->mask) != opcode->opcode
+	  || (opcode->flags & dialect) == 0)
+		continue;
+
+      /* Make two passes over the operands.  First see if any of them
+		 have extraction functions, and, if they do, make sure the
+		 instruction is valid.  */
+      invalid = 0;
+      for (opindex = opcode->operands; *opindex != 0; opindex++)
+		{
+		  operand = powerpc_operands + *opindex;
+		  if (operand->extract)
+		    (*operand->extract) (insn, &invalid);
+		}
+      if (invalid)
+		continue;
+
+      /* The instruction is valid.  */
+      (*info->fprintf_func)(info->stream, "%s", opcode->name);
+      if (opcode->operands[0] != 0)
+		(*info->fprintf_func)(info->stream, "\t");
+
+      /* Now extract and print the operands.  */
+      need_comma = 0;
+      need_paren = 0;
+      for (opindex = opcode->operands; *opindex != 0; opindex++)
+		{
+		  int32_t value;
+
+		  operand = powerpc_operands + *opindex;
+
+		  /* Operands that are marked FAKE are simply ignored.  We
+		     already made sure that the extract function considered
+		     the instruction to be valid.  */
+		  if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+		    continue;
+
+		  /* Extract the value from the instruction.  */
+		  if (operand->extract)
+		    value = (*operand->extract) (insn, (int *) 0);
+		  else
+		    {
+		      value = (insn >> operand->shift) & ((1 << operand->bits) - 1);
+		      if ((operand->flags & PPC_OPERAND_SIGNED) != 0
+			  && (value & (1 << (operand->bits - 1))) != 0)
+			value -= 1 << operand->bits;
+		    }
+
+		  /* If the operand is optional, and the value is zero, don't
+		     print anything.  */
+		  if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+		      && (operand->flags & PPC_OPERAND_NEXT) == 0
+		      && value == 0)
+		    continue;
+
+		  if (need_comma)
+		    {
+		      (*info->fprintf_func)(info->stream, ",");
+		      need_comma = 0;
+		    }
+
+		  /* Print the operand as directed by the flags.  */
+		  if ((operand->flags & PPC_OPERAND_GPR) != 0)
+		    (*info->fprintf_func)(info->stream, "r%d", value);
+		  else if ((operand->flags & PPC_OPERAND_FPR) != 0)
+		    (*info->fprintf_func)(info->stream, "f%d", value);
+		  else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0)
+		    (*info->fprintf_func)(info->stream, "%08X", memaddr + value);
+		  else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+		    (*info->fprintf_func)(info->stream, "%08X", value & 0xffffffff);
+		  else if ((operand->flags & PPC_OPERAND_CR) == 0
+			   || (dialect & PPC_OPCODE_PPC) == 0)
+		    (*info->fprintf_func)(info->stream, "%d", value);
+		  else
+		    {
+		      if (operand->bits == 3)
+				(*info->fprintf_func)(info->stream, "cr%d", value);
+		      else
+			{
+			  static const char *cbnames[4] = { "lt", "gt", "eq", "so" };
+			  int cr;
+			  int cc;
+
+			  cr = value >> 2;
+			  if (cr != 0)
+			    (*info->fprintf_func)(info->stream, "4*cr%d", cr);
+			  cc = value & 3;
+			  if (cc != 0)
+			    {
+			      if (cr != 0)
+					(*info->fprintf_func)(info->stream, "+");
+			      (*info->fprintf_func)(info->stream, "%s", cbnames[cc]);
+			    }
+			}
+	    }
+
+	  if (need_paren)
+	    {
+	      (*info->fprintf_func)(info->stream, ")");
+	      need_paren = 0;
+	    }
+
+	  if ((operand->flags & PPC_OPERAND_PARENS) == 0)
+	    need_comma = 1;
+	  else
+	    {
+	      (*info->fprintf_func)(info->stream, "(");
+	      need_paren = 1;
+	    }
+	}
+
+      /* We have found and printed an instruction; return.  */
+      return 4;
+    }
+
+  /* We could not find a match.  */
+  (*info->fprintf_func)(info->stream, ".long 0x%x", insn);
+
+  return 4;
+}
diff --git a/ppc.ld b/ppc.ld
new file mode 100644
index 0000000..1e6bbe9
--- /dev/null
+++ b/ppc.ld
@@ -0,0 +1,228 @@
+/* ld script to make i386 Linux kernel
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
+ */
+OUTPUT_FORMAT("elf32-powerpc", "elf32-powerpc", "elf32-powerpc")
+OUTPUT_ARCH(powerpc:common)
+SEARCH_DIR(/usr/powerpc-linux-gnu/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib)
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp     : { *(.interp) 	}
+  .hash          : { *(.hash)		}
+  .dynsym        : { *(.dynsym)		}
+  .dynstr        : { *(.dynstr)		}
+  .gnu.version   : { *(.gnu.version)	}
+  .gnu.version_d   : { *(.gnu.version_d)	}
+  .gnu.version_r   : { *(.gnu.version_r)	}
+  .rel.init       : { *(.rel.init) }
+  .rela.init      : { *(.rela.init) }
+  .rel.text       : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+  .rela.text      : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+  .rel.fini       : { *(.rel.fini) }
+  .rela.fini      : { *(.rela.fini) }
+  .rel.rodata     : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+  .rela.rodata    : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+  .rel.data.rel.ro   : { *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*) }
+  .rela.data.rel.ro   : { *(.rela.data.rel.ro* .rela.gnu.linkonce.d.rel.ro.*) }
+  .rel.data       : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+  .rela.data      : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+  .rel.tdata	  : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+  .rela.tdata	  : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+  .rel.tbss	  : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+  .rela.tbss	  : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+  .rel.ctors      : { *(.rel.ctors) }
+  .rela.ctors     : { *(.rela.ctors) }
+  .rel.dtors      : { *(.rel.dtors) }
+  .rela.dtors     : { *(.rela.dtors) }
+  .rel.got        : { *(.rel.got) }
+  .rela.got       : { *(.rela.got) }
+  .rela.got1           : { *(.rela.got1) }
+  .rela.got2           : { *(.rela.got2) }
+  .rel.sdata      : { *(.rel.sdata .rel.sdata.* .rel.gnu.linkonce.s.*) }
+  .rela.sdata     : { *(.rela.sdata .rela.sdata.* .rela.gnu.linkonce.s.*) }
+  .rel.sbss       : { *(.rel.sbss .rel.sbss.* .rel.gnu.linkonce.sb.*) }
+  .rela.sbss      : { *(.rela.sbss .rela.sbss.* .rela.gnu.linkonce.sb.*) }
+  .rel.sdata2     : { *(.rel.sdata2 .rel.sdata2.* .rel.gnu.linkonce.s2.*) }
+  .rela.sdata2    : { *(.rela.sdata2 .rela.sdata2.* .rela.gnu.linkonce.s2.*) }
+  .rel.sbss2      : { *(.rel.sbss2 .rel.sbss2.* .rel.gnu.linkonce.sb2.*) }
+  .rela.sbss2     : { *(.rela.sbss2 .rela.sbss2.* .rela.gnu.linkonce.sb2.*) }
+  .rel.bss        : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+  .rela.bss       : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    KEEP (*(.text.*personality*))
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.glink)
+  } =0x47ff041f
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0x47ff041f
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .sdata2         :
+  {
+    PROVIDE (_SDA2_BASE_ = 32768);
+    *(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
+  }
+  .sbss2          : { *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN (0x10000) - ((0x10000 - .) & (0x10000 - 1)); . = DATA_SEGMENT_ALIGN (0x10000, 0x1000);
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+  /* Thread Local Storage sections  */
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .preinit_array     :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  }
+  .init_array     :
+  {
+     PROVIDE_HIDDEN (__init_array_start = .);
+     KEEP (*(SORT(.init_array.*)))
+     KEEP (*(.init_array))
+     PROVIDE_HIDDEN (__init_array_end = .);
+  }
+  .fini_array     :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(.fini_array))
+    KEEP (*(SORT(.fini_array.*)))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin*.o(.ctors))
+    /* We don't want to include the .ctor section from
+       the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin*.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+  .got1           : { *(.got1) }
+  .got2           : { *(.got2) }
+  .dynamic        : { *(.dynamic) }
+  .got            : SPECIAL { *(.got) }
+  . = DATA_SEGMENT_RELRO_END (0, .);
+  .plt            : SPECIAL { *(.plt) }
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    KEEP (*(.gnu.linkonce.d.*personality*))
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  .got            : SPECIAL { *(.got) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata          :
+  {
+    PROVIDE (_SDA_BASE_ = 32768);
+    *(.sdata .sdata.* .gnu.linkonce.s.*)
+  }
+  _edata = .; PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss           :
+  {
+    PROVIDE (__sbss_start = .); PROVIDE (___sbss_start = .);
+    *(.dynsbss)
+    *(.sbss .sbss.* .gnu.linkonce.sb.*)
+    *(.scommon)
+    PROVIDE (__sbss_end = .); PROVIDE (___sbss_end = .);
+  }
+  .plt            : SPECIAL { *(.plt) }
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.
+      FIXME: Why do we need it? When there is no .bss section, we don't
+      pad the .data section.  */
+   . = ALIGN(. != 0 ? 32 / 8 : 1);
+  }
+  . = ALIGN(32 / 8);
+  . = ALIGN(32 / 8);
+  _end = .; PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* These must appear regardless of  .  */
+  /DISCARD/    : { *(.fixup) }
+  /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/proxy/proxy_common.c b/proxy/proxy_common.c
new file mode 100644
index 0000000..7794a62
--- /dev/null
+++ b/proxy/proxy_common.c
@@ -0,0 +1,532 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "proxy_int.h"
+#include "sockets.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "android/utils/misc.h"
+#include "android/utils/system.h"
+#include <stdlib.h>
+
+int  proxy_log = 0;
+
+void
+proxy_LOG(const char*  fmt, ...)
+{
+    va_list  args;
+    va_start(args, fmt);
+    vfprintf(stderr, fmt, args);
+    va_end(args);
+    fprintf(stderr, "\n");
+}
+
+void
+proxy_set_verbose(int  mode)
+{
+    proxy_log = mode;
+}
+
+/** Global connection list
+ **/
+
+static ProxyConnection  s_connections[1];
+
+#define  MAX_HEX_DUMP  512
+
+static void
+hex_dump( void*   base, int  size, const char*  prefix )
+{
+    STRALLOC_DEFINE(s);
+    if (size > MAX_HEX_DUMP)
+        size = MAX_HEX_DUMP;
+    stralloc_add_hexdump(s, base, size, prefix);
+    proxy_LOG( "%s", stralloc_cstr(s) );
+    stralloc_reset(s);
+}
+
+void
+proxy_connection_init( ProxyConnection*           conn,
+                       int                        socket,
+                       SockAddress*               address,
+                       ProxyService*              service,
+                       ProxyConnectionFreeFunc    conn_free,
+                       ProxyConnectionSelectFunc  conn_select,
+                       ProxyConnectionPollFunc    conn_poll )
+{
+    conn->socket    = socket;
+    conn->address   = address[0];
+    conn->service   = service;
+    conn->next      = NULL;
+
+    conn->conn_free   = conn_free;
+    conn->conn_select = conn_select;
+    conn->conn_poll   = conn_poll;
+
+    socket_set_nonblock(socket);
+
+    {
+        SocketType  type = socket_get_type(socket);
+
+        snprintf( conn->name, sizeof(conn->name),
+                  "%s:%s(%d)",
+                  (type == SOCKET_STREAM) ? "tcp" : "udp",
+                  sock_address_to_string(address), socket );
+
+        /* just in case */
+        conn->name[sizeof(conn->name)-1] = 0;
+    }
+
+    stralloc_reset(conn->str);
+    conn->str_pos = 0;
+}
+
+void
+proxy_connection_done( ProxyConnection*  conn )
+{
+    stralloc_reset( conn->str );
+    if (conn->socket >= 0) {
+        socket_close(conn->socket);
+        conn->socket = -1;
+    }
+}
+
+
+void
+proxy_connection_rewind( ProxyConnection*  conn )
+{
+    stralloc_t*  str = conn->str;
+
+    /* only keep a small buffer in the heap */
+    conn->str_pos = 0;
+    str->n        = 0;
+    if (str->a > 1024)
+        stralloc_reset(str);
+}
+
+DataStatus
+proxy_connection_send( ProxyConnection*  conn, int  fd )
+{
+    stralloc_t*  str    = conn->str;
+    int          avail  = str->n - conn->str_pos;
+
+    conn->str_sent = 0;
+
+    if (avail <= 0)
+        return 1;
+
+    if (proxy_log) {
+        PROXY_LOG("%s: sending %d bytes:", conn->name, avail );
+        hex_dump( str->s + conn->str_pos, avail, ">> " );
+    }
+
+    while (avail > 0) {
+        int  n = socket_send(fd, str->s + conn->str_pos, avail);
+        if (n == 0) {
+            PROXY_LOG("%s: connection reset by peer (send)",
+                      conn->name);
+            return DATA_ERROR;
+        }
+        if (n < 0) {
+            if (errno == EWOULDBLOCK || errno == EAGAIN)
+                return DATA_NEED_MORE;
+
+            PROXY_LOG("%s: error: %s", conn->name, errno_str);
+            return DATA_ERROR;
+        }
+        conn->str_pos  += n;
+        conn->str_sent += n;
+        avail          -= n;
+    }
+
+    proxy_connection_rewind(conn);
+    return DATA_COMPLETED;
+}
+
+
+DataStatus
+proxy_connection_receive( ProxyConnection*  conn, int  fd, int  wanted )
+{
+    stralloc_t*  str    = conn->str;
+
+    conn->str_recv = 0;
+
+    while (wanted > 0) {
+        int  n;
+
+        stralloc_readyplus( str, wanted );
+        n = socket_recv(fd, str->s + str->n, wanted);
+        if (n == 0) {
+            PROXY_LOG("%s: connection reset by peer (receive)",
+                      conn->name);
+            return DATA_ERROR;
+        }
+        if (n < 0) {
+            if (errno == EWOULDBLOCK || errno == EAGAIN)
+                return DATA_NEED_MORE;
+
+            PROXY_LOG("%s: error: %s", conn->name, errno_str);
+            return DATA_ERROR;
+        }
+
+        if (proxy_log) {
+            PROXY_LOG("%s: received %d bytes:", conn->name, n );
+            hex_dump( str->s + str->n, n, "<< " );
+        }
+
+        str->n         += n;
+        wanted         -= n;
+        conn->str_recv += n;
+    }
+    return DATA_COMPLETED;
+}
+
+
+DataStatus
+proxy_connection_receive_line( ProxyConnection*  conn, int  fd )
+{
+    stralloc_t*  str = conn->str;
+
+    for (;;) {
+        char  c;
+        int   n = socket_recv(fd, &c, 1);
+        if (n == 0) {
+            PROXY_LOG("%s: disconnected from server", conn->name );
+            return DATA_ERROR;
+        }
+        if (n < 0) {
+            if (errno == EWOULDBLOCK || errno == EAGAIN) {
+                PROXY_LOG("%s: blocked", conn->name);
+                return DATA_NEED_MORE;
+            }
+            PROXY_LOG("%s: error: %s", conn->name, errno_str);
+            return DATA_ERROR;
+        }
+
+        stralloc_add_c(str, c);
+        if (c == '\n') {
+            str->s[--str->n] = 0;
+            if (str->n > 0 && str->s[str->n-1] == '\r')
+                str->s[--str->n] = 0;
+
+            PROXY_LOG("%s: received '%s'", conn->name,
+                      quote_bytes(str->s, str->n));
+            return DATA_COMPLETED;
+        }
+    }
+}
+
+static void
+proxy_connection_insert( ProxyConnection*  conn, ProxyConnection*  after )
+{
+    conn->next        = after->next;
+    after->next->prev = conn;
+    after->next       = conn;
+    conn->prev        = after;
+}
+
+static void
+proxy_connection_remove( ProxyConnection*  conn )
+{
+    conn->prev->next = conn->next;
+    conn->next->prev = conn->prev;
+
+    conn->next = conn->prev = conn;
+}
+
+/** Global service list
+ **/
+
+#define  MAX_SERVICES  4
+
+static  ProxyService*  s_services[ MAX_SERVICES ];
+static  int            s_num_services;
+static  int            s_init;
+
+static void  proxy_manager_atexit( void );
+
+static void
+proxy_manager_init(void)
+{
+    s_init = 1;
+    s_connections->next = s_connections;
+    s_connections->prev = s_connections;
+    atexit( proxy_manager_atexit );
+}
+
+
+extern int
+proxy_manager_add_service( ProxyService*  service )
+{
+    if (!service || s_num_services >= MAX_SERVICES)
+        return -1;
+
+    if (!s_init)
+        proxy_manager_init();
+
+    s_services[s_num_services++] = service;
+    return 0;
+}
+
+
+extern void
+proxy_manager_atexit( void )
+{
+    ProxyConnection*  conn = s_connections->next;
+    int               n;
+
+    /* free all proxy connections */
+    while (conn != s_connections) {
+        ProxyConnection*  next = conn->next;
+        conn->conn_free( conn );
+        conn = next;
+    }
+    conn->next = conn;
+    conn->prev = conn;
+
+    /* free all proxy services */
+    for (n = s_num_services; n-- > 0;) {
+        ProxyService*  service = s_services[n];
+        service->serv_free( service->opaque );
+    }
+    s_num_services = 0;
+}
+
+
+void
+proxy_connection_free( ProxyConnection*  conn,
+                       int               keep_alive,
+                       ProxyEvent        event )
+{
+    if (conn) {
+        int  fd = conn->socket;
+
+        proxy_connection_remove(conn);
+
+        if (event != PROXY_EVENT_NONE)
+            conn->ev_func( conn->ev_opaque, fd, event );
+
+        if (keep_alive)
+            conn->socket = -1;
+
+        conn->conn_free(conn);
+    }
+}
+
+
+int
+proxy_manager_add( SockAddress*    address,
+                   SocketType      sock_type,
+                   ProxyEventFunc  ev_func,
+                   void*           ev_opaque )
+{
+    int  n;
+
+    if (!s_init) {
+        proxy_manager_init();
+    }
+
+    for (n = 0; n < s_num_services; n++) {
+        ProxyService*     service = s_services[n];
+        ProxyConnection*  conn    = service->serv_connect( service->opaque,
+                                                           sock_type,
+                                                           address );
+        if (conn != NULL) {
+            conn->ev_func   = ev_func;
+            conn->ev_opaque = ev_opaque;
+            proxy_connection_insert(conn, s_connections->prev);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+
+/* remove an on-going proxified socket connection from the manager's list.
+ * this is only necessary when the socket connection must be canceled before
+ * the connection accept/refusal occured
+ */
+void
+proxy_manager_del( void*  ev_opaque )
+{
+    ProxyConnection*  conn = s_connections->next;
+    for ( ; conn != s_connections; conn = conn->next ) {
+        if (conn->ev_opaque == ev_opaque) {
+            proxy_connection_remove(conn);
+            conn->conn_free(conn);
+            return;
+        }
+    }
+}
+
+void
+proxy_select_set( ProxySelect*  sel,
+                  int           fd,
+                  unsigned      flags )
+{
+    if (fd < 0 || !flags)
+        return;
+
+    if (*sel->pcount < fd+1)
+        *sel->pcount = fd+1;
+
+    if (flags & PROXY_SELECT_READ) {
+        FD_SET( fd, sel->reads );
+    } else {
+        FD_CLR( fd, sel->reads );
+    }
+    if (flags & PROXY_SELECT_WRITE) {
+        FD_SET( fd, sel->writes );
+    } else {
+        FD_CLR( fd, sel->writes );
+    }
+    if (flags & PROXY_SELECT_ERROR) {
+        FD_SET( fd, sel->errors );
+    } else {
+        FD_CLR( fd, sel->errors );
+    }
+}
+
+unsigned
+proxy_select_poll( ProxySelect*  sel, int  fd )
+{
+    unsigned  flags = 0;
+
+    if (fd >= 0) {
+        if ( FD_ISSET(fd, sel->reads) )
+            flags |= PROXY_SELECT_READ;
+        if ( FD_ISSET(fd, sel->writes) )
+            flags |= PROXY_SELECT_WRITE;
+        if ( FD_ISSET(fd, sel->errors) )
+            flags |= PROXY_SELECT_ERROR;
+    }
+    return flags;
+}
+
+/* this function is called to update the select file descriptor sets
+ * with those of the proxified connection sockets that are currently managed */
+void
+proxy_manager_select_fill( int  *pcount, fd_set*  read_fds, fd_set*  write_fds, fd_set*  err_fds)
+{
+    ProxyConnection*  conn;
+    ProxySelect       sel[1];
+
+    if (!s_init)
+        proxy_manager_init();
+
+    sel->pcount = pcount;
+    sel->reads  = read_fds;
+    sel->writes = write_fds;
+    sel->errors = err_fds;
+
+    conn = s_connections->next;
+    while (conn != s_connections) {
+        ProxyConnection*  next = conn->next;
+        conn->conn_select(conn, sel);
+        conn = next;
+    }
+}
+
+/* this function is called to act on proxified connection sockets when network events arrive */
+void
+proxy_manager_poll( fd_set*  read_fds, fd_set*  write_fds, fd_set*  err_fds )
+{
+    ProxyConnection*  conn = s_connections->next;
+    ProxySelect       sel[1];
+
+    sel->pcount = NULL;
+    sel->reads  = read_fds;
+    sel->writes = write_fds;
+    sel->errors = err_fds;
+
+    while (conn != s_connections) {
+        ProxyConnection*  next  = conn->next;
+        conn->conn_poll( conn, sel );
+        conn = next;
+    }
+}
+
+
+int
+proxy_base64_encode( const char*  src, int  srclen,
+                     char*        dst, int  dstlen )
+{
+    static const char cb64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+    const char*       srcend = src + srclen;
+    int               result = 0;
+
+    while (src+3 <= srcend && result+4 <= dstlen)
+    {
+        dst[result+0] = cb64[ src[0] >> 2 ];
+        dst[result+1] = cb64[ ((src[0] & 3) << 4) | ((src[1] & 0xf0) >> 4) ];
+        dst[result+2] = cb64[ ((src[1] & 0xf) << 2) | ((src[2] & 0xc0) >> 6) ];
+        dst[result+3] = cb64[ src[2] & 0x3f ];
+        src    += 3;
+        result += 4;
+    }
+
+    if (src < srcend) {
+        unsigned char  in[4];
+
+        if (result+4 > dstlen)
+            return -1;
+
+        in[0] = src[0];
+        in[1] = src+1 < srcend ? src[1] : 0;
+        in[2] = src+2 < srcend ? src[2] : 0;
+
+        dst[result+0] = cb64[ in[0] >> 2 ];
+        dst[result+1] = cb64[ ((in[0] & 3) << 4) | ((in[1] & 0xf0) >> 4) ];
+        dst[result+2] = (unsigned char) (src+1 < srcend ? cb64[ ((in[1] & 0xf) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
+        dst[result+3] = (unsigned char) (src+2 < srcend ? cb64[ in[2] & 0x3f ] : '=');
+        result += 4;
+    }
+    return result;
+}
+
+int
+proxy_resolve_server( SockAddress*   addr,
+                      const char*    servername,
+                      int            servernamelen,
+                      int            serverport )
+{
+    char  name0[64], *name = name0;
+    int   result = -1;
+
+    if (servernamelen < 0)
+        servernamelen = strlen(servername);
+
+    if (servernamelen >= sizeof(name0)) {
+        AARRAY_NEW(name, servernamelen+1);
+    }
+
+    memcpy(name, servername, servernamelen);
+    name[servernamelen] = 0;
+
+    if (sock_address_init_resolve( addr, name, serverport, 0 ) < 0) {
+        PROXY_LOG("%s: can't resolve proxy server name '%s'",
+                  __FUNCTION__, name);
+        goto Exit;
+    }
+
+    PROXY_LOG("server name '%s' resolved to %s", name, sock_address_to_string(addr));
+    result = 0;
+
+Exit:
+    if (name != name0)
+        AFREE(name);
+
+    return result;
+}
+
+
diff --git a/proxy/proxy_common.h b/proxy/proxy_common.h
new file mode 100644
index 0000000..78eddd8
--- /dev/null
+++ b/proxy/proxy_common.h
@@ -0,0 +1,88 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _PROXY_COMMON_H_
+#define _PROXY_COMMON_H_
+
+#include "sockets.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/select.h>
+#endif
+
+/* types and definitions used by all proxy connections */
+
+typedef enum {
+    PROXY_EVENT_NONE,
+    PROXY_EVENT_CONNECTED,
+    PROXY_EVENT_CONNECTION_REFUSED,
+    PROXY_EVENT_SERVER_ERROR
+} ProxyEvent;
+
+/* event can't be NONE when this callback is called */
+typedef void (*ProxyEventFunc)( void*  opaque, int  fd, ProxyEvent  event );
+
+extern void  proxy_set_verbose(int  mode);
+
+
+typedef enum {
+    PROXY_OPTION_AUTH_USERNAME = 1,
+    PROXY_OPTION_AUTH_PASSWORD,
+
+    PROXY_OPTION_HTTP_NOCACHE = 100,
+    PROXY_OPTION_HTTP_KEEPALIVE,
+    PROXY_OPTION_HTTP_USER_AGENT,
+
+    PROXY_OPTION_MAX
+
+} ProxyOptionType;
+
+
+typedef struct {
+    ProxyOptionType  type;
+    const char*      string;
+    int              string_len;
+} ProxyOption;
+
+
+/* add a new proxified socket connection to the manager's list. the event function
+ * will be called when the connection is established or refused.
+ *
+ * only IPv4 is supported at the moment, since our slirp code cannot handle IPv6
+ *
+ * returns 0 on success, or -1 if there is no proxy service for this type of connection
+ */
+extern int   proxy_manager_add( SockAddress*         address,
+                                SocketType           sock_type,
+                                ProxyEventFunc       ev_func,
+                                void*                ev_opaque );
+
+/* remove an on-going proxified socket connection from the manager's list.
+ * this is only necessary when the socket connection must be canceled before
+ * the connection accept/refusal occured
+ */
+extern void  proxy_manager_del( void*  ev_opaque );
+
+/* this function is called to update the select file descriptor sets
+ * with those of the proxified connection sockets that are currently managed */
+extern void  proxy_manager_select_fill( int     *pcount, 
+                                        fd_set*  read_fds, 
+                                        fd_set*  write_fds, 
+                                        fd_set*  err_fds);
+
+/* this function is called to act on proxified connection sockets when network events arrive */
+extern void  proxy_manager_poll( fd_set*  read_fds, 
+                                 fd_set*  write_fds, 
+                                 fd_set*  err_fds );
+
+#endif /* END */
diff --git a/proxy/proxy_http.c b/proxy/proxy_http.c
new file mode 100644
index 0000000..f753587
--- /dev/null
+++ b/proxy/proxy_http.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "proxy_int.h"
+#include "proxy_http_int.h"
+#include "qemu-common.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#define  HTTP_VERSION  "1.1"
+
+static void
+http_service_free( HttpService*  service )
+{
+    PROXY_LOG("%s", __FUNCTION__);
+    if (service->footer != service->footer0)
+        qemu_free(service->footer);
+    qemu_free(service);
+}
+
+
+static ProxyConnection*
+http_service_connect( HttpService*  service,
+                      SocketType    sock_type,
+                      SockAddress*  address )
+{
+    /* the HTTP proxy can only handle TCP connections */
+    if (sock_type != SOCKET_STREAM)
+        return NULL;
+
+    /* if the client tries to directly connect to the proxy, let it do so */
+    if (sock_address_equal( address, &service->server_addr ))
+        return NULL;
+
+    PROXY_LOG("%s: trying to connect to %s",
+              __FUNCTION__, sock_address_to_string(address));
+
+    if (sock_address_get_port(address) == 80) {
+        /* use the rewriter for HTTP */
+        PROXY_LOG("%s: using HTTP rewriter", __FUNCTION__);
+        return http_rewriter_connect(service, address);
+    } else {
+        PROXY_LOG("%s: using HTTP rewriter", __FUNCTION__);
+        return http_connector_connect(service, address);
+    }
+}
+
+
+int
+proxy_http_setup( const char*         servername,
+                  int                 servernamelen,
+                  int                 serverport,
+                  int                 num_options,
+                  const ProxyOption*  options )
+{
+    HttpService*        service;
+    SockAddress         server_addr;
+    const ProxyOption*  opt_nocache   = NULL;
+    const ProxyOption*  opt_keepalive = NULL;
+    const ProxyOption*  opt_auth_user = NULL;
+    const ProxyOption*  opt_auth_pass = NULL;
+    const ProxyOption*  opt_user_agent = NULL;
+
+    if (servernamelen < 0)
+        servernamelen = strlen(servername);
+
+    PROXY_LOG( "%s: creating http proxy service connecting to: %.*s:%d",
+               __FUNCTION__, servernamelen, servername, serverport );
+
+    /* resolve server address */
+    if (proxy_resolve_server(&server_addr, servername,
+                             servernamelen, serverport) < 0)
+    {
+        return -1;
+    }
+
+    /* create service object */
+    service = qemu_mallocz(sizeof(*service));
+    if (service == NULL) {
+        PROXY_LOG("%s: not enough memory to allocate new proxy service", __FUNCTION__);
+        return -1;
+    }
+
+    service->server_addr = server_addr;
+
+    /* parse options */
+    {
+        const ProxyOption*  opt = options;
+        const ProxyOption*  end = opt + num_options;
+
+        for ( ; opt < end; opt++ ) {
+            switch (opt->type) {
+                case PROXY_OPTION_HTTP_NOCACHE:     opt_nocache    = opt; break;
+                case PROXY_OPTION_HTTP_KEEPALIVE:   opt_keepalive  = opt; break;
+                case PROXY_OPTION_AUTH_USERNAME:    opt_auth_user  = opt; break;
+                case PROXY_OPTION_AUTH_PASSWORD:    opt_auth_pass  = opt; break;
+                case PROXY_OPTION_HTTP_USER_AGENT:  opt_user_agent = opt; break;
+                default: ;
+            }
+        }
+    }
+
+    /* prepare footer */
+    {
+        int    wlen;
+        char*  p    = service->footer0;
+        char*  end  = p + sizeof(service->footer0);
+
+        /* no-cache */
+        if (opt_nocache) {
+            p += snprintf(p, end-p, "Pragma: no-cache\r\nCache-Control: no-cache\r\n");
+            if (p >= end) goto FooterOverflow;
+        }
+        /* keep-alive */
+        if (opt_keepalive) {
+            p += snprintf(p, end-p, "Connection: Keep-Alive\r\nProxy-Connection: Keep-Alive\r\n");
+            if (p >= end) goto FooterOverflow;
+        }
+        /* authentication */
+        if (opt_auth_user && opt_auth_pass) {
+            char  user_pass[256];
+            char  encoded[512];
+            int   uplen;
+
+            uplen = snprintf( user_pass, sizeof(user_pass), "%.*s:%.*s",
+                              opt_auth_user->string_len, opt_auth_user->string,
+                              opt_auth_pass->string_len, opt_auth_pass->string );
+
+            if (uplen >= sizeof(user_pass)) goto FooterOverflow;
+
+            wlen = proxy_base64_encode(user_pass, uplen, encoded, (int)sizeof(encoded));
+            if (wlen < 0) {
+                PROXY_LOG( "could not base64 encode '%.*s'", uplen, user_pass);
+                goto FooterOverflow;
+            }
+
+            p += snprintf(p, end-p, "Proxy-authorization: Basic %.*s\r\n", wlen, encoded);
+            if (p >= end) goto FooterOverflow;
+        }
+        /* user agent */
+        if (opt_user_agent) {
+            p += snprintf(p, end-p, "User-Agent: %.*s\r\n",
+                          opt_user_agent->string_len,
+                          opt_user_agent->string);
+            if (p >= end) goto FooterOverflow;
+        }
+
+        p += snprintf(p, end-p, "\r\n");
+
+        if (p >= end) {
+        FooterOverflow:
+            PROXY_LOG( "%s: buffer overflow when creating connection footer",
+                       __FUNCTION__);
+            http_service_free(service);
+            return -1;
+        }
+
+        service->footer     = service->footer0;
+        service->footer_len = (p - service->footer);
+    }
+
+    PROXY_LOG( "%s: creating HTTP Proxy Service Footer is (len=%d):\n'%.*s'",
+               __FUNCTION__, service->footer_len,
+               service->footer_len, service->footer );
+
+    service->root->opaque       = service;
+    service->root->serv_free    = (ProxyServiceFreeFunc)    http_service_free;
+    service->root->serv_connect = (ProxyServiceConnectFunc) http_service_connect;
+
+    if (proxy_manager_add_service( service->root ) < 0) {
+        PROXY_LOG("%s: could not register service ?", __FUNCTION__);
+        http_service_free(service);
+        return -1;
+    }
+    return 0;
+}
+
diff --git a/proxy/proxy_http.h b/proxy/proxy_http.h
new file mode 100644
index 0000000..a2e2917
--- /dev/null
+++ b/proxy/proxy_http.h
@@ -0,0 +1,24 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _PROXY_HTTP_H
+#define _PROXY_HTTP_H
+
+#include "proxy_common.h"
+
+extern int
+proxy_http_setup( const char*         servername,
+                  int                 servernamelen,
+                  int                 serverport,
+                  int                 num_options,
+                  const ProxyOption*  options );
+
+#endif /* END */
diff --git a/proxy/proxy_http_connector.c b/proxy/proxy_http_connector.c
new file mode 100644
index 0000000..6f03d9b
--- /dev/null
+++ b/proxy/proxy_http_connector.c
@@ -0,0 +1,203 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "proxy_http_int.h"
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "qemu-common.h"
+
+/* A HttpConnector implements a non-HTTP proxied connection
+ * through the CONNECT method. Many firewalls are configured
+ * to reject these for port 80, so these connections should
+ * use a HttpRewriter instead.
+ */
+
+typedef enum {
+    STATE_NONE = 0,
+    STATE_CONNECTING,           /* connecting to the server */
+    STATE_SEND_HEADER,          /* connected, sending header to the server */
+    STATE_RECEIVE_ANSWER_LINE1,
+    STATE_RECEIVE_ANSWER_LINE2  /* connected, reading server's answer */
+} ConnectorState;
+
+typedef struct Connection {
+    ProxyConnection  root[1];
+    ConnectorState   state;
+} Connection;
+
+
+static void
+connection_free( ProxyConnection*  root )
+{
+    proxy_connection_done(root);
+    qemu_free(root);
+}
+
+
+
+#define  HTTP_VERSION  "1.1"
+
+static int
+connection_init( Connection*  conn )
+{
+    HttpService*      service = (HttpService*) conn->root->service;
+    ProxyConnection*  root    = conn->root;
+    stralloc_t*       str     = root->str;
+
+    proxy_connection_rewind(root);
+    stralloc_add_format(str, "CONNECT %s HTTP/" HTTP_VERSION "\r\n",
+                        sock_address_to_string(&root->address));
+
+    stralloc_add_bytes(str, service->footer, service->footer_len);
+
+    if (!socket_connect( root->socket, &service->server_addr )) {
+        /* immediate connection ?? */
+        conn->state = STATE_SEND_HEADER;
+        PROXY_LOG("%s: immediate connection", root->name);
+    }
+    else {
+        if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
+            conn->state = STATE_CONNECTING;
+            PROXY_LOG("%s: connecting", root->name);
+        }
+        else {
+            PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+
+static void
+connection_select( ProxyConnection*   root,
+                   ProxySelect*       sel )
+{
+    unsigned     flags;
+    Connection*  conn = (Connection*)root;
+
+    switch (conn->state) {
+        case STATE_RECEIVE_ANSWER_LINE1:
+        case STATE_RECEIVE_ANSWER_LINE2:
+            flags = PROXY_SELECT_READ;
+            break;
+
+        case STATE_CONNECTING:
+        case STATE_SEND_HEADER:
+            flags = PROXY_SELECT_WRITE;
+            break;
+
+        default:
+            flags = 0;
+    };
+    proxy_select_set(sel, root->socket, flags);
+}
+
+static void
+connection_poll( ProxyConnection*   root,
+                 ProxySelect*       sel )
+{
+    DataStatus   ret  = DATA_NEED_MORE;
+    Connection*  conn = (Connection*)root;
+    int          fd   = root->socket;
+
+    if (!proxy_select_poll(sel, fd))
+        return;
+
+    switch (conn->state)
+    {
+        case STATE_CONNECTING:
+            PROXY_LOG("%s: connected to http proxy, sending header", root->name);
+            conn->state = STATE_SEND_HEADER;
+            break;
+
+        case STATE_SEND_HEADER:
+            ret = proxy_connection_send(root, fd);
+            if (ret == DATA_COMPLETED) {
+                conn->state = STATE_RECEIVE_ANSWER_LINE1;
+                PROXY_LOG("%s: header sent, receiving first answer line", root->name);
+            }
+            break;
+
+        case STATE_RECEIVE_ANSWER_LINE1:
+        case STATE_RECEIVE_ANSWER_LINE2:
+            ret = proxy_connection_receive_line(root, root->socket);
+            if (ret == DATA_COMPLETED) {
+                if (conn->state == STATE_RECEIVE_ANSWER_LINE1) {
+                    int  http1, http2, codenum;
+                    const char*  line = root->str->s;
+
+                    if ( sscanf(line, "HTTP/%d.%d %d", &http1, &http2, &codenum) != 3 ) {
+                        PROXY_LOG( "%s: invalid answer from proxy: '%s'",
+                                    root->name, line );
+                        ret = DATA_ERROR;
+                        break;
+                    }
+
+                    /* success is 2xx */
+                    if (codenum/2 != 100) {
+                        PROXY_LOG( "%s: connection refused, error=%d",
+                                    root->name, codenum );
+                        proxy_connection_free( root, 0, PROXY_EVENT_CONNECTION_REFUSED );
+                        return;
+                    }
+                    PROXY_LOG("%s: receiving second answer line", root->name);
+                    conn->state = STATE_RECEIVE_ANSWER_LINE2;
+                    proxy_connection_rewind(root);
+                } else {
+                    /* ok, we're connected */
+                    PROXY_LOG("%s: connection succeeded", root->name);
+                    proxy_connection_free( root, 1, PROXY_EVENT_CONNECTED );
+                }
+            }
+            break;
+
+        default:
+            PROXY_LOG("%s: invalid state for read event: %d", root->name, conn->state);
+    }
+
+    if (ret == DATA_ERROR) {
+        proxy_connection_free( root, 0, PROXY_EVENT_SERVER_ERROR );
+    }
+}
+
+
+
+ProxyConnection*
+http_connector_connect( HttpService*  service,
+                        SockAddress*  address )
+{
+    Connection*  conn;
+    int          s;
+
+    s = socket_create_inet( SOCKET_STREAM );
+    if (s < 0)
+        return NULL;
+
+    conn = qemu_mallocz(sizeof(*conn));
+    if (conn == NULL) {
+        socket_close(s);
+        return NULL;
+    }
+
+    proxy_connection_init( conn->root, s, address, service->root,
+                           connection_free,
+                           connection_select,
+                           connection_poll );
+
+    if ( connection_init( conn ) < 0 ) {
+        connection_free( conn->root );
+        return NULL;
+    }
+
+    return conn->root;
+}
diff --git a/proxy/proxy_http_int.h b/proxy/proxy_http_int.h
new file mode 100644
index 0000000..6daa9cb
--- /dev/null
+++ b/proxy/proxy_http_int.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _PROXY_HTTP_INT_H
+#define _PROXY_HTTP_INT_H
+
+#include "proxy_http.h"
+#include "proxy_int.h"
+
+/* the HttpService object */
+typedef struct HttpService {
+    ProxyService        root[1];
+    SockAddress         server_addr;  /* server address and port */
+    char*               footer;      /* the footer contains the static parts of the */
+    int                 footer_len;  /* connection header, we generate it only once */
+    char                footer0[512];
+} HttpService;
+
+/* create a CONNECT connection (for port != 80) */
+extern ProxyConnection*  http_connector_connect(
+                                HttpService*   service,
+                                SockAddress*   address );
+
+/* create a HTTP rewriting connection (for port == 80) */
+extern ProxyConnection*  http_rewriter_connect(
+                                HttpService*   service,
+                                SockAddress*   address );
+
+
+#endif /* _PROXY_HTTP_INT_H */
diff --git a/proxy/proxy_http_rewriter.c b/proxy/proxy_http_rewriter.c
new file mode 100644
index 0000000..812bf9c
--- /dev/null
+++ b/proxy/proxy_http_rewriter.c
@@ -0,0 +1,1125 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "proxy_http_int.h"
+#include <stdio.h>
+#include <string.h>
+#include "qemu-common.h"
+#include "android/utils/system.h"  /* strsep */
+
+/* this implements a transparent HTTP rewriting proxy
+ *
+ * this is needed because the HTTP spec mandates that
+ * any query made to a proxy uses an absolute URI as
+ * in:
+ *
+ *    GET http://www.example.com/index.html HTTP/1.1
+ *
+ * while the Android browser will think it's talking to
+ * a normal web server and will issue a:
+ *
+ *    GET /index.html HTTP/1.1
+ *    Host: www.example.com
+ *
+ * what we do here is thus the following:
+ *
+ * - read the request header
+ * - rewrite the request's URI to use absolute URI
+ * - send the rewritten header to the proxy
+ * - then read the rest of the request, and tunnel it to the
+ *   proxy as well
+ * - read the answer as-is and send it back to the system
+ *
+ * this sounds all easy, but the rules for computing the
+ * sizes of HTTP Message Bodies makes the implementation
+ * a *bit* funky.
+ */
+
+/* define D_ACTIVE to 1 to dump additionnal debugging
+ * info when -debug-proxy is used. These are only needed
+ * when debugging the proxy code.
+ */
+#define  D_ACTIVE  1
+
+#if D_ACTIVE
+#  define  D(...)   PROXY_LOG(__VA_ARGS__)
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+
+/** *************************************************************
+ **
+ **   HTTP HEADERS
+ **
+ **/
+
+typedef struct HttpHeader {
+    struct HttpHeader*  next;
+    const char*         key;
+    const char*         value;
+} HttpHeader;
+
+static void
+http_header_free( HttpHeader*  h )
+{
+    if (h) {
+        qemu_free((char*)h->value);
+        qemu_free(h);
+    }
+}
+
+static int
+http_header_append( HttpHeader*  h, const char*  value )
+{
+    int    old = strlen(h->value);
+    int    new = strlen(value);
+    char*  s   = realloc((char*)h->value, old+new+1);
+    if (s == NULL)
+        return -1;
+    memcpy(s + old, value, new+1);
+    h->value = (const char*)s;
+    return 0;
+}
+
+static HttpHeader*
+http_header_alloc( const char*  key, const char*  value )
+{
+    int          len = strlen(key)+1;
+    HttpHeader*  h   = malloc(sizeof(*h) + len+1);
+    if (h) {
+        h->next  = NULL;
+        h->key   = (const char*)(h+1);
+        memcpy( (char*)h->key, key, len );
+        h->value = qemu_strdup(value);
+    }
+    return h;
+}
+
+typedef struct {
+    HttpHeader*   first;
+    HttpHeader*   last;
+} HttpHeaderList;
+
+static void
+http_header_list_init( HttpHeaderList*  l )
+{
+    l->first = l->last = NULL;
+}
+
+static void
+http_header_list_done( HttpHeaderList*  l )
+{
+    while (l->first) {
+        HttpHeader*  h = l->first;
+        l->first = h->next;
+        http_header_free(h);
+    }
+    l->last = NULL;
+}
+
+static void
+http_header_list_add( HttpHeaderList*  l,
+                      HttpHeader*      h )
+{
+    if (!l->first) {
+        l->first = h;
+    } else {
+        l->last->next = h;
+    }
+    h->next = NULL;
+    l->last = h;
+}
+
+static const char*
+http_header_list_find( HttpHeaderList*  l,
+                       const char*      key )
+{
+    HttpHeader*  h;
+    for (h = l->first; h; h = h->next)
+        if (!strcasecmp(h->key, key))
+            return h->value;
+
+    return NULL;
+}
+
+/** *************************************************************
+ **
+ **   HTTP REQUEST AND REPLY
+ **
+ **/
+
+typedef enum {
+    HTTP_REQUEST_UNSUPPORTED = 0,
+    HTTP_REQUEST_GET,
+    HTTP_REQUEST_HEAD,
+    HTTP_REQUEST_POST,
+    HTTP_REQUEST_PUT,
+    HTTP_REQUEST_DELETE,
+} HttpRequestType;
+
+typedef struct {
+    HttpRequestType   req_type;
+    char*             req_method;
+    char*             req_uri;
+    char*             req_version;
+    char*             rep_version;
+    int               rep_code;
+    char*             rep_readable;
+    HttpHeaderList    headers[1];
+} HttpRequest;
+
+
+static HttpRequest*
+http_request_alloc( const char*      method,
+                    const char*      uri,
+                    const char*      version )
+{
+    HttpRequest*  r = malloc(sizeof(*r));
+
+    r->req_method   = qemu_strdup(method);
+    r->req_uri      = qemu_strdup(uri);
+    r->req_version  = qemu_strdup(version);
+    r->rep_version  = NULL;
+    r->rep_code     = -1;
+    r->rep_readable = NULL;
+
+    if (!strcmp(method,"GET")) {
+        r->req_type = HTTP_REQUEST_GET;
+    } else if (!strcmp(method,"POST")) {
+        r->req_type = HTTP_REQUEST_POST;
+    } else if (!strcmp(method,"HEAD")) {
+        r->req_type = HTTP_REQUEST_HEAD;
+    } else if (!strcmp(method,"PUT")) {
+        r->req_type = HTTP_REQUEST_PUT;
+    } else if (!strcmp(method,"DELETE")) {
+        r->req_type = HTTP_REQUEST_DELETE;
+    } else
+        r->req_type = HTTP_REQUEST_UNSUPPORTED;
+
+    http_header_list_init(r->headers);
+    return r;
+}
+
+static void
+http_request_replace_uri( HttpRequest*  r,
+                          const char*   uri )
+{
+    const char*  old = r->req_uri;
+    r->req_uri = qemu_strdup(uri);
+    qemu_free((char*)old);
+}
+
+static void
+http_request_free( HttpRequest*  r )
+{
+    if (r) {
+        http_header_list_done(r->headers);
+
+        qemu_free(r->req_method);
+        qemu_free(r->req_uri);
+        qemu_free(r->req_version);
+        qemu_free(r->rep_version);
+        qemu_free(r->rep_readable);
+        qemu_free(r);
+    }
+}
+
+static char*
+http_request_find_header( HttpRequest*  r,
+                          const char*   key )
+{
+    return (char*)http_header_list_find(r->headers, key);
+}
+
+
+static int
+http_request_add_header( HttpRequest*  r,
+                         const char*   key,
+                         const char*   value )
+{
+    HttpHeader*  h = http_header_alloc(key,value);
+    if (h) {
+        http_header_list_add(r->headers, h);
+        return 0;
+    }
+    return -1;
+}
+
+static int
+http_request_add_to_last_header( HttpRequest*  r,
+                                 const char*   line )
+{
+    if (r->headers->last) {
+        return http_header_append( r->headers->last, line );
+    } else {
+        return -1;
+    }
+}
+
+static int
+http_request_set_reply( HttpRequest*  r,
+                        const char*   version,
+                        const char*   code,
+                        const char*   readable )
+{
+    if (strcmp(version,"HTTP/1.0") && strcmp(version,"HTTP/1.1")) {
+        PROXY_LOG("%s: bad reply protocol: %s", __FUNCTION__, version);
+        return -1;
+    }
+    r->rep_code = atoi(code);
+    if (r->rep_code == 0) {
+        PROXY_LOG("%s: bad reply code: %d", __FUNCTION__, code);
+        return -1;
+    }
+
+    r->rep_version  = qemu_strdup(version);
+    r->rep_readable = qemu_strdup(readable);
+
+    /* reset the list of headers */
+    http_header_list_done(r->headers);
+    return 0;
+}
+
+/** *************************************************************
+ **
+ **   REWRITER CONNECTION
+ **
+ **/
+
+typedef enum {
+    STATE_CONNECTING = 0,
+    STATE_CREATE_SOCKET_PAIR,
+    STATE_REQUEST_FIRST_LINE,
+    STATE_REQUEST_HEADERS,
+    STATE_REQUEST_SEND,
+    STATE_REQUEST_BODY,
+    STATE_REPLY_FIRST_LINE,
+    STATE_REPLY_HEADERS,
+    STATE_REPLY_SEND,
+    STATE_REPLY_BODY,
+} ConnectionState;
+
+/* root->socket is connected to the proxy server. while
+ * slirp_fd is connected to the slirp code through a
+ * socket_pair() we created for this specific purpose.
+ */
+
+typedef enum {
+    BODY_NONE = 0,
+    BODY_KNOWN_LENGTH,
+    BODY_UNTIL_CLOSE,
+    BODY_CHUNKED,
+    BODY_MODE_MAX
+} BodyMode;
+
+static const char* const  body_mode_str[BODY_MODE_MAX] = {
+    "NONE", "KNOWN_LENGTH", "UNTIL_CLOSE", "CHUNKED"
+};
+
+typedef struct {
+    ProxyConnection   root[1];
+    int               slirp_fd;
+    ConnectionState   state;
+    HttpRequest*      request;
+    BodyMode          body_mode;
+    int64_t           body_length;
+    int64_t           body_total;
+    int64_t           body_sent;
+    int64_t           chunk_length;
+    int64_t           chunk_total;
+    char              body_has_data;
+    char              body_is_full;
+    char              body_is_closed;
+    char              parse_chunk_header;
+    char              parse_chunk_trailer;
+} RewriteConnection;
+
+
+static void
+rewrite_connection_free( ProxyConnection*  root )
+{
+    RewriteConnection*  conn = (RewriteConnection*)root;
+
+    if (conn->slirp_fd >= 0) {
+        socket_close(conn->slirp_fd);
+        conn->slirp_fd = -1;
+    }
+    http_request_free(conn->request);
+    proxy_connection_done(root);
+    qemu_free(conn);
+}
+
+
+static int
+rewrite_connection_init( RewriteConnection*   conn )
+{
+    HttpService*      service = (HttpService*) conn->root->service;
+    ProxyConnection*  root    = conn->root;
+
+    conn->slirp_fd = -1;
+    conn->state    = STATE_CONNECTING;
+
+    if (socket_connect( root->socket, &service->server_addr ) < 0) {
+        if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
+            PROXY_LOG("%s: connecting", conn->root->name);
+        }
+        else {
+            PROXY_LOG("%s: cannot connect to proxy: %s", root->name, errno_str);
+            return -1;
+        }
+    }
+    else {
+        PROXY_LOG("%s: immediate connection", root->name);
+        conn->state = STATE_CREATE_SOCKET_PAIR;
+    }
+    return 0;
+}
+
+static int
+rewrite_connection_create_sockets( RewriteConnection*  conn )
+{
+    /* immediate connection to the proxy. now create a socket
+        * pair and send a 'success' event to slirp */
+    int               slirp_1;
+    ProxyConnection*  root = conn->root;
+
+    if (socket_pair( &slirp_1, &conn->slirp_fd ) < 0) {
+        PROXY_LOG("%s: coult not create socket pair: %s",
+                    root->name, errno_str);
+        return -1;
+    }
+
+    root->ev_func( root->ev_opaque, slirp_1, PROXY_EVENT_CONNECTED );
+    conn->state = STATE_REQUEST_FIRST_LINE;
+    return 0;
+}
+
+
+/* read the first line of a given HTTP request. returns -1/0/+1 */
+static DataStatus
+rewrite_connection_read_request( RewriteConnection*  conn )
+{
+    ProxyConnection*  root = conn->root;
+    DataStatus        ret;
+
+    ret = proxy_connection_receive_line(root, conn->slirp_fd);
+    if (ret == DATA_COMPLETED) {
+        /* now parse the first line to see if we can handle it */
+        char*  line   = root->str->s;
+        char*  method;
+        char*  uri;
+        char*  version;
+        char*  p = line;
+
+        method = strsep(&p, " ");
+        if (p == NULL) {
+            PROXY_LOG("%s: can't parse method in '%'", 
+                      root->name, line);
+            return DATA_ERROR;
+        }
+        uri = strsep(&p, " ");
+        if (p == NULL) {
+            PROXY_LOG( "%s: can't parse URI in '%s'",
+                       root->name, line);
+            return DATA_ERROR;
+        }
+        version = strsep(&p, " ");
+        if (p != NULL) {
+            PROXY_LOG( "%s: extra data after version in '%s'",
+                       root->name, line);
+            return DATA_ERROR;
+        }
+        if (conn->request)
+            http_request_free(conn->request);
+
+        conn->request = http_request_alloc( method, uri, version );
+        if (!conn->request)
+            return DATA_ERROR;
+
+        proxy_connection_rewind(root);
+    }
+    return ret;
+}
+
+
+static DataStatus
+rewrite_connection_read_reply( RewriteConnection*  conn )
+{
+    ProxyConnection*  root = conn->root;
+    DataStatus        ret;
+
+    ret = proxy_connection_receive_line( root, root->socket );
+    if (ret == DATA_COMPLETED) {
+        HttpRequest*  request = conn->request;
+
+        char*  line = stralloc_cstr( root->str );
+        char*  p = line;
+        char*  protocol;
+        char*  number;
+        char*  readable;
+
+        protocol = strsep(&p, " ");
+        if (p == NULL) {
+            PROXY_LOG("%s: can't parse response protocol: '%s'",
+                      root->name, line);
+            return DATA_ERROR;
+        }
+        number = strsep(&p, " ");
+        if (p == NULL) {
+            PROXY_LOG("%s: can't parse response number: '%s'",
+                      root->name, line);
+            return DATA_ERROR;
+        }
+        readable = p;
+
+        if (http_request_set_reply(request, protocol, number, readable) < 0)
+            return DATA_ERROR;
+
+        proxy_connection_rewind(root);
+    }
+    return ret;
+}
+
+
+static DataStatus
+rewrite_connection_read_headers( RewriteConnection*   conn,
+                                 int                  fd )
+{
+    int               ret;
+    ProxyConnection*  root = conn->root;
+
+    for (;;) {
+        char*        line;
+        stralloc_t*  str = root->str;
+
+        ret = proxy_connection_receive_line(root, fd);
+        if (ret != DATA_COMPLETED)
+            break;
+
+        str->n = 0;
+        line   = str->s;
+
+        if (line[0] == 0) {
+            /* an empty line means the end of headers */
+            ret = 1;
+            break;
+        }
+
+        /* it this a continuation ? */
+        if (line[0] == ' ' || line[0] == '\t') {
+            ret = http_request_add_to_last_header( conn->request, line );
+        }
+        else {
+            char*  key;
+            char*  value;
+
+            value = line;
+            key   = strsep(&value, ":");
+            if (value == NULL) {
+                PROXY_LOG("%s: can't parse header '%s'", root->name, line);
+                ret = -1;
+                break;
+            }
+            value += strspn(value, " ");
+            if (http_request_add_header(conn->request, key, value) < 0)
+                ret = -1;
+        }
+        if (ret == DATA_ERROR)
+            break;
+    }
+    return ret;
+}
+
+static int
+rewrite_connection_rewrite_request( RewriteConnection*  conn )
+{
+    ProxyConnection* root    = conn->root;
+    HttpService*     service = (HttpService*) root->service;
+    HttpRequest*     r       = conn->request;
+    stralloc_t*      str     = root->str;
+    HttpHeader*      h;
+
+    proxy_connection_rewind(conn->root);
+
+    /* only rewrite the URI if it is not absolute */
+    if (r->req_uri[0] == '/') {
+        char*  host = http_request_find_header(r, "Host");
+        if (host == NULL) {
+            PROXY_LOG("%s: uh oh, not Host: in request ?", root->name);
+        } else {
+            /* now create new URI */
+            stralloc_add_str(str, "http://");
+            stralloc_add_str(str, host);
+            stralloc_add_str(str, r->req_uri);
+            http_request_replace_uri(r, stralloc_cstr(str));
+            proxy_connection_rewind(root);
+        }
+    }
+
+    stralloc_format( str, "%s %s %s\r\n", r->req_method, r->req_uri, r->req_version );
+    for (h = r->headers->first; h; h = h->next) {
+        stralloc_add_format( str, "%s: %s\r\n", h->key, h->value );
+    }
+    /* add the service's footer - includes final \r\n */
+    stralloc_add_bytes( str, service->footer, service->footer_len );
+
+    return 0;
+}
+
+static int
+rewrite_connection_rewrite_reply( RewriteConnection*  conn )
+{
+    HttpRequest*     r    = conn->request;
+    ProxyConnection* root = conn->root;
+    stralloc_t*      str  = root->str;
+    HttpHeader*      h;
+
+    proxy_connection_rewind(root);
+    stralloc_format(str, "%s %d %s\r\n", r->rep_version, r->rep_code, r->rep_readable);
+    for (h = r->headers->first; h; h = h->next) {
+        stralloc_add_format(str, "%s: %s\r\n", h->key, h->value);
+    }
+    stralloc_add_str(str, "\r\n");
+
+    return 0;
+}
+
+
+static int
+rewrite_connection_get_body_length( RewriteConnection*  conn,
+                                    int                 is_request )
+{
+    HttpRequest*      r    = conn->request;
+    ProxyConnection*  root = conn->root;
+    char*             content_length;
+    char*             transfer_encoding;
+
+    conn->body_mode      = BODY_NONE;
+    conn->body_length    = 0;
+    conn->body_total     = 0;
+    conn->body_sent      = 0;
+    conn->body_is_closed = 0;
+    conn->body_is_full   = 0;
+    conn->body_has_data  = 0;
+
+    proxy_connection_rewind(root);
+
+    if (is_request) {
+        /* only POST and PUT should have a body */
+        if (r->req_type != HTTP_REQUEST_POST &&
+            r->req_type != HTTP_REQUEST_PUT)
+        {
+            return 0;
+        }
+    } else {
+        /* HTTP 1.1 Section 4.3 Message Body states that HEAD requests must not have
+        * a message body, as well as any 1xx, 204 and 304 replies */
+        if (r->req_type == HTTP_REQUEST_HEAD || r->rep_code/100 == 1 ||
+            r->rep_code == 204 || r->rep_code == 304)
+            return 0;
+    }
+
+    content_length = http_request_find_header(r, "Content-Length");
+    if (content_length != NULL) {
+        char*    end;
+        int64_t  body_len = strtoll( content_length, &end, 10 );
+        if (*end != '\0' || *content_length == '\0' || body_len < 0) {
+            PROXY_LOG("%s: bad content length: %s", root->name, content_length);
+            return DATA_ERROR;
+        }
+        if (body_len > 0) {
+            conn->body_mode   = BODY_KNOWN_LENGTH;
+            conn->body_length = body_len;
+        }
+    } else {
+        char*  connection = http_request_find_header(r, "Proxy-Connection");
+
+        if (!connection)
+            connection = http_request_find_header(r, "Connection");
+
+        if (!connection || strcasecmp(connection, "Close")) {
+            /* hum, we can't support this at all */
+            PROXY_LOG("%s: can't determine content length, and client wants"
+                        " to keep connection opened",
+                        root->name);
+            return -1;
+        }
+        /* a negative value means that the data ends when the client
+         * disconnects the connection.
+         */
+        conn->body_mode = BODY_UNTIL_CLOSE;
+    }
+    transfer_encoding = http_request_find_header(r, "Transfer-Encoding");
+    if (transfer_encoding && !strcasecmp(transfer_encoding, "Chunked")) {
+        conn->body_mode           = BODY_CHUNKED;
+        conn->parse_chunk_header  = 0;
+        conn->parse_chunk_trailer = 0;
+        conn->chunk_length        = -1;
+        conn->chunk_total         = 0;
+    }
+    D("%s: body_length=%lld body_mode=%s",
+      root->name, conn->body_length, 
+      body_mode_str[conn->body_mode]);
+
+    proxy_connection_rewind(root);
+    return 0;
+}
+
+#define  MAX_BODY_BUFFER  65536
+
+static DataStatus
+rewrite_connection_read_body( RewriteConnection*  conn, int  fd )
+{
+    ProxyConnection*  root   = conn->root;
+    stralloc_t*       str    = root->str;
+    int               wanted = 0, current, avail;
+    DataStatus        ret;
+
+    if (conn->body_is_closed) {
+        return DATA_NEED_MORE;
+    }
+
+    /* first, determine how many bytes we want to read. */
+    switch (conn->body_mode) {
+    case BODY_NONE:
+        D("%s: INTERNAL ERROR: SHOULDN'T BE THERE", root->name);
+        return DATA_COMPLETED;
+
+    case BODY_KNOWN_LENGTH:
+        {
+            if (conn->body_length == 0)
+                return DATA_COMPLETED;
+
+            if (conn->body_length > MAX_BODY_BUFFER)
+                wanted = MAX_BODY_BUFFER;
+            else
+                wanted = (int)conn->body_length;
+        }
+        break;
+
+    case BODY_UNTIL_CLOSE:
+        wanted = MAX_BODY_BUFFER;
+        break;
+
+    case BODY_CHUNKED:
+        if (conn->chunk_length < 0) {
+            /* chunk_length < 0 means we need to read a chunk header */
+            /* ensure that 'str' is flushed before doing this */
+            if (!conn->parse_chunk_header) {
+                if (conn->body_has_data)
+                    return DATA_NEED_MORE;
+                D("%s: waiting chunk header", root->name);
+                conn->parse_chunk_header = 1;
+            }
+            ret = proxy_connection_receive_line(root, fd);
+            if (ret == DATA_COMPLETED) {
+                char*      line = str->s;
+                char*      end;
+                long long  length;
+
+                length = strtoll(line, &end, 16);
+                if (line[0] == ' ' || (end[0] != '\0' && end[0] != ';')) {
+                    PROXY_LOG("%s: invalid chunk header: %s",
+                              root->name, line);
+                    return DATA_ERROR;
+                }
+                if (length < 0) {
+                    PROXY_LOG("%s: invalid chunk length %lld",
+                              root->name, length);
+                    return DATA_ERROR;
+                }
+                conn->chunk_length = length;
+                conn->chunk_total  = 0;
+                if (length == 0) {
+                    /* the last chunk, no we need to add the trailer */
+                    conn->parse_chunk_trailer = 0;
+                }
+                conn->parse_chunk_header = 0;
+            }
+        }
+
+        if (conn->chunk_length == 0) {
+            /* chunk_length == 0 means we're reading the chunk trailer */
+            /* ensure that 'str' is flushed before reading the trailer */
+            if (!conn->parse_chunk_trailer) {
+                if (conn->body_has_data)
+                    return DATA_NEED_MORE;
+                conn->parse_chunk_trailer = 1;
+            }
+            ret = rewrite_connection_read_headers(conn, fd);
+            if (ret == DATA_COMPLETED) {
+                conn->body_is_closed = 1;
+            }
+            return ret;
+        }
+
+        /* if we get here, body_length > 0 */
+        if (conn->chunk_length > MAX_BODY_BUFFER)
+            wanted = MAX_BODY_BUFFER;
+        else
+            wanted = (int)conn->chunk_length;
+        break;
+
+    default:
+        ;
+    }
+
+    /* we don't want more than MAX_BODY_BUFFER bytes in the
+     * buffer we used to pass the body */
+    current = str->n;
+    avail   = MAX_BODY_BUFFER - current;
+    if (avail <= 0) {
+        /* wait for some flush */
+        conn->body_is_full = 1;
+        D("%s: waiting to flush %d bytes", 
+          root->name, current);
+        return DATA_NEED_MORE;
+    }
+
+    if (wanted > avail)
+        wanted = avail;
+
+    ret = proxy_connection_receive(root, fd, wanted);
+    conn->body_has_data = (str->n > 0);
+    conn->body_is_full  = (str->n == MAX_BODY_BUFFER);
+
+    if (ret == DATA_ERROR) {
+        if (conn->body_mode == BODY_UNTIL_CLOSE) {
+            /* a disconnection here is normal and signals the
+             * end of the body */
+            conn->body_total    += root->str_recv;
+            D("%s: body completed by close (%lld bytes)", 
+                root->name, conn->body_total);
+            conn->body_is_closed = 1;
+            ret = DATA_COMPLETED;
+        }
+    } else {
+        avail = root->str_recv;
+        ret   = DATA_NEED_MORE;  /* we're not really done yet */
+
+        switch (conn->body_mode) {
+        case BODY_CHUNKED:
+            conn->chunk_total  += avail;
+            conn->chunk_length -= avail;
+
+            if (conn->chunk_length == 0) {
+                D("%s: chunk completed (%lld bytes)", 
+                    root->name, conn->chunk_length);
+                conn->body_total  += conn->chunk_total;
+                conn->chunk_total  = 0;
+                conn->chunk_length = -1;
+            }
+            break;
+
+        case BODY_KNOWN_LENGTH:
+            conn->body_length -= avail;
+            conn->body_total  += avail;
+
+            if (conn->body_length == 0) {
+                D("%s: body completed (%lld bytes)", 
+                    root->name, conn->body_total);
+                conn->body_is_closed = 1;
+                ret = DATA_COMPLETED;
+            }
+            break;
+
+        case BODY_UNTIL_CLOSE:
+            conn->body_total += avail;
+            break;
+
+        default:
+            ;
+        }
+    }
+    return ret;
+}
+
+static DataStatus
+rewrite_connection_send_body( RewriteConnection*  conn, int  fd )
+{
+    ProxyConnection*  root   = conn->root;
+    stralloc_t*       str    = root->str;
+    DataStatus        ret    = DATA_NEED_MORE;
+
+    if (conn->body_has_data) {
+        ret = proxy_connection_send(root, fd);
+        if (ret != DATA_ERROR) {
+            int  pos = root->str_pos;
+
+            memmove(str->s, str->s+pos, str->n-pos);
+            str->n         -= pos;
+            root->str_pos   = 0;
+            conn->body_is_full  = (str->n == MAX_BODY_BUFFER);
+            conn->body_has_data = (str->n > 0);
+            conn->body_sent    += root->str_sent;
+
+            /* ensure that we return DATA_COMPLETED only when
+            * we have sent everything, and there is no more
+            * body pieces to read */
+            if (ret == DATA_COMPLETED) {
+                if (!conn->body_is_closed || conn->body_has_data)
+                    ret = DATA_NEED_MORE;
+                else {
+                    D("%s: sent all body (%lld bytes)",
+                        root->name, conn->body_sent);
+                }
+            }
+            D("%s: sent closed=%d data=%d n=%d ret=%d",
+                root->name, conn->body_is_closed,
+                conn->body_has_data, str->n,
+                ret);
+        }
+    }
+    return ret;
+}
+
+
+static void
+rewrite_connection_select( ProxyConnection*  root,
+                           ProxySelect*      sel )
+{
+    RewriteConnection*  conn = (RewriteConnection*)root;
+    int  slirp = conn->slirp_fd;
+    int  proxy = root->socket;
+
+    switch (conn->state) {
+        case STATE_CONNECTING:
+        case STATE_CREATE_SOCKET_PAIR:
+            /* try to connect to the proxy server */
+            proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
+            break;
+
+        case STATE_REQUEST_FIRST_LINE:
+        case STATE_REQUEST_HEADERS:
+            proxy_select_set( sel, slirp, PROXY_SELECT_READ );
+            break;
+
+        case STATE_REQUEST_SEND:
+            proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
+            break;
+
+        case STATE_REQUEST_BODY:
+            if (!conn->body_is_closed && !conn->body_is_full)
+                proxy_select_set( sel, slirp, PROXY_SELECT_READ );
+
+            if (conn->body_has_data)
+                proxy_select_set( sel, proxy, PROXY_SELECT_WRITE );
+            break;
+
+        case STATE_REPLY_FIRST_LINE:
+        case STATE_REPLY_HEADERS:
+            proxy_select_set( sel, proxy, PROXY_SELECT_READ );
+            break;
+
+        case STATE_REPLY_SEND:
+            proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
+            break;
+
+        case STATE_REPLY_BODY:
+            if (conn->body_has_data)
+                proxy_select_set( sel, slirp, PROXY_SELECT_WRITE );
+
+            if (!conn->body_is_closed && !conn->body_is_full)
+                proxy_select_set( sel, proxy, PROXY_SELECT_READ );
+            break;
+        default:
+            ;
+    };
+}
+
+static void
+rewrite_connection_poll( ProxyConnection*  root,
+                         ProxySelect*      sel )
+{
+    RewriteConnection*  conn = (RewriteConnection*)root;
+
+    int         slirp     = conn->slirp_fd;
+    int         proxy     = root->socket;
+    int         has_slirp = proxy_select_poll(sel, slirp);
+    int         has_proxy = proxy_select_poll(sel, proxy);
+    DataStatus  ret       = DATA_NEED_MORE;
+
+    switch (conn->state) {
+        case STATE_CONNECTING:
+            if (has_proxy) {
+                PROXY_LOG("%s: connected to proxy", root->name);
+                conn->state = STATE_CREATE_SOCKET_PAIR;
+            }
+            break;
+
+        case STATE_CREATE_SOCKET_PAIR:
+            if (has_proxy) {
+                if (rewrite_connection_create_sockets(conn) < 0) {
+                    ret = DATA_ERROR;
+                } else {
+                    D("%s: socket pair created", root->name);
+                    conn->state = STATE_REQUEST_FIRST_LINE;
+                }
+            }
+            break;
+
+        case STATE_REQUEST_FIRST_LINE:
+            if (has_slirp) {
+                ret = rewrite_connection_read_request(conn);
+                if (ret == DATA_COMPLETED) {
+                    PROXY_LOG("%s: request first line ok", root->name);
+                    conn->state = STATE_REQUEST_HEADERS;
+                }
+            }
+            break;
+
+        case STATE_REQUEST_HEADERS:
+            if (has_slirp) {
+                ret = rewrite_connection_read_headers(conn, slirp);
+                if (ret == DATA_COMPLETED) {
+                    PROXY_LOG("%s: request headers ok", root->name);
+                    if (rewrite_connection_rewrite_request(conn) < 0)
+                        ret = DATA_ERROR;
+                    else
+                        conn->state = STATE_REQUEST_SEND;
+                }
+            }
+            break;
+
+        case STATE_REQUEST_SEND:
+            if (has_proxy) {
+                ret = proxy_connection_send(root, proxy);
+                if (ret == DATA_COMPLETED) {
+                    if (rewrite_connection_get_body_length(conn, 1) < 0) {
+                        ret = DATA_ERROR;
+                    } else if (conn->body_mode != BODY_NONE) {
+                        PROXY_LOG("%s: request sent, waiting for body",
+                                   root->name);
+                        conn->state = STATE_REQUEST_BODY;
+                    } else {
+                        PROXY_LOG("%s: request sent, waiting for reply",
+                                  root->name);
+                        conn->state = STATE_REPLY_FIRST_LINE;
+                    }
+                }
+            }
+            break;
+
+        case STATE_REQUEST_BODY:
+            if (has_slirp) {
+                ret = rewrite_connection_read_body(conn, slirp);
+            }
+            if (ret != DATA_ERROR && has_proxy) {
+                ret = rewrite_connection_send_body(conn, proxy);
+                if (ret == DATA_COMPLETED) {
+                    PROXY_LOG("%s: request body ok, waiting for reply",
+                              root->name);
+                    conn->state = STATE_REPLY_FIRST_LINE;
+                }
+            }
+            break;
+
+        case STATE_REPLY_FIRST_LINE:
+            if (has_proxy) {
+                ret = rewrite_connection_read_reply(conn);
+                if (ret == DATA_COMPLETED) {
+                    PROXY_LOG("%s: reply first line ok", root->name);
+                    conn->state = STATE_REPLY_HEADERS;
+                }
+            }
+            break;
+
+        case STATE_REPLY_HEADERS:
+            if (has_proxy) {
+                ret = rewrite_connection_read_headers(conn, proxy);
+                if (ret == DATA_COMPLETED) {
+                    PROXY_LOG("%s: reply headers ok", root->name);
+                    if (rewrite_connection_rewrite_reply(conn) < 0)
+                        ret = DATA_ERROR;
+                    else
+                        conn->state = STATE_REPLY_SEND;
+                }
+            }
+            break;
+
+        case STATE_REPLY_SEND:
+            if (has_slirp) {
+                ret = proxy_connection_send(conn->root, slirp);
+                if (ret == DATA_COMPLETED) {
+                    if (rewrite_connection_get_body_length(conn, 0) < 0) {
+                        ret = DATA_ERROR;
+                    } else if (conn->body_mode != BODY_NONE) {
+                        PROXY_LOG("%s: reply sent, waiting for body",
+                                  root->name);
+                        conn->state = STATE_REPLY_BODY;
+                    } else {
+                        PROXY_LOG("%s: reply sent, looping to waiting request",
+                                  root->name);
+                        conn->state = STATE_REQUEST_FIRST_LINE;
+                    }
+                }
+            }
+            break;
+
+        case STATE_REPLY_BODY:
+            if (has_proxy) {
+                ret = rewrite_connection_read_body(conn, proxy);
+            }
+            if (ret != DATA_ERROR && has_slirp) {
+                ret = rewrite_connection_send_body(conn, slirp);
+                if (ret == DATA_COMPLETED) {
+                    if (conn->body_mode == BODY_UNTIL_CLOSE) {
+                        PROXY_LOG("%s: closing connection", root->name);
+                        ret = DATA_ERROR;
+                    } else {
+                        PROXY_LOG("%s: reply body ok, looping to waiting request",
+                                root->name);
+                        conn->state = STATE_REQUEST_FIRST_LINE;
+                    }
+                }
+            }
+            break;
+
+        default:
+            ;
+    }
+    if (ret == DATA_ERROR)
+        proxy_connection_free(root, 0, PROXY_EVENT_NONE);
+
+    return;
+}
+
+
+ProxyConnection*
+http_rewriter_connect( HttpService*  service,
+                       SockAddress*  address )
+{
+    RewriteConnection*  conn;
+    int                 s;
+
+    s = socket_create_inet( SOCKET_STREAM );
+    if (s < 0)
+        return NULL;
+
+    conn = qemu_mallocz(sizeof(*conn));
+    if (conn == NULL) {
+        socket_close(s);
+        return NULL;
+    }
+
+    proxy_connection_init( conn->root, s, address, service->root,
+                           rewrite_connection_free,
+                           rewrite_connection_select,
+                           rewrite_connection_poll );
+
+    if ( rewrite_connection_init( conn ) < 0 ) {
+        rewrite_connection_free( conn->root );
+        return NULL;
+    }
+
+    return conn->root;
+}
diff --git a/proxy/proxy_int.h b/proxy/proxy_int.h
new file mode 100644
index 0000000..739bb75
--- /dev/null
+++ b/proxy/proxy_int.h
@@ -0,0 +1,201 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _PROXY_INT_H
+#define _PROXY_INT_H
+
+#include "proxy_common.h"
+#include "sockets.h"
+#include "android/utils/stralloc.h"
+
+extern int  proxy_log;
+
+extern void
+proxy_LOG(const char*  fmt, ...);
+
+#define  PROXY_LOG(...)   \
+    do { if (proxy_log) proxy_LOG(__VA_ARGS__); } while (0)
+
+
+/* ProxySelect is used to handle events */
+
+enum {
+    PROXY_SELECT_READ  = (1 << 0),
+    PROXY_SELECT_WRITE = (1 << 1),
+    PROXY_SELECT_ERROR = (1 << 2)
+};
+
+typedef struct {
+    int*     pcount;
+    fd_set*  reads;
+    fd_set*  writes;
+    fd_set*  errors;
+} ProxySelect;
+
+extern void     proxy_select_set( ProxySelect*  sel,
+                                  int           fd,
+                                  unsigned      flags );
+
+extern unsigned  proxy_select_poll( ProxySelect*  sel, int  fd );
+
+
+/* sockets proxy manager internals */
+
+typedef struct ProxyConnection   ProxyConnection;
+typedef struct ProxyService      ProxyService;
+
+/* free a given proxified connection */
+typedef void              (*ProxyConnectionFreeFunc)   ( ProxyConnection*  conn );
+
+/* modify the ProxySelect to tell which events to listen to */
+typedef void              (*ProxyConnectionSelectFunc) ( ProxyConnection*  conn,
+                                                         ProxySelect*      sel );
+
+/* action a proxy connection when select() returns certain events for its socket */
+typedef void              (*ProxyConnectionPollFunc)   ( ProxyConnection*  conn,
+                                                         ProxySelect*      sel );
+
+
+/* root ProxyConnection object */
+struct ProxyConnection {
+    int                 socket;
+    SockAddress         address;  /* for debugging */
+    ProxyConnection*    next;
+    ProxyConnection*    prev;
+    ProxyEventFunc      ev_func;
+    void*               ev_opaque;
+    ProxyService*       service;
+
+    /* the following is useful for all types of services */
+    char                name[64];    /* for debugging purposes */
+
+    stralloc_t          str[1];      /* network buffer (dynamic) */
+    int                 str_pos;     /* see proxy_connection_send() */
+    int                 str_sent;    /* see proxy_connection_send() */
+    int                 str_recv;    /* see proxy_connection_receive() */
+
+    /* connection methods */
+    ProxyConnectionFreeFunc    conn_free;
+    ProxyConnectionSelectFunc  conn_select;
+    ProxyConnectionPollFunc    conn_poll;
+
+    /* rest of data depend on exact implementation */
+};
+
+
+
+extern void
+proxy_connection_init( ProxyConnection*           conn,
+                       int                        socket,
+                       SockAddress*               address,
+                       ProxyService*              service,
+                       ProxyConnectionFreeFunc    conn_free,
+                       ProxyConnectionSelectFunc  conn_select,
+                       ProxyConnectionPollFunc    conn_poll );
+
+extern void
+proxy_connection_done( ProxyConnection*  conn );
+
+/* free the proxy connection object. this will also
+ * close the corresponding socket unless the 
+ * 'keep_alive' flag is set to TRUE.
+ */
+extern void
+proxy_connection_free( ProxyConnection*  conn,
+                       int               keep_alive,
+                       ProxyEvent        event );
+
+/* status of data transfer operations */
+typedef enum {
+    DATA_ERROR     = -1,
+    DATA_NEED_MORE =  0,
+    DATA_COMPLETED =  1
+} DataStatus;
+
+/* try to send data from the connection's buffer to a socket.
+ * starting from offset conn->str_pos in the buffer
+ *
+ * returns DATA_COMPLETED if everything could be written
+ * returns DATA_ERROR for a socket disconnection or error
+ * returns DATA_NEED_MORE if all data could not be sent.
+ *
+ * on exit, conn->str_sent contains the number of bytes
+ * that were really sent. conn->str_pos will be incremented
+ * by conn->str_sent as well.
+ *
+ * note that in case of success (DATA_COMPLETED), this also
+ * performs a proxy_connection_rewind which sets conn->str_pos
+ * to 0.
+ */
+extern DataStatus
+proxy_connection_send( ProxyConnection*  conn, int  fd );
+
+/* try to read 'wanted' bytes into conn->str from a socket
+ *
+ * returns DATA_COMPLETED if all bytes could be read
+ * returns DATA_NEED_MORE if not all bytes could be read
+ * returns DATA_ERROR in case of socket disconnection or error
+ *
+ * on exit, the amount of data received is in conn->str_recv
+ */
+extern DataStatus
+proxy_connection_receive( ProxyConnection*  conn, int  fd, int  wanted );
+
+/* tries to receive a line of text from the proxy.
+ * when an entire line is read, the trailing \r\n is stripped
+ * and replaced by a terminating zero. str->n will be the
+ * lenght of the line, exclusing the terminating zero.
+ * returns 1 when a line has been received
+ * returns 0 if there is still some data to receive
+ * returns -1 in case of error
+ */
+extern DataStatus
+proxy_connection_receive_line( ProxyConnection*  conn, int  fd );
+
+/* rewind the string buffer for a new operation */
+extern void
+proxy_connection_rewind( ProxyConnection*  conn );
+
+/* base64 encode a source string, returns size of encoded result,
+ * or -1 if there was not enough room in the destination buffer
+ */
+extern int
+proxy_base64_encode( const char*  src, int  srclen,
+                     char*        dst, int  dstlen );
+
+extern int
+proxy_resolve_server( SockAddress*   addr,
+                      const char*    servername,
+                      int            servernamelen,
+                      int            serverport );
+
+/* a ProxyService is really a proxy server and associated options */
+
+/* destroy a given proxy service */
+typedef void              (*ProxyServiceFreeFunc)      ( void*  opaque );
+
+/* tries to create a new proxified connection, returns NULL if the service can't
+ * handle this address */
+typedef ProxyConnection*  (*ProxyServiceConnectFunc)( void*               opaque,
+                                                      SocketType          socket_type,
+                                                      const SockAddress*  address );
+
+struct ProxyService {
+    void*                      opaque;
+    ProxyServiceFreeFunc       serv_free;
+    ProxyServiceConnectFunc    serv_connect;
+};
+
+extern int
+proxy_manager_add_service( ProxyService*  service );
+
+
+#endif /* _PROXY_INT_H */
diff --git a/qemu-char.h b/qemu-char.h
new file mode 100644
index 0000000..439e2c8
--- /dev/null
+++ b/qemu-char.h
@@ -0,0 +1,90 @@
+#ifndef QEMU_CHAR_H
+#define QEMU_CHAR_H
+
+#include "qemu-common.h"
+
+/* character device */
+
+#define CHR_EVENT_BREAK 0 /* serial break char */
+#define CHR_EVENT_FOCUS 1 /* focus to this terminal (modal input needed) */
+#define CHR_EVENT_RESET 2 /* new connection established */
+
+
+#define CHR_IOCTL_SERIAL_SET_PARAMS   1
+typedef struct {
+    int speed;
+    int parity;
+    int data_bits;
+    int stop_bits;
+} QEMUSerialSetParams;
+
+#define CHR_IOCTL_SERIAL_SET_BREAK    2
+
+#define CHR_IOCTL_PP_READ_DATA        3
+#define CHR_IOCTL_PP_WRITE_DATA       4
+#define CHR_IOCTL_PP_READ_CONTROL     5
+#define CHR_IOCTL_PP_WRITE_CONTROL    6
+#define CHR_IOCTL_PP_READ_STATUS      7
+#define CHR_IOCTL_PP_EPP_READ_ADDR    8
+#define CHR_IOCTL_PP_EPP_READ         9
+#define CHR_IOCTL_PP_EPP_WRITE_ADDR  10
+#define CHR_IOCTL_PP_EPP_WRITE       11
+#define CHR_IOCTL_PP_DATA_DIR        12
+
+#define CHR_IOCTL_SERIAL_SET_TIOCM   13
+#define CHR_IOCTL_SERIAL_GET_TIOCM   14
+
+#define CHR_TIOCM_CTS	0x020
+#define CHR_TIOCM_CAR	0x040
+#define CHR_TIOCM_DSR	0x100
+#define CHR_TIOCM_RI	0x080
+#define CHR_TIOCM_DTR	0x002
+#define CHR_TIOCM_RTS	0x004
+
+typedef void IOEventHandler(void *opaque, int event);
+
+struct CharDriverState {
+    int (*chr_write)(struct CharDriverState *s, const uint8_t *buf, int len);
+    void (*chr_update_read_handler)(struct CharDriverState *s);
+    int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg);
+    IOEventHandler *chr_event;
+    IOCanRWHandler *chr_can_read;
+    IOReadHandler *chr_read;
+    void *handler_opaque;
+    void (*chr_send_event)(struct CharDriverState *chr, int event);
+    void (*chr_close)(struct CharDriverState *chr);
+    void (*chr_accept_input)(struct CharDriverState *chr);
+    void *opaque;
+    int focus;
+    QEMUBH *bh;
+};
+
+CharDriverState *qemu_chr_open(const char *filename);
+void qemu_chr_close(CharDriverState *chr);
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...);
+int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len);
+void qemu_chr_send_event(CharDriverState *s, int event);
+void qemu_chr_add_handlers(CharDriverState *s,
+                           IOCanRWHandler *fd_can_read,
+                           IOReadHandler *fd_read,
+                           IOEventHandler *fd_event,
+                           void *opaque);
+int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg);
+void qemu_chr_reset(CharDriverState *s);
+int qemu_chr_can_read(CharDriverState *s);
+void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len);
+void qemu_chr_accept_input(CharDriverState *s);
+
+/* async I/O support */
+
+int qemu_set_fd_handler2(int fd,
+                         IOCanRWHandler *fd_read_poll,
+                         IOHandler *fd_read,
+                         IOHandler *fd_write,
+                         void *opaque);
+int qemu_set_fd_handler(int fd,
+                        IOHandler *fd_read,
+                        IOHandler *fd_write,
+                        void *opaque);
+
+#endif
diff --git a/qemu-common.h b/qemu-common.h
new file mode 100644
index 0000000..391717c
--- /dev/null
+++ b/qemu-common.h
@@ -0,0 +1,142 @@
+/* Common header file that is included by all of qemu.  */
+#ifndef QEMU_COMMON_H
+#define QEMU_COMMON_H
+
+/* we put basic includes here to avoid repeating them in device drivers */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <time.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#ifndef O_LARGEFILE
+#define O_LARGEFILE 0
+#endif
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef ENOMEDIUM
+#define ENOMEDIUM ENODEV
+#endif
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#define fsync _commit
+#define lseek _lseeki64
+#define ENOTSUP 4096
+extern int qemu_ftruncate64(int, int64_t);
+#define ftruncate qemu_ftruncate64
+
+
+static inline char *realpath(const char *path, char *resolved_path)
+{
+    _fullpath(resolved_path, path, _MAX_PATH);
+    return resolved_path;
+}
+
+#define PRId64 "I64d"
+#define PRIx64 "I64x"
+#define PRIu64 "I64u"
+#define PRIo64 "I64o"
+#endif
+
+/* FIXME: Remove NEED_CPU_H.  */
+#ifndef NEED_CPU_H
+
+#include "config-host.h"
+#include <setjmp.h>
+#include "osdep.h"
+#include "bswap.h"
+
+#else
+
+#include "cpu.h"
+
+#endif /* !defined(NEED_CPU_H) */
+
+/* bottom halves */
+typedef struct QEMUBH QEMUBH;
+
+typedef void QEMUBHFunc(void *opaque);
+
+QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque);
+void qemu_bh_schedule(QEMUBH *bh);
+void qemu_bh_cancel(QEMUBH *bh);
+void qemu_bh_delete(QEMUBH *bh);
+int qemu_bh_poll(void);
+
+uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c);
+
+void qemu_get_timedate(struct tm *tm, int offset);
+int qemu_timedate_diff(struct tm *tm);
+
+/* cutils.c */
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+int strstart(const char *str, const char *val, const char **ptr);
+int stristart(const char *str, const char *val, const char **ptr);
+time_t mktimegm(struct tm *tm);
+
+void *qemu_malloc(size_t size);
+void *qemu_realloc(void *ptr, size_t size);
+void *qemu_mallocz(size_t size);
+void qemu_free(void *ptr);
+char *qemu_strdup(const char *str);
+
+void *get_mmap_addr(unsigned long size);
+
+
+/* Error handling.  */
+
+void hw_error(const char *fmt, ...)
+    __attribute__ ((__format__ (__printf__, 1, 2)))
+    __attribute__ ((__noreturn__));
+
+/* IO callbacks.  */
+typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size);
+typedef int IOCanRWHandler(void *opaque);
+typedef void IOHandler(void *opaque);
+
+struct ParallelIOArg {
+    void *buffer;
+    int count;
+};
+
+typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size);
+
+/* A load of opaque types so that device init declarations don't have to
+   pull in all the real definitions.  */
+typedef struct NICInfo NICInfo;
+typedef struct AudioState AudioState;
+typedef struct BlockDriverState BlockDriverState;
+typedef struct DisplayState DisplayState;
+typedef struct TextConsole TextConsole;
+typedef TextConsole QEMUConsole;
+typedef struct CharDriverState CharDriverState;
+typedef struct VLANState VLANState;
+typedef struct QEMUFile QEMUFile;
+typedef struct i2c_bus i2c_bus;
+typedef struct i2c_slave i2c_slave;
+typedef struct SMBusDevice SMBusDevice;
+typedef struct QEMUTimer QEMUTimer;
+typedef struct PCIBus PCIBus;
+typedef struct PCIDevice PCIDevice;
+typedef struct SerialState SerialState;
+typedef struct IRQState *qemu_irq;
+struct pcmcia_card_s;
+
+/* CPU save/load.  */
+void cpu_save(QEMUFile *f, void *opaque);
+int cpu_load(QEMUFile *f, void *opaque, int version_id);
+
+#endif
diff --git a/qemu-lock.h b/qemu-lock.h
new file mode 100644
index 0000000..fdd8da9
--- /dev/null
+++ b/qemu-lock.h
@@ -0,0 +1,249 @@
+/*
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Locking primitives.  Most of this code should be redundant -
+   system emulation doesn't need/use locking, NPTL userspace uses
+   pthread mutexes, and non-NPTL userspace isn't threadsafe anyway.
+   In either case a spinlock is probably the wrong kind of lock.
+   Spinlocks are only good if you know annother CPU has the lock and is
+   likely to release it soon.  In environments where you have more threads
+   than physical CPUs (the extreme case being a single CPU host) a spinlock
+   simply wastes CPU until the OS decides to preempt it.  */
+#if defined(USE_NPTL)
+
+#include <pthread.h>
+#define spin_lock pthread_mutex_lock
+#define spin_unlock pthread_mutex_unlock
+#define spinlock_t pthread_mutex_t
+#define SPIN_LOCK_UNLOCKED PTHREAD_MUTEX_INITIALIZER
+
+#else
+
+#if defined(__hppa__)
+
+typedef int spinlock_t[4];
+
+#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 }
+
+static inline void resetlock (spinlock_t *p)
+{
+    (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1;
+}
+
+#else
+
+typedef int spinlock_t;
+
+#define SPIN_LOCK_UNLOCKED 0
+
+static inline void resetlock (spinlock_t *p)
+{
+    *p = SPIN_LOCK_UNLOCKED;
+}
+
+#endif
+
+#if defined(__powerpc__)
+static inline int testandset (int *p)
+{
+    int ret;
+    __asm__ __volatile__ (
+                          "0:    lwarx %0,0,%1\n"
+                          "      xor. %0,%3,%0\n"
+                          "      bne 1f\n"
+                          "      stwcx. %2,0,%1\n"
+                          "      bne- 0b\n"
+                          "1:    "
+                          : "=&r" (ret)
+                          : "r" (p), "r" (1), "r" (0)
+                          : "cr0", "memory");
+    return ret;
+}
+#elif defined(__i386__)
+static inline int testandset (int *p)
+{
+    long int readval = 0;
+
+    __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+                          : "+m" (*p), "+a" (readval)
+                          : "r" (1)
+                          : "cc");
+    return readval;
+}
+#elif defined(__x86_64__)
+static inline int testandset (int *p)
+{
+    long int readval = 0;
+
+    __asm__ __volatile__ ("lock; cmpxchgl %2, %0"
+                          : "+m" (*p), "+a" (readval)
+                          : "r" (1)
+                          : "cc");
+    return readval;
+}
+#elif defined(__s390__)
+static inline int testandset (int *p)
+{
+    int ret;
+
+    __asm__ __volatile__ ("0: cs    %0,%1,0(%2)\n"
+			  "   jl    0b"
+			  : "=&d" (ret)
+			  : "r" (1), "a" (p), "0" (*p)
+			  : "cc", "memory" );
+    return ret;
+}
+#elif defined(__alpha__)
+static inline int testandset (int *p)
+{
+    int ret;
+    unsigned long one;
+
+    __asm__ __volatile__ ("0:	mov 1,%2\n"
+			  "	ldl_l %0,%1\n"
+			  "	stl_c %2,%1\n"
+			  "	beq %2,1f\n"
+			  ".subsection 2\n"
+			  "1:	br 0b\n"
+			  ".previous"
+			  : "=r" (ret), "=m" (*p), "=r" (one)
+			  : "m" (*p));
+    return ret;
+}
+#elif defined(__sparc__)
+static inline int testandset (int *p)
+{
+	int ret;
+
+	__asm__ __volatile__("ldstub	[%1], %0"
+			     : "=r" (ret)
+			     : "r" (p)
+			     : "memory");
+
+	return (ret ? 1 : 0);
+}
+#elif defined(__arm__)
+static inline int testandset (int *spinlock)
+{
+    register unsigned int ret;
+    __asm__ __volatile__("swp %0, %1, [%2]"
+                         : "=r"(ret)
+                         : "0"(1), "r"(spinlock));
+
+    return ret;
+}
+#elif defined(__mc68000)
+static inline int testandset (int *p)
+{
+    char ret;
+    __asm__ __volatile__("tas %1; sne %0"
+                         : "=r" (ret)
+                         : "m" (p)
+                         : "cc","memory");
+    return ret;
+}
+#elif defined(__hppa__)
+
+/* Because malloc only guarantees 8-byte alignment for malloc'd data,
+   and GCC only guarantees 8-byte alignment for stack locals, we can't
+   be assured of 16-byte alignment for atomic lock data even if we
+   specify "__attribute ((aligned(16)))" in the type declaration.  So,
+   we use a struct containing an array of four ints for the atomic lock
+   type and dynamically select the 16-byte aligned int from the array
+   for the semaphore.  */
+#define __PA_LDCW_ALIGNMENT 16
+static inline void *ldcw_align (void *p) {
+    unsigned long a = (unsigned long)p;
+    a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1);
+    return (void *)a;
+}
+
+static inline int testandset (spinlock_t *p)
+{
+    unsigned int ret;
+    p = ldcw_align(p);
+    __asm__ __volatile__("ldcw 0(%1),%0"
+                         : "=r" (ret)
+                         : "r" (p)
+                         : "memory" );
+    return !ret;
+}
+
+#elif defined(__ia64)
+
+#include <ia64intrin.h>
+
+static inline int testandset (int *p)
+{
+    return __sync_lock_test_and_set (p, 1);
+}
+#elif defined(__mips__)
+static inline int testandset (int *p)
+{
+    int ret;
+
+    __asm__ __volatile__ (
+	"	.set push		\n"
+	"	.set noat		\n"
+	"	.set mips2		\n"
+	"1:	li	$1, 1		\n"
+	"	ll	%0, %1		\n"
+	"	sc	$1, %1		\n"
+	"	beqz	$1, 1b		\n"
+	"	.set pop		"
+	: "=r" (ret), "+R" (*p)
+	:
+	: "memory");
+
+    return ret;
+}
+#else
+#error unimplemented CPU support
+#endif
+
+#if defined(CONFIG_USER_ONLY)
+static inline void spin_lock(spinlock_t *lock)
+{
+    while (testandset(lock));
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+    resetlock(lock);
+}
+
+static inline int spin_trylock(spinlock_t *lock)
+{
+    return !testandset(lock);
+}
+#else
+static inline void spin_lock(spinlock_t *lock)
+{
+}
+
+static inline void spin_unlock(spinlock_t *lock)
+{
+}
+
+static inline int spin_trylock(spinlock_t *lock)
+{
+    return 1;
+}
+#endif
+
+#endif
diff --git a/qemu-log.h b/qemu-log.h
new file mode 100644
index 0000000..1ebea81
--- /dev/null
+++ b/qemu-log.h
@@ -0,0 +1,7 @@
+#ifndef QEMU_LOG_H
+#define QEMU_LOG_H
+
+extern FILE *logfile;
+extern int loglevel;
+
+#endif
diff --git a/qemu-timer.h b/qemu-timer.h
new file mode 100644
index 0000000..8264f3e
--- /dev/null
+++ b/qemu-timer.h
@@ -0,0 +1,50 @@
+#ifndef QEMU_TIMER_H
+#define QEMU_TIMER_H
+
+#include "qemu-common.h"
+
+/* timers */
+
+typedef struct QEMUClock QEMUClock;
+typedef void QEMUTimerCB(void *opaque);
+
+/* The real time clock should be used only for stuff which does not
+   change the virtual machine state, as it is run even if the virtual
+   machine is stopped. The real time clock has a frequency of 1000
+   Hz. */
+extern QEMUClock *rt_clock;
+
+/* The virtual clock is only run during the emulation. It is stopped
+   when the virtual machine is stopped. Virtual timers use a high
+   precision clock, usually cpu cycles (use ticks_per_sec). */
+extern QEMUClock *vm_clock;
+
+int64_t qemu_get_clock(QEMUClock *clock);
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque);
+void qemu_free_timer(QEMUTimer *ts);
+void qemu_del_timer(QEMUTimer *ts);
+void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time);
+int qemu_timer_pending(QEMUTimer *ts);
+
+extern int64_t ticks_per_sec;
+
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts);
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts);
+
+/* ptimer.c */
+typedef struct ptimer_state ptimer_state;
+typedef void (*ptimer_cb)(void *opaque);
+
+ptimer_state *ptimer_init(QEMUBH *bh);
+void ptimer_set_period(ptimer_state *s, int64_t period);
+void ptimer_set_freq(ptimer_state *s, uint32_t freq);
+void ptimer_set_limit(ptimer_state *s, uint64_t limit, int reload);
+uint64_t ptimer_get_count(ptimer_state *s);
+void ptimer_set_count(ptimer_state *s, uint64_t count);
+void ptimer_run(ptimer_state *s, int oneshot);
+void ptimer_stop(ptimer_state *s);
+void qemu_put_ptimer(QEMUFile *f, ptimer_state *s);
+void qemu_get_ptimer(QEMUFile *f, ptimer_state *s);
+
+#endif
diff --git a/qemu_debug.h b/qemu_debug.h
new file mode 100644
index 0000000..809a157
--- /dev/null
+++ b/qemu_debug.h
@@ -0,0 +1,5 @@
+/* this is a transitional header. It should only be included
+ * by non-specific source files. Later, a non-Android specific
+ * implementation will be hooked in
+ */
+#include "android/utils/debug.h"
diff --git a/qemu_file.h b/qemu_file.h
new file mode 100644
index 0000000..682e092
--- /dev/null
+++ b/qemu_file.h
@@ -0,0 +1,52 @@
+#ifndef _QEMU_FILE_H
+#define _QEMU_FILE_H
+
+#include "hw/hw.h"
+
+typedef enum {
+    Q_FIELD_END,          /* mark end of field list */
+    Q_FIELD_BYTE,         /* for 1-byte fields */
+    Q_FIELD_INT16,        /* for 2-byte fields */
+    Q_FIELD_INT32,        /* for 4-byte fields */
+    Q_FIELD_INT64,        /* for 8-byte fields */
+    Q_FIELD_BUFFER,       /* for buffer fields */
+    Q_FIELD_BUFFER_SIZE,  /* to specify the size of buffers */
+
+#if TARGET_LONG_BITS == 64
+    Q_FIELD_TL = Q_FIELD_INT64,           /* target long, either 4 or 8 bytes */
+#else
+    Q_FIELD_TL = Q_FIELD_INT32
+#endif
+
+} QFieldType;
+
+typedef struct {
+    QFieldType  type : 16; /* field type */
+    uint16_t    offset;    /* offset of field in structure */
+} QField;
+
+#define  QFIELD_BEGIN(name)  \
+    static const QField    name[] = {
+
+#define  _QFIELD_(t, f)    { t, offsetof(QFIELD_STRUCT,f) }
+#define  QFIELD_BYTE(f)   _QFIELD_(Q_FIELD_BYTE, f)
+#define  QFIELD_INT16(f)  _QFIELD_(Q_FIELD_INT16, f)
+#define  QFIELD_INT32(f)  _QFIELD_(Q_FIELD_INT32, f)
+#define  QFIELD_INT64(f)  _QFIELD_(Q_FIELD_INT64, f)
+#define  QFIELD_TL(f)     _QFIELD_(Q_FIELD_TL, f)
+
+#define  _QFIELD_SIZEOF(f)   sizeof(((QFIELD_STRUCT*)0)->f)
+
+#define  QFIELD_BUFFER(f)  \
+    _QFIELD_(Q_FIELD_BUFFER, f), \
+    { Q_FIELD_BUFFER_SIZE, (uint16_t)(_QFIELD_SIZEOF(f) >> 16) }, \
+    { Q_FIELD_BUFFER_SIZE, (uint16_t) _QFIELD_SIZEOF(f) }
+
+#define  QFIELD_END           \
+        { Q_FIELD_END, 0 },   \
+    };
+
+extern void  qemu_put_struct(QEMUFile*  f, const QField*  fields, const void*  s);
+extern int   qemu_get_struct(QEMUFile*  f, const QField*  fields, void*  s);
+
+#endif /* _QEMU_FILE_H */
diff --git a/qemu_socket.h b/qemu_socket.h
new file mode 100644
index 0000000..896a0b5
--- /dev/null
+++ b/qemu_socket.h
@@ -0,0 +1,8 @@
+#ifndef _qemu_socket_h
+#define _qemu_socket_h
+
+#include "sockets.h"
+#define  socket_error()  socket_errno
+#define  closesocket     socket_close
+
+#endif /* _qemu_socket_h */
diff --git a/qemu_timers.h b/qemu_timers.h
new file mode 100644
index 0000000..9642301
--- /dev/null
+++ b/qemu_timers.h
@@ -0,0 +1,7 @@
+#ifndef _QEMU_TIMERS_H
+#define _QEMU_TIMERS_H
+
+#include "qemu_file.h"
+#include "qemu-timer.h"
+
+#endif /* _QEMU_TIMERS_H */
diff --git a/readline.c b/readline.c
new file mode 100644
index 0000000..81bc491
--- /dev/null
+++ b/readline.c
@@ -0,0 +1,488 @@
+/*
+ * QEMU readline utility
+ *
+ * Copyright (c) 2003-2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu-common.h"
+#include "console.h"
+
+#define TERM_CMD_BUF_SIZE 4095
+#define TERM_MAX_CMDS 64
+#define NB_COMPLETIONS_MAX 256
+
+#define IS_NORM 0
+#define IS_ESC  1
+#define IS_CSI  2
+
+#define printf do_not_use_printf
+
+static char term_cmd_buf[TERM_CMD_BUF_SIZE + 1];
+static int term_cmd_buf_index;
+static int term_cmd_buf_size;
+
+static char term_last_cmd_buf[TERM_CMD_BUF_SIZE + 1];
+static int term_last_cmd_buf_index;
+static int term_last_cmd_buf_size;
+
+static int term_esc_state;
+static int term_esc_param;
+
+static char *term_history[TERM_MAX_CMDS];
+static int term_hist_entry = -1;
+
+static int nb_completions;
+int completion_index;
+static char *completions[NB_COMPLETIONS_MAX];
+
+static ReadLineFunc *term_readline_func;
+static int term_is_password;
+static char term_prompt[256];
+static void *term_readline_opaque;
+
+static void term_show_prompt2(void)
+{
+    term_printf("%s", term_prompt);
+    term_flush();
+    term_last_cmd_buf_index = 0;
+    term_last_cmd_buf_size = 0;
+    term_esc_state = IS_NORM;
+}
+
+static void term_show_prompt(void)
+{
+    term_show_prompt2();
+    term_cmd_buf_index = 0;
+    term_cmd_buf_size = 0;
+}
+
+/* update the displayed command line */
+static void term_update(void)
+{
+    int i, delta, len;
+
+    if (term_cmd_buf_size != term_last_cmd_buf_size ||
+        memcmp(term_cmd_buf, term_last_cmd_buf, term_cmd_buf_size) != 0) {
+        for(i = 0; i < term_last_cmd_buf_index; i++) {
+            term_printf("\033[D");
+        }
+        term_cmd_buf[term_cmd_buf_size] = '\0';
+        if (term_is_password) {
+            len = strlen(term_cmd_buf);
+            for(i = 0; i < len; i++)
+                term_printf("*");
+        } else {
+            term_printf("%s", term_cmd_buf);
+        }
+        term_printf("\033[K");
+        memcpy(term_last_cmd_buf, term_cmd_buf, term_cmd_buf_size);
+        term_last_cmd_buf_size = term_cmd_buf_size;
+        term_last_cmd_buf_index = term_cmd_buf_size;
+    }
+    if (term_cmd_buf_index != term_last_cmd_buf_index) {
+        delta = term_cmd_buf_index - term_last_cmd_buf_index;
+        if (delta > 0) {
+            for(i = 0;i < delta; i++) {
+                term_printf("\033[C");
+            }
+        } else {
+            delta = -delta;
+            for(i = 0;i < delta; i++) {
+                term_printf("\033[D");
+            }
+        }
+        term_last_cmd_buf_index = term_cmd_buf_index;
+    }
+    term_flush();
+}
+
+static void term_insert_char(int ch)
+{
+    if (term_cmd_buf_index < TERM_CMD_BUF_SIZE) {
+        memmove(term_cmd_buf + term_cmd_buf_index + 1,
+                term_cmd_buf + term_cmd_buf_index,
+                term_cmd_buf_size - term_cmd_buf_index);
+        term_cmd_buf[term_cmd_buf_index] = ch;
+        term_cmd_buf_size++;
+        term_cmd_buf_index++;
+    }
+}
+
+static void term_backward_char(void)
+{
+    if (term_cmd_buf_index > 0) {
+        term_cmd_buf_index--;
+    }
+}
+
+static void term_forward_char(void)
+{
+    if (term_cmd_buf_index < term_cmd_buf_size) {
+        term_cmd_buf_index++;
+    }
+}
+
+static void term_delete_char(void)
+{
+    if (term_cmd_buf_index < term_cmd_buf_size) {
+        memmove(term_cmd_buf + term_cmd_buf_index,
+                term_cmd_buf + term_cmd_buf_index + 1,
+                term_cmd_buf_size - term_cmd_buf_index - 1);
+        term_cmd_buf_size--;
+    }
+}
+
+static void term_backspace(void)
+{
+    if (term_cmd_buf_index > 0) {
+        term_backward_char();
+        term_delete_char();
+    }
+}
+
+static void term_backword(void)
+{
+    int start;
+
+    if (term_cmd_buf_index == 0 || term_cmd_buf_index > term_cmd_buf_size) {
+        return;
+    }
+
+    start = term_cmd_buf_index - 1;
+
+    /* find first word (backwards) */
+    while (start > 0) {
+        if (!isspace(term_cmd_buf[start])) {
+            break;
+        }
+
+        --start;
+    }
+
+    /* find first space (backwards) */
+    while (start > 0) {
+        if (isspace(term_cmd_buf[start])) {
+            ++start;
+            break;
+        }
+
+        --start;
+    }
+
+    /* remove word */
+    if (start < term_cmd_buf_index) {
+        memmove(term_cmd_buf + start,
+                term_cmd_buf + term_cmd_buf_index,
+                term_cmd_buf_size - term_cmd_buf_index);
+        term_cmd_buf_size -= term_cmd_buf_index - start;
+        term_cmd_buf_index = start;
+    }
+}
+
+static void term_bol(void)
+{
+    term_cmd_buf_index = 0;
+}
+
+static void term_eol(void)
+{
+    term_cmd_buf_index = term_cmd_buf_size;
+}
+
+static void term_up_char(void)
+{
+    int idx;
+
+    if (term_hist_entry == 0)
+	return;
+    if (term_hist_entry == -1) {
+	/* Find latest entry */
+	for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
+	    if (term_history[idx] == NULL)
+		break;
+	}
+	term_hist_entry = idx;
+    }
+    term_hist_entry--;
+    if (term_hist_entry >= 0) {
+	pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
+                term_history[term_hist_entry]);
+	term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
+    }
+}
+
+static void term_down_char(void)
+{
+    if (term_hist_entry == TERM_MAX_CMDS - 1 || term_hist_entry == -1)
+	return;
+    if (term_history[++term_hist_entry] != NULL) {
+	pstrcpy(term_cmd_buf, sizeof(term_cmd_buf),
+                term_history[term_hist_entry]);
+    } else {
+	term_hist_entry = -1;
+    }
+    term_cmd_buf_index = term_cmd_buf_size = strlen(term_cmd_buf);
+}
+
+static void term_hist_add(const char *cmdline)
+{
+    char *hist_entry, *new_entry;
+    int idx;
+
+    if (cmdline[0] == '\0')
+	return;
+    new_entry = NULL;
+    if (term_hist_entry != -1) {
+	/* We were editing an existing history entry: replace it */
+	hist_entry = term_history[term_hist_entry];
+	idx = term_hist_entry;
+	if (strcmp(hist_entry, cmdline) == 0) {
+	    goto same_entry;
+	}
+    }
+    /* Search cmdline in history buffers */
+    for (idx = 0; idx < TERM_MAX_CMDS; idx++) {
+	hist_entry = term_history[idx];
+	if (hist_entry == NULL)
+	    break;
+	if (strcmp(hist_entry, cmdline) == 0) {
+	same_entry:
+	    new_entry = hist_entry;
+	    /* Put this entry at the end of history */
+	    memmove(&term_history[idx], &term_history[idx + 1],
+		    (TERM_MAX_CMDS - idx + 1) * sizeof(char *));
+	    term_history[TERM_MAX_CMDS - 1] = NULL;
+	    for (; idx < TERM_MAX_CMDS; idx++) {
+		if (term_history[idx] == NULL)
+		    break;
+	    }
+	    break;
+	}
+    }
+    if (idx == TERM_MAX_CMDS) {
+	/* Need to get one free slot */
+	free(term_history[0]);
+	memcpy(term_history, &term_history[1],
+	       (TERM_MAX_CMDS - 1) * sizeof(char *));
+	term_history[TERM_MAX_CMDS - 1] = NULL;
+	idx = TERM_MAX_CMDS - 1;
+    }
+    if (new_entry == NULL)
+	new_entry = strdup(cmdline);
+    term_history[idx] = new_entry;
+    term_hist_entry = -1;
+}
+
+/* completion support */
+
+void add_completion(const char *str)
+{
+    if (nb_completions < NB_COMPLETIONS_MAX) {
+        completions[nb_completions++] = qemu_strdup(str);
+    }
+}
+
+static void term_completion(void)
+{
+    int len, i, j, max_width, nb_cols, max_prefix;
+    char *cmdline;
+
+    nb_completions = 0;
+
+    cmdline = qemu_malloc(term_cmd_buf_index + 1);
+    if (!cmdline)
+        return;
+    memcpy(cmdline, term_cmd_buf, term_cmd_buf_index);
+    cmdline[term_cmd_buf_index] = '\0';
+    readline_find_completion(cmdline);
+    qemu_free(cmdline);
+
+    /* no completion found */
+    if (nb_completions <= 0)
+        return;
+    if (nb_completions == 1) {
+        len = strlen(completions[0]);
+        for(i = completion_index; i < len; i++) {
+            term_insert_char(completions[0][i]);
+        }
+        /* extra space for next argument. XXX: make it more generic */
+        if (len > 0 && completions[0][len - 1] != '/')
+            term_insert_char(' ');
+    } else {
+        term_printf("\n");
+        max_width = 0;
+        max_prefix = 0;	
+        for(i = 0; i < nb_completions; i++) {
+            len = strlen(completions[i]);
+            if (i==0) {
+                max_prefix = len;
+            } else {
+                if (len < max_prefix)
+                    max_prefix = len;
+                for(j=0; j<max_prefix; j++) {
+                    if (completions[i][j] != completions[0][j])
+                        max_prefix = j;
+                }
+            }
+            if (len > max_width)
+                max_width = len;
+        }
+        if (max_prefix > 0) 
+            for(i = completion_index; i < max_prefix; i++) {
+                term_insert_char(completions[0][i]);
+            }
+        max_width += 2;
+        if (max_width < 10)
+            max_width = 10;
+        else if (max_width > 80)
+            max_width = 80;
+        nb_cols = 80 / max_width;
+        j = 0;
+        for(i = 0; i < nb_completions; i++) {
+            term_printf("%-*s", max_width, completions[i]);
+            if (++j == nb_cols || i == (nb_completions - 1)) {
+                term_printf("\n");
+                j = 0;
+            }
+        }
+        term_show_prompt2();
+    }
+}
+
+/* return true if command handled */
+void readline_handle_byte(int ch)
+{
+    switch(term_esc_state) {
+    case IS_NORM:
+        switch(ch) {
+        case 1:
+            term_bol();
+            break;
+        case 4:
+            term_delete_char();
+            break;
+        case 5:
+            term_eol();
+            break;
+        case 9:
+            term_completion();
+            break;
+        case 10:
+        case 13:
+            term_cmd_buf[term_cmd_buf_size] = '\0';
+            if (!term_is_password)
+                term_hist_add(term_cmd_buf);
+            term_printf("\n");
+            term_cmd_buf_index = 0;
+            term_cmd_buf_size = 0;
+            term_last_cmd_buf_index = 0;
+            term_last_cmd_buf_size = 0;
+            /* NOTE: readline_start can be called here */
+            term_readline_func(term_readline_opaque, term_cmd_buf);
+            break;
+        case 23:
+            /* ^W */
+            term_backword();
+            break;
+        case 27:
+            term_esc_state = IS_ESC;
+            break;
+        case 127:
+        case 8:
+            term_backspace();
+            break;
+	case 155:
+            term_esc_state = IS_CSI;
+	    break;
+        default:
+            if (ch >= 32) {
+                term_insert_char(ch);
+            }
+            break;
+        }
+        break;
+    case IS_ESC:
+        if (ch == '[') {
+            term_esc_state = IS_CSI;
+            term_esc_param = 0;
+        } else {
+            term_esc_state = IS_NORM;
+        }
+        break;
+    case IS_CSI:
+        switch(ch) {
+	case 'A':
+	case 'F':
+	    term_up_char();
+	    break;
+	case 'B':
+	case 'E':
+	    term_down_char();
+	    break;
+        case 'D':
+            term_backward_char();
+            break;
+        case 'C':
+            term_forward_char();
+            break;
+        case '0' ... '9':
+            term_esc_param = term_esc_param * 10 + (ch - '0');
+            goto the_end;
+        case '~':
+            switch(term_esc_param) {
+            case 1:
+                term_bol();
+                break;
+            case 3:
+                term_delete_char();
+                break;
+            case 4:
+                term_eol();
+                break;
+            }
+            break;
+        default:
+            break;
+        }
+        term_esc_state = IS_NORM;
+    the_end:
+        break;
+    }
+    term_update();
+}
+
+void readline_start(const char *prompt, int is_password,
+                    ReadLineFunc *readline_func, void *opaque)
+{
+    pstrcpy(term_prompt, sizeof(term_prompt), prompt);
+    term_readline_func = readline_func;
+    term_readline_opaque = opaque;
+    term_is_password = is_password;
+    term_show_prompt();
+}
+
+const char *readline_get_history(unsigned int index)
+{
+    if (index >= TERM_MAX_CMDS)
+        return NULL;
+    return term_history[index];
+}
+
+
diff --git a/sdl_keysym.h b/sdl_keysym.h
new file mode 100644
index 0000000..9a74142
--- /dev/null
+++ b/sdl_keysym.h
@@ -0,0 +1,278 @@
+typedef struct {
+	const char* name;
+	int keysym;
+} name2keysym_t;
+static name2keysym_t name2keysym[]={
+/* ascii */
+    { "space",                0x020},
+    { "exclam",               0x021},
+    { "quotedbl",             0x022},
+    { "numbersign",           0x023},
+    { "dollar",               0x024},
+    { "percent",              0x025},
+    { "ampersand",            0x026},
+    { "apostrophe",           0x027},
+    { "parenleft",            0x028},
+    { "parenright",           0x029},
+    { "asterisk",             0x02a},
+    { "plus",                 0x02b},
+    { "comma",                0x02c},
+    { "minus",                0x02d},
+    { "period",               0x02e},
+    { "slash",                0x02f},
+    { "0",                    0x030},
+    { "1",                    0x031},
+    { "2",                    0x032},
+    { "3",                    0x033},
+    { "4",                    0x034},
+    { "5",                    0x035},
+    { "6",                    0x036},
+    { "7",                    0x037},
+    { "8",                    0x038},
+    { "9",                    0x039},
+    { "colon",                0x03a},
+    { "semicolon",            0x03b},
+    { "less",                 0x03c},
+    { "equal",                0x03d},
+    { "greater",              0x03e},
+    { "question",             0x03f},
+    { "at",                   0x040},
+    { "A",                    0x041},
+    { "B",                    0x042},
+    { "C",                    0x043},
+    { "D",                    0x044},
+    { "E",                    0x045},
+    { "F",                    0x046},
+    { "G",                    0x047},
+    { "H",                    0x048},
+    { "I",                    0x049},
+    { "J",                    0x04a},
+    { "K",                    0x04b},
+    { "L",                    0x04c},
+    { "M",                    0x04d},
+    { "N",                    0x04e},
+    { "O",                    0x04f},
+    { "P",                    0x050},
+    { "Q",                    0x051},
+    { "R",                    0x052},
+    { "S",                    0x053},
+    { "T",                    0x054},
+    { "U",                    0x055},
+    { "V",                    0x056},
+    { "W",                    0x057},
+    { "X",                    0x058},
+    { "Y",                    0x059},
+    { "Z",                    0x05a},
+    { "bracketleft",          0x05b},
+    { "backslash",            0x05c},
+    { "bracketright",         0x05d},
+    { "asciicircum",          0x05e},
+    { "underscore",           0x05f},
+    { "grave",                0x060},
+    { "a",                    0x061},
+    { "b",                    0x062},
+    { "c",                    0x063},
+    { "d",                    0x064},
+    { "e",                    0x065},
+    { "f",                    0x066},
+    { "g",                    0x067},
+    { "h",                    0x068},
+    { "i",                    0x069},
+    { "j",                    0x06a},
+    { "k",                    0x06b},
+    { "l",                    0x06c},
+    { "m",                    0x06d},
+    { "n",                    0x06e},
+    { "o",                    0x06f},
+    { "p",                    0x070},
+    { "q",                    0x071},
+    { "r",                    0x072},
+    { "s",                    0x073},
+    { "t",                    0x074},
+    { "u",                    0x075},
+    { "v",                    0x076},
+    { "w",                    0x077},
+    { "x",                    0x078},
+    { "y",                    0x079},
+    { "z",                    0x07a},
+    { "braceleft",            0x07b},
+    { "bar",                  0x07c},
+    { "braceright",           0x07d},
+    { "asciitilde",           0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace",         0x0a0},
+{ "exclamdown",           0x0a1},
+{ "cent",         	  0x0a2},
+{ "sterling",             0x0a3},
+{ "currency",             0x0a4},
+{ "yen",                  0x0a5},
+{ "brokenbar",            0x0a6},
+{ "section",              0x0a7},
+{ "diaeresis",            0x0a8},
+{ "copyright",            0x0a9},
+{ "ordfeminine",          0x0aa},
+{ "guillemotleft",        0x0ab},
+{ "notsign",              0x0ac},
+{ "hyphen",               0x0ad},
+{ "registered",           0x0ae},
+{ "macron",               0x0af},
+{ "degree",               0x0b0},
+{ "plusminus",            0x0b1},
+{ "twosuperior",          0x0b2},
+{ "threesuperior",        0x0b3},
+{ "acute",                0x0b4},
+{ "mu",                   0x0b5},
+{ "paragraph",            0x0b6},
+{ "periodcentered",       0x0b7},
+{ "cedilla",              0x0b8},
+{ "onesuperior",          0x0b9},
+{ "masculine",            0x0ba},
+{ "guillemotright",       0x0bb},
+{ "onequarter",           0x0bc},
+{ "onehalf",              0x0bd},
+{ "threequarters",        0x0be},
+{ "questiondown",         0x0bf},
+{ "Agrave",               0x0c0},
+{ "Aacute",               0x0c1},
+{ "Acircumflex",          0x0c2},
+{ "Atilde",               0x0c3},
+{ "Adiaeresis",           0x0c4},
+{ "Aring",                0x0c5},
+{ "AE",                   0x0c6},
+{ "Ccedilla",             0x0c7},
+{ "Egrave",               0x0c8},
+{ "Eacute",               0x0c9},
+{ "Ecircumflex",          0x0ca},
+{ "Ediaeresis",           0x0cb},
+{ "Igrave",               0x0cc},
+{ "Iacute",               0x0cd},
+{ "Icircumflex",          0x0ce},
+{ "Idiaeresis",           0x0cf},
+{ "ETH",                  0x0d0},
+{ "Eth",                  0x0d0},
+{ "Ntilde",               0x0d1},
+{ "Ograve",               0x0d2},
+{ "Oacute",               0x0d3},
+{ "Ocircumflex",          0x0d4},
+{ "Otilde",               0x0d5},
+{ "Odiaeresis",           0x0d6},
+{ "multiply",             0x0d7},
+{ "Ooblique",             0x0d8},
+{ "Oslash",               0x0d8},
+{ "Ugrave",               0x0d9},
+{ "Uacute",               0x0da},
+{ "Ucircumflex",          0x0db},
+{ "Udiaeresis",           0x0dc},
+{ "Yacute",               0x0dd},
+{ "THORN",                0x0de},
+{ "Thorn",                0x0de},
+{ "ssharp",               0x0df},
+{ "agrave",               0x0e0},
+{ "aacute",               0x0e1},
+{ "acircumflex",          0x0e2},
+{ "atilde",               0x0e3},
+{ "adiaeresis",           0x0e4},
+{ "aring",                0x0e5},
+{ "ae",                   0x0e6},
+{ "ccedilla",             0x0e7},
+{ "egrave",               0x0e8},
+{ "eacute",               0x0e9},
+{ "ecircumflex",          0x0ea},
+{ "ediaeresis",           0x0eb},
+{ "igrave",               0x0ec},
+{ "iacute",               0x0ed},
+{ "icircumflex",          0x0ee},
+{ "idiaeresis",           0x0ef},
+{ "eth",                  0x0f0},
+{ "ntilde",               0x0f1},
+{ "ograve",               0x0f2},
+{ "oacute",               0x0f3},
+{ "ocircumflex",          0x0f4},
+{ "otilde",               0x0f5},
+{ "odiaeresis",           0x0f6},
+{ "division",             0x0f7},
+{ "oslash",               0x0f8},
+{ "ooblique",             0x0f8},
+{ "ugrave",               0x0f9},
+{ "uacute",               0x0fa},
+{ "ucircumflex",          0x0fb},
+{ "udiaeresis",           0x0fc},
+{ "yacute",               0x0fd},
+{ "thorn",                0x0fe},
+{ "ydiaeresis",           0x0ff},
+{"EuroSign", SDLK_EURO},
+
+    /* modifiers */
+{"Control_L", SDLK_LCTRL},
+{"Control_R", SDLK_RCTRL},
+{"Alt_L", SDLK_LALT},
+{"Alt_R", SDLK_RALT},
+{"Caps_Lock", SDLK_CAPSLOCK},
+{"Meta_L", SDLK_LMETA},
+{"Meta_R", SDLK_RMETA},
+{"Shift_L", SDLK_LSHIFT},
+{"Shift_R", SDLK_RSHIFT},
+{"Super_L", SDLK_LSUPER},
+{"Super_R", SDLK_RSUPER},
+
+    /* special keys */
+{"BackSpace", SDLK_BACKSPACE},
+{"Tab", SDLK_TAB},
+{"Return", SDLK_RETURN},
+{"Right", SDLK_RIGHT},
+{"Left", SDLK_LEFT},
+{"Up", SDLK_UP},
+{"Down", SDLK_DOWN},
+{"Page_Down", SDLK_PAGEDOWN},
+{"Page_Up", SDLK_PAGEUP},
+{"Insert", SDLK_INSERT},
+{"Delete", SDLK_DELETE},
+{"Home", SDLK_HOME},
+{"End", SDLK_END},
+{"Scroll_Lock", SDLK_SCROLLOCK},
+{"F1", SDLK_F1},
+{"F2", SDLK_F2},
+{"F3", SDLK_F3},
+{"F4", SDLK_F4},
+{"F5", SDLK_F5},
+{"F6", SDLK_F6},
+{"F7", SDLK_F7},
+{"F8", SDLK_F8},
+{"F9", SDLK_F9},
+{"F10", SDLK_F10},
+{"F11", SDLK_F11},
+{"F12", SDLK_F12},
+{"F13", SDLK_F13},
+{"F14", SDLK_F14},
+{"F15", SDLK_F15},
+{"Sys_Req", SDLK_SYSREQ},
+{"KP_0", SDLK_KP0},
+{"KP_1", SDLK_KP1},
+{"KP_2", SDLK_KP2},
+{"KP_3", SDLK_KP3},
+{"KP_4", SDLK_KP4},
+{"KP_5", SDLK_KP5},
+{"KP_6", SDLK_KP6},
+{"KP_7", SDLK_KP7},
+{"KP_8", SDLK_KP8},
+{"KP_9", SDLK_KP9},
+{"KP_Add", SDLK_KP_PLUS},
+{"KP_Decimal", SDLK_KP_PERIOD},
+{"KP_Divide", SDLK_KP_DIVIDE},
+{"KP_Enter", SDLK_KP_ENTER},
+{"KP_Equal", SDLK_KP_EQUALS},
+{"KP_Multiply", SDLK_KP_MULTIPLY},
+{"KP_Subtract", SDLK_KP_MINUS},
+{"help", SDLK_HELP},
+{"Menu", SDLK_MENU},
+{"Power", SDLK_POWER},
+{"Print", SDLK_PRINT},
+{"Mode_switch", SDLK_MODE},
+{"Multi_Key", SDLK_COMPOSE},
+{"Num_Lock", SDLK_NUMLOCK},
+{"Pause", SDLK_PAUSE},
+{"Escape", SDLK_ESCAPE},
+
+{0,0},
+};
diff --git a/shaper.c b/shaper.c
new file mode 100644
index 0000000..a522919
--- /dev/null
+++ b/shaper.c
@@ -0,0 +1,590 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "shaper.h"
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include <stdlib.h>
+
+#define  SHAPER_CLOCK        rt_clock
+#define  SHAPER_CLOCK_UNIT   1000.
+
+static int
+_packet_is_internal( const uint8_t*  data, size_t  size )
+{
+    const uint8_t*  end = data + size;
+
+    /* must have room for Mac + IP header */
+    if (data + 40 > end)
+        return 0;
+
+    if (data[12] != 0x08 || data[13] != 0x00 )
+        return 0;
+
+    /* must have valid IP header */
+    data += 14;
+    if ((data[0] >> 4) != 4 || (data[0] & 15) < 5)
+        return 0;
+
+    /* internal if both source and dest addresses are in 10.x.x.x */
+    return ( data[12] == 10 && data[16] == 10);
+}
+
+/* here's how we implement network shaping. we want to limit the network
+ * rate to a given constant MAX_RATE expressed as bits/second. this means
+ * that it takes 1/MAX_RATE seconds to send a single bit, and count*8/MAX_RATE
+ * seconds to send 'count' bytes.
+ *
+ * we're going to implement a scheme where, when we send a packet of
+ * 'count' bytes, no other packet will go through in the same direction for
+ * at least 'count*8/MAX_RATE' seconds. any successive packet that is "sent"
+ * in this interval is placed in a queue, associated to a timer
+ *
+ * there are different (queue/timer/rate) values for the input and output
+ * direction of the user vlan.
+ */
+typedef struct QueuedPacketRec_ {
+    int64_t                    expiration;
+    struct QueuedPacketRec_*   next;
+    size_t                     size;
+    void*                      opaque;
+    void*                      data;
+} QueuedPacketRec, *QueuedPacket;
+
+
+static QueuedPacket
+queued_packet_create( const void*   data,
+                      size_t        size,
+                      void*         opaque,
+                      int           do_copy )
+{
+    QueuedPacket   packet;
+    size_t         packet_size = sizeof(*packet);
+
+    if (do_copy)
+        packet_size += size;
+
+    packet = qemu_malloc(packet_size);
+    packet->next       = NULL;
+    packet->expiration = 0;
+    packet->size       = (size_t)size;
+    packet->opaque     = opaque;
+
+    if (do_copy) {
+        packet->data = (void*)(packet+1);
+        memcpy( (char*)packet->data, (char*)data, packet->size );
+    } else {
+        packet->data = (void*)data;
+    }
+    return packet;
+}
+
+static void
+queued_packet_free( QueuedPacket  packet )
+{
+    if (packet) {
+        qemu_free( packet );
+    }
+}
+
+typedef struct NetShaperRec_ {
+    QueuedPacket   packets;   /* list of queued packets, ordered by expiration date */
+    int            num_packets;
+    int            active;    /* is this shaper active ? */
+    int64_t        block_until;
+    double         max_rate;  /* max rate expressed in bytes/second */
+    double         inv_rate;  /* inverse of max rate                */
+    QEMUTimer*     timer;     /* QEMU timer */
+
+    int                do_copy;
+    NetShaperSendFunc  send_func;
+
+} NetShaperRec;
+
+
+void
+netshaper_destroy( NetShaper  shaper )
+{
+    if (shaper) {
+        shaper->active = 0;
+
+        while (shaper->packets) {
+            QueuedPacket  packet = shaper->packets;
+            shaper->packets = packet->next;
+            packet->next    = NULL;
+            queued_packet_free(packet);
+        }
+
+        qemu_del_timer(shaper->timer);
+        qemu_free_timer(shaper->timer);
+        shaper->timer = NULL;
+        qemu_free(shaper);
+    }
+}
+
+/* this function is called when the shaper's timer expires */
+static void
+netshaper_expires( NetShaper  shaper )
+{
+    QueuedPacket  packet;
+
+    while ((packet = shaper->packets) != NULL) {
+        int64_t   now = qemu_get_clock( SHAPER_CLOCK );
+
+       if (packet->expiration > now)
+           break;
+
+       shaper->packets = packet->next;
+       shaper->send_func( packet->data, packet->size, packet->opaque );
+       queued_packet_free(packet);
+       shaper->num_packets--;
+   }
+
+   /* reprogram timer if needed */
+   if (shaper->packets) {
+       shaper->block_until = shaper->packets->expiration;
+       qemu_mod_timer( shaper->timer, shaper->block_until );
+   } else {
+       shaper->block_until = -1;
+   }
+}
+
+
+NetShaper
+netshaper_create( int                do_copy,
+                  NetShaperSendFunc  send_func )
+{
+    NetShaper  shaper = qemu_malloc(sizeof(*shaper));
+
+    shaper->active = 0;
+    shaper->packets = NULL;
+    shaper->num_packets = 0;
+    shaper->timer   = qemu_new_timer( SHAPER_CLOCK,
+                                      (QEMUTimerCB*) netshaper_expires,
+                                      shaper );
+    shaper->send_func = send_func;
+    shaper->max_rate  = 1e6;
+    shaper->inv_rate  = 0.;
+
+    shaper->block_until = -1; /* magic value, means to not block */
+
+    return shaper;
+}
+
+void
+netshaper_set_rate( NetShaper  shaper,
+                    double     rate )
+{
+    /* send all current packets when changing the rate */
+    while (shaper->packets) {
+        QueuedPacket  packet = shaper->packets;
+        shaper->packets = packet->next;
+        shaper->send_func(packet->data, packet->size, packet->opaque);
+        qemu_free(packet);
+        shaper->num_packets = 0;
+    }
+
+    shaper->max_rate = rate;
+    if (rate > 1.) {
+        shaper->inv_rate = (8.*SHAPER_CLOCK_UNIT)/rate;  /* qemu_get_clock returns time in ms */
+        shaper->active   = 1;                            /* for the real-time clock           */
+    } else {
+        shaper->active = 0;
+    }
+
+    shaper->block_until = -1;
+}
+
+void
+netshaper_send_aux( NetShaper  shaper,
+                    void*      data,
+                    size_t     size,
+                    void*      opaque )
+{
+    int64_t   now;
+
+    if (!shaper->active || _packet_is_internal(data, size)) {
+        shaper->send_func( data, size, opaque );
+        return;
+    }
+
+    now = qemu_get_clock( SHAPER_CLOCK );
+    if (now >= shaper->block_until) {
+        shaper->send_func( data, size, opaque );
+        shaper->block_until = now + size*shaper->inv_rate;
+        //fprintf(stderr, "NETSHAPER: block for %.2fms\n", (shaper->block_until - now)*1.0 );
+        return;
+    }
+
+    /* create new packet, add it to the queue */
+    {
+        QueuedPacket   packet;
+
+        packet = queued_packet_create( data, size, opaque, shaper->do_copy );
+
+        packet->expiration = shaper->block_until;
+
+        {
+            QueuedPacket  *pnode, node;
+
+            pnode = &shaper->packets;
+            for (;;) {
+                node = *pnode;
+                if (node == NULL || node->expiration > packet->expiration )
+                    break;
+                pnode = &node->next;
+            }
+            packet->next = *pnode;
+            *pnode       = packet;
+
+            if (packet == shaper->packets)
+                qemu_mod_timer( shaper->timer, packet->expiration );
+        }
+        shaper->num_packets += 1;
+    }
+    shaper->block_until += size*shaper->inv_rate;
+    //fprintf(stderr, "NETSHAPER: block2 for %.2fms\n", (shaper->block_until - now)*1.0 );
+}
+
+void
+netshaper_send( NetShaper  shaper,
+                void*      data,
+                size_t     size )
+{
+    netshaper_send_aux(shaper, data, size, NULL);
+}
+
+
+int
+netshaper_can_send( NetShaper  shaper )
+{
+    int64_t  now;
+
+    if (!shaper->active || shaper->block_until < 0)
+        return 1;
+
+    if (shaper->packets)
+        return 0;
+
+    now = qemu_get_clock( SHAPER_CLOCK );
+    return (now >= shaper->block_until);
+}
+
+
+
+
+
+
+/* this type is used to model a session connection/state
+ * if session->packet is != NULL, then the connection is delayed
+ */
+typedef struct SessionRec_ {
+    int64_t               expiration;
+    struct SessionRec_*   next;
+    unsigned              src_ip;
+    unsigned              dst_ip;
+    unsigned short        src_port;
+    unsigned short        dst_port;
+    uint8_t               protocol;
+    QueuedPacket          packet;
+
+} SessionRec, *Session;
+
+#define  _PROTOCOL_TCP   6
+#define  _PROTOCOL_UDP   17
+
+
+
+static void
+session_free( Session  session )
+{
+    if (session) {
+        if (session->packet) {
+            queued_packet_free(session->packet);
+            session->packet = NULL;
+        }
+        qemu_free( session );
+    }
+}
+
+
+#if 0  /* useful for debugging */
+static const char*
+session_to_string( Session  session )
+{
+    static char  temp[256];
+    const char*  format = (session->protocol == _PROTOCOL_TCP) ? "TCP" : "UDP";
+    sprintf( temp, "%s[%d.%d.%d.%d:%d / %d.%d.%d.%d:%d]", format,
+             (session->src_ip >> 24) & 255, (session->src_ip >> 16) & 255,
+             (session->src_ip >> 8) & 255, (session->src_ip) & 255, session->src_port,
+             (session->dst_ip >> 24) & 255, (session->dst_ip >> 16) & 255,
+             (session->dst_ip >> 8) & 255, (session->dst_ip) & 255, session->dst_port);
+
+    return temp;
+}
+#endif
+
+/* returns TRUE if this corresponds to a SYN packet */
+int
+_packet_SYN_flags( const void*  _data, size_t   size, Session  info )
+{
+    const uint8_t*  data = (const uint8_t*)_data;
+    const uint8_t*  end  = data + size;
+
+    /* enough room for a Ethernet MAC packet ? */
+    if (data + 14 > end - 4)
+        return 0;
+
+    /* is it an IP packet ? */
+    if (data[12] != 0x8 || data[13] != 0)
+        return 0;
+
+    data += 14;
+    end  -= 4;
+
+    if (data + 20 > end)
+        return 0;
+
+    /* IP version must be 4, and the header length in words at least 5 */
+    if ((data[0] & 0xF) < 5 || (data[0] >> 4) != 4)
+        return 0;
+
+    /* time-to-live must be > 0 */
+    if (data[8] == 0)
+        return 0;
+
+    /* must be TCP or UDP packet */
+    if (data[9] != _PROTOCOL_TCP && data[9] != _PROTOCOL_UDP)
+        return 0;
+
+    info->protocol = data[9];
+    info->src_ip   = (data[12] << 24) | (data[13] << 16) | (data[14] << 8) | data[15];
+    info->dst_ip   = (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19];
+
+    data += 4*(data[0] & 15);
+    if (data + 20 > end)
+        return 0;
+
+    info->src_port = (unsigned short)((data[0] << 8) | data[1]);
+    info->dst_port = (unsigned short)((data[2] << 8) | data[3]);
+
+    return (data[13] & 0x1f);
+}
+
+
+typedef struct NetDelayRec_
+{
+    Session     sessions;
+    int         num_sessions;
+    QEMUTimer*  timer;
+    int         active;
+    int         min_ms;
+    int         max_ms;
+
+    NetShaperSendFunc  send_func;
+
+} NetDelayRec;
+
+
+static Session*
+netdelay_lookup_session( NetDelay  delay, Session  info )
+{
+    Session*  pnode = &delay->sessions;
+    Session   node;
+
+    for (;;) {
+        node = *pnode;
+        if (node == NULL)
+            break;
+
+        if (node->src_ip == info->src_ip &&
+            node->dst_ip == info->dst_ip &&
+            node->src_port == info->src_port &&
+            node->dst_port == info->dst_port &&
+            node->protocol == info->protocol )
+            break;
+
+        pnode = &node->next;
+    }
+    return pnode;
+}
+
+
+
+/* called by the delay's timer on expiration */
+static void
+netdelay_expires( NetDelay  delay )
+{
+    Session  session;
+    int64_t  now = qemu_get_clock( SHAPER_CLOCK );
+    int      rearm = 0;
+    int64_t  rearm_time = 0;
+
+    for (session = delay->sessions; session != NULL; session = session->next)
+    {
+        QueuedPacket  packet = session->packet;
+
+        if (packet == NULL)
+            continue;
+
+        if (session->expiration <= now) {
+            /* send the SYN packet now */
+                    //fprintf(stderr, "NetDelay:RST: sending creation for %s\n", session_to_string(session) );
+            delay->send_func( packet->data, packet->size, packet->opaque );
+            session->packet = NULL;
+            queued_packet_free( packet );
+        } else {
+            if (!rearm) {
+                rearm      = 1;
+                rearm_time = session->expiration;
+            }
+            else if ( session->expiration < rearm_time )
+                rearm_time = session->expiration;
+        }
+    }
+
+    if (rearm)
+        qemu_mod_timer( delay->timer, rearm_time );
+}
+
+
+NetDelay
+netdelay_create( NetShaperSendFunc  send_func )
+{
+    NetDelay  delay = qemu_malloc(sizeof(*delay));
+
+    delay->sessions     = NULL;
+    delay->num_sessions = 0;
+    delay->timer        = qemu_new_timer( SHAPER_CLOCK,
+                                          (QEMUTimerCB*) netdelay_expires,
+                                          delay );
+    delay->active = 0;
+    delay->min_ms = 0;
+    delay->max_ms = 0;
+
+    delay->send_func = send_func;
+
+    return delay;
+}
+
+
+void
+netdelay_set_latency( NetDelay  delay, int  min_ms, int  max_ms )
+{
+    /* when changing the latency, accept all sessions */
+    while (delay->sessions) {
+        Session  session = delay->sessions;
+        delay->sessions = session->next;
+        session->next = NULL;
+        if (session->packet) {
+            QueuedPacket  packet = session->packet;
+            delay->send_func( packet->data, packet->size, packet->opaque );
+        }
+        session_free(session);
+        delay->num_sessions--;
+    }
+
+    delay->min_ms = min_ms;
+    delay->max_ms = max_ms;
+    delay->active = (min_ms <= max_ms) && min_ms > 0;
+}
+
+void
+netdelay_send( NetDelay  delay, const void*  data, size_t  size )
+{
+    netdelay_send_aux(delay, data, size, NULL);
+}
+
+
+void
+netdelay_send_aux( NetDelay  delay, const void*  data, size_t  size, void* opaque )
+{
+    if (delay->active && !_packet_is_internal(data, size)) {
+        SessionRec  info[1];
+        int         flags;
+
+        flags = _packet_SYN_flags( data, size, info );
+        if ((flags & 0x05) != 0)
+        {  /* FIN or RST: drop connection */
+            Session*  lookup  = netdelay_lookup_session( delay, info );
+            Session   session = *lookup;
+            if (session != NULL) {
+                //fprintf(stderr, "NetDelay:RST: dropping %s\n", session_to_string(info) );
+
+                *lookup = session->next;
+                session_free( session );
+                delay->num_sessions -= 1;
+            }
+        }
+        else if ((flags & 0x12) == 0x02)
+        {
+            /* SYN: create connection */
+            Session*  lookup  = netdelay_lookup_session( delay, info );
+            Session   session = *lookup;
+
+            if (session != NULL) {
+                if (session->packet != NULL) {
+                   /* this is a SYN re-transmission, since we didn't
+                    * send the original SYN packet yet, just eat this one
+                    */
+                    //fprintf(stderr, "NetDelay:RST: swallow SYN re-send for %s\n", session_to_string(info) );
+                    return;
+                }
+            } else {
+                /* establish a new session slightly in the future */
+                int   latency = delay->min_ms;
+                int   range   = delay->max_ms - delay->min_ms;
+
+                 if (range > 0)
+                    latency += rand() % range;
+
+                    //fprintf(stderr, "NetDelay:RST: delay creation for %s\n", session_to_string(info) );
+                session = qemu_malloc( sizeof(*session) );
+
+                session->next        = delay->sessions;
+                delay->sessions      = session;
+                delay->num_sessions += 1;
+
+                session->expiration = qemu_get_clock( SHAPER_CLOCK ) + latency;
+
+                session->src_ip   = info->src_ip;
+                session->dst_ip   = info->dst_ip;
+                session->src_port = info->src_port;
+                session->dst_port = info->dst_port;
+                session->protocol = info->protocol;
+
+                session->packet = queued_packet_create( data, size, opaque, 1 );
+
+                netdelay_expires(delay);
+                return;
+            }
+        }
+    }
+
+    delay->send_func( (void*)data, size, opaque );
+}
+
+
+void
+netdelay_destroy( NetDelay  delay )
+{
+    if (delay) {
+        while (delay->sessions) {
+            Session  session = delay->sessions;
+            delay->sessions = session->next;
+            session_free(session);
+            delay->num_sessions -= 1;
+        }
+        delay->active = 0;
+        qemu_free( delay );
+    }
+}
+
diff --git a/shaper.h b/shaper.h
new file mode 100644
index 0000000..45dbd5b
--- /dev/null
+++ b/shaper.h
@@ -0,0 +1,51 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _SLIRP_SHAPER_H_
+#define _SLIRP_SHAPER_H_
+
+#include <stddef.h>
+
+/* a NetShaper object is used to limit the throughput of data packets
+ * at a fixed rate expressed in bits/seconds
+ */
+typedef struct NetShaperRec_*  NetShaper;
+typedef void (*NetShaperSendFunc)( void*  data, size_t  size, void*  opaque);
+
+NetShaper   netshaper_create  ( int                do_copy,
+                                NetShaperSendFunc  send_func );
+
+void        netshaper_set_rate(NetShaper  shaper, double  rate );
+
+void        netshaper_send( NetShaper  shaper, void* data, size_t  size );
+
+void        netshaper_send_aux( NetShaper  shaper, void* data, size_t  size, void*  opaque );
+
+int         netshaper_can_send( NetShaper  shaper );
+
+void        netshaper_destroy (NetShaper   shaper);
+
+/* a NetDelay object is used to simulate network connection latencies */
+typedef struct NetDelayRec_*  NetDelay;
+
+NetDelay   netdelay_create( NetShaperSendFunc  send_func );
+void       netdelay_set_latency( NetDelay  delay, int  min_ms, int  max_ms );
+void       netdelay_send( NetDelay  delay, const void*  data, size_t  size );
+void       netdelay_send_aux( NetDelay  delay, const void*  data, size_t  size, void*  opaque );
+void       netdelay_destroy( NetDelay  delay );
+
+/** in vl.c */
+/* network traffic shaper and delayer */
+extern NetShaper   slirp_shaper_in;
+extern NetShaper   slirp_shaper_out;
+extern NetDelay    slirp_delay_in;
+
+#endif /* _SLIRP_SHAPER_H_ */
diff --git a/slirp2/COPYRIGHT b/slirp2/COPYRIGHT
new file mode 100644
index 0000000..2e86862
--- /dev/null
+++ b/slirp2/COPYRIGHT
@@ -0,0 +1,64 @@
+Slirp was written by Danny Gasparovski.
+Copyright (c), 1995,1996 All Rights Reserved.
+
+Slirp is maintained by Kelly Price <tygris+slirp@erols.com>
+
+Slirp is free software; "free" as in you don't have to pay for it, and you
+are free to do whatever you want with it.  I do not accept any donations,
+monetary or otherwise, for Slirp.  Instead, I would ask you to pass this
+potential donation to your favorite charity.  In fact, I encourage
+*everyone* who finds Slirp useful to make a small donation to their
+favorite charity (for example, GreenPeace).  This is not a requirement, but
+a suggestion from someone who highly values the service they provide.
+
+The copyright terms and conditions:
+
+---BEGIN---
+
+ Copyright (c) 1995,1996 Danny Gasparovski.  All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+    must display the following acknowledgment:
+      This product includes software developed by Danny Gasparovski.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ DANNY GASPAROVSKI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+---END---
+
+This basically means you can do anything you want with the software, except
+1) call it your own, and 2) claim warranty on it.  There is no warranty for
+this software.  None.  Nada.  If you lose a million dollars while using
+Slirp, that's your loss not mine.  So, ***USE AT YOUR OWN RISK!***.
+
+If these conditions cannot be met due to legal restrictions (E.g. where it
+is against the law to give out Software without warranty), you must cease
+using the software and delete all copies you have.
+
+Slirp uses code that is copyrighted by the following people/organizations:
+
+Juha Pirkola.
+Gregory M. Christy.
+The Regents of the University of California.
+Carnegie Mellon University.
+The Australian National University.
+RSA Data Security, Inc.
+
+Please read the top of each source file for the details on the various
+copyrights.
diff --git a/slirp2/bootp.c b/slirp2/bootp.c
new file mode 100644
index 0000000..9ab4b15
--- /dev/null
+++ b/slirp2/bootp.c
@@ -0,0 +1,263 @@
+/*
+ * QEMU BOOTP/DHCP server
+ * 
+ * Copyright (c) 2004 Fabrice Bellard
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <slirp.h>
+
+/* XXX: only DHCP is supported */
+
+#define NB_ADDR 16
+
+#define START_ADDR 15
+
+#define LEASE_TIME (24 * 3600)
+
+typedef struct {
+    uint8_t allocated;
+    uint8_t macaddr[6];
+} BOOTPClient;
+
+BOOTPClient bootp_clients[NB_ADDR];
+
+const char *bootp_filename;
+
+static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE };
+
+#ifdef DEBUG
+#define dprintf(fmt, args...) \
+if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## args); fflush(dfd); }
+#else
+#define dprintf(fmt, args...)
+#endif
+
+static BOOTPClient *get_new_addr(SockAddress*  paddr)
+{
+    BOOTPClient *bc;
+    int i;
+
+    for(i = 0; i < NB_ADDR; i++) {
+        if (!bootp_clients[i].allocated)
+            goto found;
+    }
+    return NULL;
+ found:
+    bc = &bootp_clients[i];
+    bc->allocated = 1;
+    sock_address_init_inet( paddr, 
+                            special_addr_ip | (i+START_ADDR),
+                            BOOTP_CLIENT );
+    return bc;
+}
+
+static BOOTPClient *find_addr(SockAddress* paddr, const uint8_t *macaddr)
+{
+    BOOTPClient *bc;
+    int i;
+
+    for(i = 0; i < NB_ADDR; i++) {
+        if (!memcmp(macaddr, bootp_clients[i].macaddr, 6))
+            goto found;
+    }
+    return NULL;
+ found:
+    bc = &bootp_clients[i];
+    bc->allocated = 1;
+    sock_address_init_inet( paddr,
+                            special_addr_ip | (i + START_ADDR),
+                            BOOTP_CLIENT );
+    return bc;
+}
+
+static void dhcp_decode(const uint8_t *buf, int size,
+                        int *pmsg_type)
+{
+    const uint8_t *p, *p_end;
+    int len, tag;
+
+    *pmsg_type = 0;    
+
+    p = buf;
+    p_end = buf + size;
+    if (size < 5)
+        return;
+    if (memcmp(p, rfc1533_cookie, 4) != 0)
+        return;
+    p += 4;
+    while (p < p_end) {
+        tag = p[0];
+        if (tag == RFC1533_PAD) {
+            p++; 
+        } else if (tag == RFC1533_END) {
+            break;
+        } else {
+            p++;
+            if (p >= p_end)
+                break;
+            len = *p++;
+            dprintf("dhcp: tag=0x%02x len=%d\n", tag, len);
+
+            switch(tag) {
+            case RFC2132_MSG_TYPE:
+                if (len >= 1)
+                    *pmsg_type = p[0];
+                break;
+            default:
+                break;
+            }
+            p += len;
+        }
+    }
+}
+
+static void bootp_reply(struct bootp_t *bp)
+{
+    BOOTPClient *bc;
+    MBuf m;
+    struct bootp_t *rbp;
+    SockAddress  saddr, daddr;
+    uint32_t     dns_addr;
+    int dhcp_msg_type, val;
+    uint8_t *q;
+
+    /* extract exact DHCP msg type */
+    dhcp_decode(bp->bp_vend, DHCP_OPT_LEN, &dhcp_msg_type);
+    dprintf("bootp packet op=%d msgtype=%d\n", bp->bp_op, dhcp_msg_type);
+    
+    if (dhcp_msg_type == 0)
+        dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */
+        
+    if (dhcp_msg_type != DHCPDISCOVER && 
+        dhcp_msg_type != DHCPREQUEST)
+        return;
+    /* XXX: this is a hack to get the client mac address */
+    memcpy(client_ethaddr, bp->bp_hwaddr, 6);
+    
+    if ((m = mbuf_alloc()) == NULL)
+        return;
+    m->m_data += if_maxlinkhdr;
+    rbp = (struct bootp_t *)m->m_data;
+    m->m_data += sizeof(struct udpiphdr);
+    memset(rbp, 0, sizeof(struct bootp_t));
+
+    if (dhcp_msg_type == DHCPDISCOVER) {
+    new_addr:
+        bc = get_new_addr(&daddr);
+        if (!bc) {
+            dprintf("no address left\n");
+            return;
+        }
+        memcpy(bc->macaddr, client_ethaddr, 6);
+    } else {
+        bc = find_addr(&daddr, bp->bp_hwaddr);
+        if (!bc) {
+            /* if never assigned, behaves as if it was already
+               assigned (windows fix because it remembers its address) */
+            goto new_addr;
+        }
+    }
+    if (bootp_filename)
+        snprintf((char*)rbp->bp_file, sizeof(rbp->bp_file), "%s", bootp_filename);
+
+    dprintf("offered addr=%s\n", sock_address_to_string(&daddr));
+
+    sock_address_init_inet( &saddr, special_addr_ip | CTL_ALIAS,
+                            BOOTP_SERVER );
+
+    rbp->bp_op = BOOTP_REPLY;
+    rbp->bp_xid = bp->bp_xid;
+    rbp->bp_htype = 1;
+    rbp->bp_hlen = 6;
+    memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+
+    rbp->bp_yiaddr = htonl(sock_address_get_ip(&daddr)); /* Client IP address */
+    rbp->bp_siaddr = htonl(sock_address_get_ip(&saddr)); /* Server IP address */
+
+    q = rbp->bp_vend;
+    memcpy(q, rfc1533_cookie, 4);
+    q += 4;
+
+    if (dhcp_msg_type == DHCPDISCOVER) {
+        *q++ = RFC2132_MSG_TYPE;
+        *q++ = 1;
+        *q++ = DHCPOFFER;
+    } else if (dhcp_msg_type == DHCPREQUEST) {
+        *q++ = RFC2132_MSG_TYPE;
+        *q++ = 1;
+        *q++ = DHCPACK;
+    }
+
+    if (dhcp_msg_type == DHCPDISCOVER ||
+        dhcp_msg_type == DHCPREQUEST) {
+        uint32_t  saddr_ip = htonl(sock_address_get_ip(&saddr));
+        *q++ = RFC2132_SRV_ID;
+        *q++ = 4;
+        memcpy(q, &saddr_ip, 4);
+        q += 4;
+
+        *q++ = RFC1533_NETMASK;
+        *q++ = 4;
+        *q++ = 0xff;
+        *q++ = 0xff;
+        *q++ = 0xff;
+        *q++ = 0x00;
+
+        *q++ = RFC1533_GATEWAY;
+        *q++ = 4;
+        memcpy(q, &saddr_ip, 4);
+        q += 4;
+
+        *q++ = RFC1533_DNS;
+        *q++ = 4;
+        dns_addr = htonl(special_addr_ip | CTL_DNS);
+        memcpy(q, &dns_addr, 4);
+        q += 4;
+
+        *q++ = RFC2132_LEASE_TIME;
+        *q++ = 4;
+        val = htonl(LEASE_TIME);
+        memcpy(q, &val, 4);
+        q += 4;
+
+        if (*slirp_hostname) {
+            val = strlen(slirp_hostname);
+            *q++ = RFC1533_HOSTNAME;
+            *q++ = val;
+            memcpy(q, slirp_hostname, val);
+            q += val;
+        }
+    }
+    *q++ = RFC1533_END;
+    
+    m->m_len = sizeof(struct bootp_t) - 
+        sizeof(struct ip) - sizeof(struct udphdr);
+
+    udp_output2_(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+}
+
+void bootp_input(MBuf m)
+{
+    struct bootp_t *bp = MBUF_TO(m, struct bootp_t *);
+
+    if (bp->bp_op == BOOTP_REQUEST) {
+        bootp_reply(bp);
+    }
+}
diff --git a/slirp2/bootp.h b/slirp2/bootp.h
new file mode 100644
index 0000000..fbc96ac
--- /dev/null
+++ b/slirp2/bootp.h
@@ -0,0 +1,113 @@
+/* bootp/dhcp defines */
+
+#define BOOTP_SERVER	67
+#define BOOTP_CLIENT	68
+
+#define BOOTP_REQUEST	1
+#define BOOTP_REPLY	2
+
+#define RFC1533_COOKIE		99, 130, 83, 99
+#define RFC1533_PAD		0
+#define RFC1533_NETMASK		1
+#define RFC1533_TIMEOFFSET	2
+#define RFC1533_GATEWAY		3
+#define RFC1533_TIMESERVER	4
+#define RFC1533_IEN116NS	5
+#define RFC1533_DNS		6
+#define RFC1533_LOGSERVER	7
+#define RFC1533_COOKIESERVER	8
+#define RFC1533_LPRSERVER	9
+#define RFC1533_IMPRESSSERVER	10
+#define RFC1533_RESOURCESERVER	11
+#define RFC1533_HOSTNAME	12
+#define RFC1533_BOOTFILESIZE	13
+#define RFC1533_MERITDUMPFILE	14
+#define RFC1533_DOMAINNAME	15
+#define RFC1533_SWAPSERVER	16
+#define RFC1533_ROOTPATH	17
+#define RFC1533_EXTENSIONPATH	18
+#define RFC1533_IPFORWARDING	19
+#define RFC1533_IPSOURCEROUTING	20
+#define RFC1533_IPPOLICYFILTER	21
+#define RFC1533_IPMAXREASSEMBLY	22
+#define RFC1533_IPTTL		23
+#define RFC1533_IPMTU		24
+#define RFC1533_IPMTUPLATEAU	25
+#define RFC1533_INTMTU		26
+#define RFC1533_INTLOCALSUBNETS	27
+#define RFC1533_INTBROADCAST	28
+#define RFC1533_INTICMPDISCOVER	29
+#define RFC1533_INTICMPRESPOND	30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT	32
+#define RFC1533_INTSTATICROUTES	33
+#define RFC1533_LLTRAILERENCAP	34
+#define RFC1533_LLARPCACHETMO	35
+#define RFC1533_LLETHERNETENCAP	36
+#define RFC1533_TCPTTL		37
+#define RFC1533_TCPKEEPALIVETMO	38
+#define RFC1533_TCPKEEPALIVEGB	39
+#define RFC1533_NISDOMAIN	40
+#define RFC1533_NISSERVER	41
+#define RFC1533_NTPSERVER	42
+#define RFC1533_VENDOR		43
+#define RFC1533_NBNS		44
+#define RFC1533_NBDD		45
+#define RFC1533_NBNT		46
+#define RFC1533_NBSCOPE		47
+#define RFC1533_XFS		48
+#define RFC1533_XDM		49
+
+#define RFC2132_REQ_ADDR	50
+#define RFC2132_LEASE_TIME      51
+#define RFC2132_MSG_TYPE	53
+#define RFC2132_SRV_ID		54
+#define RFC2132_PARAM_LIST	55
+#define RFC2132_MAX_SIZE	57
+#define RFC2132_RENEWAL_TIME    58
+#define RFC2132_REBIND_TIME     59
+
+#define DHCPDISCOVER		1
+#define DHCPOFFER		2
+#define DHCPREQUEST		3
+#define DHCPACK			5
+
+#define RFC1533_VENDOR_MAJOR	0
+#define RFC1533_VENDOR_MINOR	0
+
+#define RFC1533_VENDOR_MAGIC	128
+#define RFC1533_VENDOR_ADDPARM	129
+#define	RFC1533_VENDOR_ETHDEV	130
+#define RFC1533_VENDOR_HOWTO    132
+#define RFC1533_VENDOR_MNUOPTS	160
+#define RFC1533_VENDOR_SELECTION 176
+#define RFC1533_VENDOR_MOTD	184
+#define RFC1533_VENDOR_NUMOFMOTD 8
+#define RFC1533_VENDOR_IMG	192
+#define RFC1533_VENDOR_NUMOFIMG	16
+
+#define RFC1533_END		255
+#define BOOTP_VENDOR_LEN	64
+#define DHCP_OPT_LEN		312
+
+struct bootp_t {
+    struct ip ip;
+    struct udphdr udp;
+    uint8_t bp_op;
+    uint8_t bp_htype;
+    uint8_t bp_hlen;
+    uint8_t bp_hops;
+    uint32_t bp_xid;
+    uint16_t bp_secs;
+    uint16_t unused;
+    uint32_t bp_ciaddr;
+    uint32_t bp_yiaddr;
+    uint32_t bp_siaddr;
+    uint32_t bp_giaddr;
+    uint8_t bp_hwaddr[16];
+    uint8_t bp_sname[64];
+    uint8_t bp_file[128];
+    uint8_t bp_vend[DHCP_OPT_LEN];
+};
+
+void bootp_input(MBuf m);
diff --git a/slirp2/cksum.c b/slirp2/cksum.c
new file mode 100644
index 0000000..9474b9e
--- /dev/null
+++ b/slirp2/cksum.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
+ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
+ */
+
+#include <slirp.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ * 
+ * XXX Since we will never span more than 1 mbuf, we can optimise this
+ */
+
+#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+int cksum(MBuf m, int len)
+{
+	register u_int16_t *w;
+	register int sum = 0;
+	register int mlen = 0;
+	int byte_swapped = 0;
+
+	union {
+		u_int8_t	c[2];
+		u_int16_t	s;
+	} s_util;
+	union {
+		u_int16_t s[2];
+		u_int32_t l;
+	} l_util;
+	
+	if (m->m_len == 0)
+	   goto cont;
+	w = MBUF_TO(m, u_int16_t *);
+	
+	mlen = m->m_len;
+	
+	if (len < mlen)
+	   mlen = len;
+	len -= mlen;
+	/*
+	 * Force to even boundary.
+	 */
+	if ((1 & (long) w) && (mlen > 0)) {
+		REDUCE;
+		sum <<= 8;
+		s_util.c[0] = *(u_int8_t *)w;
+		w = (u_int16_t *)((int8_t *)w + 1);
+		mlen--;
+		byte_swapped = 1;
+	}
+	/*
+	 * Unroll the loop to make overhead from
+	 * branches &c small.
+	 */
+	while ((mlen -= 32) >= 0) {
+		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+		sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+		sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+		sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+		w += 16;
+	}
+	mlen += 32;
+	while ((mlen -= 8) >= 0) {
+		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+		w += 4;
+	}
+	mlen += 8;
+	if (mlen == 0 && byte_swapped == 0)
+	   goto cont;
+	REDUCE;
+	while ((mlen -= 2) >= 0) {
+		sum += *w++;
+	}
+	
+	if (byte_swapped) {
+		REDUCE;
+		sum <<= 8;
+		byte_swapped = 0;
+		if (mlen == -1) {
+			s_util.c[1] = *(u_int8_t *)w;
+			sum += s_util.s;
+			mlen = 0;
+		} else
+		   
+		   mlen = -1;
+	} else if (mlen == -1)
+	   s_util.c[0] = *(u_int8_t *)w;
+	
+cont:
+#ifdef DEBUG
+	if (len) {
+		DEBUG_ERROR((dfd, "cksum: out of data\n"));
+		DEBUG_ERROR((dfd, " len = %d\n", len));
+	}
+#endif
+	if (mlen == -1) {
+		/* The last mbuf has odd # of bytes. Follow the
+		 standard (the odd byte may be shifted left by 8 bits
+			   or not as determined by endian-ness of the machine) */
+		s_util.c[1] = 0;
+		sum += s_util.s;
+	}
+	REDUCE;
+	return (~sum & 0xffff);
+}
diff --git a/slirp2/ctl.h b/slirp2/ctl.h
new file mode 100644
index 0000000..854ae9a
--- /dev/null
+++ b/slirp2/ctl.h
@@ -0,0 +1,10 @@
+#define CTL_CMD		0
+#define CTL_EXEC	1
+#define CTL_ALIAS	2
+#define CTL_DNS		3
+/* NOTE: DNS_ADDR_MAX addresses, starting from CTL_DNS, are reserved */
+
+#define CTL_IS_DNS(x)   ((unsigned)((x)-CTL_DNS) < (unsigned)dns_addr_count)
+
+#define CTL_SPECIAL	"10.0.2.0"
+#define CTL_LOCAL	"10.0.2.15"
diff --git a/slirp2/debug.c b/slirp2/debug.c
new file mode 100644
index 0000000..f3a424d
--- /dev/null
+++ b/slirp2/debug.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * Portions copyright (c) 2000 Kelly Price.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+FILE *dfd = NULL;
+#ifdef DEBUG
+int dostats = 1;
+#else
+int dostats = 0;
+#endif
+int slirp_debug = 0;
+
+extern char *strerror _P((int));
+
+/* Carry over one item from main.c so that the tty's restored. 
+ * Only done when the tty being used is /dev/tty --RedWolf */
+extern struct termios slirp_tty_settings;
+extern int slirp_tty_restore;
+
+
+void
+debug_init(file, dbg)
+	char *file;
+	int dbg;
+{
+	/* Close the old debugging file */
+	if (dfd)
+	   fclose(dfd);
+	
+	dfd = fopen(file,"w");
+	if (dfd != NULL) {
+#if 0
+		fprintf(dfd,"Slirp %s - Debugging Started.\n", SLIRP_VERSION);
+#endif
+		fprintf(dfd,"Debugging Started level %i.\r\n",dbg);
+		fflush(dfd);
+		slirp_debug = dbg;
+	} else {
+		fprintf(stderr, "Error: Debugging file \"%s\" could not be opened: %s\r\n",
+			file, strerror(errno));
+	}
+}
+
+/*
+ * Dump a packet in the same format as tcpdump -x
+ */
+#ifdef DEBUG
+void
+dump_packet(dat, n)
+	void *dat;
+	int n;
+{
+	u_char *pptr = (u_char *)dat;
+	int j,k;
+	
+	n /= 16;
+	n++;
+	DEBUG_MISC((dfd, "PACKET DUMPED: \n"));
+	for(j = 0; j < n; j++) {
+		for(k = 0; k < 6; k++)
+			DEBUG_MISC((dfd, "%02x ", *pptr++));
+		DEBUG_MISC((dfd, "\n"));
+		fflush(dfd);
+	}
+}
+#endif
+
+#if 0
+/*
+ * Statistic routines
+ * 
+ * These will print statistics to the screen, the debug file (dfd), or
+ * a buffer, depending on "type", so that the stats can be sent over
+ * the link as well.
+ */
+
+void
+ttystats(ttyp)
+	struct ttys *ttyp;
+{
+	struct slirp_ifstats *is = &ttyp->ifstats;
+	char buff[512];
+	
+	lprint(" \r\n");
+	
+	if (if_comp & IF_COMPRESS)
+	   strcpy(buff, "on");
+	else if (if_comp & IF_NOCOMPRESS)
+	   strcpy(buff, "off");
+	else
+	   strcpy(buff, "off (for now)");
+	lprint("Unit %d:\r\n", ttyp->unit);
+	lprint("  using %s encapsulation (VJ compression is %s)\r\n", (
+#ifdef USE_PPP
+	       ttyp->proto==PROTO_PPP?"PPP":
+#endif
+	       "SLIP"), buff);
+	lprint("  %d baudrate\r\n", ttyp->baud);
+	lprint("  interface is %s\r\n", ttyp->up?"up":"down");
+	lprint("  using fd %d, guardian pid is %d\r\n", ttyp->fd, ttyp->pid);
+#ifndef FULL_BOLT
+	lprint("  towrite is %d bytes\r\n", ttyp->towrite);
+#endif
+	if (ttyp->zeros)
+	   lprint("  %d zeros have been typed\r\n", ttyp->zeros);
+	else if (ttyp->ones)
+	   lprint("  %d ones have been typed\r\n", ttyp->ones);
+	lprint("Interface stats:\r\n");
+	lprint("  %6d output packets sent (%d bytes)\r\n", is->out_pkts, is->out_bytes);
+	lprint("  %6d output packets dropped (%d bytes)\r\n", is->out_errpkts, is->out_errbytes);
+	lprint("  %6d input packets received (%d bytes)\r\n", is->in_pkts, is->in_bytes);
+	lprint("  %6d input packets dropped (%d bytes)\r\n", is->in_errpkts, is->in_errbytes);
+	lprint("  %6d bad input packets\r\n", is->in_mbad);
+}
+
+void
+allttystats()
+{
+	struct ttys *ttyp;
+	
+	for (ttyp = ttys; ttyp; ttyp = ttyp->next)
+	   ttystats(ttyp);
+}
+#endif
+
+void
+ipstats()
+{
+	lprint(" \r\n");	
+
+	lprint("IP stats:\r\n");
+	lprint("  %6d total packets received (%d were unaligned)\r\n",
+			ipstat.ips_total, ipstat.ips_unaligned);
+	lprint("  %6d with incorrect version\r\n", ipstat.ips_badvers);
+	lprint("  %6d with bad header checksum\r\n", ipstat.ips_badsum);
+	lprint("  %6d with length too short (len < sizeof(iphdr))\r\n", ipstat.ips_tooshort);
+	lprint("  %6d with length too small (len < ip->len)\r\n", ipstat.ips_toosmall);
+	lprint("  %6d with bad header length\r\n", ipstat.ips_badhlen);
+	lprint("  %6d with bad packet length\r\n", ipstat.ips_badlen);
+	lprint("  %6d fragments received\r\n", ipstat.ips_fragments);
+	lprint("  %6d fragments dropped\r\n", ipstat.ips_fragdropped);
+	lprint("  %6d fragments timed out\r\n", ipstat.ips_fragtimeout);
+	lprint("  %6d packets reassembled ok\r\n", ipstat.ips_reassembled);
+	lprint("  %6d outgoing packets fragmented\r\n", ipstat.ips_fragmented);
+	lprint("  %6d total outgoing fragments\r\n", ipstat.ips_ofragments);
+	lprint("  %6d with bad protocol field\r\n", ipstat.ips_noproto);
+	lprint("  %6d total packets delivered\r\n", ipstat.ips_delivered);
+}
+
+#if 0
+void
+vjstats()
+{
+	lprint(" \r\n");
+	
+	lprint("VJ compression stats:\r\n");
+	
+	lprint("  %6d outbound packets (%d compressed)\r\n",
+	       comp_s.sls_packets, comp_s.sls_compressed);
+	lprint("  %6d searches for connection stats (%d misses)\r\n",
+	       comp_s.sls_searches, comp_s.sls_misses);
+	lprint("  %6d inbound uncompressed packets\r\n", comp_s.sls_uncompressedin);
+	lprint("  %6d inbound compressed packets\r\n", comp_s.sls_compressedin);
+	lprint("  %6d inbound unknown type packets\r\n", comp_s.sls_errorin);
+	lprint("  %6d inbound packets tossed due to error\r\n", comp_s.sls_tossed);
+}
+#endif
+
+void
+tcpstats()
+{
+	lprint(" \r\n");
+
+	lprint("TCP stats:\r\n");
+	
+	lprint("  %6d packets sent\r\n", tcpstat.tcps_sndtotal);
+	lprint("          %6d data packets (%d bytes)\r\n",
+			tcpstat.tcps_sndpack, tcpstat.tcps_sndbyte);
+	lprint("          %6d data packets retransmitted (%d bytes)\r\n",
+			tcpstat.tcps_sndrexmitpack, tcpstat.tcps_sndrexmitbyte);
+	lprint("          %6d ack-only packets (%d delayed)\r\n",
+			tcpstat.tcps_sndacks, tcpstat.tcps_delack);
+	lprint("          %6d URG only packets\r\n", tcpstat.tcps_sndurg);
+	lprint("          %6d window probe packets\r\n", tcpstat.tcps_sndprobe);
+	lprint("          %6d window update packets\r\n", tcpstat.tcps_sndwinup);
+	lprint("          %6d control (SYN/FIN/RST) packets\r\n", tcpstat.tcps_sndctrl);
+	lprint("          %6d times tcp_output did nothing\r\n", tcpstat.tcps_didnuttin);
+	
+	lprint("  %6d packets received\r\n", tcpstat.tcps_rcvtotal);       
+	lprint("          %6d acks (for %d bytes)\r\n",
+			tcpstat.tcps_rcvackpack, tcpstat.tcps_rcvackbyte);
+	lprint("          %6d duplicate acks\r\n", tcpstat.tcps_rcvdupack);
+	lprint("          %6d acks for unsent data\r\n", tcpstat.tcps_rcvacktoomuch);
+	lprint("          %6d packets received in sequence (%d bytes)\r\n",
+			tcpstat.tcps_rcvpack, tcpstat.tcps_rcvbyte);
+        lprint("          %6d completely duplicate packets (%d bytes)\r\n",
+			tcpstat.tcps_rcvduppack, tcpstat.tcps_rcvdupbyte);
+	
+	lprint("          %6d packets with some duplicate data (%d bytes duped)\r\n",
+			tcpstat.tcps_rcvpartduppack, tcpstat.tcps_rcvpartdupbyte);
+	lprint("          %6d out-of-order packets (%d bytes)\r\n",
+			tcpstat.tcps_rcvoopack, tcpstat.tcps_rcvoobyte);
+	lprint("          %6d packets of data after window (%d bytes)\r\n",
+			tcpstat.tcps_rcvpackafterwin, tcpstat.tcps_rcvbyteafterwin);
+	lprint("          %6d window probes\r\n", tcpstat.tcps_rcvwinprobe);
+	lprint("          %6d window update packets\r\n", tcpstat.tcps_rcvwinupd);
+	lprint("          %6d packets received after close\r\n", tcpstat.tcps_rcvafterclose);
+	lprint("          %6d discarded for bad checksums\r\n", tcpstat.tcps_rcvbadsum);
+	lprint("          %6d discarded for bad header offset fields\r\n",
+			tcpstat.tcps_rcvbadoff);
+	
+	lprint("  %6d connection requests\r\n", tcpstat.tcps_connattempt);
+	lprint("  %6d connection accepts\r\n", tcpstat.tcps_accepts);
+	lprint("  %6d connections established (including accepts)\r\n", tcpstat.tcps_connects);
+	lprint("  %6d connections closed (including %d drop)\r\n",
+			tcpstat.tcps_closed, tcpstat.tcps_drops);
+	lprint("  %6d embryonic connections dropped\r\n", tcpstat.tcps_conndrops);
+	lprint("  %6d segments we tried to get rtt (%d succeeded)\r\n",
+			tcpstat.tcps_segstimed, tcpstat.tcps_rttupdated);
+	lprint("  %6d retransmit timeouts\r\n", tcpstat.tcps_rexmttimeo);
+	lprint("          %6d connections dropped by rxmt timeout\r\n",
+			tcpstat.tcps_timeoutdrop);
+	lprint("  %6d persist timeouts\r\n", tcpstat.tcps_persisttimeo);
+	lprint("  %6d keepalive timeouts\r\n", tcpstat.tcps_keeptimeo);
+	lprint("          %6d keepalive probes sent\r\n", tcpstat.tcps_keepprobe);
+	lprint("          %6d connections dropped by keepalive\r\n", tcpstat.tcps_keepdrops);
+	lprint("  %6d correct ACK header predictions\r\n", tcpstat.tcps_predack);
+	lprint("  %6d correct data packet header predictions\n", tcpstat.tcps_preddat);
+	lprint("  %6d TCP cache misses\r\n", tcpstat.tcps_socachemiss);
+	
+	
+/*	lprint("    Packets received too short:		%d\r\n", tcpstat.tcps_rcvshort); */
+/*	lprint("    Segments dropped due to PAWS:	%d\r\n", tcpstat.tcps_pawsdrop); */
+
+}
+
+void
+udpstats()
+{
+        lprint(" \r\n");
+
+	lprint("UDP stats:\r\n");
+	lprint("  %6d datagrams received\r\n", udpstat.udps_ipackets);
+	lprint("  %6d with packets shorter than header\r\n", udpstat.udps_hdrops);
+	lprint("  %6d with bad checksums\r\n", udpstat.udps_badsum);
+	lprint("  %6d with data length larger than packet\r\n", udpstat.udps_badlen);
+	lprint("  %6d UDP socket cache misses\r\n", udpstat.udpps_pcbcachemiss);
+	lprint("  %6d datagrams sent\r\n", udpstat.udps_opackets);
+}
+
+void
+icmpstats()
+{
+	lprint(" \r\n");
+	lprint("ICMP stats:\r\n");
+	lprint("  %6d ICMP packets received\r\n", icmpstat.icps_received);
+	lprint("  %6d were too short\r\n", icmpstat.icps_tooshort);
+	lprint("  %6d with bad checksums\r\n", icmpstat.icps_checksum);
+	lprint("  %6d with type not supported\r\n", icmpstat.icps_notsupp);
+	lprint("  %6d with bad type feilds\r\n", icmpstat.icps_badtype);
+	lprint("  %6d ICMP packets sent in reply\r\n", icmpstat.icps_reflect);
+}
+
+void
+sockstats()
+{
+	char buff[256];
+	int n;
+	struct socket *so;
+
+        lprint(" \r\n");
+	
+	lprint(
+	   "Proto[state]     Sock     Local Address, Port  Remote Address, Port RecvQ SendQ\r\n");
+			
+	for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+		
+		n = sprintf(buff, "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE");
+		while (n < 17)
+		   buff[n++] = ' ';
+		buff[17] = 0;
+		lprint("%s %3d   %15s %5d ",
+				buff, so->s,
+				inet_iptostr(so->so_laddr_ip), so->so_laddr_port);
+		lprint("%15s %5d %5d %5d\r\n",
+				inet_iptostr(so->so_faddr_ip), so->so_faddr_port,
+				so->so_rcv.sb_cc, so->so_snd.sb_cc);
+	}
+		   
+	for (so = udb.so_next; so != &udb; so = so->so_next) {
+		
+		n = sprintf(buff, "udp[%d sec]", (so->so_expire - curtime) / 1000);
+		while (n < 17)
+		   buff[n++] = ' ';
+		buff[17] = 0;
+		lprint("%s %3d  %15s %5d  ",
+				buff, so->s,
+				inet_iptostr(so->so_laddr_ip), so->so_laddr_port);
+		lprint("%15s %5d %5d %5d\r\n",
+				inet_iptostr(so->so_faddr_ip), so->so_faddr_port,
+				so->so_rcv.sb_cc, so->so_snd.sb_cc);
+	}
+}
+
+#if 0
+void
+slirp_exit(exit_status)
+	int exit_status;
+{
+	struct ttys *ttyp;
+	
+	DEBUG_CALL("slirp_exit");
+	DEBUG_ARG("exit_status = %d", exit_status);
+
+	if (dostats) {
+		lprint_print = (int (*) _P((void *, const char *, va_list)))vfprintf;
+		if (!dfd)
+		   debug_init("slirp_stats", 0xf);
+		lprint_arg = (char **)&dfd;
+		
+		ipstats();
+		tcpstats();
+		udpstats();
+		icmpstats();
+		mbufstats();
+		sockstats();
+		allttystats();
+		vjstats();
+	}
+	
+	for (ttyp = ttys; ttyp; ttyp = ttyp->next)
+	   tty_detached(ttyp, 1);
+	
+	if (slirp_forked) {
+		/* Menendez time */
+		if (kill(getppid(), SIGQUIT) < 0)
+			lprint("Couldn't kill parent process %ld!\n",
+			    (long) getppid());
+    	}
+	
+	/* Restore the terminal if we gotta */
+	if(slirp_tty_restore)
+	  tcsetattr(0,TCSANOW, &slirp_tty_settings);  /* NOW DAMMIT! */
+	exit(exit_status);
+}
+#endif
diff --git a/slirp2/debug.h b/slirp2/debug.h
new file mode 100644
index 0000000..6e8444d
--- /dev/null
+++ b/slirp2/debug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#define PRN_STDERR	1
+#define PRN_SPRINTF	2
+
+extern FILE *dfd;
+extern FILE *lfd;
+extern int dostats;
+extern int slirp_debug;
+
+#define DBG_CALL 0x1
+#define DBG_MISC 0x2
+#define DBG_ERROR 0x4
+#define DEBUG_DEFAULT DBG_CALL|DBG_MISC|DBG_ERROR
+
+#ifdef DEBUG
+#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); }
+#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); }
+#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); }
+#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); }
+#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); }
+
+
+#else
+
+#define DEBUG_CALL(x)
+#define DEBUG_ARG(x, y)
+#define DEBUG_ARGS(x)
+#define DEBUG_MISC(x)
+#define DEBUG_ERROR(x)
+
+#endif
+
+void debug_init _P((char *, int));
+//void ttystats _P((struct ttys *));
+void allttystats _P((void));
+void ipstats _P((void));
+void vjstats _P((void));
+void tcpstats _P((void));
+void udpstats _P((void));
+void icmpstats _P((void));
+void mbufstats _P((void));
+void sockstats _P((void));
+void slirp_exit _P((int));
+
diff --git a/slirp2/helper.h b/slirp2/helper.h
new file mode 100644
index 0000000..e247f46
--- /dev/null
+++ b/slirp2/helper.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef _SLIRP_HELPER_H
+#define _SLIRP_HELPER_H
+
+typedef union {
+    u_int32_t   addr;
+    u_int8_t    data[4];
+} ipaddr_t;
+
+/* return ip address in network order */
+static __inline__ uint32_t
+ip_getn( ipaddr_t  ip )
+{
+    return ip.addr;
+}
+
+/* return ip address in host order */
+static __inline__ uint32_t
+ip_geth( ipaddr_t  ip )
+{
+    return ntohl(ip.addr);
+}
+
+/* set ip address in network order */
+static __inline__ ipaddr_t
+ip_setn( uint32_t  val )
+{
+    ipaddr_t  ip;
+    ip.addr = val;
+    return ip;
+}
+
+/* set ip address in host order */
+static __inline__ ipaddr_t
+ip_seth( uint32_t  val )
+{
+    ipaddr_t  ip;
+    ip.addr = htonl(val);
+    return ip;
+}
+
+static __inline__ int
+ip_equal( ipaddr_t  ip1, ipaddr_t  ip2 )
+{
+    return ip1.addr == ip2.addr;
+}
+
+typedef union {
+    u_int16_t  port;
+    u_int8_t   data[2];
+} port_t;
+
+static __inline__ uint16_t
+port_getn( port_t   p )
+{
+    return p.port;
+}
+
+static __inline__ uint16_t
+port_geth( port_t  p )
+{
+    return ntohs(p.port);
+}
+
+static __inline__ port_t
+port_setn( uint16_t  val )
+{
+    port_t  p;
+    p.port = val;
+    return p;
+}
+
+static __inline__ port_t
+port_seth( uint16_t  val )
+{
+    port_t  p;
+    p.port = htons(val);
+    return p;
+}
+
+#endif /* _SLIRP_HELPER_H */
diff --git a/slirp2/icmp_var.h b/slirp2/icmp_var.h
new file mode 100644
index 0000000..03fc8c3
--- /dev/null
+++ b/slirp2/icmp_var.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)icmp_var.h	8.1 (Berkeley) 6/10/93
+ * icmp_var.h,v 1.4 1995/02/16 00:27:40 wollman Exp
+ */
+
+#ifndef _NETINET_ICMP_VAR_H_
+#define _NETINET_ICMP_VAR_H_
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol.
+ */
+struct icmpstat {
+/* statistics related to input messages processed */
+	u_long  icps_received;		/* #ICMP packets received */
+	u_long	icps_tooshort;		/* packet < ICMP_MINLEN */
+	u_long	icps_checksum;		/* bad checksum */
+	u_long	icps_notsupp;		/* #ICMP packets not supported */
+	u_long  icps_badtype;		/* #with bad type feild */
+	u_long	icps_reflect;		/* number of responses */
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define	ICMPCTL_MASKREPL	1	/* allow replies to netmask requests */
+#define	ICMPCTL_STATS		2	/* statistics (read-only) */
+#define ICMPCTL_MAXID		3
+
+#define ICMPCTL_NAMES { \
+	{ 0, 0 }, \
+	{ "maskrepl", CTLTYPE_INT }, \
+	{ "stats", CTLTYPE_STRUCT }, \
+}
+
+extern struct icmpstat icmpstat;
+
+#endif
diff --git a/slirp2/if.c b/slirp2/if.c
new file mode 100644
index 0000000..cd96e65
--- /dev/null
+++ b/slirp2/if.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+int if_mtu, if_mru;
+int if_comp;
+int if_maxlinkhdr;
+int     if_queued = 0;                  /* Number of packets queued so far */
+int     if_thresh = 10;                 /* Number of packets queued before we start sending
+					 * (to prevent allocing too many mbufs) */
+
+struct  mbuf if_fastq;                  /* fast queue (for interactive data) */
+struct  mbuf if_batchq;                 /* queue for non-interactive data */
+struct	mbuf *next_m;			/* Pointer to next mbuf to output */
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+void
+ifs_insque(ifm, ifmhead)
+	MBuf ifm, ifmhead;
+{
+	ifm->ifs_next = ifmhead->ifs_next;
+	ifmhead->ifs_next = ifm;
+	ifm->ifs_prev = ifmhead;
+	ifm->ifs_next->ifs_prev = ifm;
+}
+
+void
+ifs_remque(ifm)
+	MBuf ifm;
+{
+	ifm->ifs_prev->ifs_next = ifm->ifs_next;
+	ifm->ifs_next->ifs_prev = ifm->ifs_prev;
+}
+
+void
+if_init()
+{
+#if 0
+	/*
+	 * Set if_maxlinkhdr to 48 because it's 40 bytes for TCP/IP,
+	 * and 8 bytes for PPP, but need to have it on an 8byte boundary
+	 */
+#ifdef USE_PPP
+	if_maxlinkhdr = 48;
+#else
+	if_maxlinkhdr = 40;
+#endif
+#else
+        /* 2 for alignment, 14 for ethernet, 40 for TCP/IP */
+        if_maxlinkhdr = 2 + 14 + 40;
+#endif
+	if_mtu = 1500;
+	if_mru = 1500;
+	if_comp = IF_AUTOCOMP;
+	if_fastq.ifq_next = if_fastq.ifq_prev = &if_fastq;
+	if_batchq.ifq_next = if_batchq.ifq_prev = &if_batchq;
+        //	sl_compress_init(&comp_s);
+	next_m = &if_batchq;
+}
+
+#if 0
+/*
+ * This shouldn't be needed since the modem is blocking and
+ * we don't expect any signals, but what the hell..
+ */
+inline int
+writen(fd, bptr, n)
+	int fd;
+	char *bptr;
+	int n;
+{
+	int ret;
+	int total;
+	
+	/* This should succeed most of the time */
+	ret = socket_send(fd, bptr, n);
+	if (ret == n || ret <= 0)
+	   return ret;
+	
+	/* Didn't write everything, go into the loop */
+	total = ret;
+	while (n > total) {
+		ret = socket_send(fd, bptr+total, n-total);
+		if (ret <= 0)
+		   return ret;
+		total += ret;
+	}
+	return total;
+}
+
+/*
+ * if_input - read() the tty, do "top level" processing (ie: check for any escapes),
+ * and pass onto (*ttyp->if_input)
+ * 
+ * XXXXX Any zeros arriving by themselves are NOT placed into the arriving packet.
+ */
+#define INBUFF_SIZE 2048 /* XXX */
+void
+if_input(ttyp)
+	struct ttys *ttyp;
+{
+	u_char if_inbuff[INBUFF_SIZE];
+	int if_n;
+	
+	DEBUG_CALL("if_input");
+	DEBUG_ARG("ttyp = %lx", (long)ttyp);
+	
+	if_n = socket_recv(ttyp->fd, (char *)if_inbuff, INBUFF_SIZE);
+	
+	DEBUG_MISC((dfd, " read %d bytes\n", if_n));
+	
+	if (if_n <= 0) {
+		if (if_n == 0 || (errno != EINTR && errno != EAGAIN)) {
+			if (ttyp->up)
+			   link_up--;
+			tty_detached(ttyp, 0);
+		}
+		return;
+	}
+	if (if_n == 1) {
+		if (*if_inbuff == '0') {
+			ttyp->ones = 0;
+			if (++ttyp->zeros >= 5)
+			   slirp_exit(0);
+			return;
+		}
+		if (*if_inbuff == '1') {
+			ttyp->zeros = 0;
+			if (++ttyp->ones >= 5)
+			   tty_detached(ttyp, 0);
+			return;
+		}
+	}
+	ttyp->ones = ttyp->zeros = 0;
+	
+	(*ttyp->if_input)(ttyp, if_inbuff, if_n);
+}
+#endif	
+	
+/*
+ * if_output: Queue packet into an output queue.
+ * There are 2 output queue's, if_fastq and if_batchq. 
+ * Each output queue is a doubly linked list of double linked lists
+ * of mbufs, each list belonging to one "session" (socket).  This
+ * way, we can output packets fairly by sending one packet from each
+ * session, instead of all the packets from one session, then all packets
+ * from the next session, etc.  Packets on the if_fastq get absolute 
+ * priority, but if one session hogs the link, it gets "downgraded"
+ * to the batchq until it runs out of packets, then it'll return
+ * to the fastq (eg. if the user does an ls -alR in a telnet session,
+ * it'll temporarily get downgraded to the batchq)
+ */
+void
+if_output(so, ifm)
+	struct socket *so;
+	MBuf ifm;
+{
+	MBuf ifq;
+	int on_fastq = 1;
+	
+	DEBUG_CALL("if_output");
+	DEBUG_ARG("so = %lx", (long)so);
+	DEBUG_ARG("ifm = %lx", (long)ifm);
+	
+	/*
+	 * First remove the mbuf from m_usedlist,
+	 * since we're gonna use m_next and m_prev ourselves
+	 * XXX Shouldn't need this, gotta change dtom() etc.
+	 */
+	if (ifm->m_flags & M_USEDLIST) {
+		remque(ifm);
+		ifm->m_flags &= ~M_USEDLIST;
+	}
+	
+	/*
+	 * See if there's already a batchq list for this session.  
+	 * This can include an interactive session, which should go on fastq,
+	 * but gets too greedy... hence it'll be downgraded from fastq to batchq.
+	 * We mustn't put this packet back on the fastq (or we'll send it out of order)
+	 * XXX add cache here?
+	 */
+	for (ifq = if_batchq.ifq_prev; ifq != &if_batchq; ifq = ifq->ifq_prev) {
+		if (so == ifq->ifq_so) {
+			/* A match! */
+			ifm->ifq_so = so;
+			ifs_insque(ifm, ifq->ifs_prev);
+			goto diddit;
+		}
+	}
+	
+	/* No match, check which queue to put it on */
+	if (so && (so->so_iptos & IPTOS_LOWDELAY)) {
+		ifq = if_fastq.ifq_prev;
+		on_fastq = 1;
+		/*
+		 * Check if this packet is a part of the last
+		 * packet's session
+		 */
+		if (ifq->ifq_so == so) {
+			ifm->ifq_so = so;
+			ifs_insque(ifm, ifq->ifs_prev);
+			goto diddit;
+		}
+	} else
+		ifq = if_batchq.ifq_prev;
+	
+	/* Create a new doubly linked list for this session */
+	ifm->ifq_so = so;
+	ifs_init(ifm);
+	insque(ifm, ifq);
+	
+diddit:
+	++if_queued;
+	
+	if (so) {
+		/* Update *_queued */
+		so->so_queued++;
+		so->so_nqueued++;
+		/*
+		 * Check if the interactive session should be downgraded to
+		 * the batchq.  A session is downgraded if it has queued 6
+		 * packets without pausing, and at least 3 of those packets
+		 * have been sent over the link
+		 * (XXX These are arbitrary numbers, probably not optimal..)
+		 */
+		if (on_fastq && ((so->so_nqueued >= 6) && 
+				 (so->so_nqueued - so->so_queued) >= 3)) {
+			
+			/* Remove from current queue... */
+			remque(ifm->ifs_next);
+			
+			/* ...And insert in the new.  That'll teach ya! */
+			insque(ifm->ifs_next, &if_batchq);
+		}
+	}
+
+#ifndef FULL_BOLT
+	/*
+	 * This prevents us from malloc()ing too many mbufs
+	 */
+	if (link_up) {
+		/* if_start will check towrite */
+		if_start();
+	}
+#endif
+}
+
+/*
+ * Send a packet
+ * We choose a packet based on it's position in the output queues;
+ * If there are packets on the fastq, they are sent FIFO, before
+ * everything else.  Otherwise we choose the first packet from the
+ * batchq and send it.  the next packet chosen will be from the session
+ * after this one, then the session after that one, and so on..  So,
+ * for example, if there are 3 ftp session's fighting for bandwidth,
+ * one packet will be sent from the first session, then one packet
+ * from the second session, then one packet from the third, then back
+ * to the first, etc. etc.
+ */
+void
+if_start(void)
+{
+	MBuf ifm, ifqt;
+	
+	DEBUG_CALL("if_start");
+	
+	if (if_queued == 0)
+	   return; /* Nothing to do */
+	
+ again:
+        /* check if we can really output */
+        if (!slirp_can_output())
+            return;
+
+	/*
+	 * See which queue to get next packet from
+	 * If there's something in the fastq, select it immediately
+	 */
+	if (if_fastq.ifq_next != &if_fastq) {
+		ifm = if_fastq.ifq_next;
+	} else {
+		/* Nothing on fastq, see if next_m is valid */
+		if (next_m != &if_batchq)
+		   ifm = next_m;
+		else
+		   ifm = if_batchq.ifq_next;
+		
+		/* Set which packet to send on next iteration */
+		next_m = ifm->ifq_next;
+	}
+	/* Remove it from the queue */
+	ifqt = ifm->ifq_prev;
+	remque(ifm);
+	--if_queued;
+	
+	/* If there are more packets for this session, re-queue them */
+	if (ifm->ifs_next != /* ifm->ifs_prev != */ ifm) {
+		insque(ifm->ifs_next, ifqt);
+		ifs_remque(ifm);
+	}
+	
+	/* Update so_queued */
+	if (ifm->ifq_so) {
+		if (--ifm->ifq_so->so_queued == 0)
+		   /* If there's no more queued, reset nqueued */
+		   ifm->ifq_so->so_nqueued = 0;
+	}
+	
+	/* Encapsulate the packet for sending */
+        if_encap(ifm->m_data, ifm->m_len);
+
+        mbuf_free(ifm);
+
+	if (if_queued)
+	   goto again;
+}
diff --git a/slirp2/if.h b/slirp2/if.h
new file mode 100644
index 0000000..f22f161
--- /dev/null
+++ b/slirp2/if.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IF_H_
+#define _IF_H_
+
+#define IF_COMPRESS	0x01	/* We want compression */
+#define IF_NOCOMPRESS	0x02	/* Do not do compression */
+#define IF_AUTOCOMP	0x04	/* Autodetect (default) */
+#define IF_NOCIDCOMP	0x08	/* CID compression */
+
+/* Needed for FreeBSD */
+#undef if_mtu
+extern int	if_mtu;
+extern int	if_mru;	/* MTU and MRU */
+extern int	if_comp;	/* Flags for compression */
+extern int	if_maxlinkhdr;
+extern int	if_queued;	/* Number of packets queued so far */
+extern int	if_thresh;	/* Number of packets queued before we start sending
+				 * (to prevent allocing too many mbufs) */
+
+extern	struct mbuf if_fastq;                  /* fast queue (for interactive data) */
+extern	struct mbuf if_batchq;                 /* queue for non-interactive data */
+extern	MBuf next_m;
+
+#define ifs_init(ifm) ((ifm)->ifs_next = (ifm)->ifs_prev = (ifm))
+
+/* Interface statistics */
+struct slirp_ifstats {
+	u_int out_pkts;		/* Output packets */
+	u_int out_bytes;		/* Output bytes */
+	u_int out_errpkts;	/* Output Error Packets */
+	u_int out_errbytes;	/* Output Error Bytes */
+	u_int in_pkts;		/* Input packets */
+	u_int in_bytes;		/* Input bytes */
+	u_int in_errpkts;		/* Input Error Packets */
+	u_int in_errbytes;	/* Input Error Bytes */
+	
+	u_int bytes_saved;	/* Number of bytes that compression "saved" */
+				/* ie: number of bytes that didn't need to be sent over the link
+				 * because of compression */
+	
+	u_int in_mbad;		/* Bad incoming packets */
+};
+
+#endif
diff --git a/slirp2/ip.h b/slirp2/ip.h
new file mode 100644
index 0000000..8cdb735
--- /dev/null
+++ b/slirp2/ip.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)ip.h	8.1 (Berkeley) 6/10/93
+ * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
+ */
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#include "helper.h"
+
+#ifdef WORDS_BIGENDIAN
+# ifndef NTOHL
+#  define NTOHL(d)
+# endif
+# ifndef NTOHS
+#  define NTOHS(d)
+# endif
+# ifndef HTONL
+#  define HTONL(d)
+# endif
+# ifndef HTONS
+#  define HTONS(d)
+# endif
+#else
+# ifndef NTOHL
+#  define NTOHL(d) ((d) = ntohl((d)))
+# endif
+# ifndef NTOHS
+#  define NTOHS(d) ((d) = ntohs((u_int16_t)(d)))
+# endif
+# ifndef HTONL
+#  define HTONL(d) ((d) = htonl((d)))
+# endif
+# ifndef HTONS
+#  define HTONS(d) ((d) = htons((u_int16_t)(d)))
+# endif
+#endif
+
+typedef u_int32_t n_long;                 /* long as received from the net */
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define	IPVERSION	4
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip {
+#ifdef WORDS_BIGENDIAN
+	u_int ip_v:4;		/* version */
+	u_int ip_hl:4;		/* header length */
+#else
+	u_int ip_hl:4;		/* header length */
+	u_int ip_v:4;		/* version */
+#endif
+	u_int8_t  ip_tos;			/* type of service */
+	u_int16_t	ip_len;			/* total length */
+	u_int16_t	ip_id;			/* identification */
+	u_int16_t	ip_off;			/* fragment offset field */
+#define	IP_DF 0x4000			/* don't fragment flag */
+#define	IP_MF 0x2000			/* more fragments flag */
+#define	IP_OFFMASK 0x1fff		/* mask for fragmenting bits */
+	u_int8_t ip_ttl;			/* time to live */
+	u_int8_t ip_p;			/* protocol */
+	u_int16_t	ip_sum;			/* checksum */
+        ipaddr_t        ip_src, ip_dst; /* source and dest address */
+};
+
+#define	IP_MAXPACKET	65535		/* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define	IPTOS_LOWDELAY		0x10
+#define	IPTOS_THROUGHPUT	0x08
+#define	IPTOS_RELIABILITY	0x04
+
+/*
+ * Definitions for options.
+ */
+#define	IPOPT_COPIED(o)		((o)&0x80)
+#define	IPOPT_CLASS(o)		((o)&0x60)
+#define	IPOPT_NUMBER(o)		((o)&0x1f)
+
+#define	IPOPT_CONTROL		0x00
+#define	IPOPT_RESERVED1		0x20
+#define	IPOPT_DEBMEAS		0x40
+#define	IPOPT_RESERVED2		0x60
+
+#define	IPOPT_EOL		0		/* end of option list */
+#define	IPOPT_NOP		1		/* no operation */
+
+#define	IPOPT_RR		7		/* record packet route */
+#define	IPOPT_TS		68		/* timestamp */
+#define	IPOPT_SECURITY		130		/* provide s,c,h,tcc */
+#define	IPOPT_LSRR		131		/* loose source route */
+#define	IPOPT_SATID		136		/* satnet id */
+#define	IPOPT_SSRR		137		/* strict source route */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define	IPOPT_OPTVAL		0		/* option ID */
+#define	IPOPT_OLEN		1		/* option length */
+#define IPOPT_OFFSET		2		/* offset within option */
+#define	IPOPT_MINOFF		4		/* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct	ip_timestamp {
+	u_int8_t	ipt_code;		/* IPOPT_TS */
+	u_int8_t	ipt_len;		/* size of structure (variable) */
+	u_int8_t	ipt_ptr;		/* index of current entry */
+#ifdef WORDS_BIGENDIAN
+	u_int	ipt_oflw:4,		/* overflow counter */
+		ipt_flg:4;		/* flags, see below */
+#else
+	u_int	ipt_flg:4,		/* flags, see below */
+		ipt_oflw:4;		/* overflow counter */
+#endif
+	union ipt_timestamp {
+		n_long	ipt_time[1];
+		struct	ipt_ta {
+			ipaddr_t ipt_addr;
+			n_long   ipt_time;
+		} ipt_ta[1];
+	} ipt_timestamp;
+};
+
+/* flag bits for ipt_flg */
+#define	IPOPT_TS_TSONLY		0		/* timestamps only */
+#define	IPOPT_TS_TSANDADDR	1		/* timestamps and addresses */
+#define	IPOPT_TS_PRESPEC	3		/* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define	IPOPT_SECUR_UNCLASS	0x0000
+#define	IPOPT_SECUR_CONFID	0xf135
+#define	IPOPT_SECUR_EFTO	0x789a
+#define	IPOPT_SECUR_MMMM	0xbc4d
+#define	IPOPT_SECUR_RESTR	0xaf13
+#define	IPOPT_SECUR_SECRET	0xd788
+#define	IPOPT_SECUR_TOPSECRET	0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define	MAXTTL		255		/* maximum time to live (seconds) */
+#define	IPDEFTTL	64		/* default ttl, from RFC 1340 */
+#define	IPFRAGTTL	60		/* time to live for frags, slowhz */
+#define	IPTTLDEC	1		/* subtracted when forwarding */
+
+#define	IP_MSS		576		/* default maximum segment size */
+
+#if SIZEOF_CHAR_P == 4
+struct mbuf_ptr {
+    struct mbuf *mptr;
+    uint32_t     dummy;
+};
+#else
+struct mbuf_ptr {
+    struct mbuf *mptr;
+};
+#endif
+struct qlink {
+    void*  next, *prev;
+};
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly {
+	struct mbuf_ptr ih_mbuf;      /* backpointer to mbuf */
+	u_int8_t	ih_x1;			/* (unused) */
+	u_int8_t	ih_pr;			/* protocol */
+	u_int16_t	ih_len;			/* protocol length */
+	ipaddr_t        ih_src;		/* source internet address */
+	ipaddr_t        ih_dst;		/* destination internet address */
+} __attribute__((packed));
+
+/*
+ * Ip reassembly queue structure.  Each fragment
+ * being reassembled is attached to one of these structures.
+ * They are timed out after ipq_ttl drops to 0, and may also
+ * be reclaimed if memory becomes tight.
+ * size 28 bytes
+ */
+struct ipq {
+        struct qlink  frag_link;  /* to ip headers of fragments */
+        struct qlink  ip_link;    /* to other reass headers */
+	u_int8_t	ipq_ttl;		/* time for reass q to live */
+	u_int8_t	ipq_p;			/* protocol of this fragment */
+	u_int16_t	ipq_id;			/* sequence id for reassembly */
+	ipaddr_t        ipq_src,ipq_dst;
+};
+
+/*
+ * Ip header, when holding a fragment.
+ *
+ * Note: ipf_next must be at same offset as frag_link above
+ */
+struct	ipasfrag {
+        struct qlink ipf_link;
+        struct ip    ipf_ip;
+};
+
+#define ipf_off  ipf_ip.ip_off
+#define ipf_tos  ipf_ip.ip_tos
+#define ipf_len  ipf_ip.ip_len
+#define ipf_next ipf_link.next
+#define ipf_prev ipf_link.prev
+
+/*
+ * Structure stored in mbuf in inpcb.ip_options
+ * and passed to ip_output when ip options are in use.
+ * The actual length of the options (including ipopt_dst)
+ * is in m_len.
+ */
+#define MAX_IPOPTLEN	40
+
+struct ipoption {
+	u_int32_t ipopt_dst;	/* first-hop dst if source routed */
+	int8_t	  ipopt_list[MAX_IPOPTLEN];	/* options proper */
+};
+
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+
+struct	ipstat {
+	u_long	ips_total;		/* total packets received */
+	u_long	ips_badsum;		/* checksum bad */
+	u_long	ips_tooshort;		/* packet too short */
+	u_long	ips_toosmall;		/* not enough data */
+	u_long	ips_badhlen;		/* ip header length < data size */
+	u_long	ips_badlen;		/* ip length < ip header length */
+	u_long	ips_fragments;		/* fragments received */
+	u_long	ips_fragdropped;	/* frags dropped (dups, out of space) */
+	u_long	ips_fragtimeout;	/* fragments timed out */
+	u_long	ips_forward;		/* packets forwarded */
+	u_long	ips_cantforward;	/* packets rcvd for unreachable dest */
+	u_long	ips_redirectsent;	/* packets forwarded on same net */
+	u_long	ips_noproto;		/* unknown or unsupported protocol */
+	u_long	ips_delivered;		/* datagrams delivered to upper level*/
+	u_long	ips_localout;		/* total ip packets generated here */
+	u_long	ips_odropped;		/* lost packets due to nobufs, etc. */
+	u_long	ips_reassembled;	/* total packets reassembled ok */
+	u_long	ips_fragmented;		/* datagrams successfully fragmented */
+	u_long	ips_ofragments;		/* output fragments created */
+	u_long	ips_cantfrag;		/* don't fragment flag was set, etc. */
+	u_long	ips_badoptions;		/* error in option processing */
+	u_long	ips_noroute;		/* packets discarded due to no route */
+	u_long	ips_badvers;		/* ip version != 4 */
+	u_long	ips_rawout;		/* total raw ip packets generated */
+	u_long	ips_unaligned;		/* times the ip packet was not aligned */
+};
+
+extern struct	ipstat	ipstat;
+extern struct	ipq	ipq;			/* ip reass. queue */
+extern u_int16_t	ip_id;				/* ip packet ctr, for ids */
+extern int	ip_defttl;			/* default IP ttl */
+
+#endif
diff --git a/slirp2/ip_icmp.c b/slirp2/ip_icmp.c
new file mode 100644
index 0000000..6594ec4
--- /dev/null
+++ b/slirp2/ip_icmp.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)ip_icmp.c	8.2 (Berkeley) 1/4/94
+ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+#include "sockets.h"
+
+struct icmpstat icmpstat;
+
+/* The message sent when emulating PING */
+/* Be nice and tell them it's just a psuedo-ping packet */
+char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
+
+/* list of actions for icmp_error() on RX of an icmp message */
+static int icmp_flush[19] = {
+/*  ECHO REPLY (0)  */   0,
+		         1,
+		         1,
+/* DEST UNREACH (3) */   1,
+/* SOURCE QUENCH (4)*/   1,
+/* REDIRECT (5) */       1,
+		         1,
+		         1,
+/* ECHO (8) */           0,
+/* ROUTERADVERT (9) */   1,
+/* ROUTERSOLICIT (10) */ 1,
+/* TIME EXCEEDED (11) */ 1,
+/* PARAMETER PROBLEM (12) */ 1,
+/* TIMESTAMP (13) */     0,
+/* TIMESTAMP REPLY (14) */ 0,
+/* INFO (15) */          0,
+/* INFO REPLY (16) */    0,
+/* ADDR MASK (17) */     0,
+/* ADDR MASK REPLY (18) */ 0
+};
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(m, hlen)
+     MBuf m;
+     int hlen;
+{
+  register struct icmp *icp;
+  register struct ip *ip=MBUF_TO(m, struct ip *);
+  int icmplen=ip->ip_len;
+  /* int code; */
+
+  DEBUG_CALL("icmp_input");
+  DEBUG_ARG("m = %lx", (long )m);
+  DEBUG_ARG("m_len = %d", m->m_len);
+
+  icmpstat.icps_received++;
+
+  /*
+   * Locate icmp structure in mbuf, and check
+   * that its not corrupted and of at least minimum length.
+   */
+  if (icmplen < ICMP_MINLEN) {          /* min 8 bytes payload */
+    icmpstat.icps_tooshort++;
+  freeit:
+    mbuf_free(m);
+    goto end_error;
+  }
+
+  m->m_len -= hlen;
+  m->m_data += hlen;
+  icp = MBUF_TO(m, struct icmp *);
+  if (cksum(m, icmplen)) {
+    icmpstat.icps_checksum++;
+    goto freeit;
+  }
+  m->m_len += hlen;
+  m->m_data -= hlen;
+
+  /*	icmpstat.icps_inhist[icp->icmp_type]++; */
+  /* code = icp->icmp_code; */
+
+  DEBUG_ARG("icmp_type = %d", icp->icmp_type);
+  switch (icp->icmp_type) {
+  case ICMP_ECHO:
+    icp->icmp_type = ICMP_ECHOREPLY;
+    ip->ip_len += hlen;	             /* since ip_input subtracts this */
+    if (ip_geth(ip->ip_dst) == alias_addr_ip) {
+      icmp_reflect(m);
+    } else {
+      struct socket *so;
+      SockAddress  addr;
+      uint32_t     addr_ip;
+      uint16_t     addr_port;
+
+      if ((so = socreate()) == NULL) goto freeit;
+      if(udp_attach(so) == -1) {
+	DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n",
+		    errno,errno_str));
+	sofree(so);
+	mbuf_free(m);
+	goto end_error;
+      }
+      so->so_m = m;
+      so->so_faddr_ip   = ip_geth(ip->ip_dst);
+      so->so_faddr_port = 7;
+      so->so_laddr_ip   = ip_geth(ip->ip_src);
+      so->so_laddr_port = 9;
+      so->so_iptos = ip->ip_tos;
+      so->so_type = IPPROTO_ICMP;
+      so->so_state = SS_ISFCONNECTED;
+
+      /* Send the packet */
+      if ((so->so_faddr_ip & 0xffffff00) == special_addr_ip) {
+        /* It's an alias */
+        int  low = so->so_faddr_ip & 0xff;
+
+        if (low >= CTL_DNS && low < CTL_DNS + dns_addr_count)
+            addr_ip = dns_addr[low - CTL_DNS];
+        else
+            addr_ip = loopback_addr_ip;
+      } else {
+            addr_ip = so->so_faddr_ip;
+      }
+      addr_port = so->so_faddr_port;
+
+      sock_address_init_inet( &addr, addr_ip, addr_port );
+
+      if(socket_sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), &addr) < 0) {
+        DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n",
+                    errno,errno_str));
+        icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,errno_str);
+        udp_detach(so);
+      }
+    } /* if ip->ip_dst.s_addr == alias_addr.s_addr */
+    break;
+  case ICMP_UNREACH:
+    /* XXX? report error? close socket? */
+  case ICMP_TIMXCEED:
+  case ICMP_PARAMPROB:
+  case ICMP_SOURCEQUENCH:
+  case ICMP_TSTAMP:
+  case ICMP_MASKREQ:
+  case ICMP_REDIRECT:
+    icmpstat.icps_notsupp++;
+    mbuf_free(m);
+    break;
+
+  default:
+    icmpstat.icps_badtype++;
+    mbuf_free(m);
+  } /* swith */
+
+end_error:
+  /* m is mbuf_free()'d xor put in a socket xor or given to ip_send */
+  return;
+}
+
+
+/*
+ *	Send an ICMP message in response to a situation
+ *
+ *	RFC 1122: 3.2.2	MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ *			MUST NOT change this header information.
+ *			MUST NOT reply to a multicast/broadcast IP address.
+ *			MUST NOT reply to a multicast/broadcast MAC address.
+ *			MUST reply to only the first fragment.
+ */
+/*
+ * Send ICMP_UNREACH back to the source regarding msrc.
+ * mbuf *msrc is used as a template, but is NOT mbuf_free()'d.
+ * It is reported as the bad ip packet.  The header should
+ * be fully correct and in host byte order.
+ * ICMP fragmentation is illegal.  All machines must accept 576 bytes in one
+ * packet.  The maximum payload is 576-20(ip hdr)-8(icmp hdr)=548
+ */
+
+#define ICMP_MAXDATALEN (IP_MSS-28)
+void
+icmp_error(msrc, type, code, minsize, message)
+     MBuf msrc;
+     u_char type;
+     u_char code;
+     int minsize;
+     const char *message;
+{
+  unsigned hlen, shlen, s_ip_len;
+  register struct ip *ip;
+  register struct icmp *icp;
+  register MBuf m;
+
+  DEBUG_CALL("icmp_error");
+  DEBUG_ARG("msrc = %lx", (long )msrc);
+  DEBUG_ARG("msrc_len = %d", msrc->m_len);
+
+  if(type!=ICMP_UNREACH && type!=ICMP_TIMXCEED) goto end_error;
+
+  /* check msrc */
+  if(!msrc) goto end_error;
+  ip = MBUF_TO(msrc, struct ip *);
+#if DEBUG
+  { char bufa[20], bufb[20];
+    strcpy(bufa, inet_iptostr(ip_geth(ip->ip_src)));
+    strcpy(bufb, inet_iptostr(ip_geth(ip->ip_dst)));
+    DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb));
+  }
+#endif
+  if(ip->ip_off & IP_OFFMASK) goto end_error;    /* Only reply to fragment 0 */
+
+  shlen=ip->ip_hl << 2;
+  s_ip_len=ip->ip_len;
+  if(ip->ip_p == IPPROTO_ICMP) {
+    icp = (struct icmp *)((char *)ip + shlen);
+    /*
+     *	Assume any unknown ICMP type is an error. This isn't
+     *	specified by the RFC, but think about it..
+     */
+    if(icp->icmp_type>18 || icmp_flush[icp->icmp_type]) goto end_error;
+  }
+
+  /* make a copy */
+  if(!(m=mbuf_alloc())) goto end_error;               /* get mbuf */
+  { int new_m_size;
+    new_m_size=sizeof(struct ip )+ICMP_MINLEN+msrc->m_len+ICMP_MAXDATALEN;
+    if(new_m_size>m->m_size) mbuf_ensure(m, new_m_size);
+  }
+  memcpy(m->m_data, msrc->m_data, msrc->m_len);
+  m->m_len = msrc->m_len;                        /* copy msrc to m */
+
+  /* make the header of the reply packet */
+  ip  = MBUF_TO(m, struct ip *);
+  hlen= sizeof(struct ip );     /* no options in reply */
+
+  /* fill in icmp */
+  m->m_data += hlen;
+  m->m_len -= hlen;
+
+  icp = MBUF_TO(m, struct icmp *);
+
+  if(minsize) s_ip_len=shlen+ICMP_MINLEN;   /* return header+8b only */
+  else if(s_ip_len>ICMP_MAXDATALEN)         /* maximum size */
+    s_ip_len=ICMP_MAXDATALEN;
+
+  m->m_len=ICMP_MINLEN+s_ip_len;        /* 8 bytes ICMP header */
+
+  /* min. size = 8+sizeof(struct ip)+8 */
+
+  icp->icmp_type = type;
+  icp->icmp_code = code;
+  icp->icmp_id = 0;
+  icp->icmp_seq = 0;
+
+  memcpy(&icp->icmp_ip, msrc->m_data, s_ip_len);   /* report the ip packet */
+  HTONS(icp->icmp_ip.ip_len);
+  HTONS(icp->icmp_ip.ip_id);
+  HTONS(icp->icmp_ip.ip_off);
+
+#if DEBUG
+  if(message) {           /* DEBUG : append message to ICMP packet */
+    int message_len;
+    char *cpnt;
+    message_len=strlen(message);
+    if(message_len>ICMP_MAXDATALEN) message_len=ICMP_MAXDATALEN;
+    cpnt=(char *)m->m_data+m->m_len;
+    memcpy(cpnt, message, message_len);
+    m->m_len+=message_len;
+  }
+#endif
+
+  icp->icmp_cksum = 0;
+  icp->icmp_cksum = cksum(m, m->m_len);
+
+  m->m_data -= hlen;
+  m->m_len += hlen;
+
+  /* fill in ip */
+  ip->ip_hl = hlen >> 2;
+  ip->ip_len = m->m_len;
+
+  ip->ip_tos=((ip->ip_tos & 0x1E) | 0xC0);  /* high priority for errors */
+
+  ip->ip_ttl = MAXTTL;
+  ip->ip_p = IPPROTO_ICMP;
+  ip->ip_dst = ip->ip_src;    /* ip adresses */
+  ip->ip_src = ip_setn(alias_addr_ip);
+
+  (void ) ip_output((struct socket *)NULL, m);
+
+  icmpstat.icps_reflect++;
+
+end_error:
+  return;
+}
+#undef ICMP_MAXDATALEN
+
+/*
+ * Reflect the ip packet back to the source
+ */
+void
+icmp_reflect(m)
+     MBuf m;
+{
+  register struct ip *ip = MBUF_TO(m, struct ip *);
+  int hlen = ip->ip_hl << 2;
+  int optlen = hlen - sizeof(struct ip );
+  register struct icmp *icp;
+
+  /*
+   * Send an icmp packet back to the ip level,
+   * after supplying a checksum.
+   */
+  m->m_data += hlen;
+  m->m_len -= hlen;
+  icp = MBUF_TO(m, struct icmp *);
+
+  icp->icmp_cksum = 0;
+  icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
+
+  m->m_data -= hlen;
+  m->m_len += hlen;
+
+  /* fill in ip */
+  if (optlen > 0) {
+    /*
+     * Strip out original options by copying rest of first
+     * mbuf's data back, and adjust the IP length.
+     */
+    memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen,
+	    (unsigned )(m->m_len - hlen));
+    hlen -= optlen;
+    ip->ip_hl = hlen >> 2;
+    ip->ip_len -= optlen;
+    m->m_len -= optlen;
+  }
+
+  ip->ip_ttl = MAXTTL;
+  { /* swap */
+    ipaddr_t icmp_dst;
+    icmp_dst   = ip->ip_dst;
+    ip->ip_dst = ip->ip_src;
+    ip->ip_src = icmp_dst;
+  }
+
+  (void ) ip_output((struct socket *)NULL, m);
+
+  icmpstat.icps_reflect++;
+}
diff --git a/slirp2/ip_icmp.h b/slirp2/ip_icmp.h
new file mode 100644
index 0000000..bc0be51
--- /dev/null
+++ b/slirp2/ip_icmp.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)ip_icmp.h	8.1 (Berkeley) 6/10/93
+ * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
+ */
+
+#ifndef _NETINET_IP_ICMP_H_
+#define _NETINET_IP_ICMP_H_
+
+#include "helper.h"
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+typedef u_int32_t n_time;
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+	u_char	icmp_type;		/* type of message, see below */
+	u_char	icmp_code;		/* type sub code */
+	u_short	icmp_cksum;		/* ones complement cksum of struct */
+	union {
+		u_char    ih_pptr;			/* ICMP_PARAMPROB */
+		ipaddr_t  ih_gwaddr;	/* ICMP_REDIRECT */
+		struct ih_idseq {
+			u_short	icd_id;
+			u_short	icd_seq;
+		} ih_idseq;
+		int ih_void;
+
+		/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+		struct ih_pmtu {
+			u_short ipm_void;
+			u_short ipm_nextmtu;
+		} ih_pmtu;
+	} icmp_hun;
+#define	icmp_pptr	icmp_hun.ih_pptr
+#define	icmp_gwaddr	icmp_hun.ih_gwaddr
+#define	icmp_id		icmp_hun.ih_idseq.icd_id
+#define	icmp_seq	icmp_hun.ih_idseq.icd_seq
+#define	icmp_void	icmp_hun.ih_void
+#define	icmp_pmvoid	icmp_hun.ih_pmtu.ipm_void
+#define	icmp_nextmtu	icmp_hun.ih_pmtu.ipm_nextmtu
+	union {
+		struct id_ts {
+			n_time its_otime;
+			n_time its_rtime;
+			n_time its_ttime;
+		} id_ts;
+		struct id_ip  {
+			struct ip idi_ip;
+			/* options and then 64 bits of data */
+		} id_ip;
+		uint32_t	id_mask;
+		char		id_data[1];
+	} icmp_dun;
+#define	icmp_otime	icmp_dun.id_ts.its_otime
+#define	icmp_rtime	icmp_dun.id_ts.its_rtime
+#define	icmp_ttime	icmp_dun.id_ts.its_ttime
+#define	icmp_ip		icmp_dun.id_ip.idi_ip
+#define	icmp_mask	icmp_dun.id_mask
+#define	icmp_data	icmp_dun.id_data
+};
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define	ICMP_MINLEN	8				/* abs minimum */
+#define	ICMP_TSLEN	(8 + 3 * sizeof (n_time))	/* timestamp */
+#define	ICMP_MASKLEN	12				/* address mask */
+#define	ICMP_ADVLENMIN	(8 + sizeof (struct ip) + 8)	/* min */
+#define	ICMP_ADVLEN(p)	(8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+	/* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define	ICMP_ECHOREPLY		0		/* echo reply */
+#define	ICMP_UNREACH		3		/* dest unreachable, codes: */
+#define		ICMP_UNREACH_NET	0		/* bad net */
+#define		ICMP_UNREACH_HOST	1		/* bad host */
+#define		ICMP_UNREACH_PROTOCOL	2		/* bad protocol */
+#define		ICMP_UNREACH_PORT	3		/* bad port */
+#define		ICMP_UNREACH_NEEDFRAG	4		/* IP_DF caused drop */
+#define		ICMP_UNREACH_SRCFAIL	5		/* src route failed */
+#define		ICMP_UNREACH_NET_UNKNOWN 6		/* unknown net */
+#define		ICMP_UNREACH_HOST_UNKNOWN 7		/* unknown host */
+#define		ICMP_UNREACH_ISOLATED	8		/* src host isolated */
+#define		ICMP_UNREACH_NET_PROHIB	9		/* prohibited access */
+#define		ICMP_UNREACH_HOST_PROHIB 10		/* ditto */
+#define		ICMP_UNREACH_TOSNET	11		/* bad tos for net */
+#define		ICMP_UNREACH_TOSHOST	12		/* bad tos for host */
+#define	ICMP_SOURCEQUENCH	4		/* packet lost, slow down */
+#define	ICMP_REDIRECT		5		/* shorter route, codes: */
+#define		ICMP_REDIRECT_NET	0		/* for network */
+#define		ICMP_REDIRECT_HOST	1		/* for host */
+#define		ICMP_REDIRECT_TOSNET	2		/* for tos and net */
+#define		ICMP_REDIRECT_TOSHOST	3		/* for tos and host */
+#define	ICMP_ECHO		8		/* echo service */
+#define	ICMP_ROUTERADVERT	9		/* router advertisement */
+#define	ICMP_ROUTERSOLICIT	10		/* router solicitation */
+#define	ICMP_TIMXCEED		11		/* time exceeded, code: */
+#define		ICMP_TIMXCEED_INTRANS	0		/* ttl==0 in transit */
+#define		ICMP_TIMXCEED_REASS	1		/* ttl==0 in reass */
+#define	ICMP_PARAMPROB		12		/* ip header bad */
+#define		ICMP_PARAMPROB_OPTABSENT 1		/* req. opt. absent */
+#define	ICMP_TSTAMP		13		/* timestamp request */
+#define	ICMP_TSTAMPREPLY	14		/* timestamp reply */
+#define	ICMP_IREQ		15		/* information request */
+#define	ICMP_IREQREPLY		16		/* information reply */
+#define	ICMP_MASKREQ		17		/* address mask request */
+#define	ICMP_MASKREPLY		18		/* address mask reply */
+
+#define	ICMP_MAXTYPE		18
+
+#define	ICMP_INFOTYPE(type) \
+	((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+	(type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+	(type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+	(type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+	(type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+void icmp_input _P((MBuf , int));
+void icmp_error _P((MBuf , u_char, u_char, int, const char *));
+void icmp_reflect _P((MBuf ));
+
+#endif
diff --git a/slirp2/ip_input.c b/slirp2/ip_input.c
new file mode 100644
index 0000000..fa99a5d
--- /dev/null
+++ b/slirp2/ip_input.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)ip_input.c	8.2 (Berkeley) 1/4/94
+ * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <osdep.h>
+#include "ip_icmp.h"
+
+int ip_defttl;
+struct ipstat ipstat;
+struct ipq ipq;
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init()
+{
+	ipq.ip_link.next = ipq.ip_link.prev = &ipq.ip_link;
+	ip_id = tt.tv_sec & 0xffff;
+	udp_init();
+	tcp_init();
+	ip_defttl = IPDEFTTL;
+}
+
+/*
+ * Ip input routine.  Checksum and byte swap header.  If fragmented
+ * try to reassemble.  Process options.  Pass to next level.
+ */
+void
+ip_input(m)
+	MBuf m;
+{
+	register struct ip *ip;
+	int hlen;
+	
+	DEBUG_CALL("ip_input");
+	DEBUG_ARG("m = %lx", (long)m);
+	DEBUG_ARG("m_len = %d", m->m_len);
+
+	ipstat.ips_total++;
+	
+	if (m->m_len < sizeof (struct ip)) {
+		ipstat.ips_toosmall++;
+		return;
+	}
+	
+	ip = MBUF_TO(m, struct ip *);
+	
+	if (ip->ip_v != IPVERSION) {
+		ipstat.ips_badvers++;
+		goto bad;
+	}
+
+	hlen = ip->ip_hl << 2;
+	if (hlen<sizeof(struct ip ) || hlen>m->m_len) {/* min header length */
+	  ipstat.ips_badhlen++;                     /* or packet too short */
+	  goto bad;
+	}
+
+        /* keep ip header intact for ICMP reply
+	 * ip->ip_sum = cksum(m, hlen); 
+	 * if (ip->ip_sum) { 
+	 */
+	if(cksum(m,hlen)) {
+	  ipstat.ips_badsum++;
+	  goto bad;
+	}
+
+	/*
+	 * Convert fields to host representation.
+	 */
+	NTOHS(ip->ip_len);
+	if (ip->ip_len < hlen) {
+		ipstat.ips_badlen++;
+		goto bad;
+	}
+	NTOHS(ip->ip_id);
+	NTOHS(ip->ip_off);
+
+	/*
+	 * Check that the amount of data in the buffers
+	 * is as at least much as the IP header would have us expect.
+	 * Trim mbufs if longer than we expect.
+	 * Drop packet if shorter than we expect.
+	 */
+	if (m->m_len < ip->ip_len) {
+		ipstat.ips_tooshort++;
+		goto bad;
+	}
+	/* Should drop packet if mbuf too long? hmmm... */
+	if (m->m_len > ip->ip_len)
+	   mbuf_trim(m, ip->ip_len - m->m_len);
+
+	/* check ip_ttl for a correct ICMP reply */
+	if(ip->ip_ttl==0 || ip->ip_ttl==1) {
+	  icmp_error(m, ICMP_TIMXCEED,ICMP_TIMXCEED_INTRANS, 0,"ttl");
+	  goto bad;
+	}
+
+	/*
+	 * Process options and, if not destined for us,
+	 * ship it on.  ip_dooptions returns 1 when an
+	 * error was detected (causing an icmp message
+	 * to be sent and the original packet to be freed).
+	 */
+/* We do no IP options */
+/*	if (hlen > sizeof (struct ip) && ip_dooptions(m))
+ *		goto next;
+ */
+	/*
+	 * If offset or IP_MF are set, must reassemble.
+	 * Otherwise, nothing need be done.
+	 * (We could look in the reassembly queue to see
+	 * if the packet was previously fragmented,
+	 * but it's not worth the time; just let them time out.)
+	 * 
+	 * XXX This should fail, don't fragment yet
+	 */
+	if (ip->ip_off &~ IP_DF) {
+	  register struct ipq *fp;
+	  struct qlink *l;
+		/*
+		 * Look for queue of fragments
+		 * of this datagram.
+		 */
+		for (l = ipq.ip_link.next; l != &ipq.ip_link; l = l->next) {
+		  fp = container_of(l, struct ipq, ip_link);
+		  if (ip->ip_id == fp->ipq_id &&
+		      ip_equal(ip->ip_src, fp->ipq_src) &&
+		      ip_equal(ip->ip_dst, fp->ipq_dst) &&
+		      ip->ip_p == fp->ipq_p)
+		  goto found;
+		}
+		fp = NULL;
+	found:
+
+		/*
+		 * Adjust ip_len to not reflect header,
+		 * set ip_mff if more fragments are expected,
+		 * convert offset of this to bytes.
+		 */
+		ip->ip_len -= hlen;
+		if (ip->ip_off & IP_MF)
+		  ip->ip_tos |= 1;
+		else 
+		  ip->ip_tos &= ~1;
+
+		ip->ip_off <<= 3;
+
+		/*
+		 * If datagram marked as having more fragments
+		 * or if this is not the first fragment,
+		 * attempt reassembly; if it succeeds, proceed.
+		 */
+		if (ip->ip_tos & 1 || ip->ip_off) {
+			ipstat.ips_fragments++;
+			ip = ip_reass(ip, fp);
+			if (ip == 0)
+				return;
+			ipstat.ips_reassembled++;
+			m = MBUF_FROM(ip);
+		} else
+			if (fp)
+		   	   ip_freef(fp);
+
+	} else
+		ip->ip_len -= hlen;
+
+	/*
+	 * Switch out to protocol's input routine.
+	 */
+	ipstat.ips_delivered++;
+	switch (ip->ip_p) {
+	 case IPPROTO_TCP:
+		tcp_input(m, hlen, (struct socket *)NULL);
+		break;
+	 case IPPROTO_UDP:
+		udp_input(m, hlen);
+		break;
+	 case IPPROTO_ICMP:
+		icmp_input(m, hlen);
+		break;
+	 default:
+		ipstat.ips_noproto++;
+		mbuf_free(m);
+	}
+	return;
+bad:
+	mbuf_free(m);
+	return;
+}
+
+#define iptofrag(P) ((struct ipasfrag *)(((char*)(P)) - sizeof(struct qlink)))
+#define fragtoip(P) ((struct ip*)(((char*)(P)) + sizeof(struct qlink)))
+
+/*
+ * Take incoming datagram fragment and try to
+ * reassemble it into whole datagram.  If a chain for
+ * reassembly of this datagram already exists, then it
+ * is given as fp; otherwise have to make a chain.
+ */
+struct ip *
+ip_reass(ip, fp)
+	register struct ip *ip;
+	register struct ipq *fp;
+{
+	register MBuf m = MBUF_FROM(ip);
+	register struct ipasfrag *q;
+	int hlen = ip->ip_hl << 2;
+	int i, next;
+	
+	DEBUG_CALL("ip_reass");
+	DEBUG_ARG("ip = %lx", (long)ip);
+	DEBUG_ARG("fp = %lx", (long)fp);
+	DEBUG_ARG("m = %lx", (long)m);
+
+	/*
+	 * Presence of header sizes in mbufs
+	 * would confuse code below.
+         * Fragment m_data is concatenated.
+	 */
+	m->m_data += hlen;
+	m->m_len -= hlen;
+
+	/*
+	 * If first fragment to arrive, create a reassembly queue.
+	 */
+	if (fp == 0) {
+	  MBuf t;
+	  if ((t = mbuf_alloc()) == NULL) goto dropfrag;
+	  fp = MBUF_TO(t, struct ipq *);
+	  insque(&fp->ip_link, &ipq.ip_link);
+	  fp->ipq_ttl = IPFRAGTTL;
+	  fp->ipq_p = ip->ip_p;
+	  fp->ipq_id = ip->ip_id;
+	  fp->frag_link.next = fp->frag_link.prev = &fp->frag_link;
+	  fp->ipq_src = ip->ip_src;
+	  fp->ipq_dst = ip->ip_src;
+	  q = (struct ipasfrag *)fp;
+	  goto insert;
+	}
+	
+	/*
+	 * Find a segment which begins after this one does.
+	 */
+        for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link;
+             q = q->ipf_next)
+                if (q->ipf_off > ip->ip_off)
+                    break;
+	/*
+	 * If there is a preceding segment, it may provide some of
+	 * our data already.  If so, drop the data from the incoming
+	 * segment.  If it provides all of our data, drop us.
+	 */
+        if (q->ipf_prev != &fp->frag_link) {
+            struct ipasfrag *pq = q->ipf_prev;
+                i = pq->ipf_off + pq->ipf_len - ip->ip_off;
+		if (i > 0) {
+			if (i >= ip->ip_len)
+				goto dropfrag;
+			mbuf_trim(MBUF_FROM(ip), i);
+			ip->ip_off += i;
+			ip->ip_len -= i;
+		}
+	}
+
+	/*
+	 * While we overlap succeeding segments trim them or,
+	 * if they are completely covered, dequeue them.
+	 */
+	while (q != (struct ipasfrag *)&fp->frag_link &&
+	     ip->ip_off + ip->ip_len > q->ipf_off) {
+	       i = (ip->ip_off + ip->ip_len) - q->ipf_off;
+	       if (i < q->ipf_len) {
+	           q->ipf_len -= i;
+	           q->ipf_off += i;
+                   mbuf_trim(MBUF_FROM(q), i);
+                   break;
+		}
+		q = q->ipf_next;
+		mbuf_free(MBUF_FROM(q->ipf_prev));
+		ip_deq(q->ipf_prev);
+	}
+
+insert:
+	/*
+	 * Stick new segment in its place;
+	 * check for complete reassembly.
+	 */
+	ip_enq(iptofrag(ip), q->ipf_prev);
+	next = 0;
+	for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link;
+	     q = q->ipf_next) 
+        {
+            if (q->ipf_off != next)
+                return (0);
+
+            next += q->ipf_len;
+	}
+	if (((struct ipasfrag *)(q->ipf_prev))->ipf_tos & 1)
+		return (0);
+
+	/*
+	 * Reassembly is complete; concatenate fragments.
+	 */
+	q = fp->frag_link.next;
+	m = MBUF_FROM(q);
+
+	q = (struct ipasfrag *) q->ipf_next;
+	while (q != (struct ipasfrag *)&fp->frag_link) {
+	  MBuf t = MBUF_FROM(q);
+	  q = (struct ipasfrag *) q->ipf_next;
+	  mbuf_append(m, t);
+	}
+
+	/*
+	 * Create header for new ip packet by
+	 * modifying header of first packet;
+	 * dequeue and discard fragment reassembly header.
+	 * Make header visible.
+	 */
+        q = fp->frag_link.next;
+
+	/*
+	 * If the fragments concatenated to an mbuf that's
+	 * bigger than the total size of the fragment, then and
+	 * m_ext buffer was alloced. But fp->ipq_next points to
+	 * the old buffer (in the mbuf), so we must point ip
+	 * into the new buffer.
+	 */
+	if (m->m_flags & M_EXT) {
+	  int delta = (char *)q - m->m_dat;
+	  q = (struct ipasfrag *)(m->m_ext + delta);
+	}
+
+	/* DEBUG_ARG("ip = %lx", (long)ip); 
+	 * ip=(struct ipasfrag *)m->m_data; */
+
+        ip = fragtoip(q);
+	ip->ip_len  = next;
+	ip->ip_tos &= ~1;
+	ip->ip_src  = fp->ipq_src;
+	ip->ip_dst  = fp->ipq_dst;
+	remque(&fp->ip_link);
+	(void) mbuf_free(MBUF_FROM(fp));
+	m->m_len += (ip->ip_hl << 2);
+	m->m_data -= (ip->ip_hl << 2);
+
+	return ip;
+
+dropfrag:
+	ipstat.ips_fragdropped++;
+	mbuf_free(m);
+	return (0);
+}
+
+/*
+ * Free a fragment reassembly header and all
+ * associated datagrams.
+ */
+void
+ip_freef(fp)
+	struct ipq *fp;
+{
+	register struct ipasfrag *q, *p;
+
+	for (q = fp->frag_link.next; q != (struct ipasfrag *)&fp->frag_link; q = p) {
+		p = q->ipf_next;
+		ip_deq(q);
+		mbuf_free(MBUF_FROM(q));
+	}
+	remque(&fp->ip_link);
+	(void) mbuf_free(MBUF_FROM(fp));
+}
+
+/*
+ * Put an ip fragment on a reassembly chain.
+ * Like insque, but pointers in middle of structure.
+ */
+void
+ip_enq(p, prev)
+	register struct ipasfrag *p, *prev;
+{
+	DEBUG_CALL("ip_enq");
+	DEBUG_ARG("prev = %lx", (long)prev);
+	p->ipf_prev = prev;
+	p->ipf_next = prev->ipf_next;
+	((struct ipasfrag *)(prev->ipf_next))->ipf_prev = p;
+	prev->ipf_next = p;
+}
+
+/*
+ * To ip_enq as remque is to insque.
+ */
+void
+ip_deq(p)
+	register struct ipasfrag *p;
+{
+	((struct ipasfrag *)(p->ipf_prev))->ipf_next = p->ipf_next;
+	((struct ipasfrag *)(p->ipf_next))->ipf_prev = p->ipf_prev;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo()
+{
+        struct qlink *l;
+	
+	DEBUG_CALL("ip_slowtimo");
+	
+	l = ipq.ip_link.next;
+	if (l == 0)
+	   return;
+
+        while (l != &ipq.ip_link) {
+            struct ipq *fp = container_of(l, struct ipq, ip_link);
+            l = l->next;
+            if (--fp->ipq_ttl == 0) {
+			ipstat.ips_fragtimeout++;
+			ip_freef(fp);
+		}
+	}
+}
+
+/*
+ * Do option processing on a datagram,
+ * possibly discarding it if bad options are encountered,
+ * or forwarding it if source-routed.
+ * Returns 1 if packet has been forwarded/freed,
+ * 0 if the packet should be processed further.
+ */
+
+#ifdef notdef
+
+int
+ip_dooptions(m)
+	MBuf m;
+{
+	register struct ip *ip = MBUF_TO(m, struct ip *);
+	register u_char *cp;
+	register struct ip_timestamp *ipt;
+	register struct in_ifaddr *ia;
+/*	int opt, optlen, cnt, off, code, type = ICMP_PARAMPROB, forward = 0; */
+	int opt, optlen, cnt, off, code, type, forward = 0;
+	ipaddr_t *sin, dst;
+typedef u_int32_t n_time;
+	n_time ntime;
+
+	dst = ip->ip_dst;
+	cp = (u_char *)(ip + 1);
+	cnt = (ip->ip_hl << 2) - sizeof (struct ip);
+	for (; cnt > 0; cnt -= optlen, cp += optlen) {
+		opt = cp[IPOPT_OPTVAL];
+		if (opt == IPOPT_EOL)
+			break;
+		if (opt == IPOPT_NOP)
+			optlen = 1;
+		else {
+			optlen = cp[IPOPT_OLEN];
+			if (optlen <= 0 || optlen > cnt) {
+				code = &cp[IPOPT_OLEN] - (u_char *)ip;
+				goto bad;
+			}
+		}
+		switch (opt) {
+
+		default:
+			break;
+
+		/*
+		 * Source routing with record.
+		 * Find interface with current destination address.
+		 * If none on this machine then drop if strictly routed,
+		 * or do nothing if loosely routed.
+		 * Record interface address and bring up next address
+		 * component.  If strictly routed make sure next
+		 * address is on directly accessible net.
+		 */
+		case IPOPT_LSRR:
+		case IPOPT_SSRR:
+			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+				goto bad;
+			}
+			ipaddr.sin_addr = ip->ip_dst;
+			ia = (struct in_ifaddr *)
+				ifa_ifwithaddr((struct sockaddr *)&ipaddr);
+			if (ia == 0) {
+				if (opt == IPOPT_SSRR) {
+					type = ICMP_UNREACH;
+					code = ICMP_UNREACH_SRCFAIL;
+					goto bad;
+				}
+				/*
+				 * Loose routing, and not at next destination
+				 * yet; nothing to do except forward.
+				 */
+				break;
+			}
+			off--;			/ * 0 origin *  /
+			if (off > optlen - sizeof(struct in_addr)) {
+				/*
+				 * End of source route.  Should be for us.
+				 */
+				save_rte(cp, ip->ip_src);
+				break;
+			}
+			/*
+			 * locate outgoing interface
+			 */
+			bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr,
+			    sizeof(ipaddr.sin_addr));
+			if (opt == IPOPT_SSRR) {
+#define	INA	struct in_ifaddr *
+#define	SA	struct sockaddr *
+ 			    if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0)
+				ia = (INA)ifa_ifwithnet((SA)&ipaddr);
+			} else
+				ia = ip_rtaddr(ipaddr.sin_addr);
+			if (ia == 0) {
+				type = ICMP_UNREACH;
+				code = ICMP_UNREACH_SRCFAIL;
+				goto bad;
+			}
+			ip->ip_dst = ipaddr.sin_addr;
+			bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+			    (caddr_t)(cp + off), sizeof(struct in_addr));
+			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+			/*
+			 * Let ip_intr's mcast routing check handle mcast pkts
+			 */
+			forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr));
+			break;
+
+		case IPOPT_RR:
+			if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) {
+				code = &cp[IPOPT_OFFSET] - (u_char *)ip;
+				goto bad;
+			}
+			/*
+			 * If no space remains, ignore.
+			 */
+			off--;			 * 0 origin *
+			if (off > optlen - sizeof(struct in_addr))
+				break;
+			bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr,
+			    sizeof(ipaddr.sin_addr));
+			/*
+			 * locate outgoing interface; if we're the destination,
+			 * use the incoming interface (should be same).
+			 */
+			if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 &&
+			    (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) {
+				type = ICMP_UNREACH;
+				code = ICMP_UNREACH_HOST;
+				goto bad;
+			}
+			bcopy((caddr_t)&(IA_SIN(ia)->sin_addr),
+			    (caddr_t)(cp + off), sizeof(struct in_addr));
+			cp[IPOPT_OFFSET] += sizeof(struct in_addr);
+			break;
+
+		case IPOPT_TS:
+			code = cp - (u_char *)ip;
+			ipt = (struct ip_timestamp *)cp;
+			if (ipt->ipt_len < 5)
+				goto bad;
+			if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) {
+				if (++ipt->ipt_oflw == 0)
+					goto bad;
+				break;
+			}
+			sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1);
+			switch (ipt->ipt_flg) {
+
+			case IPOPT_TS_TSONLY:
+				break;
+
+			case IPOPT_TS_TSANDADDR:
+				if (ipt->ipt_ptr + sizeof(n_time) +
+				    sizeof(struct in_addr) > ipt->ipt_len)
+					goto bad;
+				ipaddr.sin_addr = dst;
+				ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr,
+							    m->m_pkthdr.rcvif);
+				if (ia == 0)
+					continue;
+				bcopy((caddr_t)&IA_SIN(ia)->sin_addr,
+				    (caddr_t)sin, sizeof(struct in_addr));
+				ipt->ipt_ptr += sizeof(struct in_addr);
+				break;
+
+			case IPOPT_TS_PRESPEC:
+				if (ipt->ipt_ptr + sizeof(n_time) +
+				    sizeof(struct in_addr) > ipt->ipt_len)
+					goto bad;
+				bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr,
+				    sizeof(struct in_addr));
+				if (ifa_ifwithaddr((SA)&ipaddr) == 0)
+					continue;
+				ipt->ipt_ptr += sizeof(struct in_addr);
+				break;
+
+			default:
+				goto bad;
+			}
+			ntime = iptime();
+			bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1,
+			    sizeof(n_time));
+			ipt->ipt_ptr += sizeof(n_time);
+		}
+	}
+	if (forward) {
+		ip_forward(m, 1);
+		return (1);
+	}
+		}
+	}
+	return (0);
+bad:
+	/* ip->ip_len -= ip->ip_hl << 2;   XXX icmp_error adds in hdr length */
+
+/* Not yet */
+ 	icmp_error(m, type, code, 0, 0);
+
+	ipstat.ips_badoptions++;
+	return (1);
+}
+
+#endif /* notdef */
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * (XXX) should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(m, mopt)
+	register MBuf m;
+	MBuf mopt;
+{
+	register int i;
+	struct ip *ip = MBUF_TO(m, struct ip *);
+	register caddr_t opts;
+	int olen;
+
+	olen = (ip->ip_hl<<2) - sizeof (struct ip);
+	opts = (caddr_t)(ip + 1);
+	i = m->m_len - (sizeof (struct ip) + olen);
+	memcpy(opts, opts  + olen, (unsigned)i);
+	m->m_len -= olen;
+	
+	ip->ip_hl = sizeof(struct ip) >> 2;
+}
diff --git a/slirp2/ip_output.c b/slirp2/ip_output.c
new file mode 100644
index 0000000..42d789c
--- /dev/null
+++ b/slirp2/ip_output.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)ip_output.c	8.3 (Berkeley) 1/21/94
+ * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+u_int16_t ip_id;
+
+/*
+ * IP output.  The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(so, m0)
+	struct socket *so;
+	MBuf m0;
+{
+	register struct ip *ip;
+	register MBuf m = m0;
+	register int hlen = sizeof(struct ip );
+	int len, off, error = 0;
+
+	DEBUG_CALL("ip_output");
+	DEBUG_ARG("so = %lx", (long)so);
+	DEBUG_ARG("m0 = %lx", (long)m0);
+	
+	/* We do no options */
+/*	if (opt) {
+ *		m = ip_insertoptions(m, opt, &len);
+ *		hlen = len;
+ *	}
+ */
+	ip = MBUF_TO(m, struct ip *);
+	/*
+	 * Fill in IP header.
+	 */
+	ip->ip_v = IPVERSION;
+	ip->ip_off &= IP_DF;
+	ip->ip_id = htons(ip_id++);
+	ip->ip_hl = hlen >> 2;
+	ipstat.ips_localout++;
+
+	/*
+	 * Verify that we have any chance at all of being able to queue
+	 *      the packet or packet fragments
+	 */
+	/* XXX Hmmm... */
+/*	if (if_queued > if_thresh && towrite <= 0) {
+ *		error = ENOBUFS;
+ *		goto bad;
+ *	}
+ */
+	
+	/*
+	 * If small enough for interface, can just send directly.
+	 */
+	if ((u_int16_t)ip->ip_len <= if_mtu) {
+		ip->ip_len = htons((u_int16_t)ip->ip_len);
+		ip->ip_off = htons((u_int16_t)ip->ip_off);
+		ip->ip_sum = 0;
+		ip->ip_sum = cksum(m, hlen);
+
+		if_output(so, m);
+		goto done;
+	}
+
+	/*
+	 * Too large for interface; fragment if possible.
+	 * Must be able to put at least 8 bytes per fragment.
+	 */
+	if (ip->ip_off & IP_DF) {
+		error = -1;
+		ipstat.ips_cantfrag++;
+		goto bad;
+	}
+	
+	len = (if_mtu - hlen) &~ 7;       /* ip databytes per packet */
+	if (len < 8) {
+		error = -1;
+		goto bad;
+	}
+
+    {
+	int mhlen, firstlen = len;
+	MBuf *mnext = &m->m_nextpkt;
+
+	/*
+	 * Loop through length of segment after first fragment,
+	 * make new header and copy data of each part and link onto chain.
+	 */
+	m0 = m;
+	mhlen = sizeof (struct ip);
+	for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) {
+	  register struct ip *mhip;
+	  m = mbuf_alloc();
+	  if (m == 0) {
+	    error = -1;
+	    ipstat.ips_odropped++;
+	    goto sendorfree;
+	  }
+	  m->m_data += if_maxlinkhdr;
+	  mhip = MBUF_TO(m, struct ip *);
+	  *mhip = *ip;
+		
+		/* No options */
+/*		if (hlen > sizeof (struct ip)) {
+ *			mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
+ *			mhip->ip_hl = mhlen >> 2;
+ *		}
+ */
+	  m->m_len = mhlen;
+	  mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF);
+	  if (ip->ip_off & IP_MF)
+	    mhip->ip_off |= IP_MF;
+	  if (off + len >= (u_int16_t)ip->ip_len)
+	    len = (u_int16_t)ip->ip_len - off;
+	  else 
+	    mhip->ip_off |= IP_MF;
+	  mhip->ip_len = htons((u_int16_t)(len + mhlen));
+	  
+	  if (mbuf_copy(m, m0, off, len) < 0) {
+	    error = -1;
+	    goto sendorfree;
+	  }
+	  
+	  mhip->ip_off = htons((u_int16_t)mhip->ip_off);
+	  mhip->ip_sum = 0;
+	  mhip->ip_sum = cksum(m, mhlen);
+	  *mnext = m;
+	  mnext = &m->m_nextpkt;
+	  ipstat.ips_ofragments++;
+	}
+	/*
+	 * Update first fragment by trimming what's been copied out
+	 * and updating header, then send each fragment (in order).
+	 */
+	m = m0;
+	mbuf_trim(m, hlen + firstlen - (u_int16_t)ip->ip_len);
+	ip->ip_len = htons((u_int16_t)m->m_len);
+	ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF));
+	ip->ip_sum = 0;
+	ip->ip_sum = cksum(m, hlen);
+sendorfree:
+	for (m = m0; m; m = m0) {
+		m0 = m->m_nextpkt;
+		m->m_nextpkt = 0;
+		if (error == 0)
+			if_output(so, m);
+		else
+			mbuf_free(m);
+	}
+
+	if (error == 0)
+		ipstat.ips_fragmented++;
+    }
+
+done:
+	return (error);
+
+bad:
+	mbuf_free(m0);
+	goto done;
+}
diff --git a/slirp2/libslirp.h b/slirp2/libslirp.h
new file mode 100644
index 0000000..e74e71e
--- /dev/null
+++ b/slirp2/libslirp.h
@@ -0,0 +1,50 @@
+#ifndef _LIBSLIRP_H
+#define _LIBSLIRP_H
+
+#include <stdint.h>
+#include "sockets.h"
+#ifdef _WIN32
+#  define WIN32_LEAN_AND_MEAN
+#  define socket_close  winsock2_socket_close3
+#  include <winsock2.h>
+#  undef socket_close
+#else
+#  include <sys/select.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int    inet_strtoip(const char*  str, uint32_t  *ip);
+char*  inet_iptostr(uint32_t  ip);
+
+void slirp_init(void);
+
+void slirp_select_fill(int *pnfds,
+                       fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds);
+
+void slirp_input(const uint8_t *pkt, int pkt_len);
+
+/* you must provide the following functions: */
+int slirp_can_output(void);
+void slirp_output(const uint8_t *pkt, int pkt_len);
+
+int slirp_redir(int is_udp, int host_port,
+                uint32_t guest_addr, int guest_port);
+
+int slirp_unredir(int is_udp, int host_port);
+
+int slirp_add_dns_server(const SockAddress*  dns_addr);
+int slirp_get_system_dns_servers(void);
+
+extern const char *tftp_prefix;
+extern char slirp_hostname[33];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/slirp2/main.h b/slirp2/main.h
new file mode 100644
index 0000000..159e5f4
--- /dev/null
+++ b/slirp2/main.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define TOWRITEMAX 512
+
+extern struct timeval tt;
+extern int link_up;
+extern int slirp_socket;
+extern int slirp_socket_unit;
+extern int slirp_socket_port;
+extern u_int32_t slirp_socket_addr;
+extern char *slirp_socket_passwd;
+extern int ctty_closed;
+
+/*
+ * Get the difference in 2 times from updtim()
+ * Allow for wraparound times, "just in case"
+ * x is the greater of the 2 (current time) and y is
+ * what it's being compared against.
+ */
+#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y)
+
+#define  DNS_ADDR_MAX  4
+
+extern char *slirp_tty;
+extern char *exec_shell;
+extern u_int curtime;
+extern fd_set *global_readfds, *global_writefds, *global_xfds;
+extern uint32_t ctl_addr_ip;
+extern uint32_t special_addr_ip;
+extern uint32_t alias_addr_ip;
+extern uint32_t our_addr_ip;
+extern uint32_t loopback_addr_ip;
+extern uint32_t dns_addr[DNS_ADDR_MAX];
+extern int      dns_addr_count;
+extern char *username;
+extern char *socket_path;
+extern int towrite_max;
+extern int ppp_exit;
+extern int so_options;
+extern int tcp_keepintvl;
+extern uint8_t client_ethaddr[6];
+
+#define PROTO_SLIP 0x1
+#ifdef USE_PPP
+#define PROTO_PPP 0x2
+#endif
+
+void if_encap(const uint8_t *ip_data, int ip_data_len);
diff --git a/slirp2/mbuf.c b/slirp2/mbuf.c
new file mode 100644
index 0000000..efc8141
--- /dev/null
+++ b/slirp2/mbuf.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/*
+ * mbuf's in SLiRP are much simpler than the real mbufs in
+ * FreeBSD.  They are fixed size, determined by the MTU,
+ * so that one whole packet can fit.  Mbuf's cannot be
+ * chained together.  If there's more data than the mbuf
+ * could hold, an external malloced buffer is pointed to
+ * by m_ext (and the data pointers) and M_EXT is set in
+ * the flags
+ */
+
+#include <slirp.h>
+
+static int      mbuf_alloced = 0;
+static MBufRec  m_freelist, m_usedlist;
+static int      mbuf_thresh = 30;
+static int      mbuf_max = 0;
+static int      msize;
+
+/* 
+ * How much room is in the mbuf, from m_data to the end of the mbuf
+ */
+#define M_ROOM(m) ((m->m_flags & M_EXT)? \
+			(((m)->m_ext + (m)->m_size) - (m)->m_data) \
+		   : \
+			(((m)->m_dat + (m)->m_size) - (m)->m_data))
+
+/*
+ * How much free room there is
+ */
+#define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len)
+
+
+void
+mbuf_init()
+{
+	m_freelist.m_next = m_freelist.m_prev = &m_freelist;
+	m_usedlist.m_next = m_usedlist.m_prev = &m_usedlist;
+	msize_init();
+}
+
+void
+msize_init()
+{
+	/*
+	 * Find a nice value for msize
+	 * XXX if_maxlinkhdr already in mtu
+	 */
+	msize = (if_mtu > if_mru ? if_mtu : if_mru) + 
+			if_maxlinkhdr + sizeof(struct m_hdr ) + 6;
+}
+
+static void
+mbuf_insque(MBuf  m, MBuf  head)
+{
+	m->m_next         = head->m_next;
+	m->m_prev         = head;
+	head->m_next      = m;
+	m->m_next->m_prev = m;
+}
+
+static void
+mbuf_remque(MBuf  m)
+{
+	m->m_prev->m_next = m->m_next;
+	m->m_next->m_prev = m->m_prev;
+	m->m_next = m->m_prev = m;
+}
+
+/*
+ * Get an mbuf from the free list, if there are none
+ * malloc one
+ * 
+ * Because fragmentation can occur if we alloc new mbufs and
+ * free old mbufs, we mark all mbufs above mbuf_thresh as M_DOFREE,
+ * which tells m_free to actually free() it
+ */
+MBuf
+mbuf_alloc(void)
+{
+	register MBuf m;
+	int flags = 0;
+	
+	DEBUG_CALL("mbuf_alloc");
+	
+	if (m_freelist.m_next == &m_freelist) {
+		m = (MBuf) malloc(msize);
+		if (m == NULL) goto end_error;
+		mbuf_alloced++;
+		if (mbuf_alloced > mbuf_thresh)
+			flags = M_DOFREE;
+		if (mbuf_alloced > mbuf_max)
+			mbuf_max = mbuf_alloced;
+	} else {
+		m = m_freelist.m_next;
+		mbuf_remque(m);
+	}
+	
+	/* Insert it in the used list */
+	mbuf_insque(m,&m_usedlist);
+	m->m_flags = (flags | M_USEDLIST);
+	
+	/* Initialise it */
+	m->m_size  = msize - sizeof(struct m_hdr);
+	m->m_data  = m->m_dat;
+	m->m_len   = 0;
+	m->m_next2 = NULL;
+	m->m_prev2 = NULL;
+end_error:
+	DEBUG_ARG("m = %lx", (long )m);
+	return m;
+}
+
+void
+mbuf_free(MBuf  m)
+{
+	
+  DEBUG_CALL("mbuf_free");
+  DEBUG_ARG("m = %lx", (long )m);
+	
+  if(m) {
+	/* Remove from m_usedlist */
+	if (m->m_flags & M_USEDLIST)
+	   mbuf_remque(m);
+	
+	/* If it's M_EXT, free() it */
+	if (m->m_flags & M_EXT)
+	   free(m->m_ext);
+
+	/*
+	 * Either free() it or put it on the free list
+	 */
+	if (m->m_flags & M_DOFREE) {
+		free(m);
+		mbuf_alloced--;
+	} else if ((m->m_flags & M_FREELIST) == 0) {
+		mbuf_insque(m,&m_freelist);
+		m->m_flags = M_FREELIST; /* Clobber other flags */
+	}
+  } /* if(m) */
+}
+
+/*
+ * Copy data from one mbuf to the end of
+ * the other.. if result is too big for one mbuf, malloc()
+ * an M_EXT data segment
+ */
+void
+mbuf_append(MBuf  m, MBuf  n)
+{
+	/*
+	 * If there's no room, realloc
+	 */
+	if (M_FREEROOM(m) < n->m_len)
+		mbuf_ensure(m, m->m_size+MINCSIZE);
+	
+	memcpy(m->m_data+m->m_len, n->m_data, n->m_len);
+	m->m_len += n->m_len;
+
+	mbuf_free(n);
+}
+
+
+/* make m size bytes large */
+void
+mbuf_ensure(MBuf  m, int  size)
+{
+	int datasize;
+
+	/* some compiles throw up on gotos.  This one we can fake. */
+    if(m->m_size > size) return;
+
+    if (m->m_flags & M_EXT) {
+        datasize = m->m_data - m->m_ext;
+        m->m_ext = (char *)realloc(m->m_ext,size);
+        m->m_data = m->m_ext + datasize;
+    } else {
+        char *dat;
+        datasize = m->m_data - m->m_dat;
+        dat      = (char *)malloc(size);
+        memcpy(dat, m->m_dat, m->m_size);
+
+        m->m_ext    = dat;
+        m->m_data   = m->m_ext + datasize;
+        m->m_flags |= M_EXT;
+    }
+ 
+    m->m_size = size;
+}
+
+
+
+void
+mbuf_trim(MBuf  m, int  len)
+{
+	if (m == NULL)
+		return;
+	if (len >= 0) {
+		/* Trim from head */
+		m->m_data += len;
+		m->m_len  -= len;
+	} else {
+		/* Trim from tail */
+		len       = -len;
+		m->m_len -= len;
+	}
+}
+
+
+/*
+ * Copy len bytes from m, starting off bytes into n
+ */
+int
+mbuf_copy(MBuf  n, MBuf  m, int  off, int  len)
+{
+	if (len > M_FREEROOM(n))
+		return -1;
+
+	memcpy((n->m_data + n->m_len), (m->m_data + off), len);
+	n->m_len += len;
+	return 0;
+}
+
+int
+mbuf_freeroom( MBuf  m )
+{
+	return  M_FREEROOM(m);
+}
+
+/*
+ * Given a pointer into an mbuf, return the mbuf
+ * XXX This is a kludge, I should eliminate the need for it
+ * Fortunately, it's not used often
+ */
+MBuf
+mbuf_from(void*  dat)
+{
+	MBuf  m;
+	
+	DEBUG_CALL("mbuf_from");
+	DEBUG_ARG("dat = %lx", (long )dat);
+
+	/* bug corrected for M_EXT buffers */
+	for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next) {
+	  if (m->m_flags & M_EXT) {
+	    if( (unsigned)((char*)dat - m->m_ext) < (unsigned)m->m_size )
+			goto Exit;
+	  } else {
+	    if( (unsigned)((char *)dat - m->m_dat) < (unsigned)m->m_size )
+	      goto Exit;
+	  }
+	}
+	m  = NULL;
+	DEBUG_ERROR((dfd, "mbuf_from failed"));
+Exit:	
+	return m;
+}
+
+void
+mbufstats()
+{
+	MBuf m;
+	int i;
+	
+    lprint(" \r\n");
+	
+	lprint("Mbuf stats:\r\n");
+
+	lprint("  %6d mbufs allocated (%d max)\r\n", mbuf_alloced, mbuf_max);
+	
+	i = 0;
+	for (m = m_freelist.m_next; m != &m_freelist; m = m->m_next)
+		i++;
+	lprint("  %6d mbufs on free list\r\n",  i);
+	
+	i = 0;
+	for (m = m_usedlist.m_next; m != &m_usedlist; m = m->m_next)
+		i++;
+	lprint("  %6d mbufs on used list\r\n",  i);
+        lprint("  %6d mbufs queued as packets\r\n\r\n", if_queued);
+}
diff --git a/slirp2/mbuf.h b/slirp2/mbuf.h
new file mode 100644
index 0000000..ed83372
--- /dev/null
+++ b/slirp2/mbuf.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)mbuf.h	8.3 (Berkeley) 1/21/94
+ * mbuf.h,v 1.9 1994/11/14 13:54:20 bde Exp
+ */
+
+#ifndef _MBUF_H_
+#define _MBUF_H_
+
+#define MINCSIZE 4096	/* Amount to increase mbuf if too small */
+
+/* flags for the mh_flags field */
+#define M_EXT			0x01	/* m_ext points to more (malloced) data */
+#define M_FREELIST		0x02	/* mbuf is on free list */
+#define M_USEDLIST		0x04	/* XXX mbuf is on used list (for dtom()) */
+#define M_DOFREE		0x08	/* when mbuf_free is called on the mbuf, free()
+					                                  * it rather than putting it on the free list */
+
+
+/* XXX About mbufs for slirp:
+ * Only one mbuf is ever used in a chain, for each "cell" of data.
+ * m_nextpkt points to the next packet, if fragmented.
+ * If the data is too large, the M_EXT is used, and a larger block
+ * is alloced.  Therefore, mbuf_free[m] must check for M_EXT and if set
+ * free the m_ext.  This is inefficient memory-wise, but who cares.
+ */
+
+/* XXX should union some of these! */
+/* header at beginning of each mbuf: */
+
+/**
+ *  m_next, m_prev   :: used to place the MBuf in free/used linked lists
+ *  m_next2, m_prev2 :: used to place the same MBuf in other linked lists
+ *  m_flags :: bit flags describing this MBuf
+ *  m_size  :: total size of MBuf buffer
+ *  m_so    :: socket this MBuf is attached to
+ *  m_data  :: pointer to current cursor in MBuf buffer
+ *  m_len   :: amount of data recorded in this MBuf
+ */
+#define  MBUF_HEADER           \
+	struct mbuf*   m_next;     \
+	struct mbuf*   m_prev;     \
+	struct mbuf*   m_next2;    \
+	struct mbuf*   m_prev2;    \
+	int            m_flags;    \
+	int            m_size;     \
+	struct socket* m_so;       \
+	caddr_t        m_data;     \
+	int            m_len;
+
+struct m_hdr {
+	MBUF_HEADER
+};
+
+typedef struct mbuf {
+	MBUF_HEADER
+	union M_dat {
+		char   m_dat_[1]; /* ANSI doesn't like 0 sized arrays */
+		char*  m_ext_;
+	} M_dat;
+} MBufRec, *MBuf;
+
+#define m_nextpkt	m_next2
+#define m_prevpkt	m_prev2
+#define m_dat		M_dat.m_dat_
+#define m_ext		M_dat.m_ext_
+
+#define ifq_prev m_prev
+#define ifq_next m_next
+
+#define ifs_prev m_prev2
+#define ifs_next m_next2
+
+#define ifq_so m_so
+
+void mbuf_init  (void);
+void msize_init (void);
+MBuf mbuf_alloc (void);
+void mbuf_free  (MBuf  m);
+void mbuf_append(MBuf  m1, MBuf  m2);
+void mbuf_ensure(MBuf  m, int  size);
+void mbuf_trim  (MBuf  m, int  len);
+int  mbuf_copy  (MBuf  m, MBuf  n, int  n_offset, int  n_length);
+
+#define MBUF_TO(m,t)  ((t)(m)->m_data)
+#define MBUF_FROM(d)  mbuf_from(d)
+MBuf  mbuf_from (void *);
+
+int   mbuf_freeroom( MBuf  m );
+
+#endif
diff --git a/slirp2/misc.c b/slirp2/misc.c
new file mode 100644
index 0000000..9a2699a
--- /dev/null
+++ b/slirp2/misc.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define WANT_SYS_IOCTL_H
+#include <slirp.h>
+
+u_int curtime, time_fasttimo, last_slowtimo, detach_time;
+u_int detach_wait = 600000;	/* 10 minutes */
+struct emu_t *tcpemu;
+
+int
+inet_strtoip(const char*  str, uint32_t  *ip)
+{
+    int  comp[4];
+
+    if (sscanf(str, "%d.%d.%d.%d", &comp[0], &comp[1], &comp[2], &comp[3]) != 4)
+        return -1;
+
+    if ((unsigned)comp[0] >= 256 ||
+        (unsigned)comp[1] >= 256 ||
+        (unsigned)comp[2] >= 256 ||
+        (unsigned)comp[3] >= 256)
+        return -1;
+
+    *ip = (uint32_t)((comp[0] << 24) | (comp[1] << 16) |
+                     (comp[2] << 8)  |  comp[3]);
+    return 0;
+}
+
+char*  inet_iptostr(uint32_t  ip)
+{
+    static char  buff[32];
+
+    snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
+             (ip >> 24) & 255,
+             (ip >> 16) & 255,
+             (ip >> 8) & 255,
+             ip & 255);
+    return buff;
+}
+
+/*
+ * Get our IP address and put it in our_addr
+ */
+void
+getouraddr()
+{
+    char*        hostname = host_name();
+    SockAddress  hostaddr;
+
+    our_addr_ip = loopback_addr_ip;
+
+    if (sock_address_init_resolve( &hostaddr, hostname, 0, 0 ) < 0)
+        return;
+
+    our_addr_ip = sock_address_get_ip(&hostaddr);
+    if (our_addr_ip == (uint32_t)-1)
+        our_addr_ip = loopback_addr_ip;
+}
+
+struct quehead {
+	struct quehead *qh_link;
+	struct quehead *qh_rlink;
+};
+
+inline void
+insque(void*  a, void*  b)
+{
+	register struct quehead *element = (struct quehead *) a;
+	register struct quehead *head = (struct quehead *) b;
+	element->qh_link = head->qh_link;
+	head->qh_link = (struct quehead *)element;
+	element->qh_rlink = (struct quehead *)head;
+	((struct quehead *)(element->qh_link))->qh_rlink
+	= (struct quehead *)element;
+}
+
+inline void
+remque(void*  a)
+{
+  register struct quehead *element = (struct quehead *) a;
+  ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
+  ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
+  element->qh_rlink = NULL;
+  /*  element->qh_link = NULL;  TCP FIN1 crashes if you do this.  Why ? */
+}
+
+/* #endif */
+
+
+#ifndef HAVE_STRERROR
+
+/*
+ * For systems with no strerror
+ */
+
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(int  error)
+{
+	if (error < sys_nerr)
+	   return sys_errlist[error];
+	else
+	   return "Unknown error.";
+}
+
+#endif
+
+
+
+#ifndef HAVE_STRDUP
+char *
+strdup(const char*  str)
+{
+	char *bptr;
+	int   len = strlen(str);
+
+	bptr = (char *)malloc(len+1);
+	memcpy(bptr, str, len+1);
+
+	return bptr;
+}
+#endif
+
+
+int (*lprint_print) _P((void *, const char *, va_list));
+char *lprint_ptr, *lprint_ptr2, **lprint_arg;
+
+void
+lprint(const char *format, ...)
+{
+	va_list args;
+
+    va_start(args, format);
+	if (lprint_print)
+	   lprint_ptr += (*lprint_print)(*lprint_arg, format, args);
+
+	/* Check if they want output to be logged to file as well */
+	if (lfd) {
+		/*
+		 * Remove \r's
+		 * otherwise you'll get ^M all over the file
+		 */
+		int len = strlen(format);
+		char *bptr1, *bptr2;
+
+		bptr1 = bptr2 = strdup(format);
+
+		while (len--) {
+			if (*bptr1 == '\r')
+			   memcpy(bptr1, bptr1+1, len+1);
+			else
+			   bptr1++;
+		}
+		vfprintf(lfd, bptr2, args);
+		free(bptr2);
+	}
+	va_end(args);
+}
+
+void
+add_emu(char* buff)
+{
+	u_int lport, fport;
+	u_int8_t tos = 0, emu = 0;
+	char buff1[256], buff2[256], buff4[128];
+	char *buff3 = buff4;
+	struct emu_t *emup;
+	struct socket *so;
+
+	if (sscanf(buff, "%256s %256s", buff2, buff1) != 2) {
+		lprint("Error: Bad arguments\r\n");
+		return;
+	}
+
+	if (sscanf(buff1, "%d:%d", &lport, &fport) != 2) {
+		lport = 0;
+		if (sscanf(buff1, "%d", &fport) != 1) {
+			lprint("Error: Bad first argument\r\n");
+			return;
+		}
+	}
+
+	if (sscanf(buff2, "%128[^:]:%128s", buff1, buff3) != 2) {
+		buff3 = 0;
+		if (sscanf(buff2, "%256s", buff1) != 1) {
+			lprint("Error: Bad second argument\r\n");
+			return;
+		}
+	}
+
+	if (buff3) {
+		if (strcmp(buff3, "lowdelay") == 0)
+		   tos = IPTOS_LOWDELAY;
+		else if (strcmp(buff3, "throughput") == 0)
+		   tos = IPTOS_THROUGHPUT;
+		else {
+			lprint("Error: Expecting \"lowdelay\"/\"throughput\"\r\n");
+			return;
+		}
+	}
+
+	if (strcmp(buff1, "ftp") == 0)
+	   emu = EMU_FTP;
+	else if (strcmp(buff1, "irc") == 0)
+	   emu = EMU_IRC;
+	else if (strcmp(buff1, "none") == 0)
+	   emu = EMU_NONE; /* ie: no emulation */
+	else {
+		lprint("Error: Unknown service\r\n");
+		return;
+	}
+
+	/* First, check that it isn't already emulated */
+	for (emup = tcpemu; emup; emup = emup->next) {
+		if (emup->lport == lport && emup->fport == fport) {
+			lprint("Error: port already emulated\r\n");
+			return;
+		}
+	}
+
+	/* link it */
+	emup = (struct emu_t *)malloc(sizeof (struct emu_t));
+	emup->lport = (u_int16_t)lport;
+	emup->fport = (u_int16_t)fport;
+	emup->tos = tos;
+	emup->emu = emu;
+	emup->next = tcpemu;
+	tcpemu = emup;
+
+	/* And finally, mark all current sessions, if any, as being emulated */
+	for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+		if ((lport && lport == so->so_laddr_port) ||
+		    (fport && fport == so->so_faddr_port)) {
+			if (emu)
+			   so->so_emu = emu;
+			if (tos)
+			   so->so_iptos = tos;
+		}
+	}
+
+	lprint("Adding emulation for %s to port %d/%d\r\n", buff1, emup->lport, emup->fport);
+}
+
+#ifdef BAD_SPRINTF
+
+#undef vsprintf
+#undef sprintf
+
+/*
+ * Some BSD-derived systems have a sprintf which returns char *
+ */
+
+int
+vsprintf_len(char* string, const char* format, va_list args)
+{
+	vsprintf(string, format, args);
+	return strlen(string);
+}
+
+int
+sprintf_len(char *string, const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	vsprintf(string, format, args);
+	return strlen(string);
+}
+
+#endif
+
diff --git a/slirp2/misc.h b/slirp2/misc.h
new file mode 100644
index 0000000..aa4a0ce
--- /dev/null
+++ b/slirp2/misc.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+extern u_int curtime, time_fasttimo, last_slowtimo, detach_time, detach_wait;
+
+extern int (*lprint_print) _P((void *, const char *, va_list));
+extern char *lprint_ptr, *lprint_ptr2, **lprint_arg;
+//extern SBuf lprint_sb;
+
+#ifndef HAVE_STRDUP
+char *strdup _P((const char *));
+#endif
+
+void do_wait _P((int));
+
+#define EMU_NONE 0x0
+
+/* TCP emulations */
+#define EMU_CTL 0x1
+#define EMU_FTP 0x2
+#define EMU_KSH 0x3
+#define EMU_IRC 0x4
+#define EMU_REALAUDIO 0x5
+#define EMU_RLOGIN 0x6
+#define EMU_IDENT 0x7
+#define EMU_RSH 0x8
+
+#define EMU_NOCONNECT 0x10	/* Don't connect */
+
+/* UDP emulations */
+#define EMU_TALK	0x1
+#define EMU_NTALK	0x2
+#define EMU_CUSEEME	0x3
+
+struct tos_t {
+	u_int16_t lport;
+	u_int16_t fport;
+	u_int8_t tos;
+	u_int8_t emu;
+};
+
+struct emu_t {
+	u_int16_t lport;
+	u_int16_t fport;
+	u_int8_t tos;
+	u_int8_t emu;
+	struct emu_t *next;
+};
+
+extern struct emu_t *tcpemu;
+
+void getouraddr _P((void));
+inline  void slirp_insque  _P((void *, void *));
+inline  void slirp_remque  _P((void *));
+void add_emu _P((char *));
+
+#endif
diff --git a/slirp2/sbuf.c b/slirp2/sbuf.c
new file mode 100644
index 0000000..abececa
--- /dev/null
+++ b/slirp2/sbuf.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+void
+sbuf_free(SBuf  sb)
+{
+	free(sb->sb_data);
+}
+
+void
+sbuf_drop(SBuf  sb, int  num)
+{
+	/* 
+	 * We can only drop how much we have
+	 * This should never succeed 
+	 */
+	if(num > sb->sb_cc)
+		num = sb->sb_cc;
+	sb->sb_cc -= num;
+	sb->sb_rptr += num;
+	if(sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
+		sb->sb_rptr -= sb->sb_datalen;
+   
+}
+
+void
+sbuf_reserve(SBuf  sb, int  size)
+{
+    if (sb->sb_datalen == size)
+        return;
+
+    sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)realloc(sb->sb_data, size);
+    sb->sb_cc = 0;
+    if (sb->sb_wptr)
+       sb->sb_datalen = size;
+    else
+       sb->sb_datalen = 0;
+}
+
+/*
+ * Try and write() to the socket, whatever doesn't get written
+ * append to the buffer... for a host with a fast net connection,
+ * this prevents an unnecessary copy of the data
+ * (the socket is non-blocking, so we won't hang)
+ */
+void
+sbuf_append(struct socket *so, MBuf  m)
+{
+	int ret = 0;
+
+	DEBUG_CALL("sbuf_append");
+	DEBUG_ARG("so = %lx", (long)so);
+	DEBUG_ARG("m = %lx", (long)m);
+	DEBUG_ARG("m->m_len = %d", m->m_len);
+	
+	/* Shouldn't happen, but...  e.g. foreign host closes connection */
+	if (m->m_len <= 0) {
+		mbuf_free(m);
+		return;
+	}
+	
+	/*
+	 * If there is urgent data, call sosendoob
+	 * if not all was sent, sowrite will take care of the rest
+	 * (The rest of this function is just an optimisation)
+	 */
+	if (so->so_urgc) {
+		sbuf_appendsb(&so->so_rcv, m);
+		mbuf_free(m);
+		sosendoob(so);
+		return;
+	}
+	
+	/*
+	 * We only write if there's nothing in the buffer,
+	 * ottherwise it'll arrive out of order, and hence corrupt
+	 */
+	if (!so->so_rcv.sb_cc) {
+	   ret = socket_send(so->s, m->m_data, m->m_len);
+        }
+
+	if (ret <= 0) {
+		/* 
+		 * Nothing was written
+		 * It's possible that the socket has closed, but
+		 * we don't need to check because if it has closed,
+		 * it will be detected in the normal way by soread()
+		 */
+		sbuf_appendsb(&so->so_rcv, m);
+	} else if (ret != m->m_len) {
+		/*
+		 * Something was written, but not everything..
+		 * sbuf_appendsb the rest
+		 */
+		m->m_len -= ret;
+		m->m_data += ret;
+		sbuf_appendsb(&so->so_rcv, m);
+	} /* else */
+	/* Whatever happened, we free the mbuf */
+	mbuf_free(m);
+}
+
+/*
+ * Copy the data from m into sb
+ * The caller is responsible to make sure there's enough room
+ */
+void
+sbuf_appendsb(SBuf  sb, MBuf  m)
+{
+	int len, n,  nn;
+	
+	len = m->m_len;
+
+	if (sb->sb_wptr < sb->sb_rptr) {
+		n = sb->sb_rptr - sb->sb_wptr;
+		if (n > len) n = len;
+		memcpy(sb->sb_wptr, m->m_data, n);
+	} else {
+		/* Do the right edge first */
+		n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
+		if (n > len) n = len;
+		memcpy(sb->sb_wptr, m->m_data, n);
+		len -= n;
+		if (len) {
+			/* Now the left edge */
+			nn = sb->sb_rptr - sb->sb_data;
+			if (nn > len) nn = len;
+			memcpy(sb->sb_data,m->m_data+n,nn);
+			n += nn;
+		}
+	}
+
+	sb->sb_cc   += n;
+	sb->sb_wptr += n;
+	if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
+		sb->sb_wptr -= sb->sb_datalen;
+}
+
+/*
+ * Copy data from sbuf to a normal, straight buffer
+ * Don't update the sbuf rptr, this will be
+ * done in sbdrop when the data is acked
+ */
+void
+sbuf_copy(SBuf  sb, int  off, int  len, char*  to)
+{
+	char *from;
+	
+	from = sb->sb_rptr + off;
+	if (from >= sb->sb_data + sb->sb_datalen)
+		from -= sb->sb_datalen;
+
+	if (from < sb->sb_wptr) {
+		if (len > sb->sb_cc) len = sb->sb_cc;
+		memcpy(to,from,len);
+	} else {
+		/* re-use off */
+		off = (sb->sb_data + sb->sb_datalen) - from;
+		if (off > len) off = len;
+		memcpy(to,from,off);
+		len -= off;
+		if (len)
+		   memcpy(to+off,sb->sb_data,len);
+	}
+}
+		
diff --git a/slirp2/sbuf.h b/slirp2/sbuf.h
new file mode 100644
index 0000000..05fa01a
--- /dev/null
+++ b/slirp2/sbuf.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SBUF_H_
+#define _SBUF_H_
+
+#include "mbuf.h"
+#include <stddef.h>
+
+/* a SBuf is a simple circular buffer used to hold RX and TX data in a struct socket
+ */
+
+typedef struct sbuf {
+	unsigned  sb_cc;      /* actual chars in buffer */
+	unsigned  sb_datalen; /* Length of data  */
+	char*     sb_wptr;    /* write pointer. points to where the next
+			                                       * bytes should be written in the sbuf */
+	char*     sb_rptr;    /* read pointer. points to where the next
+                                                                       * byte should be read from the sbuf */
+	char*     sb_data;	/* Actual data */
+} SBufRec, *SBuf;
+
+void sbuf_free    (SBuf  sb);
+void sbuf_drop    (SBuf  sb, int  num);
+void sbuf_reserve (SBuf  sb, int  count);
+void sbuf_append  (struct socket *so, MBuf  m);
+void sbuf_appendsb(SBuf  sb, MBuf  m);
+void sbuf_copy    (SBuf  sb, int  offset, int  length, char *to);
+
+#define sbuf_flush(sb) sbuf_drop((sb),(sb)->sb_cc)
+#define sbuf_space(sb) ((sb)->sb_datalen - (sb)->sb_cc)
+
+#endif
diff --git a/slirp2/slirp.c b/slirp2/slirp.c
new file mode 100644
index 0000000..23a3e3c
--- /dev/null
+++ b/slirp2/slirp.c
@@ -0,0 +1,762 @@
+#include "slirp.h"
+#include "proxy_common.h"
+#include "android/utils/debug.h"  /* for dprint */
+#include "android/utils/bufprint.h"
+#include "android/android.h"
+#include "sockets.h"
+
+#define  D(...)   VERBOSE_PRINT(slirp,__VA_ARGS__)
+#define  DN(...)  do { if (VERBOSE_CHECK(slirp)) dprintn(__VA_ARGS__); } while (0)
+
+/* host address */
+uint32_t our_addr_ip;
+/* host dns address */
+uint32_t dns_addr[DNS_ADDR_MAX];
+int      dns_addr_count;
+
+/* host loopback address */
+uint32_t loopback_addr_ip;
+
+/* address for slirp virtual addresses */
+uint32_t  special_addr_ip;
+
+/* virtual address alias for host */
+uint32_t alias_addr_ip;
+
+const uint8_t special_ethaddr[6] = {
+    0x52, 0x54, 0x00, 0x12, 0x35, 0x00
+};
+
+uint8_t client_ethaddr[6] = {
+    0x52, 0x54, 0x00, 0x12, 0x34, 0x56
+};
+
+int do_slowtimo;
+int link_up;
+struct timeval tt;
+FILE *lfd;
+
+/* XXX: suppress those select globals */
+fd_set *global_readfds, *global_writefds, *global_xfds;
+
+char slirp_hostname[33];
+
+int slirp_add_dns_server(const SockAddress*  new_dns_addr)
+{
+    int   dns_ip;
+
+    if (dns_addr_count >= DNS_ADDR_MAX)
+        return -1;
+
+    dns_ip = sock_address_get_ip(new_dns_addr);
+    if (dns_ip < 0)
+        return -1;
+
+    dns_addr[dns_addr_count++] = dns_ip;
+    return 0;
+}
+
+
+#ifdef _WIN32
+
+int slirp_get_system_dns_servers()
+{
+    FIXED_INFO *FixedInfo=NULL;
+    ULONG    BufLen;
+    DWORD    ret;
+    IP_ADDR_STRING *pIPAddr;
+
+    if (dns_addr_count > 0)
+        return dns_addr_count;
+
+    FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));
+    BufLen = sizeof(FIXED_INFO);
+
+    if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {
+        if (FixedInfo) {
+            GlobalFree(FixedInfo);
+            FixedInfo = NULL;
+        }
+        FixedInfo = GlobalAlloc(GPTR, BufLen);
+    }
+
+    if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {
+        printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret );
+        if (FixedInfo) {
+            GlobalFree(FixedInfo);
+            FixedInfo = NULL;
+        }
+        return -1;
+    }
+
+    D( "DNS Servers:");
+    pIPAddr = &(FixedInfo->DnsServerList);
+    while (pIPAddr && dns_addr_count < DNS_ADDR_MAX) {
+        uint32_t  ip;
+        D( "  %s", pIPAddr->IpAddress.String );
+        if (inet_strtoip(pIPAddr->IpAddress.String, &ip) == 0) {
+            if (ip == loopback_addr_ip)
+                ip = our_addr_ip;
+            if (dns_addr_count < DNS_ADDR_MAX)
+                dns_addr[dns_addr_count++] = ip;
+        }
+        pIPAddr = pIPAddr->Next;
+    }
+
+    if (FixedInfo) {
+        GlobalFree(FixedInfo);
+        FixedInfo = NULL;
+    }
+    if (dns_addr_count <= 0)
+        return -1;
+
+    return dns_addr_count;
+}
+
+#else
+
+int slirp_get_system_dns_servers(void)
+{
+    char buff[512];
+    char buff2[256];
+    FILE *f;
+
+    if (dns_addr_count > 0)
+        return dns_addr_count;
+
+#ifdef CONFIG_DARWIN
+    /* on Darwin /etc/resolv.conf is a symlink to /private/var/run/resolv.conf
+     * in some siutations, the symlink can be destroyed and the system will not
+     * re-create it. Darwin-aware applications will continue to run, but "legacy"
+     * Unix ones will not.
+     */
+     f = fopen("/private/var/run/resolv.conf", "r");
+     if (!f)
+        f = fopen("/etc/resolv.conf", "r");  /* desperate attempt to sanity */
+#else
+    f = fopen("/etc/resolv.conf", "r");
+#endif
+    if (!f)
+        return -1;
+
+    DN("emulator: IP address of your DNS(s): ");
+    while (fgets(buff, 512, f) != NULL) {
+        if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {
+            uint32_t  tmp_ip;
+
+            if (inet_strtoip(buff2, &tmp_ip) < 0)
+                continue;
+            if (tmp_ip == loopback_addr_ip)
+                tmp_ip = our_addr_ip;
+            if (dns_addr_count < DNS_ADDR_MAX) {
+                dns_addr[dns_addr_count++] = tmp_ip;
+                if (dns_addr_count > 1)
+                    DN(", ");
+                DN("%s", inet_iptostr(tmp_ip));
+            } else {
+                DN("(more)");
+                break;
+            }
+        }
+    }
+    DN("\n");
+    fclose(f);
+
+    if (!dns_addr_count)
+        return -1;
+
+    return dns_addr_count;
+}
+
+#endif
+
+extern void  slirp_init_shapers();
+
+void slirp_init(void)
+{
+#if DEBUG
+    int   slirp_logmask = 0;
+    char  slirp_logfile[512];
+    {
+        const char*  env = getenv( "ANDROID_SLIRP_LOGMASK" );
+        if (env != NULL)
+            slirp_logmask = atoi(env);
+         else if (VERBOSE_CHECK(slirp))
+            slirp_logmask = DEBUG_DEFAULT;
+    }
+
+    {
+        char*  p   = slirp_logfile;
+        char*  end = p + sizeof(slirp_logfile);
+
+        p = bufprint_temp_file( p, end, "slirp.log" );
+        if (p >= end) {
+            dprint( "cannot create slirp log file in temporary directory" );
+            slirp_logmask = 0;
+        }
+    }
+    if (slirp_logmask) {
+        dprint( "sending slirp logs with mask %x to %s", slirp_logmask, slirp_logfile );
+        debug_init( slirp_logfile, slirp_logmask );
+    }
+#endif
+
+    link_up = 1;
+
+    if_init();
+    ip_init();
+
+    /* Initialise mbufs *after* setting the MTU */
+    mbuf_init();
+
+    /* set default addresses */
+    inet_strtoip("127.0.0.1", &loopback_addr_ip);
+
+    if (dns_addr_count == 0) {
+        if (slirp_get_system_dns_servers() < 0) {
+            dns_addr[0]    = loopback_addr_ip;
+            dns_addr_count = 1;
+            fprintf (stderr, "Warning: No DNS servers found\n");
+        }
+    }
+
+    inet_strtoip(CTL_SPECIAL, &special_addr_ip);
+
+    alias_addr_ip = special_addr_ip | CTL_ALIAS;
+    getouraddr();
+
+    slirp_init_shapers();
+}
+
+#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define UPD_NFDS(x) if (nfds < (x)) nfds = (x)
+
+/*
+ * curtime kept to an accuracy of 1ms
+ */
+#ifdef _WIN32
+static void updtime(void)
+{
+    struct _timeb tb;
+
+    _ftime(&tb);
+    curtime = (u_int)tb.time * (u_int)1000;
+    curtime += (u_int)tb.millitm;
+}
+#else
+static void updtime(void)
+{
+	gettimeofday(&tt, 0);
+
+	curtime = (u_int)tt.tv_sec * (u_int)1000;
+	curtime += (u_int)tt.tv_usec / (u_int)1000;
+
+	if ((tt.tv_usec % 1000) >= 500)
+	   curtime++;
+}
+#endif
+
+void slirp_select_fill(int *pnfds,
+                       fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+    struct socket *so, *so_next;
+    struct timeval timeout;
+    int nfds;
+    int tmp_time;
+
+    /* fail safe */
+    global_readfds = NULL;
+    global_writefds = NULL;
+    global_xfds = NULL;
+
+    nfds = *pnfds;
+    /*
+        * First, TCP sockets
+        */
+    do_slowtimo = 0;
+    if (link_up) {
+        /*
+            * *_slowtimo needs calling if there are IP fragments
+            * in the fragment queue, or there are TCP connections active
+            */
+        do_slowtimo = ((tcb.so_next != &tcb) ||
+                      (&ipq.ip_link != ipq.ip_link.next));
+
+        for (so = tcb.so_next; so != &tcb; so = so_next) {
+            so_next = so->so_next;
+
+               /*
+                * See if we need a tcp_fasttimo
+                */
+            if (time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK)
+                time_fasttimo = curtime; /* Flag when we want a fasttimo */
+
+               /*
+                * NOFDREF can include still connecting to local-host,
+                * newly socreated() sockets etc. Don't want to select these.
+                */
+            if (so->so_state & SS_NOFDREF || so->s == -1)
+                continue;
+
+                        /*
+                         * don't register proxified socked connections here
+                         */
+                         if ((so->so_state & SS_PROXIFIED) != 0)
+                            continue;
+
+               /*
+                * Set for reading sockets which are accepting
+                */
+            if (so->so_state & SS_FACCEPTCONN) {
+                                FD_SET(so->s, readfds);
+                UPD_NFDS(so->s);
+                continue;
+            }
+
+               /*
+                * Set for writing sockets which are connecting
+                */
+            if (so->so_state & SS_ISFCONNECTING) {
+                FD_SET(so->s, writefds);
+                UPD_NFDS(so->s);
+                continue;
+            }
+
+               /*
+                * Set for writing if we are connected, can send more, and
+                * we have something to send
+                */
+            if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {
+                FD_SET(so->s, writefds);
+                UPD_NFDS(so->s);
+            }
+
+               /*
+                * Set for reading (and urgent data) if we are connected, can
+                * receive more, and we have room for it XXX /2 ?
+                */
+            if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) {
+                FD_SET(so->s, readfds);
+                FD_SET(so->s, xfds);
+                UPD_NFDS(so->s);
+            }
+        }
+
+        /*
+            * UDP sockets
+            */
+        for (so = udb.so_next; so != &udb; so = so_next) {
+            so_next = so->so_next;
+
+            if ((so->so_state & SS_PROXIFIED) != 0)
+                continue;
+
+               /*
+                * See if it's timed out
+                */
+            if (so->so_expire) {
+                if (so->so_expire <= curtime) {
+                    udp_detach(so);
+                    continue;
+                } else
+                    do_slowtimo = 1; /* Let socket expire */
+            }
+
+               /*
+                * When UDP packets are received from over the
+                * link, they're sendto()'d straight away, so
+                * no need for setting for writing
+                * Limit the number of packets queued by this session
+                * to 4.  Note that even though we try and limit this
+                * to 4 packets, the session could have more queued
+                * if the packets needed to be fragmented
+                * (XXX <= 4 ?)
+                */
+            if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {
+                FD_SET(so->s, readfds);
+                UPD_NFDS(so->s);
+            }
+        }
+    }
+
+       /*
+        * Setup timeout to use minimum CPU usage, especially when idle
+        */
+
+       /*
+        * First, see the timeout needed by *timo
+        */
+    timeout.tv_sec = 0;
+    timeout.tv_usec = -1;
+    /*
+        * If a slowtimo is needed, set timeout to 500ms from the last
+        * slow timeout. If a fast timeout is needed, set timeout within
+        * 200ms of when it was requested.
+        */
+    if (do_slowtimo) {
+        /* XXX + 10000 because some select()'s aren't that accurate */
+        timeout.tv_usec = ((500 - (curtime - last_slowtimo)) * 1000) + 10000;
+        if (timeout.tv_usec < 0)
+            timeout.tv_usec = 0;
+        else if (timeout.tv_usec > 510000)
+            timeout.tv_usec = 510000;
+
+        /* Can only fasttimo if we also slowtimo */
+        if (time_fasttimo) {
+            tmp_time = (200 - (curtime - time_fasttimo)) * 1000;
+            if (tmp_time < 0)
+                tmp_time = 0;
+
+            /* Choose the smallest of the 2 */
+            if (tmp_time < timeout.tv_usec)
+                timeout.tv_usec = (u_int)tmp_time;
+        }
+    }
+
+        /*
+         * now, the proxified sockets
+         */
+        proxy_manager_select_fill(&nfds, readfds, writefds, xfds);
+
+        *pnfds = nfds;
+}
+
+void slirp_select_poll(fd_set *readfds, fd_set *writefds, fd_set *xfds)
+{
+    struct socket *so, *so_next;
+    int ret;
+
+    global_readfds = readfds;
+    global_writefds = writefds;
+    global_xfds = xfds;
+
+	/* Update time */
+	updtime();
+
+	/*
+	 * See if anything has timed out
+	 */
+	if (link_up) {
+		if (time_fasttimo && ((curtime - time_fasttimo) >= 2)) {
+			tcp_fasttimo();
+			time_fasttimo = 0;
+		}
+		if (do_slowtimo && ((curtime - last_slowtimo) >= 499)) {
+			ip_slowtimo();
+			tcp_slowtimo();
+			last_slowtimo = curtime;
+		}
+	}
+
+	/*
+	 * Check sockets
+	 */
+	if (link_up) {
+		/*
+		 * Check TCP sockets
+		 */
+		for (so = tcb.so_next; so != &tcb; so = so_next) {
+			so_next = so->so_next;
+
+			/*
+			 * FD_ISSET is meaningless on these sockets
+			 * (and they can crash the program)
+			 */
+			if (so->so_state & SS_NOFDREF || so->s == -1)
+			   continue;
+
+                        /*
+                         * proxified sockets are polled later in this
+                         * function.
+                         */
+                        if ((so->so_state & SS_PROXIFIED) != 0)
+                            continue;
+
+			/*
+			 * Check for URG data
+			 * This will soread as well, so no need to
+			 * test for readfds below if this succeeds
+			 */
+			if (FD_ISSET(so->s, xfds))
+			   sorecvoob(so);
+			/*
+			 * Check sockets for reading
+			 */
+			else if (FD_ISSET(so->s, readfds)) {
+				/*
+				 * Check for incoming connections
+				 */
+				if (so->so_state & SS_FACCEPTCONN) {
+					tcp_connect(so);
+					continue;
+				} /* else */
+				ret = soread(so);
+
+				/* Output it if we read something */
+				if (ret > 0)
+				   tcp_output(sototcpcb(so));
+			}
+
+			/*
+			 * Check sockets for writing
+			 */
+			if (FD_ISSET(so->s, writefds)) {
+			  /*
+			   * Check for non-blocking, still-connecting sockets
+			   */
+			  if (so->so_state & SS_ISFCONNECTING) {
+			    /* Connected */
+			    so->so_state &= ~SS_ISFCONNECTING;
+
+			    ret = socket_send(so->s, (char*)&ret, 0);
+			    if (ret < 0) {
+			      /* XXXXX Must fix, zero bytes is a NOP */
+			      if (errno == EAGAIN || errno == EWOULDBLOCK ||
+				  errno == EINPROGRESS || errno == ENOTCONN)
+				continue;
+
+			      /* else failed */
+			      so->so_state = SS_NOFDREF;
+			    }
+			    /* else so->so_state &= ~SS_ISFCONNECTING; */
+
+			    /*
+			     * Continue tcp_input
+			     */
+			    tcp_input((MBuf )NULL, sizeof(struct ip), so);
+			    /* continue; */
+			  } else
+			    ret = sowrite(so);
+			  /*
+			   * XXXXX If we wrote something (a lot), there
+			   * could be a need for a window update.
+			   * In the worst case, the remote will send
+			   * a window probe to get things going again
+			   */
+			}
+
+			/*
+			 * Probe a still-connecting, non-blocking socket
+			 * to check if it's still alive
+	 	 	 */
+#ifdef PROBE_CONN
+			if (so->so_state & SS_ISFCONNECTING) {
+			  ret = socket_recv(so->s, (char *)&ret, 0);
+
+			  if (ret < 0) {
+			    /* XXX */
+			    if (errno == EAGAIN || errno == EWOULDBLOCK ||
+				errno == EINPROGRESS || errno == ENOTCONN)
+			      continue; /* Still connecting, continue */
+
+			    /* else failed */
+			    so->so_state = SS_NOFDREF;
+
+			    /* tcp_input will take care of it */
+			  } else {
+			    ret = socket_send(so->s, &ret, 0,0);
+			    if (ret < 0) {
+			      /* XXX */
+			      if (errno == EAGAIN || errno == EWOULDBLOCK ||
+				  errno == EINPROGRESS || errno == ENOTCONN)
+				continue;
+			      /* else failed */
+			      so->so_state = SS_NOFDREF;
+			    } else
+			      so->so_state &= ~SS_ISFCONNECTING;
+
+			  }
+			  tcp_input((MBuf )NULL, sizeof(struct ip),so);
+			} /* SS_ISFCONNECTING */
+#endif
+		}
+
+		/*
+		 * Now UDP sockets.
+		 * Incoming packets are sent straight away, they're not buffered.
+		 * Incoming UDP data isn't buffered either.
+		 */
+		for (so = udb.so_next; so != &udb; so = so_next) {
+			so_next = so->so_next;
+
+                        if ((so->so_state & SS_PROXIFIED) != 0)
+                            continue;
+
+			if (so->s != -1 && FD_ISSET(so->s, readfds)) {
+                            sorecvfrom(so);
+                        }
+		}
+	}
+
+        /*
+         * Now the proxified sockets
+         */
+        proxy_manager_poll(readfds, writefds, xfds);
+
+	/*
+	 * See if we can start outputting
+	 */
+	if (if_queued && link_up)
+	   if_start();
+
+	/* clear global file descriptor sets.
+	 * these reside on the stack in vl.c
+	 * so they're unusable if we're not in
+	 * slirp_select_fill or slirp_select_poll.
+	 */
+	 global_readfds = NULL;
+	 global_writefds = NULL;
+	 global_xfds = NULL;
+}
+
+#define ETH_ALEN 6
+#define ETH_HLEN 14
+
+#define ETH_P_IP	0x0800		/* Internet Protocol packet	*/
+#define ETH_P_ARP	0x0806		/* Address Resolution packet	*/
+
+#define	ARPOP_REQUEST	1		/* ARP request			*/
+#define	ARPOP_REPLY	2		/* ARP reply			*/
+
+struct ethhdr
+{
+	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/
+	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/
+	unsigned short	h_proto;		/* packet type ID field	*/
+};
+
+struct arphdr
+{
+	unsigned short	ar_hrd;		/* format of hardware address	*/
+	unsigned short	ar_pro;		/* format of protocol address	*/
+	unsigned char	ar_hln;		/* length of hardware address	*/
+	unsigned char	ar_pln;		/* length of protocol address	*/
+	unsigned short	ar_op;		/* ARP opcode (command)		*/
+
+	 /*
+	  *	 Ethernet looks like this : This bit is variable sized however...
+	  */
+	unsigned char		ar_sha[ETH_ALEN];	/* sender hardware address	*/
+	unsigned char		ar_sip[4];		/* sender IP address		*/
+	unsigned char		ar_tha[ETH_ALEN];	/* target hardware address	*/
+	unsigned char		ar_tip[4];		/* target IP address		*/
+};
+
+void arp_input(const uint8_t *pkt, int pkt_len)
+{
+    struct ethhdr *eh = (struct ethhdr *)pkt;
+    struct arphdr *ah = (struct arphdr *)(pkt + ETH_HLEN);
+    uint8_t arp_reply[ETH_HLEN + sizeof(struct arphdr)];
+    struct ethhdr *reh = (struct ethhdr *)arp_reply;
+    struct arphdr *rah = (struct arphdr *)(arp_reply + ETH_HLEN);
+    int ar_op;
+
+    ar_op = ntohs(ah->ar_op);
+    switch(ar_op) {
+        uint32_t   ar_tip_ip;
+    case ARPOP_REQUEST:
+        ar_tip_ip = (ah->ar_tip[0] << 24) | (ah->ar_tip[1] << 16) | (ah->ar_tip[2] << 8);
+        if (ar_tip_ip == special_addr_ip) {
+            if ( CTL_IS_DNS(ah->ar_tip[3]) || ah->ar_tip[3] == CTL_ALIAS)
+                goto arp_ok;
+            return;
+        arp_ok:
+            /* XXX: make an ARP request to have the client address */
+            memcpy(client_ethaddr, eh->h_source, ETH_ALEN);
+
+            /* ARP request for alias/dns mac address */
+            memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);
+            memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 1);
+            reh->h_source[5] = ah->ar_tip[3];
+            reh->h_proto = htons(ETH_P_ARP);
+
+            rah->ar_hrd = htons(1);
+            rah->ar_pro = htons(ETH_P_IP);
+            rah->ar_hln = ETH_ALEN;
+            rah->ar_pln = 4;
+            rah->ar_op = htons(ARPOP_REPLY);
+            memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);
+            memcpy(rah->ar_sip, ah->ar_tip, 4);
+            memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);
+            memcpy(rah->ar_tip, ah->ar_sip, 4);
+            slirp_output(arp_reply, sizeof(arp_reply));
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+void slirp_input(const uint8_t *pkt, int pkt_len)
+{
+    MBuf m;
+    int proto;
+
+    if (pkt_len < ETH_HLEN)
+        return;
+
+    proto = ntohs(*(uint16_t *)(pkt + 12));
+    switch(proto) {
+    case ETH_P_ARP:
+        arp_input(pkt, pkt_len);
+        break;
+    case ETH_P_IP:
+        m = mbuf_alloc();
+        if (!m)
+            return;
+        /* Note: we add to align the IP header */
+        m->m_len = pkt_len + 2;
+        memcpy(m->m_data + 2, pkt, pkt_len);
+
+        m->m_data += 2 + ETH_HLEN;
+        m->m_len -= 2 + ETH_HLEN;
+
+        ip_input(m);
+        break;
+    default:
+        break;
+    }
+}
+
+/* output the IP packet to the ethernet device */
+void if_encap(const uint8_t *ip_data, int ip_data_len)
+{
+    uint8_t buf[1600];
+    struct ethhdr *eh = (struct ethhdr *)buf;
+
+    if (ip_data_len + ETH_HLEN > sizeof(buf))
+        return;
+
+    memcpy(eh->h_dest, client_ethaddr, ETH_ALEN);
+    memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 1);
+    /* XXX: not correct */
+    eh->h_source[5] = CTL_ALIAS;
+    eh->h_proto = htons(ETH_P_IP);
+    memcpy(buf + sizeof(struct ethhdr), ip_data, ip_data_len);
+    slirp_output(buf, ip_data_len + ETH_HLEN);
+}
+
+int slirp_redir(int is_udp, int host_port,
+                uint32_t  guest_ip, int guest_port)
+{
+    if (is_udp) {
+        if (!udp_listen(host_port,
+                        guest_ip,
+                        guest_port, 0))
+            return -1;
+    } else {
+        if (!solisten(host_port, guest_ip, guest_port, 0))
+            return -1;
+    }
+    return 0;
+}
+
+int  slirp_unredir(int  is_udp, int  host_port)
+{
+    if (is_udp)
+        return udp_unlisten( host_port );
+    else
+        return sounlisten( host_port );
+}
+
diff --git a/slirp2/slirp.h b/slirp2/slirp.h
new file mode 100644
index 0000000..e69940b
--- /dev/null
+++ b/slirp2/slirp.h
@@ -0,0 +1,276 @@
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#define CONFIG_QEMU
+
+#define DEBUG 1
+
+#ifndef CONFIG_QEMU
+#include "version.h"
+#endif
+#include "config.h"
+#include "slirp_config.h"
+
+#include <stddef.h>
+#include "sockets.h"
+
+#ifdef _WIN32
+# include <inttypes.h>
+
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+typedef uint64_t u_int64_t;
+typedef char *caddr_t;
+
+# include <windows.h>
+# include <sys/timeb.h>
+# include <iphlpapi.h>
+#else
+# define O_BINARY 0
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+#include <sys/time.h>
+
+#ifdef NEED_TYPEDEFS
+typedef char int8_t;
+typedef unsigned char u_int8_t;
+
+# if SIZEOF_SHORT == 2
+    typedef short int16_t;
+    typedef unsigned short u_int16_t;
+# else
+#  if SIZEOF_INT == 2
+    typedef int int16_t;
+    typedef unsigned int u_int16_t;
+#  else
+    #error Cannot find a type with sizeof() == 2
+#  endif
+# endif
+
+# if SIZEOF_SHORT == 4
+   typedef short int32_t;
+   typedef unsigned short u_int32_t;
+# else
+#  if SIZEOF_INT == 4
+    typedef int int32_t;
+    typedef unsigned int u_int32_t;
+#  else
+    #error Cannot find a type with sizeof() == 4
+#  endif
+# endif
+#endif /* NEED_TYPEDEFS */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+
+#ifndef HAVE_MEMMOVE
+#define memmove(x, y, z) bcopy(y, x, z)
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef _WIN32
+#include <sys/uio.h>
+#endif
+
+#ifndef _P
+#ifndef NO_PROTOTYPES
+#  define   _P(x)   x
+#else
+#  define   _P(x)   ()
+#endif
+#endif
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#endif
+
+#ifdef GETTIMEOFDAY_ONE_ARG
+#define gettimeofday(x, y) gettimeofday(x)
+#endif
+
+/* Systems lacking strdup() definition in <string.h>. */
+#if defined(ultrix)
+char *strdup _P((const char *));
+#endif
+
+/* Systems lacking malloc() definition in <stdlib.h>. */
+#if defined(ultrix) || defined(hcx)
+void *malloc _P((size_t arg));
+void free _P((void *ptr));
+#endif
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/* Avoid conflicting with the libc insque() and remque(), which
+   have different prototypes. */
+#define insque slirp_insque
+#define remque slirp_remque
+
+#ifdef HAVE_SYS_STROPTS_H
+#include <sys/stropts.h>
+#endif
+
+#include "debug.h"
+
+#include "ip.h"
+#include "tcp.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "udp.h"
+#include "icmp_var.h"
+#include "mbuf.h"
+#include "sbuf.h"
+#include "socket.h"
+#include "if.h"
+#include "main.h"
+#include "misc.h"
+#include "ctl.h"
+#ifdef USE_PPP
+#include "ppp/pppd.h"
+#include "ppp/ppp.h"
+#endif
+
+#include "bootp.h"
+#include "tftp.h"
+#include "libslirp.h"
+
+extern struct ttys *ttys_unit[MAX_INTERFACES];
+
+#ifndef NULL
+#define NULL (void *)0
+#endif
+
+#ifndef FULL_BOLT
+void if_start _P((void));
+#else
+void if_start _P((struct ttys *));
+#endif
+
+#ifdef BAD_SPRINTF
+# define vsprintf vsprintf_len
+# define sprintf sprintf_len
+ extern int vsprintf_len _P((char *, const char *, va_list));
+ extern int sprintf_len _P((char *, const char *, ...));
+#endif
+
+#ifdef DECLARE_SPRINTF
+# ifndef BAD_SPRINTF
+ extern int vsprintf _P((char *, const char *, va_list));
+# endif
+ extern int vfprintf _P((FILE *, const char *, va_list));
+#endif
+
+#ifndef HAVE_STRERROR
+ extern char *strerror _P((int error));
+#endif
+
+#ifndef HAVE_INDEX
+ char *index _P((const char *, int));
+#endif
+
+#ifndef HAVE_GETHOSTID
+ long gethostid _P((void));
+#endif
+
+void lprint _P((const char *, ...));
+
+extern int do_echo;
+
+#define DEFAULT_BAUD 115200
+
+/* cksum.c */
+int cksum(MBuf m, int len);
+
+/* if.c */
+void if_init _P((void));
+void if_output _P((struct socket *, MBuf ));
+
+/* ip_input.c */
+void ip_init _P((void));
+void ip_input _P((MBuf ));
+struct ip * ip_reass _P((register struct ip*, register struct ipq *));
+void ip_freef _P((struct ipq *));
+void ip_enq _P((register struct ipasfrag *, register struct ipasfrag *));
+void ip_deq _P((register struct ipasfrag *));
+void ip_slowtimo _P((void));
+void ip_stripoptions _P((register MBuf , MBuf ));
+
+/* ip_output.c */
+int ip_output _P((struct socket *, MBuf ));
+
+/* tcp_input.c */
+int tcp_reass _P((register struct tcpcb *, register struct tcpiphdr *, MBuf ));
+void tcp_input _P((register MBuf , int, struct socket *));
+void tcp_dooptions _P((struct tcpcb *, u_char *, int, struct tcpiphdr *));
+void tcp_xmit_timer _P((register struct tcpcb *, int));
+int tcp_mss _P((register struct tcpcb *, u_int));
+
+/* tcp_output.c */
+int tcp_output _P((register struct tcpcb *));
+void tcp_setpersist _P((register struct tcpcb *));
+
+/* tcp_subr.c */
+void tcp_init _P((void));
+void tcp_template _P((struct tcpcb *));
+void tcp_respond _P((struct tcpcb *, register struct tcpiphdr *, register MBuf , tcp_seq, tcp_seq, int));
+struct tcpcb * tcp_newtcpcb _P((struct socket *));
+struct tcpcb * tcp_close _P((register struct tcpcb *));
+void tcp_drain _P((void));
+void tcp_sockclosed _P((struct tcpcb *));
+int tcp_fconnect _P((struct socket *));
+void tcp_connect _P((struct socket *));
+int tcp_attach _P((struct socket *));
+u_int8_t tcp_tos _P((struct socket *));
+int tcp_emu _P((struct socket *, MBuf ));
+int tcp_ctl _P((struct socket *));
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err);
+
+#ifdef USE_PPP
+#define MIN_MRU MINMRU
+#define MAX_MRU MAXMRU
+#else
+#define MIN_MRU 128
+#define MAX_MRU 16384
+#endif
+
+#ifndef _WIN32
+#define min(x,y) ((x) < (y) ? (x) : (y))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+#endif
+
+#endif
diff --git a/slirp2/slirp_config.h b/slirp2/slirp_config.h
new file mode 100644
index 0000000..030d2ff
--- /dev/null
+++ b/slirp2/slirp_config.h
@@ -0,0 +1,200 @@
+/*
+ * User definable configuration options
+ */
+
+/* Undefine if you don't want talk emulation */
+#undef EMULATE_TALK
+
+/* Define if you want the connection to be probed */
+/* XXX Not working yet, so ignore this for now */
+#undef PROBE_CONN
+
+/* Define to 1 if you want KEEPALIVE timers */
+#define DO_KEEPALIVE 0
+
+/* Define to MAX interfaces you expect to use at once */
+/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
+/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
+#define MAX_INTERFACES 1
+#define MAX_PPP_INTERFACES 1
+
+/* Define if you want slirp's socket in /tmp */
+/* XXXXXX Do this in ./configure */
+#undef USE_TMPSOCKET
+
+/* Define if you want slirp to use cfsetXspeed() on the terminal */
+#undef DO_CFSETSPEED
+
+/* Define this if you want slirp to write to the tty as fast as it can */
+/* This should only be set if you are using load-balancing, slirp does a */
+/* pretty good job on single modems already, and seting this will make */
+/* interactive sessions less responsive */
+/* XXXXX Talk about having fast modem as unit 0 */
+#undef FULL_BOLT
+
+/*
+ * Define if you want slirp to use less CPU
+ * You will notice a small lag in interactive sessions, but it's not that bad
+ * Things like Netscape/ftp/etc. are completely unaffected
+ * This is mainly for sysadmins who have many slirp users
+ */
+#undef USE_LOWCPU
+
+/* Define this if your compiler doesn't like prototypes */
+#ifndef __STDC__
+#define NO_PROTOTYPES
+#endif
+
+/*********************************************************/
+/*
+ * Autoconf defined configuration options
+ * You shouldn't need to touch any of these
+ */
+
+/* Ignore this */
+#undef DUMMY_PPP
+
+/* Define if you have unistd.h */
+#define HAVE_UNISTD_H
+
+/* Define if you have stdlib.h */
+#define HAVE_STDLIB_H
+
+/* Define if you have sys/ioctl.h */
+#undef HAVE_SYS_IOCTL_H
+#ifndef _WIN32
+#define HAVE_SYS_IOCTL_H
+#endif
+
+/* Define if you have sys/filio.h */
+#undef HAVE_SYS_FILIO_H
+#ifdef __APPLE__
+#define HAVE_SYS_FILIO_H
+#endif
+
+/* Define if you have strerror */
+#define HAVE_STRERROR
+
+/* Define if you have strdup() */
+#define HAVE_STRDUP
+
+/* Define according to how time.h should be included */
+#define TIME_WITH_SYS_TIME 0
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have sys/bitypes.h */
+#undef HAVE_SYS_BITYPES_H
+
+/* Define if the machine is big endian */
+//#undef WORDS_BIGENDIAN
+
+/* Define if your sprintf returns char * instead of int */
+#undef BAD_SPRINTF
+
+/* Define if you have readv */
+#undef HAVE_READV
+
+/* Define if iovec needs to be declared */
+#undef DECLARE_IOVEC
+#ifdef _WIN32
+#define DECLARE_IOVEC
+#endif
+
+/* Define if a declaration of sprintf/fprintf is needed */
+#undef DECLARE_SPRINTF
+
+/* Define if you have a POSIX.1 sys/wait.h */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have sys/select.h */
+#undef HAVE_SYS_SELECT_H
+#ifndef _WIN32
+#define HAVE_SYS_SELECT_H
+#endif
+
+/* Define if you have strings.h */
+#define HAVE_STRING_H
+
+/* Define if you have arpa/inet.h */
+#undef HAVE_ARPA_INET_H
+#ifndef _WIN32
+#define HAVE_ARPA_INET_H
+#endif
+
+/* Define if you have sys/signal.h */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have sys/stropts.h */
+#undef HAVE_SYS_STROPTS_H
+
+/* Define to whatever your compiler thinks inline should be */
+#define inline inline
+
+/* Define to whatever your compiler thinks const should be */
+#define const const
+
+/* Define if your compiler doesn't like prototypes */
+#undef NO_PROTOTYPES
+
+/* Define if you don't have u_int32_t etc. typedef'd */
+#undef NEED_TYPEDEFS
+#ifdef __sun__
+#define NEED_TYPEDEFS
+#endif
+
+/* Define to sizeof(char) */
+#define SIZEOF_CHAR 1
+
+/* Define to sizeof(short) */
+#define SIZEOF_SHORT 2
+
+/* Define to sizeof(int) */
+#define SIZEOF_INT 4
+
+/* Define to sizeof(char *) */
+#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
+
+/* Define if you have random() */
+#undef HAVE_RANDOM
+
+/* Define if you have srandom() */
+#undef HAVE_SRANDOM
+
+/* Define if you have setenv */
+#undef HAVE_SETENV
+
+/* Define if you have index() */
+#undef HAVE_INDEX
+
+/* Define if you have bcmp() */
+#undef HAVE_BCMP
+
+/* Define if you have drand48 */
+#undef HAVE_DRAND48
+
+/* Define if you have memmove */
+#define HAVE_MEMMOVE
+
+/* Define if you have gethostid */
+#undef HAVE_GETHOSTID
+
+/* Define if you DON'T have unix-domain sockets */
+#undef NO_UNIX_SOCKETS
+#ifdef _WIN32
+#define NO_UNIX_SOCKETS
+#endif
+
+/* Define if gettimeofday only takes one argument */
+#undef GETTIMEOFDAY_ONE_ARG
+
+/* Define if you have revoke() */
+#undef HAVE_REVOKE
+
+/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
+#undef HAVE_GRANTPT
+
+/* Define if you have fchmod */
+#undef HAVE_FCHMOD
+
+/* Define if you have <sys/type32.h> */
+#undef HAVE_SYS_TYPES32_H
diff --git a/slirp2/socket.c b/slirp2/socket.c
new file mode 100644
index 0000000..05fb4b7
--- /dev/null
+++ b/slirp2/socket.c
@@ -0,0 +1,732 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define WANT_SYS_IOCTL_H
+#include <slirp.h>
+#include "ip_icmp.h"
+#include "main.h"
+#ifdef __sun__
+#include <sys/filio.h>
+#endif
+#define  SLIRP_COMPILATION 1
+#include "sockets.h"
+#include "proxy_common.h"
+
+void
+so_init()
+{
+	/* Nothing yet */
+}
+
+
+struct socket *
+solookup(head, laddr, lport, faddr, fport)
+	struct socket *head;
+	uint32_t laddr;
+	u_int lport;
+	uint32_t faddr;
+	u_int fport;
+{
+	struct socket *so;
+
+	for (so = head->so_next; so != head; so = so->so_next) {
+		if (so->so_laddr_port == lport &&
+		    so->so_laddr_ip   == laddr &&
+		    so->so_faddr_ip   == faddr &&
+		    so->so_faddr_port == fport)
+		   break;
+	}
+
+	if (so == head)
+	   return (struct socket *)NULL;
+	return so;
+
+}
+
+/*
+ * Create a new socket, initialise the fields
+ * It is the responsibility of the caller to
+ * insque() it into the correct linked-list
+ */
+struct socket *
+socreate()
+{
+  struct socket *so;
+
+  so = (struct socket *)malloc(sizeof(struct socket));
+  if(so) {
+    memset(so, 0, sizeof(struct socket));
+    so->so_state = SS_NOFDREF;
+    so->s = -1;
+  }
+  return(so);
+}
+
+/*
+ * remque and free a socket, clobber cache
+ */
+void
+sofree(so)
+	struct socket *so;
+{
+  if (so->so_state & SS_PROXIFIED)
+    proxy_manager_del(so);
+
+  if (so->extra) {
+	sofree(so->extra);
+	so->extra=NULL;
+  }
+  if (so == tcp_last_so)
+    tcp_last_so = &tcb;
+  else if (so == udp_last_so)
+    udp_last_so = &udb;
+
+  mbuf_free(so->so_m);
+
+  if(so->so_next && so->so_prev)
+    remque(so);  /* crashes if so is not in a queue */
+
+  free(so);
+}
+
+/*
+ * Read from so's socket into sb_snd, updating all relevant sbuf fields
+ * NOTE: This will only be called if it is select()ed for reading, so
+ * a read() of 0 (or less) means it's disconnected
+ */
+int
+soread(so)
+	struct socket *so;
+{
+	int n, nn, lss, total;
+	SBuf  sb = &so->so_snd;
+	int   len = sb->sb_datalen - sb->sb_cc;
+	struct iovec iov[2];
+	int mss = so->so_tcpcb->t_maxseg;
+
+	DEBUG_CALL("soread");
+	DEBUG_ARG("so = %lx", (long )so);
+
+	/*
+	 * No need to check if there's enough room to read.
+	 * soread wouldn't have been called if there weren't
+	 */
+
+	len = sb->sb_datalen - sb->sb_cc;
+
+	iov[0].iov_base = sb->sb_wptr;
+	if (sb->sb_wptr < sb->sb_rptr) {
+		iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
+		/* Should never succeed, but... */
+		if (iov[0].iov_len > len)
+		   iov[0].iov_len = len;
+		if (iov[0].iov_len > mss)
+		   iov[0].iov_len -= iov[0].iov_len%mss;
+		n = 1;
+	} else {
+		iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
+		/* Should never succeed, but... */
+		if (iov[0].iov_len > len) iov[0].iov_len = len;
+		len -= iov[0].iov_len;
+		if (len) {
+			iov[1].iov_base = sb->sb_data;
+			iov[1].iov_len = sb->sb_rptr - sb->sb_data;
+			if(iov[1].iov_len > len)
+			   iov[1].iov_len = len;
+			total = iov[0].iov_len + iov[1].iov_len;
+			if (total > mss) {
+				lss = total%mss;
+				if (iov[1].iov_len > lss) {
+					iov[1].iov_len -= lss;
+					n = 2;
+				} else {
+					lss -= iov[1].iov_len;
+					iov[0].iov_len -= lss;
+					n = 1;
+				}
+			} else
+				n = 2;
+		} else {
+			if (iov[0].iov_len > mss)
+			   iov[0].iov_len -= iov[0].iov_len%mss;
+			n = 1;
+		}
+	}
+
+#ifdef HAVE_READV
+	nn = readv(so->s, (struct iovec *)iov, n);
+	DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#else
+	nn = socket_recv(so->s, iov[0].iov_base, iov[0].iov_len);
+#endif
+	if (nn <= 0) {
+		if (nn < 0 && (errno == EINTR || errno == EAGAIN))
+			return 0;
+		else {
+			DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,errno_str));
+			sofcantrcvmore(so);
+			tcp_sockclosed(sototcpcb(so));
+			return -1;
+		}
+	}
+
+#ifndef HAVE_READV
+	/*
+	 * If there was no error, try and read the second time round
+	 * We read again if n = 2 (ie, there's another part of the buffer)
+	 * and we read as much as we could in the first read
+	 * We don't test for <= 0 this time, because there legitimately
+	 * might not be any more data (since the socket is non-blocking),
+	 * a close will be detected on next iteration.
+	 * A return of -1 wont (shouldn't) happen, since it didn't happen above
+	 */
+	if (n == 2 && nn == iov[0].iov_len) {
+            int ret;
+            ret = socket_recv(so->s, iov[1].iov_base, iov[1].iov_len);
+            if (ret > 0)
+                nn += ret;
+        }
+
+	DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn));
+#endif
+
+	/* Update fields */
+	sb->sb_cc += nn;
+	sb->sb_wptr += nn;
+	if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+		sb->sb_wptr -= sb->sb_datalen;
+	return nn;
+}
+
+/*
+ * Get urgent data
+ *
+ * When the socket is created, we set it SO_OOBINLINE,
+ * so when OOB data arrives, we soread() it and everything
+ * in the send buffer is sent as urgent data
+ */
+void
+sorecvoob(so)
+	struct socket *so;
+{
+	struct tcpcb *tp = sototcpcb(so);
+
+	DEBUG_CALL("sorecvoob");
+	DEBUG_ARG("so = %lx", (long)so);
+
+	/*
+	 * We take a guess at how much urgent data has arrived.
+	 * In most situations, when urgent data arrives, the next
+	 * read() should get all the urgent data.  This guess will
+	 * be wrong however if more data arrives just after the
+	 * urgent data, or the read() doesn't return all the
+	 * urgent data.
+	 */
+	soread(so);
+	tp->snd_up = tp->snd_una + so->so_snd.sb_cc;
+	tp->t_force = 1;
+	tcp_output(tp);
+	tp->t_force = 0;
+}
+
+/*
+ * Send urgent data
+ * There's a lot duplicated code here, but...
+ */
+int
+sosendoob(so)
+	struct socket *so;
+{
+	SBuf  sb = &so->so_rcv;
+	char  buff[2048]; /* XXX Shouldn't be sending more oob data than this */
+	int   n, len;
+
+	DEBUG_CALL("sosendoob");
+	DEBUG_ARG("so = %lx", (long)so);
+	DEBUG_ARG("sb->sb_cc = %d", sb->sb_cc);
+
+	if (so->so_urgc > 2048)
+	   so->so_urgc = 2048; /* XXXX */
+
+	if (sb->sb_rptr < sb->sb_wptr) {
+		/* We can send it directly */
+		n = socket_send_oob(so->s, sb->sb_rptr, so->so_urgc); /* |MSG_DONTWAIT)); */
+		so->so_urgc -= n;
+
+		DEBUG_MISC((dfd, " --- sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+	} else {
+		/*
+		 * Since there's no sendv or sendtov like writev,
+		 * we must copy all data to a linear buffer then
+		 * send it all
+		 */
+		len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+		if (len > so->so_urgc) len = so->so_urgc;
+		memcpy(buff, sb->sb_rptr, len);
+		so->so_urgc -= len;
+		if (so->so_urgc) {
+			n = sb->sb_wptr - sb->sb_data;
+			if (n > so->so_urgc) n = so->so_urgc;
+			memcpy((buff + len), sb->sb_data, n);
+			so->so_urgc -= n;
+			len += n;
+		}
+		n = socket_send_oob(so->s, buff, len); /* |MSG_DONTWAIT)); */
+#ifdef DEBUG
+		if (n != len)
+		   DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n"));
+#endif
+		DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc));
+	}
+
+	sb->sb_cc -= n;
+	sb->sb_rptr += n;
+	if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+		sb->sb_rptr -= sb->sb_datalen;
+
+	return n;
+}
+
+/*
+ * Write data from so_rcv to so's socket,
+ * updating all sbuf field as necessary
+ */
+int
+sowrite(so)
+	struct socket *so;
+{
+	int   n,nn;
+	SBuf  sb = &so->so_rcv;
+	int   len = sb->sb_cc;
+	struct iovec iov[2];
+
+	DEBUG_CALL("sowrite");
+	DEBUG_ARG("so = %lx", (long)so);
+
+	if (so->so_urgc) {
+		sosendoob(so);
+		if (sb->sb_cc == 0)
+			return 0;
+	}
+
+	/*
+	 * No need to check if there's something to write,
+	 * sowrite wouldn't have been called otherwise
+	 */
+
+        len = sb->sb_cc;
+
+	iov[0].iov_base = sb->sb_rptr;
+	if (sb->sb_rptr < sb->sb_wptr) {
+		iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
+		/* Should never succeed, but... */
+		if (iov[0].iov_len > len) iov[0].iov_len = len;
+		n = 1;
+	} else {
+		iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+		if (iov[0].iov_len > len) iov[0].iov_len = len;
+		len -= iov[0].iov_len;
+		if (len) {
+			iov[1].iov_base = sb->sb_data;
+			iov[1].iov_len = sb->sb_wptr - sb->sb_data;
+			if (iov[1].iov_len > len) iov[1].iov_len = len;
+			n = 2;
+		} else
+			n = 1;
+	}
+	/* Check if there's urgent data to send, and if so, send it */
+
+#ifdef HAVE_READV
+	nn = writev(so->s, (const struct iovec *)iov, n);
+
+	DEBUG_MISC((dfd, "  ... wrote nn = %d bytes\n", nn));
+#else
+	nn = socket_send(so->s, iov[0].iov_base, iov[0].iov_len);
+#endif
+	/* This should never happen, but people tell me it does *shrug* */
+	if (nn < 0 && (errno == EAGAIN || errno == EINTR))
+		return 0;
+
+	if (nn <= 0) {
+		DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n",
+			so->so_state, errno));
+		sofcantsendmore(so);
+		tcp_sockclosed(sototcpcb(so));
+		return -1;
+	}
+
+#ifndef HAVE_READV
+	if (n == 2 && nn == iov[0].iov_len) {
+            int ret;
+            ret = socket_send(so->s, iov[1].iov_base, iov[1].iov_len);
+            if (ret > 0)
+                nn += ret;
+        }
+        DEBUG_MISC((dfd, "  ... wrote nn = %d bytes\n", nn));
+#endif
+
+	/* Update sbuf */
+	sb->sb_cc -= nn;
+	sb->sb_rptr += nn;
+	if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+		sb->sb_rptr -= sb->sb_datalen;
+
+	/*
+	 * If in DRAIN mode, and there's no more data, set
+	 * it CANTSENDMORE
+	 */
+	if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
+		sofcantsendmore(so);
+
+	return nn;
+}
+
+/*
+ * recvfrom() a UDP socket
+ */
+void
+sorecvfrom(so)
+	struct socket *so;
+{
+        SockAddress  addr;
+
+	DEBUG_CALL("sorecvfrom");
+	DEBUG_ARG("so = %lx", (long)so);
+
+	if (so->so_type == IPPROTO_ICMP) {   /* This is a "ping" reply */
+	  char buff[256];
+	  int len;
+
+	  len = socket_recvfrom(so->s, buff, 256, &addr);
+	  /* XXX Check if reply is "correct"? */
+
+	  if(len == -1 || len == 0) {
+	    u_char code=ICMP_UNREACH_PORT;
+
+	    if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+	    else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+	    DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n",
+			errno,errno_str));
+	    icmp_error(so->so_m, ICMP_UNREACH,code, 0,errno_str);
+	  } else {
+	    icmp_reflect(so->so_m);
+	    so->so_m = 0; /* Don't mbuf_free() it again! */
+	  }
+	  /* No need for this socket anymore, udp_detach it */
+	  udp_detach(so);
+	} else {                            	/* A "normal" UDP packet */
+	  MBuf m;
+	  int len, n;
+
+	  if (!(m = mbuf_alloc())) return;
+	  m->m_data += if_maxlinkhdr;
+
+	  /*
+	   * XXX Shouldn't FIONREAD packets destined for port 53,
+	   * but I don't know the max packet size for DNS lookups
+	   */
+	  len = mbuf_freeroom(m);
+	  /* if (so->so_fport != htons(53)) { */
+	  n = socket_can_read(so->s);
+
+	  if (n > len) {
+	    n = (m->m_data - m->m_dat) + m->m_len + n + 1;
+	    mbuf_ensure(m, n);
+	    len = mbuf_freeroom(m);
+	  }
+	  /* } */
+
+	  m->m_len = socket_recvfrom(so->s, m->m_data, len, &addr);
+	  DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n",
+		      m->m_len, errno,errno_str));
+	  if(m->m_len<0) {
+	    u_char code=ICMP_UNREACH_PORT;
+
+	    if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+	    else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET;
+
+	    DEBUG_MISC((dfd," rx error, tx icmp ICMP_UNREACH:%i\n", code));
+	    icmp_error(so->so_m, ICMP_UNREACH,code, 0,errno_str);
+	    mbuf_free(m);
+	  } else {
+	  /*
+	   * Hack: domain name lookup will be used the most for UDP,
+	   * and since they'll only be used once there's no need
+	   * for the 4 minute (or whatever) timeout... So we time them
+	   * out much quicker (10 seconds  for now...)
+	   */
+	    if (so->so_expire) {
+	      if (so->so_faddr_port == 53)
+		so->so_expire = curtime + SO_EXPIREFAST;
+	      else
+		so->so_expire = curtime + SO_EXPIRE;
+	    }
+
+	    /*		if (m->m_len == len) {
+	     *			mbuf_ensure(m, MINCSIZE);
+	     *			m->m_len = 0;
+	     *		}
+	     */
+
+	    /*
+	     * If this packet was destined for CTL_ADDR,
+	     * make it look like that's where it came from, done by udp_output
+	     */
+	    udp_output_(so, m, &addr);
+	  } /* rx error */
+	} /* if ping packet */
+}
+
+/*
+ * sendto() a socket
+ */
+int
+sosendto(so, m)
+	struct socket *so;
+	MBuf m;
+{
+        SockAddress   addr;
+        uint32_t      addr_ip;
+        uint16_t      addr_port;
+        int           ret;
+
+	DEBUG_CALL("sosendto");
+	DEBUG_ARG("so = %lx", (long)so);
+	DEBUG_ARG("m = %lx", (long)m);
+
+	if ((so->so_faddr_ip & 0xffffff00) == special_addr_ip) {
+	  /* It's an alias */
+          int  low = so->so_faddr_ip & 0xff;
+
+          if ( CTL_IS_DNS(low) )
+            addr_ip = dns_addr[low - CTL_DNS];
+          else
+            addr_ip = loopback_addr_ip;
+	} else
+	    addr_ip = so->so_faddr_ip;
+
+	addr_port = so->so_faddr_port;
+
+        sock_address_init_inet(&addr, addr_ip, addr_port);
+
+	DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%08x\n", addr_port, addr_ip));
+
+	/* Don't care what port we get */
+	ret = socket_sendto(so->s, m->m_data, m->m_len,&addr);
+	if (ret < 0)
+		return -1;
+
+	/*
+	 * Kill the socket if there's no reply in 4 minutes,
+	 * but only if it's an expirable socket
+	 */
+	if (so->so_expire)
+		so->so_expire = curtime + SO_EXPIRE;
+	so->so_state = SS_ISFCONNECTED; /* So that it gets select()ed */
+	return 0;
+}
+
+/*
+ * XXX This should really be tcp_listen
+ */
+struct socket *
+solisten(port, laddr, lport, flags)
+	u_int port;
+	u_int32_t laddr;
+	u_int lport;
+	int flags;
+{
+	SockAddress  addr;
+	uint32_t     addr_ip;
+	struct socket *so;
+	int s;
+
+	DEBUG_CALL("solisten");
+	DEBUG_ARG("port = %d", port);
+	DEBUG_ARG("laddr = %x", laddr);
+	DEBUG_ARG("lport = %d", lport);
+	DEBUG_ARG("flags = %x", flags);
+
+	if ((so = socreate()) == NULL) {
+	  /* free(so);      Not sofree() ??? free(NULL) == NOP */
+	  return NULL;
+	}
+
+	/* Don't tcp_attach... we don't need so_snd nor so_rcv */
+	if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) {
+		free(so);
+		return NULL;
+	}
+	insque(so,&tcb);
+
+	/*
+	 * SS_FACCEPTONCE sockets must time out.
+	 */
+	if (flags & SS_FACCEPTONCE)
+	   so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
+
+	so->so_state      = (SS_FACCEPTCONN|flags);
+	so->so_laddr_port = lport; /* Kept in host format */
+        so->so_laddr_ip   = laddr; /* Ditto */
+        so->so_haddr_port = port;
+
+        s = socket_loopback_server( port, SOCKET_STREAM );
+        if (s < 0)
+            return NULL;
+
+        socket_get_address(s, &addr);
+
+	so->so_faddr_port = sock_address_get_port(&addr);
+
+        addr_ip = (uint32_t) sock_address_get_ip(&addr);
+
+        if (addr_ip == 0 || addr_ip == loopback_addr_ip)
+            so->so_faddr_ip = alias_addr_ip;
+        else
+            so->so_faddr_ip = addr_ip;
+
+	so->s = s;
+	return so;
+}
+
+
+int
+sounlisten(u_int  port)
+{
+    struct socket *so;
+
+    for (so = tcb.so_next; so != &tcb; so = so->so_next) {
+        if (so->so_haddr_port == port) {
+            break;
+        }
+    }
+
+    if (so == &tcb) {
+        return -1;
+    }
+
+    sofcantrcvmore( so );
+    sofcantsendmore( so );
+    close( so->s );
+    so->s = -1;
+    sofree( so );
+    return 0;
+}
+
+
+/*
+ * Data is available in so_rcv
+ * Just write() the data to the socket
+ * XXX not yet...
+ */
+void
+sorwakeup(so)
+	struct socket *so;
+{
+/*	sowrite(so); */
+/*	FD_CLR(so->s,&writefds); */
+}
+
+/*
+ * Data has been freed in so_snd
+ * We have room for a read() if we want to
+ * For now, don't read, it'll be done in the main loop
+ */
+void
+sowwakeup(so)
+	struct socket *so;
+{
+	/* Nothing, yet */
+}
+
+/*
+ * Various session state calls
+ * XXX Should be #define's
+ * The socket state stuff needs work, these often get call 2 or 3
+ * times each when only 1 was needed
+ */
+void
+soisfconnecting(so)
+	register struct socket *so;
+{
+	so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
+			  SS_FCANTSENDMORE|SS_FWDRAIN);
+	so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
+}
+
+void
+soisfconnected(so)
+        register struct socket *so;
+{
+	so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
+	so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
+}
+
+void
+sofcantrcvmore(so)
+	struct  socket *so;
+{
+	if ((so->so_state & SS_NOFDREF) == 0) {
+		shutdown(so->s,0);
+		if(global_writefds) {
+		  FD_CLR(so->s,global_writefds);
+		}
+	}
+	so->so_state &= ~(SS_ISFCONNECTING);
+	if (so->so_state & SS_FCANTSENDMORE)
+	   so->so_state = SS_NOFDREF; /* Don't select it */ /* XXX close() here as well? */
+	else
+	   so->so_state |= SS_FCANTRCVMORE;
+}
+
+void
+sofcantsendmore(so)
+	struct socket *so;
+{
+	if ((so->so_state & SS_NOFDREF) == 0) {
+            shutdown(so->s,1);           /* send FIN to fhost */
+            if (global_readfds) {
+                FD_CLR(so->s,global_readfds);
+            }
+            if (global_xfds) {
+                FD_CLR(so->s,global_xfds);
+            }
+	}
+	so->so_state &= ~(SS_ISFCONNECTING);
+	if (so->so_state & SS_FCANTRCVMORE)
+	   so->so_state = SS_NOFDREF; /* as above */
+	else
+	   so->so_state |= SS_FCANTSENDMORE;
+}
+
+void
+soisfdisconnected(so)
+	struct socket *so;
+{
+/*	so->so_state &= ~(SS_ISFCONNECTING|SS_ISFCONNECTED); */
+/*	close(so->s); */
+/*	so->so_state = SS_ISFDISCONNECTED; */
+	/*
+	 * XXX Do nothing ... ?
+	 */
+}
+
+/*
+ * Set write drain mode
+ * Set CANTSENDMORE once all data has been write()n
+ */
+void
+sofwdrain(so)
+	struct socket *so;
+{
+	if (so->so_rcv.sb_cc)
+		so->so_state |= SS_FWDRAIN;
+	else
+		sofcantsendmore(so);
+}
+
diff --git a/slirp2/socket.h b/slirp2/socket.h
new file mode 100644
index 0000000..5b71d45
--- /dev/null
+++ b/slirp2/socket.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/* MINE */
+
+#ifndef _SLIRP_SOCKET_H_
+#define _SLIRP_SOCKET_H_
+
+#define SO_EXPIRE 240000
+#define SO_EXPIREFAST 10000
+
+/*
+ * Our socket structure
+ */
+
+struct socket {
+  struct socket *so_next,*so_prev;      /* For a linked list of sockets */
+
+  int s;                           /* The actual socket */
+
+			/* XXX union these with not-yet-used sbuf params */
+  MBuf so_m;	           /* Pointer to the original SYN packet,
+				    * for non-blocking connect()'s, and
+				    * PING reply's */
+  struct tcpiphdr *so_ti;	   /* Pointer to the original ti within
+				    * so_mconn, for non-blocking connections */
+  int so_urgc;
+  uint32_t   so_faddr_ip;
+  uint32_t   so_laddr_ip;
+  uint16_t   so_faddr_port;
+  uint16_t   so_laddr_port;
+  uint16_t   so_haddr_port;
+
+  u_int8_t	so_iptos;	/* Type of service */
+  u_int8_t	so_emu;		/* Is the socket emulated? */
+
+  u_char	so_type;		/* Type of socket, UDP or TCP */
+  int	so_state;		/* internal state flags SS_*, below */
+
+  struct 	tcpcb *so_tcpcb;	/* pointer to TCP protocol control block */
+  u_int	so_expire;		/* When the socket will expire */
+
+  int	so_queued;		/* Number of packets queued from this socket */
+  int	so_nqueued;		/* Number of packets queued in a row
+				 * Used to determine when to "downgrade" a session
+					 * from fastq to batchq */
+
+  SBufRec so_rcv;		/* Receive buffer */
+  SBufRec so_snd;		/* Send buffer */
+  void * extra;			/* Extra pointer */
+};
+
+
+/*
+ * Socket state bits. (peer means the host on the Internet,
+ * local host means the host on the other end of the modem)
+ */
+#define SS_NOFDREF		0x001	/* No fd reference */
+
+#define SS_ISFCONNECTING	0x002	/* Socket is connecting to peer (non-blocking connect()'s) */
+#define SS_ISFCONNECTED		0x004	/* Socket is connected to peer */
+#define SS_FCANTRCVMORE		0x008	/* Socket can't receive more from peer (for half-closes) */
+#define SS_FCANTSENDMORE	0x010	/* Socket can't send more to peer (for half-closes) */
+/* #define SS_ISFDISCONNECTED	0x020*/	/* Socket has disconnected from peer, in 2MSL state */
+#define SS_FWDRAIN		0x040	/* We received a FIN, drain data and set SS_FCANTSENDMORE */
+
+#define SS_CTL			0x080
+#define SS_FACCEPTCONN		0x100	/* Socket is accepting connections from a host on the internet */
+#define SS_FACCEPTONCE		0x200	/* If set, the SS_FACCEPTCONN socket will die after one accept */
+#define SS_PROXIFIED            0x400   /* Socket is trying to connect through a proxy, only makes sense
+                                           when SS_ISFCONNECTING is also set */
+extern struct socket tcb;
+
+
+#if defined(DECLARE_IOVEC) && !defined(HAVE_READV)
+struct iovec {
+	char *iov_base;
+	size_t iov_len;
+};
+#endif
+
+void so_init _P((void));
+struct socket * solookup _P((struct socket *, uint32_t, u_int, uint32_t, u_int));
+struct socket * socreate _P((void));
+void sofree _P((struct socket *));
+int soread _P((struct socket *));
+void sorecvoob _P((struct socket *));
+int sosendoob _P((struct socket *));
+int sowrite _P((struct socket *));
+void sorecvfrom _P((struct socket *));
+int sosendto _P((struct socket *, MBuf ));
+struct socket * solisten _P((u_int, u_int32_t, u_int, int));
+int  sounlisten _P((u_int port));
+void sorwakeup _P((struct socket *));
+void sowwakeup _P((struct socket *));
+void soisfconnecting _P((register struct socket *));
+void soisfconnected _P((register struct socket *));
+void sofcantrcvmore _P((struct  socket *));
+void sofcantsendmore _P((struct socket *));
+void soisfdisconnected _P((struct socket *));
+void sofwdrain _P((struct socket *));
+
+#endif /* _SOCKET_H_ */
diff --git a/slirp2/tcp.h b/slirp2/tcp.h
new file mode 100644
index 0000000..3cddb77
--- /dev/null
+++ b/slirp2/tcp.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp.h	8.1 (Berkeley) 6/10/93
+ * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
+ */
+
+#ifndef _TCP_H_
+#define _TCP_H_
+
+#include "helper.h"
+
+typedef	u_int32_t	tcp_seq;
+
+#define      PR_SLOWHZ       2               /* 2 slow timeouts per second (approx) */
+#define      PR_FASTHZ       5               /* 5 fast timeouts per second (not important) */
+
+extern int tcp_rcvspace;
+extern int tcp_sndspace;
+extern struct socket *tcp_last_so;
+
+#define TCP_SNDSPACE 8192
+#define TCP_RCVSPACE 8192
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+	port_t	th_sport;		/* source port */
+	port_t	th_dport;		/* destination port */
+	tcp_seq	th_seq;			/* sequence number */
+	tcp_seq	th_ack;			/* acknowledgement number */
+#ifdef WORDS_BIGENDIAN
+	u_int	th_off:4,		/* data offset */
+		th_x2:4;		/* (unused) */
+#else
+	u_int	th_x2:4,		/* (unused) */
+		th_off:4;		/* data offset */
+#endif
+	u_int8_t	th_flags;
+#define	TH_FIN	0x01
+#define	TH_SYN	0x02
+#define	TH_RST	0x04
+#define	TH_PUSH	0x08
+#define	TH_ACK	0x10
+#define	TH_URG	0x20
+	u_int16_t	th_win;			/* window */
+	u_int16_t	th_sum;			/* checksum */
+	u_int16_t	th_urp;			/* urgent pointer */
+};
+
+#include "tcp_var.h"
+
+#define	TCPOPT_EOL		0
+#define	TCPOPT_NOP		1
+#define	TCPOPT_MAXSEG		2
+#define    TCPOLEN_MAXSEG		4
+#define TCPOPT_WINDOW		3
+#define    TCPOLEN_WINDOW		3
+#define TCPOPT_SACK_PERMITTED	4		/* Experimental */
+#define    TCPOLEN_SACK_PERMITTED	2
+#define TCPOPT_SACK		5		/* Experimental */
+#define TCPOPT_TIMESTAMP	8
+#define    TCPOLEN_TIMESTAMP		10
+#define    TCPOLEN_TSTAMP_APPA		(TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR	\
+    (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ *
+ * We make this 1460 because we only care about Ethernet in the qemu context.
+ */
+#define	TCP_MSS	1460
+
+#define	TCP_MAXWIN	65535	/* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT	14	/* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ */
+#undef  TCP_NODELAY 
+#define	TCP_NODELAY	0x01	/* don't delay send to coalesce packets */
+#undef  TCP_MAXSEG
+/* #define	TCP_MAXSEG	0x02 */	/* set maximum segment size */
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES     11
+
+#define TCPS_CLOSED             0       /* closed */
+#define TCPS_LISTEN             1       /* listening for connection */
+#define TCPS_SYN_SENT           2       /* active, have sent syn */
+#define TCPS_SYN_RECEIVED       3       /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED        4       /* established */
+#define TCPS_CLOSE_WAIT         5       /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1         6       /* have closed, sent fin */
+#define TCPS_CLOSING            7       /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK           8       /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2         9       /* have closed, fin is acked */
+#define TCPS_TIME_WAIT          10      /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s)     ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
+#define TCPS_HAVERCVDFIN(s)     ((s) >= TCPS_TIME_WAIT)
+
+/*
+ * TCP sequence numbers are 32 bit integers operated
+ * on with modular arithmetic.  These macros can be
+ * used to compare such integers.
+ */
+#define SEQ_LT(a,b)     ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b)    ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b)     ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b)    ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+     (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+    (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss
+
+#define TCP_ISSINCR     (125*1024)      /* increment for tcp_iss each second */
+
+extern tcp_seq tcp_iss;                /* tcp initial send seq # */
+
+extern char *tcpstates[];
+
+#endif
diff --git a/slirp2/tcp_input.c b/slirp2/tcp_input.c
new file mode 100644
index 0000000..c3196d3
--- /dev/null
+++ b/slirp2/tcp_input.c
@@ -0,0 +1,1714 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp_input.c	8.5 (Berkeley) 4/10/94
+ * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+struct socket tcb;
+
+int	tcprexmtthresh = 3;
+struct	socket *tcp_last_so = &tcb;
+
+tcp_seq tcp_iss;                /* tcp initial send seq # */
+
+#define TCP_PAWS_IDLE	(24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+#define TSTMP_LT(a,b)	((int)((a)-(b)) < 0)
+#define TSTMP_GEQ(a,b)	((int)((a)-(b)) >= 0)
+
+/*
+ * Insert segment ti into reassembly queue of tcp with
+ * control block tp.  Return TH_FIN if reassembly now includes
+ * a segment with FIN.  The macro form does the common case inline
+ * (segment is the next to be received on an established connection,
+ * and the queue is empty), avoiding linkage into and removal
+ * from the queue and repetition of various conversions.
+ * Set DELACK for segments received in order, but ack immediately
+ * when segments are out of order (so fast retransmit can work).
+ */
+#ifdef TCP_ACK_HACK
+#define TCP_REASS(tp, ti, m, so, flags) {\
+       if ((ti)->ti_seq == (tp)->rcv_nxt && \
+           tcpfrag_list_empty(tp) && \
+           (tp)->t_state == TCPS_ESTABLISHED) {\
+               if (ti->ti_flags & TH_PUSH) \
+                       tp->t_flags |= TF_ACKNOW; \
+               else \
+                       tp->t_flags |= TF_DELACK; \
+               (tp)->rcv_nxt += (ti)->ti_len; \
+               flags = (ti)->ti_flags & TH_FIN; \
+               tcpstat.tcps_rcvpack++;\
+               tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+               if (so->so_emu) { \
+		       if (tcp_emu((so),(m))) sbuf_append((so), (m)); \
+	       } else \
+	       	       sbuf_append((so), (m)); \
+/*               sorwakeup(so); */ \
+	} else {\
+               (flags) = tcp_reass((tp), (ti), (m)); \
+               tp->t_flags |= TF_ACKNOW; \
+       } \
+}
+#else
+#define	TCP_REASS(tp, ti, m, so, flags) { \
+	if ((ti)->ti_seq == (tp)->rcv_nxt && \
+	    tcpfrag_list_empty(tp) && \
+	    (tp)->t_state == TCPS_ESTABLISHED) { \
+		tp->t_flags |= TF_DELACK; \
+		(tp)->rcv_nxt += (ti)->ti_len; \
+		flags = (ti)->ti_flags & TH_FIN; \
+		tcpstat.tcps_rcvpack++;\
+		tcpstat.tcps_rcvbyte += (ti)->ti_len;\
+		if (so->so_emu) { \
+			if (tcp_emu((so),(m))) sbuf_append(so, (m)); \
+		} else \
+			sbuf_append((so), (m)); \
+/*		sorwakeup(so); */ \
+	} else { \
+		(flags) = tcp_reass((tp), (ti), (m)); \
+		tp->t_flags |= TF_ACKNOW; \
+	} \
+}
+#endif
+
+int
+tcp_reass(tp, ti, m)
+	register struct tcpcb *tp;
+	register struct tcpiphdr *ti;
+	MBuf m;
+{
+	register struct tcpiphdr *q;
+	struct socket *so = tp->t_socket;
+	int flags;
+
+	/*
+	 * Call with ti==0 after become established to
+	 * force pre-ESTABLISHED data up to user socket.
+	 */
+	if (ti == 0)
+		goto present;
+
+	/*
+	 * Find a segment which begins after this one does.
+	 */
+	for (q = tcpfrag_list_first(tp); !tcpfrag_list_end(q, tp);
+	     q = tcpiphdr_next(q))
+		if (SEQ_GT(q->ti_seq, ti->ti_seq))
+			break;
+
+	/*
+	 * If there is a preceding segment, it may provide some of
+	 * our data already.  If so, drop the data from the incoming
+	 * segment.  If it provides all of our data, drop us.
+	 */
+	if (!tcpfrag_list_end(tcpiphdr_prev(q), tp)) {
+		register int i;
+		q = tcpiphdr_prev(q);
+		/* conversion to int (in i) handles seq wraparound */
+		i = q->ti_seq + q->ti_len - ti->ti_seq;
+		if (i > 0) {
+			if (i >= ti->ti_len) {
+				tcpstat.tcps_rcvduppack++;
+				tcpstat.tcps_rcvdupbyte += ti->ti_len;
+				mbuf_free(m);
+				/*
+				 * Try to present any queued data
+				 * at the left window edge to the user.
+				 * This is needed after the 3-WHS
+				 * completes.
+				 */
+				goto present;   /* ??? */
+			}
+			mbuf_trim(m, i);
+			ti->ti_len -= i;
+			ti->ti_seq += i;
+		}
+		q = tcpiphdr_next(q);
+	}
+	tcpstat.tcps_rcvoopack++;
+	tcpstat.tcps_rcvoobyte += ti->ti_len;
+	ti->ti_mbuf = m;
+
+	/*
+	 * While we overlap succeeding segments trim them or,
+	 * if they are completely covered, dequeue them.
+	 */
+	while (!tcpfrag_list_end(q, tp)) {
+		register int i = (ti->ti_seq + ti->ti_len) - q->ti_seq;
+		if (i <= 0)
+			break;
+		if (i < q->ti_len) {
+			q->ti_seq += i;
+			q->ti_len -= i;
+			mbuf_trim(q->ti_mbuf, i);
+			break;
+		}
+		q = tcpiphdr_next(q);
+		m = tcpiphdr_prev(q)->ti_mbuf;
+		remque(tcpiphdr2qlink(tcpiphdr_prev(q)));
+		mbuf_free(m);
+	}
+
+	/*
+	 * Stick new segment in its place.
+	 */
+	insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q)));
+
+present:
+	/*
+	 * Present data to user, advancing rcv_nxt through
+	 * completed sequence space.
+	 */
+	if (!TCPS_HAVEESTABLISHED(tp->t_state))
+		return (0);
+        ti = tcpfrag_list_first(tp);
+        if (tcpfrag_list_end(ti, tp) || ti->ti_seq != tp->rcv_nxt)
+		return (0);
+	if (tp->t_state == TCPS_SYN_RECEIVED && ti->ti_len)
+		return (0);
+	do {
+		tp->rcv_nxt += ti->ti_len;
+		flags = ti->ti_flags & TH_FIN;
+		remque(tcpiphdr2qlink(ti));
+		m = ti->ti_mbuf;
+		ti = tcpiphdr_next(ti);
+/*		if (so->so_state & SS_FCANTRCVMORE) */
+		if (so->so_state & SS_FCANTSENDMORE)
+			mbuf_free(m);
+		else {
+			if (so->so_emu) {
+				if (tcp_emu(so,m)) sbuf_append(so, m);
+			} else
+				sbuf_append(so, m);
+		}
+	} while (!tcpfrag_list_end(ti, tp) && ti->ti_seq == tp->rcv_nxt);
+/*	sorwakeup(so); */
+	return (flags);
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(m, iphlen, inso)
+	register MBuf m;
+	int iphlen;
+	struct socket *inso;
+{
+  	struct ip save_ip, *ip;
+	register struct tcpiphdr *ti;
+	caddr_t optp = NULL;
+	int optlen = 0;
+	int len, tlen, off;
+	register struct tcpcb *tp = 0;
+	register int tiflags;
+	struct socket *so = 0;
+	int todrop, acked, ourfinisacked, needoutput = 0;
+/*	int dropsocket = 0; */
+	int iss = 0;
+	u_long tiwin;
+	int ret;
+/*	int ts_present = 0; */
+
+	DEBUG_CALL("tcp_input");
+	DEBUG_ARGS((dfd," m = %8lx  iphlen = %2d  inso = %lx\n",
+		    (long )m, iphlen, (long )inso ));
+
+	/*
+	 * If called with m == 0, then we're continuing the connect
+	 */
+	if (m == NULL) {
+		so = inso;
+
+		/* Re-set a few variables */
+		tp = sototcpcb(so);
+		m = so->so_m;
+		so->so_m = 0;
+		ti = so->so_ti;
+		tiwin = ti->ti_win;
+		tiflags = ti->ti_flags;
+
+		goto cont_conn;
+	}
+
+
+	tcpstat.tcps_rcvtotal++;
+	/*
+	 * Get IP and TCP header together in first mbuf.
+	 * Note: IP leaves IP header in first mbuf.
+	 */
+	ti = MBUF_TO(m, struct tcpiphdr *);
+	if (iphlen > sizeof(struct ip )) {
+	  ip_stripoptions(m, (MBuf )0);
+	  iphlen=sizeof(struct ip );
+	}
+	/* XXX Check if too short */
+
+
+	/*
+	 * Save a copy of the IP header in case we want restore it
+	 * for sending an ICMP error message in response.
+	 */
+	ip=MBUF_TO(m, struct ip *);
+	save_ip = *ip;
+	save_ip.ip_len+= iphlen;
+
+	/*
+	 * Checksum extended TCP header and data.
+	 */
+	tlen = ((struct ip *)ti)->ip_len;
+	tcpiphdr2qlink(ti)->next = tcpiphdr2qlink(ti)->prev = 0;
+	memset(&ti->ti_i.ih_mbuf, 0, sizeof(struct mbuf_ptr));
+	ti->ti_x1 = 0;
+	ti->ti_len = htons((u_int16_t)tlen);
+	len = sizeof(struct ip ) + tlen;
+	/* keep checksum for ICMP reply
+	 * ti->ti_sum = cksum(m, len);
+	 * if (ti->ti_sum) { */
+	if(cksum(m, len)) {
+	  tcpstat.tcps_rcvbadsum++;
+	  goto drop;
+	}
+
+	/*
+	 * Check that TCP offset makes sense,
+	 * pull out TCP options and adjust length.		XXX
+	 */
+	off = ti->ti_off << 2;
+	if (off < sizeof (struct tcphdr) || off > tlen) {
+	  tcpstat.tcps_rcvbadoff++;
+	  goto drop;
+	}
+	tlen -= off;
+	ti->ti_len = tlen;
+	if (off > sizeof (struct tcphdr)) {
+	  optlen = off - sizeof (struct tcphdr);
+	  optp = MBUF_TO(m, caddr_t) + sizeof (struct tcpiphdr);
+
+		/*
+		 * Do quick retrieval of timestamp options ("options
+		 * prediction?").  If timestamp is the only option and it's
+		 * formatted as recommended in RFC 1323 appendix A, we
+		 * quickly get the values now and not bother calling
+		 * tcp_dooptions(), etc.
+		 */
+/*		if ((optlen == TCPOLEN_TSTAMP_APPA ||
+ *		     (optlen > TCPOLEN_TSTAMP_APPA &&
+ *			optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
+ *		     *(u_int32_t *)optp == htonl(TCPOPT_TSTAMP_HDR) &&
+ *		     (ti->ti_flags & TH_SYN) == 0) {
+ *			ts_present = 1;
+ *			ts_val = ntohl(*(u_int32_t *)(optp + 4));
+ *			ts_ecr = ntohl(*(u_int32_t *)(optp + 8));
+ *			optp = NULL;   / * we've parsed the options * /
+ *		}
+ */
+	}
+	tiflags = ti->ti_flags;
+
+	/*
+	 * Convert TCP protocol specific fields to host format.
+	 */
+	NTOHL(ti->ti_seq);
+	NTOHL(ti->ti_ack);
+	NTOHS(ti->ti_win);
+	NTOHS(ti->ti_urp);
+
+	/*
+	 * Drop TCP, IP headers and TCP options.
+	 */
+	m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+	m->m_len  -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+	/*
+	 * Locate pcb for segment.
+	 */
+findso:
+	so = tcp_last_so;
+        {
+            uint32_t  srcip   = ip_geth(ti->ti_src);
+            uint32_t  dstip   = ip_geth(ti->ti_dst);
+            uint16_t  dstport = port_geth(ti->ti_dport);
+            uint16_t  srcport = port_geth(ti->ti_sport);
+ 
+	if (so->so_faddr_port != dstport ||
+	    so->so_laddr_port != srcport ||
+	    so->so_laddr_ip   != srcip ||
+	    so->so_faddr_ip   != dstip) {
+		so = solookup(&tcb, srcip, srcport, dstip, dstport);
+		if (so)
+			tcp_last_so = so;
+		++tcpstat.tcps_socachemiss;
+	}
+        }
+
+	/*
+	 * If the state is CLOSED (i.e., TCB does not exist) then
+	 * all data in the incoming segment is discarded.
+	 * If the TCB exists but is in CLOSED state, it is embryonic,
+	 * but should either do a listen or a connect soon.
+	 *
+	 * state == CLOSED means we've done socreate() but haven't
+	 * attached it to a protocol yet...
+	 *
+	 * XXX If a TCB does not exist, and the TH_SYN flag is
+	 * the only flag set, then create a session, mark it
+	 * as if it was LISTENING, and continue...
+	 */
+	if (so == 0) {
+	  if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN)
+	    goto dropwithreset;
+
+	  if ((so = socreate()) == NULL)
+	    goto dropwithreset;
+	  if (tcp_attach(so) < 0) {
+	    free(so); /* Not sofree (if it failed, it's not insqued) */
+	    goto dropwithreset;
+	  }
+
+	  sbuf_reserve(&so->so_snd, tcp_sndspace);
+	  sbuf_reserve(&so->so_rcv, tcp_rcvspace);
+
+	  /*		tcp_last_so = so; */  /* XXX ? */
+	  /*		tp = sototcpcb(so);    */
+
+	  so->so_laddr_ip   = ip_geth(ti->ti_src);
+	  so->so_laddr_port = port_geth(ti->ti_sport);
+	  so->so_faddr_ip   = ip_geth(ti->ti_dst);
+	  so->so_faddr_port = port_geth(ti->ti_dport);
+
+	  if ((so->so_iptos = tcp_tos(so)) == 0)
+	    so->so_iptos = ((struct ip *)ti)->ip_tos;
+
+	  tp = sototcpcb(so);
+	  tp->t_state = TCPS_LISTEN;
+	}
+
+        /*
+         * If this is a still-connecting socket, this probably
+         * a retransmit of the SYN.  Whether it's a retransmit SYN
+	 * or something else, we nuke it.
+         */
+        if (so->so_state & SS_ISFCONNECTING)
+                goto drop;
+
+	tp = sototcpcb(so);
+
+	/* XXX Should never fail */
+	if (tp == 0)
+		goto dropwithreset;
+	if (tp->t_state == TCPS_CLOSED)
+		goto drop;
+
+	/* Unscale the window into a 32-bit value. */
+/*	if ((tiflags & TH_SYN) == 0)
+ *		tiwin = ti->ti_win << tp->snd_scale;
+ *	else
+ */
+		tiwin = ti->ti_win;
+
+	/*
+	 * Segment received on connection.
+	 * Reset idle time and keep-alive timer.
+	 */
+	tp->t_idle = 0;
+	if (so_options)
+	   tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+	else
+	   tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+
+	/*
+	 * Process options if not in LISTEN state,
+	 * else do it below (after getting remote address).
+	 */
+	if (optp && tp->t_state != TCPS_LISTEN)
+		tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+/* , */
+/*			&ts_present, &ts_val, &ts_ecr); */
+
+	/*
+	 * Header prediction: check for the two common cases
+	 * of a uni-directional data xfer.  If the packet has
+	 * no control flags, is in-sequence, the window didn't
+	 * change and we're not retransmitting, it's a
+	 * candidate.  If the length is zero and the ack moved
+	 * forward, we're the sender side of the xfer.  Just
+	 * free the data acked & wake any higher level process
+	 * that was blocked waiting for space.  If the length
+	 * is non-zero and the ack didn't move, we're the
+	 * receiver side.  If we're getting packets in-order
+	 * (the reassembly queue is empty), add the data to
+	 * the socket buffer and note that we need a delayed ack.
+	 *
+	 * XXX Some of these tests are not needed
+	 * eg: the tiwin == tp->snd_wnd prevents many more
+	 * predictions.. with no *real* advantage..
+	 */
+	if (tp->t_state == TCPS_ESTABLISHED &&
+	    (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK &&
+/*	    (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) && */
+	    ti->ti_seq == tp->rcv_nxt &&
+	    tiwin && tiwin == tp->snd_wnd &&
+	    tp->snd_nxt == tp->snd_max) {
+		/*
+		 * If last ACK falls within this segment's sequence numbers,
+		 *  record the timestamp.
+		 */
+/*		if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ *		   SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len)) {
+ *			tp->ts_recent_age = tcp_now;
+ *			tp->ts_recent = ts_val;
+ *		}
+ */
+		if (ti->ti_len == 0) {
+			if (SEQ_GT(ti->ti_ack, tp->snd_una) &&
+			    SEQ_LEQ(ti->ti_ack, tp->snd_max) &&
+			    tp->snd_cwnd >= tp->snd_wnd) {
+				/*
+				 * this is a pure ack for outstanding data.
+				 */
+				++tcpstat.tcps_predack;
+/*				if (ts_present)
+ *					tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ *				else
+ */				     if (tp->t_rtt &&
+					    SEQ_GT(ti->ti_ack, tp->t_rtseq))
+					tcp_xmit_timer(tp, tp->t_rtt);
+				acked = ti->ti_ack - tp->snd_una;
+				tcpstat.tcps_rcvackpack++;
+				tcpstat.tcps_rcvackbyte += acked;
+				sbuf_drop(&so->so_snd, acked);
+				tp->snd_una = ti->ti_ack;
+				mbuf_free(m);
+
+				/*
+				 * If all outstanding data are acked, stop
+				 * retransmit timer, otherwise restart timer
+				 * using current (possibly backed-off) value.
+				 * If process is waiting for space,
+				 * wakeup/selwakeup/signal.  If data
+				 * are ready to send, let tcp_output
+				 * decide between more output or persist.
+				 */
+				if (tp->snd_una == tp->snd_max)
+					tp->t_timer[TCPT_REXMT] = 0;
+				else if (tp->t_timer[TCPT_PERSIST] == 0)
+					tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+				/*
+				 * There's room in so_snd, sowwakup will read()
+				 * from the socket if we can
+				 */
+/*				if (so->so_snd.sb_flags & SB_NOTIFY)
+ *					sowwakeup(so);
+ */
+				/*
+				 * This is called because sowwakeup might have
+				 * put data into so_snd.  Since we don't so sowwakeup,
+				 * we don't need this.. XXX???
+				 */
+				if (so->so_snd.sb_cc)
+					(void) tcp_output(tp);
+
+				return;
+			}
+		} else if (ti->ti_ack == tp->snd_una &&
+		    tcpfrag_list_empty(tp) &&
+		    ti->ti_len <= sbuf_space(&so->so_rcv)) {
+			/*
+			 * this is a pure, in-sequence data packet
+			 * with nothing on the reassembly queue and
+			 * we have enough buffer space to take it.
+			 */
+			++tcpstat.tcps_preddat;
+			tp->rcv_nxt += ti->ti_len;
+			tcpstat.tcps_rcvpack++;
+			tcpstat.tcps_rcvbyte += ti->ti_len;
+			/*
+			 * Add data to socket buffer.
+			 */
+			if (so->so_emu) {
+				if (tcp_emu(so,m)) sbuf_append(so, m);
+			} else
+				sbuf_append(so, m);
+
+			/*
+			 * XXX This is called when data arrives.  Later, check
+			 * if we can actually write() to the socket
+			 * XXX Need to check? It's be NON_BLOCKING
+			 */
+/*			sorwakeup(so); */
+
+			/*
+			 * If this is a short packet, then ACK now - with Nagel
+			 *	congestion avoidance sender won't send more until
+			 *	he gets an ACK.
+			 *
+			 * It is better to not delay acks at all to maximize
+			 * TCP throughput.  See RFC 2581.
+			 */
+			tp->t_flags |= TF_ACKNOW;
+			tcp_output(tp);
+			return;
+		}
+	} /* header prediction */
+	/*
+	 * Calculate amount of space in receive window,
+	 * and then do TCP input processing.
+	 * Receive window is amount of space in rcv queue,
+	 * but not less than advertised window.
+	 */
+	{ int win;
+          win = sbuf_space(&so->so_rcv);
+	  if (win < 0)
+	    win = 0;
+	  tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+	}
+
+	switch (tp->t_state) {
+
+	/*
+	 * If the state is LISTEN then ignore segment if it contains an RST.
+	 * If the segment contains an ACK then it is bad and send a RST.
+	 * If it does not contain a SYN then it is not interesting; drop it.
+	 * Don't bother responding if the destination was a broadcast.
+	 * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+	 * tp->iss, and send a segment:
+	 *     <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+	 * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+	 * Fill in remote peer address fields if not previously specified.
+	 * Enter SYN_RECEIVED state, and process any other fields of this
+	 * segment in this state.
+	 */
+	case TCPS_LISTEN: {
+
+	  if (tiflags & TH_RST)
+	    goto drop;
+	  if (tiflags & TH_ACK)
+	    goto dropwithreset;
+	  if ((tiflags & TH_SYN) == 0)
+	    goto drop;
+
+	  /*
+	   * This has way too many gotos...
+	   * But a bit of spaghetti code never hurt anybody :)
+	   */
+
+	  /*
+	   * If this is destined for the control address, then flag to
+	   * tcp_ctl once connected, otherwise connect
+	   */
+	  if ((so->so_faddr_ip & 0xffffff00) == special_addr_ip) {
+	    //int lastbyte=ntohl(so->so_faddr.s_addr) & 0xff;
+	    /* CTL_ALIAS: Do nothing, tcp_fconnect will be called on it */
+	  }
+
+	  if (so->so_emu & EMU_NOCONNECT) {
+	    so->so_emu &= ~EMU_NOCONNECT;
+	    goto cont_input;
+	  }
+
+	  if((tcp_fconnect(so) == -1) && (errno != EINPROGRESS) && (errno != EWOULDBLOCK)) {
+	    u_char code=ICMP_UNREACH_NET;
+	    DEBUG_MISC((dfd," tcp fconnect errno = %d-%s\n",
+			errno,errno_str));
+	    if(errno == ECONNREFUSED) {
+	      /* ACK the SYN, send RST to refuse the connection */
+	      tcp_respond(tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
+			  TH_RST|TH_ACK);
+	    } else {
+	      if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST;
+	      HTONL(ti->ti_seq);             /* restore tcp header */
+	      HTONL(ti->ti_ack);
+	      HTONS(ti->ti_win);
+	      HTONS(ti->ti_urp);
+	      m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+	      m->m_len  += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+	      *ip=save_ip;
+	      icmp_error(m, ICMP_UNREACH,code, 0,errno_str);
+	    }
+	    tp = tcp_close(tp);
+	    mbuf_free(m);
+	  } else {
+	    /*
+	     * Haven't connected yet, save the current mbuf
+	     * and ti, and return
+	     * XXX Some OS's don't tell us whether the connect()
+	     * succeeded or not.  So we must time it out.
+	     */
+	    so->so_m = m;
+	    so->so_ti = ti;
+	    tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+	    tp->t_state = TCPS_SYN_RECEIVED;
+	  }
+	  return;
+
+	cont_conn:
+	  /* m==NULL
+	   * Check if the connect succeeded
+	   */
+	  if (so->so_state & SS_NOFDREF) {
+            DEBUG_MISC((dfd, " tcp_input closing connecting socket %lx\n", (long)so));
+	    tp = tcp_close(tp);
+	    goto dropwithreset;
+	  }
+	cont_input:
+	  tcp_template(tp);
+
+	  if (optp)
+	    tcp_dooptions(tp, (u_char *)optp, optlen, ti);
+	  /* , */
+	  /*				&ts_present, &ts_val, &ts_ecr); */
+
+	  if (iss)
+	    tp->iss = iss;
+	  else
+	    tp->iss = tcp_iss;
+	  tcp_iss += TCP_ISSINCR/2;
+	  tp->irs = ti->ti_seq;
+	  tcp_sendseqinit(tp);
+	  tcp_rcvseqinit(tp);
+	  tp->t_flags |= TF_ACKNOW;
+	  tp->t_state = TCPS_SYN_RECEIVED;
+	  tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+	  tcpstat.tcps_accepts++;
+          DEBUG_MISC((dfd, " tcp_input accept connected socket %lx\n", (long)so));
+	  goto trimthenstep6;
+	} /* case TCPS_LISTEN */
+
+	/*
+	 * If the state is SYN_SENT:
+	 *	if seg contains an ACK, but not for our SYN, drop the input.
+	 *	if seg contains a RST, then drop the connection.
+	 *	if seg does not contain SYN, then drop it.
+	 * Otherwise this is an acceptable SYN segment
+	 *	initialize tp->rcv_nxt and tp->irs
+	 *	if seg contains ack then advance tp->snd_una
+	 *	if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+	 *	arrange for segment to be acked (eventually)
+	 *	continue processing rest of data/controls, beginning with URG
+	 */
+	case TCPS_SYN_SENT:
+		if ((tiflags & TH_ACK) &&
+		    (SEQ_LEQ(ti->ti_ack, tp->iss) ||
+		     SEQ_GT(ti->ti_ack, tp->snd_max)))
+			goto dropwithreset;
+
+		if (tiflags & TH_RST) {
+			if (tiflags & TH_ACK)
+				tp = tcp_drop(tp,0); /* XXX Check t_softerror! */
+			goto drop;
+		}
+
+		if ((tiflags & TH_SYN) == 0)
+			goto drop;
+		if (tiflags & TH_ACK) {
+			tp->snd_una = ti->ti_ack;
+			if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+				tp->snd_nxt = tp->snd_una;
+		}
+
+		tp->t_timer[TCPT_REXMT] = 0;
+		tp->irs = ti->ti_seq;
+		tcp_rcvseqinit(tp);
+		tp->t_flags |= TF_ACKNOW;
+		if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss)) {
+			tcpstat.tcps_connects++;
+			soisfconnected(so);
+			tp->t_state = TCPS_ESTABLISHED;
+
+			/* Do window scaling on this connection? */
+/*			if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ *				(TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ * 				tp->snd_scale = tp->requested_s_scale;
+ *				tp->rcv_scale = tp->request_r_scale;
+ *			}
+ */
+			(void) tcp_reass(tp, (struct tcpiphdr *)0,
+				(MBuf )0);
+			/*
+			 * if we didn't have to retransmit the SYN,
+			 * use its rtt as our initial srtt & rtt var.
+			 */
+			if (tp->t_rtt)
+				tcp_xmit_timer(tp, tp->t_rtt);
+		} else
+			tp->t_state = TCPS_SYN_RECEIVED;
+
+trimthenstep6:
+		/*
+		 * Advance ti->ti_seq to correspond to first data byte.
+		 * If data, trim to stay within window,
+		 * dropping FIN if necessary.
+		 */
+		ti->ti_seq++;
+		if (ti->ti_len > tp->rcv_wnd) {
+			todrop = ti->ti_len - tp->rcv_wnd;
+			mbuf_trim(m, -todrop);
+			ti->ti_len = tp->rcv_wnd;
+			tiflags &= ~TH_FIN;
+			tcpstat.tcps_rcvpackafterwin++;
+			tcpstat.tcps_rcvbyteafterwin += todrop;
+		}
+		tp->snd_wl1 = ti->ti_seq - 1;
+		tp->rcv_up = ti->ti_seq;
+		goto step6;
+	} /* switch tp->t_state */
+	/*
+	 * States other than LISTEN or SYN_SENT.
+	 * First check timestamp, if present.
+	 * Then check that at least some bytes of segment are within
+	 * receive window.  If segment begins before rcv_nxt,
+	 * drop leading data (and SYN); if nothing left, just ack.
+	 *
+	 * RFC 1323 PAWS: If we have a timestamp reply on this segment
+	 * and it's less than ts_recent, drop it.
+	 */
+/*	if (ts_present && (tiflags & TH_RST) == 0 && tp->ts_recent &&
+ *	    TSTMP_LT(ts_val, tp->ts_recent)) {
+ *
+ */		/* Check to see if ts_recent is over 24 days old.  */
+/*		if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE) {
+ */			/*
+ *			 * Invalidate ts_recent.  If this segment updates
+ *			 * ts_recent, the age will be reset later and ts_recent
+ *			 * will get a valid value.  If it does not, setting
+ *			 * ts_recent to zero will at least satisfy the
+ *			 * requirement that zero be placed in the timestamp
+ *			 * echo reply when ts_recent isn't valid.  The
+ *			 * age isn't reset until we get a valid ts_recent
+ *			 * because we don't want out-of-order segments to be
+ *			 * dropped when ts_recent is old.
+ *			 */
+/*			tp->ts_recent = 0;
+ *		} else {
+ *			tcpstat.tcps_rcvduppack++;
+ *			tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ *			tcpstat.tcps_pawsdrop++;
+ *			goto dropafterack;
+ *		}
+ *	}
+ */
+
+	todrop = tp->rcv_nxt - ti->ti_seq;
+	if (todrop > 0) {
+		if (tiflags & TH_SYN) {
+			tiflags &= ~TH_SYN;
+			ti->ti_seq++;
+			if (ti->ti_urp > 1)
+				ti->ti_urp--;
+			else
+				tiflags &= ~TH_URG;
+			todrop--;
+		}
+		/*
+		 * Following if statement from Stevens, vol. 2, p. 960.
+		 */
+		if (todrop > ti->ti_len
+		    || (todrop == ti->ti_len && (tiflags & TH_FIN) == 0)) {
+			/*
+			 * Any valid FIN must be to the left of the window.
+			 * At this point the FIN must be a duplicate or out
+			 * of sequence; drop it.
+			 */
+			tiflags &= ~TH_FIN;
+
+			/*
+			 * Send an ACK to resynchronize and drop any data.
+			 * But keep on processing for RST or ACK.
+			 */
+			tp->t_flags |= TF_ACKNOW;
+			todrop = ti->ti_len;
+			tcpstat.tcps_rcvduppack++;
+			tcpstat.tcps_rcvdupbyte += todrop;
+		} else {
+			tcpstat.tcps_rcvpartduppack++;
+			tcpstat.tcps_rcvpartdupbyte += todrop;
+		}
+		mbuf_trim(m, todrop);
+		ti->ti_seq += todrop;
+		ti->ti_len -= todrop;
+		if (ti->ti_urp > todrop)
+			ti->ti_urp -= todrop;
+		else {
+			tiflags &= ~TH_URG;
+			ti->ti_urp = 0;
+		}
+	}
+	/*
+	 * If new data are received on a connection after the
+	 * user processes are gone, then RST the other end.
+	 */
+	if ((so->so_state & SS_NOFDREF) &&
+	    tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len) {
+		tp = tcp_close(tp);
+		tcpstat.tcps_rcvafterclose++;
+		goto dropwithreset;
+	}
+
+	/*
+	 * If segment ends after window, drop trailing data
+	 * (and PUSH and FIN); if nothing left, just ACK.
+	 */
+	todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+	if (todrop > 0) {
+		tcpstat.tcps_rcvpackafterwin++;
+		if (todrop >= ti->ti_len) {
+			tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
+			/*
+			 * If a new connection request is received
+			 * while in TIME_WAIT, drop the old connection
+			 * and start over if the sequence numbers
+			 * are above the previous ones.
+			 */
+			if (tiflags & TH_SYN &&
+			    tp->t_state == TCPS_TIME_WAIT &&
+			    SEQ_GT(ti->ti_seq, tp->rcv_nxt)) {
+				iss = tp->rcv_nxt + TCP_ISSINCR;
+				tp = tcp_close(tp);
+				goto findso;
+			}
+			/*
+			 * If window is closed can only take segments at
+			 * window edge, and have to drop data and PUSH from
+			 * incoming segments.  Continue processing, but
+			 * remember to ack.  Otherwise, drop segment
+			 * and ack.
+			 */
+			if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt) {
+				tp->t_flags |= TF_ACKNOW;
+				tcpstat.tcps_rcvwinprobe++;
+			} else
+				goto dropafterack;
+		} else
+			tcpstat.tcps_rcvbyteafterwin += todrop;
+		mbuf_trim(m, -todrop);
+		ti->ti_len -= todrop;
+		tiflags &= ~(TH_PUSH|TH_FIN);
+	}
+
+	/*
+	 * If last ACK falls within this segment's sequence numbers,
+	 * record its timestamp.
+	 */
+/*	if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ *	    SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len +
+ *		   ((tiflags & (TH_SYN|TH_FIN)) != 0))) {
+ *		tp->ts_recent_age = tcp_now;
+ *		tp->ts_recent = ts_val;
+ *	}
+ */
+
+	/*
+	 * If the RST bit is set examine the state:
+	 *    SYN_RECEIVED STATE:
+	 *	If passive open, return to LISTEN state.
+	 *	If active open, inform user that connection was refused.
+	 *    ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+	 *	Inform user that connection was reset, and close tcb.
+	 *    CLOSING, LAST_ACK, TIME_WAIT STATES
+	 *	Close the tcb.
+	 */
+	if (tiflags&TH_RST) switch (tp->t_state) {
+
+	case TCPS_SYN_RECEIVED:
+/*		so->so_error = ECONNREFUSED; */
+		goto close;
+
+	case TCPS_ESTABLISHED:
+	case TCPS_FIN_WAIT_1:
+	case TCPS_FIN_WAIT_2:
+	case TCPS_CLOSE_WAIT:
+/*		so->so_error = ECONNRESET; */
+	close:
+		tp->t_state = TCPS_CLOSED;
+		tcpstat.tcps_drops++;
+		tp = tcp_close(tp);
+		goto drop;
+
+	case TCPS_CLOSING:
+	case TCPS_LAST_ACK:
+	case TCPS_TIME_WAIT:
+		tp = tcp_close(tp);
+		goto drop;
+	}
+
+	/*
+	 * If a SYN is in the window, then this is an
+	 * error and we send an RST and drop the connection.
+	 */
+	if (tiflags & TH_SYN) {
+		tp = tcp_drop(tp,0);
+		goto dropwithreset;
+	}
+
+	/*
+	 * If the ACK bit is off we drop the segment and return.
+	 */
+	if ((tiflags & TH_ACK) == 0) goto drop;
+
+	/*
+	 * Ack processing.
+	 */
+	switch (tp->t_state) {
+	/*
+	 * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+	 * ESTABLISHED state and continue processing, otherwise
+	 * send an RST.  una<=ack<=max
+	 */
+	case TCPS_SYN_RECEIVED:
+
+		if (SEQ_GT(tp->snd_una, ti->ti_ack) ||
+		    SEQ_GT(ti->ti_ack, tp->snd_max))
+			goto dropwithreset;
+		tcpstat.tcps_connects++;
+		tp->t_state = TCPS_ESTABLISHED;
+		/*
+		 * The sent SYN is ack'ed with our sequence number +1
+		 * The first data byte already in the buffer will get
+		 * lost if no correction is made.  This is only needed for
+		 * SS_CTL since the buffer is empty otherwise.
+		 * tp->snd_una++; or:
+		 */
+		tp->snd_una=ti->ti_ack;
+		if (so->so_state & SS_CTL) {
+		  /* So tcp_ctl reports the right state */
+		  ret = tcp_ctl(so);
+		  if (ret == 1) {
+		    soisfconnected(so);
+		    so->so_state &= ~SS_CTL;   /* success XXX */
+		  } else if (ret == 2) {
+		    so->so_state = SS_NOFDREF; /* CTL_CMD */
+		  } else {
+		    needoutput = 1;
+		    tp->t_state = TCPS_FIN_WAIT_1;
+		  }
+		} else {
+		  soisfconnected(so);
+		}
+
+		/* Do window scaling? */
+/*		if ((tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE)) ==
+ *			(TF_RCVD_SCALE|TF_REQ_SCALE)) {
+ *			tp->snd_scale = tp->requested_s_scale;
+ *			tp->rcv_scale = tp->request_r_scale;
+ *		}
+ */
+		(void) tcp_reass(tp, (struct tcpiphdr *)0, (MBuf )0);
+		tp->snd_wl1 = ti->ti_seq - 1;
+		/* Avoid ack processing; snd_una==ti_ack  =>  dup ack */
+		goto synrx_to_est;
+		/* fall into ... */
+
+	/*
+	 * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+	 * ACKs.  If the ack is in the range
+	 *	tp->snd_una < ti->ti_ack <= tp->snd_max
+	 * then advance tp->snd_una to ti->ti_ack and drop
+	 * data from the retransmission queue.  If this ACK reflects
+	 * more up to date window information we update our window information.
+	 */
+	case TCPS_ESTABLISHED:
+	case TCPS_FIN_WAIT_1:
+	case TCPS_FIN_WAIT_2:
+	case TCPS_CLOSE_WAIT:
+	case TCPS_CLOSING:
+	case TCPS_LAST_ACK:
+	case TCPS_TIME_WAIT:
+
+		if (SEQ_LEQ(ti->ti_ack, tp->snd_una)) {
+			if (ti->ti_len == 0 && tiwin == tp->snd_wnd) {
+			  tcpstat.tcps_rcvdupack++;
+			  DEBUG_MISC((dfd," dup ack  m = %lx  so = %lx \n",
+				      (long )m, (long )so));
+				/*
+				 * If we have outstanding data (other than
+				 * a window probe), this is a completely
+				 * duplicate ack (ie, window info didn't
+				 * change), the ack is the biggest we've
+				 * seen and we've seen exactly our rexmt
+				 * threshold of them, assume a packet
+				 * has been dropped and retransmit it.
+				 * Kludge snd_nxt & the congestion
+				 * window so we send only this one
+				 * packet.
+				 *
+				 * We know we're losing at the current
+				 * window size so do congestion avoidance
+				 * (set ssthresh to half the current window
+				 * and pull our congestion window back to
+				 * the new ssthresh).
+				 *
+				 * Dup acks mean that packets have left the
+				 * network (they're now cached at the receiver)
+				 * so bump cwnd by the amount in the receiver
+				 * to keep a constant cwnd packets in the
+				 * network.
+				 */
+				if (tp->t_timer[TCPT_REXMT] == 0 ||
+				    ti->ti_ack != tp->snd_una)
+					tp->t_dupacks = 0;
+				else if (++tp->t_dupacks == tcprexmtthresh) {
+					tcp_seq onxt = tp->snd_nxt;
+					u_int win =
+					    min(tp->snd_wnd, tp->snd_cwnd) / 2 /
+						tp->t_maxseg;
+
+					if (win < 2)
+						win = 2;
+					tp->snd_ssthresh = win * tp->t_maxseg;
+					tp->t_timer[TCPT_REXMT] = 0;
+					tp->t_rtt = 0;
+					tp->snd_nxt = ti->ti_ack;
+					tp->snd_cwnd = tp->t_maxseg;
+					(void) tcp_output(tp);
+					tp->snd_cwnd = tp->snd_ssthresh +
+					       tp->t_maxseg * tp->t_dupacks;
+					if (SEQ_GT(onxt, tp->snd_nxt))
+						tp->snd_nxt = onxt;
+					goto drop;
+				} else if (tp->t_dupacks > tcprexmtthresh) {
+					tp->snd_cwnd += tp->t_maxseg;
+					(void) tcp_output(tp);
+					goto drop;
+				}
+			} else
+				tp->t_dupacks = 0;
+			break;
+		}
+	synrx_to_est:
+		/*
+		 * If the congestion window was inflated to account
+		 * for the other side's cached packets, retract it.
+		 */
+		if (tp->t_dupacks > tcprexmtthresh &&
+		    tp->snd_cwnd > tp->snd_ssthresh)
+			tp->snd_cwnd = tp->snd_ssthresh;
+		tp->t_dupacks = 0;
+		if (SEQ_GT(ti->ti_ack, tp->snd_max)) {
+			tcpstat.tcps_rcvacktoomuch++;
+			goto dropafterack;
+		}
+		acked = ti->ti_ack - tp->snd_una;
+		tcpstat.tcps_rcvackpack++;
+		tcpstat.tcps_rcvackbyte += acked;
+
+		/*
+		 * If we have a timestamp reply, update smoothed
+		 * round trip time.  If no timestamp is present but
+		 * transmit timer is running and timed sequence
+		 * number was acked, update smoothed round trip time.
+		 * Since we now have an rtt measurement, cancel the
+		 * timer backoff (cf., Phil Karn's retransmit alg.).
+		 * Recompute the initial retransmit timer.
+		 */
+/*		if (ts_present)
+ *			tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ *		else
+ */
+		     if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+			tcp_xmit_timer(tp,tp->t_rtt);
+
+		/*
+		 * If all outstanding data is acked, stop retransmit
+		 * timer and remember to restart (more output or persist).
+		 * If there is more data to be acked, restart retransmit
+		 * timer, using current (possibly backed-off) value.
+		 */
+		if (ti->ti_ack == tp->snd_max) {
+			tp->t_timer[TCPT_REXMT] = 0;
+			needoutput = 1;
+		} else if (tp->t_timer[TCPT_PERSIST] == 0)
+			tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+		/*
+		 * When new data is acked, open the congestion window.
+		 * If the window gives us less than ssthresh packets
+		 * in flight, open exponentially (maxseg per packet).
+		 * Otherwise open linearly: maxseg per window
+		 * (maxseg^2 / cwnd per packet).
+		 */
+		{
+		  register u_int cw = tp->snd_cwnd;
+		  register u_int incr = tp->t_maxseg;
+
+		  if (cw > tp->snd_ssthresh)
+		    incr = incr * incr / cw;
+		  tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+		}
+		if (acked > so->so_snd.sb_cc) {
+			tp->snd_wnd -= so->so_snd.sb_cc;
+			sbuf_drop(&so->so_snd, (int )so->so_snd.sb_cc);
+			ourfinisacked = 1;
+		} else {
+			sbuf_drop(&so->so_snd, acked);
+			tp->snd_wnd -= acked;
+			ourfinisacked = 0;
+		}
+		/*
+		 * XXX sowwakup is called when data is acked and there's room for
+		 * for more data... it should read() the socket
+		 */
+/*		if (so->so_snd.sb_flags & SB_NOTIFY)
+ *			sowwakeup(so);
+ */
+		tp->snd_una = ti->ti_ack;
+		if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+			tp->snd_nxt = tp->snd_una;
+
+		switch (tp->t_state) {
+
+		/*
+		 * In FIN_WAIT_1 STATE in addition to the processing
+		 * for the ESTABLISHED state if our FIN is now acknowledged
+		 * then enter FIN_WAIT_2.
+		 */
+		case TCPS_FIN_WAIT_1:
+			if (ourfinisacked) {
+				/*
+				 * If we can't receive any more
+				 * data, then closing user can proceed.
+				 * Starting the timer is contrary to the
+				 * specification, but if we don't get a FIN
+				 * we'll hang forever.
+				 */
+				if (so->so_state & SS_FCANTRCVMORE) {
+					soisfdisconnected(so);
+					tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+				}
+				tp->t_state = TCPS_FIN_WAIT_2;
+			}
+			break;
+
+	 	/*
+		 * In CLOSING STATE in addition to the processing for
+		 * the ESTABLISHED state if the ACK acknowledges our FIN
+		 * then enter the TIME-WAIT state, otherwise ignore
+		 * the segment.
+		 */
+		case TCPS_CLOSING:
+			if (ourfinisacked) {
+				tp->t_state = TCPS_TIME_WAIT;
+				tcp_canceltimers(tp);
+				tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+				soisfdisconnected(so);
+			}
+			break;
+
+		/*
+		 * In LAST_ACK, we may still be waiting for data to drain
+		 * and/or to be acked, as well as for the ack of our FIN.
+		 * If our FIN is now acknowledged, delete the TCB,
+		 * enter the closed state and return.
+		 */
+		case TCPS_LAST_ACK:
+			if (ourfinisacked) {
+				tp = tcp_close(tp);
+				goto drop;
+			}
+			break;
+
+		/*
+		 * In TIME_WAIT state the only thing that should arrive
+		 * is a retransmission of the remote FIN.  Acknowledge
+		 * it and restart the finack timer.
+		 */
+		case TCPS_TIME_WAIT:
+			tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+			goto dropafterack;
+		}
+	} /* switch(tp->t_state) */
+
+step6:
+	/*
+	 * Update window information.
+	 * Don't look at window if no ACK: TAC's send garbage on first SYN.
+	 */
+	if ((tiflags & TH_ACK) &&
+	    (SEQ_LT(tp->snd_wl1, ti->ti_seq) ||
+	    (tp->snd_wl1 == ti->ti_seq && (SEQ_LT(tp->snd_wl2, ti->ti_ack) ||
+	    (tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd))))) {
+		/* keep track of pure window updates */
+		if (ti->ti_len == 0 &&
+		    tp->snd_wl2 == ti->ti_ack && tiwin > tp->snd_wnd)
+			tcpstat.tcps_rcvwinupd++;
+		tp->snd_wnd = tiwin;
+		tp->snd_wl1 = ti->ti_seq;
+		tp->snd_wl2 = ti->ti_ack;
+		if (tp->snd_wnd > tp->max_sndwnd)
+			tp->max_sndwnd = tp->snd_wnd;
+		needoutput = 1;
+	}
+
+	/*
+	 * Process segments with URG.
+	 */
+	if ((tiflags & TH_URG) && ti->ti_urp &&
+	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+		/*
+		 * This is a kludge, but if we receive and accept
+		 * random urgent pointers, we'll crash in
+		 * soreceive.  It's hard to imagine someone
+		 * actually wanting to send this much urgent data.
+		 */
+		if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen) {
+			ti->ti_urp = 0;
+			tiflags &= ~TH_URG;
+			goto dodata;
+		}
+		/*
+		 * If this segment advances the known urgent pointer,
+		 * then mark the data stream.  This should not happen
+		 * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+		 * a FIN has been received from the remote side.
+		 * In these states we ignore the URG.
+		 *
+		 * According to RFC961 (Assigned Protocols),
+		 * the urgent pointer points to the last octet
+		 * of urgent data.  We continue, however,
+		 * to consider it to indicate the first octet
+		 * of data past the urgent section as the original
+		 * spec states (in one of two places).
+		 */
+		if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up)) {
+			tp->rcv_up = ti->ti_seq + ti->ti_urp;
+			so->so_urgc =  so->so_rcv.sb_cc +
+				(tp->rcv_up - tp->rcv_nxt); /* -1; */
+			tp->rcv_up = ti->ti_seq + ti->ti_urp;
+
+		}
+	} else
+		/*
+		 * If no out of band data is expected,
+		 * pull receive urgent pointer along
+		 * with the receive window.
+		 */
+		if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+			tp->rcv_up = tp->rcv_nxt;
+dodata:
+
+	/*
+	 * Process the segment text, merging it into the TCP sequencing queue,
+	 * and arranging for acknowledgment of receipt if necessary.
+	 * This process logically involves adjusting tp->rcv_wnd as data
+	 * is presented to the user (this happens in tcp_usrreq.c,
+	 * case PRU_RCVD).  If a FIN has already been received on this
+	 * connection then we just ignore the text.
+	 */
+	if ((ti->ti_len || (tiflags&TH_FIN)) &&
+	    TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+		TCP_REASS(tp, ti, m, so, tiflags);
+		/*
+		 * Note the amount of data that peer has sent into
+		 * our window, in order to estimate the sender's
+		 * buffer size.
+		 */
+		len = so->so_rcv.sb_datalen - (tp->rcv_adv - tp->rcv_nxt);
+	} else {
+		mbuf_free(m);
+		tiflags &= ~TH_FIN;
+	}
+
+	/*
+	 * If FIN is received ACK the FIN and let the user know
+	 * that the connection is closing.
+	 */
+	if (tiflags & TH_FIN) {
+		if (TCPS_HAVERCVDFIN(tp->t_state) == 0) {
+			/*
+			 * If we receive a FIN we can't send more data,
+			 * set it SS_FDRAIN
+                         * Shutdown the socket if there is no rx data in the
+			 * buffer.
+			 * soread() is called on completion of shutdown() and
+			 * will got to TCPS_LAST_ACK, and use tcp_output()
+			 * to send the FIN.
+			 */
+/*			sofcantrcvmore(so); */
+			sofwdrain(so);
+
+			tp->t_flags |= TF_ACKNOW;
+			tp->rcv_nxt++;
+		}
+		switch (tp->t_state) {
+
+	 	/*
+		 * In SYN_RECEIVED and ESTABLISHED STATES
+		 * enter the CLOSE_WAIT state.
+		 */
+		case TCPS_SYN_RECEIVED:
+		case TCPS_ESTABLISHED:
+		  if(so->so_emu == EMU_CTL)        /* no shutdown on socket */
+		    tp->t_state = TCPS_LAST_ACK;
+		  else
+		    tp->t_state = TCPS_CLOSE_WAIT;
+		  break;
+
+	 	/*
+		 * If still in FIN_WAIT_1 STATE FIN has not been acked so
+		 * enter the CLOSING state.
+		 */
+		case TCPS_FIN_WAIT_1:
+			tp->t_state = TCPS_CLOSING;
+			break;
+
+	 	/*
+		 * In FIN_WAIT_2 state enter the TIME_WAIT state,
+		 * starting the time-wait timer, turning off the other
+		 * standard timers.
+		 */
+		case TCPS_FIN_WAIT_2:
+			tp->t_state = TCPS_TIME_WAIT;
+			tcp_canceltimers(tp);
+			tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+			soisfdisconnected(so);
+			break;
+
+		/*
+		 * In TIME_WAIT state restart the 2 MSL time_wait timer.
+		 */
+		case TCPS_TIME_WAIT:
+			tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+			break;
+		}
+	}
+
+	/*
+	 * If this is a small packet, then ACK now - with Nagel
+	 *      congestion avoidance sender won't send more until
+	 *      he gets an ACK.
+	 *
+	 * See above.
+	 */
+/*	if (ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg) {
+ */
+/*	if ((ti->ti_len && (unsigned)ti->ti_len < tp->t_maxseg &&
+ *		(so->so_iptos & IPTOS_LOWDELAY) == 0) ||
+ *	       ((so->so_iptos & IPTOS_LOWDELAY) &&
+ *	       ((struct tcpiphdr_2 *)ti)->first_char == (char)27)) {
+ */
+	if (ti->ti_len && (unsigned)ti->ti_len <= 5 &&
+	    ((struct tcpiphdr_2 *)ti)->first_char == (char)27) {
+		tp->t_flags |= TF_ACKNOW;
+	}
+
+	/*
+	 * Return any desired output.
+	 */
+	if (needoutput || (tp->t_flags & TF_ACKNOW)) {
+		(void) tcp_output(tp);
+	}
+	return;
+
+dropafterack:
+	/*
+	 * Generate an ACK dropping incoming segment if it occupies
+	 * sequence space, where the ACK reflects our state.
+	 */
+	if (tiflags & TH_RST)
+		goto drop;
+	mbuf_free(m);
+	tp->t_flags |= TF_ACKNOW;
+	(void) tcp_output(tp);
+	return;
+
+dropwithreset:
+	/* reuses m if m!=NULL, mbuf_free() unnecessary */
+	if (tiflags & TH_ACK)
+		tcp_respond(tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+	else {
+		if (tiflags & TH_SYN) ti->ti_len++;
+		tcp_respond(tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+		    TH_RST|TH_ACK);
+	}
+
+	return;
+
+drop:
+	/*
+	 * Drop space held by incoming segment and return.
+	 */
+	mbuf_free(m);
+
+	return;
+}
+
+ /* , ts_present, ts_val, ts_ecr) */
+/*	int *ts_present;
+ *	u_int32_t *ts_val, *ts_ecr;
+ */
+void
+tcp_dooptions(tp, cp, cnt, ti)
+	struct tcpcb *tp;
+	u_char *cp;
+	int cnt;
+	struct tcpiphdr *ti;
+{
+	u_int16_t mss;
+	int opt, optlen;
+
+	DEBUG_CALL("tcp_dooptions");
+	DEBUG_ARGS((dfd," tp = %lx  cnt=%i \n", (long )tp, cnt));
+
+	for (; cnt > 0; cnt -= optlen, cp += optlen) {
+		opt = cp[0];
+		if (opt == TCPOPT_EOL)
+			break;
+		if (opt == TCPOPT_NOP)
+			optlen = 1;
+		else {
+			optlen = cp[1];
+			if (optlen <= 0)
+				break;
+		}
+		switch (opt) {
+
+		default:
+			continue;
+
+		case TCPOPT_MAXSEG:
+			if (optlen != TCPOLEN_MAXSEG)
+				continue;
+			if (!(ti->ti_flags & TH_SYN))
+				continue;
+			memcpy((char *) &mss, (char *) cp + 2, sizeof(mss));
+			NTOHS(mss);
+			(void) tcp_mss(tp, mss);	/* sets t_maxseg */
+			break;
+
+/*		case TCPOPT_WINDOW:
+ *			if (optlen != TCPOLEN_WINDOW)
+ *				continue;
+ *			if (!(ti->ti_flags & TH_SYN))
+ *				continue;
+ *			tp->t_flags |= TF_RCVD_SCALE;
+ *			tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
+ *			break;
+ */
+/*		case TCPOPT_TIMESTAMP:
+ *			if (optlen != TCPOLEN_TIMESTAMP)
+ *				continue;
+ *			*ts_present = 1;
+ *			memcpy((char *) ts_val, (char *)cp + 2, sizeof(*ts_val));
+ *			NTOHL(*ts_val);
+ *			memcpy((char *) ts_ecr, (char *)cp + 6, sizeof(*ts_ecr));
+ *			NTOHL(*ts_ecr);
+ *
+ */			/*
+ *			 * A timestamp received in a SYN makes
+ *			 * it ok to send timestamp requests and replies.
+ *			 */
+/*			if (ti->ti_flags & TH_SYN) {
+ *				tp->t_flags |= TF_RCVD_TSTMP;
+ *				tp->ts_recent = *ts_val;
+ *				tp->ts_recent_age = tcp_now;
+ *			}
+ */			break;
+		}
+	}
+}
+
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+
+#ifdef notdef
+
+void
+tcp_pulloutofband(so, ti, m)
+	struct socket *so;
+	struct tcpiphdr *ti;
+	register MBuf m;
+{
+	int cnt = ti->ti_urp - 1;
+
+	while (cnt >= 0) {
+		if (m->m_len > cnt) {
+			char *cp = MBUF_TO(m, caddr_t) + cnt;
+			struct tcpcb *tp = sototcpcb(so);
+
+			tp->t_iobc = *cp;
+			tp->t_oobflags |= TCPOOB_HAVEDATA;
+			memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1));
+			m->m_len--;
+			return;
+		}
+		cnt -= m->m_len;
+		m = m->m_next; /* XXX WRONG! Fix it! */
+		if (m == 0)
+			break;
+	}
+	panic("tcp_pulloutofband");
+}
+
+#endif /* notdef */
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+
+void
+tcp_xmit_timer(tp, rtt)
+	register struct tcpcb *tp;
+	int rtt;
+{
+	register short delta;
+
+	DEBUG_CALL("tcp_xmit_timer");
+	DEBUG_ARG("tp = %lx", (long)tp);
+	DEBUG_ARG("rtt = %d", rtt);
+
+	tcpstat.tcps_rttupdated++;
+	if (tp->t_srtt != 0) {
+		/*
+		 * srtt is stored as fixed point with 3 bits after the
+		 * binary point (i.e., scaled by 8).  The following magic
+		 * is equivalent to the smoothing algorithm in rfc793 with
+		 * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+		 * point).  Adjust rtt to origin 0.
+		 */
+		delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+		if ((tp->t_srtt += delta) <= 0)
+			tp->t_srtt = 1;
+		/*
+		 * We accumulate a smoothed rtt variance (actually, a
+		 * smoothed mean difference), then set the retransmit
+		 * timer to smoothed rtt + 4 times the smoothed variance.
+		 * rttvar is stored as fixed point with 2 bits after the
+		 * binary point (scaled by 4).  The following is
+		 * equivalent to rfc793 smoothing with an alpha of .75
+		 * (rttvar = rttvar*3/4 + |delta| / 4).  This replaces
+		 * rfc793's wired-in beta.
+		 */
+		if (delta < 0)
+			delta = -delta;
+		delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+		if ((tp->t_rttvar += delta) <= 0)
+			tp->t_rttvar = 1;
+	} else {
+		/*
+		 * No rtt measurement yet - use the unsmoothed rtt.
+		 * Set the variance to half the rtt (so our first
+		 * retransmit happens at 3*rtt).
+		 */
+		tp->t_srtt = rtt << TCP_RTT_SHIFT;
+		tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+	}
+	tp->t_rtt = 0;
+	tp->t_rxtshift = 0;
+
+	/*
+	 * the retransmit should happen at rtt + 4 * rttvar.
+	 * Because of the way we do the smoothing, srtt and rttvar
+	 * will each average +1/2 tick of bias.  When we compute
+	 * the retransmit timer, we want 1/2 tick of rounding and
+	 * 1 extra tick because of +-1/2 tick uncertainty in the
+	 * firing of the timer.  The bias will give us exactly the
+	 * 1.5 tick we need.  But, because the bias is
+	 * statistical, we have to test that we don't drop below
+	 * the minimum feasible timer (which is 2 ticks).
+	 */
+	TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+	    (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+
+	/*
+	 * We received an ack for a packet that wasn't retransmitted;
+	 * it is probably safe to discard any error indications we've
+	 * received recently.  This isn't quite right, but close enough
+	 * for now (a route might have failed after we sent a segment,
+	 * and the return path might not be symmetrical).
+	 */
+	tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs.  If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks.  We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+
+int
+tcp_mss(tp, offer)
+        register struct tcpcb *tp;
+        u_int offer;
+{
+	struct socket *so = tp->t_socket;
+	int mss;
+
+	DEBUG_CALL("tcp_mss");
+	DEBUG_ARG("tp = %lx", (long)tp);
+	DEBUG_ARG("offer = %d", offer);
+
+	mss = min(if_mtu, if_mru) - sizeof(struct tcpiphdr);
+	if (offer)
+		mss = min(mss, offer);
+	mss = max(mss, 32);
+	if (mss < tp->t_maxseg || offer != 0)
+	   tp->t_maxseg = mss;
+
+	tp->snd_cwnd = mss;
+
+	sbuf_reserve(&so->so_snd, tcp_sndspace+((tcp_sndspace%mss)?(mss-(tcp_sndspace%mss)):0));
+	sbuf_reserve(&so->so_rcv, tcp_rcvspace+((tcp_rcvspace%mss)?(mss-(tcp_rcvspace%mss)):0));
+
+	DEBUG_MISC((dfd, " returning mss = %d\n", mss));
+
+	return mss;
+}
diff --git a/slirp2/tcp_output.c b/slirp2/tcp_output.c
new file mode 100644
index 0000000..95246aa
--- /dev/null
+++ b/slirp2/tcp_output.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp_output.c	8.3 (Berkeley) 12/30/93
+ * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ * 
+ * Please read the file COPYRIGHT for the 
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/*
+ * Since this is only used in "stats socket", we give meaning
+ * names instead of the REAL names
+ */
+char *tcpstates[] = {
+/*	"CLOSED",       "LISTEN",       "SYN_SENT",     "SYN_RCVD", */
+	"REDIRECT",	"LISTEN",	"SYN_SENT",     "SYN_RCVD",
+	"ESTABLISHED",  "CLOSE_WAIT",   "FIN_WAIT_1",   "CLOSING",
+	"LAST_ACK",     "FIN_WAIT_2",   "TIME_WAIT",
+};
+
+u_char  tcp_outflags[TCP_NSTATES] = {
+	TH_RST|TH_ACK, 0,      TH_SYN,        TH_SYN|TH_ACK,
+	TH_ACK,        TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, 
+	TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+
+
+#define MAX_TCPOPTLEN	32	/* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(tp)
+	register struct tcpcb *tp;
+{
+	register struct socket *so = tp->t_socket;
+	register long len, win;
+	int off, flags, error;
+	register MBuf m;
+	register struct tcpiphdr *ti;
+	u_char opt[MAX_TCPOPTLEN];
+	unsigned optlen, hdrlen;
+	int idle, sendalot;
+	
+	DEBUG_CALL("tcp_output");
+	DEBUG_ARG("tp = %lx", (long )tp);
+	
+	/*
+	 * Determine length of data that should be transmitted,
+	 * and flags that will be used.
+	 * If there is some data or critical controls (SYN, RST)
+	 * to send, then transmit; otherwise, investigate further.
+	 */
+	idle = (tp->snd_max == tp->snd_una);
+	if (idle && tp->t_idle >= tp->t_rxtcur)
+		/*
+		 * We have been idle for "a while" and no acks are
+		 * expected to clock out any data we send --
+		 * slow start to get ack "clock" running again.
+		 */
+		tp->snd_cwnd = tp->t_maxseg;
+again:
+	sendalot = 0;
+	off = tp->snd_nxt - tp->snd_una;
+	win = min(tp->snd_wnd, tp->snd_cwnd);
+
+	flags = tcp_outflags[tp->t_state];
+	
+	DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags));
+	
+	/*
+	 * If in persist timeout with window of 0, send 1 byte.
+	 * Otherwise, if window is small but nonzero
+	 * and timer expired, we will send what we can
+	 * and go to transmit state.
+	 */
+	if (tp->t_force) {
+		if (win == 0) {
+			/*
+			 * If we still have some data to send, then
+			 * clear the FIN bit.  Usually this would
+			 * happen below when it realizes that we
+			 * aren't sending all the data.  However,
+			 * if we have exactly 1 byte of unset data,
+			 * then it won't clear the FIN bit below,
+			 * and if we are in persist state, we wind
+			 * up sending the packet without recording
+			 * that we sent the FIN bit.
+			 *
+			 * We can't just blindly clear the FIN bit,
+			 * because if we don't have any more data
+			 * to send then the probe will be the FIN
+			 * itself.
+			 */
+			if (off < so->so_snd.sb_cc)
+				flags &= ~TH_FIN;
+			win = 1;
+		} else {
+			tp->t_timer[TCPT_PERSIST] = 0;
+			tp->t_rxtshift = 0;
+		}
+	}
+
+	len = min(so->so_snd.sb_cc, win) - off;
+
+	if (len < 0) {
+		/*
+		 * If FIN has been sent but not acked,
+		 * but we haven't been called to retransmit,
+		 * len will be -1.  Otherwise, window shrank
+		 * after we sent into it.  If window shrank to 0,
+		 * cancel pending retransmit and pull snd_nxt
+		 * back to (closed) window.  We will enter persist
+		 * state below.  If the window didn't close completely,
+		 * just wait for an ACK.
+		 */
+		len = 0;
+		if (win == 0) {
+			tp->t_timer[TCPT_REXMT] = 0;
+			tp->snd_nxt = tp->snd_una;
+		}
+	}
+	
+	if (len > tp->t_maxseg) {
+		len = tp->t_maxseg;
+		sendalot = 1;
+	}
+	if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
+		flags &= ~TH_FIN;
+
+	win = sbuf_space(&so->so_rcv);
+
+	/*
+	 * Sender silly window avoidance.  If connection is idle
+	 * and can send all data, a maximum segment,
+	 * at least a maximum default-size segment do it,
+	 * or are forced, do it; otherwise don't bother.
+	 * If peer's buffer is tiny, then send
+	 * when window is at least half open.
+	 * If retransmitting (possibly after persist timer forced us
+	 * to send into a small window), then must resend.
+	 */
+	if (len) {
+		if (len == tp->t_maxseg)
+			goto send;
+		if ((1 || idle || tp->t_flags & TF_NODELAY) &&
+		    len + off >= so->so_snd.sb_cc)
+			goto send;
+		if (tp->t_force)
+			goto send;
+		if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
+			goto send;
+		if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+			goto send;
+	}
+
+	/*
+	 * Compare available window to amount of window
+	 * known to peer (as advertised window less
+	 * next expected input).  If the difference is at least two
+	 * max size segments, or at least 50% of the maximum possible
+	 * window, then want to send a window update to peer.
+	 */
+	if (win > 0) {
+		/* 
+		 * "adv" is the amount we can increase the window,
+		 * taking into account that we are limited by
+		 * TCP_MAXWIN << tp->rcv_scale.
+		 */
+		long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale) -
+			(tp->rcv_adv - tp->rcv_nxt);
+
+		if (adv >= (long) (2 * tp->t_maxseg))
+			goto send;
+		if (2 * adv >= (long) so->so_rcv.sb_datalen)
+			goto send;
+	}
+
+	/*
+	 * Send if we owe peer an ACK.
+	 */
+	if (tp->t_flags & TF_ACKNOW)
+		goto send;
+	if (flags & (TH_SYN|TH_RST))
+		goto send;
+	if (SEQ_GT(tp->snd_up, tp->snd_una))
+		goto send;
+	/*
+	 * If our state indicates that FIN should be sent
+	 * and we have not yet done so, or we're retransmitting the FIN,
+	 * then we need to send.
+	 */
+	if (flags & TH_FIN &&
+	    ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+		goto send;
+
+	/*
+	 * TCP window updates are not reliable, rather a polling protocol
+	 * using ``persist'' packets is used to insure receipt of window
+	 * updates.  The three ``states'' for the output side are:
+	 *	idle			not doing retransmits or persists
+	 *	persisting		to move a small or zero window
+	 *	(re)transmitting	and thereby not persisting
+	 *
+	 * tp->t_timer[TCPT_PERSIST]
+	 *	is set when we are in persist state.
+	 * tp->t_force
+	 *	is set when we are called to send a persist packet.
+	 * tp->t_timer[TCPT_REXMT]
+	 *	is set when we are retransmitting
+	 * The output side is idle when both timers are zero.
+	 *
+	 * If send window is too small, there is data to transmit, and no
+	 * retransmit or persist is pending, then go to persist state.
+	 * If nothing happens soon, send when timer expires:
+	 * if window is nonzero, transmit what we can,
+	 * otherwise force out a byte.
+	 */
+	if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
+	    tp->t_timer[TCPT_PERSIST] == 0) {
+		tp->t_rxtshift = 0;
+		tcp_setpersist(tp);
+	}
+
+	/*
+	 * No reason to send a segment, just return.
+	 */
+	tcpstat.tcps_didnuttin++;
+	
+	return (0);
+
+send:
+	/*
+	 * Before ESTABLISHED, force sending of initial options
+	 * unless TCP set not to do any options.
+	 * NOTE: we assume that the IP/TCP header plus TCP options
+	 * always fit in a single mbuf, leaving room for a maximum
+	 * link header, i.e.
+	 *	max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+	 */
+	optlen = 0;
+	hdrlen = sizeof (struct tcpiphdr);
+	if (flags & TH_SYN) {
+		tp->snd_nxt = tp->iss;
+		if ((tp->t_flags & TF_NOOPT) == 0) {
+			u_int16_t mss;
+
+			opt[0] = TCPOPT_MAXSEG;
+			opt[1] = 4;
+			mss = htons((u_int16_t) tcp_mss(tp, 0));
+			memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
+			optlen = 4;
+
+/*			if ((tp->t_flags & TF_REQ_SCALE) &&
+ *			    ((flags & TH_ACK) == 0 ||
+ *			    (tp->t_flags & TF_RCVD_SCALE))) {
+ *				*((u_int32_t *) (opt + optlen)) = htonl(
+ *					TCPOPT_NOP << 24 |
+ *					TCPOPT_WINDOW << 16 |
+ *					TCPOLEN_WINDOW << 8 |
+ *					tp->request_r_scale);
+ *				optlen += 4;
+ *			}
+ */
+		}
+ 	}
+ 
+ 	/*
+	 * Send a timestamp and echo-reply if this is a SYN and our side 
+	 * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
+	 * and our peer have sent timestamps in our SYN's.
+ 	 */
+/* 	if ((tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP &&
+ *	     (flags & TH_RST) == 0 &&
+ *	    ((flags & (TH_SYN|TH_ACK)) == TH_SYN ||
+ *	     (tp->t_flags & TF_RCVD_TSTMP))) {
+ *		u_int32_t *lp = (u_int32_t *)(opt + optlen);
+ *
+ *		/ * Form timestamp option as shown in appendix A of RFC 1323. *  /
+ *		*lp++ = htonl(TCPOPT_TSTAMP_HDR);
+ *		*lp++ = htonl(tcp_now);
+ *		*lp   = htonl(tp->ts_recent);
+ *		optlen += TCPOLEN_TSTAMP_APPA;
+ *	}
+ */
+ 	hdrlen += optlen;
+ 
+	/*
+	 * Adjust data length if insertion of options will
+	 * bump the packet length beyond the t_maxseg length.
+	 */
+	 if (len > tp->t_maxseg - optlen) {
+		len = tp->t_maxseg - optlen;
+		sendalot = 1;
+	 }
+
+	/*
+	 * Grab a header mbuf, attaching a copy of data to
+	 * be transmitted, and initialize the header from
+	 * the template for sends on this connection.
+	 */
+	if (len) {
+		if (tp->t_force && len == 1)
+			tcpstat.tcps_sndprobe++;
+		else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
+			tcpstat.tcps_sndrexmitpack++;
+			tcpstat.tcps_sndrexmitbyte += len;
+		} else {
+			tcpstat.tcps_sndpack++;
+			tcpstat.tcps_sndbyte += len;
+		}
+
+		m = mbuf_alloc();
+		if (m == NULL) {
+/*			error = ENOBUFS; */
+			error = 1;
+			goto out;
+		}
+		m->m_data += if_maxlinkhdr;
+		m->m_len = hdrlen;
+		
+		/* 
+		 * This will always succeed, since we make sure our mbufs
+		 * are big enough to hold one MSS packet + header + ... etc.
+		 */
+/*		if (len <= MHLEN - hdrlen - max_linkhdr) { */
+
+			sbuf_copy(&so->so_snd, off, (int) len, MBUF_TO(m, caddr_t) + hdrlen);
+			m->m_len += len;
+
+/*		} else {
+ *			m->m_next = mbuf_copy(so->so_snd.sb_mb, off, (int) len);
+ *			if (m->m_next == 0)
+ *				len = 0;
+ *		}
+ */
+		/*
+		 * If we're sending everything we've got, set PUSH.
+		 * (This will keep happy those implementations which only
+		 * give data to the user when a buffer fills or
+		 * a PUSH comes in.)
+		 */
+		if (off + len == so->so_snd.sb_cc)
+			flags |= TH_PUSH;
+	} else {
+		if (tp->t_flags & TF_ACKNOW)
+			tcpstat.tcps_sndacks++;
+		else if (flags & (TH_SYN|TH_FIN|TH_RST))
+			tcpstat.tcps_sndctrl++;
+		else if (SEQ_GT(tp->snd_up, tp->snd_una))
+			tcpstat.tcps_sndurg++;
+		else
+			tcpstat.tcps_sndwinup++;
+
+		m = mbuf_alloc();
+		if (m == NULL) {
+/*			error = ENOBUFS; */
+			error = 1;
+			goto out;
+		}
+		m->m_data += if_maxlinkhdr;
+		m->m_len = hdrlen;
+	}
+
+	ti = MBUF_TO(m, struct tcpiphdr *);
+	
+	memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
+
+	/*
+	 * Fill in fields, remembering maximum advertised
+	 * window for use in delaying messages about window sizes.
+	 * If resending a FIN, be sure not to use a new sequence number.
+	 */
+	if (flags & TH_FIN && tp->t_flags & TF_SENTFIN && 
+	    tp->snd_nxt == tp->snd_max)
+		tp->snd_nxt--;
+	/*
+	 * If we are doing retransmissions, then snd_nxt will
+	 * not reflect the first unsent octet.  For ACK only
+	 * packets, we do not want the sequence number of the
+	 * retransmitted packet, we want the sequence number
+	 * of the next unsent octet.  So, if there is no data
+	 * (and no SYN or FIN), use snd_max instead of snd_nxt
+	 * when filling in ti_seq.  But if we are in persist
+	 * state, snd_max might reflect one byte beyond the
+	 * right edge of the window, so use snd_nxt in that
+	 * case, since we know we aren't doing a retransmission.
+	 * (retransmit and persist are mutually exclusive...)
+	 */
+	if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+		ti->ti_seq = htonl(tp->snd_nxt);
+	else
+		ti->ti_seq = htonl(tp->snd_max);
+	ti->ti_ack = htonl(tp->rcv_nxt);
+	if (optlen) {
+		memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
+		ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2;
+	}
+	ti->ti_flags = flags;
+	/*
+	 * Calculate receive window.  Don't shrink window,
+	 * but avoid silly window syndrome.
+	 */
+	if (win < (long)(so->so_rcv.sb_datalen / 4) && win < (long)tp->t_maxseg)
+		win = 0;
+	if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+		win = (long)TCP_MAXWIN << tp->rcv_scale;
+	if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
+		win = (long)(tp->rcv_adv - tp->rcv_nxt);
+	ti->ti_win = htons((u_int16_t) (win>>tp->rcv_scale));
+	
+	if (SEQ_GT(tp->snd_up, tp->snd_una)) {
+		ti->ti_urp = htons((u_int16_t)(tp->snd_up - ntohl(ti->ti_seq)));
+#ifdef notdef		
+	if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
+		ti->ti_urp = htons((u_int16_t)(tp->snd_up - tp->snd_nxt));
+#endif
+		ti->ti_flags |= TH_URG;
+	} else
+		/*
+		 * If no urgent pointer to send, then we pull
+		 * the urgent pointer to the left edge of the send window
+		 * so that it doesn't drift into the send window on sequence
+		 * number wraparound.
+		 */
+		tp->snd_up = tp->snd_una;		/* drag it along */
+
+	/*
+	 * Put TCP length in extended header, and then
+	 * checksum extended header and data.
+	 */
+	if (len + optlen)
+		ti->ti_len = htons((u_int16_t)(sizeof (struct tcphdr) +
+		    optlen + len));
+	ti->ti_sum = cksum(m, (int)(hdrlen + len));
+
+	/*
+	 * In transmit state, time the transmission and arrange for
+	 * the retransmit.  In persist state, just set snd_max.
+	 */
+	if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
+		tcp_seq startseq = tp->snd_nxt;
+
+		/*
+		 * Advance snd_nxt over sequence space of this segment.
+		 */
+		if (flags & (TH_SYN|TH_FIN)) {
+			if (flags & TH_SYN)
+				tp->snd_nxt++;
+			if (flags & TH_FIN) {
+				tp->snd_nxt++;
+				tp->t_flags |= TF_SENTFIN;
+			}
+		}
+		tp->snd_nxt += len;
+		if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
+			tp->snd_max = tp->snd_nxt;
+			/*
+			 * Time this transmission if not a retransmission and
+			 * not currently timing anything.
+			 */
+			if (tp->t_rtt == 0) {
+				tp->t_rtt = 1;
+				tp->t_rtseq = startseq;
+				tcpstat.tcps_segstimed++;
+			}
+		}
+
+		/*
+		 * Set retransmit timer if not currently set,
+		 * and not doing an ack or a keep-alive probe.
+		 * Initial value for retransmit timer is smoothed
+		 * round-trip time + 2 * round-trip time variance.
+		 * Initialize shift counter which is used for backoff
+		 * of retransmit time.
+		 */
+		if (tp->t_timer[TCPT_REXMT] == 0 &&
+		    tp->snd_nxt != tp->snd_una) {
+			tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+			if (tp->t_timer[TCPT_PERSIST]) {
+				tp->t_timer[TCPT_PERSIST] = 0;
+				tp->t_rxtshift = 0;
+			}
+		}
+	} else
+		if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+			tp->snd_max = tp->snd_nxt + len;
+
+	/*
+	 * Fill in IP length and desired time to live and
+	 * send to IP level.  There should be a better way
+	 * to handle ttl and tos; we could keep them in
+	 * the template, but need a way to checksum without them.
+	 */
+	m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+	
+    {
+	    
+	((struct ip *)ti)->ip_len = m->m_len;
+
+	((struct ip *)ti)->ip_ttl = ip_defttl;
+	((struct ip *)ti)->ip_tos = so->so_iptos;
+	    
+/* #if BSD >= 43 */
+	/* Don't do IP options... */
+/*	error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+ *	    so->so_options & SO_DONTROUTE, 0);
+ */
+	error = ip_output(so, m);
+
+/* #else
+ *	error = ip_output(m, (MBuf )0, &tp->t_inpcb->inp_route, 
+ *	    so->so_options & SO_DONTROUTE);
+ * #endif
+ */
+    }
+	if (error) {
+out:
+/*		if (error == ENOBUFS) {
+ *			tcp_quench(tp->t_inpcb, 0);
+ *			return (0);
+ *		}
+ */
+/*		if ((error == EHOSTUNREACH || error == ENETDOWN)
+ *		    && TCPS_HAVERCVDSYN(tp->t_state)) {
+ *			tp->t_softerror = error;
+ *			return (0);
+ *		}
+ */
+		return (error);
+	}
+	tcpstat.tcps_sndtotal++;
+
+	/*
+	 * Data sent (as far as we can tell).
+	 * If this advertises a larger window than any other segment,
+	 * then remember the size of the advertised window.
+	 * Any pending ACK has now been sent.
+	 */
+	if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+		tp->rcv_adv = tp->rcv_nxt + win;
+	tp->last_ack_sent = tp->rcv_nxt;
+	tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+	if (sendalot)
+		goto again;
+
+	return (0);
+}
+
+void
+tcp_setpersist(tp)
+	register struct tcpcb *tp;
+{
+    int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+/*	if (tp->t_timer[TCPT_REXMT])
+ *		panic("tcp_output REXMT");
+ */
+	/*
+	 * Start/restart persistence timer.
+	 */
+	TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+	    t * tcp_backoff[tp->t_rxtshift],
+	    TCPTV_PERSMIN, TCPTV_PERSMAX);
+	if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+		tp->t_rxtshift++;
+}
diff --git a/slirp2/tcp_subr.c b/slirp2/tcp_subr.c
new file mode 100644
index 0000000..e453877
--- /dev/null
+++ b/slirp2/tcp_subr.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp_subr.c	8.1 (Berkeley) 6/10/93
+ * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#define WANT_SYS_IOCTL_H
+#include <slirp.h>
+#include "proxy_common.h"
+
+/* patchable/settable parameters for tcp */
+int 	tcp_mssdflt = TCP_MSS;
+int 	tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
+int	tcp_do_rfc1323 = 0;	/* Don't do rfc1323 performance enhancements */
+int	tcp_rcvspace;	/* You may want to change this */
+int	tcp_sndspace;	/* Keep small if you have an error prone link */
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init()
+{
+	tcp_iss = 1;		/* wrong */
+	tcb.so_next = tcb.so_prev = &tcb;
+
+	/* tcp_rcvspace = our Window we advertise to the remote */
+	tcp_rcvspace = TCP_RCVSPACE;
+	tcp_sndspace = TCP_SNDSPACE;
+
+	/* Make sure tcp_sndspace is at least 2*MSS */
+	if (tcp_sndspace < 2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr)))
+		tcp_sndspace = 2*(min(if_mtu, if_mru) - sizeof(struct tcpiphdr));
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+/* struct tcpiphdr * */
+void
+tcp_template(tp)
+	struct tcpcb *tp;
+{
+	struct socket *so = tp->t_socket;
+	register struct tcpiphdr *n = &tp->t_template;
+
+	n->ti_mbuf = NULL;
+	n->ti_x1 = 0;
+	n->ti_pr = IPPROTO_TCP;
+	n->ti_len = htons(sizeof (struct tcpiphdr) - sizeof (struct ip));
+	n->ti_src = ip_seth(so->so_faddr_ip);
+	n->ti_dst = ip_seth(so->so_laddr_ip);
+	n->ti_sport = port_seth(so->so_faddr_port);
+	n->ti_dport = port_seth(so->so_laddr_port);
+
+	n->ti_seq = 0;
+	n->ti_ack = 0;
+	n->ti_x2 = 0;
+	n->ti_off = 5;
+	n->ti_flags = 0;
+	n->ti_win = 0;
+	n->ti_sum = 0;
+	n->ti_urp = 0;
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header.  If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template.  If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(tp, ti, m, ack, seq, flags)
+	struct tcpcb *tp;
+	register struct tcpiphdr *ti;
+	register MBuf m;
+	tcp_seq ack, seq;
+	int flags;
+{
+	register int tlen;
+	int win = 0;
+
+	DEBUG_CALL("tcp_respond");
+	DEBUG_ARG("tp = %lx", (long)tp);
+	DEBUG_ARG("ti = %lx", (long)ti);
+	DEBUG_ARG("m = %lx", (long)m);
+	DEBUG_ARG("ack = %u", ack);
+	DEBUG_ARG("seq = %u", seq);
+	DEBUG_ARG("flags = %x", flags);
+
+	if (tp)
+		win = sbuf_space(&tp->t_socket->so_rcv);
+	if (m == 0) {
+		if ((m = mbuf_alloc()) == NULL)
+			return;
+#ifdef TCP_COMPAT_42
+		tlen = 1;
+#else
+		tlen = 0;
+#endif
+		m->m_data += if_maxlinkhdr;
+		*MBUF_TO(m, struct tcpiphdr *) = *ti;
+		ti = MBUF_TO(m, struct tcpiphdr *);
+		flags = TH_ACK;
+	} else {
+		/*
+		 * ti points into m so the next line is just making
+		 * the mbuf point to ti
+		 */
+		m->m_data = (caddr_t)ti;
+
+		m->m_len = sizeof (struct tcpiphdr);
+		tlen = 0;
+#define xchg(a,b,type) { type t; t=a; a=b; b=t; }
+		xchg(ti->ti_dst, ti->ti_src, ipaddr_t);
+		xchg(ti->ti_dport, ti->ti_sport, port_t);
+#undef xchg
+	}
+	ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen));
+	tlen += sizeof (struct tcpiphdr);
+	m->m_len = tlen;
+
+	ti->ti_mbuf = 0;
+	ti->ti_x1 = 0;
+	ti->ti_seq = htonl(seq);
+	ti->ti_ack = htonl(ack);
+	ti->ti_x2 = 0;
+	ti->ti_off = sizeof (struct tcphdr) >> 2;
+	ti->ti_flags = flags;
+	if (tp)
+		ti->ti_win = htons((u_int16_t) (win >> tp->rcv_scale));
+	else
+		ti->ti_win = htons((u_int16_t)win);
+	ti->ti_urp = 0;
+	ti->ti_sum = 0;
+	ti->ti_sum = cksum(m, tlen);
+	((struct ip *)ti)->ip_len = tlen;
+
+	if(flags & TH_RST)
+	  ((struct ip *)ti)->ip_ttl = MAXTTL;
+	else
+	  ((struct ip *)ti)->ip_ttl = ip_defttl;
+
+	(void) ip_output((struct socket *)0, m);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(so)
+	struct socket *so;
+{
+	register struct tcpcb *tp;
+
+	tp = (struct tcpcb *)malloc(sizeof(*tp));
+	if (tp == NULL)
+		return ((struct tcpcb *)0);
+
+	memset((char *) tp, 0, sizeof(struct tcpcb));
+	tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
+	tp->t_maxseg = tcp_mssdflt;
+
+	tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+	tp->t_socket = so;
+
+	/*
+	 * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+	 * rtt estimate.  Set rttvar so that srtt + 2 * rttvar gives
+	 * reasonable initial retransmit time.
+	 */
+	tp->t_srtt = TCPTV_SRTTBASE;
+	tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
+	tp->t_rttmin = TCPTV_MIN;
+
+	TCPT_RANGESET(tp->t_rxtcur,
+	    ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+	    TCPTV_MIN, TCPTV_REXMTMAX);
+
+	tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+	tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+	tp->t_state = TCPS_CLOSED;
+
+	so->so_tcpcb = tp;
+
+	return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error.  If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *tcp_drop(struct tcpcb *tp, int err)
+{
+/* tcp_drop(tp, errno)
+	register struct tcpcb *tp;
+	int errno;
+{
+*/
+
+	DEBUG_CALL("tcp_drop");
+	DEBUG_ARG("tp = %lx", (long)tp);
+	DEBUG_ARG("errno = %d", errno);
+
+	if (TCPS_HAVERCVDSYN(tp->t_state)) {
+		tp->t_state = TCPS_CLOSED;
+		(void) tcp_output(tp);
+		tcpstat.tcps_drops++;
+	} else
+		tcpstat.tcps_conndrops++;
+/*	if (errno == ETIMEDOUT && tp->t_softerror)
+ *		errno = tp->t_softerror;
+ */
+/*	so->so_error = errno; */
+	return (tcp_close(tp));
+}
+
+/*
+ * Close a TCP control block:
+ *	discard all space held by the tcp
+ *	discard internet protocol block
+ *	wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(tp)
+	register struct tcpcb *tp;
+{
+	register struct tcpiphdr *t;
+	struct socket *so = tp->t_socket;
+	register MBuf m;
+
+	DEBUG_CALL("tcp_close");
+	DEBUG_ARG("tp = %lx", (long )tp);
+
+	/* free the reassembly queue, if any */
+	t = tcpfrag_list_first(tp);
+	while (!tcpfrag_list_end(t, tp)) {
+	    t = tcpiphdr_next(t);
+	    m = tcpiphdr_prev(t)->ti_mbuf;
+	    remque(tcpiphdr2qlink(tcpiphdr_prev(t)));
+            mbuf_free(m);
+	}
+	/* It's static */
+/*	if (tp->t_template)
+ *		(void) mbuf_free(MBUF_FROM(tp->t_template));
+ */
+/*	free(tp, M_PCB);  */
+	free(tp);
+	so->so_tcpcb = 0;
+	soisfdisconnected(so);
+	/* clobber input socket cache if we're closing the cached connection */
+	if (so == tcp_last_so)
+		tcp_last_so = &tcb;
+	socket_close(so->s);
+	sbuf_free(&so->so_rcv);
+	sbuf_free(&so->so_snd);
+	sofree(so);
+	tcpstat.tcps_closed++;
+	return ((struct tcpcb *)0);
+}
+
+void
+tcp_drain()
+{
+	/* XXX */
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment.  We will gradually open it again as we proceed.
+ */
+
+#ifdef notdef
+
+void
+tcp_quench(i, errno)
+
+	int errno;
+{
+	struct tcpcb *tp = intotcpcb(inp);
+
+	if (tp)
+		tp->snd_cwnd = tp->t_maxseg;
+}
+
+#endif /* notdef */
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it.  If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state.  In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+void
+tcp_sockclosed(tp)
+	struct tcpcb *tp;
+{
+
+	DEBUG_CALL("tcp_sockclosed");
+	DEBUG_ARG("tp = %lx", (long)tp);
+
+	switch (tp->t_state) {
+
+	case TCPS_CLOSED:
+	case TCPS_LISTEN:
+	case TCPS_SYN_SENT:
+		tp->t_state = TCPS_CLOSED;
+		tp = tcp_close(tp);
+		break;
+
+	case TCPS_SYN_RECEIVED:
+	case TCPS_ESTABLISHED:
+		tp->t_state = TCPS_FIN_WAIT_1;
+		break;
+
+	case TCPS_CLOSE_WAIT:
+		tp->t_state = TCPS_LAST_ACK;
+		break;
+	}
+/*	soisfdisconnecting(tp->t_socket); */
+	if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
+		soisfdisconnected(tp->t_socket);
+	if (tp)
+		tcp_output(tp);
+}
+
+static void
+tcp_proxy_event( struct socket*  so,
+                 int             s,
+                 ProxyEvent      event )
+{
+    so->so_state &= ~SS_PROXIFIED;
+
+    if (event == PROXY_EVENT_CONNECTED) {
+        so->s         = s;
+        so->so_state &= ~(SS_ISFCONNECTING);
+    }
+    else {
+        so->so_state = SS_NOFDREF;
+    }
+
+    /* continue the connect */
+    tcp_input(NULL, sizeof(struct ip), so);
+}
+
+/*
+ * Connect to a host on the Internet
+ * Called by tcp_input
+ * Only do a connect, the tcp fields will be set in tcp_input
+ * return 0 if there's a result of the connect,
+ * else return -1 means we're still connecting
+ * The return value is almost always -1 since the socket is
+ * nonblocking.  Connect returns after the SYN is sent, and does
+ * not wait for ACK+SYN.
+ */
+int tcp_fconnect(so)
+     struct socket *so;
+{
+    int ret=0;
+    int try_proxy = 1;
+    SockAddress    sockaddr;
+    uint32_t       sock_ip;
+    uint16_t       sock_port;
+
+    DEBUG_CALL("tcp_fconnect");
+    DEBUG_ARG("so = %lx", (long )so);
+
+    sock_ip   = so->so_faddr_ip;
+    sock_port = so->so_faddr_port;
+
+    if ((sock_ip & 0xffffff00) == special_addr_ip) {
+      /* It's an alias */
+      int  last_byte = sock_ip & 0xff;
+
+      if (CTL_IS_DNS(last_byte))
+        sock_ip = dns_addr[last_byte - CTL_DNS];
+      else
+        sock_ip = loopback_addr_ip;
+      try_proxy = 0;
+    }
+
+    sock_address_init_inet( &sockaddr, sock_ip, sock_port );
+
+    DEBUG_MISC((dfd, " connect()ing, addr=%s, proxy=%d\n",
+                sock_address_to_string(&sockaddr), try_proxy));
+
+    if (try_proxy) {
+        if (!proxy_manager_add(&sockaddr, SOCKET_STREAM, (ProxyEventFunc) tcp_proxy_event, so)) {
+            soisfconnecting(so);
+            so->s         = -1;
+            so->so_state |= SS_PROXIFIED;
+            return 0;
+        }
+    }
+
+    if ((ret=so->s=socket_create_inet(SOCKET_STREAM)) >= 0) 
+    {
+        int  s = so->s;
+
+        socket_set_nonblock(s);
+        socket_set_xreuseaddr(s);
+        socket_set_oobinline(s);
+
+        /* We don't care what port we get */
+        socket_connect(s, &sockaddr);
+
+        /*
+        * If it's not in progress, it failed, so we just return 0,
+        * without clearing SS_NOFDREF
+        */
+        soisfconnecting(so);
+  }
+
+  return(ret);
+}
+
+/*
+ * Accept the socket and connect to the local-host
+ *
+ * We have a problem. The correct thing to do would be
+ * to first connect to the local-host, and only if the
+ * connection is accepted, then do an accept() here.
+ * But, a) we need to know who's trying to connect
+ * to the socket to be able to SYN the local-host, and
+ * b) we are already connected to the foreign host by
+ * the time it gets to accept(), so... We simply accept
+ * here and SYN the local-host.
+ */
+void
+tcp_connect(inso)
+	struct socket *inso;
+{
+	struct socket *so;
+	SockAddress   addr;
+	uint32_t      addr_ip;
+	struct tcpcb *tp;
+	int s;
+
+	DEBUG_CALL("tcp_connect");
+	DEBUG_ARG("inso = %lx", (long)inso);
+
+	/*
+	 * If it's an SS_ACCEPTONCE socket, no need to socreate()
+	 * another socket, just use the accept() socket.
+	 */
+	if (inso->so_state & SS_FACCEPTONCE) {
+		/* FACCEPTONCE already have a tcpcb */
+		so = inso;
+	} else {
+		if ((so = socreate()) == NULL) {
+			/* If it failed, get rid of the pending connection */
+			socket_close(socket_accept(inso->s, NULL));
+			return;
+		}
+		if (tcp_attach(so) < 0) {
+			free(so); /* NOT sofree */
+			return;
+		}
+		so->so_laddr_ip   = inso->so_laddr_ip;
+		so->so_laddr_port = inso->so_laddr_port;
+	}
+
+	(void) tcp_mss(sototcpcb(so), 0);
+
+	if ((s = socket_accept(inso->s, &addr)) < 0) {
+		tcp_close(sototcpcb(so)); /* This will sofree() as well */
+		return;
+	}
+	socket_set_nonblock(s);
+	socket_set_xreuseaddr(s);
+	socket_set_oobinline(s);
+	socket_set_nodelay(s);
+
+	so->so_faddr_port = sock_address_get_port(&addr);
+	
+	addr_ip = sock_address_get_ip(&addr);
+
+	so->so_faddr_ip = addr_ip;
+	/* Translate connections from localhost to the real hostname */
+	if (addr_ip == 0 || addr_ip == loopback_addr_ip)
+	   so->so_faddr_ip = alias_addr_ip;
+
+	/* Close the accept() socket, set right state */
+	if (inso->so_state & SS_FACCEPTONCE) {
+		socket_close(so->s); /* If we only accept once, close the accept() socket */
+		so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
+					   /* if it's not FACCEPTONCE, it's already NOFDREF */
+	}
+	so->s = s;
+
+	so->so_iptos = tcp_tos(so);
+	tp = sototcpcb(so);
+
+	tcp_template(tp);
+
+	/* Compute window scaling to request.  */
+/*	while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
+ *		(TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
+ *		tp->request_r_scale++;
+ */
+
+/*	soisconnecting(so); */ /* NOFDREF used instead */
+	tcpstat.tcps_connattempt++;
+
+	tp->t_state = TCPS_SYN_SENT;
+	tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+	tp->iss = tcp_iss;
+	tcp_iss += TCP_ISSINCR/2;
+	tcp_sendseqinit(tp);
+	tcp_output(tp);
+}
+
+/*
+ * Attach a TCPCB to a socket.
+ */
+int
+tcp_attach(so)
+	struct socket *so;
+{
+	if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL)
+	   return -1;
+
+	insque(so, &tcb);
+
+	return 0;
+}
+
+/*
+ * Set the socket's type of service field
+ */
+struct tos_t tcptos[] = {
+	  {0, 20, IPTOS_THROUGHPUT, 0},	/* ftp data */
+	  {21, 21, IPTOS_LOWDELAY,  EMU_FTP},	/* ftp control */
+	  {0, 23, IPTOS_LOWDELAY, 0},	/* telnet */
+	  {0, 80, IPTOS_THROUGHPUT, 0},	/* WWW */
+	  {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT},	/* rlogin */
+	  {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT},	/* shell */
+	  {0, 544, IPTOS_LOWDELAY, EMU_KSH},		/* kshell */
+	  {0, 543, IPTOS_LOWDELAY, 0},	/* klogin */
+	  {0, 6667, IPTOS_THROUGHPUT, EMU_IRC},	/* IRC */
+	  {0, 6668, IPTOS_THROUGHPUT, EMU_IRC},	/* IRC undernet */
+	  {0, 7070, IPTOS_LOWDELAY, EMU_REALAUDIO }, /* RealAudio control */
+	  {0, 113, IPTOS_LOWDELAY, EMU_IDENT }, /* identd protocol */
+	  {0, 0, 0, 0}
+};
+
+
+/*
+ * Return TOS according to the above table
+ */
+u_int8_t
+tcp_tos(so)
+	struct socket *so;
+{
+	int i = 0;
+
+	while(tcptos[i].tos) {
+		if ((tcptos[i].fport && so->so_faddr_port == tcptos[i].fport) ||
+		    (tcptos[i].lport && so->so_laddr_port == tcptos[i].lport)) {
+			so->so_emu = tcptos[i].emu;
+			return tcptos[i].tos;
+		}
+		i++;
+	}
+
+	return 0;
+}
+
+int do_echo = -1;
+
+/*
+ * Emulate programs that try and connect to us
+ * This includes ftp (the data connection is
+ * initiated by the server) and IRC (DCC CHAT and
+ * DCC SEND) for now
+ *
+ * NOTE: It's possible to crash SLiRP by sending it
+ * unstandard strings to emulate... if this is a problem,
+ * more checks are needed here
+ *
+ * XXX Assumes the whole command came in one packet
+ *
+ * XXX Some ftp clients will have their TOS set to
+ * LOWDELAY and so Nagel will kick in.  Because of this,
+ * we'll get the first letter, followed by the rest, so
+ * we simply scan for ORT instead of PORT...
+ * DCC doesn't have this problem because there's other stuff
+ * in the packet before the DCC command.
+ *
+ * Return 1 if the mbuf m is still valid and should be
+ * sbuf_append()ed
+ *
+ * NOTE: if you return 0 you MUST mbuf_free() the mbuf!
+ */
+int
+tcp_emu(so, m)
+	struct socket *so;
+	MBuf m;
+{
+	u_int n1, n2, n3, n4, n5, n6;
+	char buff[256];
+	u_int32_t laddr;
+	u_int lport;
+	char *bptr;
+
+	DEBUG_CALL("tcp_emu");
+	DEBUG_ARG("so = %lx", (long)so);
+	DEBUG_ARG("m = %lx", (long)m);
+
+	switch(so->so_emu) {
+		int x, i;
+
+	 case EMU_IDENT:
+		/*
+		 * Identification protocol as per rfc-1413
+		 */
+
+		{
+			struct socket *tmpso;
+			SockAddress    addr;
+			SBuf  so_rcv = &so->so_rcv;
+
+			memcpy(so_rcv->sb_wptr, m->m_data, m->m_len);
+			so_rcv->sb_wptr += m->m_len;
+			so_rcv->sb_rptr += m->m_len;
+			m->m_data[m->m_len] = 0; /* NULL terminate */
+			if (strchr(m->m_data, '\r') || strchr(m->m_data, '\n')) {
+				if (sscanf(so_rcv->sb_data, "%d%*[ ,]%d", &n1, &n2) == 2) {
+					/* n2 is the one on our host */
+					for (tmpso = tcb.so_next; tmpso != &tcb; tmpso = tmpso->so_next) {
+						if (tmpso->so_laddr_ip == so->so_laddr_ip &&
+						    tmpso->so_laddr_port == n2 &&
+						    tmpso->so_faddr_ip == so->so_faddr_ip &&
+						    tmpso->so_faddr_port == n1) {
+							if (socket_get_address(tmpso->s, &addr) == 0)
+							   n2 = sock_address_get_port(&addr);
+							break;
+						}
+					}
+				}
+				so_rcv->sb_cc = sprintf(so_rcv->sb_data, "%d,%d\r\n", n1, n2);
+				so_rcv->sb_rptr = so_rcv->sb_data;
+				so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc;
+			}
+			mbuf_free(m);
+			return 0;
+		}
+
+        case EMU_FTP: /* ftp */
+		*(m->m_data+m->m_len) = 0; /* NULL terminate for strstr */
+		if ((bptr = (char *)strstr(m->m_data, "ORT")) != NULL) {
+			/*
+			 * Need to emulate the PORT command
+			 */
+			x = sscanf(bptr, "ORT %d,%d,%d,%d,%d,%d\r\n%256[^\177]",
+				   &n1, &n2, &n3, &n4, &n5, &n6, buff);
+			if (x < 6)
+			   return 1;
+
+			laddr = (n1 << 24) | (n2 << 16) | (n3 << 8) | (n4);
+			lport = (n5 << 8) | (n6);
+
+			if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+			   return 1;
+
+			n6 = so->so_faddr_port;
+
+			n5 = (n6 >> 8) & 0xff;
+			n6 &= 0xff;
+
+			laddr = so->so_faddr_ip;
+
+			n1 = ((laddr >> 24) & 0xff);
+			n2 = ((laddr >> 16) & 0xff);
+			n3 = ((laddr >> 8)  & 0xff);
+			n4 =  (laddr & 0xff);
+
+			m->m_len = bptr - m->m_data; /* Adjust length */
+			m->m_len += sprintf(bptr,"ORT %d,%d,%d,%d,%d,%d\r\n%s",
+					    n1, n2, n3, n4, n5, n6, x==7?buff:"");
+			return 1;
+		} else if ((bptr = (char *)strstr(m->m_data, "27 Entering")) != NULL) {
+			/*
+			 * Need to emulate the PASV response
+			 */
+			x = sscanf(bptr, "27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%256[^\177]",
+				   &n1, &n2, &n3, &n4, &n5, &n6, buff);
+			if (x < 6)
+			   return 1;
+
+			laddr = (n1 << 24) | (n2 << 16) | (n3 << 8) | (n4);
+			lport = (n5 << 8) | (n6);
+
+			if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+			   return 1;
+
+			n6 = so->so_faddr_port;
+
+			n5 = (n6 >> 8) & 0xff;
+			n6 &= 0xff;
+
+			laddr = so->so_faddr_ip;
+
+			n1 = ((laddr >> 24) & 0xff);
+			n2 = ((laddr >> 16) & 0xff);
+			n3 = ((laddr >> 8)  & 0xff);
+			n4 =  (laddr & 0xff);
+
+			m->m_len = bptr - m->m_data; /* Adjust length */
+			m->m_len += sprintf(bptr,"27 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n%s",
+					    n1, n2, n3, n4, n5, n6, x==7?buff:"");
+
+			return 1;
+		}
+
+		return 1;
+
+	 case EMU_KSH:
+		/*
+		 * The kshell (Kerberos rsh) and shell services both pass
+		 * a local port port number to carry signals to the server
+		 * and stderr to the client.  It is passed at the beginning
+		 * of the connection as a NUL-terminated decimal ASCII string.
+		 */
+		so->so_emu = 0;
+		for (lport = 0, i = 0; i < m->m_len-1; ++i) {
+			if (m->m_data[i] < '0' || m->m_data[i] > '9')
+				return 1;       /* invalid number */
+			lport *= 10;
+			lport += m->m_data[i] - '0';
+		}
+		if (m->m_data[m->m_len-1] == '\0' && lport != 0 &&
+		    (so = solisten(0, so->so_laddr_ip, lport, SS_FACCEPTONCE)) != NULL)
+			m->m_len = sprintf(m->m_data, "%d", so->so_faddr_port)+1;
+		return 1;
+
+	 case EMU_IRC:
+		/*
+		 * Need to emulate DCC CHAT, DCC SEND and DCC MOVE
+		 */
+		*(m->m_data+m->m_len) = 0; /* NULL terminate the string for strstr */
+		if ((bptr = (char *)strstr(m->m_data, "DCC")) == NULL)
+			 return 1;
+
+		/* The %256s is for the broken mIRC */
+		if (sscanf(bptr, "DCC CHAT %256s %u %u", buff, &laddr, &lport) == 3) {
+			if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+				return 1;
+
+			m->m_len = bptr - m->m_data; /* Adjust length */
+			m->m_len += sprintf(bptr, "DCC CHAT chat %lu %u%c\n",
+			     (unsigned long) so->so_faddr_ip,
+			     so->so_faddr_port, 1);
+		} else if (sscanf(bptr, "DCC SEND %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+			if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+				return 1;
+
+			m->m_len = bptr - m->m_data; /* Adjust length */
+			m->m_len += sprintf(bptr, "DCC SEND %s %lu %u %u%c\n",
+			      buff, (unsigned long)so->so_faddr_ip,
+			      so->so_faddr_port, n1, 1);
+		} else if (sscanf(bptr, "DCC MOVE %256s %u %u %u", buff, &laddr, &lport, &n1) == 4) {
+			if ((so = solisten(0, laddr, lport, SS_FACCEPTONCE)) == NULL)
+				return 1;
+
+			m->m_len = bptr - m->m_data; /* Adjust length */
+			m->m_len += sprintf(bptr, "DCC MOVE %s %lu %u %u%c\n",
+			      buff, (unsigned long)so->so_faddr_ip,
+			      so->so_faddr_port, n1, 1);
+		}
+		return 1;
+
+	 case EMU_REALAUDIO:
+                /*
+		 * RealAudio emulation - JP. We must try to parse the incoming
+		 * data and try to find the two characters that contain the
+		 * port number. Then we redirect an udp port and replace the
+		 * number with the real port we got.
+		 *
+		 * The 1.0 beta versions of the player are not supported
+		 * any more.
+		 *
+		 * A typical packet for player version 1.0 (release version):
+		 *
+		 * 0000:50 4E 41 00 05
+		 * 0000:00 01 00 02 1B D7 00 00 67 E6 6C DC 63 00 12 50 .....�..g�l�c..P
+		 * 0010:4E 43 4C 49 45 4E 54 20 31 30 31 20 41 4C 50 48 NCLIENT 101 ALPH
+		 * 0020:41 6C 00 00 52 00 17 72 61 66 69 6C 65 73 2F 76 Al..R..rafiles/v
+		 * 0030:6F 61 2F 65 6E 67 6C 69 73 68 5F 2E 72 61 79 42 oa/english_.rayB
+		 *
+		 * Now the port number 0x1BD7 is found at offset 0x04 of the
+		 * Now the port number 0x1BD7 is found at offset 0x04 of the
+		 * second packet. This time we received five bytes first and
+		 * then the rest. You never know how many bytes you get.
+		 *
+		 * A typical packet for player version 2.0 (beta):
+		 *
+		 * 0000:50 4E 41 00 06 00 02 00 00 00 01 00 02 1B C1 00 PNA...........�.
+		 * 0010:00 67 75 78 F5 63 00 0A 57 69 6E 32 2E 30 2E 30 .gux�c..Win2.0.0
+		 * 0020:2E 35 6C 00 00 52 00 1C 72 61 66 69 6C 65 73 2F .5l..R..rafiles/
+		 * 0030:77 65 62 73 69 74 65 2F 32 30 72 65 6C 65 61 73 website/20releas
+		 * 0040:65 2E 72 61 79 53 00 00 06 36 42                e.rayS...6B
+		 *
+		 * Port number 0x1BC1 is found at offset 0x0d.
+		 *
+		 * This is just a horrible switch statement. Variable ra tells
+		 * us where we're going.
+		 */
+
+		bptr = m->m_data;
+		while (bptr < m->m_data + m->m_len) {
+			u_short p;
+			static int ra = 0;
+			char ra_tbl[4];
+
+			ra_tbl[0] = 0x50;
+			ra_tbl[1] = 0x4e;
+			ra_tbl[2] = 0x41;
+			ra_tbl[3] = 0;
+
+			switch (ra) {
+			 case 0:
+			 case 2:
+			 case 3:
+				if (*bptr++ != ra_tbl[ra]) {
+					ra = 0;
+					continue;
+				}
+				break;
+
+			 case 1:
+				/*
+				 * We may get 0x50 several times, ignore them
+				 */
+				if (*bptr == 0x50) {
+					ra = 1;
+					bptr++;
+					continue;
+				} else if (*bptr++ != ra_tbl[ra]) {
+					ra = 0;
+					continue;
+				}
+				break;
+
+			 case 4:
+				/*
+				 * skip version number
+				 */
+				bptr++;
+				break;
+
+			 case 5:
+				/*
+				 * The difference between versions 1.0 and
+				 * 2.0 is here. For future versions of
+				 * the player this may need to be modified.
+				 */
+				if (*(bptr + 1) == 0x02)
+				   bptr += 8;
+				else
+				   bptr += 4;
+				break;
+
+			 case 6:
+				/* This is the field containing the port
+				 * number that RA-player is listening to.
+				 */
+				lport = (((u_char*)bptr)[0] << 8)
+				+ ((u_char *)bptr)[1];
+				if (lport < 6970)
+				   lport += 256;   /* don't know why */
+				if (lport < 6970 || lport > 7170)
+				   return 1;       /* failed */
+
+				/* try to get udp port between 6970 - 7170 */
+				for (p = 6970; p < 7071; p++) {
+					if (udp_listen( p,
+						        so->so_laddr_ip,
+						        lport,
+						        SS_FACCEPTONCE)) {
+						break;
+					}
+				}
+				if (p == 7071)
+				   p = 0;
+				*(u_char *)bptr++ = (p >> 8) & 0xff;
+				*(u_char *)bptr++ = p & 0xff;
+				ra = 0;
+				return 1;   /* port redirected, we're done */
+				break;
+
+			 default:
+				ra = 0;
+			}
+			ra++;
+		}
+		return 1;
+
+	 default:
+		/* Ooops, not emulated, won't call tcp_emu again */
+		so->so_emu = 0;
+		return 1;
+	}
+}
+
+/*
+ * Do misc. config of SLiRP while its running.
+ * Return 0 if this connections is to be closed, 1 otherwise,
+ * return 2 if this is a command-line connection
+ */
+int
+tcp_ctl(so)
+	struct socket *so;
+{
+	SBuf  sb = &so->so_snd;
+	int command;
+#if 0	
+ 	struct ex_list *ex_ptr;
+	int do_pty;
+#endif	
+        //	struct socket *tmpso;
+
+	DEBUG_CALL("tcp_ctl");
+	DEBUG_ARG("so = %lx", (long )so);
+
+#if 0
+	/*
+	 * Check if they're authorised
+	 */
+	if (ctl_addr_ip && (ctl_addr_ip == -1 || (so->so_laddr_ip != ctl_addr_ip))) {
+		sb->sb_cc = sprintf(sb->sb_wptr,"Error: Permission denied.\r\n");
+		sb->sb_wptr += sb->sb_cc;
+		return 0;
+	}
+#endif
+	command = (so->so_faddr_ip & 0xff);
+
+	switch(command) {
+	default:
+		/*
+		 * Nothing bound..
+		 */
+		/* tcp_fconnect(so); */
+
+	case CTL_ALIAS:
+	  sb->sb_cc = sprintf(sb->sb_wptr,
+			      "Error: No application configured.\r\n");
+	  sb->sb_wptr += sb->sb_cc;
+	  return(0);
+	}
+}
diff --git a/slirp2/tcp_timer.c b/slirp2/tcp_timer.c
new file mode 100644
index 0000000..ad03098
--- /dev/null
+++ b/slirp2/tcp_timer.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp_timer.c	8.1 (Berkeley) 6/10/93
+ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
+ */
+
+#include <slirp.h>
+
+int	tcp_keepidle = TCPTV_KEEP_IDLE;
+int	tcp_keepintvl = TCPTV_KEEPINTVL;
+int	tcp_maxidle;
+int	so_options = DO_KEEPALIVE;
+
+struct   tcpstat tcpstat;        /* tcp statistics */
+u_int32_t        tcp_now;                /* for RFC 1323 timestamps */
+
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo()
+{
+	register struct socket *so;
+	register struct tcpcb *tp;
+
+	DEBUG_CALL("tcp_fasttimo");
+	
+	so = tcb.so_next;
+	if (so)
+	for (; so != &tcb; so = so->so_next)
+		if ((tp = (struct tcpcb *)so->so_tcpcb) &&
+		    (tp->t_flags & TF_DELACK)) {
+			tp->t_flags &= ~TF_DELACK;
+			tp->t_flags |= TF_ACKNOW;
+			tcpstat.tcps_delack++;
+			(void) tcp_output(tp);
+		}
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo()
+{
+	register struct socket *ip, *ipnxt;
+	register struct tcpcb *tp;
+	register int i;
+
+	DEBUG_CALL("tcp_slowtimo");
+	
+	tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl;
+	/*
+	 * Search through tcb's and update active timers.
+	 */
+	ip = tcb.so_next;
+	if (ip == 0)
+	   return;
+	for (; ip != &tcb; ip = ipnxt) {
+		ipnxt = ip->so_next;
+		tp = sototcpcb(ip);
+		if (tp == 0)
+			continue;
+		for (i = 0; i < TCPT_NTIMERS; i++) {
+			if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
+				tcp_timers(tp,i);
+				if (ipnxt->so_prev != ip)
+					goto tpgone;
+			}
+		}
+		tp->t_idle++;
+		if (tp->t_rtt)
+		   tp->t_rtt++;
+tpgone:
+		;
+	}
+	tcp_iss += TCP_ISSINCR/PR_SLOWHZ;		/* increment iss */
+#ifdef TCP_COMPAT_42
+	if ((int)tcp_iss < 0)
+		tcp_iss = 0;				/* XXX */
+#endif
+	tcp_now++;					/* for timestamps */
+}
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(tp)
+	struct tcpcb *tp;
+{
+	register int i;
+
+	for (i = 0; i < TCPT_NTIMERS; i++)
+		tp->t_timer[i] = 0;
+}
+
+int	tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+   { 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
+
+/*
+ * TCP timer processing.
+ */
+struct tcpcb *
+tcp_timers(tp, timer)
+	register struct tcpcb *tp;
+	int timer;
+{
+	register int rexmt;
+	
+	DEBUG_CALL("tcp_timers");
+	
+	switch (timer) {
+
+	/*
+	 * 2 MSL timeout in shutdown went off.  If we're closed but
+	 * still waiting for peer to close and connection has been idle
+	 * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+	 * control block.  Otherwise, check again in a bit.
+	 */
+	case TCPT_2MSL:
+		if (tp->t_state != TCPS_TIME_WAIT &&
+		    tp->t_idle <= tcp_maxidle)
+			tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
+		else
+			tp = tcp_close(tp);
+		break;
+
+	/*
+	 * Retransmission timer went off.  Message has not
+	 * been acked within retransmit interval.  Back off
+	 * to a longer retransmit interval and retransmit one segment.
+	 */
+	case TCPT_REXMT:
+		
+		/*
+		 * XXXXX If a packet has timed out, then remove all the queued
+		 * packets for that session.
+		 */
+		
+		if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
+			/*
+			 * This is a hack to suit our terminal server here at the uni of canberra
+			 * since they have trouble with zeroes... It usually lets them through
+			 * unharmed, but under some conditions, it'll eat the zeros.  If we
+			 * keep retransmitting it, it'll keep eating the zeroes, so we keep
+			 * retransmitting, and eventually the connection dies...
+			 * (this only happens on incoming data)
+			 * 
+			 * So, if we were gonna drop the connection from too many retransmits,
+			 * don't... instead halve the t_maxseg, which might break up the NULLs and
+			 * let them through
+			 * 
+			 * *sigh*
+			 */
+			
+			tp->t_maxseg >>= 1;
+			if (tp->t_maxseg < 32) {
+				/*
+				 * We tried our best, now the connection must die!
+				 */
+				tp->t_rxtshift = TCP_MAXRXTSHIFT;
+				tcpstat.tcps_timeoutdrop++;
+				tp = tcp_drop(tp, tp->t_softerror);
+				/* tp->t_softerror : ETIMEDOUT); */ /* XXX */
+				return (tp); /* XXX */
+			}
+			
+			/*
+			 * Set rxtshift to 6, which is still at the maximum
+			 * backoff time
+			 */
+			tp->t_rxtshift = 6;
+		}
+		tcpstat.tcps_rexmttimeo++;
+		rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+		TCPT_RANGESET(tp->t_rxtcur, rexmt,
+		    (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+		tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+		/*
+		 * If losing, let the lower level know and try for
+		 * a better route.  Also, if we backed off this far,
+		 * our srtt estimate is probably bogus.  Clobber it
+		 * so we'll take the next rtt measurement as our srtt;
+		 * move the current srtt into rttvar to keep the current
+		 * retransmit times until then.
+		 */
+		if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
+/*			in_losing(tp->t_inpcb); */
+			tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+			tp->t_srtt = 0;
+		}
+		tp->snd_nxt = tp->snd_una;
+		/*
+		 * If timing a segment in this window, stop the timer.
+		 */
+		tp->t_rtt = 0;
+		/*
+		 * Close the congestion window down to one segment
+		 * (we'll open it by one segment for each ack we get).
+		 * Since we probably have a window's worth of unacked
+		 * data accumulated, this "slow start" keeps us from
+		 * dumping all that data as back-to-back packets (which
+		 * might overwhelm an intermediate gateway).
+		 *
+		 * There are two phases to the opening: Initially we
+		 * open by one mss on each ack.  This makes the window
+		 * size increase exponentially with time.  If the
+		 * window is larger than the path can handle, this
+		 * exponential growth results in dropped packet(s)
+		 * almost immediately.  To get more time between 
+		 * drops but still "push" the network to take advantage
+		 * of improving conditions, we switch from exponential
+		 * to linear window opening at some threshold size.
+		 * For a threshold, we use half the current window
+		 * size, truncated to a multiple of the mss.
+		 *
+		 * (the minimum cwnd that will give us exponential
+		 * growth is 2 mss.  We don't allow the threshold
+		 * to go below this.)
+		 */
+		{
+		u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+		if (win < 2)
+			win = 2;
+		tp->snd_cwnd = tp->t_maxseg;
+		tp->snd_ssthresh = win * tp->t_maxseg;
+		tp->t_dupacks = 0;
+		}
+		(void) tcp_output(tp);
+		break;
+
+	/*
+	 * Persistence timer into zero window.
+	 * Force a byte to be output, if possible.
+	 */
+	case TCPT_PERSIST:
+		tcpstat.tcps_persisttimeo++;
+		tcp_setpersist(tp);
+		tp->t_force = 1;
+		(void) tcp_output(tp);
+		tp->t_force = 0;
+		break;
+
+	/*
+	 * Keep-alive timer went off; send something
+	 * or drop connection if idle for too long.
+	 */
+	case TCPT_KEEP:
+		tcpstat.tcps_keeptimeo++;
+		if (tp->t_state < TCPS_ESTABLISHED)
+			goto dropit;
+
+/*		if (tp->t_socket->so_options & SO_KEEPALIVE && */
+		if ((so_options) && tp->t_state <= TCPS_CLOSE_WAIT) {
+		    	if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
+				goto dropit;
+			/*
+			 * Send a packet designed to force a response
+			 * if the peer is up and reachable:
+			 * either an ACK if the connection is still alive,
+			 * or an RST if the peer has closed the connection
+			 * due to timeout or reboot.
+			 * Using sequence number tp->snd_una-1
+			 * causes the transmitted zero-length segment
+			 * to lie outside the receive window;
+			 * by the protocol spec, this requires the
+			 * correspondent TCP to respond.
+			 */
+			tcpstat.tcps_keepprobe++;
+#ifdef TCP_COMPAT_42
+			/*
+			 * The keepalive packet must have nonzero length
+			 * to get a 4.2 host to respond.
+			 */
+			tcp_respond(tp, &tp->t_template, (MBuf )NULL,
+			    tp->rcv_nxt - 1, tp->snd_una - 1, 0);
+#else
+			tcp_respond(tp, &tp->t_template, (MBuf )NULL,
+			    tp->rcv_nxt, tp->snd_una - 1, 0);
+#endif
+			tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+		} else
+			tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+		break;
+
+	dropit:
+		tcpstat.tcps_keepdrops++;
+		tp = tcp_drop(tp, 0); /* ETIMEDOUT); */
+		break;
+	}
+
+	return (tp);
+}
diff --git a/slirp2/tcp_timer.h b/slirp2/tcp_timer.h
new file mode 100644
index 0000000..59933bc
--- /dev/null
+++ b/slirp2/tcp_timer.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp_timer.h	8.1 (Berkeley) 6/10/93
+ * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
+ */
+
+#ifndef _TCP_TIMER_H_
+#define _TCP_TIMER_H_
+
+/*
+ * Definitions of the TCP timers.  These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define	TCPT_NTIMERS	4
+
+#define	TCPT_REXMT	0		/* retransmit */
+#define	TCPT_PERSIST	1		/* retransmit persistence */
+#define	TCPT_KEEP	2		/* keep alive */
+#define	TCPT_2MSL	3		/* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received.  If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected).  Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut.  If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer.  When it expires, if the window is nonzero,
+ * we go to transmit state.  Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time.  The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive.  If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection.  Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection.  We force the peer to send us a segment by sending:
+ *	<SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer.  If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL       ( 5*PR_SLOWHZ)          /* max seg lifetime (hah!) */
+
+#define	TCPTV_SRTTBASE	0			/* base roundtrip time;
+						   if 0, no idea yet */
+#define	TCPTV_SRTTDFLT	(  3*PR_SLOWHZ)		/* assumed RTT if no info */
+
+#define	TCPTV_PERSMIN	(  5*PR_SLOWHZ)		/* retransmit persistence */
+#define	TCPTV_PERSMAX	( 60*PR_SLOWHZ)		/* maximum persist interval */
+
+#define	TCPTV_KEEP_INIT	( 75*PR_SLOWHZ)		/* initial connect keep alive */
+#define	TCPTV_KEEP_IDLE	(120*60*PR_SLOWHZ)	/* dflt time before probing */
+#define	TCPTV_KEEPINTVL	( 75*PR_SLOWHZ)		/* default probe interval */
+#define	TCPTV_KEEPCNT	8			/* max probes before drop */
+
+#define	TCPTV_MIN	(  1*PR_SLOWHZ)		/* minimum allowable value */
+/* #define	TCPTV_REXMTMAX	( 64*PR_SLOWHZ)	*/	/* max allowable REXMT value */
+#define TCPTV_REXMTMAX  ( 12*PR_SLOWHZ)		/* max allowable REXMT value */
+
+#define	TCP_LINGERTIME	120			/* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12                      /* maximum retransmits */
+
+
+#ifdef	TCPTIMERS
+char *tcptimers[] =
+    { "REXMT", "PERSIST", "KEEP", "2MSL" };
+#endif
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define	TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+	(tv) = (value); \
+	if ((tv) < (tvmin)) \
+		(tv) = (tvmin); \
+	else if ((tv) > (tvmax)) \
+		(tv) = (tvmax); \
+}
+
+extern int tcp_keepidle;		/* time before keepalive probes begin */
+extern int tcp_keepintvl;		/* time between keepalive probes */
+extern int tcp_maxidle;			/* time to drop after starting probes */
+extern int tcp_ttl;			/* time to live for TCP segs */
+extern int tcp_backoff[];
+
+struct tcpcb;
+
+void tcp_fasttimo _P((void));
+void tcp_slowtimo _P((void));
+void tcp_canceltimers _P((struct tcpcb *));
+struct tcpcb * tcp_timers _P((register struct tcpcb *, int));
+
+#endif
diff --git a/slirp2/tcp_var.h b/slirp2/tcp_var.h
new file mode 100644
index 0000000..3509ec8
--- /dev/null
+++ b/slirp2/tcp_var.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1982, 1986, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcp_var.h	8.3 (Berkeley) 4/10/94
+ * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
+ */
+
+#ifndef _TCP_VAR_H_
+#define _TCP_VAR_H_
+
+#include "mbuf.h"
+#include "tcpip.h"
+#include "tcp_timer.h"
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb {
+	struct tcpiphdr  *seg_next;	/* sequencing queue */
+	struct tcpiphdr  *seg_prev;
+	short	t_state;		/* state of this connection */
+	short	t_timer[TCPT_NTIMERS];	/* tcp timers */
+	short	t_rxtshift;		/* log(2) of rexmt exp. backoff */
+	short	t_rxtcur;		/* current retransmit value */
+	short	t_dupacks;		/* consecutive dup acks recd */
+	u_short	t_maxseg;		/* maximum segment size */
+	char	t_force;		/* 1 if forcing out a byte */
+	u_short	t_flags;
+#define	TF_ACKNOW	0x0001		/* ack peer immediately */
+#define	TF_DELACK	0x0002		/* ack, but try to delay it */
+#define	TF_NODELAY	0x0004		/* don't delay packets to coalesce */
+#define	TF_NOOPT	0x0008		/* don't use tcp options */
+#define	TF_SENTFIN	0x0010		/* have sent FIN */
+#define	TF_REQ_SCALE	0x0020		/* have/will request window scaling */
+#define	TF_RCVD_SCALE	0x0040		/* other side has requested scaling */
+#define	TF_REQ_TSTMP	0x0080		/* have/will request timestamps */
+#define	TF_RCVD_TSTMP	0x0100		/* a timestamp was received in SYN */
+#define	TF_SACK_PERMIT	0x0200		/* other side said I could SACK */
+
+	/* Make it static  for now */
+/*	struct	tcpiphdr *t_template;	/ * skeletal packet for transmit */
+	struct	tcpiphdr t_template;
+
+	struct	socket *t_socket;		/* back pointer to socket */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+/* send sequence variables */
+	tcp_seq	snd_una;		/* send unacknowledged */
+	tcp_seq	snd_nxt;		/* send next */
+	tcp_seq	snd_up;			/* send urgent pointer */
+	tcp_seq	snd_wl1;		/* window update seg seq number */
+	tcp_seq	snd_wl2;		/* window update seg ack number */
+	tcp_seq	iss;			/* initial send sequence number */
+	u_int32_t snd_wnd;		/* send window */
+/* receive sequence variables */
+	u_int32_t rcv_wnd;		/* receive window */
+	tcp_seq	rcv_nxt;		/* receive next */
+	tcp_seq	rcv_up;			/* receive urgent pointer */
+	tcp_seq	irs;			/* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+/* receive variables */
+	tcp_seq	rcv_adv;		/* advertised window */
+/* retransmit variables */
+	tcp_seq	snd_max;		/* highest sequence number sent;
+					 * used to recognize retransmits
+					 */
+/* congestion control (for slow start, source quench, retransmit after loss) */
+	u_int32_t snd_cwnd;		/* congestion-controlled window */
+	u_int32_t snd_ssthresh;		/* snd_cwnd size threshold for
+					 * for slow start exponential to
+					 * linear switch
+					 */
+/*
+ * transmit timing stuff.  See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+	short	t_idle;			/* inactivity time */
+	short	t_rtt;			/* round trip time */
+	tcp_seq	t_rtseq;		/* sequence number being timed */
+	short	t_srtt;			/* smoothed round-trip time */
+	short	t_rttvar;		/* variance in round-trip time */
+	u_short	t_rttmin;		/* minimum rtt allowed */
+	u_int32_t max_sndwnd;		/* largest window peer has offered */
+
+/* out-of-band data */
+	char	t_oobflags;		/* have some */
+	char	t_iobc;			/* input character */
+#define	TCPOOB_HAVEDATA	0x01
+#define	TCPOOB_HADDATA	0x02
+	short	t_softerror;		/* possible error not yet reported */
+
+/* RFC 1323 variables */
+	u_char	snd_scale;		/* window scaling for send window */
+	u_char	rcv_scale;		/* window scaling for recv window */
+	u_char	request_r_scale;	/* pending window scaling */
+	u_char	requested_s_scale;
+	u_int32_t	ts_recent;		/* timestamp echo data */
+	u_int32_t	ts_recent_age;		/* when last updated */
+	tcp_seq	last_ack_sent;
+
+};
+
+#define	sototcpcb(so)	((so)->so_tcpcb)
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875.  rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define	TCP_RTT_SCALE		8	/* multiplier for srtt; 3 bits frac. */
+#define	TCP_RTT_SHIFT		3	/* shift for srtt; 3 bits frac. */
+#define	TCP_RTTVAR_SCALE	4	/* multiplier for rttvar; 2 bits */
+#define	TCP_RTTVAR_SHIFT	2	/* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias.  When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer.  The bias will give us exactly the
+ * 1.5 tick we need.  But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define	TCP_REXMTVAL(tp) \
+	(((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+/*
+ * TCP statistics.
+ * Many of these should be kept per connection,
+ * but that's inconvenient at the moment.
+ */
+struct tcpstat {
+	u_long	tcps_connattempt;	/* connections initiated */
+	u_long	tcps_accepts;		/* connections accepted */
+	u_long	tcps_connects;		/* connections established */
+	u_long	tcps_drops;		/* connections dropped */
+	u_long	tcps_conndrops;		/* embryonic connections dropped */
+	u_long	tcps_closed;		/* conn. closed (includes drops) */
+	u_long	tcps_segstimed;		/* segs where we tried to get rtt */
+	u_long	tcps_rttupdated;	/* times we succeeded */
+	u_long	tcps_delack;		/* delayed acks sent */
+	u_long	tcps_timeoutdrop;	/* conn. dropped in rxmt timeout */
+	u_long	tcps_rexmttimeo;	/* retransmit timeouts */
+	u_long	tcps_persisttimeo;	/* persist timeouts */
+	u_long	tcps_keeptimeo;		/* keepalive timeouts */
+	u_long	tcps_keepprobe;		/* keepalive probes sent */
+	u_long	tcps_keepdrops;		/* connections dropped in keepalive */
+
+	u_long	tcps_sndtotal;		/* total packets sent */
+	u_long	tcps_sndpack;		/* data packets sent */
+	u_long	tcps_sndbyte;		/* data bytes sent */
+	u_long	tcps_sndrexmitpack;	/* data packets retransmitted */
+	u_long	tcps_sndrexmitbyte;	/* data bytes retransmitted */
+	u_long	tcps_sndacks;		/* ack-only packets sent */
+	u_long	tcps_sndprobe;		/* window probes sent */
+	u_long	tcps_sndurg;		/* packets sent with URG only */
+	u_long	tcps_sndwinup;		/* window update-only packets sent */
+	u_long	tcps_sndctrl;		/* control (SYN|FIN|RST) packets sent */
+
+	u_long	tcps_rcvtotal;		/* total packets received */
+	u_long	tcps_rcvpack;		/* packets received in sequence */
+	u_long	tcps_rcvbyte;		/* bytes received in sequence */
+	u_long	tcps_rcvbadsum;		/* packets received with ccksum errs */
+	u_long	tcps_rcvbadoff;		/* packets received with bad offset */
+/*	u_long	tcps_rcvshort;	*/	/* packets received too short */
+	u_long	tcps_rcvduppack;	/* duplicate-only packets received */
+	u_long	tcps_rcvdupbyte;	/* duplicate-only bytes received */
+	u_long	tcps_rcvpartduppack;	/* packets with some duplicate data */
+	u_long	tcps_rcvpartdupbyte;	/* dup. bytes in part-dup. packets */
+	u_long	tcps_rcvoopack;		/* out-of-order packets received */
+	u_long	tcps_rcvoobyte;		/* out-of-order bytes received */
+	u_long	tcps_rcvpackafterwin;	/* packets with data after window */
+	u_long	tcps_rcvbyteafterwin;	/* bytes rcvd after window */
+	u_long	tcps_rcvafterclose;	/* packets rcvd after "close" */
+	u_long	tcps_rcvwinprobe;	/* rcvd window probe packets */
+	u_long	tcps_rcvdupack;		/* rcvd duplicate acks */
+	u_long	tcps_rcvacktoomuch;	/* rcvd acks for unsent data */
+	u_long	tcps_rcvackpack;	/* rcvd ack packets */
+	u_long	tcps_rcvackbyte;	/* bytes acked by rcvd acks */
+	u_long	tcps_rcvwinupd;		/* rcvd window update packets */
+/*	u_long	tcps_pawsdrop;	*/	/* segments dropped due to PAWS */
+	u_long	tcps_predack;		/* times hdr predict ok for acks */
+	u_long	tcps_preddat;		/* times hdr predict ok for data pkts */
+	u_long	tcps_socachemiss;	/* tcp_last_so misses */
+	u_long	tcps_didnuttin;		/* Times tcp_output didn't do anything XXX */
+};
+
+extern struct	tcpstat tcpstat;	/* tcp statistics */
+extern u_int32_t	tcp_now;		/* for RFC 1323 timestamps */
+
+#endif
diff --git a/slirp2/tcpip.h b/slirp2/tcpip.h
new file mode 100644
index 0000000..891d699
--- /dev/null
+++ b/slirp2/tcpip.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)tcpip.h	8.1 (Berkeley) 6/10/93
+ * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
+ */
+
+#ifndef _TCPIP_H_
+#define _TCPIP_H_
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr {
+	struct 	ipovly ti_i;		/* overlaid ip structure */
+	struct	tcphdr ti_t;		/* tcp header */
+};
+#define ti_mbuf         ti_i.ih_mbuf.mptr
+#define	ti_x1		ti_i.ih_x1
+#define	ti_pr		ti_i.ih_pr
+#define	ti_len		ti_i.ih_len
+#define	ti_src		ti_i.ih_src
+#define	ti_dst		ti_i.ih_dst
+#define	ti_sport	ti_t.th_sport
+#define	ti_dport	ti_t.th_dport
+#define	ti_seq		ti_t.th_seq
+#define	ti_ack		ti_t.th_ack
+#define	ti_x2		ti_t.th_x2
+#define	ti_off		ti_t.th_off
+#define	ti_flags	ti_t.th_flags
+#define	ti_win		ti_t.th_win
+#define	ti_sum		ti_t.th_sum
+#define	ti_urp		ti_t.th_urp
+
+#define tcpiphdr2qlink(T) ((struct qlink*)(((char*)(T)) - sizeof(struct qlink)))
+#define qlink2tcpiphdr(Q) ((struct tcpiphdr*)(((char*)(Q)) + sizeof(struct qlink)))
+#define tcpiphdr_next(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->next)
+#define tcpiphdr_prev(T) qlink2tcpiphdr(tcpiphdr2qlink(T)->prev)
+#define tcpfrag_list_first(T) qlink2tcpiphdr((T)->seg_next)
+#define tcpfrag_list_end(F, T) (tcpiphdr2qlink(F) == (struct qlink*)(T))
+#define tcpfrag_list_empty(T) ((T)->seg_next == (struct tcpiphdr*)(T))
+
+/*
+ * Just a clean way to get to the first byte
+ * of the packet
+ */
+struct tcpiphdr_2 {
+	struct tcpiphdr dummy;
+	char first_char;
+};
+
+#endif
diff --git a/slirp2/tftp.c b/slirp2/tftp.c
new file mode 100644
index 0000000..37933d9
--- /dev/null
+++ b/slirp2/tftp.c
@@ -0,0 +1,430 @@
+/*
+ * tftp.c - a simple, read-only tftp server for qemu
+ *
+ * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <slirp.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+struct tftp_session {
+    int in_use;
+    unsigned char filename[TFTP_FILENAME_MAX];
+
+    uint32_t client_ip;
+    uint16_t client_port;
+
+    int timestamp;
+};
+
+struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX];
+
+const char *tftp_prefix;
+
+static void tftp_session_update(struct tftp_session *spt)
+{
+    spt->timestamp = curtime;
+    spt->in_use = 1;
+}
+
+static void tftp_session_terminate(struct tftp_session *spt)
+{
+  spt->in_use = 0;
+}
+
+static int tftp_session_allocate(struct tftp_t *tp)
+{
+  struct tftp_session *spt;
+  int k;
+
+  for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+    spt = &tftp_sessions[k];
+
+    if (!spt->in_use)
+        goto found;
+
+    /* sessions time out after 5 inactive seconds */
+    if ((int)(curtime - spt->timestamp) > 5000)
+        goto found;
+  }
+
+  return -1;
+
+ found:
+  memset(spt, 0, sizeof(*spt));
+  spt->client_ip   = ip_geth(tp->ip.ip_src);
+  spt->client_port = port_geth(tp->udp.uh_sport);
+
+  tftp_session_update(spt);
+
+  return k;
+}
+
+static int tftp_session_find(struct tftp_t *tp)
+{
+  struct tftp_session *spt;
+  int k;
+
+  for (k = 0; k < TFTP_SESSIONS_MAX; k++) {
+    spt = &tftp_sessions[k];
+
+    if (spt->in_use) {
+      if (spt->client_ip == ip_geth(tp->ip.ip_src)) {
+	if (spt->client_port == port_geth(tp->udp.uh_sport)) {
+	  return k;
+	}
+      }
+    }
+  }
+
+  return -1;
+}
+
+static int tftp_read_data(struct tftp_session *spt, u_int16_t block_nr,
+			  u_int8_t *buf, int len)
+{
+  int fd;
+  int bytes_read = 0;
+  char buffer[1024];
+  int n;
+
+  n = snprintf(buffer, sizeof(buffer), "%s/%s",
+               tftp_prefix, spt->filename);
+  if (n >= sizeof(buffer))
+      return -1;
+
+  fd = open(buffer, O_RDONLY | O_BINARY);
+
+  if (fd < 0) {
+    return -1;
+  }
+
+  if (len) {
+    lseek(fd, block_nr * 512, SEEK_SET);
+
+    bytes_read = read(fd, buf, len);
+  }
+
+  close(fd);
+
+  return bytes_read;
+}
+
+static int tftp_send_oack(struct tftp_session *spt,
+                          const char *key, uint32_t value,
+                          struct tftp_t *recv_tp)
+{
+    SockAddress  saddr, daddr;
+    MBuf m;
+    struct tftp_t *tp;
+    int n = 0;
+
+    m = mbuf_alloc();
+
+    if (!m)
+        return -1;
+
+    memset(m->m_data, 0, m->m_size);
+
+    m->m_data += if_maxlinkhdr;
+    tp = (void *)m->m_data;
+    m->m_data += sizeof(struct udpiphdr);
+
+    tp->tp_op = htons(TFTP_OACK);
+    n += sprintf((char*)tp->x.tp_buf + n, "%s", key) + 1;
+    n += sprintf((char*)tp->x.tp_buf + n, "%u", value) + 1;
+
+    sock_address_init_inet( &saddr,
+                            ip_geth(recv_tp->ip.ip_dst),
+                            port_geth(recv_tp->udp.uh_dport) );
+
+    sock_address_init_inet( &daddr,
+                            spt->client_ip,
+                            spt->client_port );
+
+    m->m_len = sizeof(struct tftp_t) - 514 + n -
+            sizeof(struct ip) - sizeof(struct udphdr);
+    udp_output2_(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+    return 0;
+}
+
+
+
+static int tftp_send_error(struct tftp_session *spt,
+			   u_int16_t errorcode, const char *msg,
+			   struct tftp_t *recv_tp)
+{
+  SockAddress saddr, daddr;
+  MBuf m;
+  struct tftp_t *tp;
+  int nobytes;
+
+  m = mbuf_alloc();
+
+  if (!m) {
+    return -1;
+  }
+
+  memset(m->m_data, 0, m->m_size);
+
+  m->m_data += if_maxlinkhdr;
+  tp = (void *)m->m_data;
+  m->m_data += sizeof(struct udpiphdr);
+
+  tp->tp_op = htons(TFTP_ERROR);
+  tp->x.tp_error.tp_error_code = htons(errorcode);
+  strcpy((char*)tp->x.tp_error.tp_msg, msg);
+
+  sock_address_init_inet( &saddr,
+                          ip_geth(recv_tp->ip.ip_dst),
+                          port_geth(recv_tp->udp.uh_dport) );
+
+  sock_address_init_inet( &daddr,
+                          spt->client_ip,
+                          spt->client_port );
+
+  nobytes = 2;
+
+  m->m_len = sizeof(struct tftp_t) - 514 + 3 + strlen(msg) -
+        sizeof(struct ip) - sizeof(struct udphdr);
+
+  udp_output2_(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+  tftp_session_terminate(spt);
+
+  return 0;
+}
+
+static int tftp_send_data(struct tftp_session *spt,
+			  u_int16_t block_nr,
+			  struct tftp_t *recv_tp)
+{
+  SockAddress saddr, daddr;
+  MBuf m;
+  struct tftp_t *tp;
+  int nobytes;
+
+  if (block_nr < 1) {
+    return -1;
+  }
+
+  m = mbuf_alloc();
+
+  if (!m) {
+    return -1;
+  }
+
+  memset(m->m_data, 0, m->m_size);
+
+  m->m_data += if_maxlinkhdr;
+  tp = (void *)m->m_data;
+  m->m_data += sizeof(struct udpiphdr);
+
+  tp->tp_op = htons(TFTP_DATA);
+  tp->x.tp_data.tp_block_nr = htons(block_nr);
+
+  sock_address_init_inet( &saddr,
+                          ip_geth(recv_tp->ip.ip_dst),
+                          port_geth(recv_tp->udp.uh_dport) );
+
+  sock_address_init_inet( &daddr,
+                          spt->client_ip,
+                          spt->client_port );
+
+  nobytes = tftp_read_data(spt, block_nr - 1, tp->x.tp_data.tp_buf, 512);
+
+  if (nobytes < 0) {
+    mbuf_free(m);
+
+    /* send "file not found" error back */
+
+    tftp_send_error(spt, 1, "File not found", tp);
+
+    return -1;
+  }
+
+  m->m_len = sizeof(struct tftp_t) - (512 - nobytes) -
+        sizeof(struct ip) - sizeof(struct udphdr);
+
+  udp_output2_(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+
+  if (nobytes == 512) {
+    tftp_session_update(spt);
+  }
+  else {
+    tftp_session_terminate(spt);
+  }
+
+  return 0;
+}
+
+static void tftp_handle_rrq(struct tftp_t *tp, int pktlen)
+{
+  struct tftp_session *spt;
+  int s, k, n;
+  u_int8_t *src, *dst;
+
+  s = tftp_session_allocate(tp);
+
+  if (s < 0) {
+    return;
+  }
+
+  spt = &tftp_sessions[s];
+
+  src = tp->x.tp_buf;
+  dst = spt->filename;
+  n = pktlen - ((uint8_t *)&tp->x.tp_buf[0] - (uint8_t *)tp);
+
+  /* get name */
+
+  for (k = 0; k < n; k++) {
+    if (k < TFTP_FILENAME_MAX) {
+      dst[k] = src[k];
+    }
+    else {
+      return;
+    }
+
+    if (src[k] == '\0') {
+      break;
+    }
+  }
+
+  if (k >= n) {
+    return;
+  }
+
+  k++;
+
+  /* check mode */
+  if ((n - k) < 6) {
+    return;
+  }
+
+  if (memcmp(&src[k], "octet\0", 6) != 0) {
+      tftp_send_error(spt, 4, "Unsupported transfer mode", tp);
+      return;
+  }
+
+  k += 6;  /* skipping octet */
+
+  /* do sanity checks on the filename */
+
+  if ((spt->filename[0] != '/')
+      || (spt->filename[strlen((const char*)spt->filename) - 1] == '/')
+      ||  strstr((const char*)spt->filename, "/../")) {
+      tftp_send_error(spt, 2, "Access violation", tp);
+      return;
+  }
+
+  /* only allow exported prefixes */
+
+  if (!tftp_prefix) {
+      tftp_send_error(spt, 2, "Access violation", tp);
+      return;
+  }
+
+  /* check if the file exists */
+
+  if (tftp_read_data(spt, 0, spt->filename, 0) < 0) {
+      tftp_send_error(spt, 1, "File not found", tp);
+      return;
+  }
+
+  if (src[n - 1] != 0) {
+      tftp_send_error(spt, 2, "Access violation", tp);
+      return;
+  }
+
+  while (k < n) {
+      const char *key, *value;
+
+      key = (const char*)src + k;
+      k += strlen(key) + 1;
+
+      if (k >= n) {
+          tftp_send_error(spt, 2, "Access violation", tp);
+          return;
+      }
+
+      value = (const char*)src + k;
+      k += strlen(value) + 1;
+
+      if (strcmp(key, "tsize") == 0) {
+          int tsize = atoi(value);
+          struct stat stat_p;
+
+          if (tsize == 0 && tftp_prefix) {
+              char buffer[1024];
+              int len;
+
+              len = snprintf(buffer, sizeof(buffer), "%s/%s",
+                             tftp_prefix, spt->filename);
+
+              if (stat(buffer, &stat_p) == 0)
+                  tsize = stat_p.st_size;
+              else {
+                  tftp_send_error(spt, 1, "File not found", tp);
+                  return;
+              }
+          }
+
+          tftp_send_oack(spt, "tsize", tsize, tp);
+      }
+  }
+
+  tftp_send_data(spt, 1, tp);
+}
+
+static void tftp_handle_ack(struct tftp_t *tp, int pktlen)
+{
+  int s;
+
+  s = tftp_session_find(tp);
+
+  if (s < 0) {
+    return;
+  }
+
+  if (tftp_send_data(&tftp_sessions[s],
+		     ntohs(tp->x.tp_data.tp_block_nr) + 1,
+		     tp) < 0) {
+    return;
+  }
+}
+
+void tftp_input(MBuf m)
+{
+  struct tftp_t *tp = (struct tftp_t *)m->m_data;
+
+  switch(ntohs(tp->tp_op)) {
+  case TFTP_RRQ:
+    tftp_handle_rrq(tp, m->m_len);
+    break;
+
+  case TFTP_ACK:
+    tftp_handle_ack(tp, m->m_len);
+    break;
+  }
+}
diff --git a/slirp2/tftp.h b/slirp2/tftp.h
new file mode 100644
index 0000000..06018a7
--- /dev/null
+++ b/slirp2/tftp.h
@@ -0,0 +1,33 @@
+/* tftp defines */
+
+#define TFTP_SESSIONS_MAX 3
+
+#define TFTP_SERVER	69
+
+#define TFTP_RRQ    1
+#define TFTP_WRQ    2
+#define TFTP_DATA   3
+#define TFTP_ACK    4
+#define TFTP_ERROR  5
+#define TFTP_OACK   6
+
+#define TFTP_FILENAME_MAX 512
+
+struct tftp_t {
+  struct ip ip;
+  struct udphdr udp;
+  u_int16_t tp_op;
+  union {
+    struct { 
+      u_int16_t tp_block_nr;
+      u_int8_t tp_buf[512];
+    } tp_data;
+    struct { 
+      u_int16_t tp_error_code;
+      u_int8_t tp_msg[512];
+    } tp_error;
+    u_int8_t tp_buf[512 + 2];
+  } x;
+};
+
+void tftp_input(MBuf m);
diff --git a/slirp2/udp.c b/slirp2/udp.c
new file mode 100644
index 0000000..9305cfd
--- /dev/null
+++ b/slirp2/udp.c
@@ -0,0 +1,495 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)udp_usrreq.c	8.4 (Berkeley) 1/21/94
+ * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+#define SLIRP_COMPILATION  1
+#include "sockets.h"
+
+struct udpstat udpstat;
+
+struct socket udb;
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
+#ifndef	COMPAT_42
+int	udpcksum = 1;
+#else
+int	udpcksum = 0;		/* XXX */
+#endif
+
+struct	socket *udp_last_so = &udb;
+
+void
+udp_init()
+{
+	udb.so_next = udb.so_prev = &udb;
+}
+/* m->m_data  points at ip packet header
+ * m->m_len   length ip packet
+ * ip->ip_len length data (IPDU)
+ */
+void
+udp_input(m, iphlen)
+	register MBuf m;
+	int iphlen;
+{
+	register struct ip *ip;
+	register struct udphdr *uh;
+/*	MBuf opts = 0;*/
+	int len;
+	struct ip save_ip;
+	struct socket *so;
+
+	DEBUG_CALL("udp_input");
+	DEBUG_ARG("m = %lx", (long)m);
+	DEBUG_ARG("iphlen = %d", iphlen);
+
+	udpstat.udps_ipackets++;
+
+	/*
+	 * Strip IP options, if any; should skip this,
+	 * make available to user, and use on returned packets,
+	 * but we don't yet have a way to check the checksum
+	 * with options still present.
+	 */
+	if(iphlen > sizeof(struct ip)) {
+		ip_stripoptions(m, (MBuf )0);
+		iphlen = sizeof(struct ip);
+	}
+
+	/*
+	 * Get IP and UDP header together in first mbuf.
+	 */
+	ip = MBUF_TO(m, struct ip *);
+	uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+	/*
+	 * Make mbuf data length reflect UDP length.
+	 * If not enough data to reflect UDP length, drop.
+	 */
+	len = ntohs((u_int16_t)uh->uh_ulen);
+
+	if (ip->ip_len != len) {
+		if (len > ip->ip_len) {
+			udpstat.udps_badlen++;
+			goto bad;
+		}
+		mbuf_trim(m, len - ip->ip_len);
+		ip->ip_len = len;
+	}
+
+	/*
+	 * Save a copy of the IP header in case we want restore it
+	 * for sending an ICMP error message in response.
+	 */
+	save_ip = *ip;
+	save_ip.ip_len+= iphlen;         /* tcp_input subtracts this */
+
+	/*
+	 * Checksum extended UDP header and data.
+	 */
+	if (udpcksum && uh->uh_sum) {
+	  memset(&((struct ipovly *)ip)->ih_mbuf, 0, sizeof(struct mbuf_ptr));
+	  ((struct ipovly *)ip)->ih_x1 = 0;
+	  ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+	  /* keep uh_sum for ICMP reply
+	   * uh->uh_sum = cksum(m, len + sizeof (struct ip));
+	   * if (uh->uh_sum) {
+	   */
+	  if(cksum(m, len + sizeof(struct ip))) {
+	    udpstat.udps_badsum++;
+	    goto bad;
+	  }
+	}
+
+        /*
+         *  handle DHCP/BOOTP
+         */
+        if (port_geth(uh->uh_dport) == BOOTP_SERVER) {
+            bootp_input(m);
+            goto bad;
+        }
+
+        /*
+         *  handle TFTP
+         */
+        if (port_geth(uh->uh_dport) == TFTP_SERVER) {
+            tftp_input(m);
+            goto bad;
+        }
+
+	/*
+	 * Locate pcb for datagram.
+	 */
+	so = udp_last_so;
+	if (so->so_laddr_port != port_geth(uh->uh_sport) ||
+	    so->so_laddr_ip   != ip_geth(ip->ip_src)) {
+		struct socket *tmp;
+
+		for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next) {
+			if (tmp->so_laddr_port == port_geth(uh->uh_sport) &&
+			    tmp->so_laddr_ip   == ip_geth(ip->ip_src)) {
+				tmp->so_faddr_ip   = ip_geth(ip->ip_dst);
+				tmp->so_faddr_port = port_geth(uh->uh_dport);
+				so = tmp;
+				break;
+			}
+		}
+		if (tmp == &udb) {
+		  so = NULL;
+		} else {
+		  udpstat.udpps_pcbcachemiss++;
+		  udp_last_so = so;
+		}
+	}
+
+	if (so == NULL) {
+	  /*
+	   * If there's no socket for this packet,
+	   * create one
+	   */
+	  if ((so = socreate()) == NULL) goto bad;
+	  if(udp_attach(so) == -1) {
+	    DEBUG_MISC((dfd," udp_attach errno = %d-%s\n",
+			errno,errno_str));
+	    sofree(so);
+	    goto bad;
+	  }
+
+	  /*
+	   * Setup fields
+	   */
+	  /* udp_last_so = so; */
+	  so->so_laddr_ip   = ip_geth(ip->ip_src);
+	  so->so_laddr_port = port_geth(uh->uh_sport);
+
+	  if ((so->so_iptos = udp_tos(so)) == 0)
+	    so->so_iptos = ip->ip_tos;
+
+	  /*
+	   * XXXXX Here, check if it's in udpexec_list,
+	   * and if it is, do the fork_exec() etc.
+	   */
+	}
+
+        so->so_faddr_ip   = ip_geth(ip->ip_dst); /* XXX */
+        so->so_faddr_port = port_geth(uh->uh_dport); /* XXX */
+
+        iphlen += sizeof(struct udphdr);
+	m->m_len -= iphlen;
+	m->m_data += iphlen;
+
+	/*
+	 * Now we sendto() the packet.
+	 */
+	if (so->so_emu)
+	   udp_emu(so, m);
+
+	if(sosendto(so,m) == -1) {
+	  m->m_len += iphlen;
+	  m->m_data -= iphlen;
+	  *ip=save_ip;
+	  DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno, errno_str));
+	  icmp_error(m, ICMP_UNREACH,ICMP_UNREACH_NET, 0,errno_str);
+	}
+
+	mbuf_free(so->so_m);   /* used for ICMP if error on sorecvfrom */
+
+	/* restore the orig mbuf packet */
+	m->m_len += iphlen;
+	m->m_data -= iphlen;
+	*ip=save_ip;
+	so->so_m=m;         /* ICMP backup */
+
+	return;
+bad:
+	mbuf_free(m);
+	/* if (opts) mbuf_free(opts); */
+	return;
+}
+
+int udp_output2_(struct socket*  so, MBuf  m, 
+                 const SockAddress*  saddr, 
+                 const SockAddress*  daddr,
+                 int  iptos)
+{
+    register struct udpiphdr *ui;
+    uint32_t  saddr_ip = sock_address_get_ip(saddr);
+    uint32_t  daddr_ip = sock_address_get_ip(daddr);
+    int       saddr_port = sock_address_get_port(saddr);
+    int       daddr_port = sock_address_get_port(daddr);
+    int error = 0;
+
+    DEBUG_CALL("udp_output");
+    DEBUG_ARG("so = %lx", (long)so);
+    DEBUG_ARG("m = %lx", (long)m);
+    DEBUG_ARG("saddr = %lx", (long) saddr_ip);
+    DEBUG_ARG("daddr = %lx", (long) daddr_ip);
+
+    /*
+        * Adjust for header
+        */
+    m->m_data -= sizeof(struct udpiphdr);
+    m->m_len  += sizeof(struct udpiphdr);
+
+    /*
+        * Fill in mbuf with extended UDP header
+        * and addresses and length put into network format.
+        */
+    ui = MBUF_TO(m, struct udpiphdr *);
+    memset(&ui->ui_i.ih_mbuf, 0 , sizeof(struct mbuf_ptr));
+    ui->ui_x1 = 0;
+    ui->ui_pr = IPPROTO_UDP;
+    ui->ui_len = htons(m->m_len - sizeof(struct ip)); /* + sizeof (struct udphdr)); */
+    /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
+    ui->ui_src   = ip_seth(saddr_ip);
+    ui->ui_dst   = ip_seth(daddr_ip);
+    ui->ui_sport = port_seth(saddr_port);
+    ui->ui_dport = port_seth(daddr_port);
+    ui->ui_ulen = ui->ui_len;
+
+    /*
+        * Stuff checksum and output datagram.
+        */
+    ui->ui_sum = 0;
+    if (udpcksum) {
+        if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ m->m_len)) == 0)
+            ui->ui_sum = 0xffff;
+    }
+    ((struct ip *)ui)->ip_len = m->m_len;
+
+    ((struct ip *)ui)->ip_ttl = ip_defttl;
+    ((struct ip *)ui)->ip_tos = iptos;
+
+    udpstat.udps_opackets++;
+
+    error = ip_output(so, m);
+
+    return (error);
+}
+
+int udp_output_(struct socket *so, MBuf m, SockAddress* from)
+{
+    SockAddress  saddr, daddr;
+    uint32_t     saddr_ip;
+    uint16_t     saddr_port;
+
+    saddr_ip   = sock_address_get_ip(from);
+    saddr_port = sock_address_get_port(from);
+
+    if ((so->so_faddr_ip & 0xffffff00) == special_addr_ip) {
+        saddr_ip = so->so_faddr_ip;
+        if ((so->so_faddr_ip & 0x000000ff) == 0xff)
+            saddr_ip = alias_addr_ip;
+    }
+
+    sock_address_init_inet( &saddr, saddr_ip, saddr_port );
+    sock_address_init_inet( &daddr, so->so_laddr_ip, so->so_laddr_port );
+
+    return udp_output2_(so, m, &saddr, &daddr, so->so_iptos);
+}
+
+int
+udp_attach(so)
+     struct socket *so;
+{
+  so->s = socket_anyaddr_server( 0, SOCKET_DGRAM );
+  if (so->s != -1) {
+      /* success, insert in queue */
+      so->so_expire = curtime + SO_EXPIRE;
+      insque(so,&udb);
+  }
+  return(so->s);
+}
+
+void
+udp_detach(so)
+	struct socket *so;
+{
+	socket_close(so->s);
+	/* if (so->so_m) mbuf_free(so->so_m);    done by sofree */
+
+	sofree(so);
+}
+
+struct tos_t udptos[] = {
+	{0, 53, IPTOS_LOWDELAY, 0},			/* DNS */
+	{517, 517, IPTOS_LOWDELAY, EMU_TALK},	/* talk */
+	{518, 518, IPTOS_LOWDELAY, EMU_NTALK},	/* ntalk */
+	{0, 7648, IPTOS_LOWDELAY, EMU_CUSEEME},	/* Cu-Seeme */
+	{0, 0, 0, 0}
+};
+
+u_int8_t
+udp_tos(so)
+	struct socket *so;
+{
+	int i = 0;
+
+	while(udptos[i].tos) {
+		if ((udptos[i].fport && so->so_faddr_port == udptos[i].fport) ||
+		    (udptos[i].lport && so->so_laddr_port == udptos[i].lport)) {
+		    	so->so_emu = udptos[i].emu;
+			return udptos[i].tos;
+		}
+		i++;
+	}
+
+	return 0;
+}
+
+/*
+ * Here, talk/ytalk/ntalk requests must be emulated
+ */
+void
+udp_emu(so, m)
+	struct socket *so;
+	MBuf m;
+{
+        SockAddress  sockaddr;
+
+struct cu_header {
+	uint16_t	d_family;		// destination family
+	uint16_t	d_port;			// destination port
+	uint32_t	d_addr;			// destination address
+	uint16_t	s_family;		// source family
+	uint16_t	s_port;			// source port
+	uint32_t	so_addr;		// source address
+	uint32_t	seqn;			// sequence number
+	uint16_t	message;		// message
+	uint16_t	data_type;		// data type
+	uint16_t	pkt_len;		// packet length
+} *cu_head;
+
+	switch(so->so_emu) {
+
+	case EMU_CUSEEME:
+
+		/*
+		 * Cu-SeeMe emulation.
+		 * Hopefully the packet is more that 16 bytes long. We don't
+		 * do any other tests, just replace the address and port
+		 * fields.
+		 */
+		if (m->m_len >= sizeof (*cu_head)) {
+			if (socket_get_address(so->s, &sockaddr) < 0)
+                            return;
+
+			cu_head = MBUF_TO(m, struct cu_header *);
+			cu_head->s_port  = htons( sock_address_get_port(&sockaddr));
+			cu_head->so_addr = htonl( sock_address_get_ip(&sockaddr));
+		}
+
+		return;
+	}
+}
+
+struct socket *
+udp_listen(port, laddr, lport, flags)
+	u_int port;
+	u_int32_t laddr;
+	u_int lport;
+	int flags;
+{
+	struct socket *so;
+	SockAddress    addr;
+	uint32_t       addr_ip;
+
+	if ((so = socreate()) == NULL) {
+		free(so);
+		return NULL;
+	}
+	so->s = socket_anyaddr_server( port, SOCKET_DGRAM );
+	so->so_expire = curtime + SO_EXPIRE;
+        so->so_haddr_port = port;
+	insque(so,&udb);
+
+	if (so->s < 0) {
+		udp_detach(so);
+		return NULL;
+	}
+
+        socket_get_address(so->s, &addr);
+
+	so->so_faddr_port = sock_address_get_port(&addr);
+	addr_ip = sock_address_get_ip(&addr);
+
+	if (addr_ip == 0 || addr_ip == loopback_addr_ip)
+	   so->so_faddr_ip = alias_addr_ip;
+	else
+	   so->so_faddr_ip = addr_ip;
+
+	so->so_laddr_port = lport;
+	so->so_laddr_ip   = laddr;
+	if (flags != SS_FACCEPTONCE)
+	   so->so_expire = 0;
+
+	so->so_state = SS_ISFCONNECTED;
+
+	return so;
+}
+
+int udp_unlisten (u_int port)
+{
+    struct socket *so;
+
+    for (so = udb.so_next; so != &udb; so = so->so_next) {
+        if (so->so_haddr_port == port) {
+            break;
+        }
+    }
+
+    if (so == &udb)
+        return -1;
+
+    sofcantrcvmore( so );
+    sofcantsendmore( so );
+    socket_close( so->s );
+    so->s = -1;
+    sofree( so );
+    return 0;
+}
diff --git a/slirp2/udp.h b/slirp2/udp.h
new file mode 100644
index 0000000..cadd17e
--- /dev/null
+++ b/slirp2/udp.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)udp.h	8.1 (Berkeley) 6/10/93
+ * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
+ */
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#include "helper.h"
+
+#define UDP_TTL 0x60
+#define UDP_UDPDATALEN 16192
+
+extern struct socket *udp_last_so;
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+	port_t	uh_sport;		/* source port */
+	port_t	uh_dport;		/* destination port */
+	int16_t	uh_ulen;		/* udp length */
+	u_int16_t	uh_sum;			/* udp checksum */
+};
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr {
+	        struct  ipovly ui_i;            /* overlaid ip structure */
+	        struct  udphdr ui_u;            /* udp header */
+};
+#define ui_mbuf         ui_i.ih_mbuf.mptr
+#define ui_x1           ui_i.ih_x1
+#define ui_pr           ui_i.ih_pr
+#define ui_len          ui_i.ih_len
+#define ui_src          ui_i.ih_src
+#define ui_dst          ui_i.ih_dst
+#define ui_sport        ui_u.uh_sport
+#define ui_dport        ui_u.uh_dport
+#define ui_ulen         ui_u.uh_ulen
+#define ui_sum          ui_u.uh_sum
+
+struct udpstat {
+	                                /* input statistics: */
+	        u_long  udps_ipackets;          /* total input packets */
+	        u_long  udps_hdrops;            /* packet shorter than header */
+	        u_long  udps_badsum;            /* checksum error */
+	        u_long  udps_badlen;            /* data length larger than packet */
+	        u_long  udps_noport;            /* no socket on port */
+	        u_long  udps_noportbcast;       /* of above, arrived as broadcast */
+	        u_long  udps_fullsock;          /* not delivered, input socket full */
+	        u_long  udpps_pcbcachemiss;     /* input packets missing pcb cache */
+	                                /* output statistics: */
+	        u_long  udps_opackets;          /* total output packets */
+};
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM         1       /* checksum UDP packets */
+#define UDPCTL_MAXID            2
+
+extern struct udpstat udpstat;
+extern struct socket udb;
+struct mbuf;
+
+void udp_init _P((void));
+void udp_input _P((register MBuf , int));
+int udp_attach _P((struct socket *));
+void udp_detach _P((struct socket *));
+u_int8_t udp_tos _P((struct socket *));
+void udp_emu _P((struct socket *, MBuf ));
+struct socket * udp_listen _P((u_int, u_int32_t, u_int, int));
+int udp_unlisten _P((u_int));
+
+int udp_output_(struct socket *, MBuf, SockAddress*);
+
+int udp_output2_(struct socket*  so, MBuf  m,
+                 const SockAddress*  saddr, const SockAddress*  daddr,
+                 int  iptos);
+
+#endif
diff --git a/sockets.c b/sockets.c
new file mode 100644
index 0000000..62c1ee3
--- /dev/null
+++ b/sockets.c
@@ -0,0 +1,1269 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sockets.h"
+#include "qemu-common.h"
+#include <fcntl.h>
+#include <stddef.h>
+#include "qemu_debug.h"
+#include <stdlib.h>
+#include <string.h>
+#include "android/utils/path.h"
+
+#ifdef _WIN32
+#  define xxWIN32_LEAN_AND_MEAN
+#  include <windows.h>
+#  include <winsock2.h>
+#  include <ws2tcpip.h>
+#else /* !_WIN32 */
+#  include <sys/ioctl.h>
+#  include <sys/socket.h>
+#  include <netinet/in.h>
+#  include <netinet/tcp.h>
+#  include <netdb.h>
+#  if HAVE_UNIX_SOCKETS
+#    include <sys/un.h>
+#    ifndef UNIX_PATH_MAX
+#      define  UNIX_PATH_MAX  (sizeof(((struct sockaddr_un*)0)->sun_path)-1)
+#    endif
+#  endif
+#endif /* !_WIN32 */
+
+
+
+/* QSOCKET_CALL is used to deal with the fact that EINTR happens pretty
+ * easily in QEMU since we use SIGALRM to implement periodic timers
+ */
+#ifdef _WIN32
+#  define  QSOCKET_CALL(_ret,_cmd)   \
+    do { _ret = (_cmd); } while ( _ret < 0 && WSAGetLastError() == WSAEINTR )
+#else
+#  define  QSOCKET_CALL(_ret,_cmd)   \
+    do { _ret = (_cmd); } while ( _ret < 0 && errno == EINTR )
+#endif
+
+#ifdef _WIN32
+
+#include <errno.h>
+
+static int  winsock_error;
+
+#define  WINSOCK_ERRORS_LIST \
+    EE(WSA_INVALID_HANDLE,EINVAL,"invalid handle") \
+    EE(WSA_NOT_ENOUGH_MEMORY,ENOMEM,"not enough memory") \
+    EE(WSA_INVALID_PARAMETER,EINVAL,"invalid parameter") \
+    EE(WSAEINTR,EINTR,"interrupted function call") \
+	EE(WSAEALREADY,EALREADY,"operation already in progress") \
+    EE(WSAEBADF,EBADF,"bad file descriptor") \
+    EE(WSAEACCES,EACCES,"permission denied") \
+    EE(WSAEFAULT,EFAULT,"bad address") \
+    EE(WSAEINVAL,EINVAL,"invalid argument") \
+    EE(WSAEMFILE,EMFILE,"too many opened files") \
+    EE(WSAEWOULDBLOCK,EAGAIN,"resource temporarily unavailable") \
+    EE(WSAEINPROGRESS,EAGAIN,"operation now in progress") \
+    EE(WSAEALREADY,EAGAIN,"operation already in progress") \
+    EE(WSAENOTSOCK,EBADF,"socket operation not on socket") \
+    EE(WSAEDESTADDRREQ,EDESTADDRREQ,"destination address required") \
+    EE(WSAEMSGSIZE,EMSGSIZE,"message too long") \
+    EE(WSAEPROTOTYPE,EPROTOTYPE,"wrong protocol type for socket") \
+    EE(WSAENOPROTOOPT,ENOPROTOOPT,"bad protocol option") \
+    EE(WSAEADDRINUSE,EADDRINUSE,"address already in use") \
+    EE(WSAEADDRNOTAVAIL,EADDRNOTAVAIL,"cannot assign requested address") \
+    EE(WSAENETDOWN,ENETDOWN,"network is down") \
+    EE(WSAENETUNREACH,ENETUNREACH,"network unreachable") \
+    EE(WSAENETRESET,ENETRESET,"network dropped connection on reset") \
+    EE(WSAECONNABORTED,ECONNABORTED,"software caused connection abort") \
+    EE(WSAECONNRESET,ECONNRESET,"connection reset by peer") \
+    EE(WSAENOBUFS,ENOBUFS,"no buffer space available") \
+    EE(WSAEISCONN,EISCONN,"socket is already connected") \
+    EE(WSAENOTCONN,ENOTCONN,"socket is not connected") \
+    EE(WSAESHUTDOWN,ESHUTDOWN,"cannot send after socket shutdown") \
+    EE(WSAETOOMANYREFS,ETOOMANYREFS,"too many references") \
+    EE(WSAETIMEDOUT,ETIMEDOUT,"connection timed out") \
+    EE(WSAECONNREFUSED,ECONNREFUSED,"connection refused") \
+    EE(WSAELOOP,ELOOP,"cannot translate name") \
+    EE(WSAENAMETOOLONG,ENAMETOOLONG,"name too long") \
+    EE(WSAEHOSTDOWN,EHOSTDOWN,"host is down") \
+    EE(WSAEHOSTUNREACH,EHOSTUNREACH,"no route to host") \
+
+typedef struct {
+    int          winsock;
+    int          unix;
+    const char*  string;
+} WinsockError;
+
+static const WinsockError  _winsock_errors[] = {
+#define  EE(w,u,s)   { w, u, s },
+    WINSOCK_ERRORS_LIST
+#undef   EE
+    { -1, -1, NULL }
+};
+
+/* this function reads the latest winsock error code and updates
+ * errno to a matching value. It also returns the new value of
+ * errno.
+ */
+static int
+_fix_errno( void )
+{
+    const WinsockError*  werr = _winsock_errors;
+    int                  unix = EINVAL;  /* generic error code */
+
+    for ( ; werr->string != NULL; werr++ ) {
+        if (werr->winsock == winsock_error) {
+            unix = werr->unix;
+            break;
+        }
+	}
+    errno = unix;
+    return -1;
+}
+
+static int
+_set_errno( int  code )
+{
+    winsock_error = -1;
+    errno         = code;
+    return -1;
+}
+
+/* this function returns a string describing the latest Winsock error */
+const char*
+_errno_str(void)
+{
+    const WinsockError*  werr   = _winsock_errors;
+    const char*          result = "<unknown error>";
+
+    for ( ; werr->string; werr++ ) {
+        if (werr->winsock == winsock_error) {
+            result = werr->string;
+            break;
+        }
+    }
+
+    if (result == NULL)
+        result = strerror(errno);
+
+    return result;
+}
+#else
+static int
+_fix_errno( void )
+{
+    return -1;
+}
+
+static int
+_set_errno( int  code )
+{
+    errno = code;
+    return -1;
+}
+#endif
+
+/* socket types */
+
+static int
+socket_family_to_bsd( SocketFamily  family )
+{
+    switch (family) {
+    case SOCKET_INET: return AF_INET;
+    case SOCKET_IN6:  return AF_INET6;
+#if HAVE_UNIX_SOCKETS
+    case SOCKET_UNIX: return AF_LOCAL;
+#endif
+    default: return -1;
+    }
+}
+
+static int
+socket_type_to_bsd( SocketType  type )
+{
+    switch (type) {
+        case SOCKET_DGRAM:  return SOCK_DGRAM;
+        case SOCKET_STREAM: return SOCK_STREAM;
+        default: return -1;
+    }
+}
+
+static SocketType
+socket_type_from_bsd( int  type )
+{
+    switch (type) {
+        case SOCK_DGRAM:  return SOCKET_DGRAM;
+        case SOCK_STREAM: return SOCKET_STREAM;
+        default:          return (SocketType) -1;
+    }
+}
+
+#if 0
+static int
+socket_type_check( SocketType  type )
+{
+    return (type == SOCKET_DGRAM || type == SOCKET_STREAM);
+}
+#endif
+
+/* socket addresses */
+
+void
+sock_address_init_inet( SockAddress*  a, uint32_t  ip, uint16_t  port )
+{
+    a->family         = SOCKET_INET;
+    a->u.inet.port    = port;
+    a->u.inet.address = ip;
+}
+
+void
+sock_address_init_in6 ( SockAddress*  a, const uint8_t*  ip6[16], uint16_t  port )
+{
+    a->family = SOCKET_IN6;
+    a->u.in6.port = port;
+    memcpy( a->u.in6.address, ip6, sizeof(a->u.in6.address) );
+}
+
+void
+sock_address_init_unix( SockAddress*  a, const char*  path )
+{
+    a->family       = SOCKET_UNIX;
+    a->u._unix.path  = strdup(path ? path : "");
+    a->u._unix.owner = 1;
+}
+
+void  sock_address_done( SockAddress*  a )
+{
+    if (a->family == SOCKET_UNIX && a->u._unix.owner) {
+        a->u._unix.owner = 0;
+        free((char*)a->u._unix.path);
+    }
+}
+
+static char*
+format_char( char*  buf, char*  end, int  c )
+{
+    if (buf >= end)
+        return buf;
+    if (buf+1 == end)
+        c = 0;
+    *buf++ = (char) c;
+    return buf;
+}
+
+static char*
+format_str( char*  buf, char*  end, const char*  str )
+{
+    int  len   = strlen(str);
+    int  avail = end - buf;
+
+    if (len > avail)
+        len = avail;
+
+    memcpy( buf, str, len );
+    buf += len;
+
+    if (buf == end)
+        buf[-1] = 0;
+    else
+        buf[0] = 0;
+
+    return buf;
+}
+
+static char*
+format_unsigned( char*  buf, char*  end, unsigned  val )
+{
+    char  temp[16];
+    int   nn;
+
+    for ( nn = 0; val != 0; nn++ ) {
+        int  rem = val % 10;
+        temp[nn] = '0'+rem;
+        val /= 10;
+    }
+
+    if (nn == 0)
+        temp[nn++] = '0';
+
+    while (nn > 0)
+        buf = format_char(buf, end, temp[--nn]);
+
+    return buf;
+}
+
+static char*
+format_hex( char*  buf, char*  end, unsigned  val, int  ndigits )
+{
+    int   shift = 4*ndigits;
+    static const char   hex[16] = "0123456789abcdef";
+
+    while (shift >= 0) {
+        buf = format_char(buf, end, hex[(val >> shift) & 15]);
+        shift -= 4;
+    }
+    return buf;
+}
+
+static char*
+format_ip4( char*  buf, char*  end, uint32_t  ip )
+{
+    buf = format_unsigned( buf, end, (unsigned)(ip >> 24) );
+    buf = format_char( buf, end, '.');
+    buf = format_unsigned( buf, end, (unsigned)((ip >> 16) & 255));
+    buf = format_char( buf, end, '.');
+    buf = format_unsigned( buf, end, (unsigned)((ip >> 8) & 255));
+    buf = format_char( buf, end, '.');
+    buf = format_unsigned( buf, end, (unsigned)(ip & 255));
+    return buf;
+}
+
+static char*
+format_ip6( char*  buf, char*  end, const uint8_t*  ip6 )
+{
+    int  nn;
+    for (nn = 0; nn < 8; nn++) {
+        int  val = (ip6[0] << 16) | ip6[1];
+        ip6 += 2;
+        if (nn > 0)
+            buf = format_char(buf, end, ':');
+        if (val == 0)
+            continue;
+        buf  = format_hex(buf, end, val, 4);
+    }
+    return buf;
+}
+
+const char*
+sock_address_to_string( const SockAddress*  a )
+{
+    static char buf0[MAX_PATH];
+    char       *buf = buf0, *end = buf + sizeof(buf0);
+
+    switch (a->family) {
+    case SOCKET_INET:
+        buf = format_ip4( buf, end, a->u.inet.address );
+        buf = format_char( buf, end, ':' );
+        buf = format_unsigned( buf, end, (unsigned) a->u.inet.port );
+        break;
+
+    case SOCKET_IN6:
+        buf = format_ip6( buf, end, a->u.in6.address );
+        buf = format_char( buf, end, ':' );
+        buf = format_unsigned( buf, end, (unsigned) a->u.in6.port );
+        break;
+
+    case SOCKET_UNIX:
+        buf = format_str( buf, end, a->u._unix.path );
+        break;
+
+    default:
+        return NULL;
+    }
+
+    return buf0;
+}
+
+int
+sock_address_equal( const SockAddress*  a, const SockAddress*  b )
+{
+    if (a->family != b->family)
+        return 0;
+
+    switch (a->family) {
+    case SOCKET_INET:
+        return (a->u.inet.address == b->u.inet.address &&
+                a->u.inet.port    == b->u.inet.port);
+
+    case SOCKET_IN6:
+        return (!memcmp(a->u.in6.address, b->u.in6.address, 16) &&
+                a->u.in6.port == b->u.in6.port);
+
+    case SOCKET_UNIX:
+        return (!strcmp(a->u._unix.path, b->u._unix.path));
+
+    default:
+        return 0;
+    }
+}
+
+int
+sock_address_get_port( const SockAddress*  a )
+{
+    switch (a->family) {
+    case SOCKET_INET:
+        return a->u.inet.port;
+    case SOCKET_IN6:
+        return a->u.in6.port;
+    default:
+        return -1;
+    }
+}
+
+void
+sock_address_set_port( SockAddress*  a, uint16_t  port )
+{
+    switch (a->family) {
+    case SOCKET_INET:
+        a->u.inet.port = port;
+        break;
+    case SOCKET_IN6:
+        a->u.in6.port = port;
+        break;
+    default:
+        ;
+    }
+}
+
+const char*
+sock_address_get_path( const SockAddress*  a )
+{
+    if (a->family == SOCKET_UNIX)
+        return a->u._unix.path;
+    else
+        return NULL;
+}
+
+int
+sock_address_get_ip( const SockAddress*  a )
+{
+    if (a->family == SOCKET_INET)
+        return a->u.inet.address;
+
+    return -1;
+}
+
+#if 0
+char*
+bufprint_sock_address( char*  p, char*  end, const SockAddress*  a )
+{
+    switch (a->family) {
+    case SOCKET_INET:
+        {
+            uint32_t  ip = a->u.inet.address;
+
+            return bufprint( p, end, "%d.%d.%d.%d:%d",
+                         (ip >> 24) & 255, (ip >> 16) & 255,
+                         (ip >> 8) & 255, ip & 255,
+                         a->u.inet.port );
+        }
+    case SOCKET_IN6:
+        {
+            int             nn     = 0;
+            const char*     column = "";
+            const uint8_t*  tab    = a->u.in6.address;
+            for (nn = 0; nn < 16; nn += 2) {
+                p = bufprint(p, end, "%s%04x", column, (tab[n] << 8) | tab[n+1]);
+                column = ":";
+            }
+            return bufprint(p, end, ":%d", a->u.in6.port);
+        }
+    case SOCKET_UNIX:
+        {
+            return bufprint(p, end, "%s", a->u._unix.path);
+        }
+    default:
+        return p;
+    }
+}
+#endif
+
+int
+sock_address_to_bsd( const SockAddress*  a, void*  paddress, size_t  *psize )
+{
+    switch (a->family) {
+    case SOCKET_INET:
+        {
+            struct sockaddr_in*  dst = (struct sockaddr_in*) paddress;
+
+            *psize = sizeof(*dst);
+
+            memset( paddress, 0, *psize );
+
+            dst->sin_family      = AF_INET;
+            dst->sin_port        = htons(a->u.inet.port);
+            dst->sin_addr.s_addr = htonl(a->u.inet.address);
+        }
+        break;
+
+#if HAVE_IN6_SOCKETS
+    case SOCKET_IN6:
+        {
+            struct sockaddr_in6*  dst = (struct sockaddr_in6*) paddress;
+
+            *psize = sizeof(*dst);
+
+            memset( paddress, 0, *psize );
+
+            dst->sin6_family = AF_INET6;
+            dst->sin6_port   = htons(a->u.in6.port);
+            memcpy( dst->sin6_addr.s6_addr, a->u.in6.address, 16 );
+        }
+        break;
+#endif /* HAVE_IN6_SOCKETS */
+
+#if HAVE_UNIX_SOCKETS
+    case SOCKET_UNIX:
+        {
+            int                  slen = strlen(a->u._unix.path);
+            struct sockaddr_un*  dst = (struct sockaddr_un*) paddress;
+
+            if (slen >= UNIX_PATH_MAX)
+                return -1;
+
+            memset( paddress, 0, sizeof(*dst) );
+
+            dst->sun_family = AF_LOCAL;
+            memcpy( dst->sun_path, a->u._unix.path, slen );
+            dst->sun_path[slen] = 0;
+
+            *psize = (char*)&dst->sun_path[slen+1] - (char*)dst;
+        }
+        break;
+#endif /* HAVE_UNIX_SOCKETS */
+
+    default:
+        return _set_errno(EINVAL);
+    }
+
+    return 0;
+}
+
+int
+sock_address_to_inet( SockAddress*  a, int  *paddr_ip, int  *paddr_port )
+{
+    struct sockaddr   addr;
+    socklen_t         addrlen;
+
+    if (a->family != SOCKET_INET) {
+        return _set_errno(EINVAL);
+    }
+
+    if (sock_address_to_bsd(a, &addr, &addrlen) < 0)
+        return -1;
+
+    *paddr_ip   = ntohl(((struct sockaddr_in*)&addr)->sin_addr.s_addr);
+    *paddr_port = ntohs(((struct sockaddr_in*)&addr)->sin_port);
+
+    return 0;
+}
+
+int
+sock_address_from_bsd( SockAddress*  a, const void*  from, size_t  fromlen )
+{
+    switch (((struct sockaddr*)from)->sa_family) {
+    case AF_INET:
+        {
+            struct sockaddr_in*  src = (struct sockaddr_in*) from;
+
+            if (fromlen < sizeof(*src))
+                return _set_errno(EINVAL);
+
+            a->family         = SOCKET_INET;
+            a->u.inet.port    = ntohs(src->sin_port);
+            a->u.inet.address = ntohl(src->sin_addr.s_addr);
+        }
+        break;
+
+#ifdef HAVE_IN6_SOCKETS
+    case AF_INET6:
+        {
+            struct sockaddr_in6*  src = (struct sockaddr_in6*) from;
+
+            if (fromlen < sizeof(*src))
+                return _set_errno(EINVAL);
+
+            a->family     = SOCKET_IN6;
+            a->u.in6.port = ntohs(src->sin6_port);
+            memcpy(a->u.in6.address, src->sin6_addr.s6_addr, 16);
+        }
+        break;
+#endif
+
+#ifdef HAVE_UNIX_SOCKETS
+    case AF_LOCAL:
+        {
+            struct sockaddr_un*  src = (struct sockaddr_un*) from;
+            char*                end;
+
+            if (fromlen < sizeof(*src))
+                return _set_errno(EINVAL);
+
+            /* check that the path is zero-terminated */
+            end = memchr(src->sun_path, 0, UNIX_PATH_MAX);
+            if (end == NULL)
+                return _set_errno(EINVAL);
+
+            a->family = SOCKET_UNIX;
+            a->u._unix.owner = 1;
+            a->u._unix.path  = strdup(src->sun_path);
+        }
+        break;
+#endif
+
+    default:
+        return _set_errno(EINVAL);
+    }
+    return 0;
+}
+
+
+int
+sock_address_init_resolve( SockAddress*  a, const char*  hostname, uint16_t  port, int  preferIn6 )
+{
+    struct addrinfo   hints[1];
+    struct addrinfo*  res;
+    int               ret;
+
+    memset(hints, 0, sizeof(hints));
+    hints->ai_family   = preferIn6 ? AF_INET6 : AF_UNSPEC;
+
+    if (getaddrinfo(hostname, NULL, hints, &res) < 0) {
+        return _fix_errno();
+    }
+
+    ret = sock_address_from_bsd( a, res->ai_addr, res->ai_addrlen );
+    freeaddrinfo(res);
+
+    /* need to set the port */
+    switch (a->family) {
+    case SOCKET_INET: a->u.inet.port = port; break;
+    case SOCKET_IN6:  a->u.in6.port  = port; break;
+    default: ;
+    }
+
+    return ret;
+}
+
+
+int
+socket_create( SocketFamily  family, SocketType  type )
+{
+    int   ret;
+    int   sfamily = socket_family_to_bsd(family);
+    int   stype   = socket_type_to_bsd(type);
+
+    if (sfamily < 0 || stype < 0) {
+        return _set_errno(EINVAL);
+    }
+
+    QSOCKET_CALL(ret, socket(sfamily, stype, 0));
+    if (ret < 0)
+        return _fix_errno();
+
+    return ret;
+}
+
+
+int
+socket_create_inet( SocketType  type )
+{
+    return socket_create( SOCKET_INET, type );
+}
+
+#if HAVE_IN6_SOCKETS
+int
+socket_create_in6 ( SocketType  type )
+{
+    return socket_create( SOCKET_IN6, type );
+}
+#endif
+
+#if HAVE_UNIX_SOCKETS
+int
+socket_create_unix( SocketType  type )
+{
+    return socket_create( SOCKET_UNIX, type );
+}
+#endif
+
+int  socket_can_read(int  fd)
+{
+#ifdef _WIN32
+    unsigned long  opt;
+
+    if (ioctlsocket(fd, FIONREAD, &opt) < 0)
+        return 0;
+
+    return opt;
+#else
+    int  opt;
+
+    if (ioctl(fd, FIONREAD, &opt) < 0)
+        return 0;
+
+    return opt;
+#endif
+}
+
+#define   SOCKET_CALL(cmd)  \
+    int  ret; \
+    QSOCKET_CALL(ret, (cmd)); \
+    if (ret < 0) \
+        return _fix_errno(); \
+    return ret; \
+
+int
+socket_send(int  fd, const void*  buf, int  buflen)
+{
+    SOCKET_CALL(send(fd, buf, buflen, 0))
+}
+
+int
+socket_send_oob( int  fd, const void*  buf, int  buflen )
+{
+    SOCKET_CALL(send(fd, buf, buflen, MSG_OOB));
+}
+
+int
+socket_sendto(int  fd, const void*  buf, int  buflen, const SockAddress*  to)
+{
+    struct sockaddr   sa;
+    socklen_t         salen;
+
+    if (sock_address_to_bsd(to, &sa, &salen) < 0)
+        return -1;
+
+    SOCKET_CALL(sendto(fd, buf, buflen, 0, &sa, salen));
+}
+
+int
+socket_recv(int  fd, void*  buf, int  len)
+{
+    SOCKET_CALL(recv(fd, buf, len, 0));
+}
+
+int
+socket_recvfrom(int  fd, void*  buf, int  len, SockAddress*  from)
+{
+    struct sockaddr   sa;
+    socklen_t         salen = sizeof(sa);
+    int               ret;
+
+    QSOCKET_CALL(ret,recvfrom(fd,buf,len,0,&sa,&salen));
+    if (ret < 0)
+        return _fix_errno();
+
+    if (sock_address_from_bsd(from, &sa, salen) < 0)
+        return -1;
+
+    return ret;
+}
+
+int
+socket_connect( int  fd, const SockAddress*  address )
+{
+    struct sockaddr   addr;
+    socklen_t         addrlen;
+
+    if (sock_address_to_bsd(address, &addr, &addrlen) < 0)
+        return -1;
+
+    SOCKET_CALL(connect(fd,&addr,addrlen));
+}
+
+int
+socket_bind( int  fd, const SockAddress*  address )
+{
+    struct sockaddr  addr;
+    socklen_t        addrlen;
+
+    if (sock_address_to_bsd(address, &addr, &addrlen) < 0)
+        return -1;
+
+    SOCKET_CALL(bind(fd, &addr, addrlen));
+}
+
+int
+socket_get_address( int  fd, SockAddress*  address )
+{
+    struct sockaddr   addr;
+    socklen_t         addrlen = sizeof(addr);
+    int               ret;
+
+    QSOCKET_CALL(ret, getsockname(fd, &addr, &addrlen));
+    if (ret < 0)
+        return _fix_errno();
+
+    return sock_address_from_bsd(address, &addr, addrlen);
+}
+
+int
+socket_listen( int  fd, int  backlog )
+{
+    SOCKET_CALL(listen(fd, backlog));
+}
+
+int
+socket_accept( int  fd, SockAddress*  address )
+{
+    struct sockaddr   addr;
+    socklen_t         addrlen = sizeof(addr);
+    int               ret;
+
+    QSOCKET_CALL(ret, accept(fd, &addr, &addrlen));
+    if (ret < 0)
+        return _fix_errno();
+
+    if (address) {
+        if (sock_address_from_bsd(address, &addr, addrlen) < 0) {
+            socket_close(ret);
+            return -1;
+        }
+    }
+    return ret;
+}
+
+SocketType socket_get_type(int fd)
+{
+    int  opt    = -1;
+    int  optlen = sizeof(opt);
+    getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&opt, (void*)&optlen );
+
+    return socket_type_from_bsd(opt);
+}
+
+int socket_set_nonblock(int fd)
+{
+#ifdef _WIN32
+    unsigned long opt = 1;
+    return ioctlsocket(fd, FIONBIO, &opt);
+#else
+    int   flags = fcntl(fd, F_GETFL);
+    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+#endif
+}
+
+int socket_set_blocking(int fd)
+{
+#ifdef _WIN32
+    unsigned long opt = 0;
+    return ioctlsocket(fd, FIONBIO, &opt);
+#else
+    int   flags = fcntl(fd, F_GETFL);
+    return fcntl(fd, F_SETFL, flags & ~O_NONBLOCK);
+#endif
+}
+
+static int
+socket_setoption(int  fd, int  domain, int  option, int  _flag)
+{
+#ifdef _WIN32
+    DWORD  flag = (DWORD) _flag;
+#else
+    int    flag = _flag;
+#endif
+    return setsockopt( fd, domain, option, (const char*)&flag, sizeof(flag) );
+}
+
+
+int socket_set_xreuseaddr(int  fd)
+{
+#ifdef _WIN32
+   /* on Windows, SO_REUSEADDR is used to indicate that several programs can
+    * bind to the same port. this is completely different from the Unix
+    * semantics. instead of SO_EXCLUSIVEADDR to ensure that explicitely prevent
+    * this.
+    */
+    return socket_setoption(fd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, 1);
+#else
+    return socket_setoption(fd, SOL_SOCKET, SO_REUSEADDR, 1);
+#endif
+}
+
+
+int socket_set_oobinline(int  fd)
+{
+    return socket_setoption(fd, SOL_SOCKET, SO_OOBINLINE, 1);
+}
+
+
+int  socket_set_nodelay(int  fd)
+{
+    return socket_setoption(fd, IPPROTO_TCP, TCP_NODELAY, 1);
+}
+
+
+#ifdef _WIN32
+#include <stdlib.h>
+
+static void socket_cleanup(void)
+{
+    WSACleanup();
+}
+
+int socket_init(void)
+{
+    WSADATA Data;
+    int ret, err;
+
+    ret = WSAStartup(MAKEWORD(2,2), &Data);
+    if (ret != 0) {
+        err = WSAGetLastError();
+        return -1;
+    }
+    atexit(socket_cleanup);
+    return 0;
+}
+
+#else /* !_WIN32 */
+
+int socket_init(void)
+{
+   return 0;   /* nothing to do on Unix */
+}
+
+#endif /* !_WIN32 */
+
+#ifdef _WIN32
+
+static void
+socket_close_handler( void*  _fd )
+{
+    int   fd = (int)_fd;
+    int   ret;
+    char  buff[64];
+
+    /* we want to drain the read side of the socket before closing it */
+    do {
+        ret = recv( fd, buff, sizeof(buff), 0 );
+    } while (ret < 0 && WSAGetLastError() == WSAEINTR);
+
+    if (ret < 0 && WSAGetLastError() == EWOULDBLOCK)
+        return;
+
+    qemu_set_fd_handler( fd, NULL, NULL, NULL );
+    closesocket( fd );
+}
+
+void
+socket_close( int  fd )
+{
+    int  old_errno = errno;
+
+    shutdown( fd, SD_BOTH );
+    /* we want to drain the socket before closing it */
+    qemu_set_fd_handler( fd, socket_close_handler, NULL, (void*)fd );
+
+    errno = old_errno;
+}
+
+#else /* !_WIN32 */
+
+#include <unistd.h>
+
+void
+socket_close( int  fd )
+{
+    int  old_errno = errno;
+
+    shutdown( fd, SHUT_RDWR );
+    close( fd );
+
+    errno = old_errno;
+}
+
+#endif /* !_WIN32 */
+
+
+static int
+socket_bind_server( int  s, const SockAddress*  to, SocketType  type )
+{
+    socket_set_xreuseaddr(s);
+
+    if (socket_bind(s, to) < 0) {
+        dprint("could not bind server socket address %s: %s", 
+                sock_address_to_string(to), errno_str);
+        goto FAIL;
+    }
+
+    if (type == SOCKET_STREAM) {
+        if (socket_listen(s, 4) < 0) {
+            dprint("could not listen server socket %s: %s",
+                   sock_address_to_string(to), errno_str);
+            goto FAIL;
+        }
+    }
+    return  s;
+
+FAIL:
+    socket_close(s);
+    return -1;
+}
+
+
+static int
+socket_connect_client( int  s, const SockAddress*  to )
+{
+    if (socket_connect(s, to) < 0) {
+        dprint( "could not connect client socket to %s: %s\n",
+                sock_address_to_string(to), errno_str );
+        socket_close(s);
+        return -1;
+    }
+
+    socket_set_nonblock( s );
+    return s;
+}
+
+
+static int
+socket_in_server( int  address, int  port, SocketType  type )
+{
+    SockAddress  addr;
+    int          s;
+
+    sock_address_init_inet( &addr, address, port );
+    s = socket_create_inet( type );
+    if (s < 0)
+        return -1;
+
+    return socket_bind_server( s, &addr, type );
+}
+
+
+static int
+socket_in_client( SockAddress*  to, SocketType  type )
+{
+    int  s;
+
+    s = socket_create_inet( type );
+    if (s < 0) return -1;
+
+    return socket_connect_client( s, to );
+}
+
+
+int
+socket_loopback_server( int  port, SocketType  type )
+{
+    return socket_in_server( SOCK_ADDRESS_INET_LOOPBACK, port, type );
+}
+
+int
+socket_loopback_client( int  port, SocketType  type )
+{
+    SockAddress  addr;
+
+    sock_address_init_inet( &addr, SOCK_ADDRESS_INET_LOOPBACK, port );
+    return socket_in_client( &addr, type );
+}
+
+
+int
+socket_network_client( const char*  host, int  port, SocketType  type )
+{
+    SockAddress  addr;
+
+    if (sock_address_init_resolve( &addr, host, port, 0) < 0)
+        return -1;
+
+    return socket_in_client( &addr, type );
+}
+
+
+int
+socket_anyaddr_server( int  port, SocketType  type )
+{
+    return socket_in_server( SOCK_ADDRESS_INET_ANY, port, type );
+}
+
+int
+socket_accept_any( int  server_fd )
+{
+    int  fd;
+
+    QSOCKET_CALL(fd, accept( server_fd, NULL, 0 ));
+    if (fd < 0) {
+        dprint( "could not accept client connection from fd %d: %s",
+                server_fd, errno_str );
+        return -1;
+    }
+
+    /* set to non-blocking */
+    socket_set_nonblock( fd );
+    return fd;
+}
+
+
+#if HAVE_UNIX_SOCKETS
+
+int
+socket_unix_server( const char*  name, SocketType  type )
+{
+    SockAddress   addr;
+    int           s, ret;
+
+    s = socket_create_unix( type );
+    if (s < 0)
+        return -1;
+
+    sock_address_init_unix( &addr, name );
+
+    do {
+        ret = unlink( name );
+    } while (ret < 0 && errno == EINTR);
+
+    ret = socket_bind_server( s, &addr, type );
+
+    sock_address_done( &addr );
+    return ret;
+}
+
+int
+socket_unix_client( const char*  name, SocketType  type )
+{
+    SockAddress   addr;
+    int           s, ret;
+
+    s = socket_create_unix(type);
+    if (s < 0)
+        return -1;
+
+    sock_address_init_unix( &addr, name );
+
+    ret =  socket_connect_client( s, &addr );
+
+    sock_address_done( &addr );
+    return ret;
+}
+
+#endif /* HAVE_UNIX_SOCKETS */
+
+
+
+int
+socket_pair(int *fd1, int *fd2)
+{
+#ifndef _WIN32
+    int   fds[2];
+    int   ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+
+    if (!ret) {
+        socket_set_nonblock(fds[0]);
+        socket_set_nonblock(fds[1]);
+        *fd1 = fds[0];
+        *fd2 = fds[1];
+    }
+    return ret;
+#else /* _WIN32 */
+    /* on Windows, select() only works with network sockets, which
+     * means we absolutely cannot use Win32 PIPEs to implement
+     * socket pairs with the current event loop implementation.
+     * We're going to do like Cygwin: create a random pair
+     * of localhost TCP sockets and connect them together
+     */
+    int                 s0, s1, s2, port;
+    struct sockaddr_in  sockin;
+    socklen_t           len;
+
+    /* first, create the 'server' socket.
+     * a port number of 0 means 'any port between 1024 and 5000.
+     * see Winsock bind() documentation for details */
+    s0 = socket_loopback_server( 0, SOCK_STREAM );
+    if (s0 < 0)
+        return -1;
+
+    /* now connect a client socket to it, we first need to
+     * extract the server socket's port number */
+    len = sizeof sockin;
+    if (getsockname(s0, (struct sockaddr*) &sockin, &len) < 0) {
+        closesocket (s0);
+        return -1;
+    }
+
+    port = ntohs(sockin.sin_port);
+    s2   = socket_loopback_client( port, SOCK_STREAM );
+    if (s2 < 0) {
+        closesocket(s0);
+        return -1;
+    }
+
+    /* we need to accept the connection on the server socket
+     * this will create the second socket for the pair
+     */
+    len = sizeof sockin;
+    s1  = accept(s0, (struct sockaddr*) &sockin, &len);
+    if (s1 == INVALID_SOCKET) {
+        closesocket (s0);
+        closesocket (s2);
+        return -1;
+    }
+    socket_set_nonblock(s1);
+
+    /* close server socket */
+    closesocket(s0);
+    *fd1 = s1;
+    *fd2 = s2;
+    return 0;
+#endif /* _WIN32 */
+}
+
+
+
+int
+socket_mcast_inet_add_membership( int  s, uint32_t  ip )
+{
+    struct ip_mreq imr;
+
+    imr.imr_multiaddr.s_addr = htonl(ip);
+    imr.imr_interface.s_addr = htonl(INADDR_ANY);
+
+    if ( setsockopt( s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                     (const char *)&imr, 
+                     sizeof(struct ip_mreq)) < 0 )
+    {
+        return _fix_errno();
+    }
+    return 0;
+}
+
+int
+socket_mcast_inet_drop_membership( int  s, uint32_t  ip )
+{
+    struct ip_mreq imr;
+
+    imr.imr_multiaddr.s_addr = htonl(ip);
+    imr.imr_interface.s_addr = htonl(INADDR_ANY);
+
+    if ( setsockopt( s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+                     (const char *)&imr, 
+                     sizeof(struct ip_mreq)) < 0 )
+    {
+        return _fix_errno();
+    }
+    return 0;
+}
+
+int
+socket_mcast_inet_set_loop( int  s, int  enabled )
+{
+    return socket_setoption( s, IPPROTO_IP, IP_MULTICAST_LOOP, !!enabled );
+}
+
+int
+socket_mcast_inet_set_ttl( int  s, int  ttl )
+{
+    return socket_setoption( s, IPPROTO_IP, IP_MULTICAST_TTL, ttl );
+}
+
+
+char*
+host_name( void )
+{
+    static char buf[256];  /* 255 is the max host name length supported by DNS */
+    int         ret;
+
+    QSOCKET_CALL(ret, gethostname(buf, sizeof(buf)));
+
+    if (ret < 0)
+        return "localhost";
+    else
+        return buf;
+}
diff --git a/sockets.h b/sockets.h
new file mode 100644
index 0000000..274cf32
--- /dev/null
+++ b/sockets.h
@@ -0,0 +1,339 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+/* headers to use the BSD sockets */
+#ifndef QEMU_SOCKET_H
+#define QEMU_SOCKET_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <errno.h>
+
+/* we're going to hide the implementation details of sockets behind
+ * a simple wrapper interface declared here.
+ *
+ * all socket operations set the global 'errno' variable on error.
+ * this is unlike Winsock which instead modifies another internal
+ * variable accessed through WSAGetLastError() and WSASetLastError()
+ */
+
+/* the wrapper will convert any Winsock error message into an errno
+ * code for you. There are however a few standard Unix error codes
+ * that are not defined by the MS C library headers, so we add them
+ * here. We use the official Winsock error codes, which are documented
+ * even though we don't want to include the Winsock headers
+ */
+#ifdef _WIN32
+#  ifndef EINTR
+#    define EINTR        10004
+#  endif
+#  ifndef EWOULDBLOCK
+#    define EWOULDBLOCK  10035
+#  endif
+#  ifndef EINPROGRESS
+#    define EINPROGRESS  10036
+#  endif
+#  ifndef EALREADY
+#    define EALREADY     10037
+#  endif
+#  ifndef EDESTADDRREQ
+#    define EDESTADDRREQ 10039
+#  endif
+#  ifndef EMSGSIZE
+#    define EMSGSIZE     10040
+#  endif
+#  ifndef EPROTOTYPE
+#    define EPROTOTYPE   10041
+#  endif
+#  ifndef ENOPROTOOPT
+#    define ENOPROTOOPT  10042
+#  endif
+#  ifndef EADDRINUSE
+#    define EADDRINUSE   10048
+#  endif
+#  ifndef EADDRNOTAVAIL
+#    define EADDRNOTAVAIL 10049
+#  endif
+#  ifndef ENETDOWN
+#    define ENETDOWN     10050
+#  endif
+#  ifndef ENETUNREACH
+#    define ENETUNREACH  10051
+#  endif
+#  ifndef ENETRESET
+#    define ENETRESET    10052
+#  endif
+#  ifndef ECONNABORTED
+#    define ECONNABORTED 10053
+#  endif
+#  ifndef ECONNRESET
+#    define ECONNRESET   10054
+#  endif
+#  ifndef ENOBUFS
+#    define ENOBUFS      10055
+#  endif
+#  ifndef EISCONN
+#    define EISCONN      10056
+#  endif
+#  ifndef ENOTCONN
+#    define ENOTCONN     10057
+#  endif
+#  ifndef ESHUTDOWN
+#    define ESHUTDOWN     10058
+#  endif
+#  ifndef ETOOMANYREFS
+#    define ETOOMANYREFS  10059
+#  endif
+#  ifndef ETIMEDOUT
+#    define ETIMEDOUT     10060
+#  endif
+#  ifndef ECONNREFUSED
+#    define ECONNREFUSED  10061
+#  endif
+#  ifndef ELOOP
+#    define ELOOP         10062
+#  endif
+#  ifndef EHOSTDOWN
+#    define EHOSTDOWN     10064
+#  endif
+#  ifndef EHOSTUNREACH
+#    define EHOSTUNREACH  10065
+#  endif
+#endif /* _WIN32 */
+
+/* Define 'errno_str' as a handy macro to return the string
+ * corresponding to a given errno code. On Unix, this is
+ * equivalent to strerror(errno), but on Windows, this will
+ * take care of Winsock-originated errors as well.
+ */
+#ifdef _WIN32
+  extern const char*  _errno_str(void);
+#  define  errno_str   _errno_str()
+#else
+#  define  errno_str   strerror(errno)
+#endif
+
+/* always enable IPv6 sockets for now.
+ * the QEMU internal router is not capable of
+ * supporting them, but we plan to replace it
+ * with something better in the future.
+ */
+#define  HAVE_IN6_SOCKETS   1
+
+/* Unix sockets are not available on Win32 */
+#ifndef _WIN32
+#  define  HAVE_UNIX_SOCKETS  1
+#endif
+
+/* initialize the socket sub-system. this must be called before
+ * using any of the declarations below.
+ */
+int  socket_init( void );
+
+/* return the name of the current host */
+char*  host_name( void );
+
+/* supported socket types */
+typedef enum {
+    SOCKET_DGRAM = 0,
+    SOCKET_STREAM
+} SocketType;
+
+/* supported socket families */
+typedef enum {
+    SOCKET_UNSPEC,
+    SOCKET_INET,
+    SOCKET_IN6,
+    SOCKET_UNIX
+} SocketFamily;
+
+/* Generic socket address structure. Note that for Unix
+ * sockets, the path is stored in a heap-allocated block,
+ * unless the 'owner' field is cleared. If this is the case,
+ */
+typedef struct {
+    SocketFamily  family;
+    union {
+        struct {
+            uint16_t   port;
+            uint32_t   address;
+        } inet;
+        struct {
+            uint16_t   port;
+            uint8_t    address[16];
+        } in6;
+        struct {
+            int          owner;
+            const char*  path;
+        } _unix;
+    } u;
+} SockAddress;
+
+#define  SOCK_ADDRESS_INET_ANY       0x00000000
+#define  SOCK_ADDRESS_INET_LOOPBACK  0x7f000001
+
+/* initialize a new IPv4 socket address, the IP address and port are
+ * in host endianess.
+ */
+void  sock_address_init_inet( SockAddress*  a, uint32_t  ip, uint16_t  port );
+
+/* Initialize an IPv6 socket address, the address is in network order
+ * and the port in host endianess.
+ */
+#if HAVE_IN6_SOCKETS
+void  sock_address_init_in6 ( SockAddress*  a, const uint8_t*  ip6[16], uint16_t  port );
+#endif
+
+/* Intialize a Unix socket address, this will copy the 'path' string into the
+ * heap. You need to call sock_address_done() to release the copy
+ */
+#if HAVE_UNIX_SOCKETS
+void  sock_address_init_unix( SockAddress*  a, const char*  path );
+#endif
+
+/* Finalize a socket address, only needed for now for Unix addresses */
+void  sock_address_done( SockAddress*  a );
+
+int   sock_address_equal( const SockAddress*  a, const SockAddress*  b );
+
+/* THIS SHOULD DISAPPEAR SOON - TRANSITIONAL HELPER */
+int   sock_address_to_bsd( const SockAddress*  a, void*  sa, size_t* salen );
+int   sock_address_from_bsd( SockAddress*  a, const void*  sa, size_t  salen );
+int   sock_address_to_inet( SockAddress*  a, int  *paddr_ip, int  *paddr_port );
+
+/* return a static string describing the address */
+const char*  sock_address_to_string( const SockAddress*  a );
+
+/* return the port number of a given socket address, or -1 if it's a Unix one */
+int   sock_address_get_port( const SockAddress*  a );
+
+/* set the port number of a given socket address, don't do anything for Unix ones */
+void  sock_address_set_port( SockAddress*  a, uint16_t  port );
+
+/* return the path of a given Unix socket, returns NULL for non-Unix ones */
+const char*  sock_address_get_path( const SockAddress*  a );
+
+/* return the inet address, or -1 if it's not SOCKET_INET */
+int   sock_address_get_ip( const SockAddress*  a );
+
+/* bufprint a socket address into a human-readable string */
+char* bufprint_sock_address( char*  p, char*  end, const SockAddress*  a );
+
+/* resolve a hostname or decimal IPv4/IPv6 address into a socket address.
+ * returns 0 on success, or -1 on failure */
+int   sock_address_init_resolve( SockAddress*  a, const char*  hostname, uint16_t  port, int  preferIn6 ); 
+
+/* create a new socket, return the socket number of -1 on failure */
+int  socket_create( SocketFamily  family, SocketType  type );
+
+/* create a new socket intended for IPv4 communication. returns the socket number,
+ * or -1 on failure.
+ */
+int   socket_create_inet( SocketType  type );
+
+/* create a new socket intended for IPv6 communication. returns the socket number,
+ * or -1 on failure.
+ */
+#if HAVE_IN6_SOCKETS
+int   socket_create_in6 ( SocketType  type );
+#endif
+
+/* create a unix/local domain socket. returns the socket number,
+ * or -1 on failure.
+ */
+#if HAVE_UNIX_SOCKETS
+int   socket_create_unix( SocketType  type );
+#endif
+
+/* return the type of a given socket */
+SocketType  socket_get_type(int  fd);
+
+/* set SO_REUSEADDR on Unix, SO_EXCLUSIVEADDR on Windows */
+int  socket_set_xreuseaddr(int  fd);
+
+/* set socket in non-blocking mode */
+int  socket_set_nonblock(int fd);
+
+/* set socket in blocking mode */
+int  socket_set_blocking(int fd);
+
+/* disable the TCP Nagle algorithm for lower latency */
+int  socket_set_nodelay(int fd);
+
+/* send OOB data inline for this socket */
+int  socket_set_oobinline(int  fd);
+
+/* close an opened socket. Note that this is unlike the Unix 'close' because:
+ * - it will properly shutdown the socket in the background
+ * - it does not modify errno
+ */
+void  socket_close( int  fd );
+
+/* the following functions are equivalent to the BSD sockets ones
+ */
+int   socket_recv    ( int  fd, void*  buf, int  buflen );
+int   socket_recvfrom( int  fd, void*  buf, int  buflen, SockAddress*  from );
+
+int   socket_send  ( int  fd, const void*  buf, int  buflen );
+int   socket_send_oob( int  fd, const void*  buf, int  buflen );
+int   socket_sendto( int  fd, const void*  buf, int  buflen, const SockAddress*  to );
+
+int   socket_connect( int  fd, const SockAddress*  address );
+int   socket_bind( int  fd, const SockAddress*  address );
+int   socket_get_address( int  fd, SockAddress*  address );
+int   socket_listen( int  fd, int  backlog );
+int   socket_accept( int  fd, SockAddress*  address );
+
+/* returns the number of bytes that can be read from a socket */
+int   socket_can_read( int  fd );
+
+/* this call creates a pair of non-blocking sockets connected
+ * to each other. this is equivalent to calling the Unix function:
+ * socketpair(AF_LOCAL,SOCK_STREAM,0,&fds)
+ *
+ * on Windows, this will use a pair of TCP loopback sockets instead
+ * returns 0 on success, -1 on error.
+ */
+int  socket_pair(int  *fd1, int *fd2);
+
+/* create a server socket listening on the host's loopback interface */
+int  socket_loopback_server( int  port, SocketType  type );
+
+/* connect to a port on the host's loopback interface */
+int  socket_loopback_client( int  port, SocketType  type );
+
+/* create a server socket listening to a Unix domain path */
+#if HAVE_UNIX_SOCKETS
+int  socket_unix_server( const char*  name, SocketType  type );
+#endif
+
+/* create a Unix sockets and connects it to a Unix server */
+#if HAVE_UNIX_SOCKETS
+int  socket_unix_client( const char*  name, SocketType  type );
+#endif
+
+/* create an IPv4 client socket and connect it to a given host */
+int  socket_network_client( const char*  host, int  port, SocketType  type );
+
+/* create an IPv4 socket and binds it to a given port of the host's interface */
+int  socket_anyaddr_server( int  port, SocketType  type );
+
+/* accept a connection from the host's any interface, return the new socket
+ * descriptor or -1 */
+int  socket_accept_any( int  server_fd );
+
+
+int  socket_mcast_inet_add_membership( int  s, uint32_t  ip );
+int  socket_mcast_inet_drop_membership( int  s, uint32_t  ip );
+int  socket_mcast_inet_set_loop( int  s, int  enabled );
+int  socket_mcast_inet_set_ttl( int  s, int  ttl );
+
+#endif /* QEMU_SOCKET_H */
diff --git a/softmmu-semi.h b/softmmu-semi.h
new file mode 100644
index 0000000..8bf96f4
--- /dev/null
+++ b/softmmu-semi.h
@@ -0,0 +1,70 @@
+/*
+ * Helper routines to provide target memory access for semihosting
+ * syscalls in system emulation mode.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ *
+ * This code is licenced under the GPL
+ */
+
+static inline uint32_t softmmu_tget32(CPUState *env, uint32_t addr)
+{
+    uint32_t val;
+
+    cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 0);
+    return tswap32(val);
+}
+static inline uint32_t softmmu_tget8(CPUState *env, uint32_t addr)
+{
+    uint8_t val;
+
+    cpu_memory_rw_debug(env, addr, &val, 1, 0);
+    return val;
+}
+
+#define get_user_u32(arg, p) ({ arg = softmmu_tget32(env, p) ; 0; })
+#define get_user_u8(arg, p) ({ arg = softmmu_tget8(env, p) ; 0; })
+#define get_user_ual(arg, p) get_user_u32(arg, p)
+
+static inline void softmmu_tput32(CPUState *env, uint32_t addr, uint32_t val)
+{
+    val = tswap32(val);
+    cpu_memory_rw_debug(env, addr, (uint8_t *)&val, 4, 1);
+}
+#define put_user_u32(arg, p) ({ softmmu_tput32(env, p, arg) ; 0; })
+#define put_user_ual(arg, p) put_user_u32(arg, p)
+
+static void *softmmu_lock_user(CPUState *env, uint32_t addr, uint32_t len,
+                               int copy)
+{
+    char *p;
+    /* TODO: Make this something that isn't fixed size.  */
+    p = malloc(len);
+    if (copy)
+        cpu_memory_rw_debug(env, addr, p, len, 0);
+    return p;
+}
+#define lock_user(type, p, len, copy) softmmu_lock_user(env, p, len, copy)
+static char *softmmu_lock_user_string(CPUState *env, uint32_t addr)
+{
+    char *p;
+    char *s;
+    uint8_t c;
+    /* TODO: Make this something that isn't fixed size.  */
+    s = p = malloc(1024);
+    do {
+        cpu_memory_rw_debug(env, addr, &c, 1, 0);
+        addr++;
+        *(p++) = c;
+    } while (c);
+    return s;
+}
+#define lock_user_string(p) softmmu_lock_user_string(env, p)
+static void softmmu_unlock_user(CPUState *env, void *p, target_ulong addr,
+                                target_ulong len)
+{
+    if (len)
+        cpu_memory_rw_debug(env, addr, p, len, 1);
+    free(p);
+}
+#define unlock_user(s, args, len) softmmu_unlock_user(env, s, args, len)
diff --git a/softmmu_defs.h b/softmmu_defs.h
new file mode 100644
index 0000000..e38bb75
--- /dev/null
+++ b/softmmu_defs.h
@@ -0,0 +1,22 @@
+#ifndef SOFTMMU_DEFS_H
+#define SOFTMMU_DEFS_H
+
+uint8_t REGPARM __ldb_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stb_mmu(target_ulong addr, uint8_t val, int mmu_idx);
+uint16_t REGPARM __ldw_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stw_mmu(target_ulong addr, uint16_t val, int mmu_idx);
+uint32_t REGPARM __ldl_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stl_mmu(target_ulong addr, uint32_t val, int mmu_idx);
+uint64_t REGPARM __ldq_mmu(target_ulong addr, int mmu_idx);
+void REGPARM __stq_mmu(target_ulong addr, uint64_t val, int mmu_idx);
+
+uint8_t REGPARM __ldb_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stb_cmmu(target_ulong addr, uint8_t val, int mmu_idx);
+uint16_t REGPARM __ldw_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stw_cmmu(target_ulong addr, uint16_t val, int mmu_idx);
+uint32_t REGPARM __ldl_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stl_cmmu(target_ulong addr, uint32_t val, int mmu_idx);
+uint64_t REGPARM __ldq_cmmu(target_ulong addr, int mmu_idx);
+void REGPARM __stq_cmmu(target_ulong addr, uint64_t val, int mmu_idx);
+
+#endif
diff --git a/softmmu_exec.h b/softmmu_exec.h
new file mode 100644
index 0000000..9cc4535
--- /dev/null
+++ b/softmmu_exec.h
@@ -0,0 +1,115 @@
+/* Common softmmu definitions and inline routines.  */
+
+/* XXX: find something cleaner.
+ * Furthermore, this is false for 64 bits targets
+ */
+#define ldul_user       ldl_user
+#define ldul_kernel     ldl_kernel
+#define ldul_hypv       ldl_hypv
+#define ldul_executive  ldl_executive
+#define ldul_supervisor ldl_supervisor
+
+#include "softmmu_defs.h"
+
+#define ACCESS_TYPE 0
+#define MEMSUFFIX MMU_MODE0_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#define ACCESS_TYPE 1
+#define MEMSUFFIX MMU_MODE1_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#if (NB_MMU_MODES >= 3)
+
+#define ACCESS_TYPE 2
+#define MEMSUFFIX MMU_MODE2_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#if (NB_MMU_MODES >= 4)
+
+#define ACCESS_TYPE 3
+#define MEMSUFFIX MMU_MODE3_SUFFIX
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#if (NB_MMU_MODES > 4)
+#error "NB_MMU_MODES > 4 is not supported for now"
+#endif /* (NB_MMU_MODES > 4) */
+#endif /* (NB_MMU_MODES == 4) */
+#endif /* (NB_MMU_MODES >= 3) */
+
+/* these access are slower, they must be as rare as possible */
+#define ACCESS_TYPE (NB_MMU_MODES)
+#define MEMSUFFIX _data
+#define DATA_SIZE 1
+#include "softmmu_header.h"
+
+#define DATA_SIZE 2
+#include "softmmu_header.h"
+
+#define DATA_SIZE 4
+#include "softmmu_header.h"
+
+#define DATA_SIZE 8
+#include "softmmu_header.h"
+#undef ACCESS_TYPE
+#undef MEMSUFFIX
+
+#define ldub(p) ldub_data(p)
+#define ldsb(p) ldsb_data(p)
+#define lduw(p) lduw_data(p)
+#define ldsw(p) ldsw_data(p)
+#define ldl(p) ldl_data(p)
+#define ldq(p) ldq_data(p)
+
+#define stb(p, v) stb_data(p, v)
+#define stw(p, v) stw_data(p, v)
+#define stl(p, v) stl_data(p, v)
+#define stq(p, v) stq_data(p, v)
diff --git a/softmmu_header.h b/softmmu_header.h
new file mode 100644
index 0000000..9649717
--- /dev/null
+++ b/softmmu_header.h
@@ -0,0 +1,345 @@
+/*
+ *  Software MMU support
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#if DATA_SIZE == 8
+#define SUFFIX q
+#define USUFFIX q
+#define DATA_TYPE uint64_t
+#elif DATA_SIZE == 4
+#define SUFFIX l
+#define USUFFIX l
+#define DATA_TYPE uint32_t
+#elif DATA_SIZE == 2
+#define SUFFIX w
+#define USUFFIX uw
+#define DATA_TYPE uint16_t
+#define DATA_STYPE int16_t
+#elif DATA_SIZE == 1
+#define SUFFIX b
+#define USUFFIX ub
+#define DATA_TYPE uint8_t
+#define DATA_STYPE int8_t
+#else
+#error unsupported data size
+#endif
+
+#if ACCESS_TYPE < (NB_MMU_MODES)
+
+#define CPU_MMU_INDEX ACCESS_TYPE
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == (NB_MMU_MODES)
+
+#define CPU_MMU_INDEX (cpu_mmu_index(env))
+#define MMUSUFFIX _mmu
+
+#elif ACCESS_TYPE == (NB_MMU_MODES + 1)
+
+#define CPU_MMU_INDEX (cpu_mmu_index(env))
+#define MMUSUFFIX _cmmu
+
+#else
+#error invalid ACCESS_TYPE
+#endif
+
+#if DATA_SIZE == 8
+#define RES_TYPE uint64_t
+#else
+#define RES_TYPE int
+#endif
+
+#if ACCESS_TYPE == (NB_MMU_MODES + 1)
+#define ADDR_READ addr_code
+#else
+#define ADDR_READ addr_read
+#endif
+
+#if (DATA_SIZE <= 4) && (TARGET_LONG_BITS == 32) && defined(__i386__) && \
+    (ACCESS_TYPE < NB_MMU_MODES) && defined(ASM_SOFTMMU)
+
+static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+    int res;
+
+    asm volatile ("movl %1, %%edx\n"
+                  "movl %1, %%eax\n"
+                  "shrl %3, %%edx\n"
+                  "andl %4, %%eax\n"
+                  "andl %2, %%edx\n"
+                  "leal %5(%%edx, %%ebp), %%edx\n"
+                  "cmpl (%%edx), %%eax\n"
+                  "movl %1, %%eax\n"
+                  "je 1f\n"
+                  "movl %6, %%edx\n"
+                  "call %7\n"
+                  "movl %%eax, %0\n"
+                  "jmp 2f\n"
+                  "1:\n"
+                  "addl 12(%%edx), %%eax\n"
+#if DATA_SIZE == 1
+                  "movzbl (%%eax), %0\n"
+#elif DATA_SIZE == 2
+                  "movzwl (%%eax), %0\n"
+#elif DATA_SIZE == 4
+                  "movl (%%eax), %0\n"
+#else
+#error unsupported size
+#endif
+                  "2:\n"
+                  : "=r" (res)
+                  : "r" (ptr),
+                  "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
+                  "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
+                  "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
+                  "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MMU_INDEX][0].addr_read)),
+                  "i" (CPU_MMU_INDEX),
+                  "m" (*(uint8_t *)&glue(glue(__ld, SUFFIX), MMUSUFFIX))
+                  : "%eax", "%ecx", "%edx", "memory", "cc");
+    return res;
+}
+
+#if DATA_SIZE <= 2
+static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+    int res;
+
+    asm volatile ("movl %1, %%edx\n"
+                  "movl %1, %%eax\n"
+                  "shrl %3, %%edx\n"
+                  "andl %4, %%eax\n"
+                  "andl %2, %%edx\n"
+                  "leal %5(%%edx, %%ebp), %%edx\n"
+                  "cmpl (%%edx), %%eax\n"
+                  "movl %1, %%eax\n"
+                  "je 1f\n"
+                  "movl %6, %%edx\n"
+                  "call %7\n"
+#if DATA_SIZE == 1
+                  "movsbl %%al, %0\n"
+#elif DATA_SIZE == 2
+                  "movswl %%ax, %0\n"
+#else
+#error unsupported size
+#endif
+                  "jmp 2f\n"
+                  "1:\n"
+                  "addl 12(%%edx), %%eax\n"
+#if DATA_SIZE == 1
+                  "movsbl (%%eax), %0\n"
+#elif DATA_SIZE == 2
+                  "movswl (%%eax), %0\n"
+#else
+#error unsupported size
+#endif
+                  "2:\n"
+                  : "=r" (res)
+                  : "r" (ptr),
+                  "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
+                  "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
+                  "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
+                  "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MMU_INDEX][0].addr_read)),
+                  "i" (CPU_MMU_INDEX),
+                  "m" (*(uint8_t *)&glue(glue(__ld, SUFFIX), MMUSUFFIX))
+                  : "%eax", "%ecx", "%edx", "memory", "cc");
+    return res;
+}
+#endif
+
+static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v)
+{
+    asm volatile ("movl %0, %%edx\n"
+                  "movl %0, %%eax\n"
+                  "shrl %3, %%edx\n"
+                  "andl %4, %%eax\n"
+                  "andl %2, %%edx\n"
+                  "leal %5(%%edx, %%ebp), %%edx\n"
+                  "cmpl (%%edx), %%eax\n"
+                  "movl %0, %%eax\n"
+                  "je 1f\n"
+#if DATA_SIZE == 1
+                  "movzbl %b1, %%edx\n"
+#elif DATA_SIZE == 2
+                  "movzwl %w1, %%edx\n"
+#elif DATA_SIZE == 4
+                  "movl %1, %%edx\n"
+#else
+#error unsupported size
+#endif
+                  "movl %6, %%ecx\n"
+                  "call %7\n"
+                  "jmp 2f\n"
+                  "1:\n"
+                  "addl 8(%%edx), %%eax\n"
+#if DATA_SIZE == 1
+                  "movb %b1, (%%eax)\n"
+#elif DATA_SIZE == 2
+                  "movw %w1, (%%eax)\n"
+#elif DATA_SIZE == 4
+                  "movl %1, (%%eax)\n"
+#else
+#error unsupported size
+#endif
+                  "2:\n"
+                  :
+                  : "r" (ptr),
+#if DATA_SIZE == 1
+                  "q" (v),
+#else
+                  "r" (v),
+#endif
+                  "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
+                  "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
+                  "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
+                  "m" (*(uint32_t *)offsetof(CPUState, tlb_table[CPU_MMU_INDEX][0].addr_write)),
+                  "i" (CPU_MMU_INDEX),
+                  "m" (*(uint8_t *)&glue(glue(__st, SUFFIX), MMUSUFFIX))
+                  : "%eax", "%ecx", "%edx", "memory", "cc");
+}
+
+#else
+
+/* generic load/store macros */
+
+static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+    int page_index;
+    RES_TYPE res;
+    target_ulong addr;
+    unsigned long physaddr;
+    int mmu_idx;
+
+    addr = ptr;
+    page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    mmu_idx = CPU_MMU_INDEX;
+    if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
+                 (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
+        res = glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx);
+    } else {
+        physaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+        res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)physaddr);
+    }
+    return res;
+}
+
+#if DATA_SIZE <= 2
+static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
+{
+    int res, page_index;
+    target_ulong addr;
+    unsigned long physaddr;
+    int mmu_idx;
+
+    addr = ptr;
+    page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    mmu_idx = CPU_MMU_INDEX;
+    if (unlikely(env->tlb_table[mmu_idx][page_index].ADDR_READ !=
+                 (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
+        res = (DATA_STYPE)glue(glue(__ld, SUFFIX), MMUSUFFIX)(addr, mmu_idx);
+    } else {
+        physaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+        res = glue(glue(lds, SUFFIX), _raw)((uint8_t *)physaddr);
+    }
+    return res;
+}
+#endif
+
+#if ACCESS_TYPE != (NB_MMU_MODES + 1)
+
+/* generic store macro */
+
+static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE v)
+{
+    int page_index;
+    target_ulong addr;
+    unsigned long physaddr;
+    int mmu_idx;
+
+    addr = ptr;
+    page_index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    mmu_idx = CPU_MMU_INDEX;
+    if (unlikely(env->tlb_table[mmu_idx][page_index].addr_write !=
+                 (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) {
+        glue(glue(__st, SUFFIX), MMUSUFFIX)(addr, v, mmu_idx);
+    } else {
+        physaddr = addr + env->tlb_table[mmu_idx][page_index].addend;
+        glue(glue(st, SUFFIX), _raw)((uint8_t *)physaddr, v);
+    }
+}
+
+#endif /* ACCESS_TYPE != (NB_MMU_MODES + 1) */
+
+#endif /* !asm */
+
+#if ACCESS_TYPE != (NB_MMU_MODES + 1)
+
+#if DATA_SIZE == 8
+static inline float64 glue(ldfq, MEMSUFFIX)(target_ulong ptr)
+{
+    union {
+        float64 d;
+        uint64_t i;
+    } u;
+    u.i = glue(ldq, MEMSUFFIX)(ptr);
+    return u.d;
+}
+
+static inline void glue(stfq, MEMSUFFIX)(target_ulong ptr, float64 v)
+{
+    union {
+        float64 d;
+        uint64_t i;
+    } u;
+    u.d = v;
+    glue(stq, MEMSUFFIX)(ptr, u.i);
+}
+#endif /* DATA_SIZE == 8 */
+
+#if DATA_SIZE == 4
+static inline float32 glue(ldfl, MEMSUFFIX)(target_ulong ptr)
+{
+    union {
+        float32 f;
+        uint32_t i;
+    } u;
+    u.i = glue(ldl, MEMSUFFIX)(ptr);
+    return u.f;
+}
+
+static inline void glue(stfl, MEMSUFFIX)(target_ulong ptr, float32 v)
+{
+    union {
+        float32 f;
+        uint32_t i;
+    } u;
+    u.f = v;
+    glue(stl, MEMSUFFIX)(ptr, u.i);
+}
+#endif /* DATA_SIZE == 4 */
+
+#endif /* ACCESS_TYPE != (NB_MMU_MODES + 1) */
+
+#undef RES_TYPE
+#undef DATA_TYPE
+#undef DATA_STYPE
+#undef SUFFIX
+#undef USUFFIX
+#undef DATA_SIZE
+#undef CPU_MMU_INDEX
+#undef MMUSUFFIX
+#undef ADDR_READ
diff --git a/softmmu_template.h b/softmmu_template.h
new file mode 100644
index 0000000..98dd378
--- /dev/null
+++ b/softmmu_template.h
@@ -0,0 +1,333 @@
+/*
+ *  Software MMU support
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define DATA_SIZE (1 << SHIFT)
+
+#if DATA_SIZE == 8
+#define SUFFIX q
+#define USUFFIX q
+#define DATA_TYPE uint64_t
+#elif DATA_SIZE == 4
+#define SUFFIX l
+#define USUFFIX l
+#define DATA_TYPE uint32_t
+#elif DATA_SIZE == 2
+#define SUFFIX w
+#define USUFFIX uw
+#define DATA_TYPE uint16_t
+#elif DATA_SIZE == 1
+#define SUFFIX b
+#define USUFFIX ub
+#define DATA_TYPE uint8_t
+#else
+#error unsupported data size
+#endif
+
+#ifdef SOFTMMU_CODE_ACCESS
+#define READ_ACCESS_TYPE 2
+#define ADDR_READ addr_code
+#else
+#define READ_ACCESS_TYPE 0
+#define ADDR_READ addr_read
+#endif
+
+static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                        int mmu_idx,
+                                                        void *retaddr);
+static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
+                                              target_ulong addr,
+                                              void *retaddr)
+{
+    DATA_TYPE res;
+    int index;
+    index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+    physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
+    env->mem_io_pc = (unsigned long)retaddr;
+    if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
+            && !can_do_io(env)) {
+        cpu_io_recompile(env, retaddr);
+    }
+
+#if SHIFT <= 2
+    res = io_mem_read[index][SHIFT](io_mem_opaque[index], physaddr);
+#else
+#ifdef TARGET_WORDS_BIGENDIAN
+    res = (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr) << 32;
+    res |= io_mem_read[index][2](io_mem_opaque[index], physaddr + 4);
+#else
+    res = io_mem_read[index][2](io_mem_opaque[index], physaddr);
+    res |= (uint64_t)io_mem_read[index][2](io_mem_opaque[index], physaddr + 4) << 32;
+#endif
+#endif /* SHIFT > 2 */
+#ifdef USE_KQEMU
+    env->last_io_time = cpu_get_time_fast();
+#endif
+    return res;
+}
+
+/* handle all cases except unaligned access which span two pages */
+DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                      int mmu_idx)
+{
+    DATA_TYPE res;
+    int index;
+    target_ulong tlb_addr;
+    target_phys_addr_t addend;
+    void *retaddr;
+
+    /* test if there is match for unaligned or IO access */
+    /* XXX: could done more in memory macro in a non portable way */
+    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+    tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+        if (tlb_addr & ~TARGET_PAGE_MASK) {
+            /* IO access */
+            if ((addr & (DATA_SIZE - 1)) != 0)
+                goto do_unaligned_access;
+            retaddr = GETPC();
+            addend = env->iotlb[mmu_idx][index];
+            res = glue(io_read, SUFFIX)(addend, addr, retaddr);
+        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+            /* slow unaligned access (it spans two pages or IO) */
+        do_unaligned_access:
+            retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+            do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+#endif
+            res = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr,
+                                                         mmu_idx, retaddr);
+        } else {
+            /* unaligned/aligned access in the same page */
+#ifdef ALIGNED_ONLY
+            if ((addr & (DATA_SIZE - 1)) != 0) {
+                retaddr = GETPC();
+                do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+            }
+#endif
+            addend = env->tlb_table[mmu_idx][index].addend;
+            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));
+        }
+    } else {
+        /* the page is not in the TLB : fill it */
+        retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+        if ((addr & (DATA_SIZE - 1)) != 0)
+            do_unaligned_access(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+#endif
+        tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+        goto redo;
+    }
+    return res;
+}
+
+/* handle all unaligned cases */
+static DATA_TYPE glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                        int mmu_idx,
+                                                        void *retaddr)
+{
+    DATA_TYPE res, res1, res2;
+    int index, shift;
+    target_phys_addr_t addend;
+    target_ulong tlb_addr, addr1, addr2;
+
+    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+    tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
+    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+        if (tlb_addr & ~TARGET_PAGE_MASK) {
+            /* IO access */
+            if ((addr & (DATA_SIZE - 1)) != 0)
+                goto do_unaligned_access;
+            retaddr = GETPC();
+            addend = env->iotlb[mmu_idx][index];
+            res = glue(io_read, SUFFIX)(addend, addr, retaddr);
+        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+        do_unaligned_access:
+            /* slow unaligned access (it spans two pages) */
+            addr1 = addr & ~(DATA_SIZE - 1);
+            addr2 = addr1 + DATA_SIZE;
+            res1 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr1,
+                                                          mmu_idx, retaddr);
+            res2 = glue(glue(slow_ld, SUFFIX), MMUSUFFIX)(addr2,
+                                                          mmu_idx, retaddr);
+            shift = (addr & (DATA_SIZE - 1)) * 8;
+#ifdef TARGET_WORDS_BIGENDIAN
+            res = (res1 << shift) | (res2 >> ((DATA_SIZE * 8) - shift));
+#else
+            res = (res1 >> shift) | (res2 << ((DATA_SIZE * 8) - shift));
+#endif
+            res = (DATA_TYPE)res;
+        } else {
+            /* unaligned/aligned access in the same page */
+            addend = env->tlb_table[mmu_idx][index].addend;
+            res = glue(glue(ld, USUFFIX), _raw)((uint8_t *)(long)(addr+addend));
+        }
+    } else {
+        /* the page is not in the TLB : fill it */
+        tlb_fill(addr, READ_ACCESS_TYPE, mmu_idx, retaddr);
+        goto redo;
+    }
+    return res;
+}
+
+#ifndef SOFTMMU_CODE_ACCESS
+
+static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                   DATA_TYPE val,
+                                                   int mmu_idx,
+                                                   void *retaddr);
+
+static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
+                                          DATA_TYPE val,
+                                          target_ulong addr,
+                                          void *retaddr)
+{
+    int index;
+    index = (physaddr >> IO_MEM_SHIFT) & (IO_MEM_NB_ENTRIES - 1);
+    physaddr = (physaddr & TARGET_PAGE_MASK) + addr;
+    if (index > (IO_MEM_NOTDIRTY >> IO_MEM_SHIFT)
+            && !can_do_io(env)) {
+        cpu_io_recompile(env, retaddr);
+    }
+
+    env->mem_io_vaddr = addr;
+    env->mem_io_pc = (unsigned long)retaddr;
+#if SHIFT <= 2
+    io_mem_write[index][SHIFT](io_mem_opaque[index], physaddr, val);
+#else
+#ifdef TARGET_WORDS_BIGENDIAN
+    io_mem_write[index][2](io_mem_opaque[index], physaddr, val >> 32);
+    io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val);
+#else
+    io_mem_write[index][2](io_mem_opaque[index], physaddr, val);
+    io_mem_write[index][2](io_mem_opaque[index], physaddr + 4, val >> 32);
+#endif
+#endif /* SHIFT > 2 */
+#ifdef USE_KQEMU
+    env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                 DATA_TYPE val,
+                                                 int mmu_idx)
+{
+    target_phys_addr_t addend;
+    target_ulong tlb_addr;
+    void *retaddr;
+    int index;
+
+    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+    tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+        if (tlb_addr & ~TARGET_PAGE_MASK) {
+            /* IO access */
+            if ((addr & (DATA_SIZE - 1)) != 0)
+                goto do_unaligned_access;
+            retaddr = GETPC();
+            addend = env->iotlb[mmu_idx][index];
+            glue(io_write, SUFFIX)(addend, val, addr, retaddr);
+        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+        do_unaligned_access:
+            retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+            do_unaligned_access(addr, 1, mmu_idx, retaddr);
+#endif
+            glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val,
+                                                   mmu_idx, retaddr);
+        } else {
+            /* aligned/unaligned access in the same page */
+#ifdef ALIGNED_ONLY
+            if ((addr & (DATA_SIZE - 1)) != 0) {
+                retaddr = GETPC();
+                do_unaligned_access(addr, 1, mmu_idx, retaddr);
+            }
+#endif
+            addend = env->tlb_table[mmu_idx][index].addend;
+            glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val);
+        }
+    } else {
+        /* the page is not in the TLB : fill it */
+        retaddr = GETPC();
+#ifdef ALIGNED_ONLY
+        if ((addr & (DATA_SIZE - 1)) != 0)
+            do_unaligned_access(addr, 1, mmu_idx, retaddr);
+#endif
+        tlb_fill(addr, 1, mmu_idx, retaddr);
+        goto redo;
+    }
+}
+
+/* handles all unaligned cases */
+static void glue(glue(slow_st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                   DATA_TYPE val,
+                                                   int mmu_idx,
+                                                   void *retaddr)
+{
+    target_phys_addr_t addend;
+    target_ulong tlb_addr;
+    int index, i;
+
+    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+ redo:
+    tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
+    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+        if (tlb_addr & ~TARGET_PAGE_MASK) {
+            /* IO access */
+            if ((addr & (DATA_SIZE - 1)) != 0)
+                goto do_unaligned_access;
+            addend = env->iotlb[mmu_idx][index];
+            glue(io_write, SUFFIX)(addend, val, addr, retaddr);
+        } else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
+        do_unaligned_access:
+            /* XXX: not efficient, but simple */
+            /* Note: relies on the fact that tlb_fill() does not remove the
+             * previous page from the TLB cache.  */
+            for(i = DATA_SIZE - 1; i >= 0; i--) {
+#ifdef TARGET_WORDS_BIGENDIAN
+                glue(slow_stb, MMUSUFFIX)(addr + i, val >> (((DATA_SIZE - 1) * 8) - (i * 8)),
+                                          mmu_idx, retaddr);
+#else
+                glue(slow_stb, MMUSUFFIX)(addr + i, val >> (i * 8),
+                                          mmu_idx, retaddr);
+#endif
+            }
+        } else {
+            /* aligned/unaligned access in the same page */
+            addend = env->tlb_table[mmu_idx][index].addend;
+            glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)(addr+addend), val);
+        }
+    } else {
+        /* the page is not in the TLB : fill it */
+        tlb_fill(addr, 1, mmu_idx, retaddr);
+        goto redo;
+    }
+}
+
+#endif /* !defined(SOFTMMU_CODE_ACCESS) */
+
+#undef READ_ACCESS_TYPE
+#undef SHIFT
+#undef DATA_TYPE
+#undef SUFFIX
+#undef USUFFIX
+#undef DATA_SIZE
+#undef ADDR_READ
diff --git a/sparc.ld b/sparc.ld
new file mode 100644
index 0000000..26ab415
--- /dev/null
+++ b/sparc.ld
@@ -0,0 +1,131 @@
+OUTPUT_FORMAT("elf32-sparc", "elf32-sparc",
+              "elf32-sparc")
+OUTPUT_ARCH(sparc)
+SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
+ENTRY(_start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp     : { *(.interp)    }
+  .hash          : { *(.hash)           }
+  .dynsym        : { *(.dynsym)         }
+  .dynstr        : { *(.dynstr)         }
+  .gnu.version   : { *(.gnu.version)    }
+  .gnu.version_d   : { *(.gnu.version_d)        }
+  .gnu.version_r   : { *(.gnu.version_r)        }
+  .rel.text      :
+    { *(.rel.text) *(.rel.gnu.linkonce.t*) }
+  .rela.text     :
+    { *(.rela.text) *(.rela.gnu.linkonce.t*) }
+  .rel.data      :
+    { *(.rel.data) *(.rel.gnu.linkonce.d*) }
+  .rela.data     :
+    { *(.rela.data) *(.rela.gnu.linkonce.d*) }
+  .rel.rodata    :
+    { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
+  .rela.rodata   :
+    { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
+  .rel.got       : { *(.rel.got)                }
+  .rela.got      : { *(.rela.got)               }
+  .rel.ctors     : { *(.rel.ctors)      }
+  .rela.ctors    : { *(.rela.ctors)     }
+  .rel.dtors     : { *(.rel.dtors)      }
+  .rela.dtors    : { *(.rela.dtors)     }
+  .rel.init      : { *(.rel.init)       }
+  .rela.init     : { *(.rela.init)      }
+  .rel.fini      : { *(.rel.fini)       }
+  .rela.fini     : { *(.rela.fini)      }
+  .rel.bss       : { *(.rel.bss)                }
+  .rela.bss      : { *(.rela.bss)               }
+  .rel.plt       : { *(.rel.plt)                }
+  .rela.plt      : { *(.rela.plt)               }
+  .init          : { *(.init)   } =0x47ff041f
+  .text      :
+  {
+    *(.text)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+    *(.gnu.linkonce.t*)
+  } =0x47ff041f
+  _etext = .;
+  PROVIDE (etext = .);
+  .fini      : { *(.fini)    } =0x47ff041f
+  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
+  .rodata1   : { *(.rodata1) }
+  .reginfo : { *(.reginfo) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN(0x100000) + (. & (0x100000 - 1));
+  .data    :
+  {
+    *(.data)
+    *(.gnu.linkonce.d*)
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  .tdata    : { *(.tdata) }
+  .tbss    : { *(.tbss) }
+  .ctors         :
+  {
+    *(.ctors)
+  }
+  .dtors         :
+  {
+    *(.dtors)
+  }
+  .plt      : { *(.plt) }
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  _edata  =  .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+  }
+  _end = . ;
+  PROVIDE (end = .);
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /* These must appear regardless of  .  */
+  /DISCARD/ : { *(.note.GNU-stack) *(.note.ABI-tag) }
+}
diff --git a/sysemu.h b/sysemu.h
new file mode 100644
index 0000000..b12fae0
--- /dev/null
+++ b/sysemu.h
@@ -0,0 +1,184 @@
+#ifndef SYSEMU_H
+#define SYSEMU_H
+/* Misc. things related to the system emulator.  */
+
+/* vl.c */
+extern const char *bios_name;
+extern const char *bios_dir;
+
+extern int vm_running;
+extern const char *qemu_name;
+
+typedef struct vm_change_state_entry VMChangeStateEntry;
+typedef void VMChangeStateHandler(void *opaque, int running);
+typedef void VMStopHandler(void *opaque, int reason);
+
+VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
+                                                     void *opaque);
+void qemu_del_vm_change_state_handler(VMChangeStateEntry *e);
+
+int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque);
+void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque);
+
+void vm_start(void);
+void vm_stop(int reason);
+
+int64_t cpu_get_ticks(void);
+void cpu_enable_ticks(void);
+void cpu_disable_ticks(void);
+
+void qemu_system_reset_request(void);
+void qemu_system_shutdown_request(void);
+void qemu_system_powerdown_request(void);
+int qemu_shutdown_requested(void);
+int qemu_reset_requested(void);
+int qemu_powerdown_requested(void);
+#if !defined(TARGET_SPARC) && !defined(TARGET_I386)
+// Please implement a power failure function to signal the OS
+#define qemu_system_powerdown() do{}while(0)
+#else
+void qemu_system_powerdown(void);
+#endif
+void qemu_system_reset(void);
+
+void do_savevm(const char *name);
+void do_loadvm(const char *name);
+void do_delvm(const char *name);
+void do_info_snapshots(void);
+
+void main_loop_wait(int timeout);
+
+/* Polling handling */
+
+/* return TRUE if no sleep should be done afterwards */
+typedef int PollingFunc(void *opaque);
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque);
+void qemu_del_polling_cb(PollingFunc *func, void *opaque);
+
+#ifdef _WIN32
+/* Wait objects handling */
+typedef void WaitObjectFunc(void *opaque);
+
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque);
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque);
+#endif
+
+/* TAP win32 */
+int tap_win32_init(VLANState *vlan, const char *ifname);
+
+/* SLIRP */
+void do_info_slirp(void);
+
+extern int bios_size;
+extern int cirrus_vga_enabled;
+extern int vmsvga_enabled;
+extern int graphic_width;
+extern int graphic_height;
+extern int graphic_depth;
+extern const char *keyboard_layout;
+extern int win2k_install_hack;
+extern int alt_grab;
+extern int usb_enabled;
+extern int smp_cpus;
+extern int cursor_hide;
+extern int graphic_rotate;
+extern int no_quit;
+extern int semihosting_enabled;
+extern int autostart;
+extern int old_param;
+extern const char *bootp_filename;
+
+
+#ifdef USE_KQEMU
+extern int kqemu_allowed;
+#endif
+
+#define MAX_OPTION_ROMS 16
+extern const char *option_rom[MAX_OPTION_ROMS];
+extern int nb_option_roms;
+
+#ifdef TARGET_SPARC
+#define MAX_PROM_ENVS 128
+extern const char *prom_envs[MAX_PROM_ENVS];
+extern unsigned int nb_prom_envs;
+#endif
+
+#if defined (TARGET_PPC)
+#define BIOS_SIZE (1024 * 1024)
+#elif defined (TARGET_SPARC64)
+#define BIOS_SIZE ((512 + 32) * 1024)
+#elif defined(TARGET_MIPS)
+#define BIOS_SIZE (4 * 1024 * 1024)
+#endif
+
+typedef enum {
+    IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD
+} BlockInterfaceType;
+
+typedef struct DriveInfo {
+    BlockDriverState *bdrv;
+    BlockInterfaceType type;
+    int bus;
+    int unit;
+} DriveInfo;
+
+#define MAX_IDE_DEVS	2
+#define MAX_SCSI_DEVS	7
+#define MAX_DRIVES 32
+
+extern int nb_drives;
+extern DriveInfo drives_table[MAX_DRIVES+1];
+
+extern int drive_get_index(BlockInterfaceType type, int bus, int unit);
+extern int drive_get_max_bus(BlockInterfaceType type);
+
+/* serial ports */
+
+#define MAX_SERIAL_PORTS 4
+
+extern CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+
+/* parallel ports */
+
+#define MAX_PARALLEL_PORTS 3
+
+extern CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+
+#ifdef NEED_CPU_H
+/* loader.c */
+int get_image_size(const char *filename);
+int load_image(const char *filename, uint8_t *addr); /* deprecated */
+int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz);
+int load_elf(const char *filename, int64_t virt_to_phys_addend,
+             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr);
+int load_aout(const char *filename, target_phys_addr_t addr, int max_sz);
+int load_uboot(const char *filename, target_ulong *ep, int *is_linux);
+
+int fread_targphys(target_phys_addr_t dst_addr, size_t nbytes, FILE *f);
+int fread_targphys_ok(target_phys_addr_t dst_addr, size_t nbytes, FILE *f);
+int read_targphys(int fd, target_phys_addr_t dst_addr, size_t nbytes);
+void pstrcpy_targphys(target_phys_addr_t dest, int buf_size,
+                      const char *source);
+#endif
+
+#ifdef HAS_AUDIO
+struct soundhw {
+    const char *name;
+    const char *descr;
+    int enabled;
+    int isa;
+    union {
+        int (*init_isa) (AudioState *s, qemu_irq *pic);
+        int (*init_pci) (PCIBus *bus, AudioState *s);
+    } init;
+};
+
+extern struct soundhw soundhw[];
+#endif
+
+void do_usb_add(const char *devname);
+void do_usb_del(const char *devname);
+void usb_info(void);
+
+#endif
diff --git a/tap-win32.c b/tap-win32.c
new file mode 100644
index 0000000..b4b61d0
--- /dev/null
+++ b/tap-win32.c
@@ -0,0 +1,688 @@
+/*
+ *  TAP-Win32 -- A kernel driver to provide virtual tap device functionality
+ *               on Windows.  Originally derived from the CIPE-Win32
+ *               project by Damion K. Wilson, with extensive modifications by
+ *               James Yonan.
+ *
+ *  All source code which derives from the CIPE-Win32 project is
+ *  Copyright (C) Damion K. Wilson, 2003, and is released under the
+ *  GPL version 2 (see below).
+ *
+ *  All other source code is Copyright (C) James Yonan, 2003-2004,
+ *  and is released under the GPL version 2 (see below).
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program (see the file COPYING included with this
+ *  distribution); if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdio.h>
+#include <stdint.h>
+#include <windows.h>
+
+/* NOTE: PCIBus is redefined in winddk.h */
+#define PCIBus _PCIBus
+#include <ddk/ntapi.h>
+#include <ddk/winddk.h>
+#include <ddk/ntddk.h>
+#undef PCIBus
+
+//=============
+// TAP IOCTLs
+//=============
+
+#define TAP_CONTROL_CODE(request,method) \
+  CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
+
+#define TAP_IOCTL_GET_MAC               TAP_CONTROL_CODE (1, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_VERSION           TAP_CONTROL_CODE (2, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_MTU               TAP_CONTROL_CODE (3, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_INFO              TAP_CONTROL_CODE (4, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_POINT_TO_POINT TAP_CONTROL_CODE (5, METHOD_BUFFERED)
+#define TAP_IOCTL_SET_MEDIA_STATUS      TAP_CONTROL_CODE (6, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_MASQ      TAP_CONTROL_CODE (7, METHOD_BUFFERED)
+#define TAP_IOCTL_GET_LOG_LINE          TAP_CONTROL_CODE (8, METHOD_BUFFERED)
+#define TAP_IOCTL_CONFIG_DHCP_SET_OPT   TAP_CONTROL_CODE (9, METHOD_BUFFERED)
+
+//=================
+// Registry keys
+//=================
+
+#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
+
+//======================
+// Filesystem prefixes
+//======================
+
+#define USERMODEDEVICEDIR "\\\\.\\Global\\"
+#define TAPSUFFIX         ".tap"
+
+
+//======================
+// Compile time configuration
+//======================
+
+//#define DEBUG_TAP_WIN32 1
+
+#define TUN_ASYNCHRONOUS_WRITES 1
+
+#define TUN_BUFFER_SIZE 1560
+#define TUN_MAX_BUFFER_COUNT 32
+
+/*
+ * The data member "buffer" must be the first element in the tun_buffer
+ * structure. See the function, tap_win32_free_buffer.
+ */
+typedef struct tun_buffer_s {
+    unsigned char buffer [TUN_BUFFER_SIZE];
+    unsigned long read_size;
+    struct tun_buffer_s* next;
+} tun_buffer_t;
+
+typedef struct tap_win32_overlapped {
+    HANDLE handle;
+    HANDLE read_event;
+    HANDLE write_event;
+    HANDLE output_queue_semaphore;
+    HANDLE free_list_semaphore;
+    CRITICAL_SECTION output_queue_cs;
+    CRITICAL_SECTION free_list_cs;
+    OVERLAPPED read_overlapped;
+    OVERLAPPED write_overlapped;
+    tun_buffer_t buffers[TUN_MAX_BUFFER_COUNT];
+    tun_buffer_t* free_list;
+    tun_buffer_t* output_queue_front;
+    tun_buffer_t* output_queue_back;
+} tap_win32_overlapped_t;
+
+static tap_win32_overlapped_t tap_overlapped;
+
+static tun_buffer_t* get_buffer_from_free_list(tap_win32_overlapped_t* const overlapped) 
+{
+    tun_buffer_t* buffer = NULL;
+    WaitForSingleObject(overlapped->free_list_semaphore, INFINITE);
+    EnterCriticalSection(&overlapped->free_list_cs);
+    buffer = overlapped->free_list;
+//    assert(buffer != NULL);
+    overlapped->free_list = buffer->next;
+    LeaveCriticalSection(&overlapped->free_list_cs);
+    buffer->next = NULL;
+    return buffer;
+}
+
+static void put_buffer_on_free_list(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+    EnterCriticalSection(&overlapped->free_list_cs);
+    buffer->next = overlapped->free_list;
+    overlapped->free_list = buffer;
+    LeaveCriticalSection(&overlapped->free_list_cs);
+    ReleaseSemaphore(overlapped->free_list_semaphore, 1, NULL);
+}
+
+static tun_buffer_t* get_buffer_from_output_queue(tap_win32_overlapped_t* const overlapped, const int block) 
+{
+    tun_buffer_t* buffer = NULL;
+    DWORD result, timeout = block ? INFINITE : 0L;
+
+    // Non-blocking call
+    result = WaitForSingleObject(overlapped->output_queue_semaphore, timeout); 
+
+    switch (result) 
+    { 
+        // The semaphore object was signaled.
+        case WAIT_OBJECT_0: 
+            EnterCriticalSection(&overlapped->output_queue_cs);
+
+            buffer = overlapped->output_queue_front;
+            overlapped->output_queue_front = buffer->next;
+
+            if(overlapped->output_queue_front == NULL) {
+                overlapped->output_queue_back = NULL;
+            }
+
+            LeaveCriticalSection(&overlapped->output_queue_cs);
+            break; 
+
+        // Semaphore was nonsignaled, so a time-out occurred.
+        case WAIT_TIMEOUT: 
+            // Cannot open another window.
+            break; 
+    }
+
+    return buffer;
+}
+
+static tun_buffer_t* get_buffer_from_output_queue_immediate (tap_win32_overlapped_t* const overlapped) 
+{
+    return get_buffer_from_output_queue(overlapped, 0);
+}
+
+static void put_buffer_on_output_queue(tap_win32_overlapped_t* const overlapped, tun_buffer_t* const buffer)
+{
+    EnterCriticalSection(&overlapped->output_queue_cs);
+
+    if(overlapped->output_queue_front == NULL && overlapped->output_queue_back == NULL) {
+        overlapped->output_queue_front = overlapped->output_queue_back = buffer;
+    } else {
+        buffer->next = NULL;
+        overlapped->output_queue_back->next = buffer;
+        overlapped->output_queue_back = buffer;
+    }
+
+    LeaveCriticalSection(&overlapped->output_queue_cs);
+
+    ReleaseSemaphore(overlapped->output_queue_semaphore, 1, NULL);
+}
+
+
+static int is_tap_win32_dev(const char *guid)
+{
+    HKEY netcard_key;
+    LONG status;
+    DWORD len;
+    int i = 0;
+
+    status = RegOpenKeyEx(
+        HKEY_LOCAL_MACHINE,
+        ADAPTER_KEY,
+        0,
+        KEY_READ,
+        &netcard_key);
+
+    if (status != ERROR_SUCCESS) {
+        return FALSE;
+    }
+
+    for (;;) {
+        char enum_name[256];
+        char unit_string[256];
+        HKEY unit_key;
+        char component_id_string[] = "ComponentId";
+        char component_id[256];
+        char net_cfg_instance_id_string[] = "NetCfgInstanceId";
+        char net_cfg_instance_id[256];
+        DWORD data_type;
+
+        len = sizeof (enum_name);
+        status = RegEnumKeyEx(
+            netcard_key,
+            i,
+            enum_name,
+            &len,
+            NULL,
+            NULL,
+            NULL,
+            NULL);
+
+        if (status == ERROR_NO_MORE_ITEMS)
+            break;
+        else if (status != ERROR_SUCCESS) {
+            return FALSE;
+        }
+
+        snprintf (unit_string, sizeof(unit_string), "%s\\%s",
+                  ADAPTER_KEY, enum_name);
+
+        status = RegOpenKeyEx(
+            HKEY_LOCAL_MACHINE,
+            unit_string,
+            0,
+            KEY_READ,
+            &unit_key);
+
+        if (status != ERROR_SUCCESS) {
+            return FALSE;
+        } else {
+            len = sizeof (component_id);
+            status = RegQueryValueEx(
+                unit_key,
+                component_id_string,
+                NULL,
+                &data_type,
+                component_id,
+                &len);
+
+            if (!(status != ERROR_SUCCESS || data_type != REG_SZ)) {
+                len = sizeof (net_cfg_instance_id);
+                status = RegQueryValueEx(
+                    unit_key,
+                    net_cfg_instance_id_string,
+                    NULL,
+                    &data_type,
+                    net_cfg_instance_id,
+                    &len);
+
+                if (status == ERROR_SUCCESS && data_type == REG_SZ) {
+                    if (/* !strcmp (component_id, TAP_COMPONENT_ID) &&*/
+                        !strcmp (net_cfg_instance_id, guid)) {
+                        RegCloseKey (unit_key);
+                        RegCloseKey (netcard_key);
+                        return TRUE;
+                    }
+                }
+            }
+            RegCloseKey (unit_key);
+        }
+        ++i;
+    }
+
+    RegCloseKey (netcard_key);
+    return FALSE;
+}
+
+static int get_device_guid(
+    char *name,
+    int name_size,
+    char *actual_name,
+    int actual_name_size)
+{
+    LONG status;
+    HKEY control_net_key;
+    DWORD len;
+    int i = 0;
+    int stop = 0;
+
+    status = RegOpenKeyEx(
+        HKEY_LOCAL_MACHINE,
+        NETWORK_CONNECTIONS_KEY,
+        0,
+        KEY_READ,
+        &control_net_key);
+
+    if (status != ERROR_SUCCESS) {
+        return -1;
+    }
+
+    while (!stop)
+    {
+        char enum_name[256];
+        char connection_string[256];
+        HKEY connection_key;
+        char name_data[256];
+        DWORD name_type;
+        const char name_string[] = "Name";
+
+        len = sizeof (enum_name);
+        status = RegEnumKeyEx(
+            control_net_key,
+            i,
+            enum_name,
+            &len,
+            NULL,
+            NULL,
+            NULL,
+            NULL);
+
+        if (status == ERROR_NO_MORE_ITEMS)
+            break;
+        else if (status != ERROR_SUCCESS) {
+            return -1;
+        }
+
+        snprintf(connection_string, 
+             sizeof(connection_string),
+             "%s\\%s\\Connection",
+             NETWORK_CONNECTIONS_KEY, enum_name);
+
+        status = RegOpenKeyEx(
+            HKEY_LOCAL_MACHINE,
+            connection_string,
+            0,
+            KEY_READ,
+            &connection_key);
+        
+        if (status == ERROR_SUCCESS) {
+            len = sizeof (name_data);
+            status = RegQueryValueEx(
+                connection_key,
+                name_string,
+                NULL,
+                &name_type,
+                name_data,
+                &len);
+
+            if (status != ERROR_SUCCESS || name_type != REG_SZ) {
+                    return -1;
+            }
+            else {
+                if (is_tap_win32_dev(enum_name)) {
+                    snprintf(name, name_size, "%s", enum_name);
+                    if (actual_name) {
+                        if (strcmp(actual_name, "") != 0) {
+                            if (strcmp(name_data, actual_name) != 0) {
+                                RegCloseKey (connection_key);
+                                ++i;
+                                continue;
+                            }
+                        }
+                        else {
+                            snprintf(actual_name, actual_name_size, "%s", name_data);
+                        }
+                    }
+                    stop = 1;
+                }
+            }
+
+            RegCloseKey (connection_key);
+        }
+        ++i;
+    }
+
+    RegCloseKey (control_net_key);
+
+    if (stop == 0)
+        return -1;
+
+    return 0;
+}
+
+static int tap_win32_set_status(HANDLE handle, int status)
+{
+    unsigned long len = 0;
+
+    return DeviceIoControl(handle, TAP_IOCTL_SET_MEDIA_STATUS,
+                &status, sizeof (status),
+                &status, sizeof (status), &len, NULL);
+}
+
+static void tap_win32_overlapped_init(tap_win32_overlapped_t* const overlapped, const HANDLE handle)
+{
+    overlapped->handle = handle;
+
+    overlapped->read_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    overlapped->write_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+    overlapped->read_overlapped.Offset = 0;
+    overlapped->read_overlapped.OffsetHigh = 0;
+    overlapped->read_overlapped.hEvent = overlapped->read_event;
+
+    overlapped->write_overlapped.Offset = 0;
+    overlapped->write_overlapped.OffsetHigh = 0;
+    overlapped->write_overlapped.hEvent = overlapped->write_event;
+
+    InitializeCriticalSection(&overlapped->output_queue_cs);
+    InitializeCriticalSection(&overlapped->free_list_cs);
+
+    overlapped->output_queue_semaphore = CreateSemaphore( 
+        NULL,   // default security attributes
+        0,   // initial count
+        TUN_MAX_BUFFER_COUNT,   // maximum count
+        NULL);  // unnamed semaphore
+
+    if(!overlapped->output_queue_semaphore)  {
+        fprintf(stderr, "error creating output queue semaphore!\n");
+    }
+
+    overlapped->free_list_semaphore = CreateSemaphore( 
+        NULL,   // default security attributes
+        TUN_MAX_BUFFER_COUNT,   // initial count
+        TUN_MAX_BUFFER_COUNT,   // maximum count
+        NULL);  // unnamed semaphore
+
+    if(!overlapped->free_list_semaphore)  {
+        fprintf(stderr, "error creating free list semaphore!\n");
+    }
+
+    overlapped->free_list = overlapped->output_queue_front = overlapped->output_queue_back = NULL;
+
+    {
+        unsigned index;
+        for(index = 0; index < TUN_MAX_BUFFER_COUNT; index++) {
+            tun_buffer_t* element = &overlapped->buffers[index];
+            element->next = overlapped->free_list;
+            overlapped->free_list = element;
+        }
+    }
+}
+
+static int tap_win32_write(tap_win32_overlapped_t *overlapped, 
+                           const void *buffer, unsigned long size)
+{
+    unsigned long write_size;
+    BOOL result;
+    DWORD error;
+
+    result = GetOverlappedResult( overlapped->handle, &overlapped->write_overlapped,
+                                  &write_size, FALSE);
+
+    if (!result && GetLastError() == ERROR_IO_INCOMPLETE)
+        WaitForSingleObject(overlapped->write_event, INFINITE);
+
+    result = WriteFile(overlapped->handle, buffer, size,
+                       &write_size, &overlapped->write_overlapped);
+    
+    if (!result) { 
+        switch (error = GetLastError())
+        { 
+        case ERROR_IO_PENDING: 
+#ifndef TUN_ASYNCHRONOUS_WRITES
+            WaitForSingleObject(overlapped->write_event, INFINITE);
+#endif
+            break;
+        default:
+            return -1;
+        } 
+    }
+
+    return 0;
+}
+
+static DWORD WINAPI tap_win32_thread_entry(LPVOID param)
+{
+    tap_win32_overlapped_t *overlapped = (tap_win32_overlapped_t*)param;
+    unsigned long read_size;
+    BOOL result;
+    DWORD dwError;
+    tun_buffer_t* buffer = get_buffer_from_free_list(overlapped);
+
+
+    for (;;) {
+        result = ReadFile(overlapped->handle,
+                          buffer->buffer,
+                          sizeof(buffer->buffer),
+                          &read_size,
+                          &overlapped->read_overlapped);
+        if (!result) {
+            dwError = GetLastError();
+            if (dwError == ERROR_IO_PENDING) {
+                WaitForSingleObject(overlapped->read_event, INFINITE);
+                result = GetOverlappedResult( overlapped->handle, &overlapped->read_overlapped,
+                                              &read_size, FALSE);
+                if (!result) {
+#if DEBUG_TAP_WIN32
+                    LPVOID lpBuffer;
+                    dwError = GetLastError();
+                    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                                   NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                                   (LPTSTR) & lpBuffer, 0, NULL );
+                    fprintf(stderr, "Tap-Win32: Error GetOverlappedResult %d - %s\n", dwError, lpBuffer);
+                    LocalFree( lpBuffer );
+#endif
+                }
+            } else {
+#if DEBUG_TAP_WIN32
+                LPVOID lpBuffer;
+                FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+                               NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                               (LPTSTR) & lpBuffer, 0, NULL );
+                fprintf(stderr, "Tap-Win32: Error ReadFile %d - %s\n", dwError, lpBuffer);
+                LocalFree( lpBuffer );
+#endif
+            }
+        }
+
+        if(read_size > 0) {
+            buffer->read_size = read_size;
+            put_buffer_on_output_queue(overlapped, buffer);
+            buffer = get_buffer_from_free_list(overlapped);
+        }
+    }
+
+    return 0;
+}
+
+static int tap_win32_read(tap_win32_overlapped_t *overlapped, 
+                          uint8_t **pbuf, int max_size)
+{
+    int size = 0;
+
+    tun_buffer_t* buffer = get_buffer_from_output_queue_immediate(overlapped);
+
+    if(buffer != NULL) {
+        *pbuf = buffer->buffer;
+        size = (int)buffer->read_size;
+        if(size > max_size) {
+            size = max_size;
+        }
+    }
+
+    return size;
+}
+
+static void tap_win32_free_buffer(tap_win32_overlapped_t *overlapped, 
+                                  char* pbuf) 
+{
+    tun_buffer_t* buffer = (tun_buffer_t*)pbuf;
+    put_buffer_on_free_list(overlapped, buffer);
+}
+
+static int tap_win32_open(tap_win32_overlapped_t **phandle, 
+                          const char *prefered_name)
+{
+    char device_path[256];
+    char device_guid[0x100];
+    int rc;
+    HANDLE handle;
+    BOOL bret;
+    char name_buffer[0x100] = {0, };
+    struct {
+        unsigned long major;
+        unsigned long minor;
+        unsigned long debug;
+    } version;
+    LONG version_len;
+    DWORD idThread;
+    HANDLE hThread;
+
+    if (prefered_name != NULL)
+        snprintf(name_buffer, sizeof(name_buffer), "%s", prefered_name);
+
+    rc = get_device_guid(device_guid, sizeof(device_guid), name_buffer, sizeof(name_buffer));
+    if (rc)
+        return -1;
+
+    snprintf (device_path, sizeof(device_path), "%s%s%s",
+              USERMODEDEVICEDIR,
+              device_guid,
+              TAPSUFFIX);
+
+    handle = CreateFile (
+        device_path,
+        GENERIC_READ | GENERIC_WRITE,
+        0,
+        0,
+        OPEN_EXISTING,
+        FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED,
+        0 );
+
+    if (handle == INVALID_HANDLE_VALUE) {
+        return -1;
+    }
+
+    bret = DeviceIoControl(handle, TAP_IOCTL_GET_VERSION,
+                           &version, sizeof (version),
+                           &version, sizeof (version), &version_len, NULL);
+
+    if (bret == FALSE) {
+        CloseHandle(handle);
+        return -1;
+    }
+
+    if (!tap_win32_set_status(handle, TRUE)) {
+        return -1;
+    }
+
+    tap_win32_overlapped_init(&tap_overlapped, handle);
+
+    *phandle = &tap_overlapped;
+
+    hThread = CreateThread(NULL, 0, tap_win32_thread_entry,
+                           (LPVOID)&tap_overlapped, 0, &idThread);
+    SetThreadPriority(hThread,THREAD_PRIORITY_TIME_CRITICAL);
+
+    return 0;
+}
+
+/********************************************/
+
+ typedef struct TAPState {
+     VLANClientState *vc;
+     tap_win32_overlapped_t *handle;
+     HANDLE tap_event;
+ } TAPState;
+
+static TAPState *tap_win32_state = NULL;
+
+void tap_receive(void *opaque, const uint8_t *buf, int size)
+{
+    TAPState *s = opaque;
+
+    tap_win32_write(s->handle, buf, size);
+}
+
+/* XXX: horrible, suppress this by using proper thread signaling */
+void tap_win32_poll(void)
+{
+    TAPState *s = tap_win32_state;
+    uint8_t *buf;
+    int max_size = 4096;
+    int size;
+
+    if (!s)
+        return;
+
+    size = tap_win32_read(s->handle, &buf, max_size);
+    if (size > 0) {
+        qemu_send_packet(s->vc, buf, size);
+        tap_win32_free_buffer(s->handle, buf);
+        SetEvent(s->tap_event);
+    }
+}
+
+int tap_win32_init(VLANState *vlan, const char *ifname)
+{
+    TAPState *s;
+    
+    s = qemu_mallocz(sizeof(TAPState));
+    if (!s)
+        return -1;
+    if (tap_win32_open(&s->handle, ifname) < 0) {
+        printf("tap: Could not open '%s'\n", ifname);
+        return -1;
+    }
+
+    s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
+    
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+             "tap: ifname=%s", ifname);
+    tap_win32_state = s;
+
+    s->tap_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (!s->tap_event) {
+        fprintf(stderr, "tap-win32: Failed CreateEvent\n");
+    }
+    qemu_add_wait_object(s->tap_event, NULL, NULL);
+    return 0;
+}
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
new file mode 100644
index 0000000..ff765f7
--- /dev/null
+++ b/target-arm/cpu.h
@@ -0,0 +1,420 @@
+/*
+ * ARM virtual CPU header
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef CPU_ARM_H
+#define CPU_ARM_H
+
+#define TARGET_LONG_BITS 32
+
+#define ELF_MACHINE	EM_ARM
+
+#include "cpu-defs.h"
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+#define EXCP_UDEF            1   /* undefined instruction */
+#define EXCP_SWI             2   /* software interrupt */
+#define EXCP_PREFETCH_ABORT  3
+#define EXCP_DATA_ABORT      4
+#define EXCP_IRQ             5
+#define EXCP_FIQ             6
+#define EXCP_BKPT            7
+#define EXCP_EXCEPTION_EXIT  8   /* Return from v7M exception.  */
+#define EXCP_KERNEL_TRAP     9   /* Jumped to kernel code page.  */
+
+#define ARMV7M_EXCP_RESET   1
+#define ARMV7M_EXCP_NMI     2
+#define ARMV7M_EXCP_HARD    3
+#define ARMV7M_EXCP_MEM     4
+#define ARMV7M_EXCP_BUS     5
+#define ARMV7M_EXCP_USAGE   6
+#define ARMV7M_EXCP_SVC     11
+#define ARMV7M_EXCP_DEBUG   12
+#define ARMV7M_EXCP_PENDSV  14
+#define ARMV7M_EXCP_SYSTICK 15
+
+typedef void ARMWriteCPFunc(void *opaque, int cp_info,
+                            int srcreg, int operand, uint32_t value);
+typedef uint32_t ARMReadCPFunc(void *opaque, int cp_info,
+                               int dstreg, int operand);
+
+struct arm_boot_info;
+
+#define NB_MMU_MODES 2
+
+/* We currently assume float and double are IEEE single and double
+   precision respectively.
+   Doing runtime conversions is tricky because VFP registers may contain
+   integer values (eg. as the result of a FTOSI instruction).
+   s<2n> maps to the least significant half of d<n>
+   s<2n+1> maps to the most significant half of d<n>
+ */
+
+typedef struct CPUARMState {
+    /* Regs for current mode.  */
+    uint32_t regs[16];
+    /* Frequently accessed CPSR bits are stored separately for efficiently.
+       This contains all the other bits.  Use cpsr_{read,write} to access
+       the whole CPSR.  */
+    uint32_t uncached_cpsr;
+    uint32_t spsr;
+
+    /* Banked registers.  */
+    uint32_t banked_spsr[6];
+    uint32_t banked_r13[6];
+    uint32_t banked_r14[6];
+
+    /* These hold r8-r12.  */
+    uint32_t usr_regs[5];
+    uint32_t fiq_regs[5];
+
+    /* cpsr flag cache for faster execution */
+    uint32_t CF; /* 0 or 1 */
+    uint32_t VF; /* V is the bit 31. All other bits are undefined */
+    uint32_t NF; /* N is bit 31. All other bits are undefined.  */
+    uint32_t ZF; /* Z set if zero.  */
+    uint32_t QF; /* 0 or 1 */
+    uint32_t GE; /* cpsr[19:16] */
+    uint32_t thumb; /* cpsr[5]. 0 = arm mode, 1 = thumb mode. */
+    uint32_t condexec_bits; /* IT bits.  cpsr[15:10,26:25].  */
+
+    /* System control coprocessor (cp15) */
+    struct {
+        uint32_t c0_cpuid;
+        uint32_t c0_cachetype;
+        uint32_t c0_c1[8]; /* Feature registers.  */
+        uint32_t c0_c2[8]; /* Instruction set registers.  */
+        uint32_t c1_sys; /* System control register.  */
+        uint32_t c1_coproc; /* Coprocessor access register.  */
+        uint32_t c1_xscaleauxcr; /* XScale auxiliary control register.  */
+        uint32_t c2_base0; /* MMU translation table base 0.  */
+        uint32_t c2_base1; /* MMU translation table base 1.  */
+        uint32_t c2_mask; /* MMU translation table base mask.  */
+        uint32_t c2_data; /* MPU data cachable bits.  */
+        uint32_t c2_insn; /* MPU instruction cachable bits.  */
+        uint32_t c3; /* MMU domain access control register
+                        MPU write buffer control.  */
+        uint32_t c5_insn; /* Fault status registers.  */
+        uint32_t c5_data;
+        uint32_t c6_region[8]; /* MPU base/size registers.  */
+        uint32_t c6_insn; /* Fault address registers.  */
+        uint32_t c6_data;
+        uint32_t c9_insn; /* Cache lockdown registers.  */
+        uint32_t c9_data;
+        uint32_t c13_fcse; /* FCSE PID.  */
+        uint32_t c13_context; /* Context ID.  */
+        uint32_t c13_tls1; /* User RW Thread register.  */
+        uint32_t c13_tls2; /* User RO Thread register.  */
+        uint32_t c13_tls3; /* Privileged Thread register.  */
+        uint32_t c15_cpar; /* XScale Coprocessor Access Register */
+        uint32_t c15_ticonfig; /* TI925T configuration byte.  */
+        uint32_t c15_i_max; /* Maximum D-cache dirty line index.  */
+        uint32_t c15_i_min; /* Minimum D-cache dirty line index.  */
+        uint32_t c15_threadid; /* TI debugger thread-ID.  */
+    } cp15;
+
+    struct {
+        uint32_t other_sp;
+        uint32_t vecbase;
+        uint32_t basepri;
+        uint32_t control;
+        int current_sp;
+        int exception;
+        int pending_exception;
+        void *nvic;
+    } v7m;
+
+    /* Coprocessor IO used by peripherals */
+    struct {
+        ARMReadCPFunc *cp_read;
+        ARMWriteCPFunc *cp_write;
+        void *opaque;
+    } cp[15];
+
+    /* Internal CPU feature flags.  */
+    uint32_t features;
+
+    /* Callback for vectored interrupt controller.  */
+    int (*get_irq_vector)(struct CPUARMState *);
+    void *irq_opaque;
+
+    /* VFP coprocessor state.  */
+    struct {
+        float64 regs[32];
+
+        uint32_t xregs[16];
+        /* We store these fpcsr fields separately for convenience.  */
+        int vec_len;
+        int vec_stride;
+
+        /* scratch space when Tn are not sufficient.  */
+        uint32_t scratch[8];
+
+        float_status fp_status;
+    } vfp;
+#if defined(CONFIG_USER_ONLY)
+    struct mmon_state *mmon_entry;
+#else
+    uint32_t mmon_addr;
+#endif
+
+    /* iwMMXt coprocessor state.  */
+    struct {
+        uint64_t regs[16];
+        uint64_t val;
+
+        uint32_t cregs[16];
+    } iwmmxt;
+
+#if defined(CONFIG_USER_ONLY)
+    /* For usermode syscall translation.  */
+    int eabi;
+#endif
+
+    CPU_COMMON
+
+    /* These fields after the common ones so they are preserved on reset.  */
+    struct arm_boot_info *boot_info;
+} CPUARMState;
+
+CPUARMState *cpu_arm_init(const char *cpu_model);
+void arm_translate_init(void);
+int cpu_arm_exec(CPUARMState *s);
+void cpu_arm_close(CPUARMState *s);
+void do_interrupt(CPUARMState *);
+void switch_mode(CPUARMState *, int);
+uint32_t do_arm_semihosting(CPUARMState *env);
+
+/* you can call this signal handler from your SIGBUS and SIGSEGV
+   signal handlers to inform the virtual CPU of exceptions. non zero
+   is returned if the signal was handled by the virtual CPU.  */
+int cpu_arm_signal_handler(int host_signum, void *pinfo,
+                           void *puc);
+
+void cpu_lock(void);
+void cpu_unlock(void);
+static inline void cpu_set_tls(CPUARMState *env, target_ulong newtls)
+{
+  env->cp15.c13_tls2 = newtls;
+}
+
+#define CPSR_M (0x1f)
+#define CPSR_T (1 << 5)
+#define CPSR_F (1 << 6)
+#define CPSR_I (1 << 7)
+#define CPSR_A (1 << 8)
+#define CPSR_E (1 << 9)
+#define CPSR_IT_2_7 (0xfc00)
+#define CPSR_GE (0xf << 16)
+#define CPSR_RESERVED (0xf << 20)
+#define CPSR_J (1 << 24)
+#define CPSR_IT_0_1 (3 << 25)
+#define CPSR_Q (1 << 27)
+#define CPSR_V (1 << 28)
+#define CPSR_C (1 << 29)
+#define CPSR_Z (1 << 30)
+#define CPSR_N (1 << 31)
+#define CPSR_NZCV (CPSR_N | CPSR_Z | CPSR_C | CPSR_V)
+
+#define CPSR_IT (CPSR_IT_0_1 | CPSR_IT_2_7)
+#define CACHED_CPSR_BITS (CPSR_T | CPSR_GE | CPSR_IT | CPSR_Q | CPSR_NZCV)
+/* Bits writable in user mode.  */
+#define CPSR_USER (CPSR_NZCV | CPSR_Q | CPSR_GE)
+/* Execution state bits.  MRS read as zero, MSR writes ignored.  */
+#define CPSR_EXEC (CPSR_T | CPSR_IT | CPSR_J)
+
+/* Return the current CPSR value.  */
+uint32_t cpsr_read(CPUARMState *env);
+/* Set the CPSR.  Note that some bits of mask must be all-set or all-clear.  */
+void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask);
+
+/* Return the current xPSR value.  */
+static inline uint32_t xpsr_read(CPUARMState *env)
+{
+    int ZF;
+    ZF = (env->ZF == 0);
+    return (env->NF & 0x80000000) | (ZF << 30)
+        | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
+        | (env->thumb << 24) | ((env->condexec_bits & 3) << 25)
+        | ((env->condexec_bits & 0xfc) << 8)
+        | env->v7m.exception;
+}
+
+/* Set the xPSR.  Note that some bits of mask must be all-set or all-clear.  */
+static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
+{
+    if (mask & CPSR_NZCV) {
+        env->ZF = (~val) & CPSR_Z;
+        env->NF = val;
+        env->CF = (val >> 29) & 1;
+        env->VF = (val << 3) & 0x80000000;
+    }
+    if (mask & CPSR_Q)
+        env->QF = ((val & CPSR_Q) != 0);
+    if (mask & (1 << 24))
+        env->thumb = ((val & (1 << 24)) != 0);
+    if (mask & CPSR_IT_0_1) {
+        env->condexec_bits &= ~3;
+        env->condexec_bits |= (val >> 25) & 3;
+    }
+    if (mask & CPSR_IT_2_7) {
+        env->condexec_bits &= 3;
+        env->condexec_bits |= (val >> 8) & 0xfc;
+    }
+    if (mask & 0x1ff) {
+        env->v7m.exception = val & 0x1ff;
+    }
+}
+
+enum arm_cpu_mode {
+  ARM_CPU_MODE_USR = 0x10,
+  ARM_CPU_MODE_FIQ = 0x11,
+  ARM_CPU_MODE_IRQ = 0x12,
+  ARM_CPU_MODE_SVC = 0x13,
+  ARM_CPU_MODE_ABT = 0x17,
+  ARM_CPU_MODE_UND = 0x1b,
+  ARM_CPU_MODE_SYS = 0x1f
+};
+
+/* VFP system registers.  */
+#define ARM_VFP_FPSID   0
+#define ARM_VFP_FPSCR   1
+#define ARM_VFP_MVFR1   6
+#define ARM_VFP_MVFR0   7
+#define ARM_VFP_FPEXC   8
+#define ARM_VFP_FPINST  9
+#define ARM_VFP_FPINST2 10
+
+/* iwMMXt coprocessor control registers.  */
+#define ARM_IWMMXT_wCID		0
+#define ARM_IWMMXT_wCon		1
+#define ARM_IWMMXT_wCSSF	2
+#define ARM_IWMMXT_wCASF	3
+#define ARM_IWMMXT_wCGR0	8
+#define ARM_IWMMXT_wCGR1	9
+#define ARM_IWMMXT_wCGR2	10
+#define ARM_IWMMXT_wCGR3	11
+
+enum arm_features {
+    ARM_FEATURE_VFP,
+    ARM_FEATURE_AUXCR,  /* ARM1026 Auxiliary control register.  */
+    ARM_FEATURE_XSCALE, /* Intel XScale extensions.  */
+    ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension.  */
+    ARM_FEATURE_V6,
+    ARM_FEATURE_V6K,
+    ARM_FEATURE_V7,
+    ARM_FEATURE_THUMB2,
+    ARM_FEATURE_MPU,    /* Only has Memory Protection Unit, not full MMU.  */
+    ARM_FEATURE_VFP3,
+    ARM_FEATURE_NEON,
+    ARM_FEATURE_DIV,
+    ARM_FEATURE_M, /* Microcontroller profile.  */
+    ARM_FEATURE_OMAPCP  /* OMAP specific CP15 ops handling.  */
+};
+
+static inline int arm_feature(CPUARMState *env, int feature)
+{
+    return (env->features & (1u << feature)) != 0;
+}
+
+void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+/* Interface between CPU and Interrupt controller.  */
+void armv7m_nvic_set_pending(void *opaque, int irq);
+int armv7m_nvic_acknowledge_irq(void *opaque);
+void armv7m_nvic_complete_irq(void *opaque, int irq);
+
+void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+                       ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
+                       void *opaque);
+
+/* Does the core conform to the the "MicroController" profile. e.g. Cortex-M3.
+   Note the M in older cores (eg. ARM7TDMI) stands for Multiply. These are
+   conventional cores (ie. Application or Realtime profile).  */
+
+#define IS_M(env) arm_feature(env, ARM_FEATURE_M)
+#define ARM_CPUID(env) (env->cp15.c0_cpuid)
+
+#define ARM_CPUID_ARM1026     0x4106a262
+#define ARM_CPUID_ARM926      0x41069265
+#define ARM_CPUID_ARM946      0x41059461
+#define ARM_CPUID_TI915T      0x54029152
+#define ARM_CPUID_TI925T      0x54029252
+#define ARM_CPUID_PXA250      0x69052100
+#define ARM_CPUID_PXA255      0x69052d00
+#define ARM_CPUID_PXA260      0x69052903
+#define ARM_CPUID_PXA261      0x69052d05
+#define ARM_CPUID_PXA262      0x69052d06
+#define ARM_CPUID_PXA270      0x69054110
+#define ARM_CPUID_PXA270_A0   0x69054110
+#define ARM_CPUID_PXA270_A1   0x69054111
+#define ARM_CPUID_PXA270_B0   0x69054112
+#define ARM_CPUID_PXA270_B1   0x69054113
+#define ARM_CPUID_PXA270_C0   0x69054114
+#define ARM_CPUID_PXA270_C5   0x69054117
+#define ARM_CPUID_ARM1136     0x4117b363
+#define ARM_CPUID_ARM1136_R2  0x4107b362
+#define ARM_CPUID_ARM11MPCORE 0x410fb022
+#define ARM_CPUID_CORTEXA8    0x410fc080
+#define ARM_CPUID_CORTEXM3    0x410fc231
+#define ARM_CPUID_ANY         0xffffffff
+
+#if defined(CONFIG_USER_ONLY)
+#define TARGET_PAGE_BITS 12
+#else
+/* The ARM MMU allows 1k pages.  */
+/* ??? Linux doesn't actually use these, and they're deprecated in recent
+   architecture revisions.  Maybe a configure option to disable them.  */
+#define TARGET_PAGE_BITS 10
+#endif
+
+#define CPUState CPUARMState
+#define cpu_init cpu_arm_init
+#define cpu_exec cpu_arm_exec
+#define cpu_gen_code cpu_arm_gen_code
+#define cpu_signal_handler cpu_arm_signal_handler
+#define cpu_list arm_cpu_list
+
+#define CPU_SAVE_VERSION 1
+
+/* MMU modes definitions */
+#define MMU_MODE0_SUFFIX _kernel
+#define MMU_MODE1_SUFFIX _user
+#define MMU_USER_IDX 1
+static inline int cpu_mmu_index (CPUState *env)
+{
+    return (env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_USR ? 1 : 0;
+}
+
+#if defined(CONFIG_USER_ONLY)
+static inline void cpu_clone_regs(CPUState *env, target_ulong newsp)
+{
+    if (newsp)
+        env->regs[13] = newsp;
+    env->regs[0] = 0;
+}
+#endif
+
+#define CPU_PC_FROM_TB(env, tb) env->regs[15] = tb->pc
+
+#include "cpu-all.h"
+
+#endif
diff --git a/target-arm/exec.h b/target-arm/exec.h
new file mode 100644
index 0000000..c543cf4
--- /dev/null
+++ b/target-arm/exec.h
@@ -0,0 +1,63 @@
+/*
+ *  ARM execution defines
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "config.h"
+#include "dyngen-exec.h"
+
+register struct CPUARMState *env asm(AREG0);
+register uint32_t T0 asm(AREG1);
+register uint32_t T1 asm(AREG2);
+
+#define M0   env->iwmmxt.val
+
+#include "cpu.h"
+#include "exec-all.h"
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+                              int mmu_idx, int is_softmmu);
+
+static inline int cpu_halted(CPUState *env) {
+    if (!env->halted)
+        return 0;
+    /* An interrupt wakes the CPU even if the I and F CPSR bits are
+       set.  We use EXITTB to silently wake CPU without causing an
+       actual interrupt.  */
+    if (env->interrupt_request &
+        (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD | CPU_INTERRUPT_EXITTB)) {
+        env->halted = 0;
+        return 0;
+    }
+    return EXCP_HALTED;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif
+
+void cpu_loop_exit(void);
+
+void raise_exception(int);
diff --git a/target-arm/helper.c b/target-arm/helper.c
new file mode 100644
index 0000000..7cc8b0f
--- /dev/null
+++ b/target-arm/helper.c
@@ -0,0 +1,2553 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "gdbstub.h"
+#include "helpers.h"
+#include "qemu-common.h"
+
+static uint32_t cortexa8_cp15_c0_c1[8] =
+{ 0x1031, 0x11, 0x400, 0, 0x31100003, 0x20000000, 0x01202000, 0x11 };
+
+static uint32_t cortexa8_cp15_c0_c2[8] =
+{ 0x00101111, 0x12112111, 0x21232031, 0x11112131, 0x00111142, 0, 0, 0 };
+
+static uint32_t mpcore_cp15_c0_c1[8] =
+{ 0x111, 0x1, 0, 0x2, 0x01100103, 0x10020302, 0x01222000, 0 };
+
+static uint32_t mpcore_cp15_c0_c2[8] =
+{ 0x00100011, 0x12002111, 0x11221011, 0x01102131, 0x141, 0, 0, 0 };
+
+static uint32_t arm1136_cp15_c0_c1[8] =
+{ 0x111, 0x1, 0x2, 0x3, 0x01130003, 0x10030302, 0x01222110, 0 };
+
+static uint32_t arm1136_cp15_c0_c2[8] =
+{ 0x00140011, 0x12002111, 0x11231111, 0x01102131, 0x141, 0, 0, 0 };
+
+static uint32_t cpu_arm_find_by_name(const char *name);
+
+static inline void set_feature(CPUARMState *env, int feature)
+{
+    env->features |= 1u << feature;
+}
+
+static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
+{
+    env->cp15.c0_cpuid = id;
+    switch (id) {
+    case ARM_CPUID_ARM926:
+        set_feature(env, ARM_FEATURE_VFP);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
+        env->cp15.c0_cachetype = 0x1dd20d2;
+        env->cp15.c1_sys = 0x00090078;
+        break;
+    case ARM_CPUID_ARM946:
+        set_feature(env, ARM_FEATURE_MPU);
+        env->cp15.c0_cachetype = 0x0f004006;
+        env->cp15.c1_sys = 0x00000078;
+        break;
+    case ARM_CPUID_ARM1026:
+        set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_AUXCR);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
+        env->cp15.c0_cachetype = 0x1dd20d2;
+        env->cp15.c1_sys = 0x00090078;
+        break;
+    case ARM_CPUID_ARM1136_R2:
+    case ARM_CPUID_ARM1136:
+        set_feature(env, ARM_FEATURE_V6);
+        set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_AUXCR);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
+        env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
+        env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000;
+        memcpy(env->cp15.c0_c1, arm1136_cp15_c0_c1, 8 * sizeof(uint32_t));
+        memcpy(env->cp15.c0_c2, arm1136_cp15_c0_c2, 8 * sizeof(uint32_t));
+        env->cp15.c0_cachetype = 0x1dd20d2;
+        break;
+    case ARM_CPUID_ARM11MPCORE:
+        set_feature(env, ARM_FEATURE_V6);
+        set_feature(env, ARM_FEATURE_V6K);
+        set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_AUXCR);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x410120b4;
+        env->vfp.xregs[ARM_VFP_MVFR0] = 0x11111111;
+        env->vfp.xregs[ARM_VFP_MVFR1] = 0x00000000;
+        memcpy(env->cp15.c0_c1, mpcore_cp15_c0_c1, 8 * sizeof(uint32_t));
+        memcpy(env->cp15.c0_c2, mpcore_cp15_c0_c2, 8 * sizeof(uint32_t));
+        env->cp15.c0_cachetype = 0x1dd20d2;
+        break;
+    case ARM_CPUID_CORTEXA8:
+        set_feature(env, ARM_FEATURE_V6);
+        set_feature(env, ARM_FEATURE_V6K);
+        set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_AUXCR);
+        set_feature(env, ARM_FEATURE_THUMB2);
+        set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_VFP3);
+        set_feature(env, ARM_FEATURE_NEON);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x410330c0;
+        env->vfp.xregs[ARM_VFP_MVFR0] = 0x11110222;
+        env->vfp.xregs[ARM_VFP_MVFR1] = 0x00011100;
+        memcpy(env->cp15.c0_c1, cortexa8_cp15_c0_c1, 8 * sizeof(uint32_t));
+        memcpy(env->cp15.c0_c2, cortexa8_cp15_c0_c2, 8 * sizeof(uint32_t));
+        env->cp15.c0_cachetype = 0x1dd20d2;
+        break;
+    case ARM_CPUID_CORTEXM3:
+        set_feature(env, ARM_FEATURE_V6);
+        set_feature(env, ARM_FEATURE_THUMB2);
+        set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_M);
+        set_feature(env, ARM_FEATURE_DIV);
+        break;
+    case ARM_CPUID_ANY: /* For userspace emulation.  */
+        set_feature(env, ARM_FEATURE_V6);
+        set_feature(env, ARM_FEATURE_V6K);
+        set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_THUMB2);
+        set_feature(env, ARM_FEATURE_VFP);
+        set_feature(env, ARM_FEATURE_VFP3);
+        set_feature(env, ARM_FEATURE_NEON);
+        set_feature(env, ARM_FEATURE_DIV);
+        break;
+    case ARM_CPUID_TI915T:
+    case ARM_CPUID_TI925T:
+        set_feature(env, ARM_FEATURE_OMAPCP);
+        env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring.  */
+        env->cp15.c0_cachetype = 0x5109149;
+        env->cp15.c1_sys = 0x00000070;
+        env->cp15.c15_i_max = 0x000;
+        env->cp15.c15_i_min = 0xff0;
+        break;
+    case ARM_CPUID_PXA250:
+    case ARM_CPUID_PXA255:
+    case ARM_CPUID_PXA260:
+    case ARM_CPUID_PXA261:
+    case ARM_CPUID_PXA262:
+        set_feature(env, ARM_FEATURE_XSCALE);
+        /* JTAG_ID is ((id << 28) | 0x09265013) */
+        env->cp15.c0_cachetype = 0xd172172;
+        env->cp15.c1_sys = 0x00000078;
+        break;
+    case ARM_CPUID_PXA270_A0:
+    case ARM_CPUID_PXA270_A1:
+    case ARM_CPUID_PXA270_B0:
+    case ARM_CPUID_PXA270_B1:
+    case ARM_CPUID_PXA270_C0:
+    case ARM_CPUID_PXA270_C5:
+        set_feature(env, ARM_FEATURE_XSCALE);
+        /* JTAG_ID is ((id << 28) | 0x09265013) */
+        set_feature(env, ARM_FEATURE_IWMMXT);
+        env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q';
+        env->cp15.c0_cachetype = 0xd172172;
+        env->cp15.c1_sys = 0x00000078;
+        break;
+    default:
+        cpu_abort(env, "Bad CPU ID: %x\n", id);
+        break;
+    }
+}
+
+void cpu_reset(CPUARMState *env)
+{
+    uint32_t id;
+    id = env->cp15.c0_cpuid;
+    memset(env, 0, offsetof(CPUARMState, breakpoints));
+    if (id)
+        cpu_reset_model_id(env, id);
+#if defined (CONFIG_USER_ONLY)
+    env->uncached_cpsr = ARM_CPU_MODE_USR;
+    env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30;
+#else
+    /* SVC mode with interrupts disabled.  */
+    env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
+    /* On ARMv7-M the CPSR_I is the value of the PRIMASK register, and is
+       clear at reset.  */
+    if (IS_M(env))
+        env->uncached_cpsr &= ~CPSR_I;
+    env->vfp.xregs[ARM_VFP_FPEXC] = 0;
+#endif
+    env->regs[15] = 0;
+    tlb_flush(env, 1);
+}
+
+CPUARMState *cpu_arm_init(const char *cpu_model)
+{
+    CPUARMState *env;
+    uint32_t id;
+    static int inited = 0;
+
+    id = cpu_arm_find_by_name(cpu_model);
+    if (id == 0)
+        return NULL;
+    env = qemu_mallocz(sizeof(CPUARMState));
+    if (!env)
+        return NULL;
+    cpu_exec_init(env);
+    if (!inited) {
+        inited = 1;
+        arm_translate_init();
+    }
+
+    env->cpu_model_str = cpu_model;
+    env->cp15.c0_cpuid = id;
+    cpu_reset(env);
+    return env;
+}
+
+struct arm_cpu_t {
+    uint32_t id;
+    const char *name;
+};
+
+static const struct arm_cpu_t arm_cpu_names[] = {
+    { ARM_CPUID_ARM926, "arm926"},
+    { ARM_CPUID_ARM946, "arm946"},
+    { ARM_CPUID_ARM1026, "arm1026"},
+    { ARM_CPUID_ARM1136, "arm1136"},
+    { ARM_CPUID_ARM1136_R2, "arm1136-r2"},
+    { ARM_CPUID_ARM11MPCORE, "arm11mpcore"},
+    { ARM_CPUID_CORTEXM3, "cortex-m3"},
+    { ARM_CPUID_CORTEXA8, "cortex-a8"},
+    { ARM_CPUID_TI925T, "ti925t" },
+    { ARM_CPUID_PXA250, "pxa250" },
+    { ARM_CPUID_PXA255, "pxa255" },
+    { ARM_CPUID_PXA260, "pxa260" },
+    { ARM_CPUID_PXA261, "pxa261" },
+    { ARM_CPUID_PXA262, "pxa262" },
+    { ARM_CPUID_PXA270, "pxa270" },
+    { ARM_CPUID_PXA270_A0, "pxa270-a0" },
+    { ARM_CPUID_PXA270_A1, "pxa270-a1" },
+    { ARM_CPUID_PXA270_B0, "pxa270-b0" },
+    { ARM_CPUID_PXA270_B1, "pxa270-b1" },
+    { ARM_CPUID_PXA270_C0, "pxa270-c0" },
+    { ARM_CPUID_PXA270_C5, "pxa270-c5" },
+    { ARM_CPUID_ANY, "any"},
+    { 0, NULL}
+};
+
+void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+    int i;
+
+    (*cpu_fprintf)(f, "Available CPUs:\n");
+    for (i = 0; arm_cpu_names[i].name; i++) {
+        (*cpu_fprintf)(f, "  %s\n", arm_cpu_names[i].name);
+    }
+}
+
+/* return 0 if not found */
+static uint32_t cpu_arm_find_by_name(const char *name)
+{
+    int i;
+    uint32_t id;
+
+    id = 0;
+    for (i = 0; arm_cpu_names[i].name; i++) {
+        if (strcmp(name, arm_cpu_names[i].name) == 0) {
+            id = arm_cpu_names[i].id;
+            break;
+        }
+    }
+    return id;
+}
+
+void cpu_arm_close(CPUARMState *env)
+{
+    free(env);
+}
+
+uint32_t cpsr_read(CPUARMState *env)
+{
+    int ZF;
+    ZF = (env->ZF == 0);
+    return env->uncached_cpsr | (env->NF & 0x80000000) | (ZF << 30) |
+        (env->CF << 29) | ((env->VF & 0x80000000) >> 3) | (env->QF << 27)
+        | (env->thumb << 5) | ((env->condexec_bits & 3) << 25)
+        | ((env->condexec_bits & 0xfc) << 8)
+        | (env->GE << 16);
+}
+
+void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
+{
+    if (mask & CPSR_NZCV) {
+        env->ZF = (~val) & CPSR_Z;
+        env->NF = val;
+        env->CF = (val >> 29) & 1;
+        env->VF = (val << 3) & 0x80000000;
+    }
+    if (mask & CPSR_Q)
+        env->QF = ((val & CPSR_Q) != 0);
+    if (mask & CPSR_T)
+        env->thumb = ((val & CPSR_T) != 0);
+    if (mask & CPSR_IT_0_1) {
+        env->condexec_bits &= ~3;
+        env->condexec_bits |= (val >> 25) & 3;
+    }
+    if (mask & CPSR_IT_2_7) {
+        env->condexec_bits &= 3;
+        env->condexec_bits |= (val >> 8) & 0xfc;
+    }
+    if (mask & CPSR_GE) {
+        env->GE = (val >> 16) & 0xf;
+    }
+
+    if ((env->uncached_cpsr ^ val) & mask & CPSR_M) {
+        switch_mode(env, val & CPSR_M);
+    }
+    mask &= ~CACHED_CPSR_BITS;
+    env->uncached_cpsr = (env->uncached_cpsr & ~mask) | (val & mask);
+}
+
+/* Sign/zero extend */
+uint32_t HELPER(sxtb16)(uint32_t x)
+{
+    uint32_t res;
+    res = (uint16_t)(int8_t)x;
+    res |= (uint32_t)(int8_t)(x >> 16) << 16;
+    return res;
+}
+
+uint32_t HELPER(uxtb16)(uint32_t x)
+{
+    uint32_t res;
+    res = (uint16_t)(uint8_t)x;
+    res |= (uint32_t)(uint8_t)(x >> 16) << 16;
+    return res;
+}
+
+uint32_t HELPER(clz)(uint32_t x)
+{
+    int count;
+    for (count = 32; x; count--)
+        x >>= 1;
+    return count;
+}
+
+int32_t HELPER(sdiv)(int32_t num, int32_t den)
+{
+    if (den == 0)
+      return 0;
+    return num / den;
+}
+
+uint32_t HELPER(udiv)(uint32_t num, uint32_t den)
+{
+    if (den == 0)
+      return 0;
+    return num / den;
+}
+
+uint32_t HELPER(rbit)(uint32_t x)
+{
+    x =  ((x & 0xff000000) >> 24)
+       | ((x & 0x00ff0000) >> 8)
+       | ((x & 0x0000ff00) << 8)
+       | ((x & 0x000000ff) << 24);
+    x =  ((x & 0xf0f0f0f0) >> 4)
+       | ((x & 0x0f0f0f0f) << 4);
+    x =  ((x & 0x88888888) >> 3)
+       | ((x & 0x44444444) >> 1)
+       | ((x & 0x22222222) << 1)
+       | ((x & 0x11111111) << 3);
+    return x;
+}
+
+uint32_t HELPER(abs)(uint32_t x)
+{
+    return ((int32_t)x < 0) ? -x : x;
+}
+
+#if defined(CONFIG_USER_ONLY)
+
+void do_interrupt (CPUState *env)
+{
+    env->exception_index = -1;
+}
+
+/* Structure used to record exclusive memory locations.  */
+typedef struct mmon_state {
+    struct mmon_state *next;
+    CPUARMState *cpu_env;
+    uint32_t addr;
+} mmon_state;
+
+/* Chain of current locks.  */
+static mmon_state* mmon_head = NULL;
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
+                              int mmu_idx, int is_softmmu)
+{
+    if (rw == 2) {
+        env->exception_index = EXCP_PREFETCH_ABORT;
+        env->cp15.c6_insn = address;
+    } else {
+        env->exception_index = EXCP_DATA_ABORT;
+        env->cp15.c6_data = address;
+    }
+    return 1;
+}
+
+static void allocate_mmon_state(CPUState *env)
+{
+    env->mmon_entry = malloc(sizeof (mmon_state));
+    if (!env->mmon_entry)
+        abort();
+    memset (env->mmon_entry, 0, sizeof (mmon_state));
+    env->mmon_entry->cpu_env = env;
+    mmon_head = env->mmon_entry;
+}
+
+/* Flush any monitor locks for the specified address.  */
+static void flush_mmon(uint32_t addr)
+{
+    mmon_state *mon;
+
+    for (mon = mmon_head; mon; mon = mon->next)
+      {
+        if (mon->addr != addr)
+          continue;
+
+        mon->addr = 0;
+        break;
+      }
+}
+
+/* Mark an address for exclusive access.  */
+void HELPER(mark_exclusive)(CPUState *env, uint32_t addr)
+{
+    if (!env->mmon_entry)
+        allocate_mmon_state(env);
+    /* Clear any previous locks.  */
+    flush_mmon(addr);
+    env->mmon_entry->addr = addr;
+}
+
+/* Test if an exclusive address is still exclusive.  Returns zero
+   if the address is still exclusive.   */
+uint32_t HELPER(test_exclusive)(CPUState *env, uint32_t addr)
+{
+    int res;
+
+    if (!env->mmon_entry)
+        return 1;
+    if (env->mmon_entry->addr == addr)
+        res = 0;
+    else
+        res = 1;
+    flush_mmon(addr);
+    return res;
+}
+
+void HELPER(clrex)(CPUState *env)
+{
+    if (!(env->mmon_entry && env->mmon_entry->addr))
+        return;
+    flush_mmon(env->mmon_entry->addr);
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+    return addr;
+}
+
+/* These should probably raise undefined insn exceptions.  */
+void HELPER(set_cp)(CPUState *env, uint32_t insn, uint32_t val)
+{
+    int op1 = (insn >> 8) & 0xf;
+    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
+    return;
+}
+
+uint32_t HELPER(get_cp)(CPUState *env, uint32_t insn)
+{
+    int op1 = (insn >> 8) & 0xf;
+    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
+    return 0;
+}
+
+void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
+{
+    cpu_abort(env, "cp15 insn %08x\n", insn);
+}
+
+uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
+{
+    cpu_abort(env, "cp15 insn %08x\n", insn);
+    return 0;
+}
+
+/* These should probably raise undefined insn exceptions.  */
+void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val)
+{
+    cpu_abort(env, "v7m_mrs %d\n", reg);
+}
+
+uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg)
+{
+    cpu_abort(env, "v7m_mrs %d\n", reg);
+    return 0;
+}
+
+void switch_mode(CPUState *env, int mode)
+{
+    if (mode != ARM_CPU_MODE_USR)
+        cpu_abort(env, "Tried to switch out of user mode\n");
+}
+
+void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val)
+{
+    cpu_abort(env, "banked r13 write\n");
+}
+
+uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode)
+{
+    cpu_abort(env, "banked r13 read\n");
+    return 0;
+}
+
+#else
+
+extern int semihosting_enabled;
+
+/* Map CPU modes onto saved register banks.  */
+static inline int bank_number (int mode)
+{
+    switch (mode) {
+    case ARM_CPU_MODE_USR:
+    case ARM_CPU_MODE_SYS:
+        return 0;
+    case ARM_CPU_MODE_SVC:
+        return 1;
+    case ARM_CPU_MODE_ABT:
+        return 2;
+    case ARM_CPU_MODE_UND:
+        return 3;
+    case ARM_CPU_MODE_IRQ:
+        return 4;
+    case ARM_CPU_MODE_FIQ:
+        return 5;
+    }
+    cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
+    return -1;
+}
+
+void switch_mode(CPUState *env, int mode)
+{
+    int old_mode;
+    int i;
+
+    old_mode = env->uncached_cpsr & CPSR_M;
+    if (mode == old_mode)
+        return;
+
+    if (old_mode == ARM_CPU_MODE_FIQ) {
+        memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
+        memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
+    } else if (mode == ARM_CPU_MODE_FIQ) {
+        memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
+        memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
+    }
+
+    i = bank_number(old_mode);
+    env->banked_r13[i] = env->regs[13];
+    env->banked_r14[i] = env->regs[14];
+    env->banked_spsr[i] = env->spsr;
+
+    i = bank_number(mode);
+    env->regs[13] = env->banked_r13[i];
+    env->regs[14] = env->banked_r14[i];
+    env->spsr = env->banked_spsr[i];
+}
+
+static void v7m_push(CPUARMState *env, uint32_t val)
+{
+    env->regs[13] -= 4;
+    stl_phys(env->regs[13], val);
+}
+
+static uint32_t v7m_pop(CPUARMState *env)
+{
+    uint32_t val;
+    val = ldl_phys(env->regs[13]);
+    env->regs[13] += 4;
+    return val;
+}
+
+/* Switch to V7M main or process stack pointer.  */
+static void switch_v7m_sp(CPUARMState *env, int process)
+{
+    uint32_t tmp;
+    if (env->v7m.current_sp != process) {
+        tmp = env->v7m.other_sp;
+        env->v7m.other_sp = env->regs[13];
+        env->regs[13] = tmp;
+        env->v7m.current_sp = process;
+    }
+}
+
+static void do_v7m_exception_exit(CPUARMState *env)
+{
+    uint32_t type;
+    uint32_t xpsr;
+
+    type = env->regs[15];
+    if (env->v7m.exception != 0)
+        armv7m_nvic_complete_irq(env->v7m.nvic, env->v7m.exception);
+
+    /* Switch to the target stack.  */
+    switch_v7m_sp(env, (type & 4) != 0);
+    /* Pop registers.  */
+    env->regs[0] = v7m_pop(env);
+    env->regs[1] = v7m_pop(env);
+    env->regs[2] = v7m_pop(env);
+    env->regs[3] = v7m_pop(env);
+    env->regs[12] = v7m_pop(env);
+    env->regs[14] = v7m_pop(env);
+    env->regs[15] = v7m_pop(env);
+    xpsr = v7m_pop(env);
+    xpsr_write(env, xpsr, 0xfffffdff);
+    /* Undo stack alignment.  */
+    if (xpsr & 0x200)
+        env->regs[13] |= 4;
+    /* ??? The exception return type specifies Thread/Handler mode.  However
+       this is also implied by the xPSR value. Not sure what to do
+       if there is a mismatch.  */
+    /* ??? Likewise for mismatches between the CONTROL register and the stack
+       pointer.  */
+}
+
+void do_interrupt_v7m(CPUARMState *env)
+{
+    uint32_t xpsr = xpsr_read(env);
+    uint32_t lr;
+    uint32_t addr;
+
+    lr = 0xfffffff1;
+    if (env->v7m.current_sp)
+        lr |= 4;
+    if (env->v7m.exception == 0)
+        lr |= 8;
+
+    /* For exceptions we just mark as pending on the NVIC, and let that
+       handle it.  */
+    /* TODO: Need to escalate if the current priority is higher than the
+       one we're raising.  */
+    switch (env->exception_index) {
+    case EXCP_UDEF:
+        armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_USAGE);
+        return;
+    case EXCP_SWI:
+        env->regs[15] += 2;
+        armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_SVC);
+        return;
+    case EXCP_PREFETCH_ABORT:
+    case EXCP_DATA_ABORT:
+        armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_MEM);
+        return;
+    case EXCP_BKPT:
+        if (semihosting_enabled) {
+            int nr;
+            nr = lduw_code(env->regs[15]) & 0xff;
+            if (nr == 0xab) {
+                env->regs[15] += 2;
+                env->regs[0] = do_arm_semihosting(env);
+                return;
+            }
+        }
+        armv7m_nvic_set_pending(env->v7m.nvic, ARMV7M_EXCP_DEBUG);
+        return;
+    case EXCP_IRQ:
+        env->v7m.exception = armv7m_nvic_acknowledge_irq(env->v7m.nvic);
+        break;
+    case EXCP_EXCEPTION_EXIT:
+        do_v7m_exception_exit(env);
+        return;
+    default:
+        cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
+        return; /* Never happens.  Keep compiler happy.  */
+    }
+
+    /* Align stack pointer.  */
+    /* ??? Should only do this if Configuration Control Register
+       STACKALIGN bit is set.  */
+    if (env->regs[13] & 4) {
+        env->regs[13] -= 4;
+        xpsr |= 0x200;
+    }
+    /* Switch to the handler mode.  */
+    v7m_push(env, xpsr);
+    v7m_push(env, env->regs[15]);
+    v7m_push(env, env->regs[14]);
+    v7m_push(env, env->regs[12]);
+    v7m_push(env, env->regs[3]);
+    v7m_push(env, env->regs[2]);
+    v7m_push(env, env->regs[1]);
+    v7m_push(env, env->regs[0]);
+    switch_v7m_sp(env, 0);
+    env->uncached_cpsr &= ~CPSR_IT;
+    env->regs[14] = lr;
+    addr = ldl_phys(env->v7m.vecbase + env->v7m.exception * 4);
+    env->regs[15] = addr & 0xfffffffe;
+    env->thumb = addr & 1;
+}
+
+/* Handle a CPU exception.  */
+void do_interrupt(CPUARMState *env)
+{
+    uint32_t addr;
+    uint32_t mask;
+    int new_mode;
+    uint32_t offset;
+
+    if (IS_M(env)) {
+        do_interrupt_v7m(env);
+        return;
+    }
+    /* TODO: Vectored interrupt controller.  */
+    switch (env->exception_index) {
+    case EXCP_UDEF:
+        new_mode = ARM_CPU_MODE_UND;
+        addr = 0x04;
+        mask = CPSR_I;
+        if (env->thumb)
+            offset = 2;
+        else
+            offset = 4;
+        break;
+    case EXCP_SWI:
+        if (semihosting_enabled) {
+            /* Check for semihosting interrupt.  */
+            if (env->thumb) {
+                mask = lduw_code(env->regs[15] - 2) & 0xff;
+            } else {
+                mask = ldl_code(env->regs[15] - 4) & 0xffffff;
+            }
+            /* Only intercept calls from privileged modes, to provide some
+               semblance of security.  */
+            if (((mask == 0x123456 && !env->thumb)
+                    || (mask == 0xab && env->thumb))
+                  && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
+                env->regs[0] = do_arm_semihosting(env);
+                return;
+            }
+        }
+        new_mode = ARM_CPU_MODE_SVC;
+        addr = 0x08;
+        mask = CPSR_I;
+        /* The PC already points to the next instruction.  */
+        offset = 0;
+        break;
+    case EXCP_BKPT:
+        /* See if this is a semihosting syscall.  */
+        if (env->thumb && semihosting_enabled) {
+            mask = lduw_code(env->regs[15]) & 0xff;
+            if (mask == 0xab
+                  && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
+                env->regs[15] += 2;
+                env->regs[0] = do_arm_semihosting(env);
+                return;
+            }
+        }
+        /* Fall through to prefetch abort.  */
+    case EXCP_PREFETCH_ABORT:
+        new_mode = ARM_CPU_MODE_ABT;
+        addr = 0x0c;
+        mask = CPSR_A | CPSR_I;
+        offset = 4;
+        break;
+    case EXCP_DATA_ABORT:
+        new_mode = ARM_CPU_MODE_ABT;
+        addr = 0x10;
+        mask = CPSR_A | CPSR_I;
+        offset = 8;
+        break;
+    case EXCP_IRQ:
+        new_mode = ARM_CPU_MODE_IRQ;
+        addr = 0x18;
+        /* Disable IRQ and imprecise data aborts.  */
+        mask = CPSR_A | CPSR_I;
+        offset = 4;
+        break;
+    case EXCP_FIQ:
+        new_mode = ARM_CPU_MODE_FIQ;
+        addr = 0x1c;
+        /* Disable FIQ, IRQ and imprecise data aborts.  */
+        mask = CPSR_A | CPSR_I | CPSR_F;
+        offset = 4;
+        break;
+    default:
+        cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
+        return; /* Never happens.  Keep compiler happy.  */
+    }
+    /* High vectors.  */
+    if (env->cp15.c1_sys & (1 << 13)) {
+        addr += 0xffff0000;
+    }
+    switch_mode (env, new_mode);
+    env->spsr = cpsr_read(env);
+    /* Clear IT bits.  */
+    env->condexec_bits = 0;
+    /* Switch to the new mode, and switch to Arm mode.  */
+    /* ??? Thumb interrupt handlers not implemented.  */
+    env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
+    env->uncached_cpsr |= mask;
+    env->thumb = 0;
+    env->regs[14] = env->regs[15] + offset;
+    env->regs[15] = addr;
+    env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+/* Check section/page access permissions.
+   Returns the page protection flags, or zero if the access is not
+   permitted.  */
+static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
+                           int is_user)
+{
+  int prot_ro;
+
+  if (domain == 3)
+    return PAGE_READ | PAGE_WRITE;
+
+  if (access_type == 1)
+      prot_ro = 0;
+  else
+      prot_ro = PAGE_READ;
+
+  switch (ap) {
+  case 0:
+      if (access_type == 1)
+          return 0;
+      switch ((env->cp15.c1_sys >> 8) & 3) {
+      case 1:
+          return is_user ? 0 : PAGE_READ;
+      case 2:
+          return PAGE_READ;
+      default:
+          return 0;
+      }
+  case 1:
+      return is_user ? 0 : PAGE_READ | PAGE_WRITE;
+  case 2:
+      if (is_user)
+          return prot_ro;
+      else
+          return PAGE_READ | PAGE_WRITE;
+  case 3:
+      return PAGE_READ | PAGE_WRITE;
+  case 4: case 7: /* Reserved.  */
+      return 0;
+  case 5:
+      return is_user ? 0 : prot_ro;
+  case 6:
+      return prot_ro;
+  default:
+      abort();
+  }
+}
+
+static int get_phys_addr_v5(CPUState *env, uint32_t address, int access_type,
+			    int is_user, uint32_t *phys_ptr, int *prot)
+{
+    int code;
+    uint32_t table;
+    uint32_t desc;
+    int type;
+    int ap;
+    int domain;
+    uint32_t phys_addr;
+
+    /* Pagetable walk.  */
+    /* Lookup l1 descriptor.  */
+    if (address & env->cp15.c2_mask)
+        table = env->cp15.c2_base1;
+    else
+        table = env->cp15.c2_base0;
+    table = (table & 0xffffc000) | ((address >> 18) & 0x3ffc);
+    desc = ldl_phys(table);
+    type = (desc & 3);
+    domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
+    if (type == 0) {
+        /* Section translation fault.  */
+        code = 5;
+        goto do_fault;
+    }
+    if (domain == 0 || domain == 2) {
+        if (type == 2)
+            code = 9; /* Section domain fault.  */
+        else
+            code = 11; /* Page domain fault.  */
+        goto do_fault;
+    }
+    if (type == 2) {
+        /* 1Mb section.  */
+        phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
+        ap = (desc >> 10) & 3;
+        code = 13;
+    } else {
+        /* Lookup l2 entry.  */
+	if (type == 1) {
+	    /* Coarse pagetable.  */
+	    table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
+	} else {
+	    /* Fine pagetable.  */
+	    table = (desc & 0xfffff000) | ((address >> 8) & 0xffc);
+	}
+        desc = ldl_phys(table);
+        switch (desc & 3) {
+        case 0: /* Page translation fault.  */
+            code = 7;
+            goto do_fault;
+        case 1: /* 64k page.  */
+            phys_addr = (desc & 0xffff0000) | (address & 0xffff);
+            ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
+            break;
+        case 2: /* 4k page.  */
+            phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+            ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
+            break;
+        case 3: /* 1k page.  */
+	    if (type == 1) {
+		if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+		    phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+		} else {
+		    /* Page translation fault.  */
+		    code = 7;
+		    goto do_fault;
+		}
+	    } else {
+		phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
+	    }
+            ap = (desc >> 4) & 3;
+            break;
+        default:
+            /* Never happens, but compiler isn't smart enough to tell.  */
+            abort();
+        }
+        code = 15;
+    }
+    *prot = check_ap(env, ap, domain, access_type, is_user);
+    if (!*prot) {
+        /* Access permission fault.  */
+        goto do_fault;
+    }
+    *phys_ptr = phys_addr;
+    return 0;
+do_fault:
+    return code | (domain << 4);
+}
+
+static int get_phys_addr_v6(CPUState *env, uint32_t address, int access_type,
+			    int is_user, uint32_t *phys_ptr, int *prot)
+{
+    int code;
+    uint32_t table;
+    uint32_t desc;
+    uint32_t xn;
+    int type;
+    int ap;
+    int domain;
+    uint32_t phys_addr;
+
+    /* Pagetable walk.  */
+    /* Lookup l1 descriptor.  */
+    if (address & env->cp15.c2_mask)
+        table = env->cp15.c2_base1;
+    else
+        table = env->cp15.c2_base0;
+    table = (table & 0xffffc000) | ((address >> 18) & 0x3ffc);
+    desc = ldl_phys(table);
+    type = (desc & 3);
+    if (type == 0) {
+        /* Section translation fault.  */
+        code = 5;
+        domain = 0;
+        goto do_fault;
+    } else if (type == 2 && (desc & (1 << 18))) {
+        /* Supersection.  */
+        domain = 0;
+    } else {
+        /* Section or page.  */
+        domain = (desc >> 4) & 0x1e;
+    }
+    domain = (env->cp15.c3 >> domain) & 3;
+    if (domain == 0 || domain == 2) {
+        if (type == 2)
+            code = 9; /* Section domain fault.  */
+        else
+            code = 11; /* Page domain fault.  */
+        goto do_fault;
+    }
+    if (type == 2) {
+        if (desc & (1 << 18)) {
+            /* Supersection.  */
+            phys_addr = (desc & 0xff000000) | (address & 0x00ffffff);
+        } else {
+            /* Section.  */
+            phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
+        }
+        ap = ((desc >> 10) & 3) | ((desc >> 13) & 4);
+        xn = desc & (1 << 4);
+        code = 13;
+    } else {
+        /* Lookup l2 entry.  */
+        table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
+        desc = ldl_phys(table);
+        ap = ((desc >> 4) & 3) | ((desc >> 7) & 4);
+        switch (desc & 3) {
+        case 0: /* Page translation fault.  */
+            code = 7;
+            goto do_fault;
+        case 1: /* 64k page.  */
+            phys_addr = (desc & 0xffff0000) | (address & 0xffff);
+            xn = desc & (1 << 15);
+            break;
+        case 2: case 3: /* 4k page.  */
+            phys_addr = (desc & 0xfffff000) | (address & 0xfff);
+            xn = desc & 1;
+            break;
+        default:
+            /* Never happens, but compiler isn't smart enough to tell.  */
+            abort();
+        }
+        code = 15;
+    }
+    if (xn && access_type == 2)
+        goto do_fault;
+
+    *prot = check_ap(env, ap, domain, access_type, is_user);
+    if (!*prot) {
+        /* Access permission fault.  */
+        goto do_fault;
+    }
+    *phys_ptr = phys_addr;
+    return 0;
+do_fault:
+    return code | (domain << 4);
+}
+
+static int get_phys_addr_mpu(CPUState *env, uint32_t address, int access_type,
+			     int is_user, uint32_t *phys_ptr, int *prot)
+{
+    int n;
+    uint32_t mask;
+    uint32_t base;
+
+    *phys_ptr = address;
+    for (n = 7; n >= 0; n--) {
+	base = env->cp15.c6_region[n];
+	if ((base & 1) == 0)
+	    continue;
+	mask = 1 << ((base >> 1) & 0x1f);
+	/* Keep this shift separate from the above to avoid an
+	   (undefined) << 32.  */
+	mask = (mask << 1) - 1;
+	if (((base ^ address) & ~mask) == 0)
+	    break;
+    }
+    if (n < 0)
+	return 2;
+
+    if (access_type == 2) {
+	mask = env->cp15.c5_insn;
+    } else {
+	mask = env->cp15.c5_data;
+    }
+    mask = (mask >> (n * 4)) & 0xf;
+    switch (mask) {
+    case 0:
+	return 1;
+    case 1:
+	if (is_user)
+	  return 1;
+	*prot = PAGE_READ | PAGE_WRITE;
+	break;
+    case 2:
+	*prot = PAGE_READ;
+	if (!is_user)
+	    *prot |= PAGE_WRITE;
+	break;
+    case 3:
+	*prot = PAGE_READ | PAGE_WRITE;
+	break;
+    case 5:
+	if (is_user)
+	    return 1;
+	*prot = PAGE_READ;
+	break;
+    case 6:
+	*prot = PAGE_READ;
+	break;
+    default:
+	/* Bad permission.  */
+	return 1;
+    }
+    return 0;
+}
+
+static inline int get_phys_addr(CPUState *env, uint32_t address,
+                                int access_type, int is_user,
+                                uint32_t *phys_ptr, int *prot)
+{
+    /* Fast Context Switch Extension.  */
+    if (address < 0x02000000)
+        address += env->cp15.c13_fcse;
+
+    if ((env->cp15.c1_sys & 1) == 0) {
+        /* MMU/MPU disabled.  */
+        *phys_ptr = address;
+        *prot = PAGE_READ | PAGE_WRITE;
+        return 0;
+    } else if (arm_feature(env, ARM_FEATURE_MPU)) {
+	return get_phys_addr_mpu(env, address, access_type, is_user, phys_ptr,
+				 prot);
+    } else if (env->cp15.c1_sys & (1 << 23)) {
+        return get_phys_addr_v6(env, address, access_type, is_user, phys_ptr,
+                                prot);
+    } else {
+        return get_phys_addr_v5(env, address, access_type, is_user, phys_ptr,
+                                prot);
+    }
+}
+
+int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
+                              int access_type, int mmu_idx, int is_softmmu)
+{
+    uint32_t phys_addr;
+    int prot;
+    int ret, is_user;
+
+    is_user = mmu_idx == MMU_USER_IDX;
+    ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot);
+    if (ret == 0) {
+        /* Map a single [sub]page.  */
+        phys_addr &= ~(uint32_t)0x3ff;
+        address &= ~(uint32_t)0x3ff;
+        return tlb_set_page (env, address, phys_addr, prot, mmu_idx,
+                             is_softmmu);
+    }
+
+    if (access_type == 2) {
+        env->cp15.c5_insn = ret;
+        env->cp15.c6_insn = address;
+        env->exception_index = EXCP_PREFETCH_ABORT;
+    } else {
+        env->cp15.c5_data = ret;
+        if (access_type == 1 && arm_feature(env, ARM_FEATURE_V6))
+            env->cp15.c5_data |= (1 << 11);
+        env->cp15.c6_data = address;
+        env->exception_index = EXCP_DATA_ABORT;
+    }
+    return 1;
+}
+
+target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+    uint32_t phys_addr;
+    int prot;
+    int ret;
+
+    ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot);
+
+    if (ret != 0)
+        return -1;
+
+    return phys_addr;
+}
+
+/* Not really implemented.  Need to figure out a sane way of doing this.
+   Maybe add generic watchpoint support and use that.  */
+
+void HELPER(mark_exclusive)(CPUState *env, uint32_t addr)
+{
+    env->mmon_addr = addr;
+}
+
+uint32_t HELPER(test_exclusive)(CPUState *env, uint32_t addr)
+{
+    return (env->mmon_addr != addr);
+}
+
+void HELPER(clrex)(CPUState *env)
+{
+    env->mmon_addr = -1;
+}
+
+void HELPER(set_cp)(CPUState *env, uint32_t insn, uint32_t val)
+{
+    int cp_num = (insn >> 8) & 0xf;
+    int cp_info = (insn >> 5) & 7;
+    int src = (insn >> 16) & 0xf;
+    int operand = insn & 0xf;
+
+    if (env->cp[cp_num].cp_write)
+        env->cp[cp_num].cp_write(env->cp[cp_num].opaque,
+                                 cp_info, src, operand, val);
+}
+
+uint32_t HELPER(get_cp)(CPUState *env, uint32_t insn)
+{
+    int cp_num = (insn >> 8) & 0xf;
+    int cp_info = (insn >> 5) & 7;
+    int dest = (insn >> 16) & 0xf;
+    int operand = insn & 0xf;
+
+    if (env->cp[cp_num].cp_read)
+        return env->cp[cp_num].cp_read(env->cp[cp_num].opaque,
+                                       cp_info, dest, operand);
+    return 0;
+}
+
+/* Return basic MPU access permission bits.  */
+static uint32_t simple_mpu_ap_bits(uint32_t val)
+{
+    uint32_t ret;
+    uint32_t mask;
+    int i;
+    ret = 0;
+    mask = 3;
+    for (i = 0; i < 16; i += 2) {
+        ret |= (val >> i) & mask;
+        mask <<= 2;
+    }
+    return ret;
+}
+
+/* Pad basic MPU access permission bits to extended format.  */
+static uint32_t extended_mpu_ap_bits(uint32_t val)
+{
+    uint32_t ret;
+    uint32_t mask;
+    int i;
+    ret = 0;
+    mask = 3;
+    for (i = 0; i < 16; i += 2) {
+        ret |= (val & mask) << i;
+        mask <<= 2;
+    }
+    return ret;
+}
+
+void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
+{
+    int op1;
+    int op2;
+    int crm;
+
+    op1 = (insn >> 21) & 7;
+    op2 = (insn >> 5) & 7;
+    crm = insn & 0xf;
+    switch ((insn >> 16) & 0xf) {
+    case 0:
+        if (((insn >> 21) & 7) == 2) {
+            /* ??? Select cache level.  Ignore.  */
+            return;
+        }
+        /* ID codes.  */
+        if (arm_feature(env, ARM_FEATURE_XSCALE))
+            break;
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            break;
+        goto bad_reg;
+    case 1: /* System configuration.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
+        switch (op2) {
+        case 0:
+            if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0)
+                env->cp15.c1_sys = val;
+            /* ??? Lots of these bits are not implemented.  */
+            /* This may enable/disable the MMU, so do a TLB flush.  */
+            tlb_flush(env, 1);
+            break;
+        case 1: /* Auxiliary cotrol register.  */
+            if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+                env->cp15.c1_xscaleauxcr = val;
+                break;
+            }
+            /* Not implemented.  */
+            break;
+        case 2:
+            if (arm_feature(env, ARM_FEATURE_XSCALE))
+                goto bad_reg;
+            env->cp15.c1_coproc = val;
+            /* ??? Is this safe when called from within a TB?  */
+            tb_flush(env);
+            break;
+        default:
+            goto bad_reg;
+        }
+        break;
+    case 2: /* MMU Page table control / MPU cache control.  */
+        if (arm_feature(env, ARM_FEATURE_MPU)) {
+            switch (op2) {
+            case 0:
+                env->cp15.c2_data = val;
+                break;
+            case 1:
+                env->cp15.c2_insn = val;
+                break;
+            default:
+                goto bad_reg;
+            }
+        } else {
+	    switch (op2) {
+	    case 0:
+		env->cp15.c2_base0 = val;
+		break;
+	    case 1:
+		env->cp15.c2_base1 = val;
+		break;
+	    case 2:
+		env->cp15.c2_mask = ~(((uint32_t)0xffffffffu) >> val);
+		break;
+	    default:
+		goto bad_reg;
+	    }
+        }
+        break;
+    case 3: /* MMU Domain access control / MPU write buffer control.  */
+        env->cp15.c3 = val;
+        tlb_flush(env, 1); /* Flush TLB as domain not tracked in TLB */
+        break;
+    case 4: /* Reserved.  */
+        goto bad_reg;
+    case 5: /* MMU Fault status / MPU access permission.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
+        switch (op2) {
+        case 0:
+            if (arm_feature(env, ARM_FEATURE_MPU))
+                val = extended_mpu_ap_bits(val);
+            env->cp15.c5_data = val;
+            break;
+        case 1:
+            if (arm_feature(env, ARM_FEATURE_MPU))
+                val = extended_mpu_ap_bits(val);
+            env->cp15.c5_insn = val;
+            break;
+        case 2:
+            if (!arm_feature(env, ARM_FEATURE_MPU))
+                goto bad_reg;
+            env->cp15.c5_data = val;
+            break;
+        case 3:
+            if (!arm_feature(env, ARM_FEATURE_MPU))
+                goto bad_reg;
+            env->cp15.c5_insn = val;
+            break;
+        default:
+            goto bad_reg;
+        }
+        break;
+    case 6: /* MMU Fault address / MPU base/size.  */
+        if (arm_feature(env, ARM_FEATURE_MPU)) {
+            if (crm >= 8)
+                goto bad_reg;
+            env->cp15.c6_region[crm] = val;
+        } else {
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                op2 = 0;
+            switch (op2) {
+            case 0:
+                env->cp15.c6_data = val;
+                break;
+            case 1: /* ??? This is WFAR on armv6 */
+            case 2:
+                env->cp15.c6_insn = val;
+                break;
+            default:
+                goto bad_reg;
+            }
+        }
+        break;
+    case 7: /* Cache control.  */
+        env->cp15.c15_i_max = 0x000;
+        env->cp15.c15_i_min = 0xff0;
+        /* No cache, so nothing to do.  */
+        /* ??? MPCore has VA to PA translation functions.  */
+        break;
+    case 8: /* MMU TLB control.  */
+        switch (op2) {
+        case 0: /* Invalidate all.  */
+            tlb_flush(env, 0);
+            break;
+        case 1: /* Invalidate single TLB entry.  */
+#if 0
+            /* ??? This is wrong for large pages and sections.  */
+            /* As an ugly hack to make linux work we always flush a 4K
+               pages.  */
+            val &= 0xfffff000;
+            tlb_flush_page(env, val);
+            tlb_flush_page(env, val + 0x400);
+            tlb_flush_page(env, val + 0x800);
+            tlb_flush_page(env, val + 0xc00);
+#else
+            tlb_flush(env, 1);
+#endif
+            break;
+        case 2: /* Invalidate on ASID.  */
+            tlb_flush(env, val == 0);
+            break;
+        case 3: /* Invalidate single entry on MVA.  */
+            /* ??? This is like case 1, but ignores ASID.  */
+            tlb_flush(env, 1);
+            break;
+        default:
+            goto bad_reg;
+        }
+        break;
+    case 9:
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            break;
+        switch (crm) {
+        case 0: /* Cache lockdown.  */
+	    switch (op1) {
+	    case 0: /* L1 cache.  */
+		switch (op2) {
+		case 0:
+		    env->cp15.c9_data = val;
+		    break;
+		case 1:
+		    env->cp15.c9_insn = val;
+		    break;
+		default:
+		    goto bad_reg;
+		}
+		break;
+	    case 1: /* L2 cache.  */
+		/* Ignore writes to L2 lockdown/auxiliary registers.  */
+		break;
+	    default:
+		goto bad_reg;
+	    }
+	    break;
+        case 1: /* TCM memory region registers.  */
+            /* Not implemented.  */
+            goto bad_reg;
+        default:
+            goto bad_reg;
+        }
+        break;
+    case 10: /* MMU TLB lockdown.  */
+        /* ??? TLB lockdown not implemented.  */
+        break;
+    case 12: /* Reserved.  */
+        goto bad_reg;
+    case 13: /* Process ID.  */
+        switch (op2) {
+        case 0:
+            /* Unlike real hardware the qemu TLB uses virtual addresses,
+               not modified virtual addresses, so this causes a TLB flush.
+             */
+            if (env->cp15.c13_fcse != val)
+              tlb_flush(env, 1);
+            env->cp15.c13_fcse = val;
+            break;
+        case 1:
+            /* This changes the ASID, so do a TLB flush.  */
+            if (env->cp15.c13_context != val
+                && !arm_feature(env, ARM_FEATURE_MPU))
+              tlb_flush(env, 0);
+            env->cp15.c13_context = val;
+            break;
+        case 2:
+            env->cp15.c13_tls1 = val;
+            break;
+        case 3:
+            env->cp15.c13_tls2 = val;
+            break;
+        case 4:
+            env->cp15.c13_tls3 = val;
+            break;
+        default:
+            goto bad_reg;
+        }
+        break;
+    case 14: /* Reserved.  */
+        goto bad_reg;
+    case 15: /* Implementation specific.  */
+        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+            if (op2 == 0 && crm == 1) {
+                if (env->cp15.c15_cpar != (val & 0x3fff)) {
+                    /* Changes cp0 to cp13 behavior, so needs a TB flush.  */
+                    tb_flush(env);
+                    env->cp15.c15_cpar = val & 0x3fff;
+                }
+                break;
+            }
+            goto bad_reg;
+        }
+        if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+            switch (crm) {
+            case 0:
+                break;
+            case 1: /* Set TI925T configuration.  */
+                env->cp15.c15_ticonfig = val & 0xe7;
+                env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */
+                        ARM_CPUID_TI915T : ARM_CPUID_TI925T;
+                break;
+            case 2: /* Set I_max.  */
+                env->cp15.c15_i_max = val;
+                break;
+            case 3: /* Set I_min.  */
+                env->cp15.c15_i_min = val;
+                break;
+            case 4: /* Set thread-ID.  */
+                env->cp15.c15_threadid = val & 0xffff;
+                break;
+            case 8: /* Wait-for-interrupt (deprecated).  */
+                cpu_interrupt(env, CPU_INTERRUPT_HALT);
+                break;
+            default:
+                goto bad_reg;
+            }
+        }
+        break;
+    }
+    return;
+bad_reg:
+    /* ??? For debugging only.  Should raise illegal instruction exception.  */
+    cpu_abort(env, "Unimplemented cp15 register write (c%d, c%d, {%d, %d})\n",
+              (insn >> 16) & 0xf, crm, op1, op2);
+}
+
+uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
+{
+    int op1;
+    int op2;
+    int crm;
+
+    op1 = (insn >> 21) & 7;
+    op2 = (insn >> 5) & 7;
+    crm = insn & 0xf;
+    switch ((insn >> 16) & 0xf) {
+    case 0: /* ID codes.  */
+        switch (op1) {
+        case 0:
+            switch (crm) {
+            case 0:
+                switch (op2) {
+                case 0: /* Device ID.  */
+                    return env->cp15.c0_cpuid;
+                case 1: /* Cache Type.  */
+		    return env->cp15.c0_cachetype;
+                case 2: /* TCM status.  */
+                    return 0;
+                case 3: /* TLB type register.  */
+                    return 0; /* No lockable TLB entries.  */
+                case 5: /* CPU ID */
+                    return env->cpu_index;
+                default:
+                    goto bad_reg;
+                }
+            case 1:
+                if (!arm_feature(env, ARM_FEATURE_V6))
+                    goto bad_reg;
+                return env->cp15.c0_c1[op2];
+            case 2:
+                if (!arm_feature(env, ARM_FEATURE_V6))
+                    goto bad_reg;
+                return env->cp15.c0_c2[op2];
+            case 3: case 4: case 5: case 6: case 7:
+                return 0;
+            default:
+                goto bad_reg;
+            }
+        case 1:
+            /* These registers aren't documented on arm11 cores.  However
+               Linux looks at them anyway.  */
+            if (!arm_feature(env, ARM_FEATURE_V6))
+                goto bad_reg;
+            if (crm != 0)
+                goto bad_reg;
+            if (arm_feature(env, ARM_FEATURE_XSCALE))
+                goto bad_reg;
+            return 0;
+        default:
+            goto bad_reg;
+        }
+    case 1: /* System configuration.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
+        switch (op2) {
+        case 0: /* Control register.  */
+            return env->cp15.c1_sys;
+        case 1: /* Auxiliary control register.  */
+            if (arm_feature(env, ARM_FEATURE_XSCALE))
+                return env->cp15.c1_xscaleauxcr;
+            if (!arm_feature(env, ARM_FEATURE_AUXCR))
+                goto bad_reg;
+            switch (ARM_CPUID(env)) {
+            case ARM_CPUID_ARM1026:
+                return 1;
+            case ARM_CPUID_ARM1136:
+            case ARM_CPUID_ARM1136_R2:
+                return 7;
+            case ARM_CPUID_ARM11MPCORE:
+                return 1;
+            case ARM_CPUID_CORTEXA8:
+                return 0;
+            default:
+                goto bad_reg;
+            }
+        case 2: /* Coprocessor access register.  */
+            if (arm_feature(env, ARM_FEATURE_XSCALE))
+                goto bad_reg;
+            return env->cp15.c1_coproc;
+        default:
+            goto bad_reg;
+        }
+    case 2: /* MMU Page table control / MPU cache control.  */
+        if (arm_feature(env, ARM_FEATURE_MPU)) {
+            switch (op2) {
+            case 0:
+                return env->cp15.c2_data;
+                break;
+            case 1:
+                return env->cp15.c2_insn;
+                break;
+            default:
+                goto bad_reg;
+            }
+        } else {
+	    switch (op2) {
+	    case 0:
+		return env->cp15.c2_base0;
+	    case 1:
+		return env->cp15.c2_base1;
+	    case 2:
+		{
+		    int n;
+		    uint32_t mask;
+		    n = 0;
+		    mask = env->cp15.c2_mask;
+		    while (mask) {
+			n++;
+			mask <<= 1;
+		    }
+		    return n;
+		}
+	    default:
+		goto bad_reg;
+	    }
+	}
+    case 3: /* MMU Domain access control / MPU write buffer control.  */
+        return env->cp15.c3;
+    case 4: /* Reserved.  */
+        goto bad_reg;
+    case 5: /* MMU Fault status / MPU access permission.  */
+        if (arm_feature(env, ARM_FEATURE_OMAPCP))
+            op2 = 0;
+        switch (op2) {
+        case 0:
+            if (arm_feature(env, ARM_FEATURE_MPU))
+                return simple_mpu_ap_bits(env->cp15.c5_data);
+            return env->cp15.c5_data;
+        case 1:
+            if (arm_feature(env, ARM_FEATURE_MPU))
+                return simple_mpu_ap_bits(env->cp15.c5_data);
+            return env->cp15.c5_insn;
+        case 2:
+            if (!arm_feature(env, ARM_FEATURE_MPU))
+                goto bad_reg;
+            return env->cp15.c5_data;
+        case 3:
+            if (!arm_feature(env, ARM_FEATURE_MPU))
+                goto bad_reg;
+            return env->cp15.c5_insn;
+        default:
+            goto bad_reg;
+        }
+    case 6: /* MMU Fault address.  */
+        if (arm_feature(env, ARM_FEATURE_MPU)) {
+            if (crm >= 8)
+                goto bad_reg;
+            return env->cp15.c6_region[crm];
+        } else {
+            if (arm_feature(env, ARM_FEATURE_OMAPCP))
+                op2 = 0;
+	    switch (op2) {
+	    case 0:
+		return env->cp15.c6_data;
+	    case 1:
+		if (arm_feature(env, ARM_FEATURE_V6)) {
+		    /* Watchpoint Fault Adrress.  */
+		    return 0; /* Not implemented.  */
+		} else {
+		    /* Instruction Fault Adrress.  */
+		    /* Arm9 doesn't have an IFAR, but implementing it anyway
+		       shouldn't do any harm.  */
+		    return env->cp15.c6_insn;
+		}
+	    case 2:
+		if (arm_feature(env, ARM_FEATURE_V6)) {
+		    /* Instruction Fault Adrress.  */
+		    return env->cp15.c6_insn;
+		} else {
+		    goto bad_reg;
+		}
+	    default:
+		goto bad_reg;
+	    }
+        }
+    case 7: /* Cache control.  */
+        /* FIXME: Should only clear Z flag if destination is r15.  */
+        env->ZF = 0;
+        return 0;
+    case 8: /* MMU TLB control.  */
+        goto bad_reg;
+    case 9: /* Cache lockdown.  */
+        switch (op1) {
+        case 0: /* L1 cache.  */
+	    if (arm_feature(env, ARM_FEATURE_OMAPCP))
+		return 0;
+            switch (op2) {
+            case 0:
+                return env->cp15.c9_data;
+            case 1:
+                return env->cp15.c9_insn;
+            default:
+                goto bad_reg;
+            }
+        case 1: /* L2 cache */
+            if (crm != 0)
+                goto bad_reg;
+            /* L2 Lockdown and Auxiliary control.  */
+            return 0;
+        default:
+            goto bad_reg;
+        }
+    case 10: /* MMU TLB lockdown.  */
+        /* ??? TLB lockdown not implemented.  */
+        return 0;
+    case 11: /* TCM DMA control.  */
+    case 12: /* Reserved.  */
+        goto bad_reg;
+    case 13: /* Process ID.  */
+        switch (op2) {
+        case 0:
+            return env->cp15.c13_fcse;
+        case 1:
+            return env->cp15.c13_context;
+        case 2:
+            return env->cp15.c13_tls1;
+        case 3:
+            return env->cp15.c13_tls2;
+        case 4:
+            return env->cp15.c13_tls3;
+        default:
+            goto bad_reg;
+        }
+    case 14: /* Reserved.  */
+        goto bad_reg;
+    case 15: /* Implementation specific.  */
+        if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+            if (op2 == 0 && crm == 1)
+                return env->cp15.c15_cpar;
+
+            goto bad_reg;
+        }
+        if (arm_feature(env, ARM_FEATURE_OMAPCP)) {
+            switch (crm) {
+            case 0:
+                return 0;
+            case 1: /* Read TI925T configuration.  */
+                return env->cp15.c15_ticonfig;
+            case 2: /* Read I_max.  */
+                return env->cp15.c15_i_max;
+            case 3: /* Read I_min.  */
+                return env->cp15.c15_i_min;
+            case 4: /* Read thread-ID.  */
+                return env->cp15.c15_threadid;
+            case 8: /* TI925T_status */
+                return 0;
+            }
+            /* TODO: Peripheral port remap register:
+             * On OMAP2 mcr p15, 0, rn, c15, c2, 4 sets up the interrupt
+             * controller base address at $rn & ~0xfff and map size of
+             * 0x200 << ($rn & 0xfff), when MMU is off.  */
+            goto bad_reg;
+        }
+        return 0;
+    }
+bad_reg:
+    /* ??? For debugging only.  Should raise illegal instruction exception.  */
+    cpu_abort(env, "Unimplemented cp15 register read (c%d, c%d, {%d, %d})\n",
+              (insn >> 16) & 0xf, crm, op1, op2);
+    return 0;
+}
+
+void HELPER(set_r13_banked)(CPUState *env, uint32_t mode, uint32_t val)
+{
+    env->banked_r13[bank_number(mode)] = val;
+}
+
+uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode)
+{
+    return env->banked_r13[bank_number(mode)];
+}
+
+uint32_t HELPER(v7m_mrs)(CPUState *env, uint32_t reg)
+{
+    switch (reg) {
+    case 0: /* APSR */
+        return xpsr_read(env) & 0xf8000000;
+    case 1: /* IAPSR */
+        return xpsr_read(env) & 0xf80001ff;
+    case 2: /* EAPSR */
+        return xpsr_read(env) & 0xff00fc00;
+    case 3: /* xPSR */
+        return xpsr_read(env) & 0xff00fdff;
+    case 5: /* IPSR */
+        return xpsr_read(env) & 0x000001ff;
+    case 6: /* EPSR */
+        return xpsr_read(env) & 0x0700fc00;
+    case 7: /* IEPSR */
+        return xpsr_read(env) & 0x0700edff;
+    case 8: /* MSP */
+        return env->v7m.current_sp ? env->v7m.other_sp : env->regs[13];
+    case 9: /* PSP */
+        return env->v7m.current_sp ? env->regs[13] : env->v7m.other_sp;
+    case 16: /* PRIMASK */
+        return (env->uncached_cpsr & CPSR_I) != 0;
+    case 17: /* FAULTMASK */
+        return (env->uncached_cpsr & CPSR_F) != 0;
+    case 18: /* BASEPRI */
+    case 19: /* BASEPRI_MAX */
+        return env->v7m.basepri;
+    case 20: /* CONTROL */
+        return env->v7m.control;
+    default:
+        /* ??? For debugging only.  */
+        cpu_abort(env, "Unimplemented system register read (%d)\n", reg);
+        return 0;
+    }
+}
+
+void HELPER(v7m_msr)(CPUState *env, uint32_t reg, uint32_t val)
+{
+    switch (reg) {
+    case 0: /* APSR */
+        xpsr_write(env, val, 0xf8000000);
+        break;
+    case 1: /* IAPSR */
+        xpsr_write(env, val, 0xf8000000);
+        break;
+    case 2: /* EAPSR */
+        xpsr_write(env, val, 0xfe00fc00);
+        break;
+    case 3: /* xPSR */
+        xpsr_write(env, val, 0xfe00fc00);
+        break;
+    case 5: /* IPSR */
+        /* IPSR bits are readonly.  */
+        break;
+    case 6: /* EPSR */
+        xpsr_write(env, val, 0x0600fc00);
+        break;
+    case 7: /* IEPSR */
+        xpsr_write(env, val, 0x0600fc00);
+        break;
+    case 8: /* MSP */
+        if (env->v7m.current_sp)
+            env->v7m.other_sp = val;
+        else
+            env->regs[13] = val;
+        break;
+    case 9: /* PSP */
+        if (env->v7m.current_sp)
+            env->regs[13] = val;
+        else
+            env->v7m.other_sp = val;
+        break;
+    case 16: /* PRIMASK */
+        if (val & 1)
+            env->uncached_cpsr |= CPSR_I;
+        else
+            env->uncached_cpsr &= ~CPSR_I;
+        break;
+    case 17: /* FAULTMASK */
+        if (val & 1)
+            env->uncached_cpsr |= CPSR_F;
+        else
+            env->uncached_cpsr &= ~CPSR_F;
+        break;
+    case 18: /* BASEPRI */
+        env->v7m.basepri = val & 0xff;
+        break;
+    case 19: /* BASEPRI_MAX */
+        val &= 0xff;
+        if (val != 0 && (val < env->v7m.basepri || env->v7m.basepri == 0))
+            env->v7m.basepri = val;
+        break;
+    case 20: /* CONTROL */
+        env->v7m.control = val & 3;
+        switch_v7m_sp(env, (val & 2) != 0);
+        break;
+    default:
+        /* ??? For debugging only.  */
+        cpu_abort(env, "Unimplemented system register write (%d)\n", reg);
+        return;
+    }
+}
+
+void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
+                ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write,
+                void *opaque)
+{
+    if (cpnum < 0 || cpnum > 14) {
+        cpu_abort(env, "Bad coprocessor number: %i\n", cpnum);
+        return;
+    }
+
+    env->cp[cpnum].cp_read = cp_read;
+    env->cp[cpnum].cp_write = cp_write;
+    env->cp[cpnum].opaque = opaque;
+}
+
+#endif
+
+/* Note that signed overflow is undefined in C.  The following routines are
+   careful to use unsigned types where modulo arithmetic is required.
+   Failure to do so _will_ break on newer gcc.  */
+
+/* Signed saturating arithmetic.  */
+
+/* Perform 16-bit signed saturating addition.  */
+static inline uint16_t add16_sat(uint16_t a, uint16_t b)
+{
+    uint16_t res;
+
+    res = a + b;
+    if (((res ^ a) & 0x8000) && !((a ^ b) & 0x8000)) {
+        if (a & 0x8000)
+            res = 0x8000;
+        else
+            res = 0x7fff;
+    }
+    return res;
+}
+
+/* Perform 8-bit signed saturating addition.  */
+static inline uint8_t add8_sat(uint8_t a, uint8_t b)
+{
+    uint8_t res;
+
+    res = a + b;
+    if (((res ^ a) & 0x80) && !((a ^ b) & 0x80)) {
+        if (a & 0x80)
+            res = 0x80;
+        else
+            res = 0x7f;
+    }
+    return res;
+}
+
+/* Perform 16-bit signed saturating subtraction.  */
+static inline uint16_t sub16_sat(uint16_t a, uint16_t b)
+{
+    uint16_t res;
+
+    res = a - b;
+    if (((res ^ a) & 0x8000) && ((a ^ b) & 0x8000)) {
+        if (a & 0x8000)
+            res = 0x8000;
+        else
+            res = 0x7fff;
+    }
+    return res;
+}
+
+/* Perform 8-bit signed saturating subtraction.  */
+static inline uint8_t sub8_sat(uint8_t a, uint8_t b)
+{
+    uint8_t res;
+
+    res = a - b;
+    if (((res ^ a) & 0x80) && ((a ^ b) & 0x80)) {
+        if (a & 0x80)
+            res = 0x80;
+        else
+            res = 0x7f;
+    }
+    return res;
+}
+
+#define ADD16(a, b, n) RESULT(add16_sat(a, b), n, 16);
+#define SUB16(a, b, n) RESULT(sub16_sat(a, b), n, 16);
+#define ADD8(a, b, n)  RESULT(add8_sat(a, b), n, 8);
+#define SUB8(a, b, n)  RESULT(sub8_sat(a, b), n, 8);
+#define PFX q
+
+#include "op_addsub.h"
+
+/* Unsigned saturating arithmetic.  */
+static inline uint16_t add16_usat(uint16_t a, uint16_t b)
+{
+    uint16_t res;
+    res = a + b;
+    if (res < a)
+        res = 0xffff;
+    return res;
+}
+
+static inline uint16_t sub16_usat(uint16_t a, uint16_t b)
+{
+    if (a < b)
+        return a - b;
+    else
+        return 0;
+}
+
+static inline uint8_t add8_usat(uint8_t a, uint8_t b)
+{
+    uint8_t res;
+    res = a + b;
+    if (res < a)
+        res = 0xff;
+    return res;
+}
+
+static inline uint8_t sub8_usat(uint8_t a, uint8_t b)
+{
+    if (a < b)
+        return a - b;
+    else
+        return 0;
+}
+
+#define ADD16(a, b, n) RESULT(add16_usat(a, b), n, 16);
+#define SUB16(a, b, n) RESULT(sub16_usat(a, b), n, 16);
+#define ADD8(a, b, n)  RESULT(add8_usat(a, b), n, 8);
+#define SUB8(a, b, n)  RESULT(sub8_usat(a, b), n, 8);
+#define PFX uq
+
+#include "op_addsub.h"
+
+/* Signed modulo arithmetic.  */
+#define SARITH16(a, b, n, op) do { \
+    int32_t sum; \
+    sum = (int16_t)((uint16_t)(a) op (uint16_t)(b)); \
+    RESULT(sum, n, 16); \
+    if (sum >= 0) \
+        ge |= 3 << (n * 2); \
+    } while(0)
+
+#define SARITH8(a, b, n, op) do { \
+    int32_t sum; \
+    sum = (int8_t)((uint8_t)(a) op (uint8_t)(b)); \
+    RESULT(sum, n, 8); \
+    if (sum >= 0) \
+        ge |= 1 << n; \
+    } while(0)
+
+
+#define ADD16(a, b, n) SARITH16(a, b, n, +)
+#define SUB16(a, b, n) SARITH16(a, b, n, -)
+#define ADD8(a, b, n)  SARITH8(a, b, n, +)
+#define SUB8(a, b, n)  SARITH8(a, b, n, -)
+#define PFX s
+#define ARITH_GE
+
+#include "op_addsub.h"
+
+/* Unsigned modulo arithmetic.  */
+#define ADD16(a, b, n) do { \
+    uint32_t sum; \
+    sum = (uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b); \
+    RESULT(sum, n, 16); \
+    if ((sum >> 16) == 1) \
+        ge |= 3 << (n * 2); \
+    } while(0)
+
+#define ADD8(a, b, n) do { \
+    uint32_t sum; \
+    sum = (uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b); \
+    RESULT(sum, n, 8); \
+    if ((sum >> 8) == 1) \
+        ge |= 1 << n; \
+    } while(0)
+
+#define SUB16(a, b, n) do { \
+    uint32_t sum; \
+    sum = (uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b); \
+    RESULT(sum, n, 16); \
+    if ((sum >> 16) == 0) \
+        ge |= 3 << (n * 2); \
+    } while(0)
+
+#define SUB8(a, b, n) do { \
+    uint32_t sum; \
+    sum = (uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b); \
+    RESULT(sum, n, 8); \
+    if ((sum >> 8) == 0) \
+        ge |= 1 << n; \
+    } while(0)
+
+#define PFX u
+#define ARITH_GE
+
+#include "op_addsub.h"
+
+/* Halved signed arithmetic.  */
+#define ADD16(a, b, n) \
+  RESULT(((int32_t)(int16_t)(a) + (int32_t)(int16_t)(b)) >> 1, n, 16)
+#define SUB16(a, b, n) \
+  RESULT(((int32_t)(int16_t)(a) - (int32_t)(int16_t)(b)) >> 1, n, 16)
+#define ADD8(a, b, n) \
+  RESULT(((int32_t)(int8_t)(a) + (int32_t)(int8_t)(b)) >> 1, n, 8)
+#define SUB8(a, b, n) \
+  RESULT(((int32_t)(int8_t)(a) - (int32_t)(int8_t)(b)) >> 1, n, 8)
+#define PFX sh
+
+#include "op_addsub.h"
+
+/* Halved unsigned arithmetic.  */
+#define ADD16(a, b, n) \
+  RESULT(((uint32_t)(uint16_t)(a) + (uint32_t)(uint16_t)(b)) >> 1, n, 16)
+#define SUB16(a, b, n) \
+  RESULT(((uint32_t)(uint16_t)(a) - (uint32_t)(uint16_t)(b)) >> 1, n, 16)
+#define ADD8(a, b, n) \
+  RESULT(((uint32_t)(uint8_t)(a) + (uint32_t)(uint8_t)(b)) >> 1, n, 8)
+#define SUB8(a, b, n) \
+  RESULT(((uint32_t)(uint8_t)(a) - (uint32_t)(uint8_t)(b)) >> 1, n, 8)
+#define PFX uh
+
+#include "op_addsub.h"
+
+static inline uint8_t do_usad(uint8_t a, uint8_t b)
+{
+    if (a > b)
+        return a - b;
+    else
+        return b - a;
+}
+
+/* Unsigned sum of absolute byte differences.  */
+uint32_t HELPER(usad8)(uint32_t a, uint32_t b)
+{
+    uint32_t sum;
+    sum = do_usad(a, b);
+    sum += do_usad(a >> 8, b >> 8);
+    sum += do_usad(a >> 16, b >>16);
+    sum += do_usad(a >> 24, b >> 24);
+    return sum;
+}
+
+/* For ARMv6 SEL instruction.  */
+uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b)
+{
+    uint32_t mask;
+
+    mask = 0;
+    if (flags & 1)
+        mask |= 0xff;
+    if (flags & 2)
+        mask |= 0xff00;
+    if (flags & 4)
+        mask |= 0xff0000;
+    if (flags & 8)
+        mask |= 0xff000000;
+    return (a & mask) | (b & ~mask);
+}
+
+uint32_t HELPER(logicq_cc)(uint64_t val)
+{
+    return (val >> 32) | (val != 0);
+}
+
+/* VFP support.  We follow the convention used for VFP instrunctions:
+   Single precition routines have a "s" suffix, double precision a
+   "d" suffix.  */
+
+/* Convert host exception flags to vfp form.  */
+static inline int vfp_exceptbits_from_host(int host_bits)
+{
+    int target_bits = 0;
+
+    if (host_bits & float_flag_invalid)
+        target_bits |= 1;
+    if (host_bits & float_flag_divbyzero)
+        target_bits |= 2;
+    if (host_bits & float_flag_overflow)
+        target_bits |= 4;
+    if (host_bits & float_flag_underflow)
+        target_bits |= 8;
+    if (host_bits & float_flag_inexact)
+        target_bits |= 0x10;
+    return target_bits;
+}
+
+uint32_t HELPER(vfp_get_fpscr)(CPUState *env)
+{
+    int i;
+    uint32_t fpscr;
+
+    fpscr = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff)
+            | (env->vfp.vec_len << 16)
+            | (env->vfp.vec_stride << 20);
+    i = get_float_exception_flags(&env->vfp.fp_status);
+    fpscr |= vfp_exceptbits_from_host(i);
+    return fpscr;
+}
+
+/* Convert vfp exception flags to target form.  */
+static inline int vfp_exceptbits_to_host(int target_bits)
+{
+    int host_bits = 0;
+
+    if (target_bits & 1)
+        host_bits |= float_flag_invalid;
+    if (target_bits & 2)
+        host_bits |= float_flag_divbyzero;
+    if (target_bits & 4)
+        host_bits |= float_flag_overflow;
+    if (target_bits & 8)
+        host_bits |= float_flag_underflow;
+    if (target_bits & 0x10)
+        host_bits |= float_flag_inexact;
+    return host_bits;
+}
+
+void HELPER(vfp_set_fpscr)(CPUState *env, uint32_t val)
+{
+    int i;
+    uint32_t changed;
+
+    changed = env->vfp.xregs[ARM_VFP_FPSCR];
+    env->vfp.xregs[ARM_VFP_FPSCR] = (val & 0xffc8ffff);
+    env->vfp.vec_len = (val >> 16) & 7;
+    env->vfp.vec_stride = (val >> 20) & 3;
+
+    changed ^= val;
+    if (changed & (3 << 22)) {
+        i = (val >> 22) & 3;
+        switch (i) {
+        case 0:
+            i = float_round_nearest_even;
+            break;
+        case 1:
+            i = float_round_up;
+            break;
+        case 2:
+            i = float_round_down;
+            break;
+        case 3:
+            i = float_round_to_zero;
+            break;
+        }
+        set_float_rounding_mode(i, &env->vfp.fp_status);
+    }
+
+    i = vfp_exceptbits_to_host((val >> 8) & 0x1f);
+    set_float_exception_flags(i, &env->vfp.fp_status);
+    /* XXX: FZ and DN are not implemented.  */
+}
+
+#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p))
+
+#define VFP_BINOP(name) \
+float32 VFP_HELPER(name, s)(float32 a, float32 b, CPUState *env) \
+{ \
+    return float32_ ## name (a, b, &env->vfp.fp_status); \
+} \
+float64 VFP_HELPER(name, d)(float64 a, float64 b, CPUState *env) \
+{ \
+    return float64_ ## name (a, b, &env->vfp.fp_status); \
+}
+VFP_BINOP(add)
+VFP_BINOP(sub)
+VFP_BINOP(mul)
+VFP_BINOP(div)
+#undef VFP_BINOP
+
+float32 VFP_HELPER(neg, s)(float32 a)
+{
+    return float32_chs(a);
+}
+
+float64 VFP_HELPER(neg, d)(float64 a)
+{
+    return float64_chs(a);
+}
+
+float32 VFP_HELPER(abs, s)(float32 a)
+{
+    return float32_abs(a);
+}
+
+float64 VFP_HELPER(abs, d)(float64 a)
+{
+    return float64_abs(a);
+}
+
+float32 VFP_HELPER(sqrt, s)(float32 a, CPUState *env)
+{
+    return float32_sqrt(a, &env->vfp.fp_status);
+}
+
+float64 VFP_HELPER(sqrt, d)(float64 a, CPUState *env)
+{
+    return float64_sqrt(a, &env->vfp.fp_status);
+}
+
+/* XXX: check quiet/signaling case */
+#define DO_VFP_cmp(p, type) \
+void VFP_HELPER(cmp, p)(type a, type b, CPUState *env)  \
+{ \
+    uint32_t flags; \
+    switch(type ## _compare_quiet(a, b, &env->vfp.fp_status)) { \
+    case 0: flags = 0x6; break; \
+    case -1: flags = 0x8; break; \
+    case 1: flags = 0x2; break; \
+    default: case 2: flags = 0x3; break; \
+    } \
+    env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28) \
+        | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
+} \
+void VFP_HELPER(cmpe, p)(type a, type b, CPUState *env) \
+{ \
+    uint32_t flags; \
+    switch(type ## _compare(a, b, &env->vfp.fp_status)) { \
+    case 0: flags = 0x6; break; \
+    case -1: flags = 0x8; break; \
+    case 1: flags = 0x2; break; \
+    default: case 2: flags = 0x3; break; \
+    } \
+    env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28) \
+        | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \
+}
+DO_VFP_cmp(s, float32)
+DO_VFP_cmp(d, float64)
+#undef DO_VFP_cmp
+
+/* Helper routines to perform bitwise copies between float and int.  */
+static inline float32 vfp_itos(uint32_t i)
+{
+    union {
+        uint32_t i;
+        float32 s;
+    } v;
+
+    v.i = i;
+    return v.s;
+}
+
+static inline uint32_t vfp_stoi(float32 s)
+{
+    union {
+        uint32_t i;
+        float32 s;
+    } v;
+
+    v.s = s;
+    return v.i;
+}
+
+static inline float64 vfp_itod(uint64_t i)
+{
+    union {
+        uint64_t i;
+        float64 d;
+    } v;
+
+    v.i = i;
+    return v.d;
+}
+
+static inline uint64_t vfp_dtoi(float64 d)
+{
+    union {
+        uint64_t i;
+        float64 d;
+    } v;
+
+    v.d = d;
+    return v.i;
+}
+
+/* Integer to float conversion.  */
+float32 VFP_HELPER(uito, s)(float32 x, CPUState *env)
+{
+    return uint32_to_float32(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+float64 VFP_HELPER(uito, d)(float32 x, CPUState *env)
+{
+    return uint32_to_float64(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+float32 VFP_HELPER(sito, s)(float32 x, CPUState *env)
+{
+    return int32_to_float32(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+float64 VFP_HELPER(sito, d)(float32 x, CPUState *env)
+{
+    return int32_to_float64(vfp_stoi(x), &env->vfp.fp_status);
+}
+
+/* Float to integer conversion.  */
+float32 VFP_HELPER(toui, s)(float32 x, CPUState *env)
+{
+    return vfp_itos(float32_to_uint32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(toui, d)(float64 x, CPUState *env)
+{
+    return vfp_itos(float64_to_uint32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosi, s)(float32 x, CPUState *env)
+{
+    return vfp_itos(float32_to_int32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosi, d)(float64 x, CPUState *env)
+{
+    return vfp_itos(float64_to_int32(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(touiz, s)(float32 x, CPUState *env)
+{
+    return vfp_itos(float32_to_uint32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(touiz, d)(float64 x, CPUState *env)
+{
+    return vfp_itos(float64_to_uint32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosiz, s)(float32 x, CPUState *env)
+{
+    return vfp_itos(float32_to_int32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+float32 VFP_HELPER(tosiz, d)(float64 x, CPUState *env)
+{
+    return vfp_itos(float64_to_int32_round_to_zero(x, &env->vfp.fp_status));
+}
+
+/* floating point conversion */
+float64 VFP_HELPER(fcvtd, s)(float32 x, CPUState *env)
+{
+    return float32_to_float64(x, &env->vfp.fp_status);
+}
+
+float32 VFP_HELPER(fcvts, d)(float64 x, CPUState *env)
+{
+    return float64_to_float32(x, &env->vfp.fp_status);
+}
+
+/* VFP3 fixed point conversion.  */
+#define VFP_CONV_FIX(name, p, ftype, itype, sign) \
+ftype VFP_HELPER(name##to, p)(ftype x, uint32_t shift, CPUState *env) \
+{ \
+    ftype tmp; \
+    tmp = sign##int32_to_##ftype ((itype)vfp_##p##toi(x), \
+                                  &env->vfp.fp_status); \
+    return ftype##_scalbn(tmp, shift, &env->vfp.fp_status); \
+} \
+ftype VFP_HELPER(to##name, p)(ftype x, uint32_t shift, CPUState *env) \
+{ \
+    ftype tmp; \
+    tmp = ftype##_scalbn(x, shift, &env->vfp.fp_status); \
+    return vfp_ito##p((itype)ftype##_to_##sign##int32_round_to_zero(tmp, \
+        &env->vfp.fp_status)); \
+}
+
+VFP_CONV_FIX(sh, d, float64, int16, )
+VFP_CONV_FIX(sl, d, float64, int32, )
+VFP_CONV_FIX(uh, d, float64, uint16, u)
+VFP_CONV_FIX(ul, d, float64, uint32, u)
+VFP_CONV_FIX(sh, s, float32, int16, )
+VFP_CONV_FIX(sl, s, float32, int32, )
+VFP_CONV_FIX(uh, s, float32, uint16, u)
+VFP_CONV_FIX(ul, s, float32, uint32, u)
+#undef VFP_CONV_FIX
+
+float32 HELPER(recps_f32)(float32 a, float32 b, CPUState *env)
+{
+    float_status *s = &env->vfp.fp_status;
+    float32 two = int32_to_float32(2, s);
+    return float32_sub(two, float32_mul(a, b, s), s);
+}
+
+float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUState *env)
+{
+    float_status *s = &env->vfp.fp_status;
+    float32 three = int32_to_float32(3, s);
+    return float32_sub(three, float32_mul(a, b, s), s);
+}
+
+/* NEON helpers.  */
+
+/* TODO: The architecture specifies the value that the estimate functions
+   should return.  We return the exact reciprocal/root instead.  */
+float32 HELPER(recpe_f32)(float32 a, CPUState *env)
+{
+    float_status *s = &env->vfp.fp_status;
+    float32 one = int32_to_float32(1, s);
+    return float32_div(one, a, s);
+}
+
+float32 HELPER(rsqrte_f32)(float32 a, CPUState *env)
+{
+    float_status *s = &env->vfp.fp_status;
+    float32 one = int32_to_float32(1, s);
+    return float32_div(one, float32_sqrt(a, s), s);
+}
+
+uint32_t HELPER(recpe_u32)(uint32_t a, CPUState *env)
+{
+    float_status *s = &env->vfp.fp_status;
+    float32 tmp;
+    tmp = int32_to_float32(a, s);
+    tmp = float32_scalbn(tmp, -32, s);
+    tmp = helper_recpe_f32(tmp, env);
+    tmp = float32_scalbn(tmp, 31, s);
+    return float32_to_int32(tmp, s);
+}
+
+uint32_t HELPER(rsqrte_u32)(uint32_t a, CPUState *env)
+{
+    float_status *s = &env->vfp.fp_status;
+    float32 tmp;
+    tmp = int32_to_float32(a, s);
+    tmp = float32_scalbn(tmp, -32, s);
+    tmp = helper_rsqrte_f32(tmp, env);
+    tmp = float32_scalbn(tmp, 31, s);
+    return float32_to_int32(tmp, s);
+}
+
+#ifdef CONFIG_TRACE
+#include "trace.h"
+void  HELPER(traceTicks)(uint32_t  ticks)
+{
+    sim_time += ticks;
+}
+
+void  HELPER(traceInsn)(void)
+{
+    trace_insn_helper();
+}
+
+#if HOST_LONG_BITS == 32
+void HELPER(traceBB32)(uint32_t  hi, uint32_t  lo, uint32_t  tb)
+{
+    uint64_t  bb_num = ((uint64_t)hi << 32) | lo;
+    trace_bb_helper(bb_num, (void*)tb);
+}
+#endif
+
+#if HOST_LONG_BITS == 64
+void HELPER(traceBB64)(uint64_t  bb_num, uint64_t  tb)
+{
+    trace_bb_helper(bb_num, (void*)tb);
+}
+#endif
+
+#endif /* CONFIG_TRACE */
diff --git a/target-arm/helpers.h b/target-arm/helpers.h
new file mode 100644
index 0000000..cef53be
--- /dev/null
+++ b/target-arm/helpers.h
@@ -0,0 +1,548 @@
+#define DEF_HELPER(name, ret, args) ret glue(helper_,name) args;
+
+#ifdef GEN_HELPER
+#define DEF_HELPER_0_0(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(void) \
+{ \
+    tcg_gen_helper_0_0(helper_##name); \
+}
+#define DEF_HELPER_0_1(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv arg1) \
+{ \
+    tcg_gen_helper_0_1(helper_##name, arg1); \
+}
+#define DEF_HELPER_0_2(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv arg1, TCGv arg2) \
+{ \
+    tcg_gen_helper_0_2(helper_##name, arg1, arg2); \
+}
+#define DEF_HELPER_0_3(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name( \
+    TCGv arg1, TCGv arg2, TCGv arg3) \
+{ \
+    tcg_gen_helper_0_3(helper_##name, arg1, arg2, arg3); \
+}
+#define DEF_HELPER_1_0(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv ret) \
+{ \
+    tcg_gen_helper_1_0(helper_##name, ret); \
+}
+#define DEF_HELPER_1_1(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv ret, TCGv arg1) \
+{ \
+    tcg_gen_helper_1_1(helper_##name, ret, arg1); \
+}
+#define DEF_HELPER_1_2(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv ret, TCGv arg1, TCGv arg2) \
+{ \
+    tcg_gen_helper_1_2(helper_##name, ret, arg1, arg2); \
+}
+#define DEF_HELPER_1_3(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv ret, \
+    TCGv arg1, TCGv arg2, TCGv arg3) \
+{ \
+    tcg_gen_helper_1_3(helper_##name, ret, arg1, arg2, arg3); \
+}
+#define DEF_HELPER_1_4(name, ret, args) \
+DEF_HELPER(name, ret, args) \
+static inline void gen_helper_##name(TCGv ret, \
+    TCGv arg1, TCGv arg2, TCGv arg3, TCGv arg4) \
+{ \
+    tcg_gen_helper_1_4(helper_##name, ret, arg1, arg2, arg3, arg4); \
+}
+#else /* !GEN_HELPER */
+#define DEF_HELPER_0_0 DEF_HELPER
+#define DEF_HELPER_0_1 DEF_HELPER
+#define DEF_HELPER_0_2 DEF_HELPER
+#define DEF_HELPER_0_3 DEF_HELPER
+#define DEF_HELPER_1_0 DEF_HELPER
+#define DEF_HELPER_1_1 DEF_HELPER
+#define DEF_HELPER_1_2 DEF_HELPER
+#define DEF_HELPER_1_3 DEF_HELPER
+#define DEF_HELPER_1_4 DEF_HELPER
+#define HELPER(x) glue(helper_,x)
+#endif
+
+DEF_HELPER_1_1(clz, uint32_t, (uint32_t))
+DEF_HELPER_1_1(sxtb16, uint32_t, (uint32_t))
+DEF_HELPER_1_1(uxtb16, uint32_t, (uint32_t))
+
+DEF_HELPER_1_2(add_setq, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(add_saturate, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(sub_saturate, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(add_usaturate, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(sub_usaturate, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_1(double_saturate, uint32_t, (int32_t))
+DEF_HELPER_1_2(sdiv, int32_t, (int32_t, int32_t))
+DEF_HELPER_1_2(udiv, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_1(rbit, uint32_t, (uint32_t))
+DEF_HELPER_1_1(abs, uint32_t, (uint32_t))
+
+#ifdef CONFIG_TRACE
+DEF_HELPER_0_1(traceTicks,void,(uint32_t))
+DEF_HELPER_0_0(traceInsn,void,(void))
+DEF_HELPER_0_3(traceBB32,void,(uint32_t,uint32_t,uint32_t))
+DEF_HELPER_0_2(traceBB64,void,(uint64_t,uint64_t))
+#endif
+
+#define PAS_OP(pfx)  \
+    DEF_HELPER_1_3(pfx ## add8, uint32_t, (uint32_t, uint32_t, uint32_t *)) \
+    DEF_HELPER_1_3(pfx ## sub8, uint32_t, (uint32_t, uint32_t, uint32_t *)) \
+    DEF_HELPER_1_3(pfx ## sub16, uint32_t, (uint32_t, uint32_t, uint32_t *)) \
+    DEF_HELPER_1_3(pfx ## add16, uint32_t, (uint32_t, uint32_t, uint32_t *)) \
+    DEF_HELPER_1_3(pfx ## addsubx, uint32_t, (uint32_t, uint32_t, uint32_t *)) \
+    DEF_HELPER_1_3(pfx ## subaddx, uint32_t, (uint32_t, uint32_t, uint32_t *))
+
+PAS_OP(s)
+PAS_OP(u)
+#undef PAS_OP
+
+#define PAS_OP(pfx)  \
+    DEF_HELPER_1_2(pfx ## add8, uint32_t, (uint32_t, uint32_t)) \
+    DEF_HELPER_1_2(pfx ## sub8, uint32_t, (uint32_t, uint32_t)) \
+    DEF_HELPER_1_2(pfx ## sub16, uint32_t, (uint32_t, uint32_t)) \
+    DEF_HELPER_1_2(pfx ## add16, uint32_t, (uint32_t, uint32_t)) \
+    DEF_HELPER_1_2(pfx ## addsubx, uint32_t, (uint32_t, uint32_t)) \
+    DEF_HELPER_1_2(pfx ## subaddx, uint32_t, (uint32_t, uint32_t))
+PAS_OP(q)
+PAS_OP(sh)
+PAS_OP(uq)
+PAS_OP(uh)
+#undef PAS_OP
+
+DEF_HELPER_1_2(ssat, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(usat, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(ssat16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(usat16, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(usad8, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_1(logicq_cc, uint32_t, (uint64_t))
+
+DEF_HELPER_1_3(sel_flags, uint32_t, (uint32_t, uint32_t, uint32_t))
+DEF_HELPER_0_1(exception, void, (uint32_t))
+DEF_HELPER_0_0(wfi, void, (void))
+
+DEF_HELPER_0_2(cpsr_write, void, (uint32_t, uint32_t))
+DEF_HELPER_1_0(cpsr_read, uint32_t, (void))
+
+DEF_HELPER_0_3(v7m_msr, void, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_2(v7m_mrs, uint32_t, (CPUState *, uint32_t))
+
+DEF_HELPER_0_3(set_cp15, void, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_2(get_cp15, uint32_t, (CPUState *, uint32_t))
+
+DEF_HELPER_0_3(set_cp, void, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_2(get_cp, uint32_t, (CPUState *, uint32_t))
+
+DEF_HELPER_1_2(get_r13_banked, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_0_3(set_r13_banked, void, (CPUState *, uint32_t, uint32_t))
+
+DEF_HELPER_0_2(mark_exclusive, void, (CPUState *, uint32_t))
+DEF_HELPER_1_2(test_exclusive, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_0_1(clrex, void, (CPUState *))
+
+DEF_HELPER_1_1(get_user_reg, uint32_t, (uint32_t))
+DEF_HELPER_0_2(set_user_reg, void, (uint32_t, uint32_t))
+
+DEF_HELPER_1_1(vfp_get_fpscr, uint32_t, (CPUState *))
+DEF_HELPER_0_2(vfp_set_fpscr, void, (CPUState *, uint32_t))
+
+DEF_HELPER_1_3(vfp_adds, float32, (float32, float32, CPUState *))
+DEF_HELPER_1_3(vfp_addd, float64, (float64, float64, CPUState *))
+DEF_HELPER_1_3(vfp_subs, float32, (float32, float32, CPUState *))
+DEF_HELPER_1_3(vfp_subd, float64, (float64, float64, CPUState *))
+DEF_HELPER_1_3(vfp_muls, float32, (float32, float32, CPUState *))
+DEF_HELPER_1_3(vfp_muld, float64, (float64, float64, CPUState *))
+DEF_HELPER_1_3(vfp_divs, float32, (float32, float32, CPUState *))
+DEF_HELPER_1_3(vfp_divd, float64, (float64, float64, CPUState *))
+DEF_HELPER_1_1(vfp_negs, float32, (float32))
+DEF_HELPER_1_1(vfp_negd, float64, (float64))
+DEF_HELPER_1_1(vfp_abss, float32, (float32))
+DEF_HELPER_1_1(vfp_absd, float64, (float64))
+DEF_HELPER_1_2(vfp_sqrts, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_sqrtd, float64, (float64, CPUState *))
+DEF_HELPER_0_3(vfp_cmps, void, (float32, float32, CPUState *))
+DEF_HELPER_0_3(vfp_cmpd, void, (float64, float64, CPUState *))
+DEF_HELPER_0_3(vfp_cmpes, void, (float32, float32, CPUState *))
+DEF_HELPER_0_3(vfp_cmped, void, (float64, float64, CPUState *))
+
+DEF_HELPER_1_2(vfp_fcvtds, float64, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_fcvtsd, float32, (float64, CPUState *))
+
+DEF_HELPER_1_2(vfp_uitos, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_uitod, float64, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_sitos, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_sitod, float64, (float32, CPUState *))
+
+DEF_HELPER_1_2(vfp_touis, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_touid, float32, (float64, CPUState *))
+DEF_HELPER_1_2(vfp_touizs, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_touizd, float32, (float64, CPUState *))
+DEF_HELPER_1_2(vfp_tosis, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_tosid, float32, (float64, CPUState *))
+DEF_HELPER_1_2(vfp_tosizs, float32, (float32, CPUState *))
+DEF_HELPER_1_2(vfp_tosizd, float32, (float64, CPUState *))
+
+DEF_HELPER_1_3(vfp_toshs, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_tosls, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_touhs, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_touls, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_toshd, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_tosld, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_touhd, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_tould, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_shtos, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_sltos, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_uhtos, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_ultos, float32, (float32, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_shtod, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_sltod, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_uhtod, float64, (float64, uint32_t, CPUState *))
+DEF_HELPER_1_3(vfp_ultod, float64, (float64, uint32_t, CPUState *))
+
+DEF_HELPER_1_3(recps_f32, float32, (float32, float32, CPUState *))
+DEF_HELPER_1_3(rsqrts_f32, float32, (float32, float32, CPUState *))
+DEF_HELPER_1_2(recpe_f32, float32, (float32, CPUState *))
+DEF_HELPER_1_2(rsqrte_f32, float32, (float32, CPUState *))
+DEF_HELPER_1_2(recpe_u32, uint32_t, (uint32_t, CPUState *))
+DEF_HELPER_1_2(rsqrte_u32, uint32_t, (uint32_t, CPUState *))
+DEF_HELPER_1_4(neon_tbl, uint32_t, (uint32_t, uint32_t, uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_add_saturate_u64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_add_saturate_s64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_sub_saturate_u64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_sub_saturate_s64, uint64_t, (uint64_t, uint64_t))
+
+DEF_HELPER_1_2(add_cc, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(adc_cc, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(sub_cc, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(sbc_cc, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(shl, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(shr, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(sar, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(ror, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(shl_cc, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(shr_cc, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(sar_cc, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(ror_cc, uint32_t, (uint32_t, uint32_t))
+
+/* neon_helper.c */
+DEF_HELPER_1_3(neon_qadd_u8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qadd_s8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qadd_u16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qadd_s16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qsub_u8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qsub_s8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qsub_u16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qsub_s16, uint32_t, (CPUState *, uint32_t, uint32_t))
+
+DEF_HELPER_1_2(neon_hadd_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hadd_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hadd_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hadd_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hadd_s32, int32_t, (int32_t, int32_t))
+DEF_HELPER_1_2(neon_hadd_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rhadd_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rhadd_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rhadd_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rhadd_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rhadd_s32, int32_t, (int32_t, int32_t))
+DEF_HELPER_1_2(neon_rhadd_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hsub_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hsub_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hsub_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hsub_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_hsub_s32, int32_t, (int32_t, int32_t))
+DEF_HELPER_1_2(neon_hsub_u32, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(neon_cgt_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cgt_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cgt_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cgt_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cgt_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cgt_s32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_s32, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(neon_min_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_min_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_min_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_min_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_min_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_min_s32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_s32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmin_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmin_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmin_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmin_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmin_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmin_s32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmax_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmax_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmax_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmax_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmax_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_pmax_s32, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(neon_abd_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abd_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abd_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abd_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abd_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abd_s32, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(neon_shl_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_shl_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_shl_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_shl_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_shl_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_shl_s32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_shl_u64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_shl_s64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_rshl_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rshl_s8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rshl_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rshl_s16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rshl_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rshl_s32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_rshl_u64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_rshl_s64, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_3(neon_qshl_u8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qshl_s8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qshl_u16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qshl_s16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qshl_u32, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qshl_s32, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qshl_u64, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(neon_qshl_s64, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(neon_qrshl_u8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrshl_s8, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrshl_u16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrshl_s16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrshl_u32, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrshl_s32, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrshl_u64, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(neon_qrshl_s64, uint64_t, (CPUState *, uint64_t, uint64_t))
+
+DEF_HELPER_1_2(neon_add_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_add_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_padd_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_padd_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_sub_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_sub_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mul_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mul_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mul_p8, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_2(neon_tst_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_tst_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_tst_u32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_ceq_u8, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_ceq_u16, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_ceq_u32, uint32_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_1(neon_abs_s8, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_abs_s16, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_clz_u8, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_clz_u16, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_cls_s8, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_cls_s16, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_cls_s32, uint32_t, (uint32_t))
+DEF_HELPER_1_1(neon_cnt_u8, uint32_t, (uint32_t))
+
+DEF_HELPER_1_3(neon_qdmulh_s16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrdmulh_s16, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qdmulh_s32, uint32_t, (CPUState *, uint32_t, uint32_t))
+DEF_HELPER_1_3(neon_qrdmulh_s32, uint32_t, (CPUState *, uint32_t, uint32_t))
+
+DEF_HELPER_1_1(neon_narrow_u8, uint32_t, (uint64_t))
+DEF_HELPER_1_1(neon_narrow_u16, uint32_t, (uint64_t))
+DEF_HELPER_1_2(neon_narrow_sat_u8, uint32_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(neon_narrow_sat_s8, uint32_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(neon_narrow_sat_u16, uint32_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(neon_narrow_sat_s16, uint32_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(neon_narrow_sat_u32, uint32_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(neon_narrow_sat_s32, uint32_t, (CPUState *, uint64_t))
+DEF_HELPER_1_1(neon_narrow_high_u8, uint32_t, (uint64_t))
+DEF_HELPER_1_1(neon_narrow_high_u16, uint32_t, (uint64_t))
+DEF_HELPER_1_1(neon_narrow_round_high_u8, uint32_t, (uint64_t))
+DEF_HELPER_1_1(neon_narrow_round_high_u16, uint32_t, (uint64_t))
+DEF_HELPER_1_1(neon_widen_u8, uint64_t, (uint32_t))
+DEF_HELPER_1_1(neon_widen_s8, uint64_t, (uint32_t))
+DEF_HELPER_1_1(neon_widen_u16, uint64_t, (uint32_t))
+DEF_HELPER_1_1(neon_widen_s16, uint64_t, (uint32_t))
+
+DEF_HELPER_1_2(neon_addl_u16, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_addl_u32, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_paddl_u16, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_paddl_u32, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_subl_u16, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_subl_u32, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_3(neon_addl_saturate_s32, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(neon_addl_saturate_s64, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_2(neon_abdl_u16, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abdl_s16, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abdl_u32, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abdl_s32, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abdl_u64, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abdl_s64, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mull_u8, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mull_s8, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mull_u16, uint64_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mull_s16, uint64_t, (uint32_t, uint32_t))
+
+DEF_HELPER_1_1(neon_negl_u16, uint64_t, (uint64_t))
+DEF_HELPER_1_1(neon_negl_u32, uint64_t, (uint64_t))
+DEF_HELPER_1_1(neon_negl_u64, uint64_t, (uint64_t))
+
+DEF_HELPER_1_2(neon_qabs_s8, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_1_2(neon_qabs_s16, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_1_2(neon_qabs_s32, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_1_2(neon_qneg_s8, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_1_2(neon_qneg_s16, uint32_t, (CPUState *, uint32_t))
+DEF_HELPER_1_2(neon_qneg_s32, uint32_t, (CPUState *, uint32_t))
+
+DEF_HELPER_0_0(neon_trn_u8, void, (void))
+DEF_HELPER_0_0(neon_trn_u16, void, (void))
+DEF_HELPER_0_0(neon_unzip_u8, void, (void))
+DEF_HELPER_0_0(neon_zip_u8, void, (void))
+DEF_HELPER_0_0(neon_zip_u16, void, (void))
+
+DEF_HELPER_1_2(neon_min_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_max_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_abd_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_add_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_sub_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_mul_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_ceq_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cge_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_cgt_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_acge_f32, uint32_t, (uint32_t, uint32_t))
+DEF_HELPER_1_2(neon_acgt_f32, uint32_t, (uint32_t, uint32_t))
+
+/* iwmmxt_helper.c */
+DEF_HELPER_1_2(iwmmxt_maddsq, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_madduq, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_sadb, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_sadw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_mulslw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_mulshw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_mululw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_muluhw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_macsw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_2(iwmmxt_macuw, uint64_t, (uint64_t, uint64_t))
+DEF_HELPER_1_1(iwmmxt_setpsr_nz, uint32_t, (uint64_t))
+
+#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \
+DEF_HELPER_1_3(iwmmxt_##name##b, uint64_t, (CPUState *, uint64_t, uint64_t)) \
+DEF_HELPER_1_3(iwmmxt_##name##w, uint64_t, (CPUState *, uint64_t, uint64_t)) \
+DEF_HELPER_1_3(iwmmxt_##name##l, uint64_t, (CPUState *, uint64_t, uint64_t)) \
+
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackl)
+DEF_IWMMXT_HELPER_SIZE_ENV(unpackh)
+
+DEF_HELPER_1_2(iwmmxt_unpacklub, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackluw, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpacklul, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackhub, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackhuw, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackhul, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpacklsb, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpacklsw, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpacklsl, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackhsb, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackhsw, uint64_t, (CPUState *, uint64_t))
+DEF_HELPER_1_2(iwmmxt_unpackhsl, uint64_t, (CPUState *, uint64_t))
+
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu)
+DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(mins)
+DEF_IWMMXT_HELPER_SIZE_ENV(minu)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxs)
+DEF_IWMMXT_HELPER_SIZE_ENV(maxu)
+
+DEF_IWMMXT_HELPER_SIZE_ENV(subn)
+DEF_IWMMXT_HELPER_SIZE_ENV(addn)
+DEF_IWMMXT_HELPER_SIZE_ENV(subu)
+DEF_IWMMXT_HELPER_SIZE_ENV(addu)
+DEF_IWMMXT_HELPER_SIZE_ENV(subs)
+DEF_IWMMXT_HELPER_SIZE_ENV(adds)
+
+DEF_HELPER_1_3(iwmmxt_avgb0, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_avgb1, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_avgw0, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_avgw1, uint64_t, (CPUState *, uint64_t, uint64_t))
+
+DEF_HELPER_1_2(iwmmxt_msadb, uint64_t, (uint64_t, uint64_t))
+
+DEF_HELPER_1_3(iwmmxt_align, uint64_t, (uint64_t, uint64_t, uint32_t))
+DEF_HELPER_1_4(iwmmxt_insr, uint64_t, (uint64_t, uint32_t, uint32_t, uint32_t))
+
+DEF_HELPER_1_1(iwmmxt_bcstb, uint64_t, (uint32_t))
+DEF_HELPER_1_1(iwmmxt_bcstw, uint64_t, (uint32_t))
+DEF_HELPER_1_1(iwmmxt_bcstl, uint64_t, (uint32_t))
+
+DEF_HELPER_1_1(iwmmxt_addcb, uint64_t, (uint64_t))
+DEF_HELPER_1_1(iwmmxt_addcw, uint64_t, (uint64_t))
+DEF_HELPER_1_1(iwmmxt_addcl, uint64_t, (uint64_t))
+
+DEF_HELPER_1_1(iwmmxt_msbb, uint32_t, (uint64_t))
+DEF_HELPER_1_1(iwmmxt_msbw, uint32_t, (uint64_t))
+DEF_HELPER_1_1(iwmmxt_msbl, uint32_t, (uint64_t))
+
+DEF_HELPER_1_3(iwmmxt_srlw, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_srll, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_srlq, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_sllw, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_slll, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_sllq, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_sraw, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_sral, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_sraq, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_rorw, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_rorl, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_rorq, uint64_t, (CPUState *, uint64_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_shufh, uint64_t, (CPUState *, uint64_t, uint32_t))
+
+DEF_HELPER_1_3(iwmmxt_packuw, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_packul, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_packuq, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_packsw, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_packsl, uint64_t, (CPUState *, uint64_t, uint64_t))
+DEF_HELPER_1_3(iwmmxt_packsq, uint64_t, (CPUState *, uint64_t, uint64_t))
+
+DEF_HELPER_1_3(iwmmxt_muladdsl, uint64_t, (uint64_t, uint32_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_muladdsw, uint64_t, (uint64_t, uint32_t, uint32_t))
+DEF_HELPER_1_3(iwmmxt_muladdswl, uint64_t, (uint64_t, uint32_t, uint32_t))
+
+#undef DEF_HELPER
+#undef DEF_HELPER_0_0
+#undef DEF_HELPER_0_1
+#undef DEF_HELPER_0_2
+#undef DEF_HELPER_0_3
+#undef DEF_HELPER_1_0
+#undef DEF_HELPER_1_1
+#undef DEF_HELPER_1_2
+#undef DEF_HELPER_1_3
+#undef DEF_HELPER_1_4
+#undef GEN_HELPER
diff --git a/target-arm/iwmmxt_helper.c b/target-arm/iwmmxt_helper.c
new file mode 100644
index 0000000..6e801c8
--- /dev/null
+++ b/target-arm/iwmmxt_helper.c
@@ -0,0 +1,682 @@
+/*
+ * iwMMXt micro operations for XScale.
+ *
+ * Copyright (c) 2007 OpenedHand, Ltd.
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
+ * Copyright (c) 2008 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "helpers.h"
+
+/* iwMMXt macros extracted from GNU gdb.  */
+
+/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations.  */
+#define SIMD8_SET( v, n, b)	((v != 0) << ((((b) + 1) * 4) + (n)))
+#define SIMD16_SET(v, n, h)	((v != 0) << ((((h) + 1) * 8) + (n)))
+#define SIMD32_SET(v, n, w)	((v != 0) << ((((w) + 1) * 16) + (n)))
+#define SIMD64_SET(v, n)	((v != 0) << (32 + (n)))
+/* Flags to pass as "n" above.  */
+#define SIMD_NBIT	-1
+#define SIMD_ZBIT	-2
+#define SIMD_CBIT	-3
+#define SIMD_VBIT	-4
+/* Various status bit macros.  */
+#define NBIT8(x)	((x) & 0x80)
+#define NBIT16(x)	((x) & 0x8000)
+#define NBIT32(x)	((x) & 0x80000000)
+#define NBIT64(x)	((x) & 0x8000000000000000ULL)
+#define ZBIT8(x)	(((x) & 0xff) == 0)
+#define ZBIT16(x)	(((x) & 0xffff) == 0)
+#define ZBIT32(x)	(((x) & 0xffffffff) == 0)
+#define ZBIT64(x)	(x == 0)
+/* Sign extension macros.  */
+#define EXTEND8H(a)	((uint16_t) (int8_t) (a))
+#define EXTEND8(a)	((uint32_t) (int8_t) (a))
+#define EXTEND16(a)	((uint32_t) (int16_t) (a))
+#define EXTEND16S(a)	((int32_t) (int16_t) (a))
+#define EXTEND32(a)	((uint64_t) (int32_t) (a))
+
+uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b)
+{
+    a = ((
+            EXTEND16S((a >> 0) & 0xffff) * EXTEND16S((b >> 0) & 0xffff) +
+            EXTEND16S((a >> 16) & 0xffff) * EXTEND16S((b >> 16) & 0xffff)
+        ) & 0xffffffff) | ((uint64_t) (
+            EXTEND16S((a >> 32) & 0xffff) * EXTEND16S((b >> 32) & 0xffff) +
+            EXTEND16S((a >> 48) & 0xffff) * EXTEND16S((b >> 48) & 0xffff)
+        ) << 32);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_madduq)(uint64_t a, uint64_t b)
+{
+    a = ((
+            ((a >> 0) & 0xffff) * ((b >> 0) & 0xffff) +
+            ((a >> 16) & 0xffff) * ((b >> 16) & 0xffff)
+        ) & 0xffffffff) | ((
+            ((a >> 32) & 0xffff) * ((b >> 32) & 0xffff) +
+            ((a >> 48) & 0xffff) * ((b >> 48) & 0xffff)
+        ) << 32);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_sadb)(uint64_t a, uint64_t b)
+{
+#define abs(x) (((x) >= 0) ? x : -x)
+#define SADB(SHR) abs((int) ((a >> SHR) & 0xff) - (int) ((b >> SHR) & 0xff))
+    return
+        SADB(0) + SADB(8) + SADB(16) + SADB(24) +
+        SADB(32) + SADB(40) + SADB(48) + SADB(56);
+#undef SADB
+}
+
+uint64_t HELPER(iwmmxt_sadw)(uint64_t a, uint64_t b)
+{
+#define SADW(SHR) \
+    abs((int) ((a >> SHR) & 0xffff) - (int) ((b >> SHR) & 0xffff))
+    return SADW(0) + SADW(16) + SADW(32) + SADW(48);
+#undef SADW
+}
+
+uint64_t HELPER(iwmmxt_mulslw)(uint64_t a, uint64_t b)
+{
+#define MULS(SHR) ((uint64_t) ((( \
+        EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \
+    ) >> 0) & 0xffff) << SHR)
+    return MULS(0) | MULS(16) | MULS(32) | MULS(48);
+#undef MULS
+}
+
+uint64_t HELPER(iwmmxt_mulshw)(uint64_t a, uint64_t b)
+{
+#define MULS(SHR) ((uint64_t) ((( \
+        EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \
+    ) >> 16) & 0xffff) << SHR)
+    return MULS(0) | MULS(16) | MULS(32) | MULS(48);
+#undef MULS
+}
+
+uint64_t HELPER(iwmmxt_mululw)(uint64_t a, uint64_t b)
+{
+#define MULU(SHR) ((uint64_t) ((( \
+        ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \
+    ) >> 0) & 0xffff) << SHR)
+    return MULU(0) | MULU(16) | MULU(32) | MULU(48);
+#undef MULU
+}
+
+uint64_t HELPER(iwmmxt_muluhw)(uint64_t a, uint64_t b)
+{
+#define MULU(SHR) ((uint64_t) ((( \
+        ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \
+    ) >> 16) & 0xffff) << SHR)
+    return MULU(0) | MULU(16) | MULU(32) | MULU(48);
+#undef MULU
+}
+
+uint64_t HELPER(iwmmxt_macsw)(uint64_t a, uint64_t b)
+{
+#define MACS(SHR) ( \
+        EXTEND16((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff))
+    return (int64_t) (MACS(0) + MACS(16) + MACS(32) + MACS(48));
+#undef MACS
+}
+
+uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b)
+{
+#define MACU(SHR) ( \
+        (uint32_t) ((a >> SHR) & 0xffff) * \
+        (uint32_t) ((b >> SHR) & 0xffff))
+    return MACU(0) + MACU(16) + MACU(32) + MACU(48);
+#undef MACU
+}
+
+#define NZBIT8(x, i) \
+    SIMD8_SET(NBIT8((x) & 0xff), SIMD_NBIT, i) | \
+    SIMD8_SET(ZBIT8((x) & 0xff), SIMD_ZBIT, i)
+#define NZBIT16(x, i) \
+    SIMD16_SET(NBIT16((x) & 0xffff), SIMD_NBIT, i) | \
+    SIMD16_SET(ZBIT16((x) & 0xffff), SIMD_ZBIT, i)
+#define NZBIT32(x, i) \
+    SIMD32_SET(NBIT32((x) & 0xffffffff), SIMD_NBIT, i) | \
+    SIMD32_SET(ZBIT32((x) & 0xffffffff), SIMD_ZBIT, i)
+#define NZBIT64(x) \
+    SIMD64_SET(NBIT64(x), SIMD_NBIT) | \
+    SIMD64_SET(ZBIT64(x), SIMD_ZBIT)
+#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3)			\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUState *env, \
+                                                 uint64_t a, uint64_t b) \
+{								\
+    a =							        \
+        (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) |	\
+        (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) |	\
+        (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) |	\
+        (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56);	\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) |		        \
+        NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) |		\
+        NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) |		\
+        NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7);		\
+    return a;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUState *env, \
+                                        uint64_t a, uint64_t b) \
+{								\
+    a =							        \
+        (((a >> SH0) & 0xffff) << 0) |				\
+        (((b >> SH0) & 0xffff) << 16) |			        \
+        (((a >> SH2) & 0xffff) << 32) |			        \
+        (((b >> SH2) & 0xffff) << 48);				\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) |		\
+        NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3);		\
+    return a;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUState *env, \
+                                        uint64_t a, uint64_t b) \
+{								\
+    a =							        \
+        (((a >> SH0) & 0xffffffff) << 0) |			\
+        (((b >> SH0) & 0xffffffff) << 32);			\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1);		\
+    return a;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUState *env, \
+                                                  uint64_t x)   \
+{								\
+    x =							        \
+        (((x >> SH0) & 0xff) << 0) |				\
+        (((x >> SH1) & 0xff) << 16) |				\
+        (((x >> SH2) & 0xff) << 32) |				\
+        (((x >> SH3) & 0xff) << 48);				\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |		\
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);		\
+    return x;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUState *env, \
+                                                  uint64_t x)   \
+{								\
+    x =							        \
+        (((x >> SH0) & 0xffff) << 0) |				\
+        (((x >> SH2) & 0xffff) << 32);				\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);		\
+    return x;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUState *env, \
+                                                  uint64_t x)   \
+{								\
+    x = (((x >> SH0) & 0xffffffff) << 0);			\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0);	\
+    return x;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUState *env, \
+                                                  uint64_t x)   \
+{								\
+    x =							        \
+        ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) |	        \
+        ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) |	\
+        ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) |	\
+        ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48);	        \
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |		\
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);		\
+    return x;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUState *env, \
+                                                  uint64_t x)   \
+{								\
+    x =							        \
+        ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) |	\
+        ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32);	\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);		\
+    return x;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUState *env, \
+                                                  uint64_t x)   \
+{								\
+    x = EXTEND32((x >> SH0) & 0xffffffff);			\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0);	\
+    return x;                                                   \
+}
+IWMMXT_OP_UNPACK(l, 0, 8, 16, 24)
+IWMMXT_OP_UNPACK(h, 32, 40, 48, 56)
+
+#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O)			\
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUState *env,    \
+                                        uint64_t a, uint64_t b) \
+{								\
+    a =							        \
+        CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) |		\
+        CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) |		\
+        CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) |		\
+        CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff);		\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) |		        \
+        NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) |		\
+        NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) |		\
+        NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7);		\
+    return a;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUState *env,    \
+                                        uint64_t a, uint64_t b) \
+{								\
+    a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) |	\
+        CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff);	\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) |		\
+        NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3);		\
+    return a;                                                   \
+}								\
+uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUState *env,    \
+                                        uint64_t a, uint64_t b) \
+{								\
+    a = CMP(0, Tl, O, 0xffffffff) |				\
+        CMP(32, Tl, O, 0xffffffff);				\
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =			\
+        NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1);		\
+    return a;                                                   \
+}
+#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \
+            (TYPE) ((b >> SHR) & MASK)) ? (uint64_t) MASK : 0) << SHR)
+IWMMXT_OP_CMP(cmpeq, uint8_t, uint16_t, uint32_t, ==)
+IWMMXT_OP_CMP(cmpgts, int8_t, int16_t, int32_t, >)
+IWMMXT_OP_CMP(cmpgtu, uint8_t, uint16_t, uint32_t, >)
+#undef CMP
+#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \
+            (TYPE) ((b >> SHR) & MASK)) ? a : b) & ((uint64_t) MASK << SHR))
+IWMMXT_OP_CMP(mins, int8_t, int16_t, int32_t, <)
+IWMMXT_OP_CMP(minu, uint8_t, uint16_t, uint32_t, <)
+IWMMXT_OP_CMP(maxs, int8_t, int16_t, int32_t, >)
+IWMMXT_OP_CMP(maxu, uint8_t, uint16_t, uint32_t, >)
+#undef CMP
+#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \
+            OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR)
+IWMMXT_OP_CMP(subn, uint8_t, uint16_t, uint32_t, -)
+IWMMXT_OP_CMP(addn, uint8_t, uint16_t, uint32_t, +)
+#undef CMP
+/* TODO Signed- and Unsigned-Saturation */
+#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \
+            OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR)
+IWMMXT_OP_CMP(subu, uint8_t, uint16_t, uint32_t, -)
+IWMMXT_OP_CMP(addu, uint8_t, uint16_t, uint32_t, +)
+IWMMXT_OP_CMP(subs, int8_t, int16_t, int32_t, -)
+IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +)
+#undef CMP
+#undef IWMMXT_OP_CMP
+
+#define AVGB(SHR) ((( \
+        ((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR)
+#define IWMMXT_OP_AVGB(r)                                                 \
+uint64_t HELPER(iwmmxt_avgb##r)(CPUState *env, uint64_t a, uint64_t b)    \
+{                                                                         \
+    const int round = r;                                                  \
+    a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) |                         \
+        AVGB(32) | AVGB(40) | AVGB(48) | AVGB(56);                        \
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =                                 \
+        SIMD8_SET(ZBIT8((a >> 0) & 0xff), SIMD_ZBIT, 0) |                 \
+        SIMD8_SET(ZBIT8((a >> 8) & 0xff), SIMD_ZBIT, 1) |                 \
+        SIMD8_SET(ZBIT8((a >> 16) & 0xff), SIMD_ZBIT, 2) |                \
+        SIMD8_SET(ZBIT8((a >> 24) & 0xff), SIMD_ZBIT, 3) |                \
+        SIMD8_SET(ZBIT8((a >> 32) & 0xff), SIMD_ZBIT, 4) |                \
+        SIMD8_SET(ZBIT8((a >> 40) & 0xff), SIMD_ZBIT, 5) |                \
+        SIMD8_SET(ZBIT8((a >> 48) & 0xff), SIMD_ZBIT, 6) |                \
+        SIMD8_SET(ZBIT8((a >> 56) & 0xff), SIMD_ZBIT, 7);                 \
+    return a;                                                             \
+}
+IWMMXT_OP_AVGB(0)
+IWMMXT_OP_AVGB(1)
+#undef IWMMXT_OP_AVGB
+#undef AVGB
+
+#define AVGW(SHR) ((( \
+        ((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR)
+#define IWMMXT_OP_AVGW(r)                                               \
+uint64_t HELPER(iwmmxt_avgw##r)(CPUState *env, uint64_t a, uint64_t b)  \
+{                                                                       \
+    const int round = r;                                                \
+    a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48);                       \
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =                               \
+        SIMD16_SET(ZBIT16((a >> 0) & 0xffff), SIMD_ZBIT, 0) |           \
+        SIMD16_SET(ZBIT16((a >> 16) & 0xffff), SIMD_ZBIT, 1) |          \
+        SIMD16_SET(ZBIT16((a >> 32) & 0xffff), SIMD_ZBIT, 2) |          \
+        SIMD16_SET(ZBIT16((a >> 48) & 0xffff), SIMD_ZBIT, 3);           \
+    return a;                                                           \
+}
+IWMMXT_OP_AVGW(0)
+IWMMXT_OP_AVGW(1)
+#undef IWMMXT_OP_AVGW
+#undef AVGW
+
+uint64_t HELPER(iwmmxt_msadb)(uint64_t a, uint64_t b)
+{
+    a =  ((((a >> 0 ) & 0xffff) * ((b >> 0) & 0xffff) +
+           ((a >> 16) & 0xffff) * ((b >> 16) & 0xffff)) & 0xffffffff) |
+         ((((a >> 32) & 0xffff) * ((b >> 32) & 0xffff) +
+           ((a >> 48) & 0xffff) * ((b >> 48) & 0xffff)) << 32);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_align)(uint64_t a, uint64_t b, uint32_t n)
+{
+    a >>= n << 3;
+    a |= b << (64 - (n << 3));
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_insr)(uint64_t x, uint32_t a, uint32_t b, uint32_t n)
+{
+    x &= ~((uint64_t) b << n);
+    x |= (uint64_t) (a & b) << n;
+    return x;
+}
+
+uint32_t HELPER(iwmmxt_setpsr_nz)(uint64_t x)
+{
+    return SIMD64_SET((x == 0), SIMD_ZBIT) |
+           SIMD64_SET((x & (1ULL << 63)), SIMD_NBIT);
+}
+
+uint64_t HELPER(iwmmxt_bcstb)(uint32_t arg)
+{
+    arg &= 0xff;
+    return
+        ((uint64_t) arg << 0 ) | ((uint64_t) arg << 8 ) |
+        ((uint64_t) arg << 16) | ((uint64_t) arg << 24) |
+        ((uint64_t) arg << 32) | ((uint64_t) arg << 40) |
+        ((uint64_t) arg << 48) | ((uint64_t) arg << 56);
+}
+
+uint64_t HELPER(iwmmxt_bcstw)(uint32_t arg)
+{
+    arg &= 0xffff;
+    return
+        ((uint64_t) arg << 0 ) | ((uint64_t) arg << 16) |
+        ((uint64_t) arg << 32) | ((uint64_t) arg << 48);
+}
+
+uint64_t HELPER(iwmmxt_bcstl)(uint32_t arg)
+{
+    return arg | ((uint64_t) arg << 32);
+}
+
+uint64_t HELPER(iwmmxt_addcb)(uint64_t x)
+{
+    return
+        ((x >> 0) & 0xff) + ((x >> 8) & 0xff) +
+        ((x >> 16) & 0xff) + ((x >> 24) & 0xff) +
+        ((x >> 32) & 0xff) + ((x >> 40) & 0xff) +
+        ((x >> 48) & 0xff) + ((x >> 56) & 0xff);
+}
+
+uint64_t HELPER(iwmmxt_addcw)(uint64_t x)
+{
+    return
+        ((x >> 0) & 0xffff) + ((x >> 16) & 0xffff) +
+        ((x >> 32) & 0xffff) + ((x >> 48) & 0xffff);
+}
+
+uint64_t HELPER(iwmmxt_addcl)(uint64_t x)
+{
+    return (x & 0xffffffff) + (x >> 32);
+}
+
+uint32_t HELPER(iwmmxt_msbb)(uint64_t x)
+{
+    return
+        ((x >> 7) & 0x01) | ((x >> 14) & 0x02) |
+        ((x >> 21) & 0x04) | ((x >> 28) & 0x08) |
+        ((x >> 35) & 0x10) | ((x >> 42) & 0x20) |
+        ((x >> 49) & 0x40) | ((x >> 56) & 0x80);
+}
+
+uint32_t HELPER(iwmmxt_msbw)(uint64_t x)
+{
+    return
+        ((x >> 15) & 0x01) | ((x >> 30) & 0x02) |
+        ((x >> 45) & 0x04) | ((x >> 52) & 0x08);
+}
+
+uint32_t HELPER(iwmmxt_msbl)(uint64_t x)
+{
+    return ((x >> 31) & 0x01) | ((x >> 62) & 0x02);
+}
+
+/* FIXME: Split wCASF setting into a separate op to avoid env use.  */
+uint64_t HELPER(iwmmxt_srlw)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) |
+        (((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) |
+        (((x & (0xffffll << 32)) >> n) & (0xffffll << 32)) |
+        (((x & (0xffffll << 48)) >> n) & (0xffffll << 48));
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_srll)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = ((x & (0xffffffffll << 0)) >> n) |
+        ((x >> n) & (0xffffffffll << 32));
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_srlq)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x >>= n;
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_sllw)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) |
+        (((x & (0xffffll << 16)) << n) & (0xffffll << 16)) |
+        (((x & (0xffffll << 32)) << n) & (0xffffll << 32)) |
+        (((x & (0xffffll << 48)) << n) & (0xffffll << 48));
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_slll)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = ((x << n) & (0xffffffffll << 0)) |
+        ((x & (0xffffffffll << 32)) << n);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_sllq)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x <<= n;
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_sraw)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) |
+        ((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) |
+        ((uint64_t) ((EXTEND16(x >> 32) >> n) & 0xffff) << 32) |
+        ((uint64_t) ((EXTEND16(x >> 48) >> n) & 0xffff) << 48);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_sral)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) |
+        (((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_sraq)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = (int64_t) x >> n;
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_rorw)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = ((((x & (0xffffll << 0)) >> n) |
+          ((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) |
+        ((((x & (0xffffll << 16)) >> n) |
+          ((x & (0xffffll << 16)) << (16 - n))) & (0xffffll << 16)) |
+        ((((x & (0xffffll << 32)) >> n) |
+          ((x & (0xffffll << 32)) << (16 - n))) & (0xffffll << 32)) |
+        ((((x & (0xffffll << 48)) >> n) |
+          ((x & (0xffffll << 48)) << (16 - n))) & (0xffffll << 48));
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_rorl)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = ((x & (0xffffffffll << 0)) >> n) |
+        ((x >> n) & (0xffffffffll << 32)) |
+        ((x << (32 - n)) & (0xffffffffll << 0)) |
+        ((x & (0xffffffffll << 32)) << (32 - n));
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_rorq)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = (x >> n) | (x << (64 - n));
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x);
+    return x;
+}
+
+uint64_t HELPER(iwmmxt_shufh)(CPUState *env, uint64_t x, uint32_t n)
+{
+    x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) |
+        (((x >> ((n << 2) & 0x30)) & 0xffff) << 16) |
+        (((x >> ((n << 0) & 0x30)) & 0xffff) << 32) |
+        (((x >> ((n >> 2) & 0x30)) & 0xffff) << 48);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) |
+        NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3);
+    return x;
+}
+
+/* TODO: Unsigned-Saturation */
+uint64_t HELPER(iwmmxt_packuw)(CPUState *env, uint64_t a, uint64_t b)
+{
+    a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
+        (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
+        (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) |
+        (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) |
+        NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) |
+        NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) |
+        NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_packul)(CPUState *env, uint64_t a, uint64_t b)
+{
+    a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
+        (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) |
+        NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_packuq)(CPUState *env, uint64_t a, uint64_t b)
+{
+    a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1);
+    return a;
+}
+
+/* TODO: Signed-Saturation */
+uint64_t HELPER(iwmmxt_packsw)(CPUState *env, uint64_t a, uint64_t b)
+{
+    a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) |
+        (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) |
+        (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) |
+        (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) |
+        NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) |
+        NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) |
+        NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_packsl)(CPUState *env, uint64_t a, uint64_t b)
+{
+    a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) |
+        (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) |
+        NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_packsq)(CPUState *env, uint64_t a, uint64_t b)
+{
+    a = (a & 0xffffffff) | ((b & 0xffffffff) << 32);
+    env->iwmmxt.cregs[ARM_IWMMXT_wCASF] =
+        NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1);
+    return a;
+}
+
+uint64_t HELPER(iwmmxt_muladdsl)(uint64_t c, uint32_t a, uint32_t b)
+{
+    return c + ((int32_t) EXTEND32(a) * (int32_t) EXTEND32(b));
+}
+
+uint64_t HELPER(iwmmxt_muladdsw)(uint64_t c, uint32_t a, uint32_t b)
+{
+    c += EXTEND32(EXTEND16S((a >> 0) & 0xffff) *
+                  EXTEND16S((b >> 0) & 0xffff));
+    c += EXTEND32(EXTEND16S((a >> 16) & 0xffff) *
+                  EXTEND16S((b >> 16) & 0xffff));
+    return c;
+}
+
+uint64_t HELPER(iwmmxt_muladdswl)(uint64_t c, uint32_t a, uint32_t b)
+{
+    return c + (EXTEND32(EXTEND16S(a & 0xffff) *
+                         EXTEND16S(b & 0xffff)));
+}
diff --git a/target-arm/machine.c b/target-arm/machine.c
new file mode 100644
index 0000000..3368741
--- /dev/null
+++ b/target-arm/machine.c
@@ -0,0 +1,218 @@
+#include "hw/hw.h"
+#include "hw/boards.h"
+
+void register_machines(void)
+{
+#if 0  /* ANDROID */
+    qemu_register_machine(&integratorcp_machine);
+    qemu_register_machine(&versatilepb_machine);
+    qemu_register_machine(&versatileab_machine);
+    qemu_register_machine(&realview_machine);
+    qemu_register_machine(&akitapda_machine);
+    qemu_register_machine(&spitzpda_machine);
+    qemu_register_machine(&borzoipda_machine);
+    qemu_register_machine(&terrierpda_machine);
+    qemu_register_machine(&palmte_machine);
+    qemu_register_machine(&n800_machine);
+    qemu_register_machine(&n810_machine);
+    qemu_register_machine(&lm3s811evb_machine);
+    qemu_register_machine(&lm3s6965evb_machine);
+    qemu_register_machine(&connex_machine);
+    qemu_register_machine(&verdex_machine);
+    qemu_register_machine(&mainstone2_machine);
+    qemu_register_machine(&musicpal_machine);
+    qemu_register_machine(&tosapda_machine);
+#endif
+    qemu_register_machine(&android_arm_machine);
+}
+
+void cpu_save(QEMUFile *f, void *opaque)
+{
+    int i;
+    CPUARMState *env = (CPUARMState *)opaque;
+
+    for (i = 0; i < 16; i++) {
+        qemu_put_be32(f, env->regs[i]);
+    }
+    qemu_put_be32(f, cpsr_read(env));
+    qemu_put_be32(f, env->spsr);
+    for (i = 0; i < 6; i++) {
+        qemu_put_be32(f, env->banked_spsr[i]);
+        qemu_put_be32(f, env->banked_r13[i]);
+        qemu_put_be32(f, env->banked_r14[i]);
+    }
+    for (i = 0; i < 5; i++) {
+        qemu_put_be32(f, env->usr_regs[i]);
+        qemu_put_be32(f, env->fiq_regs[i]);
+    }
+    qemu_put_be32(f, env->cp15.c0_cpuid);
+    qemu_put_be32(f, env->cp15.c0_cachetype);
+    qemu_put_be32(f, env->cp15.c1_sys);
+    qemu_put_be32(f, env->cp15.c1_coproc);
+    qemu_put_be32(f, env->cp15.c1_xscaleauxcr);
+    qemu_put_be32(f, env->cp15.c2_base0);
+    qemu_put_be32(f, env->cp15.c2_base1);
+    qemu_put_be32(f, env->cp15.c2_mask);
+    qemu_put_be32(f, env->cp15.c2_data);
+    qemu_put_be32(f, env->cp15.c2_insn);
+    qemu_put_be32(f, env->cp15.c3);
+    qemu_put_be32(f, env->cp15.c5_insn);
+    qemu_put_be32(f, env->cp15.c5_data);
+    for (i = 0; i < 8; i++) {
+        qemu_put_be32(f, env->cp15.c6_region[i]);
+    }
+    qemu_put_be32(f, env->cp15.c6_insn);
+    qemu_put_be32(f, env->cp15.c6_data);
+    qemu_put_be32(f, env->cp15.c9_insn);
+    qemu_put_be32(f, env->cp15.c9_data);
+    qemu_put_be32(f, env->cp15.c13_fcse);
+    qemu_put_be32(f, env->cp15.c13_context);
+    qemu_put_be32(f, env->cp15.c13_tls1);
+    qemu_put_be32(f, env->cp15.c13_tls2);
+    qemu_put_be32(f, env->cp15.c13_tls3);
+    qemu_put_be32(f, env->cp15.c15_cpar);
+
+    qemu_put_be32(f, env->features);
+
+    if (arm_feature(env, ARM_FEATURE_VFP)) {
+        for (i = 0;  i < 16; i++) {
+            CPU_DoubleU u;
+            u.d = env->vfp.regs[i];
+            qemu_put_be32(f, u.l.upper);
+            qemu_put_be32(f, u.l.lower);
+        }
+        for (i = 0; i < 16; i++) {
+            qemu_put_be32(f, env->vfp.xregs[i]);
+        }
+
+        /* TODO: Should use proper FPSCR access functions.  */
+        qemu_put_be32(f, env->vfp.vec_len);
+        qemu_put_be32(f, env->vfp.vec_stride);
+
+        if (arm_feature(env, ARM_FEATURE_VFP3)) {
+            for (i = 16;  i < 32; i++) {
+                CPU_DoubleU u;
+                u.d = env->vfp.regs[i];
+                qemu_put_be32(f, u.l.upper);
+                qemu_put_be32(f, u.l.lower);
+            }
+        }
+    }
+
+    if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+        for (i = 0; i < 16; i++) {
+            qemu_put_be64(f, env->iwmmxt.regs[i]);
+        }
+        for (i = 0; i < 16; i++) {
+            qemu_put_be32(f, env->iwmmxt.cregs[i]);
+        }
+    }
+
+    if (arm_feature(env, ARM_FEATURE_M)) {
+        qemu_put_be32(f, env->v7m.other_sp);
+        qemu_put_be32(f, env->v7m.vecbase);
+        qemu_put_be32(f, env->v7m.basepri);
+        qemu_put_be32(f, env->v7m.control);
+        qemu_put_be32(f, env->v7m.current_sp);
+        qemu_put_be32(f, env->v7m.exception);
+    }
+}
+
+int cpu_load(QEMUFile *f, void *opaque, int version_id)
+{
+    CPUARMState *env = (CPUARMState *)opaque;
+    int i;
+
+    if (version_id != CPU_SAVE_VERSION)
+        return -EINVAL;
+
+    for (i = 0; i < 16; i++) {
+        env->regs[i] = qemu_get_be32(f);
+    }
+    cpsr_write(env, qemu_get_be32(f), 0xffffffff);
+    env->spsr = qemu_get_be32(f);
+    for (i = 0; i < 6; i++) {
+        env->banked_spsr[i] = qemu_get_be32(f);
+        env->banked_r13[i] = qemu_get_be32(f);
+        env->banked_r14[i] = qemu_get_be32(f);
+    }
+    for (i = 0; i < 5; i++) {
+        env->usr_regs[i] = qemu_get_be32(f);
+        env->fiq_regs[i] = qemu_get_be32(f);
+    }
+    env->cp15.c0_cpuid = qemu_get_be32(f);
+    env->cp15.c0_cachetype = qemu_get_be32(f);
+    env->cp15.c1_sys = qemu_get_be32(f);
+    env->cp15.c1_coproc = qemu_get_be32(f);
+    env->cp15.c1_xscaleauxcr = qemu_get_be32(f);
+    env->cp15.c2_base0 = qemu_get_be32(f);
+    env->cp15.c2_base1 = qemu_get_be32(f);
+    env->cp15.c2_mask = qemu_get_be32(f);
+    env->cp15.c2_data = qemu_get_be32(f);
+    env->cp15.c2_insn = qemu_get_be32(f);
+    env->cp15.c3 = qemu_get_be32(f);
+    env->cp15.c5_insn = qemu_get_be32(f);
+    env->cp15.c5_data = qemu_get_be32(f);
+    for (i = 0; i < 8; i++) {
+        env->cp15.c6_region[i] = qemu_get_be32(f);
+    }
+    env->cp15.c6_insn = qemu_get_be32(f);
+    env->cp15.c6_data = qemu_get_be32(f);
+    env->cp15.c9_insn = qemu_get_be32(f);
+    env->cp15.c9_data = qemu_get_be32(f);
+    env->cp15.c13_fcse = qemu_get_be32(f);
+    env->cp15.c13_context = qemu_get_be32(f);
+    env->cp15.c13_tls1 = qemu_get_be32(f);
+    env->cp15.c13_tls2 = qemu_get_be32(f);
+    env->cp15.c13_tls3 = qemu_get_be32(f);
+    env->cp15.c15_cpar = qemu_get_be32(f);
+
+    env->features = qemu_get_be32(f);
+
+    if (arm_feature(env, ARM_FEATURE_VFP)) {
+        for (i = 0;  i < 16; i++) {
+            CPU_DoubleU u;
+            u.l.upper = qemu_get_be32(f);
+            u.l.lower = qemu_get_be32(f);
+            env->vfp.regs[i] = u.d;
+        }
+        for (i = 0; i < 16; i++) {
+            env->vfp.xregs[i] = qemu_get_be32(f);
+        }
+
+        /* TODO: Should use proper FPSCR access functions.  */
+        env->vfp.vec_len = qemu_get_be32(f);
+        env->vfp.vec_stride = qemu_get_be32(f);
+
+        if (arm_feature(env, ARM_FEATURE_VFP3)) {
+            for (i = 0;  i < 16; i++) {
+                CPU_DoubleU u;
+                u.l.upper = qemu_get_be32(f);
+                u.l.lower = qemu_get_be32(f);
+                env->vfp.regs[i] = u.d;
+            }
+        }
+    }
+
+    if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+        for (i = 0; i < 16; i++) {
+            env->iwmmxt.regs[i] = qemu_get_be64(f);
+        }
+        for (i = 0; i < 16; i++) {
+            env->iwmmxt.cregs[i] = qemu_get_be32(f);
+        }
+    }
+
+    if (arm_feature(env, ARM_FEATURE_M)) {
+        env->v7m.other_sp = qemu_get_be32(f);
+        env->v7m.vecbase = qemu_get_be32(f);
+        env->v7m.basepri = qemu_get_be32(f);
+        env->v7m.control = qemu_get_be32(f);
+        env->v7m.current_sp = qemu_get_be32(f);
+        env->v7m.exception = qemu_get_be32(f);
+    }
+
+    return 0;
+}
+
+
diff --git a/target-arm/neon_helper.c b/target-arm/neon_helper.c
new file mode 100644
index 0000000..4ee5658
--- /dev/null
+++ b/target-arm/neon_helper.c
@@ -0,0 +1,1457 @@
+/*
+ * ARM NEON vector operations.
+ *
+ * Copyright (c) 2007, 2008 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GNU GPL v2.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "helpers.h"
+
+#define SIGNBIT (uint32_t)0x80000000
+#define SIGNBIT64 ((uint64_t)1 << 63)
+
+#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] = CPSR_Q
+
+static float_status neon_float_status;
+#define NFS &neon_float_status
+
+/* Helper routines to perform bitwise copies between float and int.  */
+static inline float32 vfp_itos(uint32_t i)
+{
+    union {
+        uint32_t i;
+        float32 s;
+    } v;
+
+    v.i = i;
+    return v.s;
+}
+
+static inline uint32_t vfp_stoi(float32 s)
+{
+    union {
+        uint32_t i;
+        float32 s;
+    } v;
+
+    v.s = s;
+    return v.i;
+}
+
+#define NEON_TYPE1(name, type) \
+typedef struct \
+{ \
+    type v1; \
+} neon_##name;
+#ifdef WORDS_BIGENDIAN
+#define NEON_TYPE2(name, type) \
+typedef struct \
+{ \
+    type v2; \
+    type v1; \
+} neon_##name;
+#define NEON_TYPE4(name, type) \
+typedef struct \
+{ \
+    type v4; \
+    type v3; \
+    type v2; \
+    type v1; \
+} neon_##name;
+#else
+#define NEON_TYPE2(name, type) \
+typedef struct \
+{ \
+    type v1; \
+    type v2; \
+} neon_##name;
+#define NEON_TYPE4(name, type) \
+typedef struct \
+{ \
+    type v1; \
+    type v2; \
+    type v3; \
+    type v4; \
+} neon_##name;
+#endif
+
+NEON_TYPE4(s8, int8_t)
+NEON_TYPE4(u8, uint8_t)
+NEON_TYPE2(s16, int16_t)
+NEON_TYPE2(u16, uint16_t)
+NEON_TYPE1(s32, int32_t)
+NEON_TYPE1(u32, uint32_t)
+#undef NEON_TYPE4
+#undef NEON_TYPE2
+#undef NEON_TYPE1
+
+/* Copy from a uint32_t to a vector structure type.  */
+#define NEON_UNPACK(vtype, dest, val) do { \
+    union { \
+        vtype v; \
+        uint32_t i; \
+    } conv_u; \
+    conv_u.i = (val); \
+    dest = conv_u.v; \
+    } while(0)
+
+/* Copy from a vector structure type to a uint32_t.  */
+#define NEON_PACK(vtype, dest, val) do { \
+    union { \
+        vtype v; \
+        uint32_t i; \
+    } conv_u; \
+    conv_u.v = (val); \
+    dest = conv_u.i; \
+    } while(0)
+
+#define NEON_DO1 \
+    NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1);
+#define NEON_DO2 \
+    NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \
+    NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2);
+#define NEON_DO4 \
+    NEON_FN(vdest.v1, vsrc1.v1, vsrc2.v1); \
+    NEON_FN(vdest.v2, vsrc1.v2, vsrc2.v2); \
+    NEON_FN(vdest.v3, vsrc1.v3, vsrc2.v3); \
+    NEON_FN(vdest.v4, vsrc1.v4, vsrc2.v4);
+
+#define NEON_VOP_BODY(vtype, n) \
+{ \
+    uint32_t res; \
+    vtype vsrc1; \
+    vtype vsrc2; \
+    vtype vdest; \
+    NEON_UNPACK(vtype, vsrc1, arg1); \
+    NEON_UNPACK(vtype, vsrc2, arg2); \
+    NEON_DO##n; \
+    NEON_PACK(vtype, res, vdest); \
+    return res; \
+}
+
+#define NEON_VOP(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \
+NEON_VOP_BODY(vtype, n)
+
+#define NEON_VOP_ENV(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(CPUState *env, uint32_t arg1, uint32_t arg2) \
+NEON_VOP_BODY(vtype, n)
+
+/* Pairwise operations.  */
+/* For 32-bit elements each segment only contains a single element, so
+   the elementwise and pairwise operations are the same.  */
+#define NEON_PDO2 \
+    NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \
+    NEON_FN(vdest.v2, vsrc2.v1, vsrc2.v2);
+#define NEON_PDO4 \
+    NEON_FN(vdest.v1, vsrc1.v1, vsrc1.v2); \
+    NEON_FN(vdest.v2, vsrc1.v3, vsrc1.v4); \
+    NEON_FN(vdest.v3, vsrc2.v1, vsrc2.v2); \
+    NEON_FN(vdest.v4, vsrc2.v3, vsrc2.v4); \
+
+#define NEON_POP(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(uint32_t arg1, uint32_t arg2) \
+{ \
+    uint32_t res; \
+    vtype vsrc1; \
+    vtype vsrc2; \
+    vtype vdest; \
+    NEON_UNPACK(vtype, vsrc1, arg1); \
+    NEON_UNPACK(vtype, vsrc2, arg2); \
+    NEON_PDO##n; \
+    NEON_PACK(vtype, res, vdest); \
+    return res; \
+}
+
+/* Unary operators.  */
+#define NEON_VOP1(name, vtype, n) \
+uint32_t HELPER(glue(neon_,name))(uint32_t arg) \
+{ \
+    vtype vsrc1; \
+    vtype vdest; \
+    NEON_UNPACK(vtype, vsrc1, arg); \
+    NEON_DO##n; \
+    NEON_PACK(vtype, arg, vdest); \
+    return arg; \
+}
+
+
+#define NEON_USAT(dest, src1, src2, type) do { \
+    uint32_t tmp = (uint32_t)src1 + (uint32_t)src2; \
+    if (tmp != (type)tmp) { \
+        SET_QC(); \
+        dest = ~0; \
+    } else { \
+        dest = tmp; \
+    }} while(0)
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
+NEON_VOP_ENV(qadd_u8, neon_u8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
+NEON_VOP_ENV(qadd_u16, neon_u16, 2)
+#undef NEON_FN
+#undef NEON_USAT
+
+#define NEON_SSAT(dest, src1, src2, type) do { \
+    int32_t tmp = (uint32_t)src1 + (uint32_t)src2; \
+    if (tmp != (type)tmp) { \
+        SET_QC(); \
+        if (src2 > 0) { \
+            tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \
+        } else { \
+            tmp = 1 << (sizeof(type) * 8 - 1); \
+        } \
+    } \
+    dest = tmp; \
+    } while(0)
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
+NEON_VOP_ENV(qadd_s8, neon_s8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
+NEON_VOP_ENV(qadd_s16, neon_s16, 2)
+#undef NEON_FN
+#undef NEON_SSAT
+
+#define NEON_USAT(dest, src1, src2, type) do { \
+    uint32_t tmp = (uint32_t)src1 - (uint32_t)src2; \
+    if (tmp != (type)tmp) { \
+        SET_QC(); \
+        dest = 0; \
+    } else { \
+        dest = tmp; \
+    }} while(0)
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint8_t)
+NEON_VOP_ENV(qsub_u8, neon_u8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_USAT(dest, src1, src2, uint16_t)
+NEON_VOP_ENV(qsub_u16, neon_u16, 2)
+#undef NEON_FN
+#undef NEON_USAT
+
+#define NEON_SSAT(dest, src1, src2, type) do { \
+    int32_t tmp = (uint32_t)src1 - (uint32_t)src2; \
+    if (tmp != (type)tmp) { \
+        SET_QC(); \
+        if (src2 < 0) { \
+            tmp = (1 << (sizeof(type) * 8 - 1)) - 1; \
+        } else { \
+            tmp = 1 << (sizeof(type) * 8 - 1); \
+        } \
+    } \
+    dest = tmp; \
+    } while(0)
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int8_t)
+NEON_VOP_ENV(qsub_s8, neon_s8, 4)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_SSAT(dest, src1, src2, int16_t)
+NEON_VOP_ENV(qsub_s16, neon_s16, 2)
+#undef NEON_FN
+#undef NEON_SSAT
+
+#define NEON_FN(dest, src1, src2) dest = (src1 + src2) >> 1
+NEON_VOP(hadd_s8, neon_s8, 4)
+NEON_VOP(hadd_u8, neon_u8, 4)
+NEON_VOP(hadd_s16, neon_s16, 2)
+NEON_VOP(hadd_u16, neon_u16, 2)
+#undef NEON_FN
+
+int32_t HELPER(neon_hadd_s32)(int32_t src1, int32_t src2)
+{
+    int32_t dest;
+
+    dest = (src1 >> 1) + (src2 >> 1);
+    if (src1 & src2 & 1)
+        dest++;
+    return dest;
+}
+
+uint32_t HELPER(neon_hadd_u32)(uint32_t src1, uint32_t src2)
+{
+    uint32_t dest;
+
+    dest = (src1 >> 1) + (src2 >> 1);
+    if (src1 & src2 & 1)
+        dest++;
+    return dest;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 + src2 + 1) >> 1
+NEON_VOP(rhadd_s8, neon_s8, 4)
+NEON_VOP(rhadd_u8, neon_u8, 4)
+NEON_VOP(rhadd_s16, neon_s16, 2)
+NEON_VOP(rhadd_u16, neon_u16, 2)
+#undef NEON_FN
+
+int32_t HELPER(neon_rhadd_s32)(int32_t src1, int32_t src2)
+{
+    int32_t dest;
+
+    dest = (src1 >> 1) + (src2 >> 1);
+    if ((src1 | src2) & 1)
+        dest++;
+    return dest;
+}
+
+uint32_t HELPER(neon_rhadd_u32)(uint32_t src1, uint32_t src2)
+{
+    uint32_t dest;
+
+    dest = (src1 >> 1) + (src2 >> 1);
+    if ((src1 | src2) & 1)
+        dest++;
+    return dest;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 - src2) >> 1
+NEON_VOP(hsub_s8, neon_s8, 4)
+NEON_VOP(hsub_u8, neon_u8, 4)
+NEON_VOP(hsub_s16, neon_s16, 2)
+NEON_VOP(hsub_u16, neon_u16, 2)
+#undef NEON_FN
+
+int32_t HELPER(neon_hsub_s32)(int32_t src1, int32_t src2)
+{
+    int32_t dest;
+
+    dest = (src1 >> 1) - (src2 >> 1);
+    if ((~src1) & src2 & 1)
+        dest--;
+    return dest;
+}
+
+uint32_t HELPER(neon_hsub_u32)(uint32_t src1, uint32_t src2)
+{
+    uint32_t dest;
+
+    dest = (src1 >> 1) - (src2 >> 1);
+    if ((~src1) & src2 & 1)
+        dest--;
+    return dest;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? ~0 : 0
+NEON_VOP(cgt_s8, neon_s8, 4)
+NEON_VOP(cgt_u8, neon_u8, 4)
+NEON_VOP(cgt_s16, neon_s16, 2)
+NEON_VOP(cgt_u16, neon_u16, 2)
+NEON_VOP(cgt_s32, neon_s32, 1)
+NEON_VOP(cgt_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 >= src2) ? ~0 : 0
+NEON_VOP(cge_s8, neon_s8, 4)
+NEON_VOP(cge_u8, neon_u8, 4)
+NEON_VOP(cge_s16, neon_s16, 2)
+NEON_VOP(cge_u16, neon_u16, 2)
+NEON_VOP(cge_s32, neon_s32, 1)
+NEON_VOP(cge_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2
+NEON_VOP(min_s8, neon_s8, 4)
+NEON_VOP(min_u8, neon_u8, 4)
+NEON_VOP(min_s16, neon_s16, 2)
+NEON_VOP(min_u16, neon_u16, 2)
+NEON_VOP(min_s32, neon_s32, 1)
+NEON_VOP(min_u32, neon_u32, 1)
+NEON_POP(pmin_s8, neon_s8, 4)
+NEON_POP(pmin_u8, neon_u8, 4)
+NEON_POP(pmin_s16, neon_s16, 2)
+NEON_POP(pmin_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? src1 : src2
+NEON_VOP(max_s8, neon_s8, 4)
+NEON_VOP(max_u8, neon_u8, 4)
+NEON_VOP(max_s16, neon_s16, 2)
+NEON_VOP(max_u16, neon_u16, 2)
+NEON_VOP(max_s32, neon_s32, 1)
+NEON_VOP(max_u32, neon_u32, 1)
+NEON_POP(pmax_s8, neon_s8, 4)
+NEON_POP(pmax_u8, neon_u8, 4)
+NEON_POP(pmax_s16, neon_s16, 2)
+NEON_POP(pmax_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) \
+    dest = (src1 > src2) ? (src1 - src2) : (src2 - src1)
+NEON_VOP(abd_s8, neon_s8, 4)
+NEON_VOP(abd_u8, neon_u8, 4)
+NEON_VOP(abd_s16, neon_s16, 2)
+NEON_VOP(abd_u16, neon_u16, 2)
+NEON_VOP(abd_s32, neon_s32, 1)
+NEON_VOP(abd_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp >= sizeof(src1) * 8 || tmp <= -sizeof(src1) * 8) { \
+        dest = 0; \
+    } else if (tmp < 0) { \
+        dest = src1 >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+    }} while (0)
+NEON_VOP(shl_u8, neon_u8, 4)
+NEON_VOP(shl_u16, neon_u16, 2)
+NEON_VOP(shl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_shl_u64)(uint64_t val, uint64_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    if (shift >= 64 || shift <= -64) {
+        val = 0;
+    } else if (shift < 0) {
+        val >>= -shift;
+    } else {
+        val <<= shift;
+    }
+    return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp >= sizeof(src1) * 8) { \
+        dest = 0; \
+    } else if (tmp <= -sizeof(src1) * 8) { \
+        dest = src1 >> (sizeof(src1) * 8 - 1); \
+    } else if (tmp < 0) { \
+        dest = src1 >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+    }} while (0)
+NEON_VOP(shl_s8, neon_s8, 4)
+NEON_VOP(shl_s16, neon_s16, 2)
+NEON_VOP(shl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_shl_s64)(uint64_t valop, uint64_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    int64_t val = valop;
+    if (shift >= 64) {
+        val = 0;
+    } else if (shift <= -64) {
+        val >>= 63;
+    } else if (shift < 0) {
+        val >>= -shift;
+    } else {
+        val <<= shift;
+    }
+    return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp >= sizeof(src1) * 8) { \
+        dest = 0; \
+    } else if (tmp < -sizeof(src1) * 8) { \
+        dest >>= sizeof(src1) * 8 - 1; \
+    } else if (tmp == -sizeof(src1) * 8) { \
+        dest = src1 >> (tmp - 1); \
+        dest++; \
+        src2 >>= 1; \
+    } else if (tmp < 0) { \
+        dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+    }} while (0)
+NEON_VOP(rshl_s8, neon_s8, 4)
+NEON_VOP(rshl_s16, neon_s16, 2)
+NEON_VOP(rshl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_rshl_s64)(uint64_t valop, uint64_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    int64_t val = valop;
+    if (shift >= 64) {
+        val = 0;
+    } else if (shift < -64) {
+        val >>= 63;
+    } else if (shift == -63) {
+        val >>= 63;
+        val++;
+        val >>= 1;
+    } else if (shift < 0) {
+        val = (val + ((int64_t)1 << (-1 - shift))) >> -shift;
+    } else {
+        val <<= shift;
+    }
+    return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp >= sizeof(src1) * 8 || tmp < -sizeof(src1) * 8) { \
+        dest = 0; \
+    } else if (tmp == -sizeof(src1) * 8) { \
+        dest = src1 >> (tmp - 1); \
+    } else if (tmp < 0) { \
+        dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+    }} while (0)
+NEON_VOP(rshl_u8, neon_u8, 4)
+NEON_VOP(rshl_u16, neon_u16, 2)
+NEON_VOP(rshl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_rshl_u64)(uint64_t val, uint64_t shiftop)
+{
+    int8_t shift = (uint8_t)shiftop;
+    if (shift >= 64 || shift < 64) {
+        val = 0;
+    } else if (shift == -64) {
+        /* Rounding a 1-bit result just preserves that bit.  */
+        val >>= 63;
+    } if (shift < 0) {
+        val = (val + ((uint64_t)1 << (-1 - shift))) >> -shift;
+        val >>= -shift;
+    } else {
+        val <<= shift;
+    }
+    return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp >= sizeof(src1) * 8) { \
+        if (src1) { \
+            SET_QC(); \
+            dest = ~0; \
+        } else { \
+            dest = 0; \
+        } \
+    } else if (tmp <= -sizeof(src1) * 8) { \
+        dest = 0; \
+    } else if (tmp < 0) { \
+        dest = src1 >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+        if ((dest >> tmp) != src1) { \
+            SET_QC(); \
+            dest = ~0; \
+        } \
+    }} while (0)
+NEON_VOP_ENV(qshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qshl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    if (shift >= 64) {
+        if (val) {
+            val = ~(uint64_t)0;
+            SET_QC();
+        } else {
+            val = 0;
+        }
+    } else if (shift <= -64) {
+        val = 0;
+    } else if (shift < 0) {
+        val >>= -shift;
+    } else {
+        uint64_t tmp = val;
+        val <<= shift;
+        if ((val >> shift) != tmp) {
+            SET_QC();
+            val = ~(uint64_t)0;
+        }
+    }
+    return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp >= sizeof(src1) * 8) { \
+        if (src1) \
+            SET_QC(); \
+        dest = src1 >> 31; \
+    } else if (tmp <= -sizeof(src1) * 8) { \
+        dest = src1 >> 31; \
+    } else if (tmp < 0) { \
+        dest = src1 >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+        if ((dest >> tmp) != src1) { \
+            SET_QC(); \
+            dest = src2 >> 31; \
+        } \
+    }} while (0)
+NEON_VOP_ENV(qshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qshl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
+{
+    int8_t shift = (uint8_t)shiftop;
+    int64_t val = valop;
+    if (shift >= 64) {
+        if (val) {
+            SET_QC();
+            val = (val >> 63) & ~SIGNBIT64;
+        }
+    } else if (shift <= 64) {
+        val >>= 63;
+    } else if (shift < 0) {
+        val >>= -shift;
+    } else {
+        int64_t tmp = val;
+        val <<= shift;
+        if ((val >> shift) != tmp) {
+            SET_QC();
+            val = (tmp >> 63) ^ ~SIGNBIT64;
+        }
+    }
+    return val;
+}
+
+
+/* FIXME: This is wrong.  */
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp < 0) { \
+        dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+        if ((dest >> tmp) != src1) { \
+            SET_QC(); \
+            dest = ~0; \
+        } \
+    }} while (0)
+NEON_VOP_ENV(qrshl_u8, neon_u8, 4)
+NEON_VOP_ENV(qrshl_u16, neon_u16, 2)
+NEON_VOP_ENV(qrshl_u32, neon_u32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qrshl_u64)(CPUState *env, uint64_t val, uint64_t shiftop)
+{
+    int8_t shift = (int8_t)shiftop;
+    if (shift < 0) {
+        val = (val + (1 << (-1 - shift))) >> -shift;
+    } else { \
+        uint64_t tmp = val;
+        val <<= shift;
+        if ((val >> shift) != tmp) {
+            SET_QC();
+            val = ~0;
+        }
+    }
+    return val;
+}
+
+#define NEON_FN(dest, src1, src2) do { \
+    int8_t tmp; \
+    tmp = (int8_t)src2; \
+    if (tmp < 0) { \
+        dest = (src1 + (1 << (-1 - tmp))) >> -tmp; \
+    } else { \
+        dest = src1 << tmp; \
+        if ((dest >> tmp) != src1) { \
+            SET_QC(); \
+            dest = src1 >> 31; \
+        } \
+    }} while (0)
+NEON_VOP_ENV(qrshl_s8, neon_s8, 4)
+NEON_VOP_ENV(qrshl_s16, neon_s16, 2)
+NEON_VOP_ENV(qrshl_s32, neon_s32, 1)
+#undef NEON_FN
+
+uint64_t HELPER(neon_qrshl_s64)(CPUState *env, uint64_t valop, uint64_t shiftop)
+{
+    int8_t shift = (uint8_t)shiftop;
+    int64_t val = valop;
+
+    if (shift < 0) {
+        val = (val + (1 << (-1 - shift))) >> -shift;
+    } else {
+        int64_t tmp = val;;
+        val <<= shift;
+        if ((val >> shift) != tmp) {
+            SET_QC();
+            val = tmp >> 31;
+        }
+    }
+    return val;
+}
+
+uint32_t HELPER(neon_add_u8)(uint32_t a, uint32_t b)
+{
+    uint32_t mask;
+    mask = (a ^ b) & 0x80808080u;
+    a &= ~0x80808080u;
+    b &= ~0x80808080u;
+    return (a + b) ^ mask;
+}
+
+uint32_t HELPER(neon_add_u16)(uint32_t a, uint32_t b)
+{
+    uint32_t mask;
+    mask = (a ^ b) & 0x80008000u;
+    a &= ~0x80008000u;
+    b &= ~0x80008000u;
+    return (a + b) ^ mask;
+}
+
+#define NEON_FN(dest, src1, src2) dest = src1 + src2
+NEON_POP(padd_u8, neon_u8, 4)
+NEON_POP(padd_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = src1 - src2
+NEON_VOP(sub_u8, neon_u8, 4)
+NEON_VOP(sub_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = src1 * src2
+NEON_VOP(mul_u8, neon_u8, 4)
+NEON_VOP(mul_u16, neon_u16, 2)
+#undef NEON_FN
+
+/* Polynomial multiplication is like integer multiplication except the
+   partial products are XORed, not added.  */
+uint32_t HELPER(neon_mul_p8)(uint32_t op1, uint32_t op2)
+{
+    uint32_t mask;
+    uint32_t result;
+    result = 0;
+    while (op1) {
+        mask = 0;
+        if (op1 & 1)
+            mask |= 0xff;
+        if (op1 & (1 << 8))
+            mask |= (0xff << 8);
+        if (op1 & (1 << 16))
+            mask |= (0xff << 16);
+        if (op1 & (1 << 24))
+            mask |= (0xff << 24);
+        result ^= op2 & mask;
+        op1 = (op1 >> 1) & 0x7f7f7f7f;
+        op2 = (op2 << 1) & 0xfefefefe;
+    }
+    return result;
+}
+
+#define NEON_FN(dest, src1, src2) dest = (src1 & src2) ? -1 : 0
+NEON_VOP(tst_u8, neon_u8, 4)
+NEON_VOP(tst_u16, neon_u16, 2)
+NEON_VOP(tst_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src1, src2) dest = (src1 == src2) ? -1 : 0
+NEON_VOP(ceq_u8, neon_u8, 4)
+NEON_VOP(ceq_u16, neon_u16, 2)
+NEON_VOP(ceq_u32, neon_u32, 1)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = (src < 0) ? -src : src
+NEON_VOP1(abs_s8, neon_s8, 4)
+NEON_VOP1(abs_s16, neon_s16, 2)
+#undef NEON_FN
+
+/* Count Leading Sign/Zero Bits.  */
+static inline int do_clz8(uint8_t x)
+{
+    int n;
+    for (n = 8; x; n--)
+        x >>= 1;
+    return n;
+}
+
+static inline int do_clz16(uint16_t x)
+{
+    int n;
+    for (n = 16; x; n--)
+        x >>= 1;
+    return n;
+}
+
+#define NEON_FN(dest, src, dummy) dest = do_clz8(src)
+NEON_VOP1(clz_u8, neon_u8, 4)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = do_clz16(src)
+NEON_VOP1(clz_u16, neon_u16, 2)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = do_clz8((src < 0) ? ~src : src) - 1
+NEON_VOP1(cls_s8, neon_s8, 4)
+#undef NEON_FN
+
+#define NEON_FN(dest, src, dummy) dest = do_clz16((src < 0) ? ~src : src) - 1
+NEON_VOP1(cls_s16, neon_s16, 2)
+#undef NEON_FN
+
+uint32_t HELPER(neon_cls_s32)(uint32_t x)
+{
+    int count;
+    if ((int32_t)x < 0)
+        x = ~x;
+    for (count = 32; x; count--)
+        x = x >> 1;
+    return count - 1;
+}
+
+/* Bit count.  */
+uint32_t HELPER(neon_cnt_u8)(uint32_t x)
+{
+    x = (x & 0x55555555) + ((x >>  1) & 0x55555555);
+    x = (x & 0x33333333) + ((x >>  2) & 0x33333333);
+    x = (x & 0x0f0f0f0f) + ((x >>  4) & 0x0f0f0f0f);
+    return x;
+}
+
+#define NEON_QDMULH16(dest, src1, src2, round) do { \
+    uint32_t tmp = (int32_t)(int16_t) src1 * (int16_t) src2; \
+    if ((tmp ^ (tmp << 1)) & SIGNBIT) { \
+        SET_QC(); \
+        tmp = (tmp >> 31) ^ ~SIGNBIT; \
+    } \
+    tmp <<= 1; \
+    if (round) { \
+        int32_t old = tmp; \
+        tmp += 1 << 15; \
+        if ((int32_t)tmp < old) { \
+            SET_QC(); \
+            tmp = SIGNBIT - 1; \
+        } \
+    } \
+    dest = tmp >> 16; \
+    } while(0)
+#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 0)
+NEON_VOP_ENV(qdmulh_s16, neon_s16, 2)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_QDMULH16(dest, src1, src2, 1)
+NEON_VOP_ENV(qrdmulh_s16, neon_s16, 2)
+#undef NEON_FN
+#undef NEON_QDMULH16
+
+#define NEON_QDMULH32(dest, src1, src2, round) do { \
+    uint64_t tmp = (int64_t)(int32_t) src1 * (int32_t) src2; \
+    if ((tmp ^ (tmp << 1)) & SIGNBIT64) { \
+        SET_QC(); \
+        tmp = (tmp >> 63) ^ ~SIGNBIT64; \
+    } else { \
+        tmp <<= 1; \
+    } \
+    if (round) { \
+        int64_t old = tmp; \
+        tmp += (int64_t)1 << 31; \
+        if ((int64_t)tmp < old) { \
+            SET_QC(); \
+            tmp = SIGNBIT64 - 1; \
+        } \
+    } \
+    dest = tmp >> 32; \
+    } while(0)
+#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 0)
+NEON_VOP_ENV(qdmulh_s32, neon_s32, 1)
+#undef NEON_FN
+#define NEON_FN(dest, src1, src2) NEON_QDMULH32(dest, src1, src2, 1)
+NEON_VOP_ENV(qrdmulh_s32, neon_s32, 1)
+#undef NEON_FN
+#undef NEON_QDMULH32
+
+uint32_t HELPER(neon_narrow_u8)(uint64_t x)
+{
+    return (x & 0xffu) | ((x >> 8) & 0xff00u) | ((x >> 16) & 0xff0000u)
+           | ((x >> 24) & 0xff000000u);
+}
+
+uint32_t HELPER(neon_narrow_u16)(uint64_t x)
+{
+    return (x & 0xffffu) | ((x >> 16) & 0xffff0000u);
+}
+
+uint32_t HELPER(neon_narrow_high_u8)(uint64_t x)
+{
+    return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00)
+            | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000);
+}
+
+uint32_t HELPER(neon_narrow_high_u16)(uint64_t x)
+{
+    return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
+}
+
+uint32_t HELPER(neon_narrow_round_high_u8)(uint64_t x)
+{
+    x &= 0xff80ff80ff80ff80ull;
+    x += 0x0080008000800080ull;
+    return ((x >> 8) & 0xff) | ((x >> 16) & 0xff00)
+            | ((x >> 24) & 0xff0000) | ((x >> 32) & 0xff000000);
+}
+
+uint32_t HELPER(neon_narrow_round_high_u16)(uint64_t x)
+{
+    x &= 0xffff8000ffff8000ull;
+    x += 0x0000800000008000ull;
+    return ((x >> 16) & 0xffff) | ((x >> 32) & 0xffff0000);
+}
+
+uint32_t HELPER(neon_narrow_sat_u8)(CPUState *env, uint64_t x)
+{
+    uint16_t s;
+    uint8_t d;
+    uint32_t res = 0;
+#define SAT8(n) \
+    s = x >> n; \
+    if (s > 0xff) { \
+        d = 0xff; \
+        SET_QC(); \
+    } else  { \
+        d = s; \
+    } \
+    res |= (uint32_t)d << (n / 2);
+
+    SAT8(0);
+    SAT8(16);
+    SAT8(32);
+    SAT8(48);
+#undef SAT8
+    return res;
+}
+
+uint32_t HELPER(neon_narrow_sat_s8)(CPUState *env, uint64_t x)
+{
+    int16_t s;
+    uint8_t d;
+    uint32_t res = 0;
+#define SAT8(n) \
+    s = x >> n; \
+    if (s != (int8_t)s) { \
+        d = (s >> 15) ^ 0x7f; \
+        SET_QC(); \
+    } else  { \
+        d = s; \
+    } \
+    res |= (uint32_t)d << (n / 2);
+
+    SAT8(0);
+    SAT8(16);
+    SAT8(32);
+    SAT8(48);
+#undef SAT8
+    return res;
+}
+
+uint32_t HELPER(neon_narrow_sat_u16)(CPUState *env, uint64_t x)
+{
+    uint32_t high;
+    uint32_t low;
+    low = x;
+    if (low > 0xffff) {
+        low = 0xffff;
+        SET_QC();
+    }
+    high = x >> 32;
+    if (high > 0xffff) {
+        high = 0xffff;
+        SET_QC();
+    }
+    return low | (high << 16);
+}
+
+uint32_t HELPER(neon_narrow_sat_s16)(CPUState *env, uint64_t x)
+{
+    int32_t low;
+    int32_t high;
+    low = x;
+    if (low != (int16_t)low) {
+        low = (low >> 31) ^ 0x7fff;
+        SET_QC();
+    }
+    high = x >> 32;
+    if (high != (int16_t)high) {
+        high = (high >> 31) ^ 0x7fff;
+        SET_QC();
+    }
+    return (uint16_t)low | (high << 16);
+}
+
+uint32_t HELPER(neon_narrow_sat_u32)(CPUState *env, uint64_t x)
+{
+    if (x > 0xffffffffu) {
+        SET_QC();
+        return 0xffffffffu;
+    }
+    return x;
+}
+
+uint32_t HELPER(neon_narrow_sat_s32)(CPUState *env, uint64_t x)
+{
+    if ((int64_t)x != (int32_t)x) {
+        SET_QC();
+        return (x >> 63) ^ 0x7fffffff;
+    }
+    return x;
+}
+
+uint64_t HELPER(neon_widen_u8)(uint32_t x)
+{
+    uint64_t tmp;
+    uint64_t ret;
+    ret = (uint8_t)x;
+    tmp = (uint8_t)(x >> 8);
+    ret |= tmp << 16;
+    tmp = (uint8_t)(x >> 16);
+    ret |= tmp << 32;
+    tmp = (uint8_t)(x >> 24);
+    ret |= tmp << 48;
+    return ret;
+}
+
+uint64_t HELPER(neon_widen_s8)(uint32_t x)
+{
+    uint64_t tmp;
+    uint64_t ret;
+    ret = (uint16_t)(int8_t)x;
+    tmp = (uint16_t)(int8_t)(x >> 8);
+    ret |= tmp << 16;
+    tmp = (uint16_t)(int8_t)(x >> 16);
+    ret |= tmp << 32;
+    tmp = (uint16_t)(int8_t)(x >> 24);
+    ret |= tmp << 48;
+    return ret;
+}
+
+uint64_t HELPER(neon_widen_u16)(uint32_t x)
+{
+    uint64_t high = (uint16_t)(x >> 16);
+    return ((uint16_t)x) | (high << 32);
+}
+
+uint64_t HELPER(neon_widen_s16)(uint32_t x)
+{
+    uint64_t high = (int16_t)(x >> 16);
+    return ((uint32_t)(int16_t)x) | (high << 32);
+}
+
+uint64_t HELPER(neon_addl_u16)(uint64_t a, uint64_t b)
+{
+    uint64_t mask;
+    mask = (a ^ b) & 0x8000800080008000ull;
+    a &= ~0x8000800080008000ull;
+    b &= ~0x8000800080008000ull;
+    return (a + b) ^ mask;
+}
+
+uint64_t HELPER(neon_addl_u32)(uint64_t a, uint64_t b)
+{
+    uint64_t mask;
+    mask = (a ^ b) & 0x8000000080000000ull;
+    a &= ~0x8000000080000000ull;
+    b &= ~0x8000000080000000ull;
+    return (a + b) ^ mask;
+}
+
+uint64_t HELPER(neon_paddl_u16)(uint64_t a, uint64_t b)
+{
+    uint64_t tmp;
+    uint64_t tmp2;
+
+    tmp = a & 0x0000ffff0000ffffull;
+    tmp += (a >> 16) & 0x0000ffff0000ffffull;
+    tmp2 = b & 0xffff0000ffff0000ull;
+    tmp2 += (b << 16) & 0xffff0000ffff0000ull;
+    return    ( tmp         & 0xffff)
+            | ((tmp  >> 16) & 0xffff0000ull)
+            | ((tmp2 << 16) & 0xffff00000000ull)
+            | ( tmp2        & 0xffff000000000000ull);
+}
+
+uint64_t HELPER(neon_paddl_u32)(uint64_t a, uint64_t b)
+{
+    uint32_t low = a + (a >> 32);
+    uint32_t high = b + (b >> 32);
+    return low + ((uint64_t)high << 32);
+}
+
+uint64_t HELPER(neon_subl_u16)(uint64_t a, uint64_t b)
+{
+    uint64_t mask;
+    mask = (a ^ ~b) & 0x8000800080008000ull;
+    a |= 0x8000800080008000ull;
+    b &= ~0x8000800080008000ull;
+    return (a - b) ^ mask;
+}
+
+uint64_t HELPER(neon_subl_u32)(uint64_t a, uint64_t b)
+{
+    uint64_t mask;
+    mask = (a ^ ~b) & 0x8000000080000000ull;
+    a |= 0x8000000080000000ull;
+    b &= ~0x8000000080000000ull;
+    return (a - b) ^ mask;
+}
+
+uint64_t HELPER(neon_addl_saturate_s32)(CPUState *env, uint64_t a, uint64_t b)
+{
+    uint32_t x, y;
+    uint32_t low, high;
+
+    x = a;
+    y = b;
+    low = x + y;
+    if (((low ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) {
+        SET_QC();
+        low = ((int32_t)x >> 31) ^ ~SIGNBIT;
+    }
+    x = a >> 32;
+    y = b >> 32;
+    high = x + y;
+    if (((high ^ x) & SIGNBIT) && !((x ^ y) & SIGNBIT)) {
+        SET_QC();
+        high = ((int32_t)x >> 31) ^ ~SIGNBIT;
+    }
+    return low | ((uint64_t)high << 32);
+}
+
+uint64_t HELPER(neon_addl_saturate_s64)(CPUState *env, uint64_t a, uint64_t b)
+{
+    uint64_t result;
+
+    result = a + b;
+    if (((result ^ a) & SIGNBIT64) && !((a ^ b) & SIGNBIT64)) {
+        SET_QC();
+        result = ((int64_t)a >> 63) ^ ~SIGNBIT64;
+    }
+    return result;
+}
+
+#define DO_ABD(dest, x, y, type) do { \
+    type tmp_x = x; \
+    type tmp_y = y; \
+    dest = ((tmp_x > tmp_y) ? tmp_x - tmp_y : tmp_y - tmp_x); \
+    } while(0)
+
+uint64_t HELPER(neon_abdl_u16)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+    DO_ABD(result, a, b, uint8_t);
+    DO_ABD(tmp, a >> 8, b >> 8, uint8_t);
+    result |= tmp << 16;
+    DO_ABD(tmp, a >> 16, b >> 16, uint8_t);
+    result |= tmp << 32;
+    DO_ABD(tmp, a >> 24, b >> 24, uint8_t);
+    result |= tmp << 48;
+    return result;
+}
+
+uint64_t HELPER(neon_abdl_s16)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+    DO_ABD(result, a, b, int8_t);
+    DO_ABD(tmp, a >> 8, b >> 8, int8_t);
+    result |= tmp << 16;
+    DO_ABD(tmp, a >> 16, b >> 16, int8_t);
+    result |= tmp << 32;
+    DO_ABD(tmp, a >> 24, b >> 24, int8_t);
+    result |= tmp << 48;
+    return result;
+}
+
+uint64_t HELPER(neon_abdl_u32)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+    DO_ABD(result, a, b, uint16_t);
+    DO_ABD(tmp, a >> 16, b >> 16, uint16_t);
+    return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_abdl_s32)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+    DO_ABD(result, a, b, int16_t);
+    DO_ABD(tmp, a >> 16, b >> 16, int16_t);
+    return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_abdl_u64)(uint32_t a, uint32_t b)
+{
+    uint64_t result;
+    DO_ABD(result, a, b, uint32_t);
+    return result;
+}
+
+uint64_t HELPER(neon_abdl_s64)(uint32_t a, uint32_t b)
+{
+    uint64_t result;
+    DO_ABD(result, a, b, int32_t);
+    return result;
+}
+#undef DO_ABD
+
+/* Widening multiply. Named type is the source type.  */
+#define DO_MULL(dest, x, y, type1, type2) do { \
+    type1 tmp_x = x; \
+    type1 tmp_y = y; \
+    dest = (type2)((type2)tmp_x * (type2)tmp_y); \
+    } while(0)
+
+uint64_t HELPER(neon_mull_u8)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+
+    DO_MULL(result, a, b, uint8_t, uint16_t);
+    DO_MULL(tmp, a >> 8, b >> 8, uint8_t, uint16_t);
+    result |= tmp << 16;
+    DO_MULL(tmp, a >> 16, b >> 16, uint8_t, uint16_t);
+    result |= tmp << 32;
+    DO_MULL(tmp, a >> 24, b >> 24, uint8_t, uint16_t);
+    result |= tmp << 48;
+    return result;
+}
+
+uint64_t HELPER(neon_mull_s8)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+
+    DO_MULL(result, a, b, int8_t, uint16_t);
+    DO_MULL(tmp, a >> 8, b >> 8, int8_t, uint16_t);
+    result |= tmp << 16;
+    DO_MULL(tmp, a >> 16, b >> 16, int8_t, uint16_t);
+    result |= tmp << 32;
+    DO_MULL(tmp, a >> 24, b >> 24, int8_t, uint16_t);
+    result |= tmp << 48;
+    return result;
+}
+
+uint64_t HELPER(neon_mull_u16)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+
+    DO_MULL(result, a, b, uint16_t, uint32_t);
+    DO_MULL(tmp, a >> 16, b >> 16, uint16_t, uint32_t);
+    return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_mull_s16)(uint32_t a, uint32_t b)
+{
+    uint64_t tmp;
+    uint64_t result;
+
+    DO_MULL(result, a, b, int16_t, uint32_t);
+    DO_MULL(tmp, a >> 16, b >> 16, int16_t, uint32_t);
+    return result | (tmp << 32);
+}
+
+uint64_t HELPER(neon_negl_u16)(uint64_t x)
+{
+    uint16_t tmp;
+    uint64_t result;
+    result = (uint16_t)-x;
+    tmp = -(x >> 16);
+    result |= (uint64_t)tmp << 16;
+    tmp = -(x >> 32);
+    result |= (uint64_t)tmp << 32;
+    tmp = -(x >> 48);
+    result |= (uint64_t)tmp << 48;
+    return result;
+}
+
+#include <stdio.h>
+uint64_t HELPER(neon_negl_u32)(uint64_t x)
+{
+    uint32_t low = -x;
+    uint32_t high = -(x >> 32);
+    return low | ((uint64_t)high << 32);
+}
+
+/* FIXME:  There should be a native op for this.  */
+uint64_t HELPER(neon_negl_u64)(uint64_t x)
+{
+    return -x;
+}
+
+/* Saturnating sign manuipulation.  */
+/* ??? Make these use NEON_VOP1 */
+#define DO_QABS8(x) do { \
+    if (x == (int8_t)0x80) { \
+        x = 0x7f; \
+        SET_QC(); \
+    } else if (x < 0) { \
+        x = -x; \
+    }} while (0)
+uint32_t HELPER(neon_qabs_s8)(CPUState *env, uint32_t x)
+{
+    neon_s8 vec;
+    NEON_UNPACK(neon_s8, vec, x);
+    DO_QABS8(vec.v1);
+    DO_QABS8(vec.v2);
+    DO_QABS8(vec.v3);
+    DO_QABS8(vec.v4);
+    NEON_PACK(neon_s8, x, vec);
+    return x;
+}
+#undef DO_QABS8
+
+#define DO_QNEG8(x) do { \
+    if (x == (int8_t)0x80) { \
+        x = 0x7f; \
+        SET_QC(); \
+    } else { \
+        x = -x; \
+    }} while (0)
+uint32_t HELPER(neon_qneg_s8)(CPUState *env, uint32_t x)
+{
+    neon_s8 vec;
+    NEON_UNPACK(neon_s8, vec, x);
+    DO_QNEG8(vec.v1);
+    DO_QNEG8(vec.v2);
+    DO_QNEG8(vec.v3);
+    DO_QNEG8(vec.v4);
+    NEON_PACK(neon_s8, x, vec);
+    return x;
+}
+#undef DO_QNEG8
+
+#define DO_QABS16(x) do { \
+    if (x == (int16_t)0x8000) { \
+        x = 0x7fff; \
+        SET_QC(); \
+    } else if (x < 0) { \
+        x = -x; \
+    }} while (0)
+uint32_t HELPER(neon_qabs_s16)(CPUState *env, uint32_t x)
+{
+    neon_s16 vec;
+    NEON_UNPACK(neon_s16, vec, x);
+    DO_QABS16(vec.v1);
+    DO_QABS16(vec.v2);
+    NEON_PACK(neon_s16, x, vec);
+    return x;
+}
+#undef DO_QABS16
+
+#define DO_QNEG16(x) do { \
+    if (x == (int16_t)0x8000) { \
+        x = 0x7fff; \
+        SET_QC(); \
+    } else { \
+        x = -x; \
+    }} while (0)
+uint32_t HELPER(neon_qneg_s16)(CPUState *env, uint32_t x)
+{
+    neon_s16 vec;
+    NEON_UNPACK(neon_s16, vec, x);
+    DO_QNEG16(vec.v1);
+    DO_QNEG16(vec.v2);
+    NEON_PACK(neon_s16, x, vec);
+    return x;
+}
+#undef DO_QNEG16
+
+uint32_t HELPER(neon_qabs_s32)(CPUState *env, uint32_t x)
+{
+    if (x == SIGNBIT) {
+        SET_QC();
+        x = ~SIGNBIT;
+    } else if ((int32_t)x < 0) {
+        x = -x;
+    }
+    return x;
+}
+
+uint32_t HELPER(neon_qneg_s32)(CPUState *env, uint32_t x)
+{
+    if (x == SIGNBIT) {
+        SET_QC();
+        x = ~SIGNBIT;
+    } else {
+        x = -x;
+    }
+    return x;
+}
+
+/* NEON Float helpers.  */
+uint32_t HELPER(neon_min_f32)(uint32_t a, uint32_t b)
+{
+    float32 f0 = vfp_itos(a);
+    float32 f1 = vfp_itos(b);
+    return (float32_compare_quiet(f0, f1, NFS) == -1) ? a : b;
+}
+
+uint32_t HELPER(neon_max_f32)(uint32_t a, uint32_t b)
+{
+    float32 f0 = vfp_itos(a);
+    float32 f1 = vfp_itos(b);
+    return (float32_compare_quiet(f0, f1, NFS) == 1) ? a : b;
+}
+
+uint32_t HELPER(neon_abd_f32)(uint32_t a, uint32_t b)
+{
+    float32 f0 = vfp_itos(a);
+    float32 f1 = vfp_itos(b);
+    return vfp_stoi((float32_compare_quiet(f0, f1, NFS) == 1)
+                    ? float32_sub(f0, f1, NFS)
+                    : float32_sub(f1, f0, NFS));
+}
+
+uint32_t HELPER(neon_add_f32)(uint32_t a, uint32_t b)
+{
+    return vfp_stoi(float32_add(vfp_itos(a), vfp_itos(b), NFS));
+}
+
+uint32_t HELPER(neon_sub_f32)(uint32_t a, uint32_t b)
+{
+    return vfp_stoi(float32_sub(vfp_itos(a), vfp_itos(b), NFS));
+}
+
+uint32_t HELPER(neon_mul_f32)(uint32_t a, uint32_t b)
+{
+    return vfp_stoi(float32_mul(vfp_itos(a), vfp_itos(b), NFS));
+}
+
+/* Floating point comparisons produce an integer result.  */
+#define NEON_VOP_FCMP(name, cmp) \
+uint32_t HELPER(neon_##name)(uint32_t a, uint32_t b) \
+{ \
+    if (float32_compare_quiet(vfp_itos(a), vfp_itos(b), NFS) cmp 0) \
+        return ~0; \
+    else \
+        return 0; \
+}
+
+NEON_VOP_FCMP(ceq_f32, ==)
+NEON_VOP_FCMP(cge_f32, >=)
+NEON_VOP_FCMP(cgt_f32, >)
+
+uint32_t HELPER(neon_acge_f32)(uint32_t a, uint32_t b)
+{
+    float32 f0 = float32_abs(vfp_itos(a));
+    float32 f1 = float32_abs(vfp_itos(b));
+    return (float32_compare_quiet(f0, f1,NFS) >= 0) ? ~0 : 0;
+}
+
+uint32_t HELPER(neon_acgt_f32)(uint32_t a, uint32_t b)
+{
+    float32 f0 = float32_abs(vfp_itos(a));
+    float32 f1 = float32_abs(vfp_itos(b));
+    return (float32_compare_quiet(f0, f1, NFS) > 0) ? ~0 : 0;
+}
diff --git a/target-arm/op_addsub.h b/target-arm/op_addsub.h
new file mode 100644
index 0000000..376ee27
--- /dev/null
+++ b/target-arm/op_addsub.h
@@ -0,0 +1,103 @@
+/*
+ * ARMv6 integer SIMD operations.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#ifdef ARITH_GE
+#define GE_ARG , uint32_t *gep
+#define DECLARE_GE uint32_t ge = 0
+#define SET_GE *gep = ge
+#else
+#define GE_ARG
+#define DECLARE_GE do{}while(0)
+#define SET_GE do{}while(0)
+#endif
+
+#define RESULT(val, n, width) \
+    res |= ((uint32_t)(glue(glue(uint,width),_t))(val)) << (n * width)
+
+uint32_t HELPER(glue(PFX,add16))(uint32_t a, uint32_t b GE_ARG)
+{
+    uint32_t res = 0;
+    DECLARE_GE;
+
+    ADD16(a, b, 0);
+    ADD16(a >> 16, b >> 16, 1);
+    SET_GE;
+    return res;
+}
+
+uint32_t HELPER(glue(PFX,add8))(uint32_t a, uint32_t b GE_ARG)
+{
+    uint32_t res = 0;
+    DECLARE_GE;
+
+    ADD8(a, b, 0);
+    ADD8(a >> 8, b >> 8, 1);
+    ADD8(a >> 16, b >> 16, 2);
+    ADD8(a >> 24, b >> 24, 3);
+    SET_GE;
+    return res;
+}
+
+uint32_t HELPER(glue(PFX,sub16))(uint32_t a, uint32_t b GE_ARG)
+{
+    uint32_t res = 0;
+    DECLARE_GE;
+
+    SUB16(a, b, 0);
+    SUB16(a >> 16, b >> 16, 1);
+    SET_GE;
+    return res;
+}
+
+uint32_t HELPER(glue(PFX,sub8))(uint32_t a, uint32_t b GE_ARG)
+{
+    uint32_t res = 0;
+    DECLARE_GE;
+
+    SUB8(a, b, 0);
+    SUB8(a >> 8, b >> 8, 1);
+    SUB8(a >> 16, b >> 16, 2);
+    SUB8(a >> 24, b >> 24, 3);
+    SET_GE;
+    return res;
+}
+
+uint32_t HELPER(glue(PFX,subaddx))(uint32_t a, uint32_t b GE_ARG)
+{
+    uint32_t res = 0;
+    DECLARE_GE;
+
+    ADD16(a, b, 0);
+    SUB16(a >> 16, b >> 16, 1);
+    SET_GE;
+    return res;
+}
+
+uint32_t HELPER(glue(PFX,addsubx))(uint32_t a, uint32_t b GE_ARG)
+{
+    uint32_t res = 0;
+    DECLARE_GE;
+
+    SUB16(a, b, 0);
+    ADD16(a >> 16, b >> 16, 1);
+    SET_GE;
+    return res;
+}
+
+#undef GE_ARG
+#undef DECLARE_GE
+#undef SET_GE
+#undef RESULT
+
+#undef ARITH_GE
+#undef PFX
+#undef ADD16
+#undef SUB16
+#undef ADD8
+#undef SUB8
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c
new file mode 100644
index 0000000..36de55b
--- /dev/null
+++ b/target-arm/op_helper.c
@@ -0,0 +1,688 @@
+/*
+ *  ARM helper routines
+ *
+ *  Copyright (c) 2005-2007 CodeSourcery, LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "exec.h"
+#include "helpers.h"
+
+#define SIGNBIT (uint32_t)0x80000000
+#define SIGNBIT64 ((uint64_t)1 << 63)
+
+void raise_exception(int tt)
+{
+    env->exception_index = tt;
+    cpu_loop_exit();
+}
+
+/* thread support */
+
+spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED;
+
+void cpu_lock(void)
+{
+    spin_lock(&global_cpu_lock);
+}
+
+void cpu_unlock(void)
+{
+    spin_unlock(&global_cpu_lock);
+}
+
+uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
+                          uint32_t rn, uint32_t maxindex)
+{
+    uint32_t val;
+    uint32_t tmp;
+    int index;
+    int shift;
+    uint64_t *table;
+    table = (uint64_t *)&env->vfp.regs[rn];
+    val = 0;
+    for (shift = 0; shift < 32; shift += 8) {
+        index = (ireg >> shift) & 0xff;
+        if (index < maxindex) {
+            tmp = (table[index >> 3] >> (index & 7)) & 0xff;
+            val |= tmp << shift;
+        } else {
+            val |= def & (0xff << shift);
+        }
+    }
+    return val;
+}
+
+#if !defined(CONFIG_USER_ONLY)
+
+static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr);
+
+#define MMUSUFFIX _mmu
+#define ALIGNED_ONLY  1
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+static void do_unaligned_access (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+    //printf("::UNALIGNED:: addr=%lx is_write=%d is_user=%d retaddr=%p\n", addr, is_write, is_user, retaddr);
+    if (mmu_idx)
+    {
+        env = cpu_single_env;
+        env->cp15.c5_data = 0x00000001;  /* corresponds to an alignment fault */
+        env->cp15.c6_data = addr;
+        env->exception_index = EXCP_DATA_ABORT;
+        cpu_loop_exit();
+    }
+}
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+   NULL, it means that the function was called in C code (i.e. not
+   from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
+{
+    TranslationBlock *tb;
+    CPUState *saved_env;
+    unsigned long pc;
+    int ret;
+
+    /* XXX: hack to restore env in all cases, even if not called from
+       generated code */
+    saved_env = env;
+    env = cpu_single_env;
+    ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
+    if (unlikely(ret)) {
+        if (retaddr) {
+            /* now we have a real cpu fault */
+            pc = (unsigned long)retaddr;
+            tb = tb_find_pc(pc);
+            if (tb) {
+                /* the PC is inside the translated code. It means that we have
+                   a virtual CPU fault */
+                cpu_restore_state(tb, env, pc, NULL);
+            }
+        }
+        raise_exception(env->exception_index);
+    }
+    env = saved_env;
+}
+
+#if 1
+#include <string.h>
+/*
+ * The following functions are address translation helper functions 
+ * for fast memory access in QEMU. 
+ */
+static target_phys_addr_t v2p_mmu(target_ulong addr, int mmu_idx)
+{
+    int index;
+    target_ulong tlb_addr;
+    target_phys_addr_t physaddr;
+    void *retaddr;
+
+    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+redo:
+    tlb_addr = env->tlb_table[mmu_idx][index].addr_read;
+    if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
+        physaddr = addr + env->tlb_table[mmu_idx][index].addend;
+    } else {
+        /* the page is not in the TLB : fill it */
+        retaddr = GETPC();
+        tlb_fill(addr, 0, mmu_idx, retaddr);
+        goto redo;
+    }
+    return physaddr;
+}
+
+/* 
+ * translation from virtual address of simulated OS 
+ * to the address of simulation host (not the physical 
+ * address of simulated OS.
+ */
+target_phys_addr_t v2p(target_ulong ptr, int mmu_idx)
+{
+    CPUState *saved_env;
+    int index;
+    target_ulong addr;
+    target_phys_addr_t physaddr;
+
+    saved_env = env;
+    env = cpu_single_env;
+    addr = ptr;
+    index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
+    if (__builtin_expect(env->tlb_table[mmu_idx][index].addr_read != 
+                (addr & TARGET_PAGE_MASK), 0)) 
+    {
+        physaddr = v2p_mmu(addr, mmu_idx);
+    } else {
+        physaddr = (target_phys_addr_t)addr + env->tlb_table[mmu_idx][index].addend;
+    }
+    env = saved_env;
+    return physaddr;
+}
+
+#define MINSIZE(x,y)    ((x) < (y) ? (x) : (y))
+/* copy memory from the simulated virtual space to a buffer in QEMU */
+void vmemcpy(target_ulong ptr, char *buf, int size)
+{
+    if (buf == NULL) return;
+    while (size) {
+        int page_remain = TARGET_PAGE_SIZE - (ptr & ~TARGET_PAGE_MASK);
+        int to_copy = MINSIZE(size, page_remain);
+        char *phys = (char *)v2p(ptr, 0);
+        if (phys == NULL) return;
+        memcpy(buf, phys, to_copy);
+        ptr += to_copy;
+        buf += to_copy;
+        size -= to_copy;
+    }
+}
+
+/* copy memory from the QEMU buffer to simulated virtual space */
+void pmemcpy(target_ulong ptr, const char *buf, int size)
+{
+    if (buf == NULL) return;
+    while (size) {
+        int page_remain = TARGET_PAGE_SIZE - (ptr & ~TARGET_PAGE_MASK);
+        int to_copy = MINSIZE(size, page_remain);
+        char *phys = (char *)v2p(ptr, 0);
+        if (phys == NULL) return;
+        memcpy(phys, buf, to_copy);
+        ptr += to_copy;
+        buf += to_copy;
+        size -= to_copy;
+    }
+}
+
+/* copy a string from the simulated virtual space to a buffer in QEMU */
+void vstrcpy(target_ulong ptr, char *buf, int max)
+{
+    char *phys = 0;
+    unsigned long page = 0;
+
+    if (buf == NULL) return;
+
+    while (max) {
+        if ((ptr & TARGET_PAGE_MASK) != page) {
+            phys = (char *)v2p(ptr, 0);
+            page = ptr & TARGET_PAGE_MASK;
+        }
+        *buf = *phys;
+        if (*phys == '\0')
+            return;
+        ptr ++;
+        buf ++;
+        phys ++;
+        max --;
+    }
+}
+#endif
+#endif
+
+/* FIXME: Pass an axplicit pointer to QF to CPUState, and move saturating
+   instructions into helper.c  */
+uint32_t HELPER(add_setq)(uint32_t a, uint32_t b)
+{
+    uint32_t res = a + b;
+    if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT))
+        env->QF = 1;
+    return res;
+}
+
+uint32_t HELPER(add_saturate)(uint32_t a, uint32_t b)
+{
+    uint32_t res = a + b;
+    if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
+        env->QF = 1;
+        res = ~(((int32_t)a >> 31) ^ SIGNBIT);
+    }
+    return res;
+}
+
+uint32_t HELPER(sub_saturate)(uint32_t a, uint32_t b)
+{
+    uint32_t res = a - b;
+    if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
+        env->QF = 1;
+        res = ~(((int32_t)a >> 31) ^ SIGNBIT);
+    }
+    return res;
+}
+
+uint32_t HELPER(double_saturate)(int32_t val)
+{
+    uint32_t res;
+    if (val >= 0x40000000) {
+        res = ~SIGNBIT;
+        env->QF = 1;
+    } else if (val <= (int32_t)0xc0000000) {
+        res = SIGNBIT;
+        env->QF = 1;
+    } else {
+        res = val << 1;
+    }
+    return res;
+}
+
+uint32_t HELPER(add_usaturate)(uint32_t a, uint32_t b)
+{
+    uint32_t res = a + b;
+    if (res < a) {
+        env->QF = 1;
+        res = ~0;
+    }
+    return res;
+}
+
+uint32_t HELPER(sub_usaturate)(uint32_t a, uint32_t b)
+{
+    uint32_t res = a - b;
+    if (res > a) {
+        env->QF = 1;
+        res = 0;
+    }
+    return res;
+}
+
+/* Signed saturation.  */
+static inline uint32_t do_ssat(int32_t val, int shift)
+{
+    int32_t top;
+    uint32_t mask;
+
+    top = val >> shift;
+    mask = (1u << shift) - 1;
+    if (top > 0) {
+        env->QF = 1;
+        return mask;
+    } else if (top < -1) {
+        env->QF = 1;
+        return ~mask;
+    }
+    return val;
+}
+
+/* Unsigned saturation.  */
+static inline uint32_t do_usat(int32_t val, int shift)
+{
+    uint32_t max;
+
+    max = (1u << shift) - 1;
+    if (val < 0) {
+        env->QF = 1;
+        return 0;
+    } else if (val > max) {
+        env->QF = 1;
+        return max;
+    }
+    return val;
+}
+
+/* Signed saturate.  */
+uint32_t HELPER(ssat)(uint32_t x, uint32_t shift)
+{
+    return do_ssat(x, shift);
+}
+
+/* Dual halfword signed saturate.  */
+uint32_t HELPER(ssat16)(uint32_t x, uint32_t shift)
+{
+    uint32_t res;
+
+    res = (uint16_t)do_ssat((int16_t)x, shift);
+    res |= do_ssat(((int32_t)x) >> 16, shift) << 16;
+    return res;
+}
+
+/* Unsigned saturate.  */
+uint32_t HELPER(usat)(uint32_t x, uint32_t shift)
+{
+    return do_usat(x, shift);
+}
+
+/* Dual halfword unsigned saturate.  */
+uint32_t HELPER(usat16)(uint32_t x, uint32_t shift)
+{
+    uint32_t res;
+
+    res = (uint16_t)do_usat((int16_t)x, shift);
+    res |= do_usat(((int32_t)x) >> 16, shift) << 16;
+    return res;
+}
+
+void HELPER(wfi)(void)
+{
+    env->exception_index = EXCP_HLT;
+    env->halted = 1;
+    cpu_loop_exit();
+}
+
+void HELPER(exception)(uint32_t excp)
+{
+    env->exception_index = excp;
+    cpu_loop_exit();
+}
+
+uint32_t HELPER(cpsr_read)(void)
+{
+    return cpsr_read(env) & ~CPSR_EXEC;
+}
+
+void HELPER(cpsr_write)(uint32_t val, uint32_t mask)
+{
+    cpsr_write(env, val, mask);
+}
+
+/* Access to user mode registers from privileged modes.  */
+uint32_t HELPER(get_user_reg)(uint32_t regno)
+{
+    uint32_t val;
+
+    if (regno == 13) {
+        val = env->banked_r13[0];
+    } else if (regno == 14) {
+        val = env->banked_r14[0];
+    } else if (regno >= 8
+               && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+        val = env->usr_regs[regno - 8];
+    } else {
+        val = env->regs[regno];
+    }
+    return val;
+}
+
+void HELPER(set_user_reg)(uint32_t regno, uint32_t val)
+{
+    if (regno == 13) {
+        env->banked_r13[0] = val;
+    } else if (regno == 14) {
+        env->banked_r14[0] = val;
+    } else if (regno >= 8
+               && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
+        env->usr_regs[regno - 8] = val;
+    } else {
+        env->regs[regno] = val;
+    }
+}
+
+/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
+   The only way to do that in TCG is a conditional branch, which clobbers
+   all our temporaries.  For now implement these as helper functions.  */
+
+uint32_t HELPER (add_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    result = T0 + T1;
+    env->NF = env->ZF = result;
+    env->CF = result < a;
+    env->VF = (a ^ b ^ -1) & (a ^ result);
+    return result;
+}
+
+uint32_t HELPER(adc_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    if (!env->CF) {
+        result = a + b;
+        env->CF = result < a;
+    } else {
+        result = a + b + 1;
+        env->CF = result <= a;
+    }
+    env->VF = (a ^ b ^ -1) & (a ^ result);
+    env->NF = env->ZF = result;
+    return result;
+}
+
+uint32_t HELPER(sub_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    result = a - b;
+    env->NF = env->ZF = result;
+    env->CF = a >= b;
+    env->VF = (a ^ b) & (a ^ result);
+    return result;
+}
+
+uint32_t HELPER(sbc_cc)(uint32_t a, uint32_t b)
+{
+    uint32_t result;
+    if (!env->CF) {
+        result = a - b - 1;
+        env->CF = a > b;
+    } else {
+        result = a - b;
+        env->CF = a >= b;
+    }
+    env->VF = (a ^ b) & (a ^ result);
+    env->NF = env->ZF = result;
+    return result;
+}
+
+/* Similarly for variable shift instructions.  */
+
+uint32_t HELPER(shl)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32)
+        return 0;
+    return x << shift;
+}
+
+uint32_t HELPER(shr)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32)
+        return 0;
+    return (uint32_t)x >> shift;
+}
+
+uint32_t HELPER(sar)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32)
+        shift = 31;
+    return (int32_t)x >> shift;
+}
+
+uint32_t HELPER(ror)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift == 0)
+        return x;
+    return (x >> shift) | (x << (32 - shift));
+}
+
+uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32) {
+        if (shift == 32)
+            env->CF = x & 1;
+        else
+            env->CF = 0;
+        return 0;
+    } else if (shift != 0) {
+        env->CF = (x >> (32 - shift)) & 1;
+        return x << shift;
+    }
+    return x;
+}
+
+uint32_t HELPER(shr_cc)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32) {
+        if (shift == 32)
+            env->CF = (x >> 31) & 1;
+        else
+            env->CF = 0;
+        return 0;
+    } else if (shift != 0) {
+        env->CF = (x >> (shift - 1)) & 1;
+        return x >> shift;
+    }
+    return x;
+}
+
+uint32_t HELPER(sar_cc)(uint32_t x, uint32_t i)
+{
+    int shift = i & 0xff;
+    if (shift >= 32) {
+        env->CF = (x >> 31) & 1;
+        return (int32_t)x >> 31;
+    } else if (shift != 0) {
+        env->CF = (x >> (shift - 1)) & 1;
+        return (int32_t)x >> shift;
+    }
+    return x;
+}
+
+uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
+{
+    int shift1, shift;
+    shift1 = i & 0xff;
+    shift = shift1 & 0x1f;
+    if (shift == 0) {
+        if (shift1 != 0)
+            env->CF = (x >> 31) & 1;
+        return x;
+    } else {
+        env->CF = (x >> (shift - 1)) & 1;
+        return ((uint32_t)x >> shift) | (x << (32 - shift));
+    }
+}
+
+uint64_t HELPER(neon_add_saturate_s64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 + src2;
+    if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) {
+        env->QF = 1;
+        res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_add_saturate_u64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 + src2;
+    if (res < src1) {
+        env->QF = 1;
+        res = ~(uint64_t)0;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_sub_saturate_s64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    res = src1 - src2;
+    if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) {
+        env->QF = 1;
+        res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64;
+    }
+    return res;
+}
+
+uint64_t HELPER(neon_sub_saturate_u64)(uint64_t src1, uint64_t src2)
+{
+    uint64_t res;
+
+    if (src1 < src2) {
+        env->QF = 1;
+        res = 0;
+    } else {
+        res = src1 - src2;
+    }
+    return res;
+}
+
+/* These need to return a pair of value, so still use T0/T1.  */
+/* Transpose.  Argument order is rather strange to avoid special casing
+   the tranlation code.
+   On input T0 = rm, T1 = rd.  On output T0 = rd, T1 = rm  */
+void HELPER(neon_trn_u8)(void)
+{
+    uint32_t rd;
+    uint32_t rm;
+    rd = ((T0 & 0x00ff00ff) << 8) | (T1 & 0x00ff00ff);
+    rm = ((T1 & 0xff00ff00) >> 8) | (T0 & 0xff00ff00);
+    T0 = rd;
+    T1 = rm;
+    FORCE_RET();
+}
+
+void HELPER(neon_trn_u16)(void)
+{
+    uint32_t rd;
+    uint32_t rm;
+    rd = (T0 << 16) | (T1 & 0xffff);
+    rm = (T1 >> 16) | (T0 & 0xffff0000);
+    T0 = rd;
+    T1 = rm;
+    FORCE_RET();
+}
+
+/* Worker routines for zip and unzip.  */
+void HELPER(neon_unzip_u8)(void)
+{
+    uint32_t rd;
+    uint32_t rm;
+    rd = (T0 & 0xff) | ((T0 >> 8) & 0xff00)
+         | ((T1 << 16) & 0xff0000) | ((T1 << 8) & 0xff000000);
+    rm = ((T0 >> 8) & 0xff) | ((T0 >> 16) & 0xff00)
+         | ((T1 << 8) & 0xff0000) | (T1 & 0xff000000);
+    T0 = rd;
+    T1 = rm;
+    FORCE_RET();
+}
+
+void HELPER(neon_zip_u8)(void)
+{
+    uint32_t rd;
+    uint32_t rm;
+    rd = (T0 & 0xff) | ((T1 << 8) & 0xff00)
+         | ((T0 << 16) & 0xff0000) | ((T1 << 24) & 0xff000000);
+    rm = ((T0 >> 16) & 0xff) | ((T1 >> 8) & 0xff00)
+         | ((T0 >> 8) & 0xff0000) | (T1 & 0xff000000);
+    T0 = rd;
+    T1 = rm;
+    FORCE_RET();
+}
+
+void HELPER(neon_zip_u16)(void)
+{
+    uint32_t tmp;
+
+    tmp = (T0 & 0xffff) | (T1 << 16);
+    T1 = (T1 & 0xffff0000) | (T0 >> 16);
+    T0 = tmp;
+    FORCE_RET();
+}
diff --git a/target-arm/translate.c b/target-arm/translate.c
new file mode 100644
index 0000000..ff27d28
--- /dev/null
+++ b/target-arm/translate.c
@@ -0,0 +1,8963 @@
+/*
+ *  ARM translation
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *  Copyright (c) 2005-2007 CodeSourcery
+ *  Copyright (c) 2007 OpenedHand, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg-op.h"
+#include "qemu-log.h"
+
+#ifdef CONFIG_TRACE
+#include "trace.h"
+#endif
+
+#define GEN_HELPER 1
+#include "helpers.h"
+
+#define ENABLE_ARCH_5J    0
+#define ENABLE_ARCH_6     arm_feature(env, ARM_FEATURE_V6)
+#define ENABLE_ARCH_6K   arm_feature(env, ARM_FEATURE_V6K)
+#define ENABLE_ARCH_6T2   arm_feature(env, ARM_FEATURE_THUMB2)
+#define ENABLE_ARCH_7     arm_feature(env, ARM_FEATURE_V7)
+
+#define ARCH(x) if (!ENABLE_ARCH_##x) goto illegal_op;
+
+/* internal defines */
+typedef struct DisasContext {
+    target_ulong pc;
+    int is_jmp;
+    /* Nonzero if this instruction has been conditionally skipped.  */
+    int condjmp;
+    /* The label that will be jumped to when the instruction is skipped.  */
+    int condlabel;
+    /* Thumb-2 condtional execution bits.  */
+    int condexec_mask;
+    int condexec_cond;
+    struct TranslationBlock *tb;
+    int singlestep_enabled;
+    int thumb;
+    int is_mem;
+#if !defined(CONFIG_USER_ONLY)
+    int user;
+#endif
+} DisasContext;
+
+#if defined(CONFIG_USER_ONLY)
+#define IS_USER(s) 1
+#else
+#define IS_USER(s) (s->user)
+#endif
+
+#ifdef CONFIG_TRACE
+#include "helpers.h"
+#endif
+
+/* These instructions trap after executing, so defer them until after the
+   conditional executions state has been updated.  */
+#define DISAS_WFI 4
+#define DISAS_SWI 5
+
+static TCGv cpu_env;
+/* We reuse the same 64-bit temporaries for efficiency.  */
+static TCGv cpu_V0, cpu_V1, cpu_M0;
+
+/* FIXME:  These should be removed.  */
+static TCGv cpu_T[2];
+static TCGv cpu_F0s, cpu_F1s, cpu_F0d, cpu_F1d;
+
+#define ICOUNT_TEMP cpu_T[0]
+#include "gen-icount.h"
+
+/* initialize TCG globals.  */
+void arm_translate_init(void)
+{
+    cpu_env = tcg_global_reg_new(TCG_TYPE_PTR, TCG_AREG0, "env");
+
+    cpu_T[0] = tcg_global_reg_new(TCG_TYPE_I32, TCG_AREG1, "T0");
+    cpu_T[1] = tcg_global_reg_new(TCG_TYPE_I32, TCG_AREG2, "T1");
+}
+
+/* The code generator doesn't like lots of temporaries, so maintain our own
+   cache for reuse within a function.  */
+#define MAX_TEMPS 8
+static int num_temps;
+static TCGv temps[MAX_TEMPS];
+
+/* Allocate a temporary variable.  */
+static TCGv new_tmp(void)
+{
+    TCGv tmp;
+    if (num_temps == MAX_TEMPS)
+        abort();
+
+    if (GET_TCGV(temps[num_temps]))
+      return temps[num_temps++];
+
+    tmp = tcg_temp_new(TCG_TYPE_I32);
+    temps[num_temps++] = tmp;
+    return tmp;
+}
+
+/* Release a temporary variable.  */
+static void dead_tmp(TCGv tmp)
+{
+    int i;
+    num_temps--;
+    i = num_temps;
+    if (GET_TCGV(temps[i]) == GET_TCGV(tmp))
+        return;
+
+    /* Shuffle this temp to the last slot.  */
+    while (GET_TCGV(temps[i]) != GET_TCGV(tmp))
+        i--;
+    while (i < num_temps) {
+        temps[i] = temps[i + 1];
+        i++;
+    }
+    temps[i] = tmp;
+}
+
+static inline TCGv load_cpu_offset(int offset)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_ld_i32(tmp, cpu_env, offset);
+    return tmp;
+}
+
+#define load_cpu_field(name) load_cpu_offset(offsetof(CPUState, name))
+
+static inline void store_cpu_offset(TCGv var, int offset)
+{
+    tcg_gen_st_i32(var, cpu_env, offset);
+    dead_tmp(var);
+}
+
+#define store_cpu_field(var, name) \
+    store_cpu_offset(var, offsetof(CPUState, name))
+
+/* Set a variable to the value of a CPU register.  */
+static void load_reg_var(DisasContext *s, TCGv var, int reg)
+{
+    if (reg == 15) {
+        uint32_t addr;
+        /* normaly, since we updated PC, we need only to add one insn */
+        if (s->thumb)
+            addr = (long)s->pc + 2;
+        else
+            addr = (long)s->pc + 4;
+        tcg_gen_movi_i32(var, addr);
+    } else {
+        tcg_gen_ld_i32(var, cpu_env, offsetof(CPUState, regs[reg]));
+    }
+}
+
+/* Create a new temporary and set it to the value of a CPU register.  */
+static inline TCGv load_reg(DisasContext *s, int reg)
+{
+    TCGv tmp = new_tmp();
+    load_reg_var(s, tmp, reg);
+    return tmp;
+}
+
+/* Set a CPU register.  The source must be a temporary and will be
+   marked as dead.  */
+static void store_reg(DisasContext *s, int reg, TCGv var)
+{
+    if (reg == 15) {
+        tcg_gen_andi_i32(var, var, ~1);
+        s->is_jmp = DISAS_JUMP;
+    }
+    tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, regs[reg]));
+    dead_tmp(var);
+}
+
+
+/* Basic operations.  */
+#define gen_op_movl_T0_T1() tcg_gen_mov_i32(cpu_T[0], cpu_T[1])
+#define gen_op_movl_T1_T0() tcg_gen_mov_i32(cpu_T[1], cpu_T[0])
+#define gen_op_movl_T0_im(im) tcg_gen_movi_i32(cpu_T[0], im)
+#define gen_op_movl_T1_im(im) tcg_gen_movi_i32(cpu_T[1], im)
+
+#define gen_op_addl_T1_im(im) tcg_gen_addi_i32(cpu_T[1], cpu_T[1], im)
+#define gen_op_addl_T0_T1() tcg_gen_add_i32(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_subl_T0_T1() tcg_gen_sub_i32(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_rsbl_T0_T1() tcg_gen_sub_i32(cpu_T[0], cpu_T[1], cpu_T[0])
+
+#define gen_op_addl_T0_T1_cc() gen_helper_add_cc(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_adcl_T0_T1_cc() gen_helper_adc_cc(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_subl_T0_T1_cc() gen_helper_sub_cc(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_sbcl_T0_T1_cc() gen_helper_sbc_cc(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_rsbl_T0_T1_cc() gen_helper_sub_cc(cpu_T[0], cpu_T[1], cpu_T[0])
+#define gen_op_rscl_T0_T1_cc() gen_helper_sbc_cc(cpu_T[0], cpu_T[1], cpu_T[0])
+
+#define gen_op_andl_T0_T1() tcg_gen_and_i32(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_xorl_T0_T1() tcg_gen_xor_i32(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_orl_T0_T1() tcg_gen_or_i32(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_op_notl_T0() tcg_gen_not_i32(cpu_T[0], cpu_T[0])
+#define gen_op_notl_T1() tcg_gen_not_i32(cpu_T[1], cpu_T[1])
+#define gen_op_logic_T0_cc() gen_logic_CC(cpu_T[0]);
+#define gen_op_logic_T1_cc() gen_logic_CC(cpu_T[1]);
+
+#define gen_op_shll_T0_im(im) tcg_gen_shli_i32(cpu_T[0], cpu_T[0], im)
+#define gen_op_shll_T1_im(im) tcg_gen_shli_i32(cpu_T[1], cpu_T[1], im)
+#define gen_op_shrl_T1_im(im) tcg_gen_shri_i32(cpu_T[1], cpu_T[1], im)
+#define gen_op_sarl_T1_im(im) tcg_gen_sari_i32(cpu_T[1], cpu_T[1], im)
+#define gen_op_rorl_T1_im(im) tcg_gen_rori_i32(cpu_T[1], cpu_T[1], im)
+
+/* Value extensions.  */
+#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var)
+#define gen_uxth(var) tcg_gen_ext16u_i32(var, var)
+#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var)
+#define gen_sxth(var) tcg_gen_ext16s_i32(var, var)
+
+#define gen_sxtb16(var) gen_helper_sxtb16(var, var)
+#define gen_uxtb16(var) gen_helper_uxtb16(var, var)
+
+#define gen_op_mul_T0_T1() tcg_gen_mul_i32(cpu_T[0], cpu_T[0], cpu_T[1])
+
+#define gen_set_cpsr(var, mask) gen_helper_cpsr_write(var, tcg_const_i32(mask))
+/* Set NZCV flags from the high 4 bits of var.  */
+#define gen_set_nzcv(var) gen_set_cpsr(var, CPSR_NZCV)
+
+#ifdef CONFIG_TRACE
+static void  gen_traceTicks(int  count)
+{
+    TCGv  t0 = new_tmp();
+    tcg_gen_movi_i32(t0, count);
+    gen_helper_traceTicks(t0);
+    dead_tmp(t0);
+}
+
+static void gen_traceBB(uint64_t  bb_num, target_phys_addr_t  tb)
+{
+#if HOST_LONG_BITS ==64
+    TCGv  t0 = tcg_const_i64(bb_num);
+    TCGv  t1 = tcg_const_i64(tb);
+    gen_helper_traceBB64(t0, t1);
+    tcg_temp_free(t1);
+    tcg_temp_free(t0);
+#else
+    TCGv  t0 = new_tmp();
+    TCGv  t1 = new_tmp();
+    TCGv  t2 = new_tmp();
+    tcg_gen_movi_i32(t0, (int32_t)(bb_num >> 32));
+    tcg_gen_movi_i32(t1, (int32_t)(bb_num));
+    tcg_gen_movi_i32(t2, (int32_t)tb);
+    gen_helper_traceBB32(t0, t1, t2);
+    dead_tmp(t2);
+    dead_tmp(t1);
+    dead_tmp(t0);
+#endif
+}
+#endif /* CONFIG_TRACE */
+
+static void gen_exception(int excp)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_movi_i32(tmp, excp);
+    gen_helper_exception(tmp);
+    dead_tmp(tmp);
+}
+
+static void gen_smul_dual(TCGv a, TCGv b)
+{
+    TCGv tmp1 = new_tmp();
+    TCGv tmp2 = new_tmp();
+    tcg_gen_ext16s_i32(tmp1, a);
+    tcg_gen_ext16s_i32(tmp2, b);
+    tcg_gen_mul_i32(tmp1, tmp1, tmp2);
+    dead_tmp(tmp2);
+    tcg_gen_sari_i32(a, a, 16);
+    tcg_gen_sari_i32(b, b, 16);
+    tcg_gen_mul_i32(b, b, a);
+    tcg_gen_mov_i32(a, tmp1);
+    dead_tmp(tmp1);
+}
+
+/* Byteswap each halfword.  */
+static void gen_rev16(TCGv var)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_shri_i32(tmp, var, 8);
+    tcg_gen_andi_i32(tmp, tmp, 0x00ff00ff);
+    tcg_gen_shli_i32(var, var, 8);
+    tcg_gen_andi_i32(var, var, 0xff00ff00);
+    tcg_gen_or_i32(var, var, tmp);
+    dead_tmp(tmp);
+}
+
+/* Byteswap low halfword and sign extend.  */
+static void gen_revsh(TCGv var)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_shri_i32(tmp, var, 8);
+    tcg_gen_andi_i32(tmp, tmp, 0x00ff);
+    tcg_gen_shli_i32(var, var, 8);
+    tcg_gen_ext8s_i32(var, var);
+    tcg_gen_or_i32(var, var, tmp);
+    dead_tmp(tmp);
+}
+
+/* Unsigned bitfield extract.  */
+static void gen_ubfx(TCGv var, int shift, uint32_t mask)
+{
+    if (shift)
+        tcg_gen_shri_i32(var, var, shift);
+    tcg_gen_andi_i32(var, var, mask);
+}
+
+/* Signed bitfield extract.  */
+static void gen_sbfx(TCGv var, int shift, int width)
+{
+    uint32_t signbit;
+
+    if (shift)
+        tcg_gen_sari_i32(var, var, shift);
+    if (shift + width < 32) {
+        signbit = 1u << (width - 1);
+        tcg_gen_andi_i32(var, var, (1u << width) - 1);
+        tcg_gen_xori_i32(var, var, signbit);
+        tcg_gen_subi_i32(var, var, signbit);
+    }
+}
+
+/* Bitfield insertion.  Insert val into base.  Clobbers base and val.  */
+static void gen_bfi(TCGv dest, TCGv base, TCGv val, int shift, uint32_t mask)
+{
+    tcg_gen_andi_i32(val, val, mask);
+    tcg_gen_shli_i32(val, val, shift);
+    tcg_gen_andi_i32(base, base, ~(mask << shift));
+    tcg_gen_or_i32(dest, base, val);
+}
+
+/* Round the top 32 bits of a 64-bit value.  */
+static void gen_roundqd(TCGv a, TCGv b)
+{
+    tcg_gen_shri_i32(a, a, 31);
+    tcg_gen_add_i32(a, a, b);
+}
+
+/* FIXME: Most targets have native widening multiplication.
+   It would be good to use that instead of a full wide multiply.  */
+/* 32x32->64 multiply.  Marks inputs as dead.  */
+static TCGv gen_mulu_i64_i32(TCGv a, TCGv b)
+{
+    TCGv tmp1 = tcg_temp_new(TCG_TYPE_I64);
+    TCGv tmp2 = tcg_temp_new(TCG_TYPE_I64);
+
+    tcg_gen_extu_i32_i64(tmp1, a);
+    dead_tmp(a);
+    tcg_gen_extu_i32_i64(tmp2, b);
+    dead_tmp(b);
+    tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+    return tmp1;
+}
+
+static TCGv gen_muls_i64_i32(TCGv a, TCGv b)
+{
+    TCGv tmp1 = tcg_temp_new(TCG_TYPE_I64);
+    TCGv tmp2 = tcg_temp_new(TCG_TYPE_I64);
+
+    tcg_gen_ext_i32_i64(tmp1, a);
+    dead_tmp(a);
+    tcg_gen_ext_i32_i64(tmp2, b);
+    dead_tmp(b);
+    tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+    return tmp1;
+}
+
+/* Unsigned 32x32->64 multiply.  */
+static void gen_op_mull_T0_T1(void)
+{
+    TCGv tmp1 = tcg_temp_new(TCG_TYPE_I64);
+    TCGv tmp2 = tcg_temp_new(TCG_TYPE_I64);
+
+    tcg_gen_extu_i32_i64(tmp1, cpu_T[0]);
+    tcg_gen_extu_i32_i64(tmp2, cpu_T[1]);
+    tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+    tcg_gen_trunc_i64_i32(cpu_T[0], tmp1);
+    tcg_gen_shri_i64(tmp1, tmp1, 32);
+    tcg_gen_trunc_i64_i32(cpu_T[1], tmp1);
+}
+
+/* Signed 32x32->64 multiply.  */
+static void gen_imull(TCGv a, TCGv b)
+{
+    TCGv tmp1 = tcg_temp_new(TCG_TYPE_I64);
+    TCGv tmp2 = tcg_temp_new(TCG_TYPE_I64);
+
+    tcg_gen_ext_i32_i64(tmp1, a);
+    tcg_gen_ext_i32_i64(tmp2, b);
+    tcg_gen_mul_i64(tmp1, tmp1, tmp2);
+    tcg_gen_trunc_i64_i32(a, tmp1);
+    tcg_gen_shri_i64(tmp1, tmp1, 32);
+    tcg_gen_trunc_i64_i32(b, tmp1);
+}
+#define gen_op_imull_T0_T1() gen_imull(cpu_T[0], cpu_T[1])
+
+/* Swap low and high halfwords.  */
+static void gen_swap_half(TCGv var)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_shri_i32(tmp, var, 16);
+    tcg_gen_shli_i32(var, var, 16);
+    tcg_gen_or_i32(var, var, tmp);
+    dead_tmp(tmp);
+}
+
+/* Dual 16-bit add.  Result placed in t0 and t1 is marked as dead.
+    tmp = (t0 ^ t1) & 0x8000;
+    t0 &= ~0x8000;
+    t1 &= ~0x8000;
+    t0 = (t0 + t1) ^ tmp;
+ */
+
+static void gen_add16(TCGv t0, TCGv t1)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_xor_i32(tmp, t0, t1);
+    tcg_gen_andi_i32(tmp, tmp, 0x8000);
+    tcg_gen_andi_i32(t0, t0, ~0x8000);
+    tcg_gen_andi_i32(t1, t1, ~0x8000);
+    tcg_gen_add_i32(t0, t0, t1);
+    tcg_gen_xor_i32(t0, t0, tmp);
+    dead_tmp(tmp);
+    dead_tmp(t1);
+}
+
+#define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, CF))
+
+/* Set CF to the top bit of var.  */
+static void gen_set_CF_bit31(TCGv var)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_shri_i32(tmp, var, 31);
+    gen_set_CF(var);
+    dead_tmp(tmp);
+}
+
+/* Set N and Z flags from var.  */
+static inline void gen_logic_CC(TCGv var)
+{
+    tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, NF));
+    tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, ZF));
+}
+
+/* T0 += T1 + CF.  */
+static void gen_adc_T0_T1(void)
+{
+    TCGv tmp;
+    gen_op_addl_T0_T1();
+    tmp = load_cpu_field(CF);
+    tcg_gen_add_i32(cpu_T[0], cpu_T[0], tmp);
+    dead_tmp(tmp);
+}
+
+/* dest = T0 - T1 + CF - 1.  */
+static void gen_sub_carry(TCGv dest, TCGv t0, TCGv t1)
+{
+    TCGv tmp;
+    tcg_gen_sub_i32(dest, t0, t1);
+    tmp = load_cpu_field(CF);
+    tcg_gen_add_i32(dest, dest, tmp);
+    tcg_gen_subi_i32(dest, dest, 1);
+    dead_tmp(tmp);
+}
+
+#define gen_sbc_T0_T1() gen_sub_carry(cpu_T[0], cpu_T[0], cpu_T[1])
+#define gen_rsc_T0_T1() gen_sub_carry(cpu_T[0], cpu_T[1], cpu_T[0])
+
+/* T0 &= ~T1.  Clobbers T1.  */
+/* FIXME: Implement bic natively.  */
+static inline void tcg_gen_bic_i32(TCGv dest, TCGv t0, TCGv t1)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_not_i32(tmp, t1);
+    tcg_gen_and_i32(dest, t0, tmp);
+    dead_tmp(tmp);
+}
+static inline void gen_op_bicl_T0_T1(void)
+{
+    gen_op_notl_T1();
+    gen_op_andl_T0_T1();
+}
+
+/* FIXME:  Implement this natively.  */
+#define tcg_gen_abs_i32(t0, t1) gen_helper_abs(t0, t1)
+
+/* FIXME:  Implement this natively.  */
+static void tcg_gen_rori_i32(TCGv t0, TCGv t1, int i)
+{
+    TCGv tmp;
+
+    if (i == 0)
+        return;
+
+    tmp = new_tmp();
+    tcg_gen_shri_i32(tmp, t1, i);
+    tcg_gen_shli_i32(t1, t1, 32 - i);
+    tcg_gen_or_i32(t0, t1, tmp);
+    dead_tmp(tmp);
+}
+
+static void shifter_out_im(TCGv var, int shift)
+{
+    TCGv tmp = new_tmp();
+    if (shift == 0) {
+        tcg_gen_andi_i32(tmp, var, 1);
+    } else {
+        tcg_gen_shri_i32(tmp, var, shift);
+        if (shift != 31);
+            tcg_gen_andi_i32(tmp, tmp, 1);
+    }
+    gen_set_CF(tmp);
+    dead_tmp(tmp);
+}
+
+/* Shift by immediate.  Includes special handling for shift == 0.  */
+static inline void gen_arm_shift_im(TCGv var, int shiftop, int shift, int flags)
+{
+    switch (shiftop) {
+    case 0: /* LSL */
+        if (shift != 0) {
+            if (flags)
+                shifter_out_im(var, 32 - shift);
+            tcg_gen_shli_i32(var, var, shift);
+        }
+        break;
+    case 1: /* LSR */
+        if (shift == 0) {
+            if (flags) {
+                tcg_gen_shri_i32(var, var, 31);
+                gen_set_CF(var);
+            }
+            tcg_gen_movi_i32(var, 0);
+        } else {
+            if (flags)
+                shifter_out_im(var, shift - 1);
+            tcg_gen_shri_i32(var, var, shift);
+        }
+        break;
+    case 2: /* ASR */
+        if (shift == 0)
+            shift = 32;
+        if (flags)
+            shifter_out_im(var, shift - 1);
+        if (shift == 32)
+          shift = 31;
+        tcg_gen_sari_i32(var, var, shift);
+        break;
+    case 3: /* ROR/RRX */
+        if (shift != 0) {
+            if (flags)
+                shifter_out_im(var, shift - 1);
+            tcg_gen_rori_i32(var, var, shift); break;
+        } else {
+            TCGv tmp = load_cpu_field(CF);
+            if (flags)
+                shifter_out_im(var, 0);
+            tcg_gen_shri_i32(var, var, 1);
+            tcg_gen_shli_i32(tmp, tmp, 31);
+            tcg_gen_or_i32(var, var, tmp);
+            dead_tmp(tmp);
+        }
+    }
+};
+
+static inline void gen_arm_shift_reg(TCGv var, int shiftop,
+                                     TCGv shift, int flags)
+{
+    if (flags) {
+        switch (shiftop) {
+        case 0: gen_helper_shl_cc(var, var, shift); break;
+        case 1: gen_helper_shr_cc(var, var, shift); break;
+        case 2: gen_helper_sar_cc(var, var, shift); break;
+        case 3: gen_helper_ror_cc(var, var, shift); break;
+        }
+    } else {
+        switch (shiftop) {
+        case 0: gen_helper_shl(var, var, shift); break;
+        case 1: gen_helper_shr(var, var, shift); break;
+        case 2: gen_helper_sar(var, var, shift); break;
+        case 3: gen_helper_ror(var, var, shift); break;
+        }
+    }
+    dead_tmp(shift);
+}
+
+#define PAS_OP(pfx) \
+    switch (op2) {  \
+    case 0: gen_pas_helper(glue(pfx,add16)); break; \
+    case 1: gen_pas_helper(glue(pfx,addsubx)); break; \
+    case 2: gen_pas_helper(glue(pfx,subaddx)); break; \
+    case 3: gen_pas_helper(glue(pfx,sub16)); break; \
+    case 4: gen_pas_helper(glue(pfx,add8)); break; \
+    case 7: gen_pas_helper(glue(pfx,sub8)); break; \
+    }
+static void gen_arm_parallel_addsub(int op1, int op2, TCGv a, TCGv b)
+{
+    TCGv tmp;
+
+    switch (op1) {
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp)
+    case 1:
+        tmp = tcg_temp_new(TCG_TYPE_PTR);
+        tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+        PAS_OP(s)
+        break;
+    case 5:
+        tmp = tcg_temp_new(TCG_TYPE_PTR);
+        tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+        PAS_OP(u)
+        break;
+#undef gen_pas_helper
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b)
+    case 2:
+        PAS_OP(q);
+        break;
+    case 3:
+        PAS_OP(sh);
+        break;
+    case 6:
+        PAS_OP(uq);
+        break;
+    case 7:
+        PAS_OP(uh);
+        break;
+#undef gen_pas_helper
+    }
+}
+#undef PAS_OP
+
+/* For unknown reasons Arm and Thumb-2 use arbitrarily different encodings.  */
+#define PAS_OP(pfx) \
+    switch (op2) {  \
+    case 0: gen_pas_helper(glue(pfx,add8)); break; \
+    case 1: gen_pas_helper(glue(pfx,add16)); break; \
+    case 2: gen_pas_helper(glue(pfx,addsubx)); break; \
+    case 4: gen_pas_helper(glue(pfx,sub8)); break; \
+    case 5: gen_pas_helper(glue(pfx,sub16)); break; \
+    case 6: gen_pas_helper(glue(pfx,subaddx)); break; \
+    }
+static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv a, TCGv b)
+{
+    TCGv tmp;
+
+    switch (op1) {
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp)
+    case 0:
+        tmp = tcg_temp_new(TCG_TYPE_PTR);
+        tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+        PAS_OP(s)
+        break;
+    case 4:
+        tmp = tcg_temp_new(TCG_TYPE_PTR);
+        tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
+        PAS_OP(u)
+        break;
+#undef gen_pas_helper
+#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b)
+    case 1:
+        PAS_OP(q);
+        break;
+    case 2:
+        PAS_OP(sh);
+        break;
+    case 5:
+        PAS_OP(uq);
+        break;
+    case 6:
+        PAS_OP(uh);
+        break;
+#undef gen_pas_helper
+    }
+}
+#undef PAS_OP
+
+static void gen_test_cc(int cc, int label)
+{
+    TCGv tmp;
+    TCGv tmp2;
+    int inv;
+
+    switch (cc) {
+    case 0: /* eq: Z */
+        tmp = load_cpu_field(ZF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+        break;
+    case 1: /* ne: !Z */
+        tmp = load_cpu_field(ZF);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
+        break;
+    case 2: /* cs: C */
+        tmp = load_cpu_field(CF);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
+        break;
+    case 3: /* cc: !C */
+        tmp = load_cpu_field(CF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+        break;
+    case 4: /* mi: N */
+        tmp = load_cpu_field(NF);
+        tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+        break;
+    case 5: /* pl: !N */
+        tmp = load_cpu_field(NF);
+        tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+        break;
+    case 6: /* vs: V */
+        tmp = load_cpu_field(VF);
+        tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+        break;
+    case 7: /* vc: !V */
+        tmp = load_cpu_field(VF);
+        tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+        break;
+    case 8: /* hi: C && !Z */
+        inv = gen_new_label();
+        tmp = load_cpu_field(CF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv);
+        dead_tmp(tmp);
+        tmp = load_cpu_field(ZF);
+        tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
+        gen_set_label(inv);
+        break;
+    case 9: /* ls: !C || Z */
+        tmp = load_cpu_field(CF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+        dead_tmp(tmp);
+        tmp = load_cpu_field(ZF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+        break;
+    case 10: /* ge: N == V -> N ^ V == 0 */
+        tmp = load_cpu_field(VF);
+        tmp2 = load_cpu_field(NF);
+        tcg_gen_xor_i32(tmp, tmp, tmp2);
+        dead_tmp(tmp2);
+        tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+        break;
+    case 11: /* lt: N != V -> N ^ V != 0 */
+        tmp = load_cpu_field(VF);
+        tmp2 = load_cpu_field(NF);
+        tcg_gen_xor_i32(tmp, tmp, tmp2);
+        dead_tmp(tmp2);
+        tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+        break;
+    case 12: /* gt: !Z && N == V */
+        inv = gen_new_label();
+        tmp = load_cpu_field(ZF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv);
+        dead_tmp(tmp);
+        tmp = load_cpu_field(VF);
+        tmp2 = load_cpu_field(NF);
+        tcg_gen_xor_i32(tmp, tmp, tmp2);
+        dead_tmp(tmp2);
+        tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
+        gen_set_label(inv);
+        break;
+    case 13: /* le: Z || N != V */
+        tmp = load_cpu_field(ZF);
+        tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
+        dead_tmp(tmp);
+        tmp = load_cpu_field(VF);
+        tmp2 = load_cpu_field(NF);
+        tcg_gen_xor_i32(tmp, tmp, tmp2);
+        dead_tmp(tmp2);
+        tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
+        break;
+    default:
+        fprintf(stderr, "Bad condition code 0x%x\n", cc);
+        abort();
+    }
+    dead_tmp(tmp);
+}
+
+const uint8_t table_logic_cc[16] = {
+    1, /* and */
+    1, /* xor */
+    0, /* sub */
+    0, /* rsb */
+    0, /* add */
+    0, /* adc */
+    0, /* sbc */
+    0, /* rsc */
+    1, /* andl */
+    1, /* xorl */
+    0, /* cmp */
+    0, /* cmn */
+    1, /* orr */
+    1, /* mov */
+    1, /* bic */
+    1, /* mvn */
+};
+
+/* Set PC and Thumb state from an immediate address.  */
+static inline void gen_bx_im(DisasContext *s, uint32_t addr)
+{
+    TCGv tmp;
+
+    s->is_jmp = DISAS_UPDATE;
+    tmp = new_tmp();
+    if (s->thumb != (addr & 1)) {
+        tcg_gen_movi_i32(tmp, addr & 1);
+        tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, thumb));
+    }
+    tcg_gen_movi_i32(tmp, addr & ~1);
+    tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, regs[15]));
+    dead_tmp(tmp);
+}
+
+/* Set PC and Thumb state from var.  var is marked as dead.  */
+static inline void gen_bx(DisasContext *s, TCGv var)
+{
+    TCGv tmp;
+
+    s->is_jmp = DISAS_UPDATE;
+    tmp = new_tmp();
+    tcg_gen_andi_i32(tmp, var, 1);
+    store_cpu_field(tmp, thumb);
+    tcg_gen_andi_i32(var, var, ~1);
+    store_cpu_field(var, regs[15]);
+}
+
+/* TODO: This should be removed.  Use gen_bx instead.  */
+static inline void gen_bx_T0(DisasContext *s)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_mov_i32(tmp, cpu_T[0]);
+    gen_bx(s, tmp);
+}
+
+#if defined(CONFIG_USER_ONLY)
+#define gen_ldst(name, s) gen_op_##name##_raw()
+#else
+#define gen_ldst(name, s) do { \
+    s->is_mem = 1; \
+    if (IS_USER(s)) \
+        gen_op_##name##_user(); \
+    else \
+        gen_op_##name##_kernel(); \
+    } while (0)
+#endif
+static inline TCGv gen_ld8s(TCGv addr, int index)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_qemu_ld8s(tmp, addr, index);
+    return tmp;
+}
+static inline TCGv gen_ld8u(TCGv addr, int index)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_qemu_ld8u(tmp, addr, index);
+    return tmp;
+}
+static inline TCGv gen_ld16s(TCGv addr, int index)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_qemu_ld16s(tmp, addr, index);
+    return tmp;
+}
+static inline TCGv gen_ld16u(TCGv addr, int index)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_qemu_ld16u(tmp, addr, index);
+    return tmp;
+}
+static inline TCGv gen_ld32(TCGv addr, int index)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_qemu_ld32u(tmp, addr, index);
+    return tmp;
+}
+static inline void gen_st8(TCGv val, TCGv addr, int index)
+{
+    tcg_gen_qemu_st8(val, addr, index);
+    dead_tmp(val);
+}
+static inline void gen_st16(TCGv val, TCGv addr, int index)
+{
+    tcg_gen_qemu_st16(val, addr, index);
+    dead_tmp(val);
+}
+static inline void gen_st32(TCGv val, TCGv addr, int index)
+{
+    tcg_gen_qemu_st32(val, addr, index);
+    dead_tmp(val);
+}
+
+static inline void gen_movl_T0_reg(DisasContext *s, int reg)
+{
+    load_reg_var(s, cpu_T[0], reg);
+}
+
+static inline void gen_movl_T1_reg(DisasContext *s, int reg)
+{
+    load_reg_var(s, cpu_T[1], reg);
+}
+
+static inline void gen_movl_T2_reg(DisasContext *s, int reg)
+{
+    load_reg_var(s, cpu_T[2], reg);
+}
+
+static inline void gen_set_pc_im(uint32_t val)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_movi_i32(tmp, val);
+    store_cpu_field(tmp, regs[15]);
+}
+
+static inline void gen_movl_reg_TN(DisasContext *s, int reg, int t)
+{
+    TCGv tmp;
+    if (reg == 15) {
+        tmp = new_tmp();
+        tcg_gen_andi_i32(tmp, cpu_T[t], ~1);
+    } else {
+        tmp = cpu_T[t];
+    }
+    tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, regs[reg]));
+    if (reg == 15) {
+        dead_tmp(tmp);
+        s->is_jmp = DISAS_JUMP;
+    }
+}
+
+static inline void gen_movl_reg_T0(DisasContext *s, int reg)
+{
+    gen_movl_reg_TN(s, reg, 0);
+}
+
+static inline void gen_movl_reg_T1(DisasContext *s, int reg)
+{
+    gen_movl_reg_TN(s, reg, 1);
+}
+
+/* Force a TB lookup after an instruction that changes the CPU state.  */
+static inline void gen_lookup_tb(DisasContext *s)
+{
+    gen_op_movl_T0_im(s->pc);
+    gen_movl_reg_T0(s, 15);
+    s->is_jmp = DISAS_UPDATE;
+}
+
+static inline void gen_add_data_offset(DisasContext *s, unsigned int insn,
+                                       TCGv var)
+{
+    int val, rm, shift, shiftop;
+    TCGv offset;
+
+    if (!(insn & (1 << 25))) {
+        /* immediate */
+        val = insn & 0xfff;
+        if (!(insn & (1 << 23)))
+            val = -val;
+        if (val != 0)
+            tcg_gen_addi_i32(var, var, val);
+    } else {
+        /* shift/register */
+        rm = (insn) & 0xf;
+        shift = (insn >> 7) & 0x1f;
+        shiftop = (insn >> 5) & 3;
+        offset = load_reg(s, rm);
+        gen_arm_shift_im(offset, shiftop, shift, 0);
+        if (!(insn & (1 << 23)))
+            tcg_gen_sub_i32(var, var, offset);
+        else
+            tcg_gen_add_i32(var, var, offset);
+        dead_tmp(offset);
+    }
+}
+
+static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn,
+                                        int extra, TCGv var)
+{
+    int val, rm;
+    TCGv offset;
+
+    if (insn & (1 << 22)) {
+        /* immediate */
+        val = (insn & 0xf) | ((insn >> 4) & 0xf0);
+        if (!(insn & (1 << 23)))
+            val = -val;
+        val += extra;
+        if (val != 0)
+            tcg_gen_addi_i32(var, var, val);
+    } else {
+        /* register */
+        if (extra)
+            tcg_gen_addi_i32(var, var, extra);
+        rm = (insn) & 0xf;
+        offset = load_reg(s, rm);
+        if (!(insn & (1 << 23)))
+            tcg_gen_sub_i32(var, var, offset);
+        else
+            tcg_gen_add_i32(var, var, offset);
+        dead_tmp(offset);
+    }
+}
+
+#define VFP_OP2(name)                                                 \
+static inline void gen_vfp_##name(int dp)                             \
+{                                                                     \
+    if (dp)                                                           \
+        gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \
+    else                                                              \
+        gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \
+}
+
+#define VFP_OP1(name)                               \
+static inline void gen_vfp_##name(int dp, int arg)  \
+{                                                   \
+    if (dp)                                         \
+        gen_op_vfp_##name##d(arg);                  \
+    else                                            \
+        gen_op_vfp_##name##s(arg);                  \
+}
+
+VFP_OP2(add)
+VFP_OP2(sub)
+VFP_OP2(mul)
+VFP_OP2(div)
+
+#undef VFP_OP2
+
+static inline void gen_vfp_abs(int dp)
+{
+    if (dp)
+        gen_helper_vfp_absd(cpu_F0d, cpu_F0d);
+    else
+        gen_helper_vfp_abss(cpu_F0s, cpu_F0s);
+}
+
+static inline void gen_vfp_neg(int dp)
+{
+    if (dp)
+        gen_helper_vfp_negd(cpu_F0d, cpu_F0d);
+    else
+        gen_helper_vfp_negs(cpu_F0s, cpu_F0s);
+}
+
+static inline void gen_vfp_sqrt(int dp)
+{
+    if (dp)
+        gen_helper_vfp_sqrtd(cpu_F0d, cpu_F0d, cpu_env);
+    else
+        gen_helper_vfp_sqrts(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_cmp(int dp)
+{
+    if (dp)
+        gen_helper_vfp_cmpd(cpu_F0d, cpu_F1d, cpu_env);
+    else
+        gen_helper_vfp_cmps(cpu_F0s, cpu_F1s, cpu_env);
+}
+
+static inline void gen_vfp_cmpe(int dp)
+{
+    if (dp)
+        gen_helper_vfp_cmped(cpu_F0d, cpu_F1d, cpu_env);
+    else
+        gen_helper_vfp_cmpes(cpu_F0s, cpu_F1s, cpu_env);
+}
+
+static inline void gen_vfp_F1_ld0(int dp)
+{
+    if (dp)
+        tcg_gen_movi_i64(cpu_F1d, 0);
+    else
+        tcg_gen_movi_i32(cpu_F1s, 0);
+}
+
+static inline void gen_vfp_uito(int dp)
+{
+    if (dp)
+        gen_helper_vfp_uitod(cpu_F0d, cpu_F0s, cpu_env);
+    else
+        gen_helper_vfp_uitos(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_sito(int dp)
+{
+    if (dp)
+        gen_helper_vfp_sitod(cpu_F0d, cpu_F0s, cpu_env);
+    else
+        gen_helper_vfp_sitos(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_toui(int dp)
+{
+    if (dp)
+        gen_helper_vfp_touid(cpu_F0s, cpu_F0d, cpu_env);
+    else
+        gen_helper_vfp_touis(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_touiz(int dp)
+{
+    if (dp)
+        gen_helper_vfp_touizd(cpu_F0s, cpu_F0d, cpu_env);
+    else
+        gen_helper_vfp_touizs(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_tosi(int dp)
+{
+    if (dp)
+        gen_helper_vfp_tosid(cpu_F0s, cpu_F0d, cpu_env);
+    else
+        gen_helper_vfp_tosis(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+static inline void gen_vfp_tosiz(int dp)
+{
+    if (dp)
+        gen_helper_vfp_tosizd(cpu_F0s, cpu_F0d, cpu_env);
+    else
+        gen_helper_vfp_tosizs(cpu_F0s, cpu_F0s, cpu_env);
+}
+
+#define VFP_GEN_FIX(name) \
+static inline void gen_vfp_##name(int dp, int shift) \
+{ \
+    if (dp) \
+        gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tcg_const_i32(shift), cpu_env);\
+    else \
+        gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tcg_const_i32(shift), cpu_env);\
+}
+VFP_GEN_FIX(tosh)
+VFP_GEN_FIX(tosl)
+VFP_GEN_FIX(touh)
+VFP_GEN_FIX(toul)
+VFP_GEN_FIX(shto)
+VFP_GEN_FIX(slto)
+VFP_GEN_FIX(uhto)
+VFP_GEN_FIX(ulto)
+#undef VFP_GEN_FIX
+
+static inline void gen_vfp_ld(DisasContext *s, int dp)
+{
+    if (dp)
+        tcg_gen_qemu_ld64(cpu_F0d, cpu_T[1], IS_USER(s));
+    else
+        tcg_gen_qemu_ld32u(cpu_F0s, cpu_T[1], IS_USER(s));
+}
+
+static inline void gen_vfp_st(DisasContext *s, int dp)
+{
+    if (dp)
+        tcg_gen_qemu_st64(cpu_F0d, cpu_T[1], IS_USER(s));
+    else
+        tcg_gen_qemu_st32(cpu_F0s, cpu_T[1], IS_USER(s));
+}
+
+static inline long
+vfp_reg_offset (int dp, int reg)
+{
+    if (dp)
+        return offsetof(CPUARMState, vfp.regs[reg]);
+    else if (reg & 1) {
+        return offsetof(CPUARMState, vfp.regs[reg >> 1])
+          + offsetof(CPU_DoubleU, l.upper);
+    } else {
+        return offsetof(CPUARMState, vfp.regs[reg >> 1])
+          + offsetof(CPU_DoubleU, l.lower);
+    }
+}
+
+/* Return the offset of a 32-bit piece of a NEON register.
+   zero is the least significant end of the register.  */
+static inline long
+neon_reg_offset (int reg, int n)
+{
+    int sreg;
+    sreg = reg * 2 + n;
+    return vfp_reg_offset(0, sreg);
+}
+
+/* FIXME: Remove these.  */
+#define neon_T0 cpu_T[0]
+#define neon_T1 cpu_T[1]
+#define NEON_GET_REG(T, reg, n) \
+  tcg_gen_ld_i32(neon_##T, cpu_env, neon_reg_offset(reg, n))
+#define NEON_SET_REG(T, reg, n) \
+  tcg_gen_st_i32(neon_##T, cpu_env, neon_reg_offset(reg, n))
+
+static TCGv neon_load_reg(int reg, int pass)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_ld_i32(tmp, cpu_env, neon_reg_offset(reg, pass));
+    return tmp;
+}
+
+static void neon_store_reg(int reg, int pass, TCGv var)
+{
+    tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass));
+    dead_tmp(var);
+}
+
+static inline void neon_load_reg64(TCGv var, int reg)
+{
+    tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg));
+}
+
+static inline void neon_store_reg64(TCGv var, int reg)
+{
+    tcg_gen_st_i64(var, cpu_env, vfp_reg_offset(1, reg));
+}
+
+#define tcg_gen_ld_f32 tcg_gen_ld_i32
+#define tcg_gen_ld_f64 tcg_gen_ld_i64
+#define tcg_gen_st_f32 tcg_gen_st_i32
+#define tcg_gen_st_f64 tcg_gen_st_i64
+
+static inline void gen_mov_F0_vreg(int dp, int reg)
+{
+    if (dp)
+        tcg_gen_ld_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg));
+    else
+        tcg_gen_ld_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg));
+}
+
+static inline void gen_mov_F1_vreg(int dp, int reg)
+{
+    if (dp)
+        tcg_gen_ld_f64(cpu_F1d, cpu_env, vfp_reg_offset(dp, reg));
+    else
+        tcg_gen_ld_f32(cpu_F1s, cpu_env, vfp_reg_offset(dp, reg));
+}
+
+static inline void gen_mov_vreg_F0(int dp, int reg)
+{
+    if (dp)
+        tcg_gen_st_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg));
+    else
+        tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg));
+}
+
+#define ARM_CP_RW_BIT	(1 << 20)
+
+static inline void iwmmxt_load_reg(TCGv var, int reg)
+{
+    tcg_gen_ld_i64(var, cpu_env, offsetof(CPUState, iwmmxt.regs[reg]));
+}
+
+static inline void iwmmxt_store_reg(TCGv var, int reg)
+{
+    tcg_gen_st_i64(var, cpu_env, offsetof(CPUState, iwmmxt.regs[reg]));
+}
+
+static inline void gen_op_iwmmxt_movl_wCx_T0(int reg)
+{
+    tcg_gen_st_i32(cpu_T[0], cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
+}
+
+static inline void gen_op_iwmmxt_movl_T0_wCx(int reg)
+{
+    tcg_gen_ld_i32(cpu_T[0], cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
+}
+
+static inline void gen_op_iwmmxt_movl_T1_wCx(int reg)
+{
+    tcg_gen_ld_i32(cpu_T[1], cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
+}
+
+static inline void gen_op_iwmmxt_movq_wRn_M0(int rn)
+{
+    iwmmxt_store_reg(cpu_M0, rn);
+}
+
+static inline void gen_op_iwmmxt_movq_M0_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_M0, rn);
+}
+
+static inline void gen_op_iwmmxt_orq_M0_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_V1, rn);
+    tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+static inline void gen_op_iwmmxt_andq_M0_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_V1, rn);
+    tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_V1, rn);
+    tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+#define IWMMXT_OP(name) \
+static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
+{ \
+    iwmmxt_load_reg(cpu_V1, rn); \
+    gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \
+}
+
+#define IWMMXT_OP_ENV(name) \
+static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
+{ \
+    iwmmxt_load_reg(cpu_V1, rn); \
+    gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \
+}
+
+#define IWMMXT_OP_ENV_SIZE(name) \
+IWMMXT_OP_ENV(name##b) \
+IWMMXT_OP_ENV(name##w) \
+IWMMXT_OP_ENV(name##l)
+
+#define IWMMXT_OP_ENV1(name) \
+static inline void gen_op_iwmmxt_##name##_M0(void) \
+{ \
+    gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \
+}
+
+IWMMXT_OP(maddsq)
+IWMMXT_OP(madduq)
+IWMMXT_OP(sadb)
+IWMMXT_OP(sadw)
+IWMMXT_OP(mulslw)
+IWMMXT_OP(mulshw)
+IWMMXT_OP(mululw)
+IWMMXT_OP(muluhw)
+IWMMXT_OP(macsw)
+IWMMXT_OP(macuw)
+
+IWMMXT_OP_ENV_SIZE(unpackl)
+IWMMXT_OP_ENV_SIZE(unpackh)
+
+IWMMXT_OP_ENV1(unpacklub)
+IWMMXT_OP_ENV1(unpackluw)
+IWMMXT_OP_ENV1(unpacklul)
+IWMMXT_OP_ENV1(unpackhub)
+IWMMXT_OP_ENV1(unpackhuw)
+IWMMXT_OP_ENV1(unpackhul)
+IWMMXT_OP_ENV1(unpacklsb)
+IWMMXT_OP_ENV1(unpacklsw)
+IWMMXT_OP_ENV1(unpacklsl)
+IWMMXT_OP_ENV1(unpackhsb)
+IWMMXT_OP_ENV1(unpackhsw)
+IWMMXT_OP_ENV1(unpackhsl)
+
+IWMMXT_OP_ENV_SIZE(cmpeq)
+IWMMXT_OP_ENV_SIZE(cmpgtu)
+IWMMXT_OP_ENV_SIZE(cmpgts)
+
+IWMMXT_OP_ENV_SIZE(mins)
+IWMMXT_OP_ENV_SIZE(minu)
+IWMMXT_OP_ENV_SIZE(maxs)
+IWMMXT_OP_ENV_SIZE(maxu)
+
+IWMMXT_OP_ENV_SIZE(subn)
+IWMMXT_OP_ENV_SIZE(addn)
+IWMMXT_OP_ENV_SIZE(subu)
+IWMMXT_OP_ENV_SIZE(addu)
+IWMMXT_OP_ENV_SIZE(subs)
+IWMMXT_OP_ENV_SIZE(adds)
+
+IWMMXT_OP_ENV(avgb0)
+IWMMXT_OP_ENV(avgb1)
+IWMMXT_OP_ENV(avgw0)
+IWMMXT_OP_ENV(avgw1)
+
+IWMMXT_OP(msadb)
+
+IWMMXT_OP_ENV(packuw)
+IWMMXT_OP_ENV(packul)
+IWMMXT_OP_ENV(packuq)
+IWMMXT_OP_ENV(packsw)
+IWMMXT_OP_ENV(packsl)
+IWMMXT_OP_ENV(packsq)
+
+static inline void gen_op_iwmmxt_muladdsl_M0_T0_T1(void)
+{
+    gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1]);
+}
+
+static inline void gen_op_iwmmxt_muladdsw_M0_T0_T1(void)
+{
+    gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1]);
+}
+
+static inline void gen_op_iwmmxt_muladdswl_M0_T0_T1(void)
+{
+    gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1]);
+}
+
+static inline void gen_op_iwmmxt_align_M0_T0_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_V1, rn);
+    gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, cpu_T[0]);
+}
+
+static inline void gen_op_iwmmxt_insr_M0_T0_T1(int shift)
+{
+    TCGv tmp = tcg_const_i32(shift);
+    gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1], tmp);
+}
+
+static inline void gen_op_iwmmxt_extrsb_T0_M0(int shift)
+{
+    tcg_gen_shri_i64(cpu_M0, cpu_M0, shift);
+    tcg_gen_trunc_i64_i32(cpu_T[0], cpu_M0);
+    tcg_gen_ext8s_i32(cpu_T[0], cpu_T[0]);
+}
+
+static inline void gen_op_iwmmxt_extrsw_T0_M0(int shift)
+{
+    tcg_gen_shri_i64(cpu_M0, cpu_M0, shift);
+    tcg_gen_trunc_i64_i32(cpu_T[0], cpu_M0);
+    tcg_gen_ext16s_i32(cpu_T[0], cpu_T[0]);
+}
+
+static inline void gen_op_iwmmxt_extru_T0_M0(int shift, uint32_t mask)
+{
+    tcg_gen_shri_i64(cpu_M0, cpu_M0, shift);
+    tcg_gen_trunc_i64_i32(cpu_T[0], cpu_M0);
+    if (mask != ~0u)
+        tcg_gen_andi_i32(cpu_T[0], cpu_T[0], mask);
+}
+
+static void gen_op_iwmmxt_set_mup(void)
+{
+    TCGv tmp;
+    tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]);
+    tcg_gen_ori_i32(tmp, tmp, 2);
+    store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]);
+}
+
+static void gen_op_iwmmxt_set_cup(void)
+{
+    TCGv tmp;
+    tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]);
+    tcg_gen_ori_i32(tmp, tmp, 1);
+    store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]);
+}
+
+static void gen_op_iwmmxt_setpsr_nz(void)
+{
+    TCGv tmp = new_tmp();
+    gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0);
+    store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]);
+}
+
+static inline void gen_op_iwmmxt_addl_M0_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_V1, rn);
+    tcg_gen_ext32u_i64(cpu_V1, cpu_V1);
+    tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1);
+}
+
+
+static void gen_iwmmxt_movl_T0_T1_wRn(int rn)
+{
+    iwmmxt_load_reg(cpu_V0, rn);
+    tcg_gen_trunc_i64_i32(cpu_T[0], cpu_V0);
+    tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+    tcg_gen_trunc_i64_i32(cpu_T[1], cpu_V0);
+}
+
+static void gen_iwmmxt_movl_wRn_T0_T1(int rn)
+{
+    tcg_gen_extu_i32_i64(cpu_V0, cpu_T[0]);
+    tcg_gen_extu_i32_i64(cpu_V1, cpu_T[0]);
+    tcg_gen_shli_i64(cpu_V1, cpu_V1, 32);
+    tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
+    iwmmxt_store_reg(cpu_V0, rn);
+}
+
+static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn)
+{
+    int rd;
+    uint32_t offset;
+
+    rd = (insn >> 16) & 0xf;
+    gen_movl_T1_reg(s, rd);
+
+    offset = (insn & 0xff) << ((insn >> 7) & 2);
+    if (insn & (1 << 24)) {
+        /* Pre indexed */
+        if (insn & (1 << 23))
+            gen_op_addl_T1_im(offset);
+        else
+            gen_op_addl_T1_im(-offset);
+
+        if (insn & (1 << 21))
+            gen_movl_reg_T1(s, rd);
+    } else if (insn & (1 << 21)) {
+        /* Post indexed */
+        if (insn & (1 << 23))
+            gen_op_movl_T0_im(offset);
+        else
+            gen_op_movl_T0_im(- offset);
+        gen_op_addl_T0_T1();
+        gen_movl_reg_T0(s, rd);
+    } else if (!(insn & (1 << 23)))
+        return 1;
+    return 0;
+}
+
+static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask)
+{
+    int rd = (insn >> 0) & 0xf;
+
+    if (insn & (1 << 8))
+        if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3)
+            return 1;
+        else
+            gen_op_iwmmxt_movl_T0_wCx(rd);
+    else
+        gen_iwmmxt_movl_T0_T1_wRn(rd);
+
+    gen_op_movl_T1_im(mask);
+    gen_op_andl_T0_T1();
+    return 0;
+}
+
+/* Disassemble an iwMMXt instruction.  Returns nonzero if an error occured
+   (ie. an undefined instruction).  */
+static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+    int rd, wrd;
+    int rdhi, rdlo, rd0, rd1, i;
+    TCGv tmp;
+
+    if ((insn & 0x0e000e00) == 0x0c000000) {
+        if ((insn & 0x0fe00ff0) == 0x0c400000) {
+            wrd = insn & 0xf;
+            rdlo = (insn >> 12) & 0xf;
+            rdhi = (insn >> 16) & 0xf;
+            if (insn & ARM_CP_RW_BIT) {			/* TMRRC */
+                gen_iwmmxt_movl_T0_T1_wRn(wrd);
+                gen_movl_reg_T0(s, rdlo);
+                gen_movl_reg_T1(s, rdhi);
+            } else {					/* TMCRR */
+                gen_movl_T0_reg(s, rdlo);
+                gen_movl_T1_reg(s, rdhi);
+                gen_iwmmxt_movl_wRn_T0_T1(wrd);
+                gen_op_iwmmxt_set_mup();
+            }
+            return 0;
+        }
+
+        wrd = (insn >> 12) & 0xf;
+        if (gen_iwmmxt_address(s, insn))
+            return 1;
+        if (insn & ARM_CP_RW_BIT) {
+            if ((insn >> 28) == 0xf) {			/* WLDRW wCx */
+                tmp = gen_ld32(cpu_T[1], IS_USER(s));
+                tcg_gen_mov_i32(cpu_T[0], tmp);
+                dead_tmp(tmp);
+                gen_op_iwmmxt_movl_wCx_T0(wrd);
+            } else {
+                i = 1;
+                if (insn & (1 << 8)) {
+                    if (insn & (1 << 22)) {		/* WLDRD */
+                        tcg_gen_qemu_ld64(cpu_M0, cpu_T[1], IS_USER(s));
+                        i = 0;
+                    } else {				/* WLDRW wRd */
+                        tmp = gen_ld32(cpu_T[1], IS_USER(s));
+                    }
+                } else {
+                    if (insn & (1 << 22)) {		/* WLDRH */
+                        tmp = gen_ld16u(cpu_T[1], IS_USER(s));
+                    } else {				/* WLDRB */
+                        tmp = gen_ld8u(cpu_T[1], IS_USER(s));
+                    }
+                }
+                if (i) {
+                    tcg_gen_extu_i32_i64(cpu_M0, tmp);
+                    dead_tmp(tmp);
+                }
+                gen_op_iwmmxt_movq_wRn_M0(wrd);
+            }
+        } else {
+            if ((insn >> 28) == 0xf) {			/* WSTRW wCx */
+                gen_op_iwmmxt_movl_T0_wCx(wrd);
+                tmp = new_tmp();
+                tcg_gen_mov_i32(tmp, cpu_T[0]);
+                gen_st32(tmp, cpu_T[1], IS_USER(s));
+            } else {
+                gen_op_iwmmxt_movq_M0_wRn(wrd);
+                tmp = new_tmp();
+                if (insn & (1 << 8)) {
+                    if (insn & (1 << 22)) {		/* WSTRD */
+                        dead_tmp(tmp);
+                        tcg_gen_qemu_st64(cpu_M0, cpu_T[1], IS_USER(s));
+                    } else {				/* WSTRW wRd */
+                        tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+                        gen_st32(tmp, cpu_T[1], IS_USER(s));
+                    }
+                } else {
+                    if (insn & (1 << 22)) {		/* WSTRH */
+                        tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+                        gen_st16(tmp, cpu_T[1], IS_USER(s));
+                    } else {				/* WSTRB */
+                        tcg_gen_trunc_i64_i32(tmp, cpu_M0);
+                        gen_st8(tmp, cpu_T[1], IS_USER(s));
+                    }
+                }
+            }
+        }
+        return 0;
+    }
+
+    if ((insn & 0x0f000000) != 0x0e000000)
+        return 1;
+
+    switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) {
+    case 0x000:						/* WOR */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 0) & 0xf;
+        rd1 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        gen_op_iwmmxt_orq_M0_wRn(rd1);
+        gen_op_iwmmxt_setpsr_nz();
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x011:						/* TMCR */
+        if (insn & 0xf)
+            return 1;
+        rd = (insn >> 12) & 0xf;
+        wrd = (insn >> 16) & 0xf;
+        switch (wrd) {
+        case ARM_IWMMXT_wCID:
+        case ARM_IWMMXT_wCASF:
+            break;
+        case ARM_IWMMXT_wCon:
+            gen_op_iwmmxt_set_cup();
+            /* Fall through.  */
+        case ARM_IWMMXT_wCSSF:
+            gen_op_iwmmxt_movl_T0_wCx(wrd);
+            gen_movl_T1_reg(s, rd);
+            gen_op_bicl_T0_T1();
+            gen_op_iwmmxt_movl_wCx_T0(wrd);
+            break;
+        case ARM_IWMMXT_wCGR0:
+        case ARM_IWMMXT_wCGR1:
+        case ARM_IWMMXT_wCGR2:
+        case ARM_IWMMXT_wCGR3:
+            gen_op_iwmmxt_set_cup();
+            gen_movl_reg_T0(s, rd);
+            gen_op_iwmmxt_movl_wCx_T0(wrd);
+            break;
+        default:
+            return 1;
+        }
+        break;
+    case 0x100:						/* WXOR */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 0) & 0xf;
+        rd1 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        gen_op_iwmmxt_xorq_M0_wRn(rd1);
+        gen_op_iwmmxt_setpsr_nz();
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x111:						/* TMRC */
+        if (insn & 0xf)
+            return 1;
+        rd = (insn >> 12) & 0xf;
+        wrd = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movl_T0_wCx(wrd);
+        gen_movl_reg_T0(s, rd);
+        break;
+    case 0x300:						/* WANDN */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 0) & 0xf;
+        rd1 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        tcg_gen_neg_i64(cpu_M0, cpu_M0);
+        gen_op_iwmmxt_andq_M0_wRn(rd1);
+        gen_op_iwmmxt_setpsr_nz();
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x200:						/* WAND */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 0) & 0xf;
+        rd1 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        gen_op_iwmmxt_andq_M0_wRn(rd1);
+        gen_op_iwmmxt_setpsr_nz();
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x810: case 0xa10:				/* WMADD */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 0) & 0xf;
+        rd1 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (insn & (1 << 21))
+            gen_op_iwmmxt_maddsq_M0_wRn(rd1);
+        else
+            gen_op_iwmmxt_madduq_M0_wRn(rd1);
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x10e: case 0x50e: case 0x90e: case 0xd0e:	/* WUNPCKIL */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            gen_op_iwmmxt_unpacklb_M0_wRn(rd1);
+            break;
+        case 1:
+            gen_op_iwmmxt_unpacklw_M0_wRn(rd1);
+            break;
+        case 2:
+            gen_op_iwmmxt_unpackll_M0_wRn(rd1);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x10c: case 0x50c: case 0x90c: case 0xd0c:	/* WUNPCKIH */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            gen_op_iwmmxt_unpackhb_M0_wRn(rd1);
+            break;
+        case 1:
+            gen_op_iwmmxt_unpackhw_M0_wRn(rd1);
+            break;
+        case 2:
+            gen_op_iwmmxt_unpackhl_M0_wRn(rd1);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x012: case 0x112: case 0x412: case 0x512:	/* WSAD */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (insn & (1 << 22))
+            gen_op_iwmmxt_sadw_M0_wRn(rd1);
+        else
+            gen_op_iwmmxt_sadb_M0_wRn(rd1);
+        if (!(insn & (1 << 20)))
+            gen_op_iwmmxt_addl_M0_wRn(wrd);
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x010: case 0x110: case 0x210: case 0x310:	/* WMUL */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (insn & (1 << 21)) {
+            if (insn & (1 << 20))
+                gen_op_iwmmxt_mulshw_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_mulslw_M0_wRn(rd1);
+        } else {
+            if (insn & (1 << 20))
+                gen_op_iwmmxt_muluhw_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_mululw_M0_wRn(rd1);
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x410: case 0x510: case 0x610: case 0x710:	/* WMAC */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (insn & (1 << 21))
+            gen_op_iwmmxt_macsw_M0_wRn(rd1);
+        else
+            gen_op_iwmmxt_macuw_M0_wRn(rd1);
+        if (!(insn & (1 << 20))) {
+            iwmmxt_load_reg(cpu_V1, wrd);
+            tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1);
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x006: case 0x406: case 0x806: case 0xc06:	/* WCMPEQ */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            gen_op_iwmmxt_cmpeqb_M0_wRn(rd1);
+            break;
+        case 1:
+            gen_op_iwmmxt_cmpeqw_M0_wRn(rd1);
+            break;
+        case 2:
+            gen_op_iwmmxt_cmpeql_M0_wRn(rd1);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x800: case 0x900: case 0xc00: case 0xd00:	/* WAVG2 */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (insn & (1 << 22)) {
+            if (insn & (1 << 20))
+                gen_op_iwmmxt_avgw1_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_avgw0_M0_wRn(rd1);
+        } else {
+            if (insn & (1 << 20))
+                gen_op_iwmmxt_avgb1_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_avgb0_M0_wRn(rd1);
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x802: case 0x902: case 0xa02: case 0xb02:	/* WALIGNR */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        gen_op_iwmmxt_movl_T0_wCx(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3));
+        gen_op_movl_T1_im(7);
+        gen_op_andl_T0_T1();
+        gen_op_iwmmxt_align_M0_T0_wRn(rd1);
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x601: case 0x605: case 0x609: case 0x60d:	/* TINSR */
+        rd = (insn >> 12) & 0xf;
+        wrd = (insn >> 16) & 0xf;
+        gen_movl_T0_reg(s, rd);
+        gen_op_iwmmxt_movq_M0_wRn(wrd);
+        switch ((insn >> 6) & 3) {
+        case 0:
+            gen_op_movl_T1_im(0xff);
+            gen_op_iwmmxt_insr_M0_T0_T1((insn & 7) << 3);
+            break;
+        case 1:
+            gen_op_movl_T1_im(0xffff);
+            gen_op_iwmmxt_insr_M0_T0_T1((insn & 3) << 4);
+            break;
+        case 2:
+            gen_op_movl_T1_im(0xffffffff);
+            gen_op_iwmmxt_insr_M0_T0_T1((insn & 1) << 5);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x107: case 0x507: case 0x907: case 0xd07:	/* TEXTRM */
+        rd = (insn >> 12) & 0xf;
+        wrd = (insn >> 16) & 0xf;
+        if (rd == 15)
+            return 1;
+        gen_op_iwmmxt_movq_M0_wRn(wrd);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            if (insn & 8)
+                gen_op_iwmmxt_extrsb_T0_M0((insn & 7) << 3);
+            else {
+                gen_op_iwmmxt_extru_T0_M0((insn & 7) << 3, 0xff);
+            }
+            break;
+        case 1:
+            if (insn & 8)
+                gen_op_iwmmxt_extrsw_T0_M0((insn & 3) << 4);
+            else {
+                gen_op_iwmmxt_extru_T0_M0((insn & 3) << 4, 0xffff);
+            }
+            break;
+        case 2:
+            gen_op_iwmmxt_extru_T0_M0((insn & 1) << 5, ~0u);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_movl_reg_T0(s, rd);
+        break;
+    case 0x117: case 0x517: case 0x917: case 0xd17:	/* TEXTRC */
+        if ((insn & 0x000ff008) != 0x0003f000)
+            return 1;
+        gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            gen_op_shrl_T1_im(((insn & 7) << 2) + 0);
+            break;
+        case 1:
+            gen_op_shrl_T1_im(((insn & 3) << 3) + 4);
+            break;
+        case 2:
+            gen_op_shrl_T1_im(((insn & 1) << 4) + 12);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_shll_T1_im(28);
+        gen_set_nzcv(cpu_T[1]);
+        break;
+    case 0x401: case 0x405: case 0x409: case 0x40d:	/* TBCST */
+        rd = (insn >> 12) & 0xf;
+        wrd = (insn >> 16) & 0xf;
+        gen_movl_T0_reg(s, rd);
+        switch ((insn >> 6) & 3) {
+        case 0:
+            gen_helper_iwmmxt_bcstb(cpu_M0, cpu_T[0]);
+            break;
+        case 1:
+            gen_helper_iwmmxt_bcstw(cpu_M0, cpu_T[0]);
+            break;
+        case 2:
+            gen_helper_iwmmxt_bcstl(cpu_M0, cpu_T[0]);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x113: case 0x513: case 0x913: case 0xd13:	/* TANDC */
+        if ((insn & 0x000ff00f) != 0x0003f000)
+            return 1;
+        gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            for (i = 0; i < 7; i ++) {
+                gen_op_shll_T1_im(4);
+                gen_op_andl_T0_T1();
+            }
+            break;
+        case 1:
+            for (i = 0; i < 3; i ++) {
+                gen_op_shll_T1_im(8);
+                gen_op_andl_T0_T1();
+            }
+            break;
+        case 2:
+            gen_op_shll_T1_im(16);
+            gen_op_andl_T0_T1();
+            break;
+        case 3:
+            return 1;
+        }
+        gen_set_nzcv(cpu_T[0]);
+        break;
+    case 0x01c: case 0x41c: case 0x81c: case 0xc1c:	/* WACC */
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0);
+            break;
+        case 1:
+            gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0);
+            break;
+        case 2:
+            gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x115: case 0x515: case 0x915: case 0xd15:	/* TORC */
+        if ((insn & 0x000ff00f) != 0x0003f000)
+            return 1;
+        gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            for (i = 0; i < 7; i ++) {
+                gen_op_shll_T1_im(4);
+                gen_op_orl_T0_T1();
+            }
+            break;
+        case 1:
+            for (i = 0; i < 3; i ++) {
+                gen_op_shll_T1_im(8);
+                gen_op_orl_T0_T1();
+            }
+            break;
+        case 2:
+            gen_op_shll_T1_im(16);
+            gen_op_orl_T0_T1();
+            break;
+        case 3:
+            return 1;
+        }
+        gen_set_nzcv(cpu_T[0]);
+        break;
+    case 0x103: case 0x503: case 0x903: case 0xd03:	/* TMOVMSK */
+        rd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        if ((insn & 0xf) != 0)
+            return 1;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            gen_helper_iwmmxt_msbb(cpu_T[0], cpu_M0);
+            break;
+        case 1:
+            gen_helper_iwmmxt_msbw(cpu_T[0], cpu_M0);
+            break;
+        case 2:
+            gen_helper_iwmmxt_msbl(cpu_T[0], cpu_M0);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_movl_reg_T0(s, rd);
+        break;
+    case 0x106: case 0x306: case 0x506: case 0x706:	/* WCMPGT */
+    case 0x906: case 0xb06: case 0xd06: case 0xf06:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_cmpgtub_M0_wRn(rd1);
+            break;
+        case 1:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1);
+            break;
+        case 2:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_cmpgtul_M0_wRn(rd1);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x00e: case 0x20e: case 0x40e: case 0x60e:	/* WUNPCKEL */
+    case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_unpacklsb_M0();
+            else
+                gen_op_iwmmxt_unpacklub_M0();
+            break;
+        case 1:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_unpacklsw_M0();
+            else
+                gen_op_iwmmxt_unpackluw_M0();
+            break;
+        case 2:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_unpacklsl_M0();
+            else
+                gen_op_iwmmxt_unpacklul_M0();
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x00c: case 0x20c: case 0x40c: case 0x60c:	/* WUNPCKEH */
+    case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_unpackhsb_M0();
+            else
+                gen_op_iwmmxt_unpackhub_M0();
+            break;
+        case 1:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_unpackhsw_M0();
+            else
+                gen_op_iwmmxt_unpackhuw_M0();
+            break;
+        case 2:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_unpackhsl_M0();
+            else
+                gen_op_iwmmxt_unpackhul_M0();
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x204: case 0x604: case 0xa04: case 0xe04:	/* WSRL */
+    case 0x214: case 0x614: case 0xa14: case 0xe14:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (gen_iwmmxt_shift(insn, 0xff))
+            return 1;
+        switch ((insn >> 22) & 3) {
+        case 0:
+            return 1;
+        case 1:
+            gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 2:
+            gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 3:
+            gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x004: case 0x404: case 0x804: case 0xc04:	/* WSRA */
+    case 0x014: case 0x414: case 0x814: case 0xc14:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (gen_iwmmxt_shift(insn, 0xff))
+            return 1;
+        switch ((insn >> 22) & 3) {
+        case 0:
+            return 1;
+        case 1:
+            gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 2:
+            gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 3:
+            gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x104: case 0x504: case 0x904: case 0xd04:	/* WSLL */
+    case 0x114: case 0x514: case 0x914: case 0xd14:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (gen_iwmmxt_shift(insn, 0xff))
+            return 1;
+        switch ((insn >> 22) & 3) {
+        case 0:
+            return 1;
+        case 1:
+            gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 2:
+            gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 3:
+            gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x304: case 0x704: case 0xb04: case 0xf04:	/* WROR */
+    case 0x314: case 0x714: case 0xb14: case 0xf14:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            return 1;
+        case 1:
+            if (gen_iwmmxt_shift(insn, 0xf))
+                return 1;
+            gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 2:
+            if (gen_iwmmxt_shift(insn, 0x1f))
+                return 1;
+            gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        case 3:
+            if (gen_iwmmxt_shift(insn, 0x3f))
+                return 1;
+            gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+            break;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x116: case 0x316: case 0x516: case 0x716:	/* WMIN */
+    case 0x916: case 0xb16: case 0xd16: case 0xf16:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_minsb_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_minub_M0_wRn(rd1);
+            break;
+        case 1:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_minsw_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_minuw_M0_wRn(rd1);
+            break;
+        case 2:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_minsl_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_minul_M0_wRn(rd1);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x016: case 0x216: case 0x416: case 0x616:	/* WMAX */
+    case 0x816: case 0xa16: case 0xc16: case 0xe16:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 22) & 3) {
+        case 0:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_maxsb_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_maxub_M0_wRn(rd1);
+            break;
+        case 1:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_maxsw_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_maxuw_M0_wRn(rd1);
+            break;
+        case 2:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_maxsl_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_maxul_M0_wRn(rd1);
+            break;
+        case 3:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x002: case 0x102: case 0x202: case 0x302:	/* WALIGNI */
+    case 0x402: case 0x502: case 0x602: case 0x702:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        gen_op_movl_T0_im((insn >> 20) & 3);
+        gen_op_iwmmxt_align_M0_T0_wRn(rd1);
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    case 0x01a: case 0x11a: case 0x21a: case 0x31a:	/* WSUB */
+    case 0x41a: case 0x51a: case 0x61a: case 0x71a:
+    case 0x81a: case 0x91a: case 0xa1a: case 0xb1a:
+    case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 20) & 0xf) {
+        case 0x0:
+            gen_op_iwmmxt_subnb_M0_wRn(rd1);
+            break;
+        case 0x1:
+            gen_op_iwmmxt_subub_M0_wRn(rd1);
+            break;
+        case 0x3:
+            gen_op_iwmmxt_subsb_M0_wRn(rd1);
+            break;
+        case 0x4:
+            gen_op_iwmmxt_subnw_M0_wRn(rd1);
+            break;
+        case 0x5:
+            gen_op_iwmmxt_subuw_M0_wRn(rd1);
+            break;
+        case 0x7:
+            gen_op_iwmmxt_subsw_M0_wRn(rd1);
+            break;
+        case 0x8:
+            gen_op_iwmmxt_subnl_M0_wRn(rd1);
+            break;
+        case 0x9:
+            gen_op_iwmmxt_subul_M0_wRn(rd1);
+            break;
+        case 0xb:
+            gen_op_iwmmxt_subsl_M0_wRn(rd1);
+            break;
+        default:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x01e: case 0x11e: case 0x21e: case 0x31e:	/* WSHUFH */
+    case 0x41e: case 0x51e: case 0x61e: case 0x71e:
+    case 0x81e: case 0x91e: case 0xa1e: case 0xb1e:
+    case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        gen_op_movl_T0_im(((insn >> 16) & 0xf0) | (insn & 0x0f));
+        gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x018: case 0x118: case 0x218: case 0x318:	/* WADD */
+    case 0x418: case 0x518: case 0x618: case 0x718:
+    case 0x818: case 0x918: case 0xa18: case 0xb18:
+    case 0xc18: case 0xd18: case 0xe18: case 0xf18:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        switch ((insn >> 20) & 0xf) {
+        case 0x0:
+            gen_op_iwmmxt_addnb_M0_wRn(rd1);
+            break;
+        case 0x1:
+            gen_op_iwmmxt_addub_M0_wRn(rd1);
+            break;
+        case 0x3:
+            gen_op_iwmmxt_addsb_M0_wRn(rd1);
+            break;
+        case 0x4:
+            gen_op_iwmmxt_addnw_M0_wRn(rd1);
+            break;
+        case 0x5:
+            gen_op_iwmmxt_adduw_M0_wRn(rd1);
+            break;
+        case 0x7:
+            gen_op_iwmmxt_addsw_M0_wRn(rd1);
+            break;
+        case 0x8:
+            gen_op_iwmmxt_addnl_M0_wRn(rd1);
+            break;
+        case 0x9:
+            gen_op_iwmmxt_addul_M0_wRn(rd1);
+            break;
+        case 0xb:
+            gen_op_iwmmxt_addsl_M0_wRn(rd1);
+            break;
+        default:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x008: case 0x108: case 0x208: case 0x308:	/* WPACK */
+    case 0x408: case 0x508: case 0x608: case 0x708:
+    case 0x808: case 0x908: case 0xa08: case 0xb08:
+    case 0xc08: case 0xd08: case 0xe08: case 0xf08:
+        wrd = (insn >> 12) & 0xf;
+        rd0 = (insn >> 16) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        gen_op_iwmmxt_movq_M0_wRn(rd0);
+        if (!(insn & (1 << 20)))
+            return 1;
+        switch ((insn >> 22) & 3) {
+        case 0:
+            return 1;
+        case 1:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_packsw_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_packuw_M0_wRn(rd1);
+            break;
+        case 2:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_packsl_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_packul_M0_wRn(rd1);
+            break;
+        case 3:
+            if (insn & (1 << 21))
+                gen_op_iwmmxt_packsq_M0_wRn(rd1);
+            else
+                gen_op_iwmmxt_packuq_M0_wRn(rd1);
+            break;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        gen_op_iwmmxt_set_cup();
+        break;
+    case 0x201: case 0x203: case 0x205: case 0x207:
+    case 0x209: case 0x20b: case 0x20d: case 0x20f:
+    case 0x211: case 0x213: case 0x215: case 0x217:
+    case 0x219: case 0x21b: case 0x21d: case 0x21f:
+        wrd = (insn >> 5) & 0xf;
+        rd0 = (insn >> 12) & 0xf;
+        rd1 = (insn >> 0) & 0xf;
+        if (rd0 == 0xf || rd1 == 0xf)
+            return 1;
+        gen_op_iwmmxt_movq_M0_wRn(wrd);
+        switch ((insn >> 16) & 0xf) {
+        case 0x0:					/* TMIA */
+            gen_movl_T0_reg(s, rd0);
+            gen_movl_T1_reg(s, rd1);
+            gen_op_iwmmxt_muladdsl_M0_T0_T1();
+            break;
+        case 0x8:					/* TMIAPH */
+            gen_movl_T0_reg(s, rd0);
+            gen_movl_T1_reg(s, rd1);
+            gen_op_iwmmxt_muladdsw_M0_T0_T1();
+            break;
+        case 0xc: case 0xd: case 0xe: case 0xf:		/* TMIAxy */
+            gen_movl_T1_reg(s, rd0);
+            if (insn & (1 << 16))
+                gen_op_shrl_T1_im(16);
+            gen_op_movl_T0_T1();
+            gen_movl_T1_reg(s, rd1);
+            if (insn & (1 << 17))
+                gen_op_shrl_T1_im(16);
+            gen_op_iwmmxt_muladdswl_M0_T0_T1();
+            break;
+        default:
+            return 1;
+        }
+        gen_op_iwmmxt_movq_wRn_M0(wrd);
+        gen_op_iwmmxt_set_mup();
+        break;
+    default:
+        return 1;
+    }
+
+    return 0;
+}
+
+/* Disassemble an XScale DSP instruction.  Returns nonzero if an error occured
+   (ie. an undefined instruction).  */
+static int disas_dsp_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+    int acc, rd0, rd1, rdhi, rdlo;
+
+    if ((insn & 0x0ff00f10) == 0x0e200010) {
+        /* Multiply with Internal Accumulate Format */
+        rd0 = (insn >> 12) & 0xf;
+        rd1 = insn & 0xf;
+        acc = (insn >> 5) & 7;
+
+        if (acc != 0)
+            return 1;
+
+        switch ((insn >> 16) & 0xf) {
+        case 0x0:					/* MIA */
+            gen_movl_T0_reg(s, rd0);
+            gen_movl_T1_reg(s, rd1);
+            gen_op_iwmmxt_muladdsl_M0_T0_T1();
+            break;
+        case 0x8:					/* MIAPH */
+            gen_movl_T0_reg(s, rd0);
+            gen_movl_T1_reg(s, rd1);
+            gen_op_iwmmxt_muladdsw_M0_T0_T1();
+            break;
+        case 0xc:					/* MIABB */
+        case 0xd:					/* MIABT */
+        case 0xe:					/* MIATB */
+        case 0xf:					/* MIATT */
+            gen_movl_T1_reg(s, rd0);
+            if (insn & (1 << 16))
+                gen_op_shrl_T1_im(16);
+            gen_op_movl_T0_T1();
+            gen_movl_T1_reg(s, rd1);
+            if (insn & (1 << 17))
+                gen_op_shrl_T1_im(16);
+            gen_op_iwmmxt_muladdswl_M0_T0_T1();
+            break;
+        default:
+            return 1;
+        }
+
+        gen_op_iwmmxt_movq_wRn_M0(acc);
+        return 0;
+    }
+
+    if ((insn & 0x0fe00ff8) == 0x0c400000) {
+        /* Internal Accumulator Access Format */
+        rdhi = (insn >> 16) & 0xf;
+        rdlo = (insn >> 12) & 0xf;
+        acc = insn & 7;
+
+        if (acc != 0)
+            return 1;
+
+        if (insn & ARM_CP_RW_BIT) {			/* MRA */
+            gen_iwmmxt_movl_T0_T1_wRn(acc);
+            gen_movl_reg_T0(s, rdlo);
+            gen_op_movl_T0_im((1 << (40 - 32)) - 1);
+            gen_op_andl_T0_T1();
+            gen_movl_reg_T0(s, rdhi);
+        } else {					/* MAR */
+            gen_movl_T0_reg(s, rdlo);
+            gen_movl_T1_reg(s, rdhi);
+            gen_iwmmxt_movl_wRn_T0_T1(acc);
+        }
+        return 0;
+    }
+
+    return 1;
+}
+
+/* Disassemble system coprocessor instruction.  Return nonzero if
+   instruction is not defined.  */
+static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+    TCGv tmp;
+    uint32_t rd = (insn >> 12) & 0xf;
+    uint32_t cp = (insn >> 8) & 0xf;
+    if (IS_USER(s)) {
+        return 1;
+    }
+
+    if (insn & ARM_CP_RW_BIT) {
+        if (!env->cp[cp].cp_read)
+            return 1;
+        gen_set_pc_im(s->pc);
+        tmp = new_tmp();
+        gen_helper_get_cp(tmp, cpu_env, tcg_const_i32(insn));
+        store_reg(s, rd, tmp);
+    } else {
+        if (!env->cp[cp].cp_write)
+            return 1;
+        gen_set_pc_im(s->pc);
+        tmp = load_reg(s, rd);
+        gen_helper_set_cp(cpu_env, tcg_const_i32(insn), tmp);
+        dead_tmp(tmp);
+    }
+    return 0;
+}
+
+static int cp15_user_ok(uint32_t insn)
+{
+    int cpn = (insn >> 16) & 0xf;
+    int cpm = insn & 0xf;
+    int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
+
+    if (cpn == 13 && cpm == 0) {
+        /* TLS register.  */
+        if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT)))
+            return 1;
+    }
+    if (cpn == 7) {
+        /* ISB, DSB, DMB.  */
+        if ((cpm == 5 && op == 4)
+                || (cpm == 10 && (op == 4 || op == 5)))
+            return 1;
+    }
+    return 0;
+}
+
+/* Disassemble system coprocessor (cp15) instruction.  Return nonzero if
+   instruction is not defined.  */
+static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
+{
+    uint32_t rd;
+    TCGv tmp;
+
+    /* M profile cores use memory mapped registers instead of cp15.  */
+    if (arm_feature(env, ARM_FEATURE_M))
+	return 1;
+
+    if ((insn & (1 << 25)) == 0) {
+        if (insn & (1 << 20)) {
+            /* mrrc */
+            return 1;
+        }
+        /* mcrr.  Used for block cache operations, so implement as no-op.  */
+        return 0;
+    }
+    if ((insn & (1 << 4)) == 0) {
+        /* cdp */
+        return 1;
+    }
+    if (IS_USER(s) && !cp15_user_ok(insn)) {
+        return 1;
+    }
+    if ((insn & 0x0fff0fff) == 0x0e070f90
+        || (insn & 0x0fff0fff) == 0x0e070f58) {
+        /* Wait for interrupt.  */
+        gen_set_pc_im(s->pc);
+        s->is_jmp = DISAS_WFI;
+        return 0;
+    }
+    rd = (insn >> 12) & 0xf;
+    if (insn & ARM_CP_RW_BIT) {
+        tmp = new_tmp();
+        gen_helper_get_cp15(tmp, cpu_env, tcg_const_i32(insn));
+        /* If the destination register is r15 then sets condition codes.  */
+        if (rd != 15)
+            store_reg(s, rd, tmp);
+        else
+            dead_tmp(tmp);
+    } else {
+        tmp = load_reg(s, rd);
+        gen_helper_set_cp15(cpu_env, tcg_const_i32(insn), tmp);
+        dead_tmp(tmp);
+        /* Normally we would always end the TB here, but Linux
+         * arch/arm/mach-pxa/sleep.S expects two instructions following
+         * an MMU enable to execute from cache.  Imitate this behaviour.  */
+        if (!arm_feature(env, ARM_FEATURE_XSCALE) ||
+                (insn & 0x0fff0fff) != 0x0e010f10)
+            gen_lookup_tb(s);
+    }
+    return 0;
+}
+
+#define VFP_REG_SHR(x, n) (((n) > 0) ? (x) >> (n) : (x) << -(n))
+#define VFP_SREG(insn, bigbit, smallbit) \
+  ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1))
+#define VFP_DREG(reg, insn, bigbit, smallbit) do { \
+    if (arm_feature(env, ARM_FEATURE_VFP3)) { \
+        reg = (((insn) >> (bigbit)) & 0x0f) \
+              | (((insn) >> ((smallbit) - 4)) & 0x10); \
+    } else { \
+        if (insn & (1 << (smallbit))) \
+            return 1; \
+        reg = ((insn) >> (bigbit)) & 0x0f; \
+    }} while (0)
+
+#define VFP_SREG_D(insn) VFP_SREG(insn, 12, 22)
+#define VFP_DREG_D(reg, insn) VFP_DREG(reg, insn, 12, 22)
+#define VFP_SREG_N(insn) VFP_SREG(insn, 16,  7)
+#define VFP_DREG_N(reg, insn) VFP_DREG(reg, insn, 16,  7)
+#define VFP_SREG_M(insn) VFP_SREG(insn,  0,  5)
+#define VFP_DREG_M(reg, insn) VFP_DREG(reg, insn,  0,  5)
+
+/* Move between integer and VFP cores.  */
+static TCGv gen_vfp_mrs(void)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_mov_i32(tmp, cpu_F0s);
+    return tmp;
+}
+
+static void gen_vfp_msr(TCGv tmp)
+{
+    tcg_gen_mov_i32(cpu_F0s, tmp);
+    dead_tmp(tmp);
+}
+
+static inline int
+vfp_enabled(CPUState * env)
+{
+    return ((env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) != 0);
+}
+
+static void gen_neon_dup_u8(TCGv var, int shift)
+{
+    TCGv tmp = new_tmp();
+    if (shift)
+        tcg_gen_shri_i32(var, var, shift);
+    tcg_gen_ext8u_i32(var, var);
+    tcg_gen_shli_i32(tmp, var, 8);
+    tcg_gen_or_i32(var, var, tmp);
+    tcg_gen_shli_i32(tmp, var, 16);
+    tcg_gen_or_i32(var, var, tmp);
+    dead_tmp(tmp);
+}
+
+static void gen_neon_dup_low16(TCGv var)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_ext16u_i32(var, var);
+    tcg_gen_shli_i32(tmp, var, 16);
+    tcg_gen_or_i32(var, var, tmp);
+    dead_tmp(tmp);
+}
+
+static void gen_neon_dup_high16(TCGv var)
+{
+    TCGv tmp = new_tmp();
+    tcg_gen_andi_i32(var, var, 0xffff0000);
+    tcg_gen_shri_i32(tmp, var, 16);
+    tcg_gen_or_i32(var, var, tmp);
+    dead_tmp(tmp);
+}
+
+/* Disassemble a VFP instruction.  Returns nonzero if an error occured
+   (ie. an undefined instruction).  */
+static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+    uint32_t rd, rn, rm, op, i, n, offset, delta_d, delta_m, bank_mask;
+    int dp, veclen;
+    TCGv tmp;
+    TCGv tmp2;
+
+    if (!arm_feature(env, ARM_FEATURE_VFP))
+        return 1;
+
+    if (!vfp_enabled(env)) {
+        /* VFP disabled.  Only allow fmxr/fmrx to/from some control regs.  */
+        if ((insn & 0x0fe00fff) != 0x0ee00a10)
+            return 1;
+        rn = (insn >> 16) & 0xf;
+        if (rn != ARM_VFP_FPSID && rn != ARM_VFP_FPEXC
+            && rn != ARM_VFP_MVFR1 && rn != ARM_VFP_MVFR0)
+            return 1;
+    }
+    dp = ((insn & 0xf00) == 0xb00);
+    switch ((insn >> 24) & 0xf) {
+    case 0xe:
+        if (insn & (1 << 4)) {
+            /* single register transfer */
+            rd = (insn >> 12) & 0xf;
+            if (dp) {
+                int size;
+                int pass;
+
+                VFP_DREG_N(rn, insn);
+                if (insn & 0xf)
+                    return 1;
+                if (insn & 0x00c00060
+                    && !arm_feature(env, ARM_FEATURE_NEON))
+                    return 1;
+
+                pass = (insn >> 21) & 1;
+                if (insn & (1 << 22)) {
+                    size = 0;
+                    offset = ((insn >> 5) & 3) * 8;
+                } else if (insn & (1 << 5)) {
+                    size = 1;
+                    offset = (insn & (1 << 6)) ? 16 : 0;
+                } else {
+                    size = 2;
+                    offset = 0;
+                }
+                if (insn & ARM_CP_RW_BIT) {
+                    /* vfp->arm */
+                    tmp = neon_load_reg(rn, pass);
+                    switch (size) {
+                    case 0:
+                        if (offset)
+                            tcg_gen_shri_i32(tmp, tmp, offset);
+                        if (insn & (1 << 23))
+                            gen_uxtb(tmp);
+                        else
+                            gen_sxtb(tmp);
+                        break;
+                    case 1:
+                        if (insn & (1 << 23)) {
+                            if (offset) {
+                                tcg_gen_shri_i32(tmp, tmp, 16);
+                            } else {
+                                gen_uxth(tmp);
+                            }
+                        } else {
+                            if (offset) {
+                                tcg_gen_sari_i32(tmp, tmp, 16);
+                            } else {
+                                gen_sxth(tmp);
+                            }
+                        }
+                        break;
+                    case 2:
+                        break;
+                    }
+                    store_reg(s, rd, tmp);
+                } else {
+                    /* arm->vfp */
+                    tmp = load_reg(s, rd);
+                    if (insn & (1 << 23)) {
+                        /* VDUP */
+                        if (size == 0) {
+                            gen_neon_dup_u8(tmp, 0);
+                        } else if (size == 1) {
+                            gen_neon_dup_low16(tmp);
+                        }
+                        tmp2 = new_tmp();
+                        tcg_gen_mov_i32(tmp2, tmp);
+                        neon_store_reg(rn, 0, tmp2);
+                        neon_store_reg(rn, 0, tmp);
+                    } else {
+                        /* VMOV */
+                        switch (size) {
+                        case 0:
+                            tmp2 = neon_load_reg(rn, pass);
+                            gen_bfi(tmp, tmp2, tmp, offset, 0xff);
+                            dead_tmp(tmp2);
+                            break;
+                        case 1:
+                            tmp2 = neon_load_reg(rn, pass);
+                            gen_bfi(tmp, tmp2, tmp, offset, 0xffff);
+                            dead_tmp(tmp2);
+                            break;
+                        case 2:
+                            break;
+                        }
+                        neon_store_reg(rn, pass, tmp);
+                    }
+                }
+            } else { /* !dp */
+                if ((insn & 0x6f) != 0x00)
+                    return 1;
+                rn = VFP_SREG_N(insn);
+                if (insn & ARM_CP_RW_BIT) {
+                    /* vfp->arm */
+                    if (insn & (1 << 21)) {
+                        /* system register */
+                        rn >>= 1;
+
+                        switch (rn) {
+                        case ARM_VFP_FPSID:
+                            /* VFP2 allows access to FSID from userspace.
+                               VFP3 restricts all id registers to privileged
+                               accesses.  */
+                            if (IS_USER(s)
+                                && arm_feature(env, ARM_FEATURE_VFP3))
+                                return 1;
+                            tmp = load_cpu_field(vfp.xregs[rn]);
+                            break;
+                        case ARM_VFP_FPEXC:
+                            if (IS_USER(s))
+                                return 1;
+                            tmp = load_cpu_field(vfp.xregs[rn]);
+                            break;
+                        case ARM_VFP_FPINST:
+                        case ARM_VFP_FPINST2:
+                            /* Not present in VFP3.  */
+                            if (IS_USER(s)
+                                || arm_feature(env, ARM_FEATURE_VFP3))
+                                return 1;
+                            tmp = load_cpu_field(vfp.xregs[rn]);
+                            break;
+                        case ARM_VFP_FPSCR:
+                            if (rd == 15) {
+                                tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
+                                tcg_gen_andi_i32(tmp, tmp, 0xf0000000);
+                            } else {
+                                tmp = new_tmp();
+                                gen_helper_vfp_get_fpscr(tmp, cpu_env);
+                            }
+                            break;
+                        case ARM_VFP_MVFR0:
+                        case ARM_VFP_MVFR1:
+                            if (IS_USER(s)
+                                || !arm_feature(env, ARM_FEATURE_VFP3))
+                                return 1;
+                            tmp = load_cpu_field(vfp.xregs[rn]);
+                            break;
+                        default:
+                            return 1;
+                        }
+                    } else {
+                        gen_mov_F0_vreg(0, rn);
+                        tmp = gen_vfp_mrs();
+                    }
+                    if (rd == 15) {
+                        /* Set the 4 flag bits in the CPSR.  */
+                        gen_set_nzcv(tmp);
+                        dead_tmp(tmp);
+                    } else {
+                        store_reg(s, rd, tmp);
+                    }
+                } else {
+                    /* arm->vfp */
+                    tmp = load_reg(s, rd);
+                    if (insn & (1 << 21)) {
+                        rn >>= 1;
+                        /* system register */
+                        switch (rn) {
+                        case ARM_VFP_FPSID:
+                        case ARM_VFP_MVFR0:
+                        case ARM_VFP_MVFR1:
+                            /* Writes are ignored.  */
+                            break;
+                        case ARM_VFP_FPSCR:
+                            gen_helper_vfp_set_fpscr(cpu_env, tmp);
+                            dead_tmp(tmp);
+                            gen_lookup_tb(s);
+                            break;
+                        case ARM_VFP_FPEXC:
+                            if (IS_USER(s))
+                                return 1;
+                            store_cpu_field(tmp, vfp.xregs[rn]);
+                            gen_lookup_tb(s);
+                            break;
+                        case ARM_VFP_FPINST:
+                        case ARM_VFP_FPINST2:
+                            store_cpu_field(tmp, vfp.xregs[rn]);
+                            break;
+                        default:
+                            return 1;
+                        }
+                    } else {
+                        gen_vfp_msr(tmp);
+                        gen_mov_vreg_F0(0, rn);
+                    }
+                }
+            }
+        } else {
+            /* data processing */
+            /* The opcode is in bits 23, 21, 20 and 6.  */
+            op = ((insn >> 20) & 8) | ((insn >> 19) & 6) | ((insn >> 6) & 1);
+            if (dp) {
+                if (op == 15) {
+                    /* rn is opcode */
+                    rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
+                } else {
+                    /* rn is register number */
+                    VFP_DREG_N(rn, insn);
+                }
+
+                if (op == 15 && (rn == 15 || rn > 17)) {
+                    /* Integer or single precision destination.  */
+                    rd = VFP_SREG_D(insn);
+                } else {
+                    VFP_DREG_D(rd, insn);
+                }
+
+                if (op == 15 && (rn == 16 || rn == 17)) {
+                    /* Integer source.  */
+                    rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1);
+                } else {
+                    VFP_DREG_M(rm, insn);
+                }
+            } else {
+                rn = VFP_SREG_N(insn);
+                if (op == 15 && rn == 15) {
+                    /* Double precision destination.  */
+                    VFP_DREG_D(rd, insn);
+                } else {
+                    rd = VFP_SREG_D(insn);
+                }
+                rm = VFP_SREG_M(insn);
+            }
+
+            veclen = env->vfp.vec_len;
+            if (op == 15 && rn > 3)
+                veclen = 0;
+
+            /* Shut up compiler warnings.  */
+            delta_m = 0;
+            delta_d = 0;
+            bank_mask = 0;
+
+            if (veclen > 0) {
+                if (dp)
+                    bank_mask = 0xc;
+                else
+                    bank_mask = 0x18;
+
+                /* Figure out what type of vector operation this is.  */
+                if ((rd & bank_mask) == 0) {
+                    /* scalar */
+                    veclen = 0;
+                } else {
+                    if (dp)
+                        delta_d = (env->vfp.vec_stride >> 1) + 1;
+                    else
+                        delta_d = env->vfp.vec_stride + 1;
+
+                    if ((rm & bank_mask) == 0) {
+                        /* mixed scalar/vector */
+                        delta_m = 0;
+                    } else {
+                        /* vector */
+                        delta_m = delta_d;
+                    }
+                }
+            }
+
+            /* Load the initial operands.  */
+            if (op == 15) {
+                switch (rn) {
+                case 16:
+                case 17:
+                    /* Integer source */
+                    gen_mov_F0_vreg(0, rm);
+                    break;
+                case 8:
+                case 9:
+                    /* Compare */
+                    gen_mov_F0_vreg(dp, rd);
+                    gen_mov_F1_vreg(dp, rm);
+                    break;
+                case 10:
+                case 11:
+                    /* Compare with zero */
+                    gen_mov_F0_vreg(dp, rd);
+                    gen_vfp_F1_ld0(dp);
+                    break;
+                case 20:
+                case 21:
+                case 22:
+                case 23:
+                    /* Source and destination the same.  */
+                    gen_mov_F0_vreg(dp, rd);
+                    break;
+                default:
+                    /* One source operand.  */
+                    gen_mov_F0_vreg(dp, rm);
+                    break;
+                }
+            } else {
+                /* Two source operands.  */
+                gen_mov_F0_vreg(dp, rn);
+                gen_mov_F1_vreg(dp, rm);
+            }
+
+            for (;;) {
+                /* Perform the calculation.  */
+                switch (op) {
+                case 0: /* mac: fd + (fn * fm) */
+                    gen_vfp_mul(dp);
+                    gen_mov_F1_vreg(dp, rd);
+                    gen_vfp_add(dp);
+                    break;
+                case 1: /* nmac: fd - (fn * fm) */
+                    gen_vfp_mul(dp);
+                    gen_vfp_neg(dp);
+                    gen_mov_F1_vreg(dp, rd);
+                    gen_vfp_add(dp);
+                    break;
+                case 2: /* msc: -fd + (fn * fm) */
+                    gen_vfp_mul(dp);
+                    gen_mov_F1_vreg(dp, rd);
+                    gen_vfp_sub(dp);
+                    break;
+                case 3: /* nmsc: -fd - (fn * fm)  */
+                    gen_vfp_mul(dp);
+                    gen_mov_F1_vreg(dp, rd);
+                    gen_vfp_add(dp);
+                    gen_vfp_neg(dp);
+                    break;
+                case 4: /* mul: fn * fm */
+                    gen_vfp_mul(dp);
+                    break;
+                case 5: /* nmul: -(fn * fm) */
+                    gen_vfp_mul(dp);
+                    gen_vfp_neg(dp);
+                    break;
+                case 6: /* add: fn + fm */
+                    gen_vfp_add(dp);
+                    break;
+                case 7: /* sub: fn - fm */
+                    gen_vfp_sub(dp);
+                    break;
+                case 8: /* div: fn / fm */
+                    gen_vfp_div(dp);
+                    break;
+                case 14: /* fconst */
+                    if (!arm_feature(env, ARM_FEATURE_VFP3))
+                      return 1;
+
+                    n = (insn << 12) & 0x80000000;
+                    i = ((insn >> 12) & 0x70) | (insn & 0xf);
+                    if (dp) {
+                        if (i & 0x40)
+                            i |= 0x3f80;
+                        else
+                            i |= 0x4000;
+                        n |= i << 16;
+                        tcg_gen_movi_i64(cpu_F0d, ((uint64_t)n) << 32);
+                    } else {
+                        if (i & 0x40)
+                            i |= 0x780;
+                        else
+                            i |= 0x800;
+                        n |= i << 19;
+                        tcg_gen_movi_i32(cpu_F0s, n);
+                    }
+                    break;
+                case 15: /* extension space */
+                    switch (rn) {
+                    case 0: /* cpy */
+                        /* no-op */
+                        break;
+                    case 1: /* abs */
+                        gen_vfp_abs(dp);
+                        break;
+                    case 2: /* neg */
+                        gen_vfp_neg(dp);
+                        break;
+                    case 3: /* sqrt */
+                        gen_vfp_sqrt(dp);
+                        break;
+                    case 8: /* cmp */
+                        gen_vfp_cmp(dp);
+                        break;
+                    case 9: /* cmpe */
+                        gen_vfp_cmpe(dp);
+                        break;
+                    case 10: /* cmpz */
+                        gen_vfp_cmp(dp);
+                        break;
+                    case 11: /* cmpez */
+                        gen_vfp_F1_ld0(dp);
+                        gen_vfp_cmpe(dp);
+                        break;
+                    case 15: /* single<->double conversion */
+                        if (dp)
+                            gen_helper_vfp_fcvtsd(cpu_F0s, cpu_F0d, cpu_env);
+                        else
+                            gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env);
+                        break;
+                    case 16: /* fuito */
+                        gen_vfp_uito(dp);
+                        break;
+                    case 17: /* fsito */
+                        gen_vfp_sito(dp);
+                        break;
+                    case 20: /* fshto */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_shto(dp, rm);
+                        break;
+                    case 21: /* fslto */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_slto(dp, rm);
+                        break;
+                    case 22: /* fuhto */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_uhto(dp, rm);
+                        break;
+                    case 23: /* fulto */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_ulto(dp, rm);
+                        break;
+                    case 24: /* ftoui */
+                        gen_vfp_toui(dp);
+                        break;
+                    case 25: /* ftouiz */
+                        gen_vfp_touiz(dp);
+                        break;
+                    case 26: /* ftosi */
+                        gen_vfp_tosi(dp);
+                        break;
+                    case 27: /* ftosiz */
+                        gen_vfp_tosiz(dp);
+                        break;
+                    case 28: /* ftosh */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_tosh(dp, rm);
+                        break;
+                    case 29: /* ftosl */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_tosl(dp, rm);
+                        break;
+                    case 30: /* ftouh */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_touh(dp, rm);
+                        break;
+                    case 31: /* ftoul */
+                        if (!arm_feature(env, ARM_FEATURE_VFP3))
+                          return 1;
+                        gen_vfp_toul(dp, rm);
+                        break;
+                    default: /* undefined */
+                        printf ("rn:%d\n", rn);
+                        return 1;
+                    }
+                    break;
+                default: /* undefined */
+                    printf ("op:%d\n", op);
+                    return 1;
+                }
+
+                /* Write back the result.  */
+                if (op == 15 && (rn >= 8 && rn <= 11))
+                    ; /* Comparison, do nothing.  */
+                else if (op == 15 && rn > 17)
+                    /* Integer result.  */
+                    gen_mov_vreg_F0(0, rd);
+                else if (op == 15 && rn == 15)
+                    /* conversion */
+                    gen_mov_vreg_F0(!dp, rd);
+                else
+                    gen_mov_vreg_F0(dp, rd);
+
+                /* break out of the loop if we have finished  */
+                if (veclen == 0)
+                    break;
+
+                if (op == 15 && delta_m == 0) {
+                    /* single source one-many */
+                    while (veclen--) {
+                        rd = ((rd + delta_d) & (bank_mask - 1))
+                             | (rd & bank_mask);
+                        gen_mov_vreg_F0(dp, rd);
+                    }
+                    break;
+                }
+                /* Setup the next operands.  */
+                veclen--;
+                rd = ((rd + delta_d) & (bank_mask - 1))
+                     | (rd & bank_mask);
+
+                if (op == 15) {
+                    /* One source operand.  */
+                    rm = ((rm + delta_m) & (bank_mask - 1))
+                         | (rm & bank_mask);
+                    gen_mov_F0_vreg(dp, rm);
+                } else {
+                    /* Two source operands.  */
+                    rn = ((rn + delta_d) & (bank_mask - 1))
+                         | (rn & bank_mask);
+                    gen_mov_F0_vreg(dp, rn);
+                    if (delta_m) {
+                        rm = ((rm + delta_m) & (bank_mask - 1))
+                             | (rm & bank_mask);
+                        gen_mov_F1_vreg(dp, rm);
+                    }
+                }
+            }
+        }
+        break;
+    case 0xc:
+    case 0xd:
+        if (dp && (insn & 0x03e00000) == 0x00400000) {
+            /* two-register transfer */
+            rn = (insn >> 16) & 0xf;
+            rd = (insn >> 12) & 0xf;
+            if (dp) {
+                VFP_DREG_M(rm, insn);
+            } else {
+                rm = VFP_SREG_M(insn);
+            }
+
+            if (insn & ARM_CP_RW_BIT) {
+                /* vfp->arm */
+                if (dp) {
+                    gen_mov_F0_vreg(0, rm * 2);
+                    tmp = gen_vfp_mrs();
+                    store_reg(s, rd, tmp);
+                    gen_mov_F0_vreg(0, rm * 2 + 1);
+                    tmp = gen_vfp_mrs();
+                    store_reg(s, rn, tmp);
+                } else {
+                    gen_mov_F0_vreg(0, rm);
+                    tmp = gen_vfp_mrs();
+                    store_reg(s, rn, tmp);
+                    gen_mov_F0_vreg(0, rm + 1);
+                    tmp = gen_vfp_mrs();
+                    store_reg(s, rd, tmp);
+                }
+            } else {
+                /* arm->vfp */
+                if (dp) {
+                    tmp = load_reg(s, rd);
+                    gen_vfp_msr(tmp);
+                    gen_mov_vreg_F0(0, rm * 2);
+                    tmp = load_reg(s, rn);
+                    gen_vfp_msr(tmp);
+                    gen_mov_vreg_F0(0, rm * 2 + 1);
+                } else {
+                    tmp = load_reg(s, rn);
+                    gen_vfp_msr(tmp);
+                    gen_mov_vreg_F0(0, rm);
+                    tmp = load_reg(s, rd);
+                    gen_vfp_msr(tmp);
+                    gen_mov_vreg_F0(0, rm + 1);
+                }
+            }
+        } else {
+            /* Load/store */
+            rn = (insn >> 16) & 0xf;
+            if (dp)
+                VFP_DREG_D(rd, insn);
+            else
+                rd = VFP_SREG_D(insn);
+            if (s->thumb && rn == 15) {
+                gen_op_movl_T1_im(s->pc & ~2);
+            } else {
+                gen_movl_T1_reg(s, rn);
+            }
+            if ((insn & 0x01200000) == 0x01000000) {
+                /* Single load/store */
+                offset = (insn & 0xff) << 2;
+                if ((insn & (1 << 23)) == 0)
+                    offset = -offset;
+                gen_op_addl_T1_im(offset);
+                if (insn & (1 << 20)) {
+                    gen_vfp_ld(s, dp);
+                    gen_mov_vreg_F0(dp, rd);
+                } else {
+                    gen_mov_F0_vreg(dp, rd);
+                    gen_vfp_st(s, dp);
+                }
+            } else {
+                /* load/store multiple */
+                if (dp)
+                    n = (insn >> 1) & 0x7f;
+                else
+                    n = insn & 0xff;
+
+                if (insn & (1 << 24)) /* pre-decrement */
+                    gen_op_addl_T1_im(-((insn & 0xff) << 2));
+
+                if (dp)
+                    offset = 8;
+                else
+                    offset = 4;
+                for (i = 0; i < n; i++) {
+                    if (insn & ARM_CP_RW_BIT) {
+                        /* load */
+                        gen_vfp_ld(s, dp);
+                        gen_mov_vreg_F0(dp, rd + i);
+                    } else {
+                        /* store */
+                        gen_mov_F0_vreg(dp, rd + i);
+                        gen_vfp_st(s, dp);
+                    }
+                    gen_op_addl_T1_im(offset);
+                }
+                if (insn & (1 << 21)) {
+                    /* writeback */
+                    if (insn & (1 << 24))
+                        offset = -offset * n;
+                    else if (dp && (insn & 1))
+                        offset = 4;
+                    else
+                        offset = 0;
+
+                    if (offset != 0)
+                        gen_op_addl_T1_im(offset);
+                    gen_movl_reg_T1(s, rn);
+                }
+            }
+        }
+        break;
+    default:
+        /* Should never happen.  */
+        return 1;
+    }
+    return 0;
+}
+
+static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest)
+{
+    TranslationBlock *tb;
+
+    tb = s->tb;
+    if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+        tcg_gen_goto_tb(n);
+        gen_set_pc_im(dest);
+        tcg_gen_exit_tb((long)tb + n);
+    } else {
+        gen_set_pc_im(dest);
+        tcg_gen_exit_tb(0);
+    }
+}
+
+static inline void gen_jmp (DisasContext *s, uint32_t dest)
+{
+    if (unlikely(s->singlestep_enabled)) {
+        /* An indirect jump so that we still trigger the debug exception.  */
+        if (s->thumb)
+            dest |= 1;
+        gen_bx_im(s, dest);
+    } else {
+        gen_goto_tb(s, 0, dest);
+        s->is_jmp = DISAS_TB_JUMP;
+    }
+}
+
+static inline void gen_mulxy(TCGv t0, TCGv t1, int x, int y)
+{
+    if (x)
+        tcg_gen_sari_i32(t0, t0, 16);
+    else
+        gen_sxth(t0);
+    if (y)
+        tcg_gen_sari_i32(t1, t1, 16);
+    else
+        gen_sxth(t1);
+    tcg_gen_mul_i32(t0, t0, t1);
+}
+
+/* Return the mask of PSR bits set by a MSR instruction.  */
+static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
+    uint32_t mask;
+
+    mask = 0;
+    if (flags & (1 << 0))
+        mask |= 0xff;
+    if (flags & (1 << 1))
+        mask |= 0xff00;
+    if (flags & (1 << 2))
+        mask |= 0xff0000;
+    if (flags & (1 << 3))
+        mask |= 0xff000000;
+
+    /* Mask out undefined bits.  */
+    mask &= ~CPSR_RESERVED;
+    if (!arm_feature(env, ARM_FEATURE_V6))
+        mask &= ~(CPSR_E | CPSR_GE);
+    if (!arm_feature(env, ARM_FEATURE_THUMB2))
+        mask &= ~CPSR_IT;
+    /* Mask out execution state bits.  */
+    if (!spsr)
+        mask &= ~CPSR_EXEC;
+    /* Mask out privileged bits.  */
+    if (IS_USER(s))
+        mask &= CPSR_USER;
+    return mask;
+}
+
+/* Returns nonzero if access to the PSR is not permitted.  */
+static int gen_set_psr_T0(DisasContext *s, uint32_t mask, int spsr)
+{
+    TCGv tmp;
+    if (spsr) {
+        /* ??? This is also undefined in system mode.  */
+        if (IS_USER(s))
+            return 1;
+
+        tmp = load_cpu_field(spsr);
+        tcg_gen_andi_i32(tmp, tmp, ~mask);
+        tcg_gen_andi_i32(cpu_T[0], cpu_T[0], mask);
+        tcg_gen_or_i32(tmp, tmp, cpu_T[0]);
+        store_cpu_field(tmp, spsr);
+    } else {
+        gen_set_cpsr(cpu_T[0], mask);
+    }
+    gen_lookup_tb(s);
+    return 0;
+}
+
+/* Generate an old-style exception return.  */
+static void gen_exception_return(DisasContext *s)
+{
+    TCGv tmp;
+    gen_movl_reg_T0(s, 15);
+    tmp = load_cpu_field(spsr);
+    gen_set_cpsr(tmp, 0xffffffff);
+    dead_tmp(tmp);
+    s->is_jmp = DISAS_UPDATE;
+}
+
+/* Generate a v6 exception return.  Marks both values as dead.  */
+static void gen_rfe(DisasContext *s, TCGv pc, TCGv cpsr)
+{
+    gen_set_cpsr(cpsr, 0xffffffff);
+    dead_tmp(cpsr);
+    store_reg(s, 15, pc);
+    s->is_jmp = DISAS_UPDATE;
+}
+
+static inline void
+gen_set_condexec (DisasContext *s)
+{
+    if (s->condexec_mask) {
+        uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1);
+        TCGv tmp = new_tmp();
+        tcg_gen_movi_i32(tmp, val);
+        store_cpu_field(tmp, condexec_bits);
+    }
+}
+
+static void gen_nop_hint(DisasContext *s, int val)
+{
+    switch (val) {
+    case 3: /* wfi */
+        gen_set_pc_im(s->pc);
+        s->is_jmp = DISAS_WFI;
+        break;
+    case 2: /* wfe */
+    case 4: /* sev */
+        /* TODO: Implement SEV and WFE.  May help SMP performance.  */
+    default: /* nop */
+        break;
+    }
+}
+
+/* These macros help make the code more readable when migrating from the
+   old dyngen helpers.  They should probably be removed when
+   T0/T1 are removed.  */
+#define CPU_T001 cpu_T[0], cpu_T[0], cpu_T[1]
+#define CPU_T0E01 cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]
+
+#define CPU_V001 cpu_V0, cpu_V0, cpu_V1
+
+static inline int gen_neon_add(int size)
+{
+    switch (size) {
+    case 0: gen_helper_neon_add_u8(CPU_T001); break;
+    case 1: gen_helper_neon_add_u16(CPU_T001); break;
+    case 2: gen_op_addl_T0_T1(); break;
+    default: return 1;
+    }
+    return 0;
+}
+
+static inline void gen_neon_rsb(int size)
+{
+    switch (size) {
+    case 0: gen_helper_neon_sub_u8(cpu_T[0], cpu_T[1], cpu_T[0]); break;
+    case 1: gen_helper_neon_sub_u16(cpu_T[0], cpu_T[1], cpu_T[0]); break;
+    case 2: gen_op_rsbl_T0_T1(); break;
+    default: return;
+    }
+}
+
+/* 32-bit pairwise ops end up the same as the elementwise versions.  */
+#define gen_helper_neon_pmax_s32  gen_helper_neon_max_s32
+#define gen_helper_neon_pmax_u32  gen_helper_neon_max_u32
+#define gen_helper_neon_pmin_s32  gen_helper_neon_min_s32
+#define gen_helper_neon_pmin_u32  gen_helper_neon_min_u32
+
+/* FIXME: This is wrong.  They set the wrong overflow bit.  */
+#define gen_helper_neon_qadd_s32(a, e, b, c) gen_helper_add_saturate(a, b, c)
+#define gen_helper_neon_qadd_u32(a, e, b, c) gen_helper_add_usaturate(a, b, c)
+#define gen_helper_neon_qsub_s32(a, e, b, c) gen_helper_sub_saturate(a, b, c)
+#define gen_helper_neon_qsub_u32(a, e, b, c) gen_helper_sub_usaturate(a, b, c)
+
+#define GEN_NEON_INTEGER_OP_ENV(name) do { \
+    switch ((size << 1) | u) { \
+    case 0: \
+        gen_helper_neon_##name##_s8(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
+        break; \
+    case 1: \
+        gen_helper_neon_##name##_u8(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
+        break; \
+    case 2: \
+        gen_helper_neon_##name##_s16(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
+        break; \
+    case 3: \
+        gen_helper_neon_##name##_u16(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
+        break; \
+    case 4: \
+        gen_helper_neon_##name##_s32(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
+        break; \
+    case 5: \
+        gen_helper_neon_##name##_u32(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
+        break; \
+    default: return 1; \
+    }} while (0)
+
+#define GEN_NEON_INTEGER_OP(name) do { \
+    switch ((size << 1) | u) { \
+    case 0: \
+        gen_helper_neon_##name##_s8(cpu_T[0], cpu_T[0], cpu_T[1]); \
+        break; \
+    case 1: \
+        gen_helper_neon_##name##_u8(cpu_T[0], cpu_T[0], cpu_T[1]); \
+        break; \
+    case 2: \
+        gen_helper_neon_##name##_s16(cpu_T[0], cpu_T[0], cpu_T[1]); \
+        break; \
+    case 3: \
+        gen_helper_neon_##name##_u16(cpu_T[0], cpu_T[0], cpu_T[1]); \
+        break; \
+    case 4: \
+        gen_helper_neon_##name##_s32(cpu_T[0], cpu_T[0], cpu_T[1]); \
+        break; \
+    case 5: \
+        gen_helper_neon_##name##_u32(cpu_T[0], cpu_T[0], cpu_T[1]); \
+        break; \
+    default: return 1; \
+    }} while (0)
+
+static inline void
+gen_neon_movl_scratch_T0(int scratch)
+{
+  uint32_t offset;
+
+  offset = offsetof(CPUARMState, vfp.scratch[scratch]);
+  tcg_gen_st_i32(cpu_T[0], cpu_env, offset);
+}
+
+static inline void
+gen_neon_movl_scratch_T1(int scratch)
+{
+  uint32_t offset;
+
+  offset = offsetof(CPUARMState, vfp.scratch[scratch]);
+  tcg_gen_st_i32(cpu_T[1], cpu_env, offset);
+}
+
+static inline void
+gen_neon_movl_T0_scratch(int scratch)
+{
+  uint32_t offset;
+
+  offset = offsetof(CPUARMState, vfp.scratch[scratch]);
+  tcg_gen_ld_i32(cpu_T[0], cpu_env, offset);
+}
+
+static inline void
+gen_neon_movl_T1_scratch(int scratch)
+{
+  uint32_t offset;
+
+  offset = offsetof(CPUARMState, vfp.scratch[scratch]);
+  tcg_gen_ld_i32(cpu_T[1], cpu_env, offset);
+}
+
+static inline void gen_neon_get_scalar(int size, int reg)
+{
+    if (size == 1) {
+        NEON_GET_REG(T0, reg >> 1, reg & 1);
+    } else {
+        NEON_GET_REG(T0, reg >> 2, (reg >> 1) & 1);
+        if (reg & 1)
+            gen_neon_dup_low16(cpu_T[0]);
+        else
+            gen_neon_dup_high16(cpu_T[0]);
+    }
+}
+
+static void gen_neon_unzip(int reg, int q, int tmp, int size)
+{
+    int n;
+
+    for (n = 0; n < q + 1; n += 2) {
+        NEON_GET_REG(T0, reg, n);
+        NEON_GET_REG(T0, reg, n + n);
+        switch (size) {
+        case 0: gen_helper_neon_unzip_u8(); break;
+        case 1: gen_helper_neon_zip_u16(); break; /* zip and unzip are the same.  */
+        case 2: /* no-op */; break;
+        default: abort();
+        }
+        gen_neon_movl_scratch_T0(tmp + n);
+        gen_neon_movl_scratch_T1(tmp + n + 1);
+    }
+}
+
+static struct {
+    int nregs;
+    int interleave;
+    int spacing;
+} neon_ls_element_type[11] = {
+    {4, 4, 1},
+    {4, 4, 2},
+    {4, 1, 1},
+    {4, 2, 1},
+    {3, 3, 1},
+    {3, 3, 2},
+    {3, 1, 1},
+    {1, 1, 1},
+    {2, 2, 1},
+    {2, 2, 2},
+    {2, 1, 1}
+};
+
+/* Translate a NEON load/store element instruction.  Return nonzero if the
+   instruction is invalid.  */
+static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+    int rd, rn, rm;
+    int op;
+    int nregs;
+    int interleave;
+    int stride;
+    int size;
+    int reg;
+    int pass;
+    int load;
+    int shift;
+    int n;
+    TCGv tmp;
+    TCGv tmp2;
+
+    if (!vfp_enabled(env))
+      return 1;
+    VFP_DREG_D(rd, insn);
+    rn = (insn >> 16) & 0xf;
+    rm = insn & 0xf;
+    load = (insn & (1 << 21)) != 0;
+    if ((insn & (1 << 23)) == 0) {
+        /* Load store all elements.  */
+        op = (insn >> 8) & 0xf;
+        size = (insn >> 6) & 3;
+        if (op > 10 || size == 3)
+            return 1;
+        nregs = neon_ls_element_type[op].nregs;
+        interleave = neon_ls_element_type[op].interleave;
+        gen_movl_T1_reg(s, rn);
+        stride = (1 << size) * interleave;
+        for (reg = 0; reg < nregs; reg++) {
+            if (interleave > 2 || (interleave == 2 && nregs == 2)) {
+                gen_movl_T1_reg(s, rn);
+                gen_op_addl_T1_im((1 << size) * reg);
+            } else if (interleave == 2 && nregs == 4 && reg == 2) {
+                gen_movl_T1_reg(s, rn);
+                gen_op_addl_T1_im(1 << size);
+            }
+            for (pass = 0; pass < 2; pass++) {
+                if (size == 2) {
+                    if (load) {
+                        tmp = gen_ld32(cpu_T[1], IS_USER(s));
+                        neon_store_reg(rd, pass, tmp);
+                    } else {
+                        tmp = neon_load_reg(rd, pass);
+                        gen_st32(tmp, cpu_T[1], IS_USER(s));
+                    }
+                    gen_op_addl_T1_im(stride);
+                } else if (size == 1) {
+                    if (load) {
+                        tmp = gen_ld16u(cpu_T[1], IS_USER(s));
+                        gen_op_addl_T1_im(stride);
+                        tmp2 = gen_ld16u(cpu_T[1], IS_USER(s));
+                        gen_op_addl_T1_im(stride);
+                        gen_bfi(tmp, tmp, tmp2, 16, 0xffff);
+                        dead_tmp(tmp2);
+                        neon_store_reg(rd, pass, tmp);
+                    } else {
+                        tmp = neon_load_reg(rd, pass);
+                        tmp2 = new_tmp();
+                        tcg_gen_shri_i32(tmp2, tmp, 16);
+                        gen_st16(tmp, cpu_T[1], IS_USER(s));
+                        gen_op_addl_T1_im(stride);
+                        gen_st16(tmp2, cpu_T[1], IS_USER(s));
+                        gen_op_addl_T1_im(stride);
+                    }
+                } else /* size == 0 */ {
+                    if (load) {
+                        TCGV_UNUSED(tmp2);
+                        for (n = 0; n < 4; n++) {
+                            tmp = gen_ld8u(cpu_T[1], IS_USER(s));
+                            gen_op_addl_T1_im(stride);
+                            if (n == 0) {
+                                tmp2 = tmp;
+                            } else {
+                                gen_bfi(tmp2, tmp2, tmp, n * 8, 0xff);
+                                dead_tmp(tmp);
+                            }
+                        }
+                        neon_store_reg(rd, pass, tmp2);
+                    } else {
+                        tmp2 = neon_load_reg(rd, pass);
+                        for (n = 0; n < 4; n++) {
+                            tmp = new_tmp();
+                            if (n == 0) {
+                                tcg_gen_mov_i32(tmp, tmp2);
+                            } else {
+                                tcg_gen_shri_i32(tmp, tmp2, n * 8);
+                            }
+                            gen_st8(tmp, cpu_T[1], IS_USER(s));
+                            gen_op_addl_T1_im(stride);
+                        }
+                        dead_tmp(tmp2);
+                    }
+                }
+            }
+            rd += neon_ls_element_type[op].spacing;
+        }
+        stride = nregs * 8;
+    } else {
+        size = (insn >> 10) & 3;
+        if (size == 3) {
+            /* Load single element to all lanes.  */
+            if (!load)
+                return 1;
+            size = (insn >> 6) & 3;
+            nregs = ((insn >> 8) & 3) + 1;
+            stride = (insn & (1 << 5)) ? 2 : 1;
+            gen_movl_T1_reg(s, rn);
+            for (reg = 0; reg < nregs; reg++) {
+                switch (size) {
+                case 0:
+                    tmp = gen_ld8u(cpu_T[1], IS_USER(s));
+                    gen_neon_dup_u8(tmp, 0);
+                    break;
+                case 1:
+                    tmp = gen_ld16u(cpu_T[1], IS_USER(s));
+                    gen_neon_dup_low16(tmp);
+                    break;
+                case 2:
+                    tmp = gen_ld32(cpu_T[0], IS_USER(s));
+                    break;
+                case 3:
+                    return 1;
+                default: /* Avoid compiler warnings.  */
+                    abort();
+                }
+                gen_op_addl_T1_im(1 << size);
+                tmp2 = new_tmp();
+                tcg_gen_mov_i32(tmp2, tmp);
+                neon_store_reg(rd, 0, tmp2);
+                neon_store_reg(rd, 0, tmp);
+                rd += stride;
+            }
+            stride = (1 << size) * nregs;
+        } else {
+            /* Single element.  */
+            pass = (insn >> 7) & 1;
+            switch (size) {
+            case 0:
+                shift = ((insn >> 5) & 3) * 8;
+                stride = 1;
+                break;
+            case 1:
+                shift = ((insn >> 6) & 1) * 16;
+                stride = (insn & (1 << 5)) ? 2 : 1;
+                break;
+            case 2:
+                shift = 0;
+                stride = (insn & (1 << 6)) ? 2 : 1;
+                break;
+            default:
+                abort();
+            }
+            nregs = ((insn >> 8) & 3) + 1;
+            gen_movl_T1_reg(s, rn);
+            for (reg = 0; reg < nregs; reg++) {
+                if (load) {
+                    switch (size) {
+                    case 0:
+                        tmp = gen_ld8u(cpu_T[1], IS_USER(s));
+                        break;
+                    case 1:
+                        tmp = gen_ld16u(cpu_T[1], IS_USER(s));
+                        break;
+                    case 2:
+                        tmp = gen_ld32(cpu_T[1], IS_USER(s));
+                        break;
+                    default: /* Avoid compiler warnings.  */
+                        abort();
+                    }
+                    if (size != 2) {
+                        tmp2 = neon_load_reg(rd, pass);
+                        gen_bfi(tmp, tmp2, tmp, shift, size ? 0xffff : 0xff);
+                        dead_tmp(tmp2);
+                    }
+                    neon_store_reg(rd, pass, tmp);
+                } else { /* Store */
+                    tmp = neon_load_reg(rd, pass);
+                    if (shift)
+                        tcg_gen_shri_i32(tmp, tmp, shift);
+                    switch (size) {
+                    case 0:
+                        gen_st8(tmp, cpu_T[1], IS_USER(s));
+                        break;
+                    case 1:
+                        gen_st16(tmp, cpu_T[1], IS_USER(s));
+                        break;
+                    case 2:
+                        gen_st32(tmp, cpu_T[1], IS_USER(s));
+                        break;
+                    }
+                }
+                rd += stride;
+                gen_op_addl_T1_im(1 << size);
+            }
+            stride = nregs * (1 << size);
+        }
+    }
+    if (rm != 15) {
+        TCGv base;
+
+        base = load_reg(s, rn);
+        if (rm == 13) {
+            tcg_gen_addi_i32(base, base, stride);
+        } else {
+            TCGv index;
+            index = load_reg(s, rm);
+            tcg_gen_add_i32(base, base, index);
+            dead_tmp(index);
+        }
+        store_reg(s, rn, base);
+    }
+    return 0;
+}
+
+/* Bitwise select.  dest = c ? t : f.  Clobbers T and F.  */
+static void gen_neon_bsl(TCGv dest, TCGv t, TCGv f, TCGv c)
+{
+    tcg_gen_and_i32(t, t, c);
+    tcg_gen_bic_i32(f, f, c);
+    tcg_gen_or_i32(dest, t, f);
+}
+
+static inline void gen_neon_narrow(int size, TCGv dest, TCGv src)
+{
+    switch (size) {
+    case 0: gen_helper_neon_narrow_u8(dest, src); break;
+    case 1: gen_helper_neon_narrow_u16(dest, src); break;
+    case 2: tcg_gen_trunc_i64_i32(dest, src); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv src)
+{
+    switch (size) {
+    case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break;
+    case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break;
+    case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv src)
+{
+    switch (size) {
+    case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break;
+    case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break;
+    case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_shift_narrow(int size, TCGv var, TCGv shift,
+                                         int q, int u)
+{
+    if (q) {
+        if (u) {
+            switch (size) {
+            case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
+            case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
+            default: abort();
+            }
+        } else {
+            switch (size) {
+            case 1: gen_helper_neon_rshl_s16(var, var, shift); break;
+            case 2: gen_helper_neon_rshl_s32(var, var, shift); break;
+            default: abort();
+            }
+        }
+    } else {
+        if (u) {
+            switch (size) {
+            case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
+            case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
+            default: abort();
+            }
+        } else {
+            switch (size) {
+            case 1: gen_helper_neon_shl_s16(var, var, shift); break;
+            case 2: gen_helper_neon_shl_s32(var, var, shift); break;
+            default: abort();
+            }
+        }
+    }
+}
+
+static inline void gen_neon_widen(TCGv dest, TCGv src, int size, int u)
+{
+    if (u) {
+        switch (size) {
+        case 0: gen_helper_neon_widen_u8(dest, src); break;
+        case 1: gen_helper_neon_widen_u16(dest, src); break;
+        case 2: tcg_gen_extu_i32_i64(dest, src); break;
+        default: abort();
+        }
+    } else {
+        switch (size) {
+        case 0: gen_helper_neon_widen_s8(dest, src); break;
+        case 1: gen_helper_neon_widen_s16(dest, src); break;
+        case 2: tcg_gen_ext_i32_i64(dest, src); break;
+        default: abort();
+        }
+    }
+    dead_tmp(src);
+}
+
+static inline void gen_neon_addl(int size)
+{
+    switch (size) {
+    case 0: gen_helper_neon_addl_u16(CPU_V001); break;
+    case 1: gen_helper_neon_addl_u32(CPU_V001); break;
+    case 2: tcg_gen_add_i64(CPU_V001); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_subl(int size)
+{
+    switch (size) {
+    case 0: gen_helper_neon_subl_u16(CPU_V001); break;
+    case 1: gen_helper_neon_subl_u32(CPU_V001); break;
+    case 2: tcg_gen_sub_i64(CPU_V001); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_negl(TCGv var, int size)
+{
+    switch (size) {
+    case 0: gen_helper_neon_negl_u16(var, var); break;
+    case 1: gen_helper_neon_negl_u32(var, var); break;
+    case 2: gen_helper_neon_negl_u64(var, var); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_addl_saturate(TCGv op0, TCGv op1, int size)
+{
+    switch (size) {
+    case 1: gen_helper_neon_addl_saturate_s32(op0, cpu_env, op0, op1); break;
+    case 2: gen_helper_neon_addl_saturate_s64(op0, cpu_env, op0, op1); break;
+    default: abort();
+    }
+}
+
+static inline void gen_neon_mull(TCGv dest, TCGv a, TCGv b, int size, int u)
+{
+    TCGv tmp;
+
+    switch ((size << 1) | u) {
+    case 0: gen_helper_neon_mull_s8(dest, a, b); break;
+    case 1: gen_helper_neon_mull_u8(dest, a, b); break;
+    case 2: gen_helper_neon_mull_s16(dest, a, b); break;
+    case 3: gen_helper_neon_mull_u16(dest, a, b); break;
+    case 4:
+        tmp = gen_muls_i64_i32(a, b);
+        tcg_gen_mov_i64(dest, tmp);
+        break;
+    case 5:
+        tmp = gen_mulu_i64_i32(a, b);
+        tcg_gen_mov_i64(dest, tmp);
+        break;
+    default: abort();
+    }
+    if (size < 2) {
+        dead_tmp(b);
+        dead_tmp(a);
+    }
+}
+
+/* Translate a NEON data processing instruction.  Return nonzero if the
+   instruction is invalid.
+   We process data in a mixture of 32-bit and 64-bit chunks.
+   Mostly we use 32-bit chunks so we can use normal scalar instructions.  */
+
+static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+    int op;
+    int q;
+    int rd, rn, rm;
+    int size;
+    int shift;
+    int pass;
+    int count;
+    int pairwise;
+    int u;
+    int n;
+    uint32_t imm;
+    TCGv tmp;
+    TCGv tmp2;
+    TCGv tmp3;
+
+    if (!vfp_enabled(env))
+      return 1;
+    q = (insn & (1 << 6)) != 0;
+    u = (insn >> 24) & 1;
+    VFP_DREG_D(rd, insn);
+    VFP_DREG_N(rn, insn);
+    VFP_DREG_M(rm, insn);
+    size = (insn >> 20) & 3;
+    if ((insn & (1 << 23)) == 0) {
+        /* Three register same length.  */
+        op = ((insn >> 7) & 0x1e) | ((insn >> 4) & 1);
+        if (size == 3 && (op == 1 || op == 5 || op == 8 || op == 9
+                          || op == 10 || op  == 11 || op == 16)) {
+            /* 64-bit element instructions.  */
+            for (pass = 0; pass < (q ? 2 : 1); pass++) {
+                neon_load_reg64(cpu_V0, rn + pass);
+                neon_load_reg64(cpu_V1, rm + pass);
+                switch (op) {
+                case 1: /* VQADD */
+                    if (u) {
+                        gen_helper_neon_add_saturate_u64(CPU_V001);
+                    } else {
+                        gen_helper_neon_add_saturate_s64(CPU_V001);
+                    }
+                    break;
+                case 5: /* VQSUB */
+                    if (u) {
+                        gen_helper_neon_sub_saturate_u64(CPU_V001);
+                    } else {
+                        gen_helper_neon_sub_saturate_s64(CPU_V001);
+                    }
+                    break;
+                case 8: /* VSHL */
+                    if (u) {
+                        gen_helper_neon_shl_u64(cpu_V0, cpu_V1, cpu_V0);
+                    } else {
+                        gen_helper_neon_shl_s64(cpu_V0, cpu_V1, cpu_V0);
+                    }
+                    break;
+                case 9: /* VQSHL */
+                    if (u) {
+                        gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
+                                                 cpu_V0, cpu_V0);
+                    } else {
+                        gen_helper_neon_qshl_s64(cpu_V1, cpu_env,
+                                                 cpu_V1, cpu_V0);
+                    }
+                    break;
+                case 10: /* VRSHL */
+                    if (u) {
+                        gen_helper_neon_rshl_u64(cpu_V0, cpu_V1, cpu_V0);
+                    } else {
+                        gen_helper_neon_rshl_s64(cpu_V0, cpu_V1, cpu_V0);
+                    }
+                    break;
+                case 11: /* VQRSHL */
+                    if (u) {
+                        gen_helper_neon_qrshl_u64(cpu_V0, cpu_env,
+                                                  cpu_V1, cpu_V0);
+                    } else {
+                        gen_helper_neon_qrshl_s64(cpu_V0, cpu_env,
+                                                  cpu_V1, cpu_V0);
+                    }
+                    break;
+                case 16:
+                    if (u) {
+                        tcg_gen_sub_i64(CPU_V001);
+                    } else {
+                        tcg_gen_add_i64(CPU_V001);
+                    }
+                    break;
+                default:
+                    abort();
+                }
+                neon_store_reg64(cpu_V0, rd + pass);
+            }
+            return 0;
+        }
+        switch (op) {
+        case 8: /* VSHL */
+        case 9: /* VQSHL */
+        case 10: /* VRSHL */
+        case 11: /* VQRSHL */
+            {
+                int rtmp;
+                /* Shift instruction operands are reversed.  */
+                rtmp = rn;
+                rn = rm;
+                rm = rtmp;
+                pairwise = 0;
+            }
+            break;
+        case 20: /* VPMAX */
+        case 21: /* VPMIN */
+        case 23: /* VPADD */
+            pairwise = 1;
+            break;
+        case 26: /* VPADD (float) */
+            pairwise = (u && size < 2);
+            break;
+        case 30: /* VPMIN/VPMAX (float) */
+            pairwise = u;
+            break;
+        default:
+            pairwise = 0;
+            break;
+        }
+        for (pass = 0; pass < (q ? 4 : 2); pass++) {
+
+        if (pairwise) {
+            /* Pairwise.  */
+            if (q)
+                n = (pass & 1) * 2;
+            else
+                n = 0;
+            if (pass < q + 1) {
+                NEON_GET_REG(T0, rn, n);
+                NEON_GET_REG(T1, rn, n + 1);
+            } else {
+                NEON_GET_REG(T0, rm, n);
+                NEON_GET_REG(T1, rm, n + 1);
+            }
+        } else {
+            /* Elementwise.  */
+            NEON_GET_REG(T0, rn, pass);
+            NEON_GET_REG(T1, rm, pass);
+        }
+        switch (op) {
+        case 0: /* VHADD */
+            GEN_NEON_INTEGER_OP(hadd);
+            break;
+        case 1: /* VQADD */
+            GEN_NEON_INTEGER_OP_ENV(qadd);
+            break;
+        case 2: /* VRHADD */
+            GEN_NEON_INTEGER_OP(rhadd);
+            break;
+        case 3: /* Logic ops.  */
+            switch ((u << 2) | size) {
+            case 0: /* VAND */
+                gen_op_andl_T0_T1();
+                break;
+            case 1: /* BIC */
+                gen_op_bicl_T0_T1();
+                break;
+            case 2: /* VORR */
+                gen_op_orl_T0_T1();
+                break;
+            case 3: /* VORN */
+                gen_op_notl_T1();
+                gen_op_orl_T0_T1();
+                break;
+            case 4: /* VEOR */
+                gen_op_xorl_T0_T1();
+                break;
+            case 5: /* VBSL */
+                tmp = neon_load_reg(rd, pass);
+                gen_neon_bsl(cpu_T[0], cpu_T[0], cpu_T[1], tmp);
+                dead_tmp(tmp);
+                break;
+            case 6: /* VBIT */
+                tmp = neon_load_reg(rd, pass);
+                gen_neon_bsl(cpu_T[0], cpu_T[0], tmp, cpu_T[1]);
+                dead_tmp(tmp);
+                break;
+            case 7: /* VBIF */
+                tmp = neon_load_reg(rd, pass);
+                gen_neon_bsl(cpu_T[0], tmp, cpu_T[0], cpu_T[1]);
+                dead_tmp(tmp);
+                break;
+            }
+            break;
+        case 4: /* VHSUB */
+            GEN_NEON_INTEGER_OP(hsub);
+            break;
+        case 5: /* VQSUB */
+            GEN_NEON_INTEGER_OP_ENV(qsub);
+            break;
+        case 6: /* VCGT */
+            GEN_NEON_INTEGER_OP(cgt);
+            break;
+        case 7: /* VCGE */
+            GEN_NEON_INTEGER_OP(cge);
+            break;
+        case 8: /* VSHL */
+            GEN_NEON_INTEGER_OP(shl);
+            break;
+        case 9: /* VQSHL */
+            GEN_NEON_INTEGER_OP_ENV(qshl);
+            break;
+        case 10: /* VRSHL */
+            GEN_NEON_INTEGER_OP(rshl);
+            break;
+        case 11: /* VQRSHL */
+            GEN_NEON_INTEGER_OP_ENV(qrshl);
+            break;
+        case 12: /* VMAX */
+            GEN_NEON_INTEGER_OP(max);
+            break;
+        case 13: /* VMIN */
+            GEN_NEON_INTEGER_OP(min);
+            break;
+        case 14: /* VABD */
+            GEN_NEON_INTEGER_OP(abd);
+            break;
+        case 15: /* VABA */
+            GEN_NEON_INTEGER_OP(abd);
+            NEON_GET_REG(T1, rd, pass);
+            gen_neon_add(size);
+            break;
+        case 16:
+            if (!u) { /* VADD */
+                if (gen_neon_add(size))
+                    return 1;
+            } else { /* VSUB */
+                switch (size) {
+                case 0: gen_helper_neon_sub_u8(CPU_T001); break;
+                case 1: gen_helper_neon_sub_u16(CPU_T001); break;
+                case 2: gen_op_subl_T0_T1(); break;
+                default: return 1;
+                }
+            }
+            break;
+        case 17:
+            if (!u) { /* VTST */
+                switch (size) {
+                case 0: gen_helper_neon_tst_u8(CPU_T001); break;
+                case 1: gen_helper_neon_tst_u16(CPU_T001); break;
+                case 2: gen_helper_neon_tst_u32(CPU_T001); break;
+                default: return 1;
+                }
+            } else { /* VCEQ */
+                switch (size) {
+                case 0: gen_helper_neon_ceq_u8(CPU_T001); break;
+                case 1: gen_helper_neon_ceq_u16(CPU_T001); break;
+                case 2: gen_helper_neon_ceq_u32(CPU_T001); break;
+                default: return 1;
+                }
+            }
+            break;
+        case 18: /* Multiply.  */
+            switch (size) {
+            case 0: gen_helper_neon_mul_u8(CPU_T001); break;
+            case 1: gen_helper_neon_mul_u16(CPU_T001); break;
+            case 2: gen_op_mul_T0_T1(); break;
+            default: return 1;
+            }
+            NEON_GET_REG(T1, rd, pass);
+            if (u) { /* VMLS */
+                gen_neon_rsb(size);
+            } else { /* VMLA */
+                gen_neon_add(size);
+            }
+            break;
+        case 19: /* VMUL */
+            if (u) { /* polynomial */
+                gen_helper_neon_mul_p8(CPU_T001);
+            } else { /* Integer */
+                switch (size) {
+                case 0: gen_helper_neon_mul_u8(CPU_T001); break;
+                case 1: gen_helper_neon_mul_u16(CPU_T001); break;
+                case 2: gen_op_mul_T0_T1(); break;
+                default: return 1;
+                }
+            }
+            break;
+        case 20: /* VPMAX */
+            GEN_NEON_INTEGER_OP(pmax);
+            break;
+        case 21: /* VPMIN */
+            GEN_NEON_INTEGER_OP(pmin);
+            break;
+        case 22: /* Hultiply high.  */
+            if (!u) { /* VQDMULH */
+                switch (size) {
+                case 1: gen_helper_neon_qdmulh_s16(CPU_T0E01); break;
+                case 2: gen_helper_neon_qdmulh_s32(CPU_T0E01); break;
+                default: return 1;
+                }
+            } else { /* VQRDHMUL */
+                switch (size) {
+                case 1: gen_helper_neon_qrdmulh_s16(CPU_T0E01); break;
+                case 2: gen_helper_neon_qrdmulh_s32(CPU_T0E01); break;
+                default: return 1;
+                }
+            }
+            break;
+        case 23: /* VPADD */
+            if (u)
+                return 1;
+            switch (size) {
+            case 0: gen_helper_neon_padd_u8(CPU_T001); break;
+            case 1: gen_helper_neon_padd_u16(CPU_T001); break;
+            case 2: gen_op_addl_T0_T1(); break;
+            default: return 1;
+            }
+            break;
+        case 26: /* Floating point arithnetic.  */
+            switch ((u << 2) | size) {
+            case 0: /* VADD */
+                gen_helper_neon_add_f32(CPU_T001);
+                break;
+            case 2: /* VSUB */
+                gen_helper_neon_sub_f32(CPU_T001);
+                break;
+            case 4: /* VPADD */
+                gen_helper_neon_add_f32(CPU_T001);
+                break;
+            case 6: /* VABD */
+                gen_helper_neon_abd_f32(CPU_T001);
+                break;
+            default:
+                return 1;
+            }
+            break;
+        case 27: /* Float multiply.  */
+            gen_helper_neon_mul_f32(CPU_T001);
+            if (!u) {
+                NEON_GET_REG(T1, rd, pass);
+                if (size == 0) {
+                    gen_helper_neon_add_f32(CPU_T001);
+                } else {
+                    gen_helper_neon_sub_f32(cpu_T[0], cpu_T[1], cpu_T[0]);
+                }
+            }
+            break;
+        case 28: /* Float compare.  */
+            if (!u) {
+                gen_helper_neon_ceq_f32(CPU_T001);
+            } else {
+                if (size == 0)
+                    gen_helper_neon_cge_f32(CPU_T001);
+                else
+                    gen_helper_neon_cgt_f32(CPU_T001);
+            }
+            break;
+        case 29: /* Float compare absolute.  */
+            if (!u)
+                return 1;
+            if (size == 0)
+                gen_helper_neon_acge_f32(CPU_T001);
+            else
+                gen_helper_neon_acgt_f32(CPU_T001);
+            break;
+        case 30: /* Float min/max.  */
+            if (size == 0)
+                gen_helper_neon_max_f32(CPU_T001);
+            else
+                gen_helper_neon_min_f32(CPU_T001);
+            break;
+        case 31:
+            if (size == 0)
+                gen_helper_recps_f32(cpu_T[0], cpu_T[0], cpu_T[1], cpu_env);
+            else
+                gen_helper_rsqrts_f32(cpu_T[0], cpu_T[0], cpu_T[1], cpu_env);
+            break;
+        default:
+            abort();
+        }
+        /* Save the result.  For elementwise operations we can put it
+           straight into the destination register.  For pairwise operations
+           we have to be careful to avoid clobbering the source operands.  */
+        if (pairwise && rd == rm) {
+            gen_neon_movl_scratch_T0(pass);
+        } else {
+            NEON_SET_REG(T0, rd, pass);
+        }
+
+        } /* for pass */
+        if (pairwise && rd == rm) {
+            for (pass = 0; pass < (q ? 4 : 2); pass++) {
+                gen_neon_movl_T0_scratch(pass);
+                NEON_SET_REG(T0, rd, pass);
+            }
+        }
+        /* End of 3 register same size operations.  */
+    } else if (insn & (1 << 4)) {
+        if ((insn & 0x00380080) != 0) {
+            /* Two registers and shift.  */
+            op = (insn >> 8) & 0xf;
+            if (insn & (1 << 7)) {
+                /* 64-bit shift.   */
+                size = 3;
+            } else {
+                size = 2;
+                while ((insn & (1 << (size + 19))) == 0)
+                    size--;
+            }
+            shift = (insn >> 16) & ((1 << (3 + size)) - 1);
+            /* To avoid excessive dumplication of ops we implement shift
+               by immediate using the variable shift operations.  */
+            if (op < 8) {
+                /* Shift by immediate:
+                   VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VQSHL, VQSHLU.  */
+                /* Right shifts are encoded as N - shift, where N is the
+                   element size in bits.  */
+                if (op <= 4)
+                    shift = shift - (1 << (size + 3));
+                if (size == 3) {
+                    count = q + 1;
+                } else {
+                    count = q ? 4: 2;
+                }
+                switch (size) {
+                case 0:
+                    imm = (uint8_t) shift;
+                    imm |= imm << 8;
+                    imm |= imm << 16;
+                    break;
+                case 1:
+                    imm = (uint16_t) shift;
+                    imm |= imm << 16;
+                    break;
+                case 2:
+                case 3:
+                    imm = shift;
+                    break;
+                default:
+                    abort();
+                }
+
+                for (pass = 0; pass < count; pass++) {
+                    if (size == 3) {
+                        neon_load_reg64(cpu_V0, rm + pass);
+                        tcg_gen_movi_i64(cpu_V1, imm);
+                        switch (op) {
+                        case 0:  /* VSHR */
+                        case 1:  /* VSRA */
+                            if (u)
+                                gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
+                            else
+                                gen_helper_neon_shl_s64(cpu_V0, cpu_V0, cpu_V1);
+                            break;
+                        case 2: /* VRSHR */
+                        case 3: /* VRSRA */
+                            if (u)
+                                gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, cpu_V1);
+                            else
+                                gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, cpu_V1);
+                            break;
+                        case 4: /* VSRI */
+                            if (!u)
+                                return 1;
+                            gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
+                            break;
+                        case 5: /* VSHL, VSLI */
+                            gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
+                            break;
+                        case 6: /* VQSHL */
+                            if (u)
+                                gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1);
+                            else
+                                gen_helper_neon_qshl_s64(cpu_V0, cpu_env, cpu_V0, cpu_V1);
+                            break;
+                        case 7: /* VQSHLU */
+                            gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1);
+                            break;
+                        }
+                        if (op == 1 || op == 3) {
+                            /* Accumulate.  */
+                            neon_load_reg64(cpu_V0, rd + pass);
+                            tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1);
+                        } else if (op == 4 || (op == 5 && u)) {
+                            /* Insert */
+                            cpu_abort(env, "VS[LR]I.64 not implemented");
+                        }
+                        neon_store_reg64(cpu_V0, rd + pass);
+                    } else { /* size < 3 */
+                        /* Operands in T0 and T1.  */
+                        gen_op_movl_T1_im(imm);
+                        NEON_GET_REG(T0, rm, pass);
+                        switch (op) {
+                        case 0:  /* VSHR */
+                        case 1:  /* VSRA */
+                            GEN_NEON_INTEGER_OP(shl);
+                            break;
+                        case 2: /* VRSHR */
+                        case 3: /* VRSRA */
+                            GEN_NEON_INTEGER_OP(rshl);
+                            break;
+                        case 4: /* VSRI */
+                            if (!u)
+                                return 1;
+                            GEN_NEON_INTEGER_OP(shl);
+                            break;
+                        case 5: /* VSHL, VSLI */
+                            switch (size) {
+                            case 0: gen_helper_neon_shl_u8(CPU_T001); break;
+                            case 1: gen_helper_neon_shl_u16(CPU_T001); break;
+                            case 2: gen_helper_neon_shl_u32(CPU_T001); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 6: /* VQSHL */
+                            GEN_NEON_INTEGER_OP_ENV(qshl);
+                            break;
+                        case 7: /* VQSHLU */
+                            switch (size) {
+                            case 0: gen_helper_neon_qshl_u8(CPU_T0E01); break;
+                            case 1: gen_helper_neon_qshl_u16(CPU_T0E01); break;
+                            case 2: gen_helper_neon_qshl_u32(CPU_T0E01); break;
+                            default: return 1;
+                            }
+                            break;
+                        }
+
+                        if (op == 1 || op == 3) {
+                            /* Accumulate.  */
+                            NEON_GET_REG(T1, rd, pass);
+                            gen_neon_add(size);
+                        } else if (op == 4 || (op == 5 && u)) {
+                            /* Insert */
+                            switch (size) {
+                            case 0:
+                                if (op == 4)
+                                    imm = 0xff >> -shift;
+                                else
+                                    imm = (uint8_t)(0xff << shift);
+                                imm |= imm << 8;
+                                imm |= imm << 16;
+                                break;
+                            case 1:
+                                if (op == 4)
+                                    imm = 0xffff >> -shift;
+                                else
+                                    imm = (uint16_t)(0xffff << shift);
+                                imm |= imm << 16;
+                                break;
+                            case 2:
+                                if (op == 4)
+                                    imm = 0xffffffffu >> -shift;
+                                else
+                                    imm = 0xffffffffu << shift;
+                                break;
+                            default:
+                                abort();
+                            }
+                            tmp = neon_load_reg(rd, pass);
+                            tcg_gen_andi_i32(cpu_T[0], cpu_T[0], imm);
+                            tcg_gen_andi_i32(tmp, tmp, ~imm);
+                            tcg_gen_or_i32(cpu_T[0], cpu_T[0], tmp);
+                        }
+                        NEON_SET_REG(T0, rd, pass);
+                    }
+                } /* for pass */
+            } else if (op < 10) {
+                /* Shift by immediate and narrow:
+                   VSHRN, VRSHRN, VQSHRN, VQRSHRN.  */
+                shift = shift - (1 << (size + 3));
+                size++;
+                switch (size) {
+                case 1:
+                    imm = (uint16_t)shift;
+                    imm |= imm << 16;
+                    tmp2 = tcg_const_i32(imm);
+                    break;
+                case 2:
+                    imm = (uint32_t)shift;
+                    tmp2 = tcg_const_i32(imm);
+                case 3:
+                    tmp2 = tcg_const_i64(shift);
+                    break;
+                default:
+                    abort();
+                }
+
+                for (pass = 0; pass < 2; pass++) {
+                    if (size == 3) {
+                        neon_load_reg64(cpu_V0, rm + pass);
+                        if (q) {
+                          if (u)
+                            gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, tmp2);
+                          else
+                            gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, tmp2);
+                        } else {
+                          if (u)
+                            gen_helper_neon_shl_u64(cpu_V0, cpu_V0, tmp2);
+                          else
+                            gen_helper_neon_shl_s64(cpu_V0, cpu_V0, tmp2);
+                        }
+                    } else {
+                        tmp = neon_load_reg(rm + pass, 0);
+                        gen_neon_shift_narrow(size, tmp, tmp2, q, u);
+                        tcg_gen_extu_i32_i64(cpu_V0, tmp);
+                        dead_tmp(tmp);
+                        tmp = neon_load_reg(rm + pass, 1);
+                        gen_neon_shift_narrow(size, tmp, tmp2, q, u);
+                        tcg_gen_extu_i32_i64(cpu_V1, tmp);
+                        dead_tmp(tmp);
+                        tcg_gen_shli_i64(cpu_V1, cpu_V1, 32);
+                        tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
+                    }
+                    tmp = new_tmp();
+                    if (op == 8 && !u) {
+                        gen_neon_narrow(size - 1, tmp, cpu_V0);
+                    } else {
+                        if (op == 8)
+                            gen_neon_narrow_sats(size - 1, tmp, cpu_V0);
+                        else
+                            gen_neon_narrow_satu(size - 1, tmp, cpu_V0);
+                    }
+                    if (pass == 0) {
+                        tmp2 = tmp;
+                    } else {
+                        neon_store_reg(rd, 0, tmp2);
+                        neon_store_reg(rd, 1, tmp);
+                    }
+                } /* for pass */
+            } else if (op == 10) {
+                /* VSHLL */
+                if (q || size == 3)
+                    return 1;
+                tmp = neon_load_reg(rm, 0);
+                tmp2 = neon_load_reg(rm, 1);
+                for (pass = 0; pass < 2; pass++) {
+                    if (pass == 1)
+                        tmp = tmp2;
+
+                    gen_neon_widen(cpu_V0, tmp, size, u);
+
+                    if (shift != 0) {
+                        /* The shift is less than the width of the source
+                           type, so we can just shift the whole register.  */
+                        tcg_gen_shli_i64(cpu_V0, cpu_V0, shift);
+                        if (size < 2 || !u) {
+                            uint64_t imm64;
+                            if (size == 0) {
+                                imm = (0xffu >> (8 - shift));
+                                imm |= imm << 16;
+                            } else {
+                                imm = 0xffff >> (16 - shift);
+                            }
+                            imm64 = imm | (((uint64_t)imm) << 32);
+                            tcg_gen_andi_i64(cpu_V0, cpu_V0, imm64);
+                        }
+                    }
+                    neon_store_reg64(cpu_V0, rd + pass);
+                }
+            } else if (op == 15 || op == 16) {
+                /* VCVT fixed-point.  */
+                for (pass = 0; pass < (q ? 4 : 2); pass++) {
+                    tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass));
+                    if (op & 1) {
+                        if (u)
+                            gen_vfp_ulto(0, shift);
+                        else
+                            gen_vfp_slto(0, shift);
+                    } else {
+                        if (u)
+                            gen_vfp_toul(0, shift);
+                        else
+                            gen_vfp_tosl(0, shift);
+                    }
+                    tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass));
+                }
+            } else {
+                return 1;
+            }
+        } else { /* (insn & 0x00380080) == 0 */
+            int invert;
+
+            op = (insn >> 8) & 0xf;
+            /* One register and immediate.  */
+            imm = (u << 7) | ((insn >> 12) & 0x70) | (insn & 0xf);
+            invert = (insn & (1 << 5)) != 0;
+            switch (op) {
+            case 0: case 1:
+                /* no-op */
+                break;
+            case 2: case 3:
+                imm <<= 8;
+                break;
+            case 4: case 5:
+                imm <<= 16;
+                break;
+            case 6: case 7:
+                imm <<= 24;
+                break;
+            case 8: case 9:
+                imm |= imm << 16;
+                break;
+            case 10: case 11:
+                imm = (imm << 8) | (imm << 24);
+                break;
+            case 12:
+                imm = (imm < 8) | 0xff;
+                break;
+            case 13:
+                imm = (imm << 16) | 0xffff;
+                break;
+            case 14:
+                imm |= (imm << 8) | (imm << 16) | (imm << 24);
+                if (invert)
+                    imm = ~imm;
+                break;
+            case 15:
+                imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19)
+                      | ((imm & 0x40) ? (0x1f << 25) : (1 << 30));
+                break;
+            }
+            if (invert)
+                imm = ~imm;
+
+            if (op != 14 || !invert)
+                gen_op_movl_T1_im(imm);
+
+            for (pass = 0; pass < (q ? 4 : 2); pass++) {
+                if (op & 1 && op < 12) {
+                    tmp = neon_load_reg(rd, pass);
+                    if (invert) {
+                        /* The immediate value has already been inverted, so
+                           BIC becomes AND.  */
+                        tcg_gen_andi_i32(tmp, tmp, imm);
+                    } else {
+                        tcg_gen_ori_i32(tmp, tmp, imm);
+                    }
+                } else {
+                    /* VMOV, VMVN.  */
+                    tmp = new_tmp();
+                    if (op == 14 && invert) {
+                        uint32_t val;
+                        val = 0;
+                        for (n = 0; n < 4; n++) {
+                            if (imm & (1 << (n + (pass & 1) * 4)))
+                                val |= 0xff << (n * 8);
+                        }
+                        tcg_gen_movi_i32(tmp, val);
+                    } else {
+                        tcg_gen_movi_i32(tmp, imm);
+                    }
+                }
+                neon_store_reg(rd, pass, tmp);
+            }
+        }
+    } else { /* (insn & 0x00800010 == 0x00800010) */
+        if (size != 3) {
+            op = (insn >> 8) & 0xf;
+            if ((insn & (1 << 6)) == 0) {
+                /* Three registers of different lengths.  */
+                int src1_wide;
+                int src2_wide;
+                int prewiden;
+                /* prewiden, src1_wide, src2_wide */
+                static const int neon_3reg_wide[16][3] = {
+                    {1, 0, 0}, /* VADDL */
+                    {1, 1, 0}, /* VADDW */
+                    {1, 0, 0}, /* VSUBL */
+                    {1, 1, 0}, /* VSUBW */
+                    {0, 1, 1}, /* VADDHN */
+                    {0, 0, 0}, /* VABAL */
+                    {0, 1, 1}, /* VSUBHN */
+                    {0, 0, 0}, /* VABDL */
+                    {0, 0, 0}, /* VMLAL */
+                    {0, 0, 0}, /* VQDMLAL */
+                    {0, 0, 0}, /* VMLSL */
+                    {0, 0, 0}, /* VQDMLSL */
+                    {0, 0, 0}, /* Integer VMULL */
+                    {0, 0, 0}, /* VQDMULL */
+                    {0, 0, 0}  /* Polynomial VMULL */
+                };
+
+                prewiden = neon_3reg_wide[op][0];
+                src1_wide = neon_3reg_wide[op][1];
+                src2_wide = neon_3reg_wide[op][2];
+
+                if (size == 0 && (op == 9 || op == 11 || op == 13))
+                    return 1;
+
+                /* Avoid overlapping operands.  Wide source operands are
+                   always aligned so will never overlap with wide
+                   destinations in problematic ways.  */
+                if (rd == rm && !src2_wide) {
+                    NEON_GET_REG(T0, rm, 1);
+                    gen_neon_movl_scratch_T0(2);
+                } else if (rd == rn && !src1_wide) {
+                    NEON_GET_REG(T0, rn, 1);
+                    gen_neon_movl_scratch_T0(2);
+                }
+                TCGV_UNUSED(tmp3);
+                for (pass = 0; pass < 2; pass++) {
+                    if (src1_wide) {
+                        neon_load_reg64(cpu_V0, rn + pass);
+                        TCGV_UNUSED(tmp);
+                    } else {
+                        if (pass == 1 && rd == rn) {
+                            gen_neon_movl_T0_scratch(2);
+                            tmp = new_tmp();
+                            tcg_gen_mov_i32(tmp, cpu_T[0]);
+                        } else {
+                            tmp = neon_load_reg(rn, pass);
+                        }
+                        if (prewiden) {
+                            gen_neon_widen(cpu_V0, tmp, size, u);
+                        }
+                    }
+                    if (src2_wide) {
+                        neon_load_reg64(cpu_V1, rm + pass);
+                        TCGV_UNUSED(tmp2);
+                    } else {
+                        if (pass == 1 && rd == rm) {
+                            gen_neon_movl_T0_scratch(2);
+                            tmp2 = new_tmp();
+                            tcg_gen_mov_i32(tmp2, cpu_T[0]);
+                        } else {
+                            tmp2 = neon_load_reg(rm, pass);
+                        }
+                        if (prewiden) {
+                            gen_neon_widen(cpu_V1, tmp2, size, u);
+                        }
+                    }
+                    switch (op) {
+                    case 0: case 1: case 4: /* VADDL, VADDW, VADDHN, VRADDHN */
+                        gen_neon_addl(size);
+                        break;
+                    case 2: case 3: case 6: /* VSUBL, VSUBW, VSUBHL, VRSUBHL */
+                        gen_neon_subl(size);
+                        break;
+                    case 5: case 7: /* VABAL, VABDL */
+                        switch ((size << 1) | u) {
+                        case 0:
+                            gen_helper_neon_abdl_s16(cpu_V0, tmp, tmp2);
+                            break;
+                        case 1:
+                            gen_helper_neon_abdl_u16(cpu_V0, tmp, tmp2);
+                            break;
+                        case 2:
+                            gen_helper_neon_abdl_s32(cpu_V0, tmp, tmp2);
+                            break;
+                        case 3:
+                            gen_helper_neon_abdl_u32(cpu_V0, tmp, tmp2);
+                            break;
+                        case 4:
+                            gen_helper_neon_abdl_s64(cpu_V0, tmp, tmp2);
+                            break;
+                        case 5:
+                            gen_helper_neon_abdl_u64(cpu_V0, tmp, tmp2);
+                            break;
+                        default: abort();
+                        }
+                        dead_tmp(tmp2);
+                        dead_tmp(tmp);
+                        break;
+                    case 8: case 9: case 10: case 11: case 12: case 13:
+                        /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */
+                        gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
+                        break;
+                    case 14: /* Polynomial VMULL */
+                        cpu_abort(env, "Polynomial VMULL not implemented");
+
+                    default: /* 15 is RESERVED.  */
+                        return 1;
+                    }
+                    if (op == 5 || op == 13 || (op >= 8 && op <= 11)) {
+                        /* Accumulate.  */
+                        if (op == 10 || op == 11) {
+                            gen_neon_negl(cpu_V0, size);
+                        }
+
+                        if (op != 13) {
+                            neon_load_reg64(cpu_V1, rd + pass);
+                        }
+
+                        switch (op) {
+                        case 5: case 8: case 10: /* VABAL, VMLAL, VMLSL */
+                            gen_neon_addl(size);
+                            break;
+                        case 9: case 11: /* VQDMLAL, VQDMLSL */
+                            gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+                            gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
+                            break;
+                            /* Fall through.  */
+                        case 13: /* VQDMULL */
+                            gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+                            break;
+                        default:
+                            abort();
+                        }
+                        neon_store_reg64(cpu_V0, rd + pass);
+                    } else if (op == 4 || op == 6) {
+                        /* Narrowing operation.  */
+                        tmp = new_tmp();
+                        if (u) {
+                            switch (size) {
+                            case 0:
+                                gen_helper_neon_narrow_high_u8(tmp, cpu_V0);
+                                break;
+                            case 1:
+                                gen_helper_neon_narrow_high_u16(tmp, cpu_V0);
+                                break;
+                            case 2:
+                                tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+                                tcg_gen_trunc_i64_i32(tmp, cpu_V0);
+                                break;
+                            default: abort();
+                            }
+                        } else {
+                            switch (size) {
+                            case 0:
+                                gen_helper_neon_narrow_round_high_u8(tmp, cpu_V0);
+                                break;
+                            case 1:
+                                gen_helper_neon_narrow_round_high_u16(tmp, cpu_V0);
+                                break;
+                            case 2:
+                                tcg_gen_addi_i64(cpu_V0, cpu_V0, 1u << 31);
+                                tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
+                                tcg_gen_trunc_i64_i32(tmp, cpu_V0);
+                                break;
+                            default: abort();
+                            }
+                        }
+                        if (pass == 0) {
+                            tmp3 = tmp;
+                        } else {
+                            neon_store_reg(rd, 0, tmp3);
+                            neon_store_reg(rd, 1, tmp);
+                        }
+                    } else {
+                        /* Write back the result.  */
+                        neon_store_reg64(cpu_V0, rd + pass);
+                    }
+                }
+            } else {
+                /* Two registers and a scalar.  */
+                switch (op) {
+                case 0: /* Integer VMLA scalar */
+                case 1: /* Float VMLA scalar */
+                case 4: /* Integer VMLS scalar */
+                case 5: /* Floating point VMLS scalar */
+                case 8: /* Integer VMUL scalar */
+                case 9: /* Floating point VMUL scalar */
+                case 12: /* VQDMULH scalar */
+                case 13: /* VQRDMULH scalar */
+                    gen_neon_get_scalar(size, rm);
+                    gen_neon_movl_scratch_T0(0);
+                    for (pass = 0; pass < (u ? 4 : 2); pass++) {
+                        if (pass != 0)
+                            gen_neon_movl_T0_scratch(0);
+                        NEON_GET_REG(T1, rn, pass);
+                        if (op == 12) {
+                            if (size == 1) {
+                                gen_helper_neon_qdmulh_s16(CPU_T0E01);
+                            } else {
+                                gen_helper_neon_qdmulh_s32(CPU_T0E01);
+                            }
+                        } else if (op == 13) {
+                            if (size == 1) {
+                                gen_helper_neon_qrdmulh_s16(CPU_T0E01);
+                            } else {
+                                gen_helper_neon_qrdmulh_s32(CPU_T0E01);
+                            }
+                        } else if (op & 1) {
+                            gen_helper_neon_mul_f32(CPU_T001);
+                        } else {
+                            switch (size) {
+                            case 0: gen_helper_neon_mul_u8(CPU_T001); break;
+                            case 1: gen_helper_neon_mul_u16(CPU_T001); break;
+                            case 2: gen_op_mul_T0_T1(); break;
+                            default: return 1;
+                            }
+                        }
+                        if (op < 8) {
+                            /* Accumulate.  */
+                            NEON_GET_REG(T1, rd, pass);
+                            switch (op) {
+                            case 0:
+                                gen_neon_add(size);
+                                break;
+                            case 1:
+                                gen_helper_neon_add_f32(CPU_T001);
+                                break;
+                            case 4:
+                                gen_neon_rsb(size);
+                                break;
+                            case 5:
+                                gen_helper_neon_sub_f32(cpu_T[0], cpu_T[1], cpu_T[0]);
+                                break;
+                            default:
+                                abort();
+                            }
+                        }
+                        NEON_SET_REG(T0, rd, pass);
+                    }
+                    break;
+                case 2: /* VMLAL sclar */
+                case 3: /* VQDMLAL scalar */
+                case 6: /* VMLSL scalar */
+                case 7: /* VQDMLSL scalar */
+                case 10: /* VMULL scalar */
+                case 11: /* VQDMULL scalar */
+                    if (size == 0 && (op == 3 || op == 7 || op == 11))
+                        return 1;
+
+                    gen_neon_get_scalar(size, rm);
+                    NEON_GET_REG(T1, rn, 1);
+
+                    for (pass = 0; pass < 2; pass++) {
+                        if (pass == 0) {
+                            tmp = neon_load_reg(rn, 0);
+                        } else {
+                            tmp = new_tmp();
+                            tcg_gen_mov_i32(tmp, cpu_T[1]);
+                        }
+                        tmp2 = new_tmp();
+                        tcg_gen_mov_i32(tmp2, cpu_T[0]);
+                        gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
+                        if (op == 6 || op == 7) {
+                            gen_neon_negl(cpu_V0, size);
+                        }
+                        if (op != 11) {
+                            neon_load_reg64(cpu_V1, rd + pass);
+                        }
+                        switch (op) {
+                        case 2: case 6:
+                            gen_neon_addl(size);
+                            break;
+                        case 3: case 7:
+                            gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+                            gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
+                            break;
+                        case 10:
+                            /* no-op */
+                            break;
+                        case 11:
+                            gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
+                            break;
+                        default:
+                            abort();
+                        }
+                        neon_store_reg64(cpu_V0, rd + pass);
+                    }
+                    break;
+                default: /* 14 and 15 are RESERVED */
+                    return 1;
+                }
+            }
+        } else { /* size == 3 */
+            if (!u) {
+                /* Extract.  */
+                imm = (insn >> 8) & 0xf;
+                count = q + 1;
+
+                if (imm > 7 && !q)
+                    return 1;
+
+                if (imm == 0) {
+                    neon_load_reg64(cpu_V0, rn);
+                    if (q) {
+                        neon_load_reg64(cpu_V1, rn + 1);
+                    }
+                } else if (imm == 8) {
+                    neon_load_reg64(cpu_V0, rn + 1);
+                    if (q) {
+                        neon_load_reg64(cpu_V1, rm);
+                    }
+                } else if (q) {
+                    tmp = tcg_temp_new(TCG_TYPE_I64);
+                    if (imm < 8) {
+                        neon_load_reg64(cpu_V0, rn);
+                        neon_load_reg64(tmp, rn + 1);
+                    } else {
+                        neon_load_reg64(cpu_V0, rn + 1);
+                        neon_load_reg64(tmp, rm);
+                    }
+                    tcg_gen_shri_i64(cpu_V0, cpu_V0, (imm & 7) * 8);
+                    tcg_gen_shli_i64(cpu_V1, tmp, 64 - ((imm & 7) * 8));
+                    tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
+                    if (imm < 8) {
+                        neon_load_reg64(cpu_V1, rm);
+                    } else {
+                        neon_load_reg64(cpu_V1, rm + 1);
+                        imm -= 8;
+                    }
+                    tcg_gen_shli_i64(cpu_V1, cpu_V1, 64 - (imm * 8));
+                    tcg_gen_shri_i64(tmp, tmp, imm * 8);
+                    tcg_gen_or_i64(cpu_V1, cpu_V1, tmp);
+                } else {
+                    neon_load_reg64(cpu_V0, rn);
+                    tcg_gen_shri_i32(cpu_V0, cpu_V0, imm * 8);
+                    neon_load_reg64(cpu_V1, rm);
+                    tcg_gen_shli_i32(cpu_V1, cpu_V1, 64 - (imm * 8));
+                    tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
+                }
+                neon_store_reg64(cpu_V0, rd);
+                if (q) {
+                    neon_store_reg64(cpu_V1, rd + 1);
+                }
+            } else if ((insn & (1 << 11)) == 0) {
+                /* Two register misc.  */
+                op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf);
+                size = (insn >> 18) & 3;
+                switch (op) {
+                case 0: /* VREV64 */
+                    if (size == 3)
+                        return 1;
+                    for (pass = 0; pass < (q ? 2 : 1); pass++) {
+                        NEON_GET_REG(T0, rm, pass * 2);
+                        NEON_GET_REG(T1, rm, pass * 2 + 1);
+                        switch (size) {
+                        case 0: tcg_gen_bswap_i32(cpu_T[0], cpu_T[0]); break;
+                        case 1: gen_swap_half(cpu_T[0]); break;
+                        case 2: /* no-op */ break;
+                        default: abort();
+                        }
+                        NEON_SET_REG(T0, rd, pass * 2 + 1);
+                        if (size == 2) {
+                            NEON_SET_REG(T1, rd, pass * 2);
+                        } else {
+                            gen_op_movl_T0_T1();
+                            switch (size) {
+                            case 0: tcg_gen_bswap_i32(cpu_T[0], cpu_T[0]); break;
+                            case 1: gen_swap_half(cpu_T[0]); break;
+                            default: abort();
+                            }
+                            NEON_SET_REG(T0, rd, pass * 2);
+                        }
+                    }
+                    break;
+                case 4: case 5: /* VPADDL */
+                case 12: case 13: /* VPADAL */
+                    if (size == 3)
+                        return 1;
+                    for (pass = 0; pass < q + 1; pass++) {
+                        tmp = neon_load_reg(rm, pass * 2);
+                        gen_neon_widen(cpu_V0, tmp, size, op & 1);
+                        tmp = neon_load_reg(rm, pass * 2 + 1);
+                        gen_neon_widen(cpu_V1, tmp, size, op & 1);
+                        switch (size) {
+                        case 0: gen_helper_neon_paddl_u16(CPU_V001); break;
+                        case 1: gen_helper_neon_paddl_u32(CPU_V001); break;
+                        case 2: tcg_gen_add_i64(CPU_V001); break;
+                        default: abort();
+                        }
+                        if (op >= 12) {
+                            /* Accumulate.  */
+                            neon_load_reg64(cpu_V1, rd + pass);
+                            gen_neon_addl(size);
+                        }
+                        neon_store_reg64(cpu_V0, rd + pass);
+                    }
+                    break;
+                case 33: /* VTRN */
+                    if (size == 2) {
+                        for (n = 0; n < (q ? 4 : 2); n += 2) {
+                            NEON_GET_REG(T0, rm, n);
+                            NEON_GET_REG(T1, rd, n + 1);
+                            NEON_SET_REG(T1, rm, n);
+                            NEON_SET_REG(T0, rd, n + 1);
+                        }
+                    } else {
+                        goto elementwise;
+                    }
+                    break;
+                case 34: /* VUZP */
+                    /* Reg  Before       After
+                       Rd   A3 A2 A1 A0  B2 B0 A2 A0
+                       Rm   B3 B2 B1 B0  B3 B1 A3 A1
+                     */
+                    if (size == 3)
+                        return 1;
+                    gen_neon_unzip(rd, q, 0, size);
+                    gen_neon_unzip(rm, q, 4, size);
+                    if (q) {
+                        static int unzip_order_q[8] =
+                            {0, 2, 4, 6, 1, 3, 5, 7};
+                        for (n = 0; n < 8; n++) {
+                            int reg = (n < 4) ? rd : rm;
+                            gen_neon_movl_T0_scratch(unzip_order_q[n]);
+                            NEON_SET_REG(T0, reg, n % 4);
+                        }
+                    } else {
+                        static int unzip_order[4] =
+                            {0, 4, 1, 5};
+                        for (n = 0; n < 4; n++) {
+                            int reg = (n < 2) ? rd : rm;
+                            gen_neon_movl_T0_scratch(unzip_order[n]);
+                            NEON_SET_REG(T0, reg, n % 2);
+                        }
+                    }
+                    break;
+                case 35: /* VZIP */
+                    /* Reg  Before       After
+                       Rd   A3 A2 A1 A0  B1 A1 B0 A0
+                       Rm   B3 B2 B1 B0  B3 A3 B2 A2
+                     */
+                    if (size == 3)
+                        return 1;
+                    count = (q ? 4 : 2);
+                    for (n = 0; n < count; n++) {
+                        NEON_GET_REG(T0, rd, n);
+                        NEON_GET_REG(T1, rd, n);
+                        switch (size) {
+                        case 0: gen_helper_neon_zip_u8(); break;
+                        case 1: gen_helper_neon_zip_u16(); break;
+                        case 2: /* no-op */; break;
+                        default: abort();
+                        }
+                        gen_neon_movl_scratch_T0(n * 2);
+                        gen_neon_movl_scratch_T1(n * 2 + 1);
+                    }
+                    for (n = 0; n < count * 2; n++) {
+                        int reg = (n < count) ? rd : rm;
+                        gen_neon_movl_T0_scratch(n);
+                        NEON_SET_REG(T0, reg, n % count);
+                    }
+                    break;
+                case 36: case 37: /* VMOVN, VQMOVUN, VQMOVN */
+                    if (size == 3)
+                        return 1;
+                    TCGV_UNUSED(tmp2);
+                    for (pass = 0; pass < 2; pass++) {
+                        neon_load_reg64(cpu_V0, rm + pass);
+                        tmp = new_tmp();
+                        if (op == 36 && q == 0) {
+                            gen_neon_narrow(size, tmp, cpu_V0);
+                        } else if (q) {
+                            gen_neon_narrow_satu(size, tmp, cpu_V0);
+                        } else {
+                            gen_neon_narrow_sats(size, tmp, cpu_V0);
+                        }
+                        if (pass == 0) {
+                            tmp2 = tmp;
+                        } else {
+                            neon_store_reg(rd, 0, tmp2);
+                            neon_store_reg(rd, 1, tmp);
+                        }
+                    }
+                    break;
+                case 38: /* VSHLL */
+                    if (q || size == 3)
+                        return 1;
+                    tmp = neon_load_reg(rm, 0);
+                    tmp2 = neon_load_reg(rm, 1);
+                    for (pass = 0; pass < 2; pass++) {
+                        if (pass == 1)
+                            tmp = tmp2;
+                        gen_neon_widen(cpu_V0, tmp, size, 1);
+                        neon_store_reg64(cpu_V0, rd + pass);
+                    }
+                    break;
+                default:
+                elementwise:
+                    for (pass = 0; pass < (q ? 4 : 2); pass++) {
+                        if (op == 30 || op == 31 || op >= 58) {
+                            tcg_gen_ld_f32(cpu_F0s, cpu_env,
+                                           neon_reg_offset(rm, pass));
+                        } else {
+                            NEON_GET_REG(T0, rm, pass);
+                        }
+                        switch (op) {
+                        case 1: /* VREV32 */
+                            switch (size) {
+                            case 0: tcg_gen_bswap_i32(cpu_T[0], cpu_T[0]); break;
+                            case 1: gen_swap_half(cpu_T[0]); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 2: /* VREV16 */
+                            if (size != 0)
+                                return 1;
+                            gen_rev16(cpu_T[0]);
+                            break;
+                        case 8: /* CLS */
+                            switch (size) {
+                            case 0: gen_helper_neon_cls_s8(cpu_T[0], cpu_T[0]); break;
+                            case 1: gen_helper_neon_cls_s16(cpu_T[0], cpu_T[0]); break;
+                            case 2: gen_helper_neon_cls_s32(cpu_T[0], cpu_T[0]); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 9: /* CLZ */
+                            switch (size) {
+                            case 0: gen_helper_neon_clz_u8(cpu_T[0], cpu_T[0]); break;
+                            case 1: gen_helper_neon_clz_u16(cpu_T[0], cpu_T[0]); break;
+                            case 2: gen_helper_clz(cpu_T[0], cpu_T[0]); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 10: /* CNT */
+                            if (size != 0)
+                                return 1;
+                            gen_helper_neon_cnt_u8(cpu_T[0], cpu_T[0]);
+                            break;
+                        case 11: /* VNOT */
+                            if (size != 0)
+                                return 1;
+                            gen_op_notl_T0();
+                            break;
+                        case 14: /* VQABS */
+                            switch (size) {
+                            case 0: gen_helper_neon_qabs_s8(cpu_T[0], cpu_env, cpu_T[0]); break;
+                            case 1: gen_helper_neon_qabs_s16(cpu_T[0], cpu_env, cpu_T[0]); break;
+                            case 2: gen_helper_neon_qabs_s32(cpu_T[0], cpu_env, cpu_T[0]); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 15: /* VQNEG */
+                            switch (size) {
+                            case 0: gen_helper_neon_qneg_s8(cpu_T[0], cpu_env, cpu_T[0]); break;
+                            case 1: gen_helper_neon_qneg_s16(cpu_T[0], cpu_env, cpu_T[0]); break;
+                            case 2: gen_helper_neon_qneg_s32(cpu_T[0], cpu_env, cpu_T[0]); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 16: case 19: /* VCGT #0, VCLE #0 */
+                            gen_op_movl_T1_im(0);
+                            switch(size) {
+                            case 0: gen_helper_neon_cgt_s8(CPU_T001); break;
+                            case 1: gen_helper_neon_cgt_s16(CPU_T001); break;
+                            case 2: gen_helper_neon_cgt_s32(CPU_T001); break;
+                            default: return 1;
+                            }
+                            if (op == 19)
+                                gen_op_notl_T0();
+                            break;
+                        case 17: case 20: /* VCGE #0, VCLT #0 */
+                            gen_op_movl_T1_im(0);
+                            switch(size) {
+                            case 0: gen_helper_neon_cge_s8(CPU_T001); break;
+                            case 1: gen_helper_neon_cge_s16(CPU_T001); break;
+                            case 2: gen_helper_neon_cge_s32(CPU_T001); break;
+                            default: return 1;
+                            }
+                            if (op == 20)
+                                gen_op_notl_T0();
+                            break;
+                        case 18: /* VCEQ #0 */
+                            gen_op_movl_T1_im(0);
+                            switch(size) {
+                            case 0: gen_helper_neon_ceq_u8(CPU_T001); break;
+                            case 1: gen_helper_neon_ceq_u16(CPU_T001); break;
+                            case 2: gen_helper_neon_ceq_u32(CPU_T001); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 22: /* VABS */
+                            switch(size) {
+                            case 0: gen_helper_neon_abs_s8(cpu_T[0], cpu_T[0]); break;
+                            case 1: gen_helper_neon_abs_s16(cpu_T[0], cpu_T[0]); break;
+                            case 2: tcg_gen_abs_i32(cpu_T[0], cpu_T[0]); break;
+                            default: return 1;
+                            }
+                            break;
+                        case 23: /* VNEG */
+                            gen_op_movl_T1_im(0);
+                            if (size == 3)
+                                return 1;
+                            gen_neon_rsb(size);
+                            break;
+                        case 24: case 27: /* Float VCGT #0, Float VCLE #0 */
+                            gen_op_movl_T1_im(0);
+                            gen_helper_neon_cgt_f32(CPU_T001);
+                            if (op == 27)
+                                gen_op_notl_T0();
+                            break;
+                        case 25: case 28: /* Float VCGE #0, Float VCLT #0 */
+                            gen_op_movl_T1_im(0);
+                            gen_helper_neon_cge_f32(CPU_T001);
+                            if (op == 28)
+                                gen_op_notl_T0();
+                            break;
+                        case 26: /* Float VCEQ #0 */
+                            gen_op_movl_T1_im(0);
+                            gen_helper_neon_ceq_f32(CPU_T001);
+                            break;
+                        case 30: /* Float VABS */
+                            gen_vfp_abs(0);
+                            break;
+                        case 31: /* Float VNEG */
+                            gen_vfp_neg(0);
+                            break;
+                        case 32: /* VSWP */
+                            NEON_GET_REG(T1, rd, pass);
+                            NEON_SET_REG(T1, rm, pass);
+                            break;
+                        case 33: /* VTRN */
+                            NEON_GET_REG(T1, rd, pass);
+                            switch (size) {
+                            case 0: gen_helper_neon_trn_u8(); break;
+                            case 1: gen_helper_neon_trn_u16(); break;
+                            case 2: abort();
+                            default: return 1;
+                            }
+                            NEON_SET_REG(T1, rm, pass);
+                            break;
+                        case 56: /* Integer VRECPE */
+                            gen_helper_recpe_u32(cpu_T[0], cpu_T[0], cpu_env);
+                            break;
+                        case 57: /* Integer VRSQRTE */
+                            gen_helper_rsqrte_u32(cpu_T[0], cpu_T[0], cpu_env);
+                            break;
+                        case 58: /* Float VRECPE */
+                            gen_helper_recpe_f32(cpu_F0s, cpu_F0s, cpu_env);
+                            break;
+                        case 59: /* Float VRSQRTE */
+                            gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env);
+                            break;
+                        case 60: /* VCVT.F32.S32 */
+                            gen_vfp_tosiz(0);
+                            break;
+                        case 61: /* VCVT.F32.U32 */
+                            gen_vfp_touiz(0);
+                            break;
+                        case 62: /* VCVT.S32.F32 */
+                            gen_vfp_sito(0);
+                            break;
+                        case 63: /* VCVT.U32.F32 */
+                            gen_vfp_uito(0);
+                            break;
+                        default:
+                            /* Reserved: 21, 29, 39-56 */
+                            return 1;
+                        }
+                        if (op == 30 || op == 31 || op >= 58) {
+                            tcg_gen_st_f32(cpu_F0s, cpu_env,
+                                           neon_reg_offset(rd, pass));
+                        } else {
+                            NEON_SET_REG(T0, rd, pass);
+                        }
+                    }
+                    break;
+                }
+            } else if ((insn & (1 << 10)) == 0) {
+                /* VTBL, VTBX.  */
+                n = (insn >> 5) & 0x18;
+                if (insn & (1 << 6)) {
+                    tmp = neon_load_reg(rd, 0);
+                } else {
+                    tmp = new_tmp();
+                    tcg_gen_movi_i32(tmp, 0);
+                }
+                tmp2 = neon_load_reg(rm, 0);
+                gen_helper_neon_tbl(tmp2, tmp2, tmp, tcg_const_i32(rn),
+                                    tcg_const_i32(n));
+                if (insn & (1 << 6)) {
+                    tmp = neon_load_reg(rd, 1);
+                } else {
+                    tmp = new_tmp();
+                    tcg_gen_movi_i32(tmp, 0);
+                }
+                tmp3 = neon_load_reg(rm, 1);
+                gen_helper_neon_tbl(tmp3, tmp3, tmp, tcg_const_i32(rn),
+                                    tcg_const_i32(n));
+                neon_store_reg(rd, 0, tmp2);
+                neon_store_reg(rd, 1, tmp2);
+            } else if ((insn & 0x380) == 0) {
+                /* VDUP */
+                if (insn & (1 << 19)) {
+                    NEON_SET_REG(T0, rm, 1);
+                } else {
+                    NEON_SET_REG(T0, rm, 0);
+                }
+                if (insn & (1 << 16)) {
+                    gen_neon_dup_u8(cpu_T[0], ((insn >> 17) & 3) * 8);
+                } else if (insn & (1 << 17)) {
+                    if ((insn >> 18) & 1)
+                        gen_neon_dup_high16(cpu_T[0]);
+                    else
+                        gen_neon_dup_low16(cpu_T[0]);
+                }
+                for (pass = 0; pass < (q ? 4 : 2); pass++) {
+                    NEON_SET_REG(T0, rd, pass);
+                }
+            } else {
+                return 1;
+            }
+        }
+    }
+    return 0;
+}
+
+static int disas_coproc_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+    int cpnum;
+
+    cpnum = (insn >> 8) & 0xf;
+    if (arm_feature(env, ARM_FEATURE_XSCALE)
+	    && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum)))
+	return 1;
+
+    switch (cpnum) {
+      case 0:
+      case 1:
+	if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+	    return disas_iwmmxt_insn(env, s, insn);
+	} else if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+	    return disas_dsp_insn(env, s, insn);
+	}
+	return 1;
+    case 10:
+    case 11:
+	return disas_vfp_insn (env, s, insn);
+    case 15:
+	return disas_cp15_insn (env, s, insn);
+    default:
+	/* Unknown coprocessor.  See if the board has hooked it.  */
+	return disas_cp_insn (env, s, insn);
+    }
+}
+
+
+/* Store a 64-bit value to a register pair.  Clobbers val.  */
+static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv val)
+{
+    TCGv tmp;
+    tmp = new_tmp();
+    tcg_gen_trunc_i64_i32(tmp, val);
+    store_reg(s, rlow, tmp);
+    tmp = new_tmp();
+    tcg_gen_shri_i64(val, val, 32);
+    tcg_gen_trunc_i64_i32(tmp, val);
+    store_reg(s, rhigh, tmp);
+}
+
+/* load a 32-bit value from a register and perform a 64-bit accumulate.  */
+static void gen_addq_lo(DisasContext *s, TCGv val, int rlow)
+{
+    TCGv tmp;
+    TCGv tmp2;
+
+    /* Load 64-bit value rd:rn.  */
+    tmp = tcg_temp_new(TCG_TYPE_I64);
+    tmp2 = load_reg(s, rlow);
+    tcg_gen_extu_i32_i64(tmp, tmp2);
+    dead_tmp(tmp2);
+    tcg_gen_add_i64(val, val, tmp);
+}
+
+/* load and add a 64-bit value from a register pair.  */
+static void gen_addq(DisasContext *s, TCGv val, int rlow, int rhigh)
+{
+    TCGv tmp;
+    TCGv tmp2;
+
+    /* Load 64-bit value rd:rn.  */
+    tmp = tcg_temp_new(TCG_TYPE_I64);
+    tmp2 = load_reg(s, rhigh);
+    tcg_gen_extu_i32_i64(tmp, tmp2);
+    dead_tmp(tmp2);
+    tcg_gen_shli_i64(tmp, tmp, 32);
+    tcg_gen_add_i64(val, val, tmp);
+
+    tmp2 = load_reg(s, rlow);
+    tcg_gen_extu_i32_i64(tmp, tmp2);
+    dead_tmp(tmp2);
+    tcg_gen_add_i64(val, val, tmp);
+}
+
+/* Set N and Z flags from a 64-bit value.  */
+static void gen_logicq_cc(TCGv val)
+{
+    TCGv tmp = new_tmp();
+    gen_helper_logicq_cc(tmp, val);
+    gen_logic_CC(tmp);
+    dead_tmp(tmp);
+}
+
+static void disas_arm_insn(CPUState * env, DisasContext *s)
+{
+    unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;
+    TCGv tmp;
+    TCGv tmp2;
+    TCGv tmp3;
+    TCGv addr;
+#ifdef CONFIG_TRACE
+    int  ticks = 0;
+#endif
+
+    insn = ldl_code(s->pc);
+#ifdef CONFIG_TRACE
+    if (tracing) {
+        trace_add_insn(insn);
+        ticks = get_insn_ticks_arm(insn);
+        gen_helper_traceInsn();
+    }
+#endif
+    s->pc += 4;
+
+    /* M variants do not implement ARM mode.  */
+    if (IS_M(env))
+        goto illegal_op;
+    cond = insn >> 28;
+    if (cond == 0xf){
+#ifdef CONFIG_TRACE
+        if (tracing) {
+            gen_traceTicks(ticks);
+        }
+#endif
+        /* Unconditional instructions.  */
+        if (((insn >> 25) & 7) == 1) {
+            /* NEON Data processing.  */
+            if (!arm_feature(env, ARM_FEATURE_NEON))
+                goto illegal_op;
+
+            if (disas_neon_data_insn(env, s, insn))
+                goto illegal_op;
+            return;
+        }
+        if ((insn & 0x0f100000) == 0x04000000) {
+            /* NEON load/store.  */
+            if (!arm_feature(env, ARM_FEATURE_NEON))
+                goto illegal_op;
+
+            if (disas_neon_ls_insn(env, s, insn))
+                goto illegal_op;
+            return;
+        }
+        if ((insn & 0x0d70f000) == 0x0550f000)
+            return; /* PLD */
+        else if ((insn & 0x0ffffdff) == 0x01010000) {
+            ARCH(6);
+            /* setend */
+            if (insn & (1 << 9)) {
+                /* BE8 mode not implemented.  */
+                goto illegal_op;
+            }
+            return;
+        } else if ((insn & 0x0fffff00) == 0x057ff000) {
+            switch ((insn >> 4) & 0xf) {
+            case 1: /* clrex */
+                ARCH(6K);
+                gen_helper_clrex(cpu_env);
+                return;
+            case 4: /* dsb */
+            case 5: /* dmb */
+            case 6: /* isb */
+                ARCH(7);
+                /* We don't emulate caches so these are a no-op.  */
+                return;
+            default:
+                goto illegal_op;
+            }
+        } else if ((insn & 0x0e5fffe0) == 0x084d0500) {
+            /* srs */
+            uint32_t offset;
+            if (IS_USER(s))
+                goto illegal_op;
+            ARCH(6);
+            op1 = (insn & 0x1f);
+            if (op1 == (env->uncached_cpsr & CPSR_M)) {
+                addr = load_reg(s, 13);
+            } else {
+                addr = new_tmp();
+                gen_helper_get_r13_banked(addr, cpu_env, tcg_const_i32(op1));
+            }
+            i = (insn >> 23) & 3;
+            switch (i) {
+            case 0: offset = -4; break; /* DA */
+            case 1: offset = -8; break; /* DB */
+            case 2: offset = 0; break; /* IA */
+            case 3: offset = 4; break; /* IB */
+            default: abort();
+            }
+            if (offset)
+                tcg_gen_addi_i32(addr, addr, offset);
+            tmp = load_reg(s, 14);
+            gen_st32(tmp, addr, 0);
+            tmp = new_tmp();
+            gen_helper_cpsr_read(tmp);
+            tcg_gen_addi_i32(addr, addr, 4);
+            gen_st32(tmp, addr, 0);
+            if (insn & (1 << 21)) {
+                /* Base writeback.  */
+                switch (i) {
+                case 0: offset = -8; break;
+                case 1: offset = -4; break;
+                case 2: offset = 4; break;
+                case 3: offset = 0; break;
+                default: abort();
+                }
+                if (offset)
+                    tcg_gen_addi_i32(addr, tmp, offset);
+                if (op1 == (env->uncached_cpsr & CPSR_M)) {
+                    gen_movl_reg_T1(s, 13);
+                } else {
+                    gen_helper_set_r13_banked(cpu_env, tcg_const_i32(op1), cpu_T[1]);
+                }
+            } else {
+                dead_tmp(addr);
+            }
+        } else if ((insn & 0x0e5fffe0) == 0x081d0a00) {
+            /* rfe */
+            uint32_t offset;
+            if (IS_USER(s))
+                goto illegal_op;
+            ARCH(6);
+            rn = (insn >> 16) & 0xf;
+            addr = load_reg(s, rn);
+            i = (insn >> 23) & 3;
+            switch (i) {
+            case 0: offset = -4; break; /* DA */
+            case 1: offset = -8; break; /* DB */
+            case 2: offset = 0; break; /* IA */
+            case 3: offset = 4; break; /* IB */
+            default: abort();
+            }
+            if (offset)
+                tcg_gen_addi_i32(addr, addr, offset);
+            /* Load PC into tmp and CPSR into tmp2.  */
+            tmp = gen_ld32(addr, 0);
+            tcg_gen_addi_i32(addr, addr, 4);
+            tmp2 = gen_ld32(addr, 0);
+            if (insn & (1 << 21)) {
+                /* Base writeback.  */
+                switch (i) {
+                case 0: offset = -8; break;
+                case 1: offset = -4; break;
+                case 2: offset = 4; break;
+                case 3: offset = 0; break;
+                default: abort();
+                }
+                if (offset)
+                    tcg_gen_addi_i32(addr, addr, offset);
+                store_reg(s, rn, addr);
+            } else {
+                dead_tmp(addr);
+            }
+            gen_rfe(s, tmp, tmp2);
+        } else if ((insn & 0x0e000000) == 0x0a000000) {
+            /* branch link and change to thumb (blx <offset>) */
+            int32_t offset;
+
+            val = (uint32_t)s->pc;
+            tmp = new_tmp();
+            tcg_gen_movi_i32(tmp, val);
+            store_reg(s, 14, tmp);
+            /* Sign-extend the 24-bit offset */
+            offset = (((int32_t)insn) << 8) >> 8;
+            /* offset * 4 + bit24 * 2 + (thumb bit) */
+            val += (offset << 2) | ((insn >> 23) & 2) | 1;
+            /* pipeline offset */
+            val += 4;
+            gen_bx_im(s, val);
+            return;
+        } else if ((insn & 0x0e000f00) == 0x0c000100) {
+            if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+                /* iWMMXt register transfer.  */
+                if (env->cp15.c15_cpar & (1 << 1))
+                    if (!disas_iwmmxt_insn(env, s, insn))
+                        return;
+            }
+        } else if ((insn & 0x0fe00000) == 0x0c400000) {
+            /* Coprocessor double register transfer.  */
+        } else if ((insn & 0x0f000010) == 0x0e000010) {
+            /* Additional coprocessor register transfer.  */
+        } else if ((insn & 0x0ff10020) == 0x01000000) {
+            uint32_t mask;
+            uint32_t val;
+            /* cps (privileged) */
+            if (IS_USER(s))
+                return;
+            mask = val = 0;
+            if (insn & (1 << 19)) {
+                if (insn & (1 << 8))
+                    mask |= CPSR_A;
+                if (insn & (1 << 7))
+                    mask |= CPSR_I;
+                if (insn & (1 << 6))
+                    mask |= CPSR_F;
+                if (insn & (1 << 18))
+                    val |= mask;
+            }
+            if (insn & (1 << 17)) {
+                mask |= CPSR_M;
+                val |= (insn & 0x1f);
+            }
+            if (mask) {
+                gen_op_movl_T0_im(val);
+                gen_set_psr_T0(s, mask, 0);
+            }
+            return;
+        }
+        goto illegal_op;
+    }
+    if (cond != 0xe) {
+#ifdef CONFIG_TRACE
+        if (tracing) {
+            /* a non-executed conditional instruction takes */
+            /* only 1 cycle */
+            gen_traceTicks(1);
+            ticks -= 1;
+        }
+#endif
+        /* if not always execute, we generate a conditional jump to
+           next instruction */
+        s->condlabel = gen_new_label();
+        gen_test_cc(cond ^ 1, s->condlabel);
+        s->condjmp = 1;
+    }
+#ifdef CONFIG_TRACE
+    if (tracing && ticks > 0) {
+        gen_traceTicks(ticks);
+    }
+#endif
+    if ((insn & 0x0f900000) == 0x03000000) {
+        if ((insn & (1 << 21)) == 0) {
+            ARCH(6T2);
+            rd = (insn >> 12) & 0xf;
+            val = ((insn >> 4) & 0xf000) | (insn & 0xfff);
+            if ((insn & (1 << 22)) == 0) {
+                /* MOVW */
+                tmp = new_tmp();
+                tcg_gen_movi_i32(tmp, val);
+            } else {
+                /* MOVT */
+                tmp = load_reg(s, rd);
+                tcg_gen_ext16u_i32(tmp, tmp);
+                tcg_gen_ori_i32(tmp, tmp, val << 16);
+            }
+            store_reg(s, rd, tmp);
+        } else {
+            if (((insn >> 12) & 0xf) != 0xf)
+                goto illegal_op;
+            if (((insn >> 16) & 0xf) == 0) {
+                gen_nop_hint(s, insn & 0xff);
+            } else {
+                /* CPSR = immediate */
+                val = insn & 0xff;
+                shift = ((insn >> 8) & 0xf) * 2;
+                if (shift)
+                    val = (val >> shift) | (val << (32 - shift));
+                gen_op_movl_T0_im(val);
+                i = ((insn & (1 << 22)) != 0);
+                if (gen_set_psr_T0(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i))
+                    goto illegal_op;
+            }
+        }
+    } else if ((insn & 0x0f900000) == 0x01000000
+               && (insn & 0x00000090) != 0x00000090) {
+        /* miscellaneous instructions */
+        op1 = (insn >> 21) & 3;
+        sh = (insn >> 4) & 0xf;
+        rm = insn & 0xf;
+        switch (sh) {
+        case 0x0: /* move program status register */
+            if (op1 & 1) {
+                /* PSR = reg */
+                gen_movl_T0_reg(s, rm);
+                i = ((op1 & 2) != 0);
+                if (gen_set_psr_T0(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i))
+                    goto illegal_op;
+            } else {
+                /* reg = PSR */
+                rd = (insn >> 12) & 0xf;
+                if (op1 & 2) {
+                    if (IS_USER(s))
+                        goto illegal_op;
+                    tmp = load_cpu_field(spsr);
+                } else {
+                    tmp = new_tmp();
+                    gen_helper_cpsr_read(tmp);
+                }
+                store_reg(s, rd, tmp);
+            }
+            break;
+        case 0x1:
+            if (op1 == 1) {
+                /* branch/exchange thumb (bx).  */
+                tmp = load_reg(s, rm);
+                gen_bx(s, tmp);
+            } else if (op1 == 3) {
+                /* clz */
+                rd = (insn >> 12) & 0xf;
+                tmp = load_reg(s, rm);
+                gen_helper_clz(tmp, tmp);
+                store_reg(s, rd, tmp);
+            } else {
+                goto illegal_op;
+            }
+            break;
+        case 0x2:
+            if (op1 == 1) {
+                ARCH(5J); /* bxj */
+                /* Trivial implementation equivalent to bx.  */
+                tmp = load_reg(s, rm);
+                gen_bx(s, tmp);
+            } else {
+                goto illegal_op;
+            }
+            break;
+        case 0x3:
+            if (op1 != 1)
+              goto illegal_op;
+
+            /* branch link/exchange thumb (blx) */
+            tmp = load_reg(s, rm);
+            tmp2 = new_tmp();
+            tcg_gen_movi_i32(tmp2, s->pc);
+            store_reg(s, 14, tmp2);
+            gen_bx(s, tmp);
+            break;
+        case 0x5: /* saturating add/subtract */
+            rd = (insn >> 12) & 0xf;
+            rn = (insn >> 16) & 0xf;
+            tmp = load_reg(s, rn);
+            tmp2 = load_reg(s, rn);
+            if (op1 & 2)
+                gen_helper_double_saturate(tmp2, tmp2);
+            if (op1 & 1)
+                gen_helper_sub_saturate(tmp, tmp, tmp2);
+            else
+                gen_helper_add_saturate(tmp, tmp, tmp2);
+            dead_tmp(tmp2);
+            store_reg(s, rd, tmp);
+            break;
+        case 7: /* bkpt */
+            gen_set_condexec(s);
+            gen_set_pc_im(s->pc - 4);
+            gen_exception(EXCP_BKPT);
+            s->is_jmp = DISAS_JUMP;
+            break;
+        case 0x8: /* signed multiply */
+        case 0xa:
+        case 0xc:
+        case 0xe:
+            rs = (insn >> 8) & 0xf;
+            rn = (insn >> 12) & 0xf;
+            rd = (insn >> 16) & 0xf;
+            if (op1 == 1) {
+                /* (32 * 16) >> 16 */
+                tmp = load_reg(s, rm);
+                tmp2 = load_reg(s, rs);
+                if (sh & 4)
+                    tcg_gen_sari_i32(tmp2, tmp2, 16);
+                else
+                    gen_sxth(tmp2);
+                tmp2 = gen_muls_i64_i32(tmp, tmp2);
+                tcg_gen_shri_i64(tmp2, tmp2, 16);
+                tmp = new_tmp();
+                tcg_gen_trunc_i64_i32(tmp, tmp2);
+                if ((sh & 2) == 0) {
+                    tmp2 = load_reg(s, rn);
+                    gen_helper_add_setq(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                }
+                store_reg(s, rd, tmp);
+            } else {
+                /* 16 * 16 */
+                tmp = load_reg(s, rm);
+                tmp2 = load_reg(s, rs);
+                gen_mulxy(tmp, tmp2, sh & 2, sh & 4);
+                dead_tmp(tmp2);
+                if (op1 == 2) {
+                    tmp2 = tcg_temp_new(TCG_TYPE_I64);
+                    tcg_gen_ext_i32_i64(tmp2, tmp);
+                    dead_tmp(tmp);
+                    gen_addq(s, tmp2, rn, rd);
+                    gen_storeq_reg(s, rn, rd, tmp2);
+                } else {
+                    if (op1 == 0) {
+                        tmp2 = load_reg(s, rn);
+                        gen_helper_add_setq(tmp, tmp, tmp2);
+                        dead_tmp(tmp2);
+                    }
+                    store_reg(s, rd, tmp);
+                }
+            }
+            break;
+        default:
+            goto illegal_op;
+        }
+    } else if (((insn & 0x0e000000) == 0 &&
+                (insn & 0x00000090) != 0x90) ||
+               ((insn & 0x0e000000) == (1 << 25))) {
+        int set_cc, logic_cc, shiftop;
+
+        op1 = (insn >> 21) & 0xf;
+        set_cc = (insn >> 20) & 1;
+        logic_cc = table_logic_cc[op1] & set_cc;
+
+        /* data processing instruction */
+        if (insn & (1 << 25)) {
+            /* immediate operand */
+            val = insn & 0xff;
+            shift = ((insn >> 8) & 0xf) * 2;
+            if (shift)
+                val = (val >> shift) | (val << (32 - shift));
+            gen_op_movl_T1_im(val);
+            if (logic_cc && shift)
+                gen_set_CF_bit31(cpu_T[1]);
+        } else {
+            /* register */
+            rm = (insn) & 0xf;
+            gen_movl_T1_reg(s, rm);
+            shiftop = (insn >> 5) & 3;
+            if (!(insn & (1 << 4))) {
+                shift = (insn >> 7) & 0x1f;
+                gen_arm_shift_im(cpu_T[1], shiftop, shift, logic_cc);
+            } else {
+                rs = (insn >> 8) & 0xf;
+                tmp = load_reg(s, rs);
+                gen_arm_shift_reg(cpu_T[1], shiftop, tmp, logic_cc);
+            }
+        }
+        if (op1 != 0x0f && op1 != 0x0d) {
+            rn = (insn >> 16) & 0xf;
+            gen_movl_T0_reg(s, rn);
+        }
+        rd = (insn >> 12) & 0xf;
+        switch(op1) {
+        case 0x00:
+            gen_op_andl_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            if (logic_cc)
+                gen_op_logic_T0_cc();
+            break;
+        case 0x01:
+            gen_op_xorl_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            if (logic_cc)
+                gen_op_logic_T0_cc();
+            break;
+        case 0x02:
+            if (set_cc && rd == 15) {
+                /* SUBS r15, ... is used for exception return.  */
+                if (IS_USER(s))
+                    goto illegal_op;
+                gen_op_subl_T0_T1_cc();
+                gen_exception_return(s);
+            } else {
+                if (set_cc)
+                    gen_op_subl_T0_T1_cc();
+                else
+                    gen_op_subl_T0_T1();
+                gen_movl_reg_T0(s, rd);
+            }
+            break;
+        case 0x03:
+            if (set_cc)
+                gen_op_rsbl_T0_T1_cc();
+            else
+                gen_op_rsbl_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            break;
+        case 0x04:
+            if (set_cc)
+                gen_op_addl_T0_T1_cc();
+            else
+                gen_op_addl_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            break;
+        case 0x05:
+            if (set_cc)
+                gen_op_adcl_T0_T1_cc();
+            else
+                gen_adc_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            break;
+        case 0x06:
+            if (set_cc)
+                gen_op_sbcl_T0_T1_cc();
+            else
+                gen_sbc_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            break;
+        case 0x07:
+            if (set_cc)
+                gen_op_rscl_T0_T1_cc();
+            else
+                gen_rsc_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            break;
+        case 0x08:
+            if (set_cc) {
+                gen_op_andl_T0_T1();
+                gen_op_logic_T0_cc();
+            }
+            break;
+        case 0x09:
+            if (set_cc) {
+                gen_op_xorl_T0_T1();
+                gen_op_logic_T0_cc();
+            }
+            break;
+        case 0x0a:
+            if (set_cc) {
+                gen_op_subl_T0_T1_cc();
+            }
+            break;
+        case 0x0b:
+            if (set_cc) {
+                gen_op_addl_T0_T1_cc();
+            }
+            break;
+        case 0x0c:
+            gen_op_orl_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            if (logic_cc)
+                gen_op_logic_T0_cc();
+            break;
+        case 0x0d:
+            if (logic_cc && rd == 15) {
+                /* MOVS r15, ... is used for exception return.  */
+                if (IS_USER(s))
+                    goto illegal_op;
+                gen_op_movl_T0_T1();
+                gen_exception_return(s);
+            } else {
+                gen_movl_reg_T1(s, rd);
+                if (logic_cc)
+                    gen_op_logic_T1_cc();
+            }
+            break;
+        case 0x0e:
+            gen_op_bicl_T0_T1();
+            gen_movl_reg_T0(s, rd);
+            if (logic_cc)
+                gen_op_logic_T0_cc();
+            break;
+        default:
+        case 0x0f:
+            gen_op_notl_T1();
+            gen_movl_reg_T1(s, rd);
+            if (logic_cc)
+                gen_op_logic_T1_cc();
+            break;
+        }
+    } else {
+        /* other instructions */
+        op1 = (insn >> 24) & 0xf;
+        switch(op1) {
+        case 0x0:
+        case 0x1:
+            /* multiplies, extra load/stores */
+            sh = (insn >> 5) & 3;
+            if (sh == 0) {
+                if (op1 == 0x0) {
+                    rd = (insn >> 16) & 0xf;
+                    rn = (insn >> 12) & 0xf;
+                    rs = (insn >> 8) & 0xf;
+                    rm = (insn) & 0xf;
+                    op1 = (insn >> 20) & 0xf;
+                    switch (op1) {
+                    case 0: case 1: case 2: case 3: case 6:
+                        /* 32 bit mul */
+                        tmp = load_reg(s, rs);
+                        tmp2 = load_reg(s, rm);
+                        tcg_gen_mul_i32(tmp, tmp, tmp2);
+                        dead_tmp(tmp2);
+                        if (insn & (1 << 22)) {
+                            /* Subtract (mls) */
+                            ARCH(6T2);
+                            tmp2 = load_reg(s, rn);
+                            tcg_gen_sub_i32(tmp, tmp2, tmp);
+                            dead_tmp(tmp2);
+                        } else if (insn & (1 << 21)) {
+                            /* Add */
+                            tmp2 = load_reg(s, rn);
+                            tcg_gen_add_i32(tmp, tmp, tmp2);
+                            dead_tmp(tmp2);
+                        }
+                        if (insn & (1 << 20))
+                            gen_logic_CC(tmp);
+                        store_reg(s, rd, tmp);
+                        break;
+                    default:
+                        /* 64 bit mul */
+                        tmp = load_reg(s, rs);
+                        tmp2 = load_reg(s, rm);
+                        if (insn & (1 << 22))
+                            tmp = gen_muls_i64_i32(tmp, tmp2);
+                        else
+                            tmp = gen_mulu_i64_i32(tmp, tmp2);
+                        if (insn & (1 << 21)) /* mult accumulate */
+                            gen_addq(s, tmp, rn, rd);
+                        if (!(insn & (1 << 23))) { /* double accumulate */
+                            ARCH(6);
+                            gen_addq_lo(s, tmp, rn);
+                            gen_addq_lo(s, tmp, rd);
+                        }
+                        if (insn & (1 << 20))
+                            gen_logicq_cc(tmp);
+                        gen_storeq_reg(s, rn, rd, tmp);
+                        break;
+                    }
+                } else {
+                    rn = (insn >> 16) & 0xf;
+                    rd = (insn >> 12) & 0xf;
+                    if (insn & (1 << 23)) {
+                        /* load/store exclusive */
+                        gen_movl_T1_reg(s, rn);
+                        addr = cpu_T[1];
+                        if (insn & (1 << 20)) {
+                            gen_helper_mark_exclusive(cpu_env, cpu_T[1]);
+                            tmp = gen_ld32(addr, IS_USER(s));
+                            store_reg(s, rd, tmp);
+                        } else {
+                            int label = gen_new_label();
+                            rm = insn & 0xf;
+                            gen_helper_test_exclusive(cpu_T[0], cpu_env, addr);
+                            tcg_gen_brcondi_i32(TCG_COND_NE, cpu_T[0],
+                                                0, label);
+                            tmp = load_reg(s,rm);
+                            gen_st32(tmp, cpu_T[1], IS_USER(s));
+                            gen_set_label(label);
+                            gen_movl_reg_T0(s, rd);
+                        }
+                    } else {
+                        /* SWP instruction */
+                        rm = (insn) & 0xf;
+
+                        /* ??? This is not really atomic.  However we know
+                           we never have multiple CPUs running in parallel,
+                           so it is good enough.  */
+                        addr = load_reg(s, rn);
+                        tmp = load_reg(s, rm);
+                        if (insn & (1 << 22)) {
+                            tmp2 = gen_ld8u(addr, IS_USER(s));
+                            gen_st8(tmp, addr, IS_USER(s));
+                        } else {
+                            tmp2 = gen_ld32(addr, IS_USER(s));
+                            gen_st32(tmp, addr, IS_USER(s));
+                        }
+                        dead_tmp(addr);
+                        store_reg(s, rd, tmp2);
+                    }
+                }
+            } else {
+                int address_offset;
+                int load;
+                /* Misc load/store */
+                rn = (insn >> 16) & 0xf;
+                rd = (insn >> 12) & 0xf;
+                addr = load_reg(s, rn);
+                if (insn & (1 << 24))
+                    gen_add_datah_offset(s, insn, 0, addr);
+                address_offset = 0;
+                if (insn & (1 << 20)) {
+                    /* load */
+                    switch(sh) {
+                    case 1:
+                        tmp = gen_ld16u(addr, IS_USER(s));
+                        break;
+                    case 2:
+                        tmp = gen_ld8s(addr, IS_USER(s));
+                        break;
+                    default:
+                    case 3:
+                        tmp = gen_ld16s(addr, IS_USER(s));
+                        break;
+                    }
+                    load = 1;
+                } else if (sh & 2) {
+                    /* doubleword */
+                    if (sh & 1) {
+                        /* store */
+                        tmp = load_reg(s, rd);
+                        gen_st32(tmp, addr, IS_USER(s));
+                        tcg_gen_addi_i32(addr, addr, 4);
+                        tmp = load_reg(s, rd + 1);
+                        gen_st32(tmp, addr, IS_USER(s));
+                        load = 0;
+                    } else {
+                        /* load */
+                        tmp = gen_ld32(addr, IS_USER(s));
+                        store_reg(s, rd, tmp);
+                        tcg_gen_addi_i32(addr, addr, 4);
+                        tmp = gen_ld32(addr, IS_USER(s));
+                        rd++;
+                        load = 1;
+                    }
+                    address_offset = -4;
+                } else {
+                    /* store */
+                    tmp = load_reg(s, rd);
+                    gen_st16(tmp, addr, IS_USER(s));
+                    load = 0;
+                }
+                /* Perform base writeback before the loaded value to
+                   ensure correct behavior with overlapping index registers.
+                   ldrd with base writeback is is undefined if the
+                   destination and index registers overlap.  */
+                if (!(insn & (1 << 24))) {
+                    gen_add_datah_offset(s, insn, address_offset, addr);
+                    store_reg(s, rn, addr);
+                } else if (insn & (1 << 21)) {
+                    if (address_offset)
+                        tcg_gen_addi_i32(addr, addr, address_offset);
+                    store_reg(s, rn, addr);
+                } else {
+                    dead_tmp(addr);
+                }
+                if (load) {
+                    /* Complete the load.  */
+                    store_reg(s, rd, tmp);
+                }
+            }
+            break;
+        case 0x4:
+        case 0x5:
+            goto do_ldst;
+        case 0x6:
+        case 0x7:
+            if (insn & (1 << 4)) {
+                ARCH(6);
+                /* Armv6 Media instructions.  */
+                rm = insn & 0xf;
+                rn = (insn >> 16) & 0xf;
+                rd = (insn >> 12) & 0xf;
+                rs = (insn >> 8) & 0xf;
+                switch ((insn >> 23) & 3) {
+                case 0: /* Parallel add/subtract.  */
+                    op1 = (insn >> 20) & 7;
+                    tmp = load_reg(s, rn);
+                    tmp2 = load_reg(s, rm);
+                    sh = (insn >> 5) & 7;
+                    if ((op1 & 3) == 0 || sh == 5 || sh == 6)
+                        goto illegal_op;
+                    gen_arm_parallel_addsub(op1, sh, tmp, tmp2);
+                    dead_tmp(tmp2);
+                    store_reg(s, rd, tmp);
+                    break;
+                case 1:
+                    if ((insn & 0x00700020) == 0) {
+                        /* Halfword pack.  */
+                        tmp = load_reg(s, rn);
+                        tmp2 = load_reg(s, rm);
+                        shift = (insn >> 7) & 0x1f;
+                        if (insn & (1 << 6)) {
+                            /* pkhtb */
+                            if (shift == 0)
+                                shift = 31;
+                            tcg_gen_sari_i32(tmp2, tmp2, shift);
+                            tcg_gen_andi_i32(tmp, tmp, 0xffff0000);
+                            tcg_gen_ext16u_i32(tmp2, tmp2);
+                        } else {
+                            /* pkhbt */
+                            if (shift)
+                                tcg_gen_shli_i32(tmp2, tmp2, shift);
+                            tcg_gen_ext16u_i32(tmp, tmp);
+                            tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000);
+                        }
+                        tcg_gen_or_i32(tmp, tmp, tmp2);
+                        dead_tmp(tmp2);
+                        store_reg(s, rd, tmp);
+                    } else if ((insn & 0x00200020) == 0x00200000) {
+                        /* [us]sat */
+                        tmp = load_reg(s, rm);
+                        shift = (insn >> 7) & 0x1f;
+                        if (insn & (1 << 6)) {
+                            if (shift == 0)
+                                shift = 31;
+                            tcg_gen_sari_i32(tmp, tmp, shift);
+                        } else {
+                            tcg_gen_shli_i32(tmp, tmp, shift);
+                        }
+                        sh = (insn >> 16) & 0x1f;
+                        if (sh != 0) {
+                            if (insn & (1 << 22))
+                                gen_helper_usat(tmp, tmp, tcg_const_i32(sh));
+                            else
+                                gen_helper_ssat(tmp, tmp, tcg_const_i32(sh));
+                        }
+                        store_reg(s, rd, tmp);
+                    } else if ((insn & 0x00300fe0) == 0x00200f20) {
+                        /* [us]sat16 */
+                        tmp = load_reg(s, rm);
+                        sh = (insn >> 16) & 0x1f;
+                        if (sh != 0) {
+                            if (insn & (1 << 22))
+                                gen_helper_usat16(tmp, tmp, tcg_const_i32(sh));
+                            else
+                                gen_helper_ssat16(tmp, tmp, tcg_const_i32(sh));
+                        }
+                        store_reg(s, rd, tmp);
+                    } else if ((insn & 0x00700fe0) == 0x00000fa0) {
+                        /* Select bytes.  */
+                        tmp = load_reg(s, rn);
+                        tmp2 = load_reg(s, rm);
+                        tmp3 = new_tmp();
+                        tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE));
+                        gen_helper_sel_flags(tmp, tmp3, tmp, tmp2);
+                        dead_tmp(tmp3);
+                        dead_tmp(tmp2);
+                        store_reg(s, rd, tmp);
+                    } else if ((insn & 0x000003e0) == 0x00000060) {
+                        tmp = load_reg(s, rm);
+                        shift = (insn >> 10) & 3;
+                        /* ??? In many cases it's not neccessary to do a
+                           rotate, a shift is sufficient.  */
+                        if (shift != 0)
+                            tcg_gen_rori_i32(tmp, tmp, shift * 8);
+                        op1 = (insn >> 20) & 7;
+                        switch (op1) {
+                        case 0: gen_sxtb16(tmp);  break;
+                        case 2: gen_sxtb(tmp);    break;
+                        case 3: gen_sxth(tmp);    break;
+                        case 4: gen_uxtb16(tmp);  break;
+                        case 6: gen_uxtb(tmp);    break;
+                        case 7: gen_uxth(tmp);    break;
+                        default: goto illegal_op;
+                        }
+                        if (rn != 15) {
+                            tmp2 = load_reg(s, rn);
+                            if ((op1 & 3) == 0) {
+                                gen_add16(tmp, tmp2);
+                            } else {
+                                tcg_gen_add_i32(tmp, tmp, tmp2);
+                                dead_tmp(tmp2);
+                            }
+                        }
+                        store_reg(s, rd, tmp);
+                    } else if ((insn & 0x003f0f60) == 0x003f0f20) {
+                        /* rev */
+                        tmp = load_reg(s, rm);
+                        if (insn & (1 << 22)) {
+                            if (insn & (1 << 7)) {
+                                gen_revsh(tmp);
+                            } else {
+                                ARCH(6T2);
+                                gen_helper_rbit(tmp, tmp);
+                            }
+                        } else {
+                            if (insn & (1 << 7))
+                                gen_rev16(tmp);
+                            else
+                                tcg_gen_bswap_i32(tmp, tmp);
+                        }
+                        store_reg(s, rd, tmp);
+                    } else {
+                        goto illegal_op;
+                    }
+                    break;
+                case 2: /* Multiplies (Type 3).  */
+                    tmp = load_reg(s, rm);
+                    tmp2 = load_reg(s, rs);
+                    if (insn & (1 << 20)) {
+                        /* Signed multiply most significant [accumulate].  */
+                        tmp2 = gen_muls_i64_i32(tmp, tmp2);
+                        if (insn & (1 << 5))
+                            tcg_gen_addi_i64(tmp2, tmp2, 0x80000000u);
+                        tcg_gen_shri_i64(tmp2, tmp2, 32);
+                        tmp = new_tmp();
+                        tcg_gen_trunc_i64_i32(tmp, tmp2);
+                        if (rn != 15) {
+                            tmp2 = load_reg(s, rn);
+                            if (insn & (1 << 6)) {
+                                tcg_gen_sub_i32(tmp, tmp, tmp2);
+                            } else {
+                                tcg_gen_add_i32(tmp, tmp, tmp2);
+                            }
+                            dead_tmp(tmp2);
+                        }
+                        store_reg(s, rd, tmp);
+                    } else {
+                        if (insn & (1 << 5))
+                            gen_swap_half(tmp2);
+                        gen_smul_dual(tmp, tmp2);
+                        /* This addition cannot overflow.  */
+                        if (insn & (1 << 6)) {
+                            tcg_gen_sub_i32(tmp, tmp, tmp2);
+                        } else {
+                            tcg_gen_add_i32(tmp, tmp, tmp2);
+                        }
+                        dead_tmp(tmp2);
+                        if (insn & (1 << 22)) {
+                            /* smlald, smlsld */
+                            tmp2 = tcg_temp_new(TCG_TYPE_I64);
+                            tcg_gen_ext_i32_i64(tmp2, tmp);
+                            dead_tmp(tmp);
+                            gen_addq(s, tmp2, rd, rn);
+                            gen_storeq_reg(s, rd, rn, tmp2);
+                        } else {
+                            /* smuad, smusd, smlad, smlsd */
+                            if (rd != 15)
+                              {
+                                tmp2 = load_reg(s, rd);
+                                gen_helper_add_setq(tmp, tmp, tmp2);
+                                dead_tmp(tmp2);
+                              }
+                            store_reg(s, rn, tmp);
+                        }
+                    }
+                    break;
+                case 3:
+                    op1 = ((insn >> 17) & 0x38) | ((insn >> 5) & 7);
+                    switch (op1) {
+                    case 0: /* Unsigned sum of absolute differences.  */
+                        ARCH(6);
+                        tmp = load_reg(s, rm);
+                        tmp2 = load_reg(s, rs);
+                        gen_helper_usad8(tmp, tmp, tmp2);
+                        dead_tmp(tmp2);
+                        if (rn != 15) {
+                            tmp2 = load_reg(s, rn);
+                            tcg_gen_add_i32(tmp, tmp, tmp2);
+                            dead_tmp(tmp2);
+                        }
+                        store_reg(s, rd, tmp);
+                        break;
+                    case 0x20: case 0x24: case 0x28: case 0x2c:
+                        /* Bitfield insert/clear.  */
+                        ARCH(6T2);
+                        shift = (insn >> 7) & 0x1f;
+                        i = (insn >> 16) & 0x1f;
+                        i = i + 1 - shift;
+                        if (rm == 15) {
+                            tmp = new_tmp();
+                            tcg_gen_movi_i32(tmp, 0);
+                        } else {
+                            tmp = load_reg(s, rm);
+                        }
+                        if (i != 32) {
+                            tmp2 = load_reg(s, rd);
+                            gen_bfi(tmp, tmp2, tmp, shift, (1u << i) - 1);
+                            dead_tmp(tmp2);
+                        }
+                        store_reg(s, rd, tmp);
+                        break;
+                    case 0x12: case 0x16: case 0x1a: case 0x1e: /* sbfx */
+                    case 0x32: case 0x36: case 0x3a: case 0x3e: /* ubfx */
+                        tmp = load_reg(s, rm);
+                        shift = (insn >> 7) & 0x1f;
+                        i = ((insn >> 16) & 0x1f) + 1;
+                        if (shift + i > 32)
+                            goto illegal_op;
+                        if (i < 32) {
+                            if (op1 & 0x20) {
+                                gen_ubfx(tmp, shift, (1u << i) - 1);
+                            } else {
+                                gen_sbfx(tmp, shift, i);
+                            }
+                        }
+                        store_reg(s, rd, tmp);
+                        break;
+                    default:
+                        goto illegal_op;
+                    }
+                    break;
+                }
+                break;
+            }
+        do_ldst:
+            /* Check for undefined extension instructions
+             * per the ARM Bible IE:
+             * xxxx 0111 1111 xxxx  xxxx xxxx 1111 xxxx
+             */
+            sh = (0xf << 20) | (0xf << 4);
+            if (op1 == 0x7 && ((insn & sh) == sh))
+            {
+                goto illegal_op;
+            }
+            /* load/store byte/word */
+            rn = (insn >> 16) & 0xf;
+            rd = (insn >> 12) & 0xf;
+            tmp2 = load_reg(s, rn);
+            i = (IS_USER(s) || (insn & 0x01200000) == 0x00200000);
+            if (insn & (1 << 24))
+                gen_add_data_offset(s, insn, tmp2);
+            if (insn & (1 << 20)) {
+                /* load */
+                s->is_mem = 1;
+                if (insn & (1 << 22)) {
+                    tmp = gen_ld8u(tmp2, i);
+                } else {
+                    tmp = gen_ld32(tmp2, i);
+                }
+            } else {
+                /* store */
+                tmp = load_reg(s, rd);
+                if (insn & (1 << 22))
+                    gen_st8(tmp, tmp2, i);
+                else
+                    gen_st32(tmp, tmp2, i);
+            }
+            if (!(insn & (1 << 24))) {
+                gen_add_data_offset(s, insn, tmp2);
+                store_reg(s, rn, tmp2);
+            } else if (insn & (1 << 21)) {
+                store_reg(s, rn, tmp2);
+            } else {
+                dead_tmp(tmp2);
+            }
+            if (insn & (1 << 20)) {
+                /* Complete the load.  */
+                if (rd == 15)
+                    gen_bx(s, tmp);
+                else
+                    store_reg(s, rd, tmp);
+            }
+            break;
+        case 0x08:
+        case 0x09:
+            {
+                int j, n, user, loaded_base;
+                TCGv loaded_var;
+                /* load/store multiple words */
+                /* XXX: store correct base if write back */
+                user = 0;
+                if (insn & (1 << 22)) {
+                    if (IS_USER(s))
+                        goto illegal_op; /* only usable in supervisor mode */
+
+                    if ((insn & (1 << 15)) == 0)
+                        user = 1;
+                }
+                rn = (insn >> 16) & 0xf;
+                addr = load_reg(s, rn);
+
+                /* compute total size */
+                loaded_base = 0;
+                TCGV_UNUSED(loaded_var);
+                n = 0;
+                for(i=0;i<16;i++) {
+                    if (insn & (1 << i))
+                        n++;
+                }
+                /* XXX: test invalid n == 0 case ? */
+                if (insn & (1 << 23)) {
+                    if (insn & (1 << 24)) {
+                        /* pre increment */
+                        tcg_gen_addi_i32(addr, addr, 4);
+                    } else {
+                        /* post increment */
+                    }
+                } else {
+                    if (insn & (1 << 24)) {
+                        /* pre decrement */
+                        tcg_gen_addi_i32(addr, addr, -(n * 4));
+                    } else {
+                        /* post decrement */
+                        if (n != 1)
+                        tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
+                    }
+                }
+                j = 0;
+                for(i=0;i<16;i++) {
+                    if (insn & (1 << i)) {
+                        if (insn & (1 << 20)) {
+                            /* load */
+                            tmp = gen_ld32(addr, IS_USER(s));
+                            if (i == 15) {
+                                gen_bx(s, tmp);
+                            } else if (user) {
+                                gen_helper_set_user_reg(tcg_const_i32(i), tmp);
+                                dead_tmp(tmp);
+                            } else if (i == rn) {
+                                loaded_var = tmp;
+                                loaded_base = 1;
+                            } else {
+                                store_reg(s, i, tmp);
+                            }
+                        } else {
+                            /* store */
+                            if (i == 15) {
+                                /* special case: r15 = PC + 8 */
+                                val = (long)s->pc + 4;
+                                tmp = new_tmp();
+                                tcg_gen_movi_i32(tmp, val);
+                            } else if (user) {
+                                tmp = new_tmp();
+                                gen_helper_get_user_reg(tmp, tcg_const_i32(i));
+                            } else {
+                                tmp = load_reg(s, i);
+                            }
+                            gen_st32(tmp, addr, IS_USER(s));
+                        }
+                        j++;
+                        /* no need to add after the last transfer */
+                        if (j != n)
+                            tcg_gen_addi_i32(addr, addr, 4);
+                    }
+                }
+                if (insn & (1 << 21)) {
+                    /* write back */
+                    if (insn & (1 << 23)) {
+                        if (insn & (1 << 24)) {
+                            /* pre increment */
+                        } else {
+                            /* post increment */
+                            tcg_gen_addi_i32(addr, addr, 4);
+                        }
+                    } else {
+                        if (insn & (1 << 24)) {
+                            /* pre decrement */
+                            if (n != 1)
+                                tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
+                        } else {
+                            /* post decrement */
+                            tcg_gen_addi_i32(addr, addr, -(n * 4));
+                        }
+                    }
+                    store_reg(s, rn, addr);
+                } else {
+                    dead_tmp(addr);
+                }
+                if (loaded_base) {
+                    store_reg(s, rn, loaded_var);
+                }
+                if ((insn & (1 << 22)) && !user) {
+                    /* Restore CPSR from SPSR.  */
+                    tmp = load_cpu_field(spsr);
+                    gen_set_cpsr(tmp, 0xffffffff);
+                    dead_tmp(tmp);
+                    s->is_jmp = DISAS_UPDATE;
+                }
+            }
+            break;
+        case 0xa:
+        case 0xb:
+            {
+                int32_t offset;
+
+                /* branch (and link) */
+                val = (int32_t)s->pc;
+                if (insn & (1 << 24)) {
+                    tmp = new_tmp();
+                    tcg_gen_movi_i32(tmp, val);
+                    store_reg(s, 14, tmp);
+                }
+                offset = (((int32_t)insn << 8) >> 8);
+                val += (offset << 2) + 4;
+                gen_jmp(s, val);
+            }
+            break;
+        case 0xc:
+        case 0xd:
+        case 0xe:
+            /* Coprocessor.  */
+            if (disas_coproc_insn(env, s, insn))
+                goto illegal_op;
+            break;
+        case 0xf:
+            /* swi */
+            gen_set_pc_im(s->pc);
+            s->is_jmp = DISAS_SWI;
+            break;
+        default:
+        illegal_op:
+            gen_set_condexec(s);
+            gen_set_pc_im(s->pc - 4);
+            gen_exception(EXCP_UDEF);
+            s->is_jmp = DISAS_JUMP;
+            break;
+        }
+    }
+}
+
+/* Return true if this is a Thumb-2 logical op.  */
+static int
+thumb2_logic_op(int op)
+{
+    return (op < 8);
+}
+
+/* Generate code for a Thumb-2 data processing operation.  If CONDS is nonzero
+   then set condition code flags based on the result of the operation.
+   If SHIFTER_OUT is nonzero then set the carry flag for logical operations
+   to the high bit of T1.
+   Returns zero if the opcode is valid.  */
+
+static int
+gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out)
+{
+    int logic_cc;
+
+    logic_cc = 0;
+    switch (op) {
+    case 0: /* and */
+        gen_op_andl_T0_T1();
+        logic_cc = conds;
+        break;
+    case 1: /* bic */
+        gen_op_bicl_T0_T1();
+        logic_cc = conds;
+        break;
+    case 2: /* orr */
+        gen_op_orl_T0_T1();
+        logic_cc = conds;
+        break;
+    case 3: /* orn */
+        gen_op_notl_T1();
+        gen_op_orl_T0_T1();
+        logic_cc = conds;
+        break;
+    case 4: /* eor */
+        gen_op_xorl_T0_T1();
+        logic_cc = conds;
+        break;
+    case 8: /* add */
+        if (conds)
+            gen_op_addl_T0_T1_cc();
+        else
+            gen_op_addl_T0_T1();
+        break;
+    case 10: /* adc */
+        if (conds)
+            gen_op_adcl_T0_T1_cc();
+        else
+            gen_adc_T0_T1();
+        break;
+    case 11: /* sbc */
+        if (conds)
+            gen_op_sbcl_T0_T1_cc();
+        else
+            gen_sbc_T0_T1();
+        break;
+    case 13: /* sub */
+        if (conds)
+            gen_op_subl_T0_T1_cc();
+        else
+            gen_op_subl_T0_T1();
+        break;
+    case 14: /* rsb */
+        if (conds)
+            gen_op_rsbl_T0_T1_cc();
+        else
+            gen_op_rsbl_T0_T1();
+        break;
+    default: /* 5, 6, 7, 9, 12, 15. */
+        return 1;
+    }
+    if (logic_cc) {
+        gen_op_logic_T0_cc();
+        if (shifter_out)
+            gen_set_CF_bit31(cpu_T[1]);
+    }
+    return 0;
+}
+
+/* Translate a 32-bit thumb instruction.  Returns nonzero if the instruction
+   is not legal.  */
+static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
+{
+    uint32_t insn, imm, shift, offset;
+    uint32_t rd, rn, rm, rs;
+    TCGv tmp;
+    TCGv tmp2;
+    TCGv tmp3;
+    TCGv addr;
+    int op;
+    int shiftop;
+    int conds;
+    int logic_cc;
+
+    if (!(arm_feature(env, ARM_FEATURE_THUMB2)
+          || arm_feature (env, ARM_FEATURE_M))) {
+        /* Thumb-1 cores may need to treat bl and blx as a pair of
+           16-bit instructions to get correct prefetch abort behavior.  */
+        insn = insn_hw1;
+        if ((insn & (1 << 12)) == 0) {
+            /* Second half of blx.  */
+            offset = ((insn & 0x7ff) << 1);
+            tmp = load_reg(s, 14);
+            tcg_gen_addi_i32(tmp, tmp, offset);
+            tcg_gen_andi_i32(tmp, tmp, 0xfffffffc);
+
+            tmp2 = new_tmp();
+            tcg_gen_movi_i32(tmp2, s->pc | 1);
+            store_reg(s, 14, tmp2);
+            gen_bx(s, tmp);
+            return 0;
+        }
+        if (insn & (1 << 11)) {
+            /* Second half of bl.  */
+            offset = ((insn & 0x7ff) << 1) | 1;
+            tmp = load_reg(s, 14);
+            tcg_gen_addi_i32(tmp, tmp, offset);
+
+            tmp2 = new_tmp();
+            tcg_gen_movi_i32(tmp2, s->pc | 1);
+            store_reg(s, 14, tmp2);
+            gen_bx(s, tmp);
+            return 0;
+        }
+        if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
+            /* Instruction spans a page boundary.  Implement it as two
+               16-bit instructions in case the second half causes an
+               prefetch abort.  */
+            offset = ((int32_t)insn << 21) >> 9;
+            gen_op_movl_T0_im(s->pc + 2 + offset);
+            gen_movl_reg_T0(s, 14);
+            return 0;
+        }
+        /* Fall through to 32-bit decode.  */
+    }
+
+    insn = lduw_code(s->pc);
+#ifdef CONFIG_TRACE
+    if (tracing) {
+        int  ticks = get_insn_ticks_thumb(insn);
+        trace_add_insn( insn_wrap_thumb(insn), 1 );
+        gen_helper_traceInsn();
+        gen_traceTicks(ticks);
+    }
+#endif
+    s->pc += 2;
+    insn |= (uint32_t)insn_hw1 << 16;
+
+    if ((insn & 0xf800e800) != 0xf000e800) {
+        ARCH(6T2);
+    }
+
+    rn = (insn >> 16) & 0xf;
+    rs = (insn >> 12) & 0xf;
+    rd = (insn >> 8) & 0xf;
+    rm = insn & 0xf;
+    switch ((insn >> 25) & 0xf) {
+    case 0: case 1: case 2: case 3:
+        /* 16-bit instructions.  Should never happen.  */
+        abort();
+    case 4:
+        if (insn & (1 << 22)) {
+            /* Other load/store, table branch.  */
+            if (insn & 0x01200000) {
+                /* Load/store doubleword.  */
+                if (rn == 15) {
+                    addr = new_tmp();
+                    tcg_gen_movi_i32(addr, s->pc & ~3);
+                } else {
+                    addr = load_reg(s, rn);
+                }
+                offset = (insn & 0xff) * 4;
+                if ((insn & (1 << 23)) == 0)
+                    offset = -offset;
+                if (insn & (1 << 24)) {
+                    tcg_gen_addi_i32(addr, addr, offset);
+                    offset = 0;
+                }
+                if (insn & (1 << 20)) {
+                    /* ldrd */
+                    tmp = gen_ld32(addr, IS_USER(s));
+                    store_reg(s, rs, tmp);
+                    tcg_gen_addi_i32(addr, addr, 4);
+                    tmp = gen_ld32(addr, IS_USER(s));
+                    store_reg(s, rd, tmp);
+                } else {
+                    /* strd */
+                    tmp = load_reg(s, rs);
+                    gen_st32(tmp, addr, IS_USER(s));
+                    tcg_gen_addi_i32(addr, addr, 4);
+                    tmp = load_reg(s, rd);
+                    gen_st32(tmp, addr, IS_USER(s));
+                }
+                if (insn & (1 << 21)) {
+                    /* Base writeback.  */
+                    if (rn == 15)
+                        goto illegal_op;
+                    tcg_gen_addi_i32(addr, addr, offset - 4);
+                    store_reg(s, rn, addr);
+                } else {
+                    dead_tmp(addr);
+                }
+            } else if ((insn & (1 << 23)) == 0) {
+                /* Load/store exclusive word.  */
+                gen_movl_T1_reg(s, rn);
+                addr = cpu_T[1];
+                if (insn & (1 << 20)) {
+                    gen_helper_mark_exclusive(cpu_env, cpu_T[1]);
+                    tmp = gen_ld32(addr, IS_USER(s));
+                    store_reg(s, rd, tmp);
+                } else {
+                    int label = gen_new_label();
+                    gen_helper_test_exclusive(cpu_T[0], cpu_env, addr);
+                    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_T[0],
+                                        0, label);
+                    tmp = load_reg(s, rs);
+                    gen_st32(tmp, cpu_T[1], IS_USER(s));
+                    gen_set_label(label);
+                    gen_movl_reg_T0(s, rd);
+                }
+            } else if ((insn & (1 << 6)) == 0) {
+                /* Table Branch.  */
+                if (rn == 15) {
+                    addr = new_tmp();
+                    tcg_gen_movi_i32(addr, s->pc);
+                } else {
+                    addr = load_reg(s, rn);
+                }
+                tmp = load_reg(s, rm);
+                tcg_gen_add_i32(addr, addr, tmp);
+                if (insn & (1 << 4)) {
+                    /* tbh */
+                    tcg_gen_add_i32(addr, addr, tmp);
+                    dead_tmp(tmp);
+                    tmp = gen_ld16u(addr, IS_USER(s));
+                } else { /* tbb */
+                    dead_tmp(tmp);
+                    tmp = gen_ld8u(addr, IS_USER(s));
+                }
+                dead_tmp(addr);
+                tcg_gen_shli_i32(tmp, tmp, 1);
+                tcg_gen_addi_i32(tmp, tmp, s->pc);
+                store_reg(s, 15, tmp);
+            } else {
+                /* Load/store exclusive byte/halfword/doubleword.  */
+                /* ??? These are not really atomic.  However we know
+                   we never have multiple CPUs running in parallel,
+                   so it is good enough.  */
+                op = (insn >> 4) & 0x3;
+                /* Must use a global reg for the address because we have
+                   a conditional branch in the store instruction.  */
+                gen_movl_T1_reg(s, rn);
+                addr = cpu_T[1];
+                if (insn & (1 << 20)) {
+                    gen_helper_mark_exclusive(cpu_env, addr);
+                    switch (op) {
+                    case 0:
+                        tmp = gen_ld8u(addr, IS_USER(s));
+                        break;
+                    case 1:
+                        tmp = gen_ld16u(addr, IS_USER(s));
+                        break;
+                    case 3:
+                        tmp = gen_ld32(addr, IS_USER(s));
+                        tcg_gen_addi_i32(addr, addr, 4);
+                        tmp2 = gen_ld32(addr, IS_USER(s));
+                        store_reg(s, rd, tmp2);
+                        break;
+                    default:
+                        goto illegal_op;
+                    }
+                    store_reg(s, rs, tmp);
+                } else {
+                    int label = gen_new_label();
+                    /* Must use a global that is not killed by the branch.  */
+                    gen_helper_test_exclusive(cpu_T[0], cpu_env, addr);
+                    tcg_gen_brcondi_i32(TCG_COND_NE, cpu_T[0], 0, label);
+                    tmp = load_reg(s, rs);
+                    switch (op) {
+                    case 0:
+                        gen_st8(tmp, addr, IS_USER(s));
+                        break;
+                    case 1:
+                        gen_st16(tmp, addr, IS_USER(s));
+                        break;
+                    case 3:
+                        gen_st32(tmp, addr, IS_USER(s));
+                        tcg_gen_addi_i32(addr, addr, 4);
+                        tmp = load_reg(s, rd);
+                        gen_st32(tmp, addr, IS_USER(s));
+                        break;
+                    default:
+                        goto illegal_op;
+                    }
+                    gen_set_label(label);
+                    gen_movl_reg_T0(s, rm);
+                }
+            }
+        } else {
+            /* Load/store multiple, RFE, SRS.  */
+            if (((insn >> 23) & 1) == ((insn >> 24) & 1)) {
+                /* Not available in user mode.  */
+                if (IS_USER(s))
+                    goto illegal_op;
+                if (insn & (1 << 20)) {
+                    /* rfe */
+                    addr = load_reg(s, rn);
+                    if ((insn & (1 << 24)) == 0)
+                        tcg_gen_addi_i32(addr, addr, -8);
+                    /* Load PC into tmp and CPSR into tmp2.  */
+                    tmp = gen_ld32(addr, 0);
+                    tcg_gen_addi_i32(addr, addr, 4);
+                    tmp2 = gen_ld32(addr, 0);
+                    if (insn & (1 << 21)) {
+                        /* Base writeback.  */
+                        if (insn & (1 << 24)) {
+                            tcg_gen_addi_i32(addr, addr, 4);
+                        } else {
+                            tcg_gen_addi_i32(addr, addr, -4);
+                        }
+                        store_reg(s, rn, addr);
+                    } else {
+                        dead_tmp(addr);
+                    }
+                    gen_rfe(s, tmp, tmp2);
+                } else {
+                    /* srs */
+                    op = (insn & 0x1f);
+                    if (op == (env->uncached_cpsr & CPSR_M)) {
+                        addr = load_reg(s, 13);
+                    } else {
+                        addr = new_tmp();
+                        gen_helper_get_r13_banked(addr, cpu_env, tcg_const_i32(op));
+                    }
+                    if ((insn & (1 << 24)) == 0) {
+                        tcg_gen_addi_i32(addr, addr, -8);
+                    }
+                    tmp = load_reg(s, 14);
+                    gen_st32(tmp, addr, 0);
+                    tcg_gen_addi_i32(addr, addr, 4);
+                    tmp = new_tmp();
+                    gen_helper_cpsr_read(tmp);
+                    gen_st32(tmp, addr, 0);
+                    if (insn & (1 << 21)) {
+                        if ((insn & (1 << 24)) == 0) {
+                            tcg_gen_addi_i32(addr, addr, -4);
+                        } else {
+                            tcg_gen_addi_i32(addr, addr, 4);
+                        }
+                        if (op == (env->uncached_cpsr & CPSR_M)) {
+                            store_reg(s, 13, addr);
+                        } else {
+                            gen_helper_set_r13_banked(cpu_env,
+                                tcg_const_i32(op), addr);
+                        }
+                    } else {
+                        dead_tmp(addr);
+                    }
+                }
+            } else {
+                int i;
+                /* Load/store multiple.  */
+                addr = load_reg(s, rn);
+                offset = 0;
+                for (i = 0; i < 16; i++) {
+                    if (insn & (1 << i))
+                        offset += 4;
+                }
+                if (insn & (1 << 24)) {
+                    tcg_gen_addi_i32(addr, addr, -offset);
+                }
+
+                for (i = 0; i < 16; i++) {
+                    if ((insn & (1 << i)) == 0)
+                        continue;
+                    if (insn & (1 << 20)) {
+                        /* Load.  */
+                        tmp = gen_ld32(addr, IS_USER(s));
+                        if (i == 15) {
+                            gen_bx(s, tmp);
+                        } else {
+                            store_reg(s, i, tmp);
+                        }
+                    } else {
+                        /* Store.  */
+                        tmp = load_reg(s, i);
+                        gen_st32(tmp, addr, IS_USER(s));
+                    }
+                    tcg_gen_addi_i32(addr, addr, 4);
+                }
+                if (insn & (1 << 21)) {
+                    /* Base register writeback.  */
+                    if (insn & (1 << 24)) {
+                        tcg_gen_addi_i32(addr, addr, -offset);
+                    }
+                    /* Fault if writeback register is in register list.  */
+                    if (insn & (1 << rn))
+                        goto illegal_op;
+                    store_reg(s, rn, addr);
+                } else {
+                    dead_tmp(addr);
+                }
+            }
+        }
+        break;
+    case 5: /* Data processing register constant shift.  */
+        if (rn == 15)
+            gen_op_movl_T0_im(0);
+        else
+            gen_movl_T0_reg(s, rn);
+        gen_movl_T1_reg(s, rm);
+        op = (insn >> 21) & 0xf;
+        shiftop = (insn >> 4) & 3;
+        shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c);
+        conds = (insn & (1 << 20)) != 0;
+        logic_cc = (conds && thumb2_logic_op(op));
+        gen_arm_shift_im(cpu_T[1], shiftop, shift, logic_cc);
+        if (gen_thumb2_data_op(s, op, conds, 0))
+            goto illegal_op;
+        if (rd != 15)
+            gen_movl_reg_T0(s, rd);
+        break;
+    case 13: /* Misc data processing.  */
+        op = ((insn >> 22) & 6) | ((insn >> 7) & 1);
+        if (op < 4 && (insn & 0xf000) != 0xf000)
+            goto illegal_op;
+        switch (op) {
+        case 0: /* Register controlled shift.  */
+            tmp = load_reg(s, rn);
+            tmp2 = load_reg(s, rm);
+            if ((insn & 0x70) != 0)
+                goto illegal_op;
+            op = (insn >> 21) & 3;
+            logic_cc = (insn & (1 << 20)) != 0;
+            gen_arm_shift_reg(tmp, op, tmp2, logic_cc);
+            if (logic_cc)
+                gen_logic_CC(tmp);
+            store_reg(s, rd, tmp);
+            break;
+        case 1: /* Sign/zero extend.  */
+            tmp = load_reg(s, rm);
+            shift = (insn >> 4) & 3;
+            /* ??? In many cases it's not neccessary to do a
+               rotate, a shift is sufficient.  */
+            if (shift != 0)
+                tcg_gen_rori_i32(tmp, tmp, shift * 8);
+            op = (insn >> 20) & 7;
+            switch (op) {
+            case 0: gen_sxth(tmp);   break;
+            case 1: gen_uxth(tmp);   break;
+            case 2: gen_sxtb16(tmp); break;
+            case 3: gen_uxtb16(tmp); break;
+            case 4: gen_sxtb(tmp);   break;
+            case 5: gen_uxtb(tmp);   break;
+            default: goto illegal_op;
+            }
+            if (rn != 15) {
+                tmp2 = load_reg(s, rn);
+                if ((op >> 1) == 1) {
+                    gen_add16(tmp, tmp2);
+                } else {
+                    tcg_gen_add_i32(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                }
+            }
+            store_reg(s, rd, tmp);
+            break;
+        case 2: /* SIMD add/subtract.  */
+            op = (insn >> 20) & 7;
+            shift = (insn >> 4) & 7;
+            if ((op & 3) == 3 || (shift & 3) == 3)
+                goto illegal_op;
+            tmp = load_reg(s, rn);
+            tmp2 = load_reg(s, rm);
+            gen_thumb2_parallel_addsub(op, shift, tmp, tmp2);
+            dead_tmp(tmp2);
+            store_reg(s, rd, tmp);
+            break;
+        case 3: /* Other data processing.  */
+            op = ((insn >> 17) & 0x38) | ((insn >> 4) & 7);
+            if (op < 4) {
+                /* Saturating add/subtract.  */
+                tmp = load_reg(s, rn);
+                tmp2 = load_reg(s, rm);
+                if (op & 2)
+                    gen_helper_double_saturate(tmp, tmp);
+                if (op & 1)
+                    gen_helper_sub_saturate(tmp, tmp2, tmp);
+                else
+                    gen_helper_add_saturate(tmp, tmp, tmp2);
+                dead_tmp(tmp2);
+            } else {
+                tmp = load_reg(s, rn);
+                switch (op) {
+                case 0x0a: /* rbit */
+                    gen_helper_rbit(tmp, tmp);
+                    break;
+                case 0x08: /* rev */
+                    tcg_gen_bswap_i32(tmp, tmp);
+                    break;
+                case 0x09: /* rev16 */
+                    gen_rev16(tmp);
+                    break;
+                case 0x0b: /* revsh */
+                    gen_revsh(tmp);
+                    break;
+                case 0x10: /* sel */
+                    tmp2 = load_reg(s, rm);
+                    tmp3 = new_tmp();
+                    tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE));
+                    gen_helper_sel_flags(tmp, tmp3, tmp, tmp2);
+                    dead_tmp(tmp3);
+                    dead_tmp(tmp2);
+                    break;
+                case 0x18: /* clz */
+                    gen_helper_clz(tmp, tmp);
+                    break;
+                default:
+                    goto illegal_op;
+                }
+            }
+            store_reg(s, rd, tmp);
+            break;
+        case 4: case 5: /* 32-bit multiply.  Sum of absolute differences.  */
+            op = (insn >> 4) & 0xf;
+            tmp = load_reg(s, rn);
+            tmp2 = load_reg(s, rm);
+            switch ((insn >> 20) & 7) {
+            case 0: /* 32 x 32 -> 32 */
+                tcg_gen_mul_i32(tmp, tmp, tmp2);
+                dead_tmp(tmp2);
+                if (rs != 15) {
+                    tmp2 = load_reg(s, rs);
+                    if (op)
+                        tcg_gen_sub_i32(tmp, tmp2, tmp);
+                    else
+                        tcg_gen_add_i32(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                }
+                break;
+            case 1: /* 16 x 16 -> 32 */
+                gen_mulxy(tmp, tmp2, op & 2, op & 1);
+                dead_tmp(tmp2);
+                if (rs != 15) {
+                    tmp2 = load_reg(s, rs);
+                    gen_helper_add_setq(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                }
+                break;
+            case 2: /* Dual multiply add.  */
+            case 4: /* Dual multiply subtract.  */
+                if (op)
+                    gen_swap_half(tmp2);
+                gen_smul_dual(tmp, tmp2);
+                /* This addition cannot overflow.  */
+                if (insn & (1 << 22)) {
+                    tcg_gen_sub_i32(tmp, tmp, tmp2);
+                } else {
+                    tcg_gen_add_i32(tmp, tmp, tmp2);
+                }
+                dead_tmp(tmp2);
+                if (rs != 15)
+                  {
+                    tmp2 = load_reg(s, rs);
+                    gen_helper_add_setq(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                  }
+                break;
+            case 3: /* 32 * 16 -> 32msb */
+                if (op)
+                    tcg_gen_sari_i32(tmp2, tmp2, 16);
+                else
+                    gen_sxth(tmp2);
+                tmp2 = gen_muls_i64_i32(tmp, tmp2);
+                tcg_gen_shri_i64(tmp2, tmp2, 16);
+                tmp = new_tmp();
+                tcg_gen_trunc_i64_i32(tmp, tmp2);
+                if (rs != 15)
+                  {
+                    tmp2 = load_reg(s, rs);
+                    gen_helper_add_setq(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                  }
+                break;
+            case 5: case 6: /* 32 * 32 -> 32msb */
+                gen_imull(tmp, tmp2);
+                if (insn & (1 << 5)) {
+                    gen_roundqd(tmp, tmp2);
+                    dead_tmp(tmp2);
+                } else {
+                    dead_tmp(tmp);
+                    tmp = tmp2;
+                }
+                if (rs != 15) {
+                    tmp2 = load_reg(s, rs);
+                    if (insn & (1 << 21)) {
+                        tcg_gen_add_i32(tmp, tmp, tmp2);
+                    } else {
+                        tcg_gen_sub_i32(tmp, tmp2, tmp);
+                    }
+                    dead_tmp(tmp2);
+                }
+                break;
+            case 7: /* Unsigned sum of absolute differences.  */
+                gen_helper_usad8(tmp, tmp, tmp2);
+                dead_tmp(tmp2);
+                if (rs != 15) {
+                    tmp2 = load_reg(s, rs);
+                    tcg_gen_add_i32(tmp, tmp, tmp2);
+                    dead_tmp(tmp2);
+                }
+                break;
+            }
+            store_reg(s, rd, tmp);
+            break;
+        case 6: case 7: /* 64-bit multiply, Divide.  */
+            op = ((insn >> 4) & 0xf) | ((insn >> 16) & 0x70);
+            tmp = load_reg(s, rn);
+            tmp2 = load_reg(s, rm);
+            if ((op & 0x50) == 0x10) {
+                /* sdiv, udiv */
+                if (!arm_feature(env, ARM_FEATURE_DIV))
+                    goto illegal_op;
+                if (op & 0x20)
+                    gen_helper_udiv(tmp, tmp, tmp2);
+                else
+                    gen_helper_sdiv(tmp, tmp, tmp2);
+                dead_tmp(tmp2);
+                store_reg(s, rd, tmp);
+            } else if ((op & 0xe) == 0xc) {
+                /* Dual multiply accumulate long.  */
+                if (op & 1)
+                    gen_swap_half(tmp2);
+                gen_smul_dual(tmp, tmp2);
+                if (op & 0x10) {
+                    tcg_gen_sub_i32(tmp, tmp, tmp2);
+                } else {
+                    tcg_gen_add_i32(tmp, tmp, tmp2);
+                }
+                dead_tmp(tmp2);
+                tmp2 = tcg_temp_new(TCG_TYPE_I64);
+                gen_addq(s, tmp, rs, rd);
+                gen_storeq_reg(s, rs, rd, tmp);
+            } else {
+                if (op & 0x20) {
+                    /* Unsigned 64-bit multiply  */
+                    tmp = gen_mulu_i64_i32(tmp, tmp2);
+                } else {
+                    if (op & 8) {
+                        /* smlalxy */
+                        gen_mulxy(tmp, tmp2, op & 2, op & 1);
+                        dead_tmp(tmp2);
+                        tmp2 = tcg_temp_new(TCG_TYPE_I64);
+                        tcg_gen_ext_i32_i64(tmp2, tmp);
+                        dead_tmp(tmp);
+                        tmp = tmp2;
+                    } else {
+                        /* Signed 64-bit multiply  */
+                        tmp = gen_muls_i64_i32(tmp, tmp2);
+                    }
+                }
+                if (op & 4) {
+                    /* umaal */
+                    gen_addq_lo(s, tmp, rs);
+                    gen_addq_lo(s, tmp, rd);
+                } else if (op & 0x40) {
+                    /* 64-bit accumulate.  */
+                    gen_addq(s, tmp, rs, rd);
+                }
+                gen_storeq_reg(s, rs, rd, tmp);
+            }
+            break;
+        }
+        break;
+    case 6: case 7: case 14: case 15:
+        /* Coprocessor.  */
+        if (((insn >> 24) & 3) == 3) {
+            /* Translate into the equivalent ARM encoding.  */
+            insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4);
+            if (disas_neon_data_insn(env, s, insn))
+                goto illegal_op;
+        } else {
+            if (insn & (1 << 28))
+                goto illegal_op;
+            if (disas_coproc_insn (env, s, insn))
+                goto illegal_op;
+        }
+        break;
+    case 8: case 9: case 10: case 11:
+        if (insn & (1 << 15)) {
+            /* Branches, misc control.  */
+            if (insn & 0x5000) {
+                /* Unconditional branch.  */
+                /* signextend(hw1[10:0]) -> offset[:12].  */
+                offset = ((int32_t)insn << 5) >> 9 & ~(int32_t)0xfff;
+                /* hw1[10:0] -> offset[11:1].  */
+                offset |= (insn & 0x7ff) << 1;
+                /* (~hw2[13, 11] ^ offset[24]) -> offset[23,22]
+                   offset[24:22] already have the same value because of the
+                   sign extension above.  */
+                offset ^= ((~insn) & (1 << 13)) << 10;
+                offset ^= ((~insn) & (1 << 11)) << 11;
+
+                if (insn & (1 << 14)) {
+                    /* Branch and link.  */
+                    gen_op_movl_T1_im(s->pc | 1);
+                    gen_movl_reg_T1(s, 14);
+                }
+
+                offset += s->pc;
+                if (insn & (1 << 12)) {
+                    /* b/bl */
+                    gen_jmp(s, offset);
+                } else {
+                    /* blx */
+                    offset &= ~(uint32_t)2;
+                    gen_bx_im(s, offset);
+                }
+            } else if (((insn >> 23) & 7) == 7) {
+                /* Misc control */
+                if (insn & (1 << 13))
+                    goto illegal_op;
+
+                if (insn & (1 << 26)) {
+                    /* Secure monitor call (v6Z) */
+                    goto illegal_op; /* not implemented.  */
+                } else {
+                    op = (insn >> 20) & 7;
+                    switch (op) {
+                    case 0: /* msr cpsr.  */
+                        if (IS_M(env)) {
+                            tmp = load_reg(s, rn);
+                            addr = tcg_const_i32(insn & 0xff);
+                            gen_helper_v7m_msr(cpu_env, addr, tmp);
+                            gen_lookup_tb(s);
+                            break;
+                        }
+                        /* fall through */
+                    case 1: /* msr spsr.  */
+                        if (IS_M(env))
+                            goto illegal_op;
+                        gen_movl_T0_reg(s, rn);
+                        if (gen_set_psr_T0(s,
+                              msr_mask(env, s, (insn >> 8) & 0xf, op == 1),
+                              op == 1))
+                            goto illegal_op;
+                        break;
+                    case 2: /* cps, nop-hint.  */
+                        if (((insn >> 8) & 7) == 0) {
+                            gen_nop_hint(s, insn & 0xff);
+                        }
+                        /* Implemented as NOP in user mode.  */
+                        if (IS_USER(s))
+                            break;
+                        offset = 0;
+                        imm = 0;
+                        if (insn & (1 << 10)) {
+                            if (insn & (1 << 7))
+                                offset |= CPSR_A;
+                            if (insn & (1 << 6))
+                                offset |= CPSR_I;
+                            if (insn & (1 << 5))
+                                offset |= CPSR_F;
+                            if (insn & (1 << 9))
+                                imm = CPSR_A | CPSR_I | CPSR_F;
+                        }
+                        if (insn & (1 << 8)) {
+                            offset |= 0x1f;
+                            imm |= (insn & 0x1f);
+                        }
+                        if (offset) {
+                            gen_op_movl_T0_im(imm);
+                            gen_set_psr_T0(s, offset, 0);
+                        }
+                        break;
+                    case 3: /* Special control operations.  */
+                        op = (insn >> 4) & 0xf;
+                        switch (op) {
+                        case 2: /* clrex */
+                            gen_helper_clrex(cpu_env);
+                            break;
+                        case 4: /* dsb */
+                        case 5: /* dmb */
+                        case 6: /* isb */
+                            /* These execute as NOPs.  */
+                            ARCH(7);
+                            break;
+                        default:
+                            goto illegal_op;
+                        }
+                        break;
+                    case 4: /* bxj */
+                        /* Trivial implementation equivalent to bx.  */
+                        tmp = load_reg(s, rn);
+                        gen_bx(s, tmp);
+                        break;
+                    case 5: /* Exception return.  */
+                        /* Unpredictable in user mode.  */
+                        goto illegal_op;
+                    case 6: /* mrs cpsr.  */
+                        tmp = new_tmp();
+                        if (IS_M(env)) {
+                            addr = tcg_const_i32(insn & 0xff);
+                            gen_helper_v7m_mrs(tmp, cpu_env, addr);
+                        } else {
+                            gen_helper_cpsr_read(tmp);
+                        }
+                        store_reg(s, rd, tmp);
+                        break;
+                    case 7: /* mrs spsr.  */
+                        /* Not accessible in user mode.  */
+                        if (IS_USER(s) || IS_M(env))
+                            goto illegal_op;
+                        tmp = load_cpu_field(spsr);
+                        store_reg(s, rd, tmp);
+                        break;
+                    }
+                }
+            } else {
+                /* Conditional branch.  */
+                op = (insn >> 22) & 0xf;
+                /* Generate a conditional jump to next instruction.  */
+                s->condlabel = gen_new_label();
+                gen_test_cc(op ^ 1, s->condlabel);
+                s->condjmp = 1;
+
+                /* offset[11:1] = insn[10:0] */
+                offset = (insn & 0x7ff) << 1;
+                /* offset[17:12] = insn[21:16].  */
+                offset |= (insn & 0x003f0000) >> 4;
+                /* offset[31:20] = insn[26].  */
+                offset |= ((int32_t)((insn << 5) & 0x80000000)) >> 11;
+                /* offset[18] = insn[13].  */
+                offset |= (insn & (1 << 13)) << 5;
+                /* offset[19] = insn[11].  */
+                offset |= (insn & (1 << 11)) << 8;
+
+                /* jump to the offset */
+                gen_jmp(s, s->pc + offset);
+            }
+        } else {
+            /* Data processing immediate.  */
+            if (insn & (1 << 25)) {
+                if (insn & (1 << 24)) {
+                    if (insn & (1 << 20))
+                        goto illegal_op;
+                    /* Bitfield/Saturate.  */
+                    op = (insn >> 21) & 7;
+                    imm = insn & 0x1f;
+                    shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c);
+                    if (rn == 15) {
+                        tmp = new_tmp();
+                        tcg_gen_movi_i32(tmp, 0);
+                    } else {
+                        tmp = load_reg(s, rn);
+                    }
+                    switch (op) {
+                    case 2: /* Signed bitfield extract.  */
+                        imm++;
+                        if (shift + imm > 32)
+                            goto illegal_op;
+                        if (imm < 32)
+                            gen_sbfx(tmp, shift, imm);
+                        break;
+                    case 6: /* Unsigned bitfield extract.  */
+                        imm++;
+                        if (shift + imm > 32)
+                            goto illegal_op;
+                        if (imm < 32)
+                            gen_ubfx(tmp, shift, (1u << imm) - 1);
+                        break;
+                    case 3: /* Bitfield insert/clear.  */
+                        if (imm < shift)
+                            goto illegal_op;
+                        imm = imm + 1 - shift;
+                        if (imm != 32) {
+                            tmp2 = load_reg(s, rd);
+                            gen_bfi(tmp, tmp2, tmp, shift, (1u << imm) - 1);
+                            dead_tmp(tmp2);
+                        }
+                        break;
+                    case 7:
+                        goto illegal_op;
+                    default: /* Saturate.  */
+                        if (shift) {
+                            if (op & 1)
+                                tcg_gen_sari_i32(tmp, tmp, shift);
+                            else
+                                tcg_gen_shli_i32(tmp, tmp, shift);
+                        }
+                        tmp2 = tcg_const_i32(imm);
+                        if (op & 4) {
+                            /* Unsigned.  */
+                            if ((op & 1) && shift == 0)
+                                gen_helper_usat16(tmp, tmp, tmp2);
+                            else
+                                gen_helper_usat(tmp, tmp, tmp2);
+                        } else {
+                            /* Signed.  */
+                            if ((op & 1) && shift == 0)
+                                gen_helper_ssat16(tmp, tmp, tmp2);
+                            else
+                                gen_helper_ssat(tmp, tmp, tmp2);
+                        }
+                        break;
+                    }
+                    store_reg(s, rd, tmp);
+                } else {
+                    imm = ((insn & 0x04000000) >> 15)
+                          | ((insn & 0x7000) >> 4) | (insn & 0xff);
+                    if (insn & (1 << 22)) {
+                        /* 16-bit immediate.  */
+                        imm |= (insn >> 4) & 0xf000;
+                        if (insn & (1 << 23)) {
+                            /* movt */
+                            tmp = load_reg(s, rd);
+                            tcg_gen_ext16u_i32(tmp, tmp);
+                            tcg_gen_ori_i32(tmp, tmp, imm << 16);
+                        } else {
+                            /* movw */
+                            tmp = new_tmp();
+                            tcg_gen_movi_i32(tmp, imm);
+                        }
+                    } else {
+                        /* Add/sub 12-bit immediate.  */
+                        if (rn == 15) {
+                            offset = s->pc & ~(uint32_t)3;
+                            if (insn & (1 << 23))
+                                offset -= imm;
+                            else
+                                offset += imm;
+                            tmp = new_tmp();
+                            tcg_gen_movi_i32(tmp, offset);
+                        } else {
+                            tmp = load_reg(s, rn);
+                            if (insn & (1 << 23))
+                                tcg_gen_subi_i32(tmp, tmp, imm);
+                            else
+                                tcg_gen_addi_i32(tmp, tmp, imm);
+                        }
+                    }
+                    store_reg(s, rd, tmp);
+                }
+            } else {
+                int shifter_out = 0;
+                /* modified 12-bit immediate.  */
+                shift = ((insn & 0x04000000) >> 23) | ((insn & 0x7000) >> 12);
+                imm = (insn & 0xff);
+                switch (shift) {
+                case 0: /* XY */
+                    /* Nothing to do.  */
+                    break;
+                case 1: /* 00XY00XY */
+                    imm |= imm << 16;
+                    break;
+                case 2: /* XY00XY00 */
+                    imm |= imm << 16;
+                    imm <<= 8;
+                    break;
+                case 3: /* XYXYXYXY */
+                    imm |= imm << 16;
+                    imm |= imm << 8;
+                    break;
+                default: /* Rotated constant.  */
+                    shift = (shift << 1) | (imm >> 7);
+                    imm |= 0x80;
+                    imm = imm << (32 - shift);
+                    shifter_out = 1;
+                    break;
+                }
+                gen_op_movl_T1_im(imm);
+                rn = (insn >> 16) & 0xf;
+                if (rn == 15)
+                    gen_op_movl_T0_im(0);
+                else
+                    gen_movl_T0_reg(s, rn);
+                op = (insn >> 21) & 0xf;
+                if (gen_thumb2_data_op(s, op, (insn & (1 << 20)) != 0,
+                                       shifter_out))
+                    goto illegal_op;
+                rd = (insn >> 8) & 0xf;
+                if (rd != 15) {
+                    gen_movl_reg_T0(s, rd);
+                }
+            }
+        }
+        break;
+    case 12: /* Load/store single data item.  */
+        {
+        int postinc = 0;
+        int writeback = 0;
+        int user;
+        if ((insn & 0x01100000) == 0x01000000) {
+            if (disas_neon_ls_insn(env, s, insn))
+                goto illegal_op;
+            break;
+        }
+        user = IS_USER(s);
+        if (rn == 15) {
+            addr = new_tmp();
+            /* PC relative.  */
+            /* s->pc has already been incremented by 4.  */
+            imm = s->pc & 0xfffffffc;
+            if (insn & (1 << 23))
+                imm += insn & 0xfff;
+            else
+                imm -= insn & 0xfff;
+            tcg_gen_movi_i32(addr, imm);
+        } else {
+            addr = load_reg(s, rn);
+            if (insn & (1 << 23)) {
+                /* Positive offset.  */
+                imm = insn & 0xfff;
+                tcg_gen_addi_i32(addr, addr, imm);
+            } else {
+                op = (insn >> 8) & 7;
+                imm = insn & 0xff;
+                switch (op) {
+                case 0: case 8: /* Shifted Register.  */
+                    shift = (insn >> 4) & 0xf;
+                    if (shift > 3)
+                        goto illegal_op;
+                    tmp = load_reg(s, rm);
+                    if (shift)
+                        tcg_gen_shli_i32(tmp, tmp, shift);
+                    tcg_gen_add_i32(addr, addr, tmp);
+                    dead_tmp(tmp);
+                    break;
+                case 4: /* Negative offset.  */
+                    tcg_gen_addi_i32(addr, addr, -imm);
+                    break;
+                case 6: /* User privilege.  */
+                    tcg_gen_addi_i32(addr, addr, imm);
+                    user = 1;
+                    break;
+                case 1: /* Post-decrement.  */
+                    imm = -imm;
+                    /* Fall through.  */
+                case 3: /* Post-increment.  */
+                    postinc = 1;
+                    writeback = 1;
+                    break;
+                case 5: /* Pre-decrement.  */
+                    imm = -imm;
+                    /* Fall through.  */
+                case 7: /* Pre-increment.  */
+                    tcg_gen_addi_i32(addr, addr, imm);
+                    writeback = 1;
+                    break;
+                default:
+                    goto illegal_op;
+                }
+            }
+        }
+        op = ((insn >> 21) & 3) | ((insn >> 22) & 4);
+        if (insn & (1 << 20)) {
+            /* Load.  */
+            if (rs == 15 && op != 2) {
+                if (op & 2)
+                    goto illegal_op;
+                /* Memory hint.  Implemented as NOP.  */
+            } else {
+                switch (op) {
+                case 0: tmp = gen_ld8u(addr, user); break;
+                case 4: tmp = gen_ld8s(addr, user); break;
+                case 1: tmp = gen_ld16u(addr, user); break;
+                case 5: tmp = gen_ld16s(addr, user); break;
+                case 2: tmp = gen_ld32(addr, user); break;
+                default: goto illegal_op;
+                }
+                if (rs == 15) {
+                    gen_bx(s, tmp);
+                } else {
+                    store_reg(s, rs, tmp);
+                }
+            }
+        } else {
+            /* Store.  */
+            if (rs == 15)
+                goto illegal_op;
+            tmp = load_reg(s, rs);
+            switch (op) {
+            case 0: gen_st8(tmp, addr, user); break;
+            case 1: gen_st16(tmp, addr, user); break;
+            case 2: gen_st32(tmp, addr, user); break;
+            default: goto illegal_op;
+            }
+        }
+        if (postinc)
+            tcg_gen_addi_i32(addr, addr, imm);
+        if (writeback) {
+            store_reg(s, rn, addr);
+        } else {
+            dead_tmp(addr);
+        }
+        }
+        break;
+    default:
+        goto illegal_op;
+    }
+    return 0;
+illegal_op:
+    return 1;
+}
+
+static void disas_thumb_insn(CPUState *env, DisasContext *s)
+{
+    uint32_t val, insn, op, rm, rn, rd, shift, cond;
+    int32_t offset;
+    int i;
+    TCGv tmp;
+    TCGv tmp2;
+    TCGv addr;
+
+    if (s->condexec_mask) {
+        cond = s->condexec_cond;
+        s->condlabel = gen_new_label();
+        gen_test_cc(cond ^ 1, s->condlabel);
+        s->condjmp = 1;
+    }
+
+    insn = lduw_code(s->pc);
+#ifdef CONFIG_TRACE
+    if (tracing) {
+        int  ticks = get_insn_ticks_thumb(insn);
+        trace_add_insn( insn_wrap_thumb(insn), 1 );
+        gen_helper_traceInsn();
+        gen_traceTicks(ticks);
+    }
+#endif
+    s->pc += 2;
+
+    switch (insn >> 12) {
+    case 0: case 1:
+        rd = insn & 7;
+        op = (insn >> 11) & 3;
+        if (op == 3) {
+            /* add/subtract */
+            rn = (insn >> 3) & 7;
+            gen_movl_T0_reg(s, rn);
+            if (insn & (1 << 10)) {
+                /* immediate */
+                gen_op_movl_T1_im((insn >> 6) & 7);
+            } else {
+                /* reg */
+                rm = (insn >> 6) & 7;
+                gen_movl_T1_reg(s, rm);
+            }
+            if (insn & (1 << 9)) {
+                if (s->condexec_mask)
+                    gen_op_subl_T0_T1();
+                else
+                    gen_op_subl_T0_T1_cc();
+            } else {
+                if (s->condexec_mask)
+                    gen_op_addl_T0_T1();
+                else
+                    gen_op_addl_T0_T1_cc();
+            }
+            gen_movl_reg_T0(s, rd);
+        } else {
+            /* shift immediate */
+            rm = (insn >> 3) & 7;
+            shift = (insn >> 6) & 0x1f;
+            tmp = load_reg(s, rm);
+            gen_arm_shift_im(tmp, op, shift, s->condexec_mask == 0);
+            if (!s->condexec_mask)
+                gen_logic_CC(tmp);
+            store_reg(s, rd, tmp);
+        }
+        break;
+    case 2: case 3:
+        /* arithmetic large immediate */
+        op = (insn >> 11) & 3;
+        rd = (insn >> 8) & 0x7;
+        if (op == 0) {
+            gen_op_movl_T0_im(insn & 0xff);
+        } else {
+            gen_movl_T0_reg(s, rd);
+            gen_op_movl_T1_im(insn & 0xff);
+        }
+        switch (op) {
+        case 0: /* mov */
+            if (!s->condexec_mask)
+                gen_op_logic_T0_cc();
+            break;
+        case 1: /* cmp */
+            gen_op_subl_T0_T1_cc();
+            break;
+        case 2: /* add */
+            if (s->condexec_mask)
+                gen_op_addl_T0_T1();
+            else
+                gen_op_addl_T0_T1_cc();
+            break;
+        case 3: /* sub */
+            if (s->condexec_mask)
+                gen_op_subl_T0_T1();
+            else
+                gen_op_subl_T0_T1_cc();
+            break;
+        }
+        if (op != 1)
+            gen_movl_reg_T0(s, rd);
+        break;
+    case 4:
+        if (insn & (1 << 11)) {
+            rd = (insn >> 8) & 7;
+            /* load pc-relative.  Bit 1 of PC is ignored.  */
+            val = s->pc + 2 + ((insn & 0xff) * 4);
+            val &= ~(uint32_t)2;
+            addr = new_tmp();
+            tcg_gen_movi_i32(addr, val);
+            tmp = gen_ld32(addr, IS_USER(s));
+            dead_tmp(addr);
+            store_reg(s, rd, tmp);
+            break;
+        }
+        if (insn & (1 << 10)) {
+            /* data processing extended or blx */
+            rd = (insn & 7) | ((insn >> 4) & 8);
+            rm = (insn >> 3) & 0xf;
+            op = (insn >> 8) & 3;
+            switch (op) {
+            case 0: /* add */
+                gen_movl_T0_reg(s, rd);
+                gen_movl_T1_reg(s, rm);
+                gen_op_addl_T0_T1();
+                gen_movl_reg_T0(s, rd);
+                break;
+            case 1: /* cmp */
+                gen_movl_T0_reg(s, rd);
+                gen_movl_T1_reg(s, rm);
+                gen_op_subl_T0_T1_cc();
+                break;
+            case 2: /* mov/cpy */
+                gen_movl_T0_reg(s, rm);
+                gen_movl_reg_T0(s, rd);
+                break;
+            case 3:/* branch [and link] exchange thumb register */
+                tmp = load_reg(s, rm);
+                if (insn & (1 << 7)) {
+                    val = (uint32_t)s->pc | 1;
+                    tmp2 = new_tmp();
+                    tcg_gen_movi_i32(tmp2, val);
+                    store_reg(s, 14, tmp2);
+                }
+                gen_bx(s, tmp);
+                break;
+            }
+            break;
+        }
+
+        /* data processing register */
+        rd = insn & 7;
+        rm = (insn >> 3) & 7;
+        op = (insn >> 6) & 0xf;
+        if (op == 2 || op == 3 || op == 4 || op == 7) {
+            /* the shift/rotate ops want the operands backwards */
+            val = rm;
+            rm = rd;
+            rd = val;
+            val = 1;
+        } else {
+            val = 0;
+        }
+
+        if (op == 9) /* neg */
+            gen_op_movl_T0_im(0);
+        else if (op != 0xf) /* mvn doesn't read its first operand */
+            gen_movl_T0_reg(s, rd);
+
+        gen_movl_T1_reg(s, rm);
+        switch (op) {
+        case 0x0: /* and */
+            gen_op_andl_T0_T1();
+            if (!s->condexec_mask)
+                gen_op_logic_T0_cc();
+            break;
+        case 0x1: /* eor */
+            gen_op_xorl_T0_T1();
+            if (!s->condexec_mask)
+                gen_op_logic_T0_cc();
+            break;
+        case 0x2: /* lsl */
+            if (s->condexec_mask) {
+                gen_helper_shl(cpu_T[1], cpu_T[1], cpu_T[0]);
+            } else {
+                gen_helper_shl_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
+                gen_op_logic_T1_cc();
+            }
+            break;
+        case 0x3: /* lsr */
+            if (s->condexec_mask) {
+                gen_helper_shr(cpu_T[1], cpu_T[1], cpu_T[0]);
+            } else {
+                gen_helper_shr_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
+                gen_op_logic_T1_cc();
+            }
+            break;
+        case 0x4: /* asr */
+            if (s->condexec_mask) {
+                gen_helper_sar(cpu_T[1], cpu_T[1], cpu_T[0]);
+            } else {
+                gen_helper_sar_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
+                gen_op_logic_T1_cc();
+            }
+            break;
+        case 0x5: /* adc */
+            if (s->condexec_mask)
+                gen_adc_T0_T1();
+            else
+                gen_op_adcl_T0_T1_cc();
+            break;
+        case 0x6: /* sbc */
+            if (s->condexec_mask)
+                gen_sbc_T0_T1();
+            else
+                gen_op_sbcl_T0_T1_cc();
+            break;
+        case 0x7: /* ror */
+            if (s->condexec_mask) {
+                gen_helper_ror(cpu_T[1], cpu_T[1], cpu_T[0]);
+            } else {
+                gen_helper_ror_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
+                gen_op_logic_T1_cc();
+            }
+            break;
+        case 0x8: /* tst */
+            gen_op_andl_T0_T1();
+            gen_op_logic_T0_cc();
+            rd = 16;
+            break;
+        case 0x9: /* neg */
+            if (s->condexec_mask)
+                tcg_gen_neg_i32(cpu_T[0], cpu_T[1]);
+            else
+                gen_op_subl_T0_T1_cc();
+            break;
+        case 0xa: /* cmp */
+            gen_op_subl_T0_T1_cc();
+            rd = 16;
+            break;
+        case 0xb: /* cmn */
+            gen_op_addl_T0_T1_cc();
+            rd = 16;
+            break;
+        case 0xc: /* orr */
+            gen_op_orl_T0_T1();
+            if (!s->condexec_mask)
+                gen_op_logic_T0_cc();
+            break;
+        case 0xd: /* mul */
+            gen_op_mull_T0_T1();
+            if (!s->condexec_mask)
+                gen_op_logic_T0_cc();
+            break;
+        case 0xe: /* bic */
+            gen_op_bicl_T0_T1();
+            if (!s->condexec_mask)
+                gen_op_logic_T0_cc();
+            break;
+        case 0xf: /* mvn */
+            gen_op_notl_T1();
+            if (!s->condexec_mask)
+                gen_op_logic_T1_cc();
+            val = 1;
+            rm = rd;
+            break;
+        }
+        if (rd != 16) {
+            if (val)
+                gen_movl_reg_T1(s, rm);
+            else
+                gen_movl_reg_T0(s, rd);
+        }
+        break;
+
+    case 5:
+        /* load/store register offset.  */
+        rd = insn & 7;
+        rn = (insn >> 3) & 7;
+        rm = (insn >> 6) & 7;
+        op = (insn >> 9) & 7;
+        addr = load_reg(s, rn);
+        tmp = load_reg(s, rm);
+        tcg_gen_add_i32(addr, addr, tmp);
+        dead_tmp(tmp);
+
+        if (op < 3) /* store */
+            tmp = load_reg(s, rd);
+
+        switch (op) {
+        case 0: /* str */
+            gen_st32(tmp, addr, IS_USER(s));
+            break;
+        case 1: /* strh */
+            gen_st16(tmp, addr, IS_USER(s));
+            break;
+        case 2: /* strb */
+            gen_st8(tmp, addr, IS_USER(s));
+            break;
+        case 3: /* ldrsb */
+            tmp = gen_ld8s(addr, IS_USER(s));
+            break;
+        case 4: /* ldr */
+            tmp = gen_ld32(addr, IS_USER(s));
+            break;
+        case 5: /* ldrh */
+            tmp = gen_ld16u(addr, IS_USER(s));
+            break;
+        case 6: /* ldrb */
+            tmp = gen_ld8u(addr, IS_USER(s));
+            break;
+        case 7: /* ldrsh */
+            tmp = gen_ld16s(addr, IS_USER(s));
+            break;
+        }
+        if (op >= 3) /* load */
+            store_reg(s, rd, tmp);
+        dead_tmp(addr);
+        break;
+
+    case 6:
+        /* load/store word immediate offset */
+        rd = insn & 7;
+        rn = (insn >> 3) & 7;
+        addr = load_reg(s, rn);
+        val = (insn >> 4) & 0x7c;
+        tcg_gen_addi_i32(addr, addr, val);
+
+        if (insn & (1 << 11)) {
+            /* load */
+            tmp = gen_ld32(addr, IS_USER(s));
+            store_reg(s, rd, tmp);
+        } else {
+            /* store */
+            tmp = load_reg(s, rd);
+            gen_st32(tmp, addr, IS_USER(s));
+        }
+        dead_tmp(addr);
+        break;
+
+    case 7:
+        /* load/store byte immediate offset */
+        rd = insn & 7;
+        rn = (insn >> 3) & 7;
+        addr = load_reg(s, rn);
+        val = (insn >> 6) & 0x1f;
+        tcg_gen_addi_i32(addr, addr, val);
+
+        if (insn & (1 << 11)) {
+            /* load */
+            tmp = gen_ld8u(addr, IS_USER(s));
+            store_reg(s, rd, tmp);
+        } else {
+            /* store */
+            tmp = load_reg(s, rd);
+            gen_st8(tmp, addr, IS_USER(s));
+        }
+        dead_tmp(addr);
+        break;
+
+    case 8:
+        /* load/store halfword immediate offset */
+        rd = insn & 7;
+        rn = (insn >> 3) & 7;
+        addr = load_reg(s, rn);
+        val = (insn >> 5) & 0x3e;
+        tcg_gen_addi_i32(addr, addr, val);
+
+        if (insn & (1 << 11)) {
+            /* load */
+            tmp = gen_ld16u(addr, IS_USER(s));
+            store_reg(s, rd, tmp);
+        } else {
+            /* store */
+            tmp = load_reg(s, rd);
+            gen_st16(tmp, addr, IS_USER(s));
+        }
+        dead_tmp(addr);
+        break;
+
+    case 9:
+        /* load/store from stack */
+        rd = (insn >> 8) & 7;
+        addr = load_reg(s, 13);
+        val = (insn & 0xff) * 4;
+        tcg_gen_addi_i32(addr, addr, val);
+
+        if (insn & (1 << 11)) {
+            /* load */
+            tmp = gen_ld32(addr, IS_USER(s));
+            store_reg(s, rd, tmp);
+        } else {
+            /* store */
+            tmp = load_reg(s, rd);
+            gen_st32(tmp, addr, IS_USER(s));
+        }
+        dead_tmp(addr);
+        break;
+
+    case 10:
+        /* add to high reg */
+        rd = (insn >> 8) & 7;
+        if (insn & (1 << 11)) {
+            /* SP */
+            tmp = load_reg(s, 13);
+        } else {
+            /* PC. bit 1 is ignored.  */
+            tmp = new_tmp();
+            tcg_gen_movi_i32(tmp, (s->pc + 2) & ~(uint32_t)2);
+        }
+        val = (insn & 0xff) * 4;
+        tcg_gen_addi_i32(tmp, tmp, val);
+        store_reg(s, rd, tmp);
+        break;
+
+    case 11:
+        /* misc */
+        op = (insn >> 8) & 0xf;
+        switch (op) {
+        case 0:
+            /* adjust stack pointer */
+            tmp = load_reg(s, 13);
+            val = (insn & 0x7f) * 4;
+            if (insn & (1 << 7))
+                val = -(int32_t)val;
+            tcg_gen_addi_i32(tmp, tmp, val);
+            store_reg(s, 13, tmp);
+            break;
+
+        case 2: /* sign/zero extend.  */
+            ARCH(6);
+            rd = insn & 7;
+            rm = (insn >> 3) & 7;
+            tmp = load_reg(s, rm);
+            switch ((insn >> 6) & 3) {
+            case 0: gen_sxth(tmp); break;
+            case 1: gen_sxtb(tmp); break;
+            case 2: gen_uxth(tmp); break;
+            case 3: gen_uxtb(tmp); break;
+            }
+            store_reg(s, rd, tmp);
+            break;
+        case 4: case 5: case 0xc: case 0xd:
+            /* push/pop */
+            addr = load_reg(s, 13);
+            if (insn & (1 << 8))
+                offset = 4;
+            else
+                offset = 0;
+            for (i = 0; i < 8; i++) {
+                if (insn & (1 << i))
+                    offset += 4;
+            }
+            if ((insn & (1 << 11)) == 0) {
+                tcg_gen_addi_i32(addr, addr, -offset);
+            }
+            for (i = 0; i < 8; i++) {
+                if (insn & (1 << i)) {
+                    if (insn & (1 << 11)) {
+                        /* pop */
+                        tmp = gen_ld32(addr, IS_USER(s));
+                        store_reg(s, i, tmp);
+                    } else {
+                        /* push */
+                        tmp = load_reg(s, i);
+                        gen_st32(tmp, addr, IS_USER(s));
+                    }
+                    /* advance to the next address.  */
+                    tcg_gen_addi_i32(addr, addr, 4);
+                }
+            }
+            TCGV_UNUSED(tmp);
+            if (insn & (1 << 8)) {
+                if (insn & (1 << 11)) {
+                    /* pop pc */
+                    tmp = gen_ld32(addr, IS_USER(s));
+                    /* don't set the pc until the rest of the instruction
+                       has completed */
+                } else {
+                    /* push lr */
+                    tmp = load_reg(s, 14);
+                    gen_st32(tmp, addr, IS_USER(s));
+                }
+                tcg_gen_addi_i32(addr, addr, 4);
+            }
+            if ((insn & (1 << 11)) == 0) {
+                tcg_gen_addi_i32(addr, addr, -offset);
+            }
+            /* write back the new stack pointer */
+            store_reg(s, 13, addr);
+            /* set the new PC value */
+            if ((insn & 0x0900) == 0x0900)
+                gen_bx(s, tmp);
+            break;
+
+        case 1: case 3: case 9: case 11: /* czb */
+            rm = insn & 7;
+            tmp = load_reg(s, rm);
+            s->condlabel = gen_new_label();
+            s->condjmp = 1;
+            if (insn & (1 << 11))
+                tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel);
+            else
+                tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, s->condlabel);
+            dead_tmp(tmp);
+            offset = ((insn & 0xf8) >> 2) | (insn & 0x200) >> 3;
+            val = (uint32_t)s->pc + 2;
+            val += offset;
+            gen_jmp(s, val);
+            break;
+
+        case 15: /* IT, nop-hint.  */
+            if ((insn & 0xf) == 0) {
+                gen_nop_hint(s, (insn >> 4) & 0xf);
+                break;
+            }
+            /* If Then.  */
+            s->condexec_cond = (insn >> 4) & 0xe;
+            s->condexec_mask = insn & 0x1f;
+            /* No actual code generated for this insn, just setup state.  */
+            break;
+
+        case 0xe: /* bkpt */
+            gen_set_condexec(s);
+            gen_set_pc_im(s->pc - 2);
+            gen_exception(EXCP_BKPT);
+            s->is_jmp = DISAS_JUMP;
+            break;
+
+        case 0xa: /* rev */
+            ARCH(6);
+            rn = (insn >> 3) & 0x7;
+            rd = insn & 0x7;
+            tmp = load_reg(s, rn);
+            switch ((insn >> 6) & 3) {
+            case 0: tcg_gen_bswap_i32(tmp, tmp); break;
+            case 1: gen_rev16(tmp); break;
+            case 3: gen_revsh(tmp); break;
+            default: goto illegal_op;
+            }
+            store_reg(s, rd, tmp);
+            break;
+
+        case 6: /* cps */
+            ARCH(6);
+            if (IS_USER(s))
+                break;
+            if (IS_M(env)) {
+                tmp = tcg_const_i32((insn & (1 << 4)) != 0);
+                /* PRIMASK */
+                if (insn & 1) {
+                    addr = tcg_const_i32(16);
+                    gen_helper_v7m_msr(cpu_env, addr, tmp);
+                }
+                /* FAULTMASK */
+                if (insn & 2) {
+                    addr = tcg_const_i32(17);
+                    gen_helper_v7m_msr(cpu_env, addr, tmp);
+                }
+                gen_lookup_tb(s);
+            } else {
+                if (insn & (1 << 4))
+                    shift = CPSR_A | CPSR_I | CPSR_F;
+                else
+                    shift = 0;
+
+                val = ((insn & 7) << 6) & shift;
+                gen_op_movl_T0_im(val);
+                gen_set_psr_T0(s, shift, 0);
+            }
+            break;
+
+        default:
+            goto undef;
+        }
+        break;
+
+    case 12:
+        /* load/store multiple */
+        rn = (insn >> 8) & 0x7;
+        addr = load_reg(s, rn);
+        for (i = 0; i < 8; i++) {
+            if (insn & (1 << i)) {
+                if (insn & (1 << 11)) {
+                    /* load */
+                    tmp = gen_ld32(addr, IS_USER(s));
+                    store_reg(s, i, tmp);
+                } else {
+                    /* store */
+                    tmp = load_reg(s, i);
+                    gen_st32(tmp, addr, IS_USER(s));
+                }
+                /* advance to the next address */
+                tcg_gen_addi_i32(addr, addr, 4);
+            }
+        }
+        /* Base register writeback.  */
+        if ((insn & (1 << rn)) == 0) {
+            store_reg(s, rn, addr);
+        } else {
+            dead_tmp(addr);
+        }
+        break;
+
+    case 13:
+        /* conditional branch or swi */
+        cond = (insn >> 8) & 0xf;
+        if (cond == 0xe)
+            goto undef;
+
+        if (cond == 0xf) {
+            /* swi */
+            gen_set_condexec(s);
+            gen_set_pc_im(s->pc);
+            s->is_jmp = DISAS_SWI;
+            break;
+        }
+        /* generate a conditional jump to next instruction */
+        s->condlabel = gen_new_label();
+        gen_test_cc(cond ^ 1, s->condlabel);
+        s->condjmp = 1;
+        gen_movl_T1_reg(s, 15);
+
+        /* jump to the offset */
+        val = (uint32_t)s->pc + 2;
+        offset = ((int32_t)insn << 24) >> 24;
+        val += offset << 1;
+        gen_jmp(s, val);
+        break;
+
+    case 14:
+        if (insn & (1 << 11)) {
+            if (disas_thumb2_insn(env, s, insn))
+              goto undef32;
+            break;
+        }
+        /* unconditional branch */
+        val = (uint32_t)s->pc;
+        offset = ((int32_t)insn << 21) >> 21;
+        val += (offset << 1) + 2;
+        gen_jmp(s, val);
+        break;
+
+    case 15:
+        if (disas_thumb2_insn(env, s, insn))
+            goto undef32;
+        break;
+    }
+    return;
+undef32:
+    gen_set_condexec(s);
+    gen_set_pc_im(s->pc - 4);
+    gen_exception(EXCP_UDEF);
+    s->is_jmp = DISAS_JUMP;
+    return;
+illegal_op:
+undef:
+    gen_set_condexec(s);
+    gen_set_pc_im(s->pc - 2);
+    gen_exception(EXCP_UDEF);
+    s->is_jmp = DISAS_JUMP;
+}
+
+/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
+   basic block 'tb'. If search_pc is TRUE, also generate PC
+   information for each intermediate instruction. */
+static inline void gen_intermediate_code_internal(CPUState *env,
+                                                  TranslationBlock *tb,
+                                                  int search_pc)
+{
+    DisasContext dc1, *dc = &dc1;
+    uint16_t *gen_opc_end;
+    int j, lj;
+    target_ulong pc_start;
+    uint32_t next_page_start;
+    int num_insns;
+    int max_insns;
+
+    /* generate intermediate code */
+    num_temps = 0;
+    memset(temps, 0, sizeof(temps));
+
+    pc_start = tb->pc;
+
+    dc->tb = tb;
+
+    gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+
+    dc->is_jmp = DISAS_NEXT;
+    dc->pc = pc_start;
+    dc->singlestep_enabled = env->singlestep_enabled;
+    dc->condjmp = 0;
+    dc->thumb = env->thumb;
+    dc->condexec_mask = (env->condexec_bits & 0xf) << 1;
+    dc->condexec_cond = env->condexec_bits >> 4;
+    dc->is_mem = 0;
+#if !defined(CONFIG_USER_ONLY)
+    if (IS_M(env)) {
+        dc->user = ((env->v7m.exception == 0) && (env->v7m.control & 1));
+    } else {
+        dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR;
+    }
+#endif
+    cpu_F0s = tcg_temp_new(TCG_TYPE_I32);
+    cpu_F1s = tcg_temp_new(TCG_TYPE_I32);
+    cpu_F0d = tcg_temp_new(TCG_TYPE_I64);
+    cpu_F1d = tcg_temp_new(TCG_TYPE_I64);
+    cpu_V0 = cpu_F0d;
+    cpu_V1 = cpu_F1d;
+    /* FIXME: cpu_M0 can probably be the same as cpu_V0.  */
+    cpu_M0 = tcg_temp_new(TCG_TYPE_I64);
+    next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
+    lj = -1;
+    num_insns = 0;
+    max_insns = tb->cflags & CF_COUNT_MASK;
+    if (max_insns == 0)
+        max_insns = CF_COUNT_MASK;
+
+    gen_icount_start();
+    /* Reset the conditional execution bits immediately. This avoids
+       complications trying to do it at the end of the block.  */
+    if (env->condexec_bits)
+      {
+        TCGv tmp = new_tmp();
+        tcg_gen_movi_i32(tmp, 0);
+        store_cpu_field(tmp, condexec_bits);
+      }
+#ifdef CONFIG_TRACE
+    if (tracing) {
+        gen_traceBB(trace_static.bb_num, (target_phys_addr_t)tb );
+        trace_bb_start(dc->pc);
+    }
+#endif
+
+    do {
+#ifdef CONFIG_USER_ONLY
+        /* Intercept jump to the magic kernel page.  */
+        if (dc->pc >= 0xffff0000) {
+            /* We always get here via a jump, so know we are not in a
+               conditional execution block.  */
+            gen_exception(EXCP_KERNEL_TRAP);
+            dc->is_jmp = DISAS_UPDATE;
+            break;
+        }
+#else
+        if (dc->pc >= 0xfffffff0 && IS_M(env)) {
+            /* We always get here via a jump, so know we are not in a
+               conditional execution block.  */
+            gen_exception(EXCP_EXCEPTION_EXIT);
+            dc->is_jmp = DISAS_UPDATE;
+            break;
+        }
+#endif
+
+        if (env->nb_breakpoints > 0) {
+            for(j = 0; j < env->nb_breakpoints; j++) {
+                if (env->breakpoints[j] == dc->pc) {
+                    gen_set_condexec(dc);
+                    gen_set_pc_im(dc->pc);
+                    gen_exception(EXCP_DEBUG);
+                    dc->is_jmp = DISAS_JUMP;
+                    /* Advance PC so that clearing the breakpoint will
+                       invalidate this TB.  */
+                    dc->pc += 2;
+                    goto done_generating;
+                    break;
+                }
+            }
+        }
+        if (search_pc) {
+            j = gen_opc_ptr - gen_opc_buf;
+            if (lj < j) {
+                lj++;
+                while (lj < j)
+                    gen_opc_instr_start[lj++] = 0;
+            }
+            gen_opc_pc[lj] = dc->pc;
+            gen_opc_instr_start[lj] = 1;
+            gen_opc_icount[lj] = num_insns;
+        }
+
+        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
+            gen_io_start();
+
+        if (env->thumb) {
+            disas_thumb_insn(env, dc);
+            if (dc->condexec_mask) {
+                dc->condexec_cond = (dc->condexec_cond & 0xe)
+                                   | ((dc->condexec_mask >> 4) & 1);
+                dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f;
+                if (dc->condexec_mask == 0) {
+                    dc->condexec_cond = 0;
+                }
+            }
+        } else {
+            disas_arm_insn(env, dc);
+        }
+        if (num_temps) {
+            fprintf(stderr, "Internal resource leak before %08x\n", dc->pc);
+            num_temps = 0;
+        }
+
+        if (dc->condjmp && !dc->is_jmp) {
+            gen_set_label(dc->condlabel);
+            dc->condjmp = 0;
+        }
+        /* Terminate the TB on memory ops if watchpoints are present.  */
+        /* FIXME: This should be replacd by the deterministic execution
+         * IRQ raising bits.  */
+        if (dc->is_mem && env->nb_watchpoints)
+            break;
+
+        /* Translation stops when a conditional branch is enoutered.
+         * Otherwise the subsequent code could get translated several times.
+         * Also stop translation when a page boundary is reached.  This
+         * ensures prefetch aborts occur at the right place.  */
+        num_insns ++;
+    } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
+             !env->singlestep_enabled &&
+             dc->pc < next_page_start &&
+             num_insns < max_insns);
+
+#ifdef CONFIG_TRACE
+    if (tracing) {
+        trace_bb_end();
+    }
+#endif
+
+    if (tb->cflags & CF_LAST_IO) {
+        if (dc->condjmp) {
+            /* FIXME:  This can theoretically happen with self-modifying
+               code.  */
+            cpu_abort(env, "IO on conditional branch instruction");
+        }
+        gen_io_end();
+    }
+
+    /* At this stage dc->condjmp will only be set when the skipped
+       instruction was a conditional branch or trap, and the PC has
+       already been written.  */
+    if (unlikely(env->singlestep_enabled)) {
+        /* Make sure the pc is updated, and raise a debug exception.  */
+        if (dc->condjmp) {
+            gen_set_condexec(dc);
+            if (dc->is_jmp == DISAS_SWI) {
+                gen_exception(EXCP_SWI);
+            } else {
+                gen_exception(EXCP_DEBUG);
+            }
+            gen_set_label(dc->condlabel);
+        }
+        if (dc->condjmp || !dc->is_jmp) {
+            gen_set_pc_im(dc->pc);
+            dc->condjmp = 0;
+        }
+        gen_set_condexec(dc);
+        if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
+            gen_exception(EXCP_SWI);
+        } else {
+            /* FIXME: Single stepping a WFI insn will not halt
+               the CPU.  */
+            gen_exception(EXCP_DEBUG);
+        }
+    } else {
+        /* While branches must always occur at the end of an IT block,
+           there are a few other things that can cause us to terminate
+           the TB in the middel of an IT block:
+            - Exception generating instructions (bkpt, swi, undefined).
+            - Page boundaries.
+            - Hardware watchpoints.
+           Hardware breakpoints have already been handled and skip this code.
+         */
+        gen_set_condexec(dc);
+        switch(dc->is_jmp) {
+        case DISAS_NEXT:
+            gen_goto_tb(dc, 1, dc->pc);
+            break;
+        default:
+        case DISAS_JUMP:
+        case DISAS_UPDATE:
+            /* indicate that the hash table must be used to find the next TB */
+            tcg_gen_exit_tb(0);
+            break;
+        case DISAS_TB_JUMP:
+            /* nothing more to generate */
+            break;
+        case DISAS_WFI:
+            gen_helper_wfi();
+            break;
+        case DISAS_SWI:
+            gen_exception(EXCP_SWI);
+            break;
+        }
+        if (dc->condjmp) {
+            gen_set_label(dc->condlabel);
+            gen_set_condexec(dc);
+            gen_goto_tb(dc, 1, dc->pc);
+            dc->condjmp = 0;
+        }
+    }
+
+done_generating:
+    gen_icount_end(tb, num_insns);
+    *gen_opc_ptr = INDEX_op_end;
+
+#ifdef DEBUG_DISAS
+    if (loglevel & CPU_LOG_TB_IN_ASM) {
+        fprintf(logfile, "----------------\n");
+        fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+        target_disas(logfile, pc_start, dc->pc - pc_start, env->thumb);
+        fprintf(logfile, "\n");
+    }
+#endif
+    if (search_pc) {
+        j = gen_opc_ptr - gen_opc_buf;
+        lj++;
+        while (lj <= j)
+            gen_opc_instr_start[lj++] = 0;
+    } else {
+        tb->size = dc->pc - pc_start;
+        tb->icount = num_insns;
+    }
+}
+
+void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(env, tb, 0);
+}
+
+void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
+{
+    gen_intermediate_code_internal(env, tb, 1);
+}
+
+static const char *cpu_mode_names[16] = {
+  "usr", "fiq", "irq", "svc", "???", "???", "???", "abt",
+  "???", "???", "???", "und", "???", "???", "???", "sys"
+};
+
+void cpu_dump_state(CPUState *env, FILE *f,
+                    int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+                    int flags)
+{
+    int i;
+#if 0
+    union {
+        uint32_t i;
+        float s;
+    } s0, s1;
+    CPU_DoubleU d;
+    /* ??? This assumes float64 and double have the same layout.
+       Oh well, it's only debug dumps.  */
+    union {
+        float64 f64;
+        double d;
+    } d0;
+#endif
+    uint32_t psr;
+
+    for(i=0;i<16;i++) {
+        cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]);
+        if ((i % 4) == 3)
+            cpu_fprintf(f, "\n");
+        else
+            cpu_fprintf(f, " ");
+    }
+    psr = cpsr_read(env);
+    cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%d\n",
+                psr,
+                psr & (1 << 31) ? 'N' : '-',
+                psr & (1 << 30) ? 'Z' : '-',
+                psr & (1 << 29) ? 'C' : '-',
+                psr & (1 << 28) ? 'V' : '-',
+                psr & CPSR_T ? 'T' : 'A',
+                cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26);
+
+#if 0
+    for (i = 0; i < 16; i++) {
+        d.d = env->vfp.regs[i];
+        s0.i = d.l.lower;
+        s1.i = d.l.upper;
+        d0.f64 = d.d;
+        cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%08x%08x(%8g)\n",
+                    i * 2, (int)s0.i, s0.s,
+                    i * 2 + 1, (int)s1.i, s1.s,
+                    i, (int)(uint32_t)d.l.upper, (int)(uint32_t)d.l.lower,
+                    d0.d);
+    }
+    cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.xregs[ARM_VFP_FPSCR]);
+#endif
+}
+
+void gen_pc_load(CPUState *env, TranslationBlock *tb,
+                unsigned long searched_pc, int pc_pos, void *puc)
+{
+    env->regs[15] = gen_opc_pc[pc_pos];
+}
diff --git a/tcg/LICENSE b/tcg/LICENSE
new file mode 100644
index 0000000..be817fa
--- /dev/null
+++ b/tcg/LICENSE
@@ -0,0 +1,3 @@
+All the files in this directory and subdirectories are released under
+a BSD like license (see header in each file). No other license is
+accepted.
diff --git a/tcg/README b/tcg/README
new file mode 100644
index 0000000..b03432e
--- /dev/null
+++ b/tcg/README
@@ -0,0 +1,425 @@
+Tiny Code Generator - Fabrice Bellard.
+
+1) Introduction
+
+TCG (Tiny Code Generator) began as a generic backend for a C
+compiler. It was simplified to be used in QEMU. It also has its roots
+in the QOP code generator written by Paul Brook. 
+
+2) Definitions
+
+The TCG "target" is the architecture for which we generate the
+code. It is of course not the same as the "target" of QEMU which is
+the emulated architecture. As TCG started as a generic C backend used
+for cross compiling, it is assumed that the TCG target is different
+from the host, although it is never the case for QEMU.
+
+A TCG "function" corresponds to a QEMU Translated Block (TB).
+
+A TCG "temporary" is a variable only live in a basic
+block. Temporaries are allocated explicitly in each function.
+
+A TCG "local temporary" is a variable only live in a function. Local
+temporaries are allocated explicitly in each function.
+
+A TCG "global" is a variable which is live in all the functions
+(equivalent of a C global variable). They are defined before the
+functions defined. A TCG global can be a memory location (e.g. a QEMU
+CPU register), a fixed host register (e.g. the QEMU CPU state pointer)
+or a memory location which is stored in a register outside QEMU TBs
+(not implemented yet).
+
+A TCG "basic block" corresponds to a list of instructions terminated
+by a branch instruction. 
+
+3) Intermediate representation
+
+3.1) Introduction
+
+TCG instructions operate on variables which are temporaries, local
+temporaries or globals. TCG instructions and variables are strongly
+typed. Two types are supported: 32 bit integers and 64 bit
+integers. Pointers are defined as an alias to 32 bit or 64 bit
+integers depending on the TCG target word size.
+
+Each instruction has a fixed number of output variable operands, input
+variable operands and always constant operands.
+
+The notable exception is the call instruction which has a variable
+number of outputs and inputs.
+
+In the textual form, output operands usually come first, followed by
+input operands, followed by constant operands. The output type is
+included in the instruction name. Constants are prefixed with a '$'.
+
+add_i32 t0, t1, t2  (t0 <- t1 + t2)
+
+3.2) Assumptions
+
+* Basic blocks
+
+- Basic blocks end after branches (e.g. brcond_i32 instruction),
+  goto_tb and exit_tb instructions.
+- Basic blocks end before legacy dyngen operations.
+- Basic blocks start after the end of a previous basic block, at a
+  set_label instruction or after a legacy dyngen operation.
+
+After the end of a basic block, the content of temporaries is
+destroyed, but local temporaries and globals are preserved.
+
+* Floating point types are not supported yet
+
+* Pointers: depending on the TCG target, pointer size is 32 bit or 64
+  bit. The type TCG_TYPE_PTR is an alias to TCG_TYPE_I32 or
+  TCG_TYPE_I64.
+
+* Helpers:
+
+Using the tcg_gen_helper_x_y it is possible to call any function
+taking i32, i64 or pointer types. Before calling an helper, all
+globals are stored at their canonical location and it is assumed that
+the function can modify them. In the future, function modifiers will
+be allowed to tell that the helper does not read or write some globals.
+
+On some TCG targets (e.g. x86), several calling conventions are
+supported.
+
+* Branches:
+
+Use the instruction 'br' to jump to a label. Use 'jmp' to jump to an
+explicit address. Conditional branches can only jump to labels.
+
+3.3) Code Optimizations
+
+When generating instructions, you can count on at least the following
+optimizations:
+
+- Single instructions are simplified, e.g.
+
+   and_i32 t0, t0, $0xffffffff
+    
+  is suppressed.
+
+- A liveness analysis is done at the basic block level. The
+  information is used to suppress moves from a dead variable to
+  another one. It is also used to remove instructions which compute
+  dead results. The later is especially useful for condition code
+  optimization in QEMU.
+
+  In the following example:
+
+  add_i32 t0, t1, t2
+  add_i32 t0, t0, $1
+  mov_i32 t0, $1
+
+  only the last instruction is kept.
+
+3.4) Instruction Reference
+
+********* Function call
+
+* call <ret> <params> ptr
+
+call function 'ptr' (pointer type)
+
+<ret> optional 32 bit or 64 bit return value
+<params> optional 32 bit or 64 bit parameters
+
+********* Jumps/Labels
+
+* jmp t0
+
+Absolute jump to address t0 (pointer type).
+
+* set_label $label
+
+Define label 'label' at the current program point.
+
+* br $label
+
+Jump to label.
+
+* brcond_i32/i64 cond, t0, t1, label
+
+Conditional jump if t0 cond t1 is true. cond can be:
+    TCG_COND_EQ
+    TCG_COND_NE
+    TCG_COND_LT /* signed */
+    TCG_COND_GE /* signed */
+    TCG_COND_LE /* signed */
+    TCG_COND_GT /* signed */
+    TCG_COND_LTU /* unsigned */
+    TCG_COND_GEU /* unsigned */
+    TCG_COND_LEU /* unsigned */
+    TCG_COND_GTU /* unsigned */
+
+********* Arithmetic
+
+* add_i32/i64 t0, t1, t2
+
+t0=t1+t2
+
+* sub_i32/i64 t0, t1, t2
+
+t0=t1-t2
+
+* neg_i32/i64 t0, t1
+
+t0=-t1 (two's complement)
+
+* mul_i32/i64 t0, t1, t2
+
+t0=t1*t2
+
+* div_i32/i64 t0, t1, t2
+
+t0=t1/t2 (signed). Undefined behavior if division by zero or overflow.
+
+* divu_i32/i64 t0, t1, t2
+
+t0=t1/t2 (unsigned). Undefined behavior if division by zero.
+
+* rem_i32/i64 t0, t1, t2
+
+t0=t1%t2 (signed). Undefined behavior if division by zero or overflow.
+
+* remu_i32/i64 t0, t1, t2
+
+t0=t1%t2 (unsigned). Undefined behavior if division by zero.
+
+********* Logical
+
+* and_i32/i64 t0, t1, t2
+
+t0=t1&t2
+
+* or_i32/i64 t0, t1, t2
+
+t0=t1|t2
+
+* xor_i32/i64 t0, t1, t2
+
+t0=t1^t2
+
+* not_i32/i64 t0, t1
+
+t0=~t1
+
+********* Shifts
+
+* shl_i32/i64 t0, t1, t2
+
+t0=t1 << t2. Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+* shr_i32/i64 t0, t1, t2
+
+t0=t1 >> t2 (unsigned). Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+* sar_i32/i64 t0, t1, t2
+
+t0=t1 >> t2 (signed). Undefined behavior if t2 < 0 or t2 >= 32 (resp 64)
+
+********* Misc
+
+* mov_i32/i64 t0, t1
+
+t0 = t1
+
+Move t1 to t0 (both operands must have the same type).
+
+* ext8s_i32/i64 t0, t1
+ext8u_i32/i64 t0, t1
+ext16s_i32/i64 t0, t1
+ext16u_i32/i64 t0, t1
+ext32s_i64 t0, t1
+ext32u_i64 t0, t1
+
+8, 16 or 32 bit sign/zero extension (both operands must have the same type)
+
+* bswap16_i32 t0, t1
+
+16 bit byte swap on a 32 bit value. The two high order bytes must be set
+to zero.
+
+* bswap_i32 t0, t1
+
+32 bit byte swap
+
+* bswap_i64 t0, t1
+
+64 bit byte swap
+
+* discard_i32/i64 t0
+
+Indicate that the value of t0 won't be used later. It is useful to
+force dead code elimination.
+
+********* Type conversions
+
+* ext_i32_i64 t0, t1
+Convert t1 (32 bit) to t0 (64 bit) and does sign extension
+
+* extu_i32_i64 t0, t1
+Convert t1 (32 bit) to t0 (64 bit) and does zero extension
+
+* trunc_i64_i32 t0, t1
+Truncate t1 (64 bit) to t0 (32 bit)
+
+********* Load/Store
+
+* ld_i32/i64 t0, t1, offset
+ld8s_i32/i64 t0, t1, offset
+ld8u_i32/i64 t0, t1, offset
+ld16s_i32/i64 t0, t1, offset
+ld16u_i32/i64 t0, t1, offset
+ld32s_i64 t0, t1, offset
+ld32u_i64 t0, t1, offset
+
+t0 = read(t1 + offset)
+Load 8, 16, 32 or 64 bits with or without sign extension from host memory. 
+offset must be a constant.
+
+* st_i32/i64 t0, t1, offset
+st8_i32/i64 t0, t1, offset
+st16_i32/i64 t0, t1, offset
+st32_i64 t0, t1, offset
+
+write(t0, t1 + offset)
+Write 8, 16, 32 or 64 bits to host memory.
+
+********* QEMU specific operations
+
+* tb_exit t0
+
+Exit the current TB and return the value t0 (word type).
+
+* goto_tb index
+
+Exit the current TB and jump to the TB index 'index' (constant) if the
+current TB was linked to this TB. Otherwise execute the next
+instructions.
+
+* qemu_ld_i32/i64 t0, t1, flags
+qemu_ld8u_i32/i64 t0, t1, flags
+qemu_ld8s_i32/i64 t0, t1, flags
+qemu_ld16u_i32/i64 t0, t1, flags
+qemu_ld16s_i32/i64 t0, t1, flags
+qemu_ld32u_i64 t0, t1, flags
+qemu_ld32s_i64 t0, t1, flags
+
+Load data at the QEMU CPU address t1 into t0. t1 has the QEMU CPU
+address type. 'flags' contains the QEMU memory index (selects user or
+kernel access) for example.
+
+* qemu_st_i32/i64 t0, t1, flags
+qemu_st8_i32/i64 t0, t1, flags
+qemu_st16_i32/i64 t0, t1, flags
+qemu_st32_i64 t0, t1, flags
+
+Store the data t0 at the QEMU CPU Address t1. t1 has the QEMU CPU
+address type. 'flags' contains the QEMU memory index (selects user or
+kernel access) for example.
+
+Note 1: Some shortcuts are defined when the last operand is known to be
+a constant (e.g. addi for add, movi for mov).
+
+Note 2: When using TCG, the opcodes must never be generated directly
+as some of them may not be available as "real" opcodes. Always use the
+function tcg_gen_xxx(args).
+
+4) Backend
+
+tcg-target.h contains the target specific definitions. tcg-target.c
+contains the target specific code.
+
+4.1) Assumptions
+
+The target word size (TCG_TARGET_REG_BITS) is expected to be 32 bit or
+64 bit. It is expected that the pointer has the same size as the word.
+
+On a 32 bit target, all 64 bit operations are converted to 32 bits. A
+few specific operations must be implemented to allow it (see add2_i32,
+sub2_i32, brcond2_i32).
+
+Floating point operations are not supported in this version. A
+previous incarnation of the code generator had full support of them,
+but it is better to concentrate on integer operations first.
+
+On a 64 bit target, no assumption is made in TCG about the storage of
+the 32 bit values in 64 bit registers.
+
+4.2) Constraints
+
+GCC like constraints are used to define the constraints of every
+instruction. Memory constraints are not supported in this
+version. Aliases are specified in the input operands as for GCC.
+
+A target can define specific register or constant constraints. If an
+operation uses a constant input constraint which does not allow all
+constants, it must also accept registers in order to have a fallback.
+
+The movi_i32 and movi_i64 operations must accept any constants.
+
+The mov_i32 and mov_i64 operations must accept any registers of the
+same type.
+
+The ld/st instructions must accept signed 32 bit constant offsets. It
+can be implemented by reserving a specific register to compute the
+address if the offset is too big.
+
+The ld/st instructions must accept any destination (ld) or source (st)
+register.
+
+4.3) Function call assumptions
+
+- The only supported types for parameters and return value are: 32 and
+  64 bit integers and pointer.
+- The stack grows downwards.
+- The first N parameters are passed in registers.
+- The next parameters are passed on the stack by storing them as words.
+- Some registers are clobbered during the call. 
+- The function can return 0 or 1 value in registers. On a 32 bit
+  target, functions must be able to return 2 values in registers for
+  64 bit return type.
+
+5) Migration from dyngen to TCG
+
+TCG is backward compatible with QEMU "dyngen" operations. It means
+that TCG instructions can be freely mixed with dyngen operations. It
+is expected that QEMU targets will be progressively fully converted to
+TCG. Once a target is fully converted to TCG, it will be possible
+to apply more optimizations because more registers will be free for
+the generated code.
+
+The exception model is the same as the dyngen one.
+
+6) Recommended coding rules for best performance
+
+- Use globals to represent the parts of the QEMU CPU state which are
+  often modified, e.g. the integer registers and the condition
+  codes. TCG will be able to use host registers to store them.
+
+- Avoid globals stored in fixed registers. They must be used only to
+  store the pointer to the CPU state and possibly to store a pointer
+  to a register window. The other uses are to ensure backward
+  compatibility with dyngen during the porting a new target to TCG.
+
+- Use temporaries. Use local temporaries only when really needed,
+  e.g. when you need to use a value after a jump. Local temporaries
+  introduce a performance hit in the current TCG implementation: their
+  content is saved to memory at end of each basic block.
+
+- Free temporaries and local temporaries when they are no longer used
+  (tcg_temp_free). Since tcg_const_x() also creates a temporary, you
+  should free it after it is used. Freeing temporaries does not yield
+  a better generated code, but it reduces the memory usage of TCG and
+  the speed of the translation.
+
+- Don't hesitate to use helpers for complicated or seldom used target
+  intructions. There is little performance advantage in using TCG to
+  implement target instructions taking more than about twenty TCG
+  instructions.
+
+- Use the 'discard' instruction if you know that TCG won't be able to
+  prove that a given global is "dead" at a given program point. The
+  x86 target uses it to improve the condition codes optimisation.
diff --git a/tcg/TODO b/tcg/TODO
new file mode 100644
index 0000000..5ca35e9
--- /dev/null
+++ b/tcg/TODO
@@ -0,0 +1,15 @@
+- Add new instructions such as: andnot, ror, rol, setcond, clz, ctz,
+  popcnt.
+
+- See if it is worth exporting mul2, mulu2, div2, divu2. 
+
+- Support of globals saved in fixed registers between TBs.
+
+Ideas:
+
+- Move the slow part of the qemu_ld/st ops after the end of the TB.
+
+- Change exception syntax to get closer to QOP system (exception
+  parameters given with a specific instruction).
+
+- Add float and vector support.
diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c
new file mode 100644
index 0000000..dee1ebc
--- /dev/null
+++ b/tcg/arm/tcg-target.c
@@ -0,0 +1,1584 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+const char *tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "%r0",
+    "%r1",
+    "%r2",
+    "%r3",
+    "%r4",
+    "%r5",
+    "%r6",
+    "%r7",
+    "%r8",
+    "%r9",
+    "%r10",
+    "%r11",
+    "%r12",
+    "%r13",
+    "%r14",
+};
+
+int tcg_target_reg_alloc_order[] = {
+    TCG_REG_R0,
+    TCG_REG_R1,
+    TCG_REG_R2,
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+};
+
+const int tcg_target_call_iarg_regs[4] = {
+    TCG_REG_R0, TCG_REG_R1, TCG_REG_R2, TCG_REG_R3
+};
+const int tcg_target_call_oarg_regs[2] = {
+    TCG_REG_R0, TCG_REG_R1
+};
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+                tcg_target_long value, tcg_target_long addend)
+{
+    switch (type) {
+    case R_ARM_ABS32:
+        *(uint32_t *) code_ptr = value;
+        break;
+
+    case R_ARM_CALL:
+    case R_ARM_JUMP24:
+    default:
+        tcg_abort();
+
+    case R_ARM_PC24:
+        *(uint32_t *) code_ptr = ((*(uint32_t *) code_ptr) & 0xff000000) |
+                (((value - ((tcg_target_long) code_ptr + 8)) >> 2) & 0xffffff);
+        break;
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    return 4;
+}
+
+/* parse target specific constraints */
+int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch (ct_str[0]) {
+    case 'r':
+#ifndef CONFIG_SOFTMMU
+    case 'd':
+    case 'D':
+    case 'x':
+    case 'X':
+#endif
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        break;
+
+#ifdef CONFIG_SOFTMMU
+    /* qemu_ld/st inputs (unless 'X', 'd' or 'D') */
+    case 'x':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+        break;
+
+    /* qemu_ld64 data_reg */
+    case 'd':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        /* r1 is still needed to load data_reg2, so don't use it.  */
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+        break;
+
+    /* qemu_ld/st64 data_reg2 */
+    case 'D':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        /* r0, r1 and optionally r2 will be overwritten by the address
+         * and the low word of data, so don't use these.  */
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+# if TARGET_LONG_BITS == 64
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R2);
+# endif
+        break;
+
+# if TARGET_LONG_BITS == 64
+    /* qemu_ld/st addr_reg2 */
+    case 'X':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        /* r0 will be overwritten by the low word of base, so don't use it.  */
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+        break;
+# endif
+#endif
+
+    case '1':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+        break;
+
+    case '2':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, (1 << TCG_TARGET_NB_REGS) - 1);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R0);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R1);
+        break;
+
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+
+    return 0;
+}
+
+/* Test if a constant matches the constraint.
+ * TODO: define constraints for:
+ *
+ * ldr/str offset:   between -0xfff and 0xfff
+ * ldrh/strh offset: between -0xff and 0xff
+ * mov operand2:     values represented with x << (2 * y), x < 0x100
+ * add, sub, eor...: ditto
+ */
+static inline int tcg_target_const_match(tcg_target_long val,
+                const TCGArgConstraint *arg_ct)
+{
+    int ct;
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    else
+        return 0;
+}
+
+enum arm_data_opc_e {
+    ARITH_AND = 0x0,
+    ARITH_EOR = 0x1,
+    ARITH_SUB = 0x2,
+    ARITH_RSB = 0x3,
+    ARITH_ADD = 0x4,
+    ARITH_ADC = 0x5,
+    ARITH_SBC = 0x6,
+    ARITH_RSC = 0x7,
+    ARITH_TST = 0x8,
+    ARITH_CMP = 0xa,
+    ARITH_CMN = 0xb,
+    ARITH_ORR = 0xc,
+    ARITH_MOV = 0xd,
+    ARITH_BIC = 0xe,
+    ARITH_MVN = 0xf,
+};
+
+#define TO_CPSR(opc) \
+  ((opc == ARITH_CMP || opc == ARITH_CMN || opc == ARITH_TST) << 20)
+
+#define SHIFT_IMM_LSL(im)	(((im) << 7) | 0x00)
+#define SHIFT_IMM_LSR(im)	(((im) << 7) | 0x20)
+#define SHIFT_IMM_ASR(im)	(((im) << 7) | 0x40)
+#define SHIFT_IMM_ROR(im)	(((im) << 7) | 0x60)
+#define SHIFT_REG_LSL(rs)	(((rs) << 8) | 0x10)
+#define SHIFT_REG_LSR(rs)	(((rs) << 8) | 0x30)
+#define SHIFT_REG_ASR(rs)	(((rs) << 8) | 0x50)
+#define SHIFT_REG_ROR(rs)	(((rs) << 8) | 0x70)
+
+enum arm_cond_code_e {
+    COND_EQ = 0x0,
+    COND_NE = 0x1,
+    COND_CS = 0x2,	/* Unsigned greater or equal */
+    COND_CC = 0x3,	/* Unsigned less than */
+    COND_MI = 0x4,	/* Negative */
+    COND_PL = 0x5,	/* Zero or greater */
+    COND_VS = 0x6,	/* Overflow */
+    COND_VC = 0x7,	/* No overflow */
+    COND_HI = 0x8,	/* Unsigned greater than */
+    COND_LS = 0x9,	/* Unsigned less or equal */
+    COND_GE = 0xa,
+    COND_LT = 0xb,
+    COND_GT = 0xc,
+    COND_LE = 0xd,
+    COND_AL = 0xe,
+};
+
+static const uint8_t tcg_cond_to_arm_cond[10] = {
+    [TCG_COND_EQ] = COND_EQ,
+    [TCG_COND_NE] = COND_NE,
+    [TCG_COND_LT] = COND_LT,
+    [TCG_COND_GE] = COND_GE,
+    [TCG_COND_LE] = COND_LE,
+    [TCG_COND_GT] = COND_GT,
+    /* unsigned */
+    [TCG_COND_LTU] = COND_CC,
+    [TCG_COND_GEU] = COND_CS,
+    [TCG_COND_LEU] = COND_LS,
+    [TCG_COND_GTU] = COND_HI,
+};
+
+static inline void tcg_out_bx(TCGContext *s, int cond, int rn)
+{
+    tcg_out32(s, (cond << 28) | 0x012fff10 | rn);
+}
+
+static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset)
+{
+    tcg_out32(s, (cond << 28) | 0x0a000000 |
+                    (((offset - 8) >> 2) & 0x00ffffff));
+}
+
+static inline void tcg_out_b_noaddr(TCGContext *s, int cond)
+{
+#ifdef WORDS_BIGENDIAN
+    tcg_out8(s, (cond << 4) | 0x0a);
+    s->code_ptr += 3;
+#else
+    s->code_ptr += 3;
+    tcg_out8(s, (cond << 4) | 0x0a);
+#endif
+}
+
+static inline void tcg_out_bl(TCGContext *s, int cond, int32_t offset)
+{
+    tcg_out32(s, (cond << 28) | 0x0b000000 |
+                    (((offset - 8) >> 2) & 0x00ffffff));
+}
+
+static inline void tcg_out_dat_reg(TCGContext *s,
+                int cond, int opc, int rd, int rn, int rm, int shift)
+{
+    tcg_out32(s, (cond << 28) | (0 << 25) | (opc << 21) | TO_CPSR(opc) |
+                    (rn << 16) | (rd << 12) | shift | rm);
+}
+
+static inline void tcg_out_dat_reg2(TCGContext *s,
+                int cond, int opc0, int opc1, int rd0, int rd1,
+                int rn0, int rn1, int rm0, int rm1, int shift)
+{
+    tcg_out32(s, (cond << 28) | (0 << 25) | (opc0 << 21) | (1 << 20) |
+                    (rn0 << 16) | (rd0 << 12) | shift | rm0);
+    tcg_out32(s, (cond << 28) | (0 << 25) | (opc1 << 21) |
+                    (rn1 << 16) | (rd1 << 12) | shift | rm1);
+}
+
+static inline void tcg_out_dat_imm(TCGContext *s,
+                int cond, int opc, int rd, int rn, int im)
+{
+    tcg_out32(s, (cond << 28) | (1 << 25) | (opc << 21) | TO_CPSR(opc) |
+                    (rn << 16) | (rd << 12) | im);
+}
+
+static inline void tcg_out_movi32(TCGContext *s,
+                int cond, int rd, int32_t arg)
+{
+    int offset = (uint32_t) arg - ((uint32_t) s->code_ptr + 8);
+
+    /* TODO: This is very suboptimal, we can easily have a constant
+     * pool somewhere after all the instructions.  */
+
+    if (arg < 0 && arg > -0x100)
+        return tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, (~arg) & 0xff);
+
+    if (offset < 0x100 && offset > -0x100)
+        return offset >= 0 ?
+                tcg_out_dat_imm(s, cond, ARITH_ADD, rd, 15, offset) :
+                tcg_out_dat_imm(s, cond, ARITH_SUB, rd, 15, -offset);
+
+    tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, arg & 0xff);
+    if (arg & 0x0000ff00)
+        tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd,
+                        ((arg >>  8) & 0xff) | 0xc00);
+    if (arg & 0x00ff0000)
+        tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd,
+                        ((arg >> 16) & 0xff) | 0x800);
+    if (arg & 0xff000000)
+        tcg_out_dat_imm(s, cond, ARITH_ORR, rd, rd,
+                        ((arg >> 24) & 0xff) | 0x400);
+}
+
+static inline void tcg_out_mul32(TCGContext *s,
+                int cond, int rd, int rs, int rm)
+{
+    if (rd != rm)
+        tcg_out32(s, (cond << 28) | (rd << 16) | (0 << 12) |
+                        (rs << 8) | 0x90 | rm);
+    else if (rd != rs)
+        tcg_out32(s, (cond << 28) | (rd << 16) | (0 << 12) |
+                        (rm << 8) | 0x90 | rs);
+    else {
+        tcg_out32(s, (cond << 28) | ( 8 << 16) | (0 << 12) |
+                        (rs << 8) | 0x90 | rm);
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        rd, 0, 8, SHIFT_IMM_LSL(0));
+    }
+}
+
+static inline void tcg_out_umull32(TCGContext *s,
+                int cond, int rd0, int rd1, int rs, int rm)
+{
+    if (rd0 != rm && rd1 != rm)
+        tcg_out32(s, (cond << 28) | 0x800090 |
+                        (rd1 << 16) | (rd0 << 12) | (rs << 8) | rm);
+    else if (rd0 != rs && rd1 != rs)
+        tcg_out32(s, (cond << 28) | 0x800090 |
+                        (rd1 << 16) | (rd0 << 12) | (rm << 8) | rs);
+    else {
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        TCG_REG_R8, 0, rm, SHIFT_IMM_LSL(0));
+        tcg_out32(s, (cond << 28) | 0x800098 |
+                        (rd1 << 16) | (rd0 << 12) | (rs << 8));
+    }
+}
+
+static inline void tcg_out_smull32(TCGContext *s,
+                int cond, int rd0, int rd1, int rs, int rm)
+{
+    if (rd0 != rm && rd1 != rm)
+        tcg_out32(s, (cond << 28) | 0xc00090 |
+                        (rd1 << 16) | (rd0 << 12) | (rs << 8) | rm);
+    else if (rd0 != rs && rd1 != rs)
+        tcg_out32(s, (cond << 28) | 0xc00090 |
+                        (rd1 << 16) | (rd0 << 12) | (rm << 8) | rs);
+    else {
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        TCG_REG_R8, 0, rm, SHIFT_IMM_LSL(0));
+        tcg_out32(s, (cond << 28) | 0xc00098 |
+                        (rd1 << 16) | (rd0 << 12) | (rs << 8));
+    }
+}
+
+static inline void tcg_out_ld32_12(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x05900000 |
+                        (rn << 16) | (rd << 12) | (im & 0xfff));
+    else
+        tcg_out32(s, (cond << 28) | 0x05100000 |
+                        (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_st32_12(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x05800000 |
+                        (rn << 16) | (rd << 12) | (im & 0xfff));
+    else
+        tcg_out32(s, (cond << 28) | 0x05000000 |
+                        (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_ld32_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x07900000 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st32_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x07800000 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+/* Register pre-increment with base writeback.  */
+static inline void tcg_out_ld32_rwb(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x07b00000 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st32_rwb(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x07a00000 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld16u_8(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x01d000b0 |
+                        (rn << 16) | (rd << 12) |
+                        ((im & 0xf0) << 4) | (im & 0xf));
+    else
+        tcg_out32(s, (cond << 28) | 0x015000b0 |
+                        (rn << 16) | (rd << 12) |
+                        (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_st16u_8(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x01c000b0 |
+                        (rn << 16) | (rd << 12) |
+                        ((im & 0xf0) << 4) | (im & 0xf));
+    else
+        tcg_out32(s, (cond << 28) | 0x014000b0 |
+                        (rn << 16) | (rd << 12) |
+                        (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_ld16u_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x019000b0 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st16u_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x018000b0 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld16s_8(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x01d000f0 |
+                        (rn << 16) | (rd << 12) |
+                        ((im & 0xf0) << 4) | (im & 0xf));
+    else
+        tcg_out32(s, (cond << 28) | 0x015000f0 |
+                        (rn << 16) | (rd << 12) |
+                        (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_st16s_8(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x01c000f0 |
+                        (rn << 16) | (rd << 12) |
+                        ((im & 0xf0) << 4) | (im & 0xf));
+    else
+        tcg_out32(s, (cond << 28) | 0x014000f0 |
+                        (rn << 16) | (rd << 12) |
+                        (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_ld16s_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x019000f0 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st16s_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x018000f0 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld8_12(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x05d00000 |
+                        (rn << 16) | (rd << 12) | (im & 0xfff));
+    else
+        tcg_out32(s, (cond << 28) | 0x05500000 |
+                        (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_st8_12(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x05c00000 |
+                        (rn << 16) | (rd << 12) | (im & 0xfff));
+    else
+        tcg_out32(s, (cond << 28) | 0x05400000 |
+                        (rn << 16) | (rd << 12) | ((-im) & 0xfff));
+}
+
+static inline void tcg_out_ld8_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x07d00000 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st8_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x07c00000 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld8s_8(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x01d000d0 |
+                        (rn << 16) | (rd << 12) |
+                        ((im & 0xf0) << 4) | (im & 0xf));
+    else
+        tcg_out32(s, (cond << 28) | 0x015000d0 |
+                        (rn << 16) | (rd << 12) |
+                        (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_st8s_8(TCGContext *s, int cond,
+                int rd, int rn, tcg_target_long im)
+{
+    if (im >= 0)
+        tcg_out32(s, (cond << 28) | 0x01c000d0 |
+                        (rn << 16) | (rd << 12) |
+                        ((im & 0xf0) << 4) | (im & 0xf));
+    else
+        tcg_out32(s, (cond << 28) | 0x014000d0 |
+                        (rn << 16) | (rd << 12) |
+                        (((-im) & 0xf0) << 4) | ((-im) & 0xf));
+}
+
+static inline void tcg_out_ld8s_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x019000d0 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_st8s_r(TCGContext *s, int cond,
+                int rd, int rn, int rm)
+{
+    tcg_out32(s, (cond << 28) | 0x018000d0 |
+                    (rn << 16) | (rd << 12) | rm);
+}
+
+static inline void tcg_out_ld32u(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xfff || offset < -0xfff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_ld32_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_ld32_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_st32(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xfff || offset < -0xfff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_st32_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_st32_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld16u(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xff || offset < -0xff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_ld16u_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld16s(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xff || offset < -0xff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_ld16s_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_st16u(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xff || offset < -0xff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_st16u_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_st16u_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld8u(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xfff || offset < -0xfff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_ld8_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_ld8s(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xff || offset < -0xff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_ld8s_8(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_st8u(TCGContext *s, int cond,
+                int rd, int rn, int32_t offset)
+{
+    if (offset > 0xfff || offset < -0xfff) {
+        tcg_out_movi32(s, cond, TCG_REG_R8, offset);
+        tcg_out_st8_r(s, cond, rd, rn, TCG_REG_R8);
+    } else
+        tcg_out_st8_12(s, cond, rd, rn, offset);
+}
+
+static inline void tcg_out_goto(TCGContext *s, int cond, uint32_t addr)
+{
+    int32_t val;
+
+    val = addr - (tcg_target_long) s->code_ptr;
+    if (val - 8 < 0x01fffffd && val - 8 > -0x01fffffd)
+        tcg_out_b(s, cond, val);
+    else {
+#if 1
+        tcg_abort();
+#else
+        if (cond == COND_AL) {
+            tcg_out_ld32_12(s, COND_AL, 15, 15, -4);
+            tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */
+        } else {
+            tcg_out_movi32(s, cond, TCG_REG_R8, val - 8);
+            tcg_out_dat_reg(s, cond, ARITH_ADD,
+                            15, 15, TCG_REG_R8, SHIFT_IMM_LSL(0));
+        }
+#endif
+    }
+}
+
+static inline void tcg_out_call(TCGContext *s, int cond, uint32_t addr)
+{
+    int32_t val;
+
+#ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R8, 0, 14, SHIFT_IMM_LSL(0));
+#endif
+
+    val = addr - (tcg_target_long) s->code_ptr;
+    if (val < 0x01fffffd && val > -0x01fffffd)
+        tcg_out_bl(s, cond, val);
+    else {
+#if 1
+        tcg_abort();
+#else
+        if (cond == COND_AL) {
+            tcg_out_dat_imm(s, cond, ARITH_ADD, 14, 15, 4);
+            tcg_out_ld32_12(s, COND_AL, 15, 15, -4);
+            tcg_out32(s, addr); /* XXX: This is l->u.value, can we use it? */
+        } else {
+            tcg_out_movi32(s, cond, TCG_REG_R9, addr);
+            tcg_out_dat_imm(s, cond, ARITH_MOV, 14, 0, 15);
+            tcg_out_bx(s, cond, TCG_REG_R9);
+        }
+#endif
+    }
+
+#ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, TCG_REG_R8, SHIFT_IMM_LSL(0));
+#endif
+}
+
+static inline void tcg_out_callr(TCGContext *s, int cond, int arg)
+{
+#ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, TCG_REG_R8, 0, 14, SHIFT_IMM_LSL(0));
+#endif
+    /* TODO: on ARMv5 and ARMv6 replace with tcg_out_blx(s, cond, arg);  */
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 15, SHIFT_IMM_LSL(0));
+    tcg_out_bx(s, cond, arg);
+#ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, TCG_REG_R8, SHIFT_IMM_LSL(0));
+#endif
+}
+
+static inline void tcg_out_goto_label(TCGContext *s, int cond, int label_index)
+{
+    TCGLabel *l = &s->labels[label_index];
+
+    if (l->has_value)
+        tcg_out_goto(s, cond, l->u.value);
+    else if (cond == COND_AL) {
+        tcg_out_ld32_12(s, COND_AL, 15, 15, -4);
+        tcg_out_reloc(s, s->code_ptr, R_ARM_ABS32, label_index, 31337);
+        s->code_ptr += 4;
+    } else {
+        /* Probably this should be preferred even for COND_AL... */
+        tcg_out_reloc(s, s->code_ptr, R_ARM_PC24, label_index, 31337);
+        tcg_out_b_noaddr(s, cond);
+    }
+}
+
+static void tcg_out_div_helper(TCGContext *s, int cond, const TCGArg *args,
+                void *helper_div, void *helper_rem, int shift)
+{
+    int div_reg = args[0];
+    int rem_reg = args[1];
+
+    /* stmdb sp!, { r0 - r3, ip, lr } */
+    /* (Note that we need an even number of registers as per EABI) */
+    tcg_out32(s, (cond << 28) | 0x092d500f);
+
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 0, 0, args[2], SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 1, 0, args[3], SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 2, 0, args[4], SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 3, 0, 2, shift);
+
+    tcg_out_call(s, cond, (uint32_t) helper_div);
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 0, SHIFT_IMM_LSL(0));
+
+    /* ldmia sp, { r0 - r3, fp, lr } */
+    tcg_out32(s, (cond << 28) | 0x089d500f);
+
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 0, 0, args[2], SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 1, 0, args[3], SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 2, 0, args[4], SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 3, 0, 2, shift);
+
+    tcg_out_call(s, cond, (uint32_t) helper_rem);
+
+    tcg_out_dat_reg(s, cond, ARITH_MOV, rem_reg, 0, 0, SHIFT_IMM_LSL(0));
+    tcg_out_dat_reg(s, cond, ARITH_MOV, div_reg, 0, 8, SHIFT_IMM_LSL(0));
+
+    /* ldr r0, [sp], #4 */
+    if (rem_reg != 0 && div_reg != 0)
+        tcg_out32(s, (cond << 28) | 0x04bd0004);
+    /* ldr r1, [sp], #4 */
+    if (rem_reg != 1 && div_reg != 1)
+        tcg_out32(s, (cond << 28) | 0x04bd1004);
+    /* ldr r2, [sp], #4 */
+    if (rem_reg != 2 && div_reg != 2)
+        tcg_out32(s, (cond << 28) | 0x04bd2004);
+    /* ldr r3, [sp], #4 */
+    if (rem_reg != 3 && div_reg != 3)
+        tcg_out32(s, (cond << 28) | 0x04bd3004);
+    /* ldr ip, [sp], #4 */
+    if (rem_reg != 12 && div_reg != 12)
+        tcg_out32(s, (cond << 28) | 0x04bdc004);
+    /* ldr lr, [sp], #4 */
+    if (rem_reg != 14 && div_reg != 14)
+        tcg_out32(s, (cond << 28) | 0x04bde004);
+}
+
+#ifdef CONFIG_SOFTMMU
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+#define TLB_SHIFT	(CPU_TLB_ENTRY_BITS + CPU_TLB_BITS)
+
+static inline void tcg_out_qemu_ld(TCGContext *s, int cond,
+                const TCGArg *args, int opc)
+{
+    int addr_reg, data_reg, data_reg2;
+#ifdef CONFIG_SOFTMMU
+    int mem_index, s_bits;
+# if TARGET_LONG_BITS == 64
+    int addr_reg2;
+# endif
+    uint32_t *label_ptr;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0; /* surpress warning */
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+#ifdef CONFIG_SOFTMMU
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    /* Should generate something like the following:
+     *  shr r8, addr_reg, #TARGET_PAGE_BITS
+     *  and r0, r8, #(CPU_TLB_SIZE - 1)   @ Assumption: CPU_TLB_BITS <= 8
+     *  add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS
+     */
+#  if CPU_TLB_BITS > 8
+#   error
+#  endif
+    tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                    8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS));
+    tcg_out_dat_imm(s, COND_AL, ARITH_AND,
+                    0, 8, CPU_TLB_SIZE - 1);
+    tcg_out_dat_reg(s, COND_AL, ARITH_ADD,
+                    0, TCG_AREG0, 0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS));
+    /* In the
+     *  ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_read))]
+     * below, the offset is likely to exceed 12 bits if mem_index != 0 and
+     * not exceed otherwise, so use an
+     *  add r0, r0, #(mem_index * sizeof *CPUState.tlb_table)
+     * before.
+     */
+    if (mem_index)
+        tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 0, 0,
+                        (mem_index << (TLB_SHIFT & 1)) |
+                        ((16 - (TLB_SHIFT >> 1)) << 8));
+    tcg_out_ld32_12(s, COND_AL, 1, 0,
+                    offsetof(CPUState, tlb_table[0][0].addr_read));
+    tcg_out_dat_reg(s, COND_AL, ARITH_CMP,
+                    0, 1, 8, SHIFT_IMM_LSL(TARGET_PAGE_BITS));
+    /* Check alignment.  */
+    if (s_bits)
+        tcg_out_dat_imm(s, COND_EQ, ARITH_TST,
+                        0, addr_reg, (1 << s_bits) - 1);
+#  if TARGET_LONG_BITS == 64
+    /* XXX: possibly we could use a block data load or writeback in
+     * the first access.  */
+    tcg_out_ld32_12(s, COND_EQ, 1, 0,
+                    offsetof(CPUState, tlb_table[0][0].addr_read) + 4);
+    tcg_out_dat_reg(s, COND_EQ, ARITH_CMP,
+                    0, 1, addr_reg2, SHIFT_IMM_LSL(0));
+#  endif
+    tcg_out_ld32_12(s, COND_EQ, 1, 0,
+                    offsetof(CPUState, tlb_table[0][0].addend));
+
+    switch (opc) {
+    case 0:
+        tcg_out_ld8_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 0 | 4:
+        tcg_out_ld8s_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 1:
+        tcg_out_ld16u_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 1 | 4:
+        tcg_out_ld16s_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 2:
+    default:
+        tcg_out_ld32_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 3:
+        tcg_out_ld32_rwb(s, COND_EQ, data_reg, 1, addr_reg);
+        tcg_out_ld32_12(s, COND_EQ, data_reg2, 1, 4);
+        break;
+    }
+
+    label_ptr = (void *) s->code_ptr;
+    tcg_out_b(s, COND_EQ, 8);
+
+# ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 14, SHIFT_IMM_LSL(0));
+# endif
+
+    /* TODO: move this code to where the constants pool will be */
+    if (addr_reg)
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        0, 0, addr_reg, SHIFT_IMM_LSL(0));
+# if TARGET_LONG_BITS == 32
+    tcg_out_dat_imm(s, cond, ARITH_MOV, 1, 0, mem_index);
+# else
+    if (addr_reg2 != 1)
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        1, 0, addr_reg2, SHIFT_IMM_LSL(0));
+    tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index);
+# endif
+    tcg_out_bl(s, cond, (tcg_target_long) qemu_ld_helpers[s_bits] -
+                    (tcg_target_long) s->code_ptr);
+
+    switch (opc) {
+    case 0 | 4:
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        0, 0, 0, SHIFT_IMM_LSL(24));
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        data_reg, 0, 0, SHIFT_IMM_ASR(24));
+        break;
+    case 1 | 4:
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        0, 0, 0, SHIFT_IMM_LSL(16));
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        data_reg, 0, 0, SHIFT_IMM_ASR(16));
+        break;
+    case 0:
+    case 1:
+    case 2:
+    default:
+        if (data_reg)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            data_reg, 0, 0, SHIFT_IMM_LSL(0));
+        break;
+    case 3:
+        if (data_reg != 0)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            data_reg, 0, 0, SHIFT_IMM_LSL(0));
+        if (data_reg2 != 1)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            data_reg2, 0, 1, SHIFT_IMM_LSL(0));
+        break;
+    }
+
+# ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 8, SHIFT_IMM_LSL(0));
+# endif
+
+    *label_ptr += ((void *) s->code_ptr - (void *) label_ptr - 8) >> 2;
+#else
+    switch (opc) {
+    case 0:
+        tcg_out_ld8_12(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 0 | 4:
+        tcg_out_ld8s_8(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 1:
+        tcg_out_ld16u_8(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 1 | 4:
+        tcg_out_ld16s_8(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 2:
+    default:
+        tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 3:
+        /* TODO: use block load -
+         * check that data_reg2 > data_reg or the other way */
+        tcg_out_ld32_12(s, COND_AL, data_reg, addr_reg, 0);
+        tcg_out_ld32_12(s, COND_AL, data_reg2, addr_reg, 4);
+        break;
+    }
+#endif
+}
+
+static inline void tcg_out_qemu_st(TCGContext *s, int cond,
+                const TCGArg *args, int opc)
+{
+    int addr_reg, data_reg, data_reg2;
+#ifdef CONFIG_SOFTMMU
+    int mem_index, s_bits;
+# if TARGET_LONG_BITS == 64
+    int addr_reg2;
+# endif
+    uint32_t *label_ptr;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0; /* surpress warning */
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+#ifdef CONFIG_SOFTMMU
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    /* Should generate something like the following:
+     *  shr r8, addr_reg, #TARGET_PAGE_BITS
+     *  and r0, r8, #(CPU_TLB_SIZE - 1)   @ Assumption: CPU_TLB_BITS <= 8
+     *  add r0, env, r0 lsl #CPU_TLB_ENTRY_BITS
+     */
+    tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                    8, 0, addr_reg, SHIFT_IMM_LSR(TARGET_PAGE_BITS));
+    tcg_out_dat_imm(s, COND_AL, ARITH_AND,
+                    0, 8, CPU_TLB_SIZE - 1);
+    tcg_out_dat_reg(s, COND_AL, ARITH_ADD,
+                    0, TCG_AREG0, 0, SHIFT_IMM_LSL(CPU_TLB_ENTRY_BITS));
+    /* In the
+     *  ldr r1 [r0, #(offsetof(CPUState, tlb_table[mem_index][0].addr_write))]
+     * below, the offset is likely to exceed 12 bits if mem_index != 0 and
+     * not exceed otherwise, so use an
+     *  add r0, r0, #(mem_index * sizeof *CPUState.tlb_table)
+     * before.
+     */
+    if (mem_index)
+        tcg_out_dat_imm(s, COND_AL, ARITH_ADD, 0, 0,
+                        (mem_index << (TLB_SHIFT & 1)) |
+                        ((16 - (TLB_SHIFT >> 1)) << 8));
+    tcg_out_ld32_12(s, COND_AL, 1, 0,
+                    offsetof(CPUState, tlb_table[0][0].addr_write));
+    tcg_out_dat_reg(s, COND_AL, ARITH_CMP,
+                    0, 1, 8, SHIFT_IMM_LSL(TARGET_PAGE_BITS));
+    /* Check alignment.  */
+    if (s_bits)
+        tcg_out_dat_imm(s, COND_EQ, ARITH_TST,
+                        0, addr_reg, (1 << s_bits) - 1);
+#  if TARGET_LONG_BITS == 64
+    /* XXX: possibly we could use a block data load or writeback in
+     * the first access.  */
+    tcg_out_ld32_12(s, COND_EQ, 1, 0,
+                    offsetof(CPUState, tlb_table[0][0].addr_write)
+                    + 4);
+    tcg_out_dat_reg(s, COND_EQ, ARITH_CMP,
+                    0, 1, addr_reg2, SHIFT_IMM_LSL(0));
+#  endif
+    tcg_out_ld32_12(s, COND_EQ, 1, 0,
+                    offsetof(CPUState, tlb_table[0][0].addend));
+
+    switch (opc) {
+    case 0:
+        tcg_out_st8_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 0 | 4:
+        tcg_out_st8s_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 1:
+        tcg_out_st16u_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 1 | 4:
+        tcg_out_st16s_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 2:
+    default:
+        tcg_out_st32_r(s, COND_EQ, data_reg, addr_reg, 1);
+        break;
+    case 3:
+        tcg_out_st32_rwb(s, COND_EQ, data_reg, 1, addr_reg);
+        tcg_out_st32_12(s, COND_EQ, data_reg2, 1, 4);
+        break;
+    }
+
+    label_ptr = (void *) s->code_ptr;
+    tcg_out_b(s, COND_EQ, 8);
+
+    /* TODO: move this code to where the constants pool will be */
+    if (addr_reg)
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        0, 0, addr_reg, SHIFT_IMM_LSL(0));
+# if TARGET_LONG_BITS == 32
+    switch (opc) {
+    case 0:
+        tcg_out_dat_imm(s, cond, ARITH_AND, 1, data_reg, 0xff);
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index);
+        break;
+    case 1:
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        1, 0, data_reg, SHIFT_IMM_LSL(16));
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        1, 0, 1, SHIFT_IMM_LSR(16));
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index);
+        break;
+    case 2:
+        if (data_reg != 1)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            1, 0, data_reg, SHIFT_IMM_LSL(0));
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 2, 0, mem_index);
+        break;
+    case 3:
+        if (data_reg != 1)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            1, 0, data_reg, SHIFT_IMM_LSL(0));
+        if (data_reg2 != 2)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            2, 0, data_reg2, SHIFT_IMM_LSL(0));
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index);
+        break;
+    }
+# else
+    if (addr_reg2 != 1)
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        1, 0, addr_reg2, SHIFT_IMM_LSL(0));
+    switch (opc) {
+    case 0:
+        tcg_out_dat_imm(s, cond, ARITH_AND, 2, data_reg, 0xff);
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index);
+        break;
+    case 1:
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        2, 0, data_reg, SHIFT_IMM_LSL(16));
+        tcg_out_dat_reg(s, cond, ARITH_MOV,
+                        2, 0, 2, SHIFT_IMM_LSR(16));
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index);
+        break;
+    case 2:
+        if (data_reg != 2)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            2, 0, data_reg, SHIFT_IMM_LSL(0));
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 3, 0, mem_index);
+        break;
+    case 3:
+        tcg_out_dat_imm(s, cond, ARITH_MOV, 8, 0, mem_index);
+        tcg_out32(s, (cond << 28) | 0x052d8010); /* str r8, [sp, #-0x10]! */
+        if (data_reg != 2)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            2, 0, data_reg, SHIFT_IMM_LSL(0));
+        if (data_reg2 != 3)
+            tcg_out_dat_reg(s, cond, ARITH_MOV,
+                            3, 0, data_reg2, SHIFT_IMM_LSL(0));
+        break;
+    }
+# endif
+
+# ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 8, 0, 14, SHIFT_IMM_LSL(0));
+# endif
+
+    tcg_out_bl(s, cond, (tcg_target_long) qemu_st_helpers[s_bits] -
+                    (tcg_target_long) s->code_ptr);
+# if TARGET_LONG_BITS == 64
+    if (opc == 3)
+        tcg_out_dat_imm(s, cond, ARITH_ADD, 13, 13, 0x10);
+# endif
+
+# ifdef SAVE_LR
+    tcg_out_dat_reg(s, cond, ARITH_MOV, 14, 0, 8, SHIFT_IMM_LSL(0));
+# endif
+
+    *label_ptr += ((void *) s->code_ptr - (void *) label_ptr - 8) >> 2;
+#else
+    switch (opc) {
+    case 0:
+        tcg_out_st8_12(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 0 | 4:
+        tcg_out_st8s_8(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 1:
+        tcg_out_st16u_8(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 1 | 4:
+        tcg_out_st16s_8(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 2:
+    default:
+        tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0);
+        break;
+    case 3:
+        /* TODO: use block store -
+         * check that data_reg2 > data_reg or the other way */
+        tcg_out_st32_12(s, COND_AL, data_reg, addr_reg, 0);
+        tcg_out_st32_12(s, COND_AL, data_reg2, addr_reg, 4);
+        break;
+    }
+#endif
+}
+
+static uint8_t *tb_ret_addr;
+
+static inline void tcg_out_op(TCGContext *s, int opc,
+                const TCGArg *args, const int *const_args)
+{
+    int c;
+
+    switch (opc) {
+    case INDEX_op_exit_tb:
+#ifdef SAVE_LR
+        if (args[0] >> 8)
+            tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, 15, 0);
+        else
+            tcg_out_dat_imm(s, COND_AL, ARITH_MOV, TCG_REG_R0, 0, args[0]);
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV, 15, 0, 14, SHIFT_IMM_LSL(0));
+        if (args[0] >> 8)
+            tcg_out32(s, args[0]);
+#else
+        if (args[0] >> 8)
+            tcg_out_ld32_12(s, COND_AL, 0, 15, 0);
+        else
+            tcg_out_dat_imm(s, COND_AL, ARITH_MOV, 0, 0, args[0]);
+        tcg_out_goto(s, COND_AL, (tcg_target_ulong) tb_ret_addr);
+        if (args[0] >> 8)
+            tcg_out32(s, args[0]);
+#endif
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* Direct jump method */
+#if 1
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            tcg_out_b(s, COND_AL, 8);
+#else
+            tcg_out_ld32_12(s, COND_AL, 15, 15, -4);
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            tcg_out32(s, 0);
+#endif
+        } else {
+            /* Indirect jump method */
+#if 1
+            c = (int) (s->tb_next + args[0]) - ((int) s->code_ptr + 8);
+            if (c > 0xfff || c < -0xfff) {
+                tcg_out_movi32(s, COND_AL, TCG_REG_R0,
+                                (tcg_target_long) (s->tb_next + args[0]));
+                tcg_out_ld32_12(s, COND_AL, 15, TCG_REG_R0, 0);
+            } else
+                tcg_out_ld32_12(s, COND_AL, 15, 15, c);
+#else
+            tcg_out_ld32_12(s, COND_AL, TCG_REG_R0, 15, 0);
+            tcg_out_ld32_12(s, COND_AL, 15, TCG_REG_R0, 0);
+            tcg_out32(s, (tcg_target_long) (s->tb_next + args[0]));
+#endif
+        }
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_call:
+        if (const_args[0])
+            tcg_out_call(s, COND_AL, args[0]);
+        else
+            tcg_out_callr(s, COND_AL, args[0]);
+        break;
+    case INDEX_op_jmp:
+        if (const_args[0])
+            tcg_out_goto(s, COND_AL, args[0]);
+        else
+            tcg_out_bx(s, COND_AL, args[0]);
+        break;
+    case INDEX_op_br:
+        tcg_out_goto_label(s, COND_AL, args[0]);
+        break;
+
+    case INDEX_op_ld8u_i32:
+        tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld8s_i32:
+        tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16u_i32:
+        tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16s_i32:
+        tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld_i32:
+        tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st8_i32:
+        tcg_out_st8u(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st16_i32:
+        tcg_out_st16u(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st_i32:
+        tcg_out_st32(s, COND_AL, args[0], args[1], args[2]);
+        break;
+
+    case INDEX_op_mov_i32:
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                        args[0], 0, args[1], SHIFT_IMM_LSL(0));
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi32(s, COND_AL, args[0], args[1]);
+        break;
+    case INDEX_op_add_i32:
+        c = ARITH_ADD;
+        goto gen_arith;
+    case INDEX_op_sub_i32:
+        c = ARITH_SUB;
+        goto gen_arith;
+    case INDEX_op_and_i32:
+        c = ARITH_AND;
+        goto gen_arith;
+    case INDEX_op_or_i32:
+        c = ARITH_ORR;
+        goto gen_arith;
+    case INDEX_op_xor_i32:
+        c = ARITH_EOR;
+        /* Fall through.  */
+    gen_arith:
+        tcg_out_dat_reg(s, COND_AL, c,
+                        args[0], args[1], args[2], SHIFT_IMM_LSL(0));
+        break;
+    case INDEX_op_add2_i32:
+        tcg_out_dat_reg2(s, COND_AL, ARITH_ADD, ARITH_ADC,
+                        args[0], args[1], args[2], args[3],
+                        args[4], args[5], SHIFT_IMM_LSL(0));
+        break;
+    case INDEX_op_sub2_i32:
+        tcg_out_dat_reg2(s, COND_AL, ARITH_SUB, ARITH_SBC,
+                        args[0], args[1], args[2], args[3],
+                        args[4], args[5], SHIFT_IMM_LSL(0));
+        break;
+    case INDEX_op_neg_i32:
+        tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0);
+        break;
+    case INDEX_op_mul_i32:
+        tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_mulu2_i32:
+        tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]);
+        break;
+    case INDEX_op_div2_i32:
+        tcg_out_div_helper(s, COND_AL, args,
+                        tcg_helper_div_i64, tcg_helper_rem_i64,
+                        SHIFT_IMM_ASR(31));
+        break;
+    case INDEX_op_divu2_i32:
+        tcg_out_div_helper(s, COND_AL, args,
+                        tcg_helper_divu_i64, tcg_helper_remu_i64,
+                        SHIFT_IMM_LSR(31));
+        break;
+    /* XXX: Perhaps args[2] & 0x1f is wrong */
+    case INDEX_op_shl_i32:
+        c = const_args[2] ?
+                SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]);
+        goto gen_shift32;
+    case INDEX_op_shr_i32:
+        c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) :
+                SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]);
+        goto gen_shift32;
+    case INDEX_op_sar_i32:
+        c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) :
+                SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]);
+        /* Fall through.  */
+    gen_shift32:
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c);
+        break;
+
+    case INDEX_op_brcond_i32:
+        tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0,
+                        args[0], args[1], SHIFT_IMM_LSL(0));
+        tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[2]], args[3]);
+        break;
+    case INDEX_op_brcond2_i32:
+        /* The resulting conditions are:
+         * TCG_COND_EQ    -->  a0 == a2 && a1 == a3,
+         * TCG_COND_NE    --> (a0 != a2 && a1 == a3) ||  a1 != a3,
+         * TCG_COND_LT(U) --> (a0 <  a2 && a1 == a3) ||  a1 <  a3,
+         * TCG_COND_GE(U) --> (a0 >= a2 && a1 == a3) || (a1 >= a3 && a1 != a3),
+         * TCG_COND_LE(U) --> (a0 <= a2 && a1 == a3) || (a1 <= a3 && a1 != a3),
+         * TCG_COND_GT(U) --> (a0 >  a2 && a1 == a3) ||  a1 >  a3,
+         */
+        tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0,
+                        args[1], args[3], SHIFT_IMM_LSL(0));
+        tcg_out_dat_reg(s, COND_EQ, ARITH_CMP, 0,
+                        args[0], args[2], SHIFT_IMM_LSL(0));
+        tcg_out_goto_label(s, tcg_cond_to_arm_cond[args[4]], args[5]);
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, COND_AL, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, COND_AL, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, COND_AL, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, COND_AL, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, COND_AL, args, 2);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld(s, COND_AL, args, 3);
+        break;
+
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, COND_AL, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, COND_AL, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, COND_AL, args, 2);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st(s, COND_AL, args, 3);
+        break;
+
+    case INDEX_op_ext8s_i32:
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                        args[0], 0, args[1], SHIFT_IMM_LSL(24));
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                        args[0], 0, args[0], SHIFT_IMM_ASR(24));
+        break;
+    case INDEX_op_ext16s_i32:
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                        args[0], 0, args[1], SHIFT_IMM_LSL(16));
+        tcg_out_dat_reg(s, COND_AL, ARITH_MOV,
+                        args[0], 0, args[0], SHIFT_IMM_ASR(16));
+        break;
+
+    default:
+        tcg_abort();
+    }
+}
+
+static const TCGTargetOpDef arm_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "ri" } },
+    { INDEX_op_jmp, { "ri" } },
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "r", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+
+    /* TODO: "r", "r", "ri" */
+    { INDEX_op_add_i32, { "r", "r", "r" } },
+    { INDEX_op_sub_i32, { "r", "r", "r" } },
+    { INDEX_op_mul_i32, { "r", "r", "r" } },
+    { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } },
+    { INDEX_op_div2_i32, { "r", "r", "r", "1", "2" } },
+    { INDEX_op_divu2_i32, { "r", "r", "r", "1", "2" } },
+    { INDEX_op_and_i32, { "r", "r", "r" } },
+    { INDEX_op_or_i32, { "r", "r", "r" } },
+    { INDEX_op_xor_i32, { "r", "r", "r" } },
+    { INDEX_op_neg_i32, { "r", "r" } },
+
+    { INDEX_op_shl_i32, { "r", "r", "ri" } },
+    { INDEX_op_shr_i32, { "r", "r", "ri" } },
+    { INDEX_op_sar_i32, { "r", "r", "ri" } },
+
+    { INDEX_op_brcond_i32, { "r", "r" } },
+
+    /* TODO: "r", "r", "r", "r", "ri", "ri" */
+    { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } },
+    { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } },
+    { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } },
+
+    { INDEX_op_qemu_ld8u, { "r", "x", "X" } },
+    { INDEX_op_qemu_ld8s, { "r", "x", "X" } },
+    { INDEX_op_qemu_ld16u, { "r", "x", "X" } },
+    { INDEX_op_qemu_ld16s, { "r", "x", "X" } },
+    { INDEX_op_qemu_ld32u, { "r", "x", "X" } },
+    { INDEX_op_qemu_ld64, { "d", "r", "x", "X" } },
+
+    { INDEX_op_qemu_st8, { "x", "x", "X" } },
+    { INDEX_op_qemu_st16, { "x", "x", "X" } },
+    { INDEX_op_qemu_st32, { "x", "x", "X" } },
+    { INDEX_op_qemu_st64, { "x", "D", "x", "X" } },
+
+    { INDEX_op_ext8s_i32, { "r", "r" } },
+    { INDEX_op_ext16s_i32, { "r", "r" } },
+
+    { -1 },
+};
+
+void tcg_target_init(TCGContext *s)
+{
+    /* fail safe */
+    if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry))
+        tcg_abort();
+
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0,
+                    ((2 << TCG_REG_R14) - 1) & ~(1 << TCG_REG_R8));
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                    ((2 << TCG_REG_R3) - 1) |
+                    (1 << TCG_REG_R12) | (1 << TCG_REG_R14));
+
+    tcg_regset_clear(s->reserved_regs);
+#ifdef SAVE_LR
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R14);
+#endif
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_CALL_STACK);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R8);
+
+    tcg_add_target_add_op_defs(arm_op_defs);
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int arg,
+                int arg1, tcg_target_long arg2)
+{
+    tcg_out_ld32u(s, COND_AL, arg, arg1, arg2);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+                int arg1, tcg_target_long arg2)
+{
+    tcg_out_st32(s, COND_AL, arg, arg1, arg2);
+}
+
+void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    if (val > 0)
+        if (val < 0x100)
+            tcg_out_dat_imm(s, COND_AL, ARITH_ADD, reg, reg, val);
+        else
+            tcg_abort();
+    else if (val < 0) {
+        if (val > -0x100)
+            tcg_out_dat_imm(s, COND_AL, ARITH_SUB, reg, reg, -val);
+        else
+            tcg_abort();
+    }
+}
+
+static inline void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    tcg_out_dat_reg(s, COND_AL, ARITH_MOV, ret, 0, arg, SHIFT_IMM_LSL(0));
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+                int ret, tcg_target_long arg)
+{
+    tcg_out_movi32(s, COND_AL, ret, arg);
+}
+
+void tcg_target_qemu_prologue(TCGContext *s)
+{
+    /* stmdb sp!, { r9 - r11, lr } */
+    tcg_out32(s, (COND_AL << 28) | 0x092d4e00);
+
+    tcg_out_bx(s, COND_AL, TCG_REG_R0);
+    tb_ret_addr = s->code_ptr;
+
+    /* ldmia sp!, { r9 - r11, pc } */
+    tcg_out32(s, (COND_AL << 28) | 0x08bd8e00);
+}
diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h
new file mode 100644
index 0000000..6c180af
--- /dev/null
+++ b/tcg/arm/tcg-target.h
@@ -0,0 +1,76 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ * Copyright (c) 2008 Andrzej Zaborowski
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_ARM 1
+
+#define TCG_TARGET_REG_BITS 32
+#undef TCG_TARGET_WORDS_BIGENDIAN
+#undef TCG_TARGET_HAS_div_i32
+#undef TCG_TARGET_HAS_div_i64
+#undef TCG_TARGET_HAS_bswap_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_neg_i32
+#undef TCG_TARGET_HAS_neg_i64
+#undef TCG_TARGET_STACK_GROWSUP
+
+enum {
+    TCG_REG_R0 = 0,
+    TCG_REG_R1,
+    TCG_REG_R2,
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+    TCG_TARGET_NB_REGS
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK		TCG_REG_R13
+#define TCG_TARGET_STACK_ALIGN		8
+#define TCG_TARGET_CALL_STACK_OFFSET	0
+
+enum {
+    /* Note: must be synced with dyngen-exec.h */
+    TCG_AREG0 = TCG_REG_R7,
+    TCG_AREG1 = TCG_REG_R4,
+    TCG_AREG2 = TCG_REG_R5,
+    TCG_AREG3 = TCG_REG_R6,
+};
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    register unsigned long _beg __asm ("a1") = start;
+    register unsigned long _end __asm ("a2") = stop;
+    register unsigned long _flg __asm ("a3") = 0;
+    __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
+}
diff --git a/tcg/hppa/tcg-target.c b/tcg/hppa/tcg-target.c
new file mode 100644
index 0000000..3affd26
--- /dev/null
+++ b/tcg/hppa/tcg-target.c
@@ -0,0 +1,973 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "%r0",
+    "%r1",
+    "%rp",
+    "%r3",
+    "%r4",
+    "%r5",
+    "%r6",
+    "%r7",
+    "%r8",
+    "%r9",
+    "%r10",
+    "%r11",
+    "%r12",
+    "%r13",
+    "%r14",
+    "%r15",
+    "%r16",
+    "%r17",
+    "%r18",
+    "%r19",
+    "%r20",
+    "%r21",
+    "%r22",
+    "%r23",
+    "%r24",
+    "%r25",
+    "%r26",
+    "%dp",
+    "%ret0",
+    "%ret1",
+    "%sp",
+    "%r31",
+};
+
+static const int tcg_target_reg_alloc_order[] = {
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+
+    TCG_REG_R17,
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+};
+
+static const int tcg_target_call_iarg_regs[4] = {
+    TCG_REG_R26,
+    TCG_REG_R25,
+    TCG_REG_R24,
+    TCG_REG_R23,
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+    TCG_REG_RET0,
+    TCG_REG_RET1,
+};
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+                        tcg_target_long value, tcg_target_long addend)
+{
+    switch (type) {
+    case R_PARISC_PCREL17F:
+        hppa_patch17f((uint32_t *)code_ptr, value, addend);
+        break;
+    default:
+        tcg_abort();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    return 4;
+}
+
+/* parse target specific constraints */
+int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch (ct_str[0]) {
+    case 'r':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        break;
+    case 'L': /* qemu_ld/st constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R26);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R25);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R24);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R23);
+        break;
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+                                         const TCGArgConstraint *arg_ct)
+{
+    int ct;
+
+    ct = arg_ct->ct;
+
+    /* TODO */
+
+    return 0;
+}
+
+#define INSN_OP(x)       ((x) << 26)
+#define INSN_EXT3BR(x)   ((x) << 13)
+#define INSN_EXT3SH(x)   ((x) << 10)
+#define INSN_EXT4(x)     ((x) << 6)
+#define INSN_EXT5(x)     (x)
+#define INSN_EXT6(x)     ((x) << 6)
+#define INSN_EXT7(x)     ((x) << 6)
+#define INSN_EXT8A(x)    ((x) << 6)
+#define INSN_EXT8B(x)    ((x) << 5)
+#define INSN_T(x)        (x)
+#define INSN_R1(x)       ((x) << 16)
+#define INSN_R2(x)       ((x) << 21)
+#define INSN_DEP_LEN(x)  (32 - (x))
+#define INSN_SHDEP_CP(x) ((31 - (x)) << 5)
+#define INSN_SHDEP_P(x)  ((x) << 5)
+#define INSN_COND(x)     ((x) << 13)
+
+#define COND_NEVER 0
+#define COND_EQUAL 1
+#define COND_LT    2
+#define COND_LTEQ  3
+#define COND_LTU   4
+#define COND_LTUEQ 5
+#define COND_SV    6
+#define COND_OD    7
+
+
+/* Logical ADD */
+#define ARITH_ADD  (INSN_OP(0x02) | INSN_EXT6(0x28))
+#define ARITH_AND  (INSN_OP(0x02) | INSN_EXT6(0x08))
+#define ARITH_OR   (INSN_OP(0x02) | INSN_EXT6(0x09))
+#define ARITH_XOR  (INSN_OP(0x02) | INSN_EXT6(0x0a))
+#define ARITH_SUB  (INSN_OP(0x02) | INSN_EXT6(0x10))
+
+#define SHD        (INSN_OP(0x34) | INSN_EXT3SH(2))
+#define VSHD       (INSN_OP(0x34) | INSN_EXT3SH(0))
+#define DEP        (INSN_OP(0x35) | INSN_EXT3SH(3))
+#define ZDEP       (INSN_OP(0x35) | INSN_EXT3SH(2))
+#define ZVDEP      (INSN_OP(0x35) | INSN_EXT3SH(0))
+#define EXTRU      (INSN_OP(0x34) | INSN_EXT3SH(6))
+#define EXTRS      (INSN_OP(0x34) | INSN_EXT3SH(7))
+#define VEXTRS     (INSN_OP(0x34) | INSN_EXT3SH(5))
+
+#define SUBI       (INSN_OP(0x25))
+#define MTCTL      (INSN_OP(0x00) | INSN_EXT8B(0xc2))
+
+#define BL         (INSN_OP(0x3a) | INSN_EXT3BR(0))
+#define BLE_SR4    (INSN_OP(0x39) | (1 << 13))
+#define BV         (INSN_OP(0x3a) | INSN_EXT3BR(6))
+#define BV_N       (INSN_OP(0x3a) | INSN_EXT3BR(6) | 2)
+#define LDIL       (INSN_OP(0x08))
+#define LDO        (INSN_OP(0x0d))
+
+#define LDB        (INSN_OP(0x10))
+#define LDH        (INSN_OP(0x11))
+#define LDW        (INSN_OP(0x12))
+#define LDWM       (INSN_OP(0x13))
+
+#define STB        (INSN_OP(0x18))
+#define STH        (INSN_OP(0x19))
+#define STW        (INSN_OP(0x1a))
+#define STWM       (INSN_OP(0x1b))
+
+#define COMBT      (INSN_OP(0x20))
+#define COMBF      (INSN_OP(0x22))
+
+static int lowsignext(uint32_t val, int start, int length)
+{
+    return (((val << 1) & ~(~0 << length)) |
+            ((val >> (length - 1)) & 1)) << start;
+}
+
+static inline void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    /* PA1.1 defines COPY as OR r,0,t */
+    tcg_out32(s, ARITH_OR | INSN_T(ret) | INSN_R1(arg) | INSN_R2(TCG_REG_R0));
+
+    /* PA2.0 defines COPY as LDO 0(r),t
+     * but hppa-dis.c is unaware of this definition */
+    /* tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(arg) | reassemble_14(0)); */
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+                                int ret, tcg_target_long arg)
+{
+    if (arg == (arg & 0x1fff)) {
+        tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(TCG_REG_R0) |
+                     reassemble_14(arg));
+    } else {
+        tcg_out32(s, LDIL | INSN_R2(ret) |
+                     reassemble_21(lrsel((uint32_t)arg, 0)));
+        if (arg & 0x7ff)
+            tcg_out32(s, LDO | INSN_R1(ret) | INSN_R2(ret) |
+                         reassemble_14(rrsel((uint32_t)arg, 0)));
+    }
+}
+
+static inline void tcg_out_ld_raw(TCGContext *s, int ret,
+                                  tcg_target_long arg)
+{
+    tcg_out32(s, LDIL | INSN_R2(ret) |
+                 reassemble_21(lrsel((uint32_t)arg, 0)));
+    tcg_out32(s, LDW | INSN_R1(ret) | INSN_R2(ret) |
+                 reassemble_14(rrsel((uint32_t)arg, 0)));
+}
+
+static inline void tcg_out_ld_ptr(TCGContext *s, int ret,
+                                  tcg_target_long arg)
+{
+    tcg_out_ld_raw(s, ret, arg);
+}
+
+static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset,
+                                int op)
+{
+    if (offset == (offset & 0xfff))
+        tcg_out32(s, op | INSN_R1(ret) | INSN_R2(addr) |
+                 reassemble_14(offset));
+    else {
+        fprintf(stderr, "unimplemented %s with offset %d\n", __func__, offset);
+        tcg_abort();
+    }
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+                              int arg1, tcg_target_long arg2)
+{
+    fprintf(stderr, "unimplemented %s\n", __func__);
+    tcg_abort();
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int ret,
+                              int arg1, tcg_target_long arg2)
+{
+    fprintf(stderr, "unimplemented %s\n", __func__);
+    tcg_abort();
+}
+
+static inline void tcg_out_arith(TCGContext *s, int t, int r1, int r2, int op)
+{
+    tcg_out32(s, op | INSN_T(t) | INSN_R1(r1) | INSN_R2(r2));
+}
+
+static inline void tcg_out_arithi(TCGContext *s, int t, int r1,
+                                  tcg_target_long val, int op)
+{
+    tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R20, val);
+    tcg_out_arith(s, t, r1, TCG_REG_R20, op);
+}
+
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    tcg_out_arithi(s, reg, reg, val, ARITH_ADD);
+}
+
+static inline void tcg_out_nop(TCGContext *s)
+{
+    tcg_out32(s, ARITH_OR | INSN_T(TCG_REG_R0) | INSN_R1(TCG_REG_R0) |
+                 INSN_R2(TCG_REG_R0));
+}
+
+static inline void tcg_out_ext8s(TCGContext *s, int ret, int arg) {
+    tcg_out32(s, EXTRS | INSN_R1(ret) | INSN_R2(arg) |
+                 INSN_SHDEP_P(31) | INSN_DEP_LEN(8));
+}
+
+static inline void tcg_out_ext16s(TCGContext *s, int ret, int arg) {
+    tcg_out32(s, EXTRS | INSN_R1(ret) | INSN_R2(arg) |
+                 INSN_SHDEP_P(31) | INSN_DEP_LEN(16));
+}
+
+static inline void tcg_out_bswap16(TCGContext *s, int ret, int arg) {
+    if(ret != arg)
+        tcg_out_mov(s, ret, arg);
+    tcg_out32(s, DEP | INSN_R2(ret) | INSN_R1(ret) |
+                 INSN_SHDEP_CP(15) | INSN_DEP_LEN(8));
+    tcg_out32(s, SHD | INSN_T(ret) | INSN_R1(TCG_REG_R0) |
+                 INSN_R2(ret) | INSN_SHDEP_CP(8));
+}
+
+static inline void tcg_out_bswap32(TCGContext *s, int ret, int arg, int temp) {
+    tcg_out32(s, SHD | INSN_T(temp) | INSN_R1(arg) |
+                 INSN_R2(arg) | INSN_SHDEP_CP(16));
+    tcg_out32(s, DEP | INSN_R2(temp) | INSN_R1(temp) |
+                 INSN_SHDEP_CP(15) | INSN_DEP_LEN(8));
+    tcg_out32(s, SHD | INSN_T(ret) | INSN_R1(arg) |
+                 INSN_R2(temp) | INSN_SHDEP_CP(8));
+}
+
+static inline void tcg_out_call(TCGContext *s, void *func)
+{
+    uint32_t val = (uint32_t)__canonicalize_funcptr_for_compare(func);
+    tcg_out32(s, LDIL | INSN_R2(TCG_REG_R20) |
+                 reassemble_21(lrsel(val, 0)));
+    tcg_out32(s, BLE_SR4 | INSN_R2(TCG_REG_R20) |
+                 reassemble_17(rrsel(val, 0) >> 2));
+    tcg_out_mov(s, TCG_REG_RP, TCG_REG_R31);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, int opc)
+{
+    int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap;
+#if defined(CONFIG_SOFTMMU)
+    uint32_t *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+#if defined(CONFIG_SOFTMMU)
+    uint32_t *label3_ptr;
+#endif
+    int addr_reg2;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0; /* surpress warning */
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    r0 = TCG_REG_R26;
+    r1 = TCG_REG_R25;
+
+#if defined(CONFIG_SOFTMMU)
+    tcg_out_mov(s, r1, addr_reg);
+
+    tcg_out_mov(s, r0, addr_reg);
+
+    tcg_out32(s, SHD | INSN_T(r1) | INSN_R1(TCG_REG_R0) | INSN_R2(r1) |
+                 INSN_SHDEP_CP(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS));
+
+    tcg_out_arithi(s, r0, r0, TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+                   ARITH_AND);
+
+    tcg_out_arithi(s, r1, r1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS,
+                   ARITH_AND);
+
+    tcg_out_arith(s, r1, r1, TCG_AREG0, ARITH_ADD);
+    tcg_out_arithi(s, r1, r1,
+                   offsetof(CPUState, tlb_table[mem_index][0].addr_read),
+                   ARITH_ADD);
+
+    tcg_out_ldst(s, TCG_REG_R20, r1, 0, LDW);
+
+#if TARGET_LONG_BITS == 32
+    /* if equal, jump to label1 */
+    label1_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(r0) |
+                 INSN_COND(COND_EQUAL));
+    tcg_out_mov(s, r0, addr_reg); /* delay slot */
+#else
+    /* if not equal, jump to label3 */
+    label3_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, COMBF | INSN_R1(TCG_REG_R20) | INSN_R2(r0) |
+                 INSN_COND(COND_EQUAL));
+    tcg_out_mov(s, r0, addr_reg); /* delay slot */
+
+    tcg_out_ldst(s, TCG_REG_R20, r1, 4, LDW);
+
+    /* if equal, jump to label1 */
+    label1_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(addr_reg2) |
+                 INSN_COND(COND_EQUAL));
+    tcg_out_nop(s); /* delay slot */
+
+    /* label3: */
+    *label3_ptr |= reassemble_12((uint32_t *)s->code_ptr - label3_ptr - 2);
+#endif
+
+#if TARGET_LONG_BITS == 32
+    tcg_out_mov(s, TCG_REG_R26, addr_reg);
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R25, mem_index);
+#else
+    tcg_out_mov(s, TCG_REG_R26, addr_reg);
+    tcg_out_mov(s, TCG_REG_R25, addr_reg2);
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R24, mem_index);
+#endif
+
+    tcg_out_call(s, qemu_ld_helpers[s_bits]);
+
+    switch(opc) {
+        case 0 | 4:
+            tcg_out_ext8s(s, data_reg, TCG_REG_RET0);
+            break;
+        case 1 | 4:
+            tcg_out_ext16s(s, data_reg, TCG_REG_RET0);
+            break;
+        case 0:
+        case 1:
+        case 2:
+        default:
+            tcg_out_mov(s, data_reg, TCG_REG_RET0);
+            break;
+        case 3:
+            tcg_abort();
+            tcg_out_mov(s, data_reg, TCG_REG_RET0);
+            tcg_out_mov(s, data_reg2, TCG_REG_RET1);
+            break;
+    }
+
+    /* jump to label2 */
+    label2_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, BL | INSN_R2(TCG_REG_R0) | 2);
+
+    /* label1: */
+    *label1_ptr |= reassemble_12((uint32_t *)s->code_ptr - label1_ptr - 2);
+
+    tcg_out_arithi(s, TCG_REG_R20, r1,
+                   offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_read),
+                   ARITH_ADD);
+    tcg_out_ldst(s, TCG_REG_R20, TCG_REG_R20, 0, LDW);
+    tcg_out_arith(s, r0, r0, TCG_REG_R20, ARITH_ADD);
+#else
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 0;
+#else
+    bswap = 1;
+#endif
+    switch (opc) {
+        case 0:
+            tcg_out_ldst(s, data_reg, r0, 0, LDB);
+            break;
+        case 0 | 4:
+            tcg_out_ldst(s, data_reg, r0, 0, LDB);
+            tcg_out_ext8s(s, data_reg, data_reg);
+            break;
+        case 1:
+            tcg_out_ldst(s, data_reg, r0, 0, LDH);
+            if (bswap)
+                tcg_out_bswap16(s, data_reg, data_reg);
+            break;
+        case 1 | 4:
+            tcg_out_ldst(s, data_reg, r0, 0, LDH);
+            if (bswap)
+                tcg_out_bswap16(s, data_reg, data_reg);
+            tcg_out_ext16s(s, data_reg, data_reg);
+            break;
+        case 2:
+            tcg_out_ldst(s, data_reg, r0, 0, LDW);
+            if (bswap)
+                tcg_out_bswap32(s, data_reg, data_reg, TCG_REG_R20);
+            break;
+        case 3:
+            tcg_abort();
+            if (!bswap) {
+                tcg_out_ldst(s, data_reg, r0, 0, LDW);
+                tcg_out_ldst(s, data_reg2, r0, 4, LDW);
+            } else {
+                tcg_out_ldst(s, data_reg, r0, 4, LDW);
+                tcg_out_bswap32(s, data_reg, data_reg, TCG_REG_R20);
+                tcg_out_ldst(s, data_reg2, r0, 0, LDW);
+                tcg_out_bswap32(s, data_reg2, data_reg2, TCG_REG_R20);
+            }
+            break;
+        default:
+            tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr |= reassemble_17((uint32_t *)s->code_ptr - label2_ptr - 2);
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, int opc)
+{
+    int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap;
+#if defined(CONFIG_SOFTMMU)
+    uint32_t *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+#if defined(CONFIG_SOFTMMU)
+    uint32_t *label3_ptr;
+#endif
+    int addr_reg2;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0; /* surpress warning */
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+    mem_index = *args;
+
+    s_bits = opc;
+
+    r0 = TCG_REG_R26;
+    r1 = TCG_REG_R25;
+
+#if defined(CONFIG_SOFTMMU)
+    tcg_out_mov(s, r1, addr_reg);
+
+    tcg_out_mov(s, r0, addr_reg);
+
+    tcg_out32(s, SHD | INSN_T(r1) | INSN_R1(TCG_REG_R0) | INSN_R2(r1) |
+                 INSN_SHDEP_CP(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS));
+
+    tcg_out_arithi(s, r0, r0, TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+                   ARITH_AND);
+
+    tcg_out_arithi(s, r1, r1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS,
+                   ARITH_AND);
+
+    tcg_out_arith(s, r1, r1, TCG_AREG0, ARITH_ADD);
+    tcg_out_arithi(s, r1, r1,
+                   offsetof(CPUState, tlb_table[mem_index][0].addr_write),
+                   ARITH_ADD);
+
+    tcg_out_ldst(s, TCG_REG_R20, r1, 0, LDW);
+
+#if TARGET_LONG_BITS == 32
+    /* if equal, jump to label1 */
+    label1_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(r0) |
+                 INSN_COND(COND_EQUAL));
+    tcg_out_mov(s, r0, addr_reg); /* delay slot */
+#else
+    /* if not equal, jump to label3 */
+    label3_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, COMBF | INSN_R1(TCG_REG_R20) | INSN_R2(r0) |
+                 INSN_COND(COND_EQUAL));
+    tcg_out_mov(s, r0, addr_reg); /* delay slot */
+
+    tcg_out_ldst(s, TCG_REG_R20, r1, 4, LDW);
+
+    /* if equal, jump to label1 */
+    label1_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, COMBT | INSN_R1(TCG_REG_R20) | INSN_R2(addr_reg2) |
+                 INSN_COND(COND_EQUAL));
+    tcg_out_nop(s); /* delay slot */
+
+    /* label3: */
+    *label3_ptr |= reassemble_12((uint32_t *)s->code_ptr - label3_ptr - 2);
+#endif
+
+    tcg_out_mov(s, TCG_REG_R26, addr_reg);
+#if TARGET_LONG_BITS == 64
+    tcg_out_mov(s, TCG_REG_R25, addr_reg2);
+    if (opc == 3) {
+        tcg_abort();
+        tcg_out_mov(s, TCG_REG_R24, data_reg);
+        tcg_out_mov(s, TCG_REG_R23, data_reg2);
+        /* TODO: push mem_index */
+        tcg_abort();
+    } else {
+        switch(opc) {
+        case 0:
+            tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R24) | INSN_R2(data_reg) |
+                         INSN_SHDEP_P(31) | INSN_DEP_LEN(8));
+            break;
+        case 1:
+            tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R24) | INSN_R2(data_reg) |
+                         INSN_SHDEP_P(31) | INSN_DEP_LEN(16));
+            break;
+        case 2:
+            tcg_out_mov(s, TCG_REG_R24, data_reg);
+            break;
+        }
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R23, mem_index);
+    }
+#else
+    if (opc == 3) {
+        tcg_abort();
+        tcg_out_mov(s, TCG_REG_R25, data_reg);
+        tcg_out_mov(s, TCG_REG_R24, data_reg2);
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R23, mem_index);
+    } else {
+        switch(opc) {
+        case 0:
+            tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R25) | INSN_R2(data_reg) |
+                         INSN_SHDEP_P(31) | INSN_DEP_LEN(8));
+            break;
+        case 1:
+            tcg_out32(s, EXTRU | INSN_R1(TCG_REG_R25) | INSN_R2(data_reg) |
+                         INSN_SHDEP_P(31) | INSN_DEP_LEN(16));
+            break;
+        case 2:
+            tcg_out_mov(s, TCG_REG_R25, data_reg);
+            break;
+        }
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_R24, mem_index);
+    }
+#endif
+    tcg_out_call(s, qemu_st_helpers[s_bits]);
+
+    /* jump to label2 */
+    label2_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, BL | INSN_R2(TCG_REG_R0) | 2);
+
+    /* label1: */
+    *label1_ptr |= reassemble_12((uint32_t *)s->code_ptr - label1_ptr - 2);
+
+    tcg_out_arithi(s, TCG_REG_R20, r1,
+                   offsetof(CPUTLBEntry, addend) - offsetof(CPUTLBEntry, addr_write),
+                   ARITH_ADD);
+    tcg_out_ldst(s, TCG_REG_R20, TCG_REG_R20, 0, LDW);
+    tcg_out_arith(s, r0, r0, TCG_REG_R20, ARITH_ADD);
+#else
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 0;
+#else
+    bswap = 1;
+#endif
+    switch (opc) {
+    case 0:
+        tcg_out_ldst(s, data_reg, r0, 0, STB);
+        break;
+    case 1:
+        if (bswap) {
+            tcg_out_bswap16(s, TCG_REG_R20, data_reg);
+            data_reg = TCG_REG_R20;
+        }
+        tcg_out_ldst(s, data_reg, r0, 0, STH);
+        break;
+    case 2:
+        if (bswap) {
+            tcg_out_bswap32(s, TCG_REG_R20, data_reg, TCG_REG_R20);
+            data_reg = TCG_REG_R20;
+        }
+        tcg_out_ldst(s, data_reg, r0, 0, STW);
+        break;
+    case 3:
+        tcg_abort();
+        if (!bswap) {
+            tcg_out_ldst(s, data_reg, r0, 0, STW);
+            tcg_out_ldst(s, data_reg2, r0, 4, STW);
+        } else {
+            tcg_out_bswap32(s, TCG_REG_R20, data_reg, TCG_REG_R20);
+            tcg_out_ldst(s, TCG_REG_R20, r0, 4, STW);
+            tcg_out_bswap32(s, TCG_REG_R20, data_reg2, TCG_REG_R20);
+            tcg_out_ldst(s, TCG_REG_R20, r0, 0, STW);
+        }
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr |= reassemble_17((uint32_t *)s->code_ptr - label2_ptr - 2);
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args,
+                              const int *const_args)
+{
+    int c;
+
+    switch (opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RET0, args[0]);
+        tcg_out32(s, BV_N | INSN_R2(TCG_REG_R18));
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+            fprintf(stderr, "goto_tb direct\n");
+            tcg_abort();
+            tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_R20, args[0]);
+            tcg_out32(s, BV_N | INSN_R2(TCG_REG_R20));
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+        } else {
+            /* indirect jump method */
+            tcg_out_ld_ptr(s, TCG_REG_R20,
+                           (tcg_target_long)(s->tb_next + args[0]));
+            tcg_out32(s, BV_N | INSN_R2(TCG_REG_R20));
+        }
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_call:
+        tcg_out32(s, BLE_SR4 | INSN_R2(args[0]));
+        tcg_out_mov(s, TCG_REG_RP, TCG_REG_R31);
+        break;
+    case INDEX_op_jmp:
+        fprintf(stderr, "unimplemented jmp\n");
+        tcg_abort();
+        break;
+    case INDEX_op_br:
+        fprintf(stderr, "unimplemented br\n");
+        tcg_abort();
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]);
+        break;
+
+    case INDEX_op_ld8u_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDB);
+        break;
+    case INDEX_op_ld8s_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDB);
+        tcg_out_ext8s(s, args[0], args[0]);
+        break;
+    case INDEX_op_ld16u_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDH);
+        break;
+    case INDEX_op_ld16s_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDH);
+        tcg_out_ext16s(s, args[0], args[0]);
+        break;
+    case INDEX_op_ld_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDW);
+        break;
+
+    case INDEX_op_st8_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], STB);
+        break;
+    case INDEX_op_st16_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], STH);
+        break;
+    case INDEX_op_st_i32:
+        tcg_out_ldst(s, args[0], args[1], args[2], STW);
+        break;
+
+    case INDEX_op_sub_i32:
+        c = ARITH_SUB;
+        goto gen_arith;
+    case INDEX_op_and_i32:
+        c = ARITH_AND;
+        goto gen_arith;
+    case INDEX_op_or_i32:
+        c = ARITH_OR;
+        goto gen_arith;
+    case INDEX_op_xor_i32:
+        c = ARITH_XOR;
+        goto gen_arith;
+    case INDEX_op_add_i32:
+        c = ARITH_ADD;
+        goto gen_arith;
+
+    case INDEX_op_shl_i32:
+        tcg_out32(s, SUBI | INSN_R1(TCG_REG_R20) | INSN_R2(args[2]) |
+                     lowsignext(0x1f, 0, 11));
+        tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(TCG_REG_R20));
+        tcg_out32(s, ZVDEP | INSN_R2(args[0]) | INSN_R1(args[1]) |
+                     INSN_DEP_LEN(32));
+        break;
+    case INDEX_op_shr_i32:
+        tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(args[2]));
+        tcg_out32(s, VSHD | INSN_T(args[0]) | INSN_R1(TCG_REG_R0) |
+                     INSN_R2(args[1]));
+        break;
+    case INDEX_op_sar_i32:
+        tcg_out32(s, SUBI | INSN_R1(TCG_REG_R20) | INSN_R2(args[2]) |
+                     lowsignext(0x1f, 0, 11));
+        tcg_out32(s, MTCTL | INSN_R2(11) | INSN_R1(TCG_REG_R20));
+        tcg_out32(s, VEXTRS | INSN_R1(args[0]) | INSN_R2(args[1]) |
+                     INSN_DEP_LEN(32));
+        break;
+
+    case INDEX_op_mul_i32:
+        fprintf(stderr, "unimplemented mul\n");
+        tcg_abort();
+        break;
+    case INDEX_op_mulu2_i32:
+        fprintf(stderr, "unimplemented mulu2\n");
+        tcg_abort();
+        break;
+    case INDEX_op_div2_i32:
+        fprintf(stderr, "unimplemented div2\n");
+        tcg_abort();
+        break;
+    case INDEX_op_divu2_i32:
+        fprintf(stderr, "unimplemented divu2\n");
+        tcg_abort();
+        break;
+
+    case INDEX_op_brcond_i32:
+        fprintf(stderr, "unimplemented brcond\n");
+        tcg_abort();
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, args, 2);
+        break;
+
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, args, 2);
+        break;
+
+    default:
+        fprintf(stderr, "unknown opcode 0x%x\n", opc);
+        tcg_abort();
+    }
+    return;
+
+gen_arith:
+    tcg_out_arith(s, args[0], args[1], args[2], c);
+}
+
+static const TCGTargetOpDef hppa_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+
+    { INDEX_op_call, { "r" } },
+    { INDEX_op_jmp, { "r" } },
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "r", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+
+    { INDEX_op_add_i32, { "r", "r", "r" } },
+    { INDEX_op_sub_i32, { "r", "r", "r" } },
+    { INDEX_op_and_i32, { "r", "r", "r" } },
+    { INDEX_op_or_i32, { "r", "r", "r" } },
+    { INDEX_op_xor_i32, { "r", "r", "r" } },
+
+    { INDEX_op_shl_i32, { "r", "r", "r" } },
+    { INDEX_op_shr_i32, { "r", "r", "r" } },
+    { INDEX_op_sar_i32, { "r", "r", "r" } },
+
+    { INDEX_op_brcond_i32, { "r", "r" } },
+
+#if TARGET_LONG_BITS == 32
+    { INDEX_op_qemu_ld8u, { "r", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "r", "L" } },
+
+    { INDEX_op_qemu_st8, { "L", "L" } },
+    { INDEX_op_qemu_st16, { "L", "L" } },
+    { INDEX_op_qemu_st32, { "L", "L" } },
+    { INDEX_op_qemu_st64, { "L", "L", "L" } },
+#else
+    { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld32s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } },
+
+    { INDEX_op_qemu_st8, { "L", "L", "L" } },
+    { INDEX_op_qemu_st16, { "L", "L", "L" } },
+    { INDEX_op_qemu_st32, { "L", "L", "L" } },
+    { INDEX_op_qemu_st64, { "L", "L", "L", "L" } },
+#endif
+    { -1 },
+};
+
+void tcg_target_init(TCGContext *s)
+{
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_R20) |
+                     (1 << TCG_REG_R21) |
+                     (1 << TCG_REG_R22) |
+                     (1 << TCG_REG_R23) |
+                     (1 << TCG_REG_R24) |
+                     (1 << TCG_REG_R25) |
+                     (1 << TCG_REG_R26));
+
+    tcg_regset_clear(s->reserved_regs);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0);  /* hardwired to zero */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1);  /* addil target */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_RP);  /* link register */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R3);  /* frame pointer */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R18); /* return pointer */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R19); /* clobbered w/o pic */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R20); /* reserved */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_DP);  /* data pointer */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP);  /* stack pointer */
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R31); /* ble link reg */
+
+    tcg_add_target_add_op_defs(hppa_op_defs);
+}
diff --git a/tcg/hppa/tcg-target.h b/tcg/hppa/tcg-target.h
new file mode 100644
index 0000000..8e2693d
--- /dev/null
+++ b/tcg/hppa/tcg-target.h
@@ -0,0 +1,204 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define TCG_TARGET_HPPA 1
+
+#if defined(_PA_RISC1_1)
+#define TCG_TARGET_REG_BITS 32
+#else
+#error unsupported
+#endif
+
+#define TCG_TARGET_WORDS_BIGENDIAN
+
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+    TCG_REG_R0 = 0,
+    TCG_REG_R1,
+    TCG_REG_RP,
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R24,
+    TCG_REG_R25,
+    TCG_REG_R26,
+    TCG_REG_DP,
+    TCG_REG_RET0,
+    TCG_REG_RET1,
+    TCG_REG_SP,
+    TCG_REG_R31,
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_SP
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_STACK_GROWSUP
+
+/* optional instructions */
+//#define TCG_TARGET_HAS_ext8s_i32
+//#define TCG_TARGET_HAS_ext16s_i32
+//#define TCG_TARGET_HAS_bswap16_i32
+//#define TCG_TARGET_HAS_bswap_i32
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_R17
+#define TCG_AREG1 TCG_REG_R14
+#define TCG_AREG2 TCG_REG_R15
+#define TCG_AREG3 TCG_REG_R16
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    start &= ~31;
+    while (start <= stop)
+    {
+        asm volatile ("fdc 0(%0)\n"
+                      "sync\n"
+                      "fic 0(%%sr4, %0)\n"
+                      "sync\n"
+                      : : "r"(start) : "memory");
+        start += 32;
+    }
+}
+
+/* supplied by libgcc */
+extern void *__canonicalize_funcptr_for_compare(void *);
+
+/* Field selection types defined by hppa */
+#define rnd(x)                  (((x)+0x1000)&~0x1fff)
+/* lsel: select left 21 bits */
+#define lsel(v,a)               (((v)+(a))>>11)
+/* rsel: select right 11 bits */
+#define rsel(v,a)               (((v)+(a))&0x7ff)
+/* lrsel with rounding of addend to nearest 8k */
+#define lrsel(v,a)              (((v)+rnd(a))>>11)
+/* rrsel with rounding of addend to nearest 8k */
+#define rrsel(v,a)              ((((v)+rnd(a))&0x7ff)+((a)-rnd(a)))
+
+#define mask(x,sz)              ((x) & ~((1<<(sz))-1))
+
+static inline int reassemble_12(int as12)
+{
+    return (((as12 & 0x800) >> 11) |
+            ((as12 & 0x400) >> 8) |
+            ((as12 & 0x3ff) << 3));
+}
+
+static inline int reassemble_14(int as14)
+{
+    return (((as14 & 0x1fff) << 1) |
+            ((as14 & 0x2000) >> 13));
+}
+
+static inline int reassemble_17(int as17)
+{
+    return (((as17 & 0x10000) >> 16) |
+            ((as17 & 0x0f800) << 5) |
+            ((as17 & 0x00400) >> 8) |
+            ((as17 & 0x003ff) << 3));
+}
+
+static inline int reassemble_21(int as21)
+{
+    return (((as21 & 0x100000) >> 20) |
+            ((as21 & 0x0ffe00) >> 8) |
+            ((as21 & 0x000180) << 7) |
+            ((as21 & 0x00007c) << 14) |
+            ((as21 & 0x000003) << 12));
+}
+
+static inline void hppa_patch21l(uint32_t *insn, int val, int addend)
+{
+    val = lrsel(val, addend);
+    *insn = mask(*insn, 21) | reassemble_21(val);
+}
+
+static inline void hppa_patch14r(uint32_t *insn, int val, int addend)
+{
+    val = rrsel(val, addend);
+    *insn = mask(*insn, 14) | reassemble_14(val);
+}
+
+static inline void hppa_patch17r(uint32_t *insn, int val, int addend)
+{
+    val = rrsel(val, addend);
+    *insn = (*insn & ~0x1f1ffd) | reassemble_17(val);
+}
+
+
+static inline void hppa_patch21l_dprel(uint32_t *insn, int val, int addend)
+{
+    register unsigned int dp asm("r27");
+    hppa_patch21l(insn, val - dp, addend);
+}
+
+static inline void hppa_patch14r_dprel(uint32_t *insn, int val, int addend)
+{
+    register unsigned int dp asm("r27");
+    hppa_patch14r(insn, val - dp, addend);
+}
+
+static inline void hppa_patch17f(uint32_t *insn, int val, int addend)
+{
+    int dot = (int)insn & ~0x3;
+    int v = ((val + addend) - dot - 8) / 4;
+    if (v > (1 << 16) || v < -(1 << 16)) {
+        printf("cannot fit branch to offset %d [%08x->%08x]\n", v, dot, val);
+        abort();
+    }
+    *insn = (*insn & ~0x1f1ffd) | reassemble_17(v);
+}
+
+static inline void hppa_load_imm21l(uint32_t *insn, int val, int addend)
+{
+    /* Transform addil L'sym(%dp) to ldil L'val, %r1 */
+    *insn = 0x20200000 | reassemble_21(lrsel(val, 0));
+}
+
+static inline void hppa_load_imm14r(uint32_t *insn, int val, int addend)
+{
+    /* Transform ldw R'sym(%r1), %rN to ldo R'sym(%r1), %rN */
+    hppa_patch14r(insn, val, addend);
+    /* HACK */
+    if (addend == 0)
+        *insn = (*insn & ~0xfc000000) | (0x0d << 26);
+}
diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c
new file mode 100644
index 0000000..08bb783
--- /dev/null
+++ b/tcg/i386/tcg-target.c
@@ -0,0 +1,1185 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+const char *tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "%eax",
+    "%ecx",
+    "%edx",
+    "%ebx",
+    "%esp",
+    "%ebp",
+    "%esi",
+    "%edi",
+};
+
+int tcg_target_reg_alloc_order[] = {
+    TCG_REG_EAX,
+    TCG_REG_EDX,
+    TCG_REG_ECX,
+    TCG_REG_EBX,
+    TCG_REG_ESI,
+    TCG_REG_EDI,
+    TCG_REG_EBP,
+};
+
+const int tcg_target_call_iarg_regs[3] = { TCG_REG_EAX, TCG_REG_EDX, TCG_REG_ECX };
+const int tcg_target_call_oarg_regs[2] = { TCG_REG_EAX, TCG_REG_EDX };
+
+static uint8_t *tb_ret_addr;
+
+static void patch_reloc(uint8_t *code_ptr, int type, 
+                        tcg_target_long value, tcg_target_long addend)
+{
+    value += addend;
+    switch(type) {
+    case R_386_32:
+        *(uint32_t *)code_ptr = value;
+        break;
+    case R_386_PC32:
+        *(uint32_t *)code_ptr = value - (long)code_ptr;
+        break;
+    default:
+        tcg_abort();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    flags &= TCG_CALL_TYPE_MASK;
+    switch(flags) {
+    case TCG_CALL_TYPE_STD:
+        return 0;
+    case TCG_CALL_TYPE_REGPARM_1:
+    case TCG_CALL_TYPE_REGPARM_2:
+    case TCG_CALL_TYPE_REGPARM:
+        return flags - TCG_CALL_TYPE_REGPARM_1 + 1;
+    default:
+        tcg_abort();
+    }
+}
+
+/* parse target specific constraints */
+int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch(ct_str[0]) {
+    case 'a':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_EAX);
+        break;
+    case 'b':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_EBX);
+        break;
+    case 'c':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_ECX);
+        break;
+    case 'd':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_EDX);
+        break;
+    case 'S':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_ESI);
+        break;
+    case 'D':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_EDI);
+        break;
+    case 'q':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xf);
+        break;
+    case 'r':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xff);
+        break;
+
+        /* qemu_ld/st address constraint */
+    case 'L':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_EAX);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_EDX);
+        break;
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+                                         const TCGArgConstraint *arg_ct)
+{
+    int ct;
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    else
+        return 0;
+}
+
+#define ARITH_ADD 0
+#define ARITH_OR  1
+#define ARITH_ADC 2
+#define ARITH_SBB 3
+#define ARITH_AND 4
+#define ARITH_SUB 5
+#define ARITH_XOR 6
+#define ARITH_CMP 7
+
+#define SHIFT_SHL 4
+#define SHIFT_SHR 5
+#define SHIFT_SAR 7
+
+#define JCC_JMP (-1)
+#define JCC_JO  0x0
+#define JCC_JNO 0x1
+#define JCC_JB  0x2
+#define JCC_JAE 0x3
+#define JCC_JE  0x4
+#define JCC_JNE 0x5
+#define JCC_JBE 0x6
+#define JCC_JA  0x7
+#define JCC_JS  0x8
+#define JCC_JNS 0x9
+#define JCC_JP  0xa
+#define JCC_JNP 0xb
+#define JCC_JL  0xc
+#define JCC_JGE 0xd
+#define JCC_JLE 0xe
+#define JCC_JG  0xf
+
+#define P_EXT   0x100 /* 0x0f opcode prefix */
+
+static const uint8_t tcg_cond_to_jcc[10] = {
+    [TCG_COND_EQ] = JCC_JE,
+    [TCG_COND_NE] = JCC_JNE,
+    [TCG_COND_LT] = JCC_JL,
+    [TCG_COND_GE] = JCC_JGE,
+    [TCG_COND_LE] = JCC_JLE,
+    [TCG_COND_GT] = JCC_JG,
+    [TCG_COND_LTU] = JCC_JB,
+    [TCG_COND_GEU] = JCC_JAE,
+    [TCG_COND_LEU] = JCC_JBE,
+    [TCG_COND_GTU] = JCC_JA,
+};
+
+static inline void tcg_out_opc(TCGContext *s, int opc)
+{
+    if (opc & P_EXT)
+        tcg_out8(s, 0x0f);
+    tcg_out8(s, opc);
+}
+
+static inline void tcg_out_modrm(TCGContext *s, int opc, int r, int rm)
+{
+    tcg_out_opc(s, opc);
+    tcg_out8(s, 0xc0 | (r << 3) | rm);
+}
+
+/* rm == -1 means no register index */
+static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, int rm, 
+                                        int32_t offset)
+{
+    tcg_out_opc(s, opc);
+    if (rm == -1) {
+        tcg_out8(s, 0x05 | (r << 3));
+        tcg_out32(s, offset);
+    } else if (offset == 0 && rm != TCG_REG_EBP) {
+        if (rm == TCG_REG_ESP) {
+            tcg_out8(s, 0x04 | (r << 3));
+            tcg_out8(s, 0x24);
+        } else {
+            tcg_out8(s, 0x00 | (r << 3) | rm);
+        }
+    } else if ((int8_t)offset == offset) {
+        if (rm == TCG_REG_ESP) {
+            tcg_out8(s, 0x44 | (r << 3));
+            tcg_out8(s, 0x24);
+        } else {
+            tcg_out8(s, 0x40 | (r << 3) | rm);
+        }
+        tcg_out8(s, offset);
+    } else {
+        if (rm == TCG_REG_ESP) {
+            tcg_out8(s, 0x84 | (r << 3));
+            tcg_out8(s, 0x24);
+        } else {
+            tcg_out8(s, 0x80 | (r << 3) | rm);
+        }
+        tcg_out32(s, offset);
+    }
+}
+
+static inline void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    if (arg != ret)
+        tcg_out_modrm(s, 0x8b, ret, arg);
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+                                int ret, int32_t arg)
+{
+    if (arg == 0) {
+        /* xor r0,r0 */
+        tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret);
+    } else {
+        tcg_out8(s, 0xb8 + ret);
+        tcg_out32(s, arg);
+    }
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+                              int arg1, tcg_target_long arg2)
+{
+    /* movl */
+    tcg_out_modrm_offset(s, 0x8b, ret, arg1, arg2);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+                              int arg1, tcg_target_long arg2)
+{
+    /* movl */
+    tcg_out_modrm_offset(s, 0x89, arg, arg1, arg2);
+}
+
+static inline void tgen_arithi(TCGContext *s, int c, int r0, int32_t val)
+{
+    if (val == (int8_t)val) {
+        tcg_out_modrm(s, 0x83, c, r0);
+        tcg_out8(s, val);
+    } else {
+        tcg_out_modrm(s, 0x81, c, r0);
+        tcg_out32(s, val);
+    }
+}
+
+void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    if (val != 0)
+        tgen_arithi(s, ARITH_ADD, reg, val);
+}
+
+static void tcg_out_jxx(TCGContext *s, int opc, int label_index)
+{
+    int32_t val, val1;
+    TCGLabel *l = &s->labels[label_index];
+    
+    if (l->has_value) {
+        val = l->u.value - (tcg_target_long)s->code_ptr;
+        val1 = val - 2;
+        if ((int8_t)val1 == val1) {
+            if (opc == -1)
+                tcg_out8(s, 0xeb);
+            else
+                tcg_out8(s, 0x70 + opc);
+            tcg_out8(s, val1);
+        } else {
+            if (opc == -1) {
+                tcg_out8(s, 0xe9);
+                tcg_out32(s, val - 5);
+            } else {
+                tcg_out8(s, 0x0f);
+                tcg_out8(s, 0x80 + opc);
+                tcg_out32(s, val - 6);
+            }
+        }
+    } else {
+        if (opc == -1) {
+            tcg_out8(s, 0xe9);
+        } else {
+            tcg_out8(s, 0x0f);
+            tcg_out8(s, 0x80 + opc);
+        }
+        tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4);
+        s->code_ptr += 4;
+    }
+}
+
+static void tcg_out_brcond(TCGContext *s, int cond, 
+                           TCGArg arg1, TCGArg arg2, int const_arg2,
+                           int label_index)
+{
+    if (const_arg2) {
+        if (arg2 == 0) {
+            /* test r, r */
+            tcg_out_modrm(s, 0x85, arg1, arg1);
+        } else {
+            tgen_arithi(s, ARITH_CMP, arg1, arg2);
+        }
+    } else {
+        tcg_out_modrm(s, 0x01 | (ARITH_CMP << 3), arg2, arg1);
+    }
+    tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index);
+}
+
+/* XXX: we implement it at the target level to avoid having to
+   handle cross basic blocks temporaries */
+static void tcg_out_brcond2(TCGContext *s,
+                            const TCGArg *args, const int *const_args)
+{
+    int label_next;
+    label_next = gen_new_label();
+    switch(args[4]) {
+    case TCG_COND_EQ:
+        tcg_out_brcond(s, TCG_COND_NE, args[0], args[2], const_args[2], label_next);
+        tcg_out_brcond(s, TCG_COND_EQ, args[1], args[3], const_args[3], args[5]);
+        break;
+    case TCG_COND_NE:
+        tcg_out_brcond(s, TCG_COND_NE, args[0], args[2], const_args[2], args[5]);
+        tcg_out_brcond(s, TCG_COND_NE, args[1], args[3], const_args[3], args[5]);
+        break;
+    case TCG_COND_LT:
+        tcg_out_brcond(s, TCG_COND_LT, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_LTU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_LE:
+        tcg_out_brcond(s, TCG_COND_LT, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_LEU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_GT:
+        tcg_out_brcond(s, TCG_COND_GT, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_GTU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_GE:
+        tcg_out_brcond(s, TCG_COND_GT, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_GEU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_LTU:
+        tcg_out_brcond(s, TCG_COND_LTU, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_LTU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_LEU:
+        tcg_out_brcond(s, TCG_COND_LTU, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_LEU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_GTU:
+        tcg_out_brcond(s, TCG_COND_GTU, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_GTU, args[0], args[2], const_args[2], args[5]);
+        break;
+    case TCG_COND_GEU:
+        tcg_out_brcond(s, TCG_COND_GTU, args[1], args[3], const_args[3], args[5]);
+        tcg_out_jxx(s, JCC_JNE, label_next);
+        tcg_out_brcond(s, TCG_COND_GEU, args[0], args[2], const_args[2], args[5]);
+        break;
+    default:
+        tcg_abort();
+    }
+    tcg_out_label(s, label_next, (tcg_target_long)s->code_ptr);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+/* XXX: qemu_ld and qemu_st could be modified to clobber only EDX and
+   EAX. It will be useful once fixed registers globals are less
+   common. */
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap;
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label3_ptr;
+#endif
+    int addr_reg2;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0;
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    r0 = TCG_REG_EAX;
+    r1 = TCG_REG_EDX;
+
+#if defined(CONFIG_SOFTMMU)
+    tcg_out_mov(s, r1, addr_reg); 
+
+    tcg_out_mov(s, r0, addr_reg); 
+ 
+    tcg_out_modrm(s, 0xc1, 5, r1); /* shr $x, r1 */
+    tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); 
+    
+    tcg_out_modrm(s, 0x81, 4, r0); /* andl $x, r0 */
+    tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+    
+    tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */
+    tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+    tcg_out_opc(s, 0x8d); /* lea offset(r1, %ebp), r1 */
+    tcg_out8(s, 0x80 | (r1 << 3) | 0x04);
+    tcg_out8(s, (5 << 3) | r1);
+    tcg_out32(s, offsetof(CPUState, tlb_table[mem_index][0].addr_read));
+
+    /* cmp 0(r1), r0 */
+    tcg_out_modrm_offset(s, 0x3b, r0, r1, 0);
+    
+    tcg_out_mov(s, r0, addr_reg);
+    
+#if TARGET_LONG_BITS == 32
+    /* je label1 */
+    tcg_out8(s, 0x70 + JCC_JE);
+    label1_ptr = s->code_ptr;
+    s->code_ptr++;
+#else
+    /* jne label3 */
+    tcg_out8(s, 0x70 + JCC_JNE);
+    label3_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* cmp 4(r1), addr_reg2 */
+    tcg_out_modrm_offset(s, 0x3b, addr_reg2, r1, 4);
+
+    /* je label1 */
+    tcg_out8(s, 0x70 + JCC_JE);
+    label1_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* label3: */
+    *label3_ptr = s->code_ptr - label3_ptr - 1;
+#endif
+
+    /* XXX: move that code at the end of the TB */
+#if TARGET_LONG_BITS == 32
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EDX, mem_index);
+#else
+    tcg_out_mov(s, TCG_REG_EDX, addr_reg2);
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index);
+#endif
+    tcg_out8(s, 0xe8);
+    tcg_out32(s, (tcg_target_long)qemu_ld_helpers[s_bits] - 
+              (tcg_target_long)s->code_ptr - 4);
+
+    switch(opc) {
+    case 0 | 4:
+        /* movsbl */
+        tcg_out_modrm(s, 0xbe | P_EXT, data_reg, TCG_REG_EAX);
+        break;
+    case 1 | 4:
+        /* movswl */
+        tcg_out_modrm(s, 0xbf | P_EXT, data_reg, TCG_REG_EAX);
+        break;
+    case 0:
+    case 1:
+    case 2:
+    default:
+        tcg_out_mov(s, data_reg, TCG_REG_EAX);
+        break;
+    case 3:
+        if (data_reg == TCG_REG_EDX) {
+            tcg_out_opc(s, 0x90 + TCG_REG_EDX); /* xchg %edx, %eax */
+            tcg_out_mov(s, data_reg2, TCG_REG_EAX);
+        } else {
+            tcg_out_mov(s, data_reg, TCG_REG_EAX);
+            tcg_out_mov(s, data_reg2, TCG_REG_EDX);
+        }
+        break;
+    }
+
+    /* jmp label2 */
+    tcg_out8(s, 0xeb);
+    label2_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* label1: */
+    *label1_ptr = s->code_ptr - label1_ptr - 1;
+
+    /* add x(r1), r0 */
+    tcg_out_modrm_offset(s, 0x03, r0, r1, offsetof(CPUTLBEntry, addend) - 
+                         offsetof(CPUTLBEntry, addr_read));
+#else
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 1;
+#else
+    bswap = 0;
+#endif
+    switch(opc) {
+    case 0:
+        /* movzbl */
+        tcg_out_modrm_offset(s, 0xb6 | P_EXT, data_reg, r0, 0);
+        break;
+    case 0 | 4:
+        /* movsbl */
+        tcg_out_modrm_offset(s, 0xbe | P_EXT, data_reg, r0, 0);
+        break;
+    case 1:
+        /* movzwl */
+        tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, 0);
+        if (bswap) {
+            /* rolw $8, data_reg */
+            tcg_out8(s, 0x66); 
+            tcg_out_modrm(s, 0xc1, 0, data_reg);
+            tcg_out8(s, 8);
+        }
+        break;
+    case 1 | 4:
+        /* movswl */
+        tcg_out_modrm_offset(s, 0xbf | P_EXT, data_reg, r0, 0);
+        if (bswap) {
+            /* rolw $8, data_reg */
+            tcg_out8(s, 0x66); 
+            tcg_out_modrm(s, 0xc1, 0, data_reg);
+            tcg_out8(s, 8);
+
+            /* movswl data_reg, data_reg */
+            tcg_out_modrm(s, 0xbf | P_EXT, data_reg, data_reg);
+        }
+        break;
+    case 2:
+        /* movl (r0), data_reg */
+        tcg_out_modrm_offset(s, 0x8b, data_reg, r0, 0);
+        if (bswap) {
+            /* bswap */
+            tcg_out_opc(s, (0xc8 + data_reg) | P_EXT);
+        }
+        break;
+    case 3:
+        /* XXX: could be nicer */
+        if (r0 == data_reg) {
+            r1 = TCG_REG_EDX;
+            if (r1 == data_reg)
+                r1 = TCG_REG_EAX;
+            tcg_out_mov(s, r1, r0);
+            r0 = r1;
+        }
+        if (!bswap) {
+            tcg_out_modrm_offset(s, 0x8b, data_reg, r0, 0);
+            tcg_out_modrm_offset(s, 0x8b, data_reg2, r0, 4);
+        } else {
+            tcg_out_modrm_offset(s, 0x8b, data_reg, r0, 4);
+            tcg_out_opc(s, (0xc8 + data_reg) | P_EXT);
+
+            tcg_out_modrm_offset(s, 0x8b, data_reg2, r0, 0);
+            /* bswap */
+            tcg_out_opc(s, (0xc8 + data_reg2) | P_EXT);
+        }
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr = s->code_ptr - label2_ptr - 1;
+#endif
+}
+
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_reg, data_reg, data_reg2, r0, r1, mem_index, s_bits, bswap;
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label3_ptr;
+#endif
+    int addr_reg2;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0;
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+    mem_index = *args;
+
+    s_bits = opc;
+
+    r0 = TCG_REG_EAX;
+    r1 = TCG_REG_EDX;
+
+#if defined(CONFIG_SOFTMMU)
+    tcg_out_mov(s, r1, addr_reg); 
+
+    tcg_out_mov(s, r0, addr_reg); 
+ 
+    tcg_out_modrm(s, 0xc1, 5, r1); /* shr $x, r1 */
+    tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); 
+    
+    tcg_out_modrm(s, 0x81, 4, r0); /* andl $x, r0 */
+    tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+    
+    tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */
+    tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+    tcg_out_opc(s, 0x8d); /* lea offset(r1, %ebp), r1 */
+    tcg_out8(s, 0x80 | (r1 << 3) | 0x04);
+    tcg_out8(s, (5 << 3) | r1);
+    tcg_out32(s, offsetof(CPUState, tlb_table[mem_index][0].addr_write));
+
+    /* cmp 0(r1), r0 */
+    tcg_out_modrm_offset(s, 0x3b, r0, r1, 0);
+    
+    tcg_out_mov(s, r0, addr_reg);
+    
+#if TARGET_LONG_BITS == 32
+    /* je label1 */
+    tcg_out8(s, 0x70 + JCC_JE);
+    label1_ptr = s->code_ptr;
+    s->code_ptr++;
+#else
+    /* jne label3 */
+    tcg_out8(s, 0x70 + JCC_JNE);
+    label3_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* cmp 4(r1), addr_reg2 */
+    tcg_out_modrm_offset(s, 0x3b, addr_reg2, r1, 4);
+
+    /* je label1 */
+    tcg_out8(s, 0x70 + JCC_JE);
+    label1_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* label3: */
+    *label3_ptr = s->code_ptr - label3_ptr - 1;
+#endif
+
+    /* XXX: move that code at the end of the TB */
+#if TARGET_LONG_BITS == 32
+    if (opc == 3) {
+        tcg_out_mov(s, TCG_REG_EDX, data_reg);
+        tcg_out_mov(s, TCG_REG_ECX, data_reg2);
+        tcg_out8(s, 0x6a); /* push Ib */
+        tcg_out8(s, mem_index);
+        tcg_out8(s, 0xe8);
+        tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - 
+                  (tcg_target_long)s->code_ptr - 4);
+        tcg_out_addi(s, TCG_REG_ESP, 4);
+    } else {
+        switch(opc) {
+        case 0:
+            /* movzbl */
+            tcg_out_modrm(s, 0xb6 | P_EXT, TCG_REG_EDX, data_reg);
+            break;
+        case 1:
+            /* movzwl */
+            tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_EDX, data_reg);
+            break;
+        case 2:
+            tcg_out_mov(s, TCG_REG_EDX, data_reg);
+            break;
+        }
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_ECX, mem_index);
+        tcg_out8(s, 0xe8);
+        tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - 
+                  (tcg_target_long)s->code_ptr - 4);
+    }
+#else
+    if (opc == 3) {
+        tcg_out_mov(s, TCG_REG_EDX, addr_reg2);
+        tcg_out8(s, 0x6a); /* push Ib */
+        tcg_out8(s, mem_index);
+        tcg_out_opc(s, 0x50 + data_reg2); /* push */
+        tcg_out_opc(s, 0x50 + data_reg); /* push */
+        tcg_out8(s, 0xe8);
+        tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - 
+                  (tcg_target_long)s->code_ptr - 4);
+        tcg_out_addi(s, TCG_REG_ESP, 12);
+    } else {
+        tcg_out_mov(s, TCG_REG_EDX, addr_reg2);
+        switch(opc) {
+        case 0:
+            /* movzbl */
+            tcg_out_modrm(s, 0xb6 | P_EXT, TCG_REG_ECX, data_reg);
+            break;
+        case 1:
+            /* movzwl */
+            tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_ECX, data_reg);
+            break;
+        case 2:
+            tcg_out_mov(s, TCG_REG_ECX, data_reg);
+            break;
+        }
+        tcg_out8(s, 0x6a); /* push Ib */
+        tcg_out8(s, mem_index);
+        tcg_out8(s, 0xe8);
+        tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - 
+                  (tcg_target_long)s->code_ptr - 4);
+        tcg_out_addi(s, TCG_REG_ESP, 4);
+    }
+#endif
+    
+    /* jmp label2 */
+    tcg_out8(s, 0xeb);
+    label2_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* label1: */
+    *label1_ptr = s->code_ptr - label1_ptr - 1;
+
+    /* add x(r1), r0 */
+    tcg_out_modrm_offset(s, 0x03, r0, r1, offsetof(CPUTLBEntry, addend) - 
+                         offsetof(CPUTLBEntry, addr_write));
+#else
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 1;
+#else
+    bswap = 0;
+#endif
+    switch(opc) {
+    case 0:
+        /* movb */
+        tcg_out_modrm_offset(s, 0x88, data_reg, r0, 0);
+        break;
+    case 1:
+        if (bswap) {
+            tcg_out_mov(s, r1, data_reg);
+            tcg_out8(s, 0x66); /* rolw $8, %ecx */
+            tcg_out_modrm(s, 0xc1, 0, r1);
+            tcg_out8(s, 8);
+            data_reg = r1;
+        }
+        /* movw */
+        tcg_out8(s, 0x66);
+        tcg_out_modrm_offset(s, 0x89, data_reg, r0, 0);
+        break;
+    case 2:
+        if (bswap) {
+            tcg_out_mov(s, r1, data_reg);
+            /* bswap data_reg */
+            tcg_out_opc(s, (0xc8 + r1) | P_EXT);
+            data_reg = r1;
+        }
+        /* movl */
+        tcg_out_modrm_offset(s, 0x89, data_reg, r0, 0);
+        break;
+    case 3:
+        if (bswap) {
+            tcg_out_mov(s, r1, data_reg2);
+            /* bswap data_reg */
+            tcg_out_opc(s, (0xc8 + r1) | P_EXT);
+            tcg_out_modrm_offset(s, 0x89, r1, r0, 0);
+            tcg_out_mov(s, r1, data_reg);
+            /* bswap data_reg */
+            tcg_out_opc(s, (0xc8 + r1) | P_EXT);
+            tcg_out_modrm_offset(s, 0x89, r1, r0, 4);
+        } else {
+            tcg_out_modrm_offset(s, 0x89, data_reg, r0, 0);
+            tcg_out_modrm_offset(s, 0x89, data_reg2, r0, 4);
+        }
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr = s->code_ptr - label2_ptr - 1;
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, int opc, 
+                              const TCGArg *args, const int *const_args)
+{
+    int c;
+    
+    switch(opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EAX, args[0]);
+        tcg_out8(s, 0xe9); /* jmp tb_ret_addr */
+        tcg_out32(s, tb_ret_addr - s->code_ptr - 4);
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+            tcg_out8(s, 0xe9); /* jmp im */
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            tcg_out32(s, 0);
+        } else {
+            /* indirect jump method */
+            /* jmp Ev */
+            tcg_out_modrm_offset(s, 0xff, 4, -1, 
+                                 (tcg_target_long)(s->tb_next + args[0]));
+        }
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_call:
+        if (const_args[0]) {
+            tcg_out8(s, 0xe8);
+            tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4);
+        } else {
+            tcg_out_modrm(s, 0xff, 2, args[0]);
+        }
+        break;
+    case INDEX_op_jmp:
+        if (const_args[0]) {
+            tcg_out8(s, 0xe9);
+            tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4);
+        } else {
+            tcg_out_modrm(s, 0xff, 4, args[0]);
+        }
+        break;
+    case INDEX_op_br:
+        tcg_out_jxx(s, JCC_JMP, args[0]);
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+        break;
+    case INDEX_op_ld8u_i32:
+        /* movzbl */
+        tcg_out_modrm_offset(s, 0xb6 | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld8s_i32:
+        /* movsbl */
+        tcg_out_modrm_offset(s, 0xbe | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16u_i32:
+        /* movzwl */
+        tcg_out_modrm_offset(s, 0xb7 | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16s_i32:
+        /* movswl */
+        tcg_out_modrm_offset(s, 0xbf | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld_i32:
+        /* movl */
+        tcg_out_modrm_offset(s, 0x8b, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st8_i32:
+        /* movb */
+        tcg_out_modrm_offset(s, 0x88, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st16_i32:
+        /* movw */
+        tcg_out8(s, 0x66);
+        tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st_i32:
+        /* movl */
+        tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_sub_i32:
+        c = ARITH_SUB;
+        goto gen_arith;
+    case INDEX_op_and_i32:
+        c = ARITH_AND;
+        goto gen_arith;
+    case INDEX_op_or_i32:
+        c = ARITH_OR;
+        goto gen_arith;
+    case INDEX_op_xor_i32:
+        c = ARITH_XOR;
+        goto gen_arith;
+    case INDEX_op_add_i32:
+        c = ARITH_ADD;
+    gen_arith:
+        if (const_args[2]) {
+            tgen_arithi(s, c, args[0], args[2]);
+        } else {
+            tcg_out_modrm(s, 0x01 | (c << 3), args[2], args[0]);
+        }
+        break;
+    case INDEX_op_mul_i32:
+        if (const_args[2]) {
+            int32_t val;
+            val = args[2];
+            if (val == (int8_t)val) {
+                tcg_out_modrm(s, 0x6b, args[0], args[0]);
+                tcg_out8(s, val);
+            } else {
+                tcg_out_modrm(s, 0x69, args[0], args[0]);
+                tcg_out32(s, val);
+            }
+        } else {
+            tcg_out_modrm(s, 0xaf | P_EXT, args[0], args[2]);
+        }
+        break;
+    case INDEX_op_mulu2_i32:
+        tcg_out_modrm(s, 0xf7, 4, args[3]);
+        break;
+    case INDEX_op_div2_i32:
+        tcg_out_modrm(s, 0xf7, 7, args[4]);
+        break;
+    case INDEX_op_divu2_i32:
+        tcg_out_modrm(s, 0xf7, 6, args[4]);
+        break;
+    case INDEX_op_shl_i32:
+        c = SHIFT_SHL;
+    gen_shift32:
+        if (const_args[2]) {
+            if (args[2] == 1) {
+                tcg_out_modrm(s, 0xd1, c, args[0]);
+            } else {
+                tcg_out_modrm(s, 0xc1, c, args[0]);
+                tcg_out8(s, args[2]);
+            }
+        } else {
+            tcg_out_modrm(s, 0xd3, c, args[0]);
+        }
+        break;
+    case INDEX_op_shr_i32:
+        c = SHIFT_SHR;
+        goto gen_shift32;
+    case INDEX_op_sar_i32:
+        c = SHIFT_SAR;
+        goto gen_shift32;
+        
+    case INDEX_op_add2_i32:
+        if (const_args[4]) 
+            tgen_arithi(s, ARITH_ADD, args[0], args[4]);
+        else
+            tcg_out_modrm(s, 0x01 | (ARITH_ADD << 3), args[4], args[0]);
+        if (const_args[5]) 
+            tgen_arithi(s, ARITH_ADC, args[1], args[5]);
+        else
+            tcg_out_modrm(s, 0x01 | (ARITH_ADC << 3), args[5], args[1]);
+        break;
+    case INDEX_op_sub2_i32:
+        if (const_args[4]) 
+            tgen_arithi(s, ARITH_SUB, args[0], args[4]);
+        else
+            tcg_out_modrm(s, 0x01 | (ARITH_SUB << 3), args[4], args[0]);
+        if (const_args[5]) 
+            tgen_arithi(s, ARITH_SBB, args[1], args[5]);
+        else
+            tcg_out_modrm(s, 0x01 | (ARITH_SBB << 3), args[5], args[1]);
+        break;
+    case INDEX_op_brcond_i32:
+        tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], args[3]);
+        break;
+    case INDEX_op_brcond2_i32:
+        tcg_out_brcond2(s, args, const_args);
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, args, 2);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld(s, args, 3);
+        break;
+        
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, args, 2);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st(s, args, 3);
+        break;
+
+    default:
+        tcg_abort();
+    }
+}
+
+static const TCGTargetOpDef x86_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "ri" } },
+    { INDEX_op_jmp, { "ri" } },
+    { INDEX_op_br, { } },
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "q", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+
+    { INDEX_op_add_i32, { "r", "0", "ri" } },
+    { INDEX_op_sub_i32, { "r", "0", "ri" } },
+    { INDEX_op_mul_i32, { "r", "0", "ri" } },
+    { INDEX_op_mulu2_i32, { "a", "d", "a", "r" } },
+    { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } },
+    { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } },
+    { INDEX_op_and_i32, { "r", "0", "ri" } },
+    { INDEX_op_or_i32, { "r", "0", "ri" } },
+    { INDEX_op_xor_i32, { "r", "0", "ri" } },
+
+    { INDEX_op_shl_i32, { "r", "0", "ci" } },
+    { INDEX_op_shr_i32, { "r", "0", "ci" } },
+    { INDEX_op_sar_i32, { "r", "0", "ci" } },
+
+    { INDEX_op_brcond_i32, { "r", "ri" } },
+
+    { INDEX_op_add2_i32, { "r", "r", "0", "1", "ri", "ri" } },
+    { INDEX_op_sub2_i32, { "r", "r", "0", "1", "ri", "ri" } },
+    { INDEX_op_brcond2_i32, { "r", "r", "ri", "ri" } },
+
+#if TARGET_LONG_BITS == 32
+    { INDEX_op_qemu_ld8u, { "r", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "r", "L" } },
+
+    { INDEX_op_qemu_st8, { "cb", "L" } },
+    { INDEX_op_qemu_st16, { "L", "L" } },
+    { INDEX_op_qemu_st32, { "L", "L" } },
+    { INDEX_op_qemu_st64, { "L", "L", "L" } },
+#else
+    { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "r", "L", "L" } },
+
+    { INDEX_op_qemu_st8, { "cb", "L", "L" } },
+    { INDEX_op_qemu_st16, { "L", "L", "L" } },
+    { INDEX_op_qemu_st32, { "L", "L", "L" } },
+    { INDEX_op_qemu_st64, { "L", "L", "L", "L" } },
+#endif
+    { -1 },
+};
+
+static int tcg_target_callee_save_regs[] = {
+    /*    TCG_REG_EBP, */ /* currently used for the global env, so no
+                             need to save */
+    TCG_REG_EBX,
+    TCG_REG_ESI,
+    TCG_REG_EDI,
+};
+
+static inline void tcg_out_push(TCGContext *s, int reg)
+{
+    tcg_out_opc(s, 0x50 + reg);
+}
+
+static inline void tcg_out_pop(TCGContext *s, int reg)
+{
+    tcg_out_opc(s, 0x58 + reg);
+}
+
+/* Generate global QEMU prologue and epilogue code */
+void tcg_target_qemu_prologue(TCGContext *s)
+{
+    int i, frame_size, push_size, stack_addend;
+    
+    /* TB prologue */
+    /* save all callee saved registers */
+    for(i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
+        tcg_out_push(s, tcg_target_callee_save_regs[i]);
+    }
+    /* reserve some stack space */
+    push_size = 4 + ARRAY_SIZE(tcg_target_callee_save_regs) * 4;
+    frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE;
+    frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & 
+        ~(TCG_TARGET_STACK_ALIGN - 1);
+    stack_addend = frame_size - push_size;
+    tcg_out_addi(s, TCG_REG_ESP, -stack_addend);
+
+    tcg_out_modrm(s, 0xff, 4, TCG_REG_EAX); /* jmp *%eax */
+    
+    /* TB epilogue */
+    tb_ret_addr = s->code_ptr;
+    tcg_out_addi(s, TCG_REG_ESP, stack_addend);
+    for(i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) {
+        tcg_out_pop(s, tcg_target_callee_save_regs[i]);
+    }
+    tcg_out8(s, 0xc3); /* ret */
+}
+
+void tcg_target_init(TCGContext *s)
+{
+    /* fail safe */
+    if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry))
+        tcg_abort();
+
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xff);
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_EAX) | 
+                     (1 << TCG_REG_EDX) | 
+                     (1 << TCG_REG_ECX));
+    
+    tcg_regset_clear(s->reserved_regs);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_ESP);
+
+    tcg_add_target_add_op_defs(x86_op_defs);
+}
diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h
new file mode 100644
index 0000000..37fdaa5
--- /dev/null
+++ b/tcg/i386/tcg-target.h
@@ -0,0 +1,55 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_I386 1
+
+#define TCG_TARGET_REG_BITS 32
+//#define TCG_TARGET_WORDS_BIGENDIAN
+
+#define TCG_TARGET_NB_REGS 8
+
+enum {
+    TCG_REG_EAX = 0,
+    TCG_REG_ECX,
+    TCG_REG_EDX,
+    TCG_REG_EBX,
+    TCG_REG_ESP,
+    TCG_REG_EBP,
+    TCG_REG_ESI,
+    TCG_REG_EDI,
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_ESP 
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_CALL_STACK_OFFSET 0
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_EBP
+#define TCG_AREG1 TCG_REG_EBX
+#define TCG_AREG2 TCG_REG_ESI
+#define TCG_AREG3 TCG_REG_EDI
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
diff --git a/tcg/ppc/tcg-target.c b/tcg/ppc/tcg-target.c
new file mode 100644
index 0000000..ad17468
--- /dev/null
+++ b/tcg/ppc/tcg-target.c
@@ -0,0 +1,1492 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static uint8_t *tb_ret_addr;
+
+#ifdef __APPLE__
+#define LINKAGE_AREA_SIZE 24
+#define BACK_CHAIN_OFFSET 8
+#else
+#define LINKAGE_AREA_SIZE 8
+#define BACK_CHAIN_OFFSET 4
+#endif
+
+#define FAST_PATH
+#if TARGET_PHYS_ADDR_BITS <= 32
+#define ADDEND_OFFSET 0
+#else
+#define ADDEND_OFFSET 4
+#endif
+
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "r0",
+    "r1",
+    "rp",
+    "r3",
+    "r4",
+    "r5",
+    "r6",
+    "r7",
+    "r8",
+    "r9",
+    "r10",
+    "r11",
+    "r12",
+    "r13",
+    "r14",
+    "r15",
+    "r16",
+    "r17",
+    "r18",
+    "r19",
+    "r20",
+    "r21",
+    "r22",
+    "r23",
+    "r24",
+    "r25",
+    "r26",
+    "r27",
+    "r28",
+    "r29",
+    "r30",
+    "r31"
+};
+
+static const int tcg_target_reg_alloc_order[] = {
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R28,
+    TCG_REG_R29,
+    TCG_REG_R30,
+    TCG_REG_R31,
+#ifdef __APPLE__
+    TCG_REG_R2,
+#endif
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+#ifndef __APPLE__
+    TCG_REG_R11,
+#endif
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R0,
+    TCG_REG_R1,
+    TCG_REG_R2,
+    TCG_REG_R24,
+    TCG_REG_R25,
+    TCG_REG_R26,
+    TCG_REG_R27
+};
+
+static const int tcg_target_call_iarg_regs[] = {
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+    TCG_REG_R3,
+    TCG_REG_R4
+};
+
+static const int tcg_target_callee_save_regs[] = {
+#ifdef __APPLE__
+    TCG_REG_R11,
+    TCG_REG_R13,
+#endif
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R28,
+    TCG_REG_R29,
+    TCG_REG_R30,
+    TCG_REG_R31
+};
+
+static uint32_t reloc_pc24_val (void *pc, tcg_target_long target)
+{
+    tcg_target_long disp;
+
+    disp = target - (tcg_target_long) pc;
+    if ((disp << 6) >> 6 != disp)
+        tcg_abort ();
+
+    return disp & 0x3fffffc;
+}
+
+static void reloc_pc24 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc)
+        | reloc_pc24_val (pc, target);
+}
+
+static uint16_t reloc_pc14_val (void *pc, tcg_target_long target)
+{
+    tcg_target_long disp;
+
+    disp = target - (tcg_target_long) pc;
+    if (disp != (int16_t) disp)
+        tcg_abort ();
+
+    return disp & 0xfffc;
+}
+
+static void reloc_pc14 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc)
+        | reloc_pc14_val (pc, target);
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+                        tcg_target_long value, tcg_target_long addend)
+{
+    value += addend;
+    switch (type) {
+    case R_PPC_REL14:
+        reloc_pc14 (code_ptr, value);
+        break;
+    case R_PPC_REL24:
+        reloc_pc24 (code_ptr, value);
+        break;
+    default:
+        tcg_abort();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    return sizeof (tcg_target_call_iarg_regs) / sizeof (tcg_target_call_iarg_regs[0]);
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch (ct_str[0]) {
+    case 'A': case 'B': case 'C': case 'D':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, 3 + ct_str[0] - 'A');
+        break;
+    case 'r':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        break;
+#ifdef CONFIG_SOFTMMU
+    case 'L':                   /* qemu_ld constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4);
+        break;
+    case 'K':                   /* qemu_st[8..32] constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5);
+#if TARGET_LONG_BITS == 64
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6);
+#endif
+        break;
+    case 'M':                   /* qemu_st64 constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R4);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R5);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R6);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R7);
+        break;
+#else
+    case 'L':
+    case 'K':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        break;
+    case 'M':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_R3);
+        break;
+#endif
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static int tcg_target_const_match(tcg_target_long val,
+                                  const TCGArgConstraint *arg_ct)
+{
+    int ct;
+
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    return 0;
+}
+
+#define OPCD(opc) ((opc)<<26)
+#define XO31(opc) (OPCD(31)|((opc)<<1))
+#define XO19(opc) (OPCD(19)|((opc)<<1))
+
+#define B      OPCD(18)
+#define BC     OPCD(16)
+#define LBZ    OPCD(34)
+#define LHZ    OPCD(40)
+#define LHA    OPCD(42)
+#define LWZ    OPCD(32)
+#define STB    OPCD(38)
+#define STH    OPCD(44)
+#define STW    OPCD(36)
+
+#define ADDI   OPCD(14)
+#define ADDIS  OPCD(15)
+#define ORI    OPCD(24)
+#define ORIS   OPCD(25)
+#define XORI   OPCD(26)
+#define XORIS  OPCD(27)
+#define ANDI   OPCD(28)
+#define ANDIS  OPCD(29)
+#define MULLI  OPCD( 7)
+#define CMPLI  OPCD(10)
+#define CMPI   OPCD(11)
+
+#define LWZU   OPCD(33)
+#define STWU   OPCD(37)
+
+#define RLWINM OPCD(21)
+
+#define BCLR   XO19( 16)
+#define BCCTR  XO19(528)
+#define CRAND  XO19(257)
+#define CRANDC XO19(129)
+#define CRNAND XO19(225)
+#define CROR   XO19(449)
+
+#define EXTSB  XO31(954)
+#define EXTSH  XO31(922)
+#define ADD    XO31(266)
+#define ADDE   XO31(138)
+#define ADDC   XO31( 10)
+#define AND    XO31( 28)
+#define SUBF   XO31( 40)
+#define SUBFC  XO31(  8)
+#define SUBFE  XO31(136)
+#define OR     XO31(444)
+#define XOR    XO31(316)
+#define MULLW  XO31(235)
+#define MULHWU XO31( 11)
+#define DIVW   XO31(491)
+#define DIVWU  XO31(459)
+#define CMP    XO31(  0)
+#define CMPL   XO31( 32)
+#define LHBRX  XO31(790)
+#define LWBRX  XO31(534)
+#define STHBRX XO31(918)
+#define STWBRX XO31(662)
+#define MFSPR  XO31(339)
+#define MTSPR  XO31(467)
+#define SRAWI  XO31(824)
+#define NEG    XO31(104)
+
+#define LBZX   XO31( 87)
+#define LHZX   XO31(276)
+#define LHAX   XO31(343)
+#define LWZX   XO31( 23)
+#define STBX   XO31(215)
+#define STHX   XO31(407)
+#define STWX   XO31(151)
+
+#define SPR(a,b) ((((a)<<5)|(b))<<11)
+#define LR     SPR(8, 0)
+#define CTR    SPR(9, 0)
+
+#define SLW    XO31( 24)
+#define SRW    XO31(536)
+#define SRAW   XO31(792)
+
+#define LMW    OPCD(46)
+#define STMW   OPCD(47)
+
+#define TW     XO31(4)
+#define TRAP   (TW | TO (31))
+
+#define RT(r) ((r)<<21)
+#define RS(r) ((r)<<21)
+#define RA(r) ((r)<<16)
+#define RB(r) ((r)<<11)
+#define TO(t) ((t)<<21)
+#define SH(s) ((s)<<11)
+#define MB(b) ((b)<<6)
+#define ME(e) ((e)<<1)
+#define BO(o) ((o)<<21)
+
+#define LK    1
+
+#define TAB(t,a,b) (RT(t) | RA(a) | RB(b))
+#define SAB(s,a,b) (RS(s) | RA(a) | RB(b))
+
+#define BF(n)    ((n)<<23)
+#define BI(n, c) (((c)+((n)*4))<<16)
+#define BT(n, c) (((c)+((n)*4))<<21)
+#define BA(n, c) (((c)+((n)*4))<<16)
+#define BB(n, c) (((c)+((n)*4))<<11)
+
+#define BO_COND_TRUE  BO (12)
+#define BO_COND_FALSE BO (4)
+#define BO_ALWAYS     BO (20)
+
+enum {
+    CR_LT,
+    CR_GT,
+    CR_EQ,
+    CR_SO
+};
+
+static const uint32_t tcg_to_bc[10] = {
+    [TCG_COND_EQ]  = BC | BI (7, CR_EQ) | BO_COND_TRUE,
+    [TCG_COND_NE]  = BC | BI (7, CR_EQ) | BO_COND_FALSE,
+    [TCG_COND_LT]  = BC | BI (7, CR_LT) | BO_COND_TRUE,
+    [TCG_COND_GE]  = BC | BI (7, CR_LT) | BO_COND_FALSE,
+    [TCG_COND_LE]  = BC | BI (7, CR_GT) | BO_COND_FALSE,
+    [TCG_COND_GT]  = BC | BI (7, CR_GT) | BO_COND_TRUE,
+    [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE,
+    [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE,
+    [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE,
+    [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE,
+};
+
+static void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    tcg_out32 (s, OR | SAB (arg, ret, arg));
+}
+
+static void tcg_out_movi(TCGContext *s, TCGType type,
+                         int ret, tcg_target_long arg)
+{
+    if (arg == (int16_t) arg)
+        tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff));
+    else {
+        tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff));
+        if (arg & 0xffff)
+            tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff));
+    }
+}
+
+static void tcg_out_ldst (TCGContext *s, int ret, int addr,
+                          int offset, int op1, int op2)
+{
+    if (offset == (int16_t) offset)
+        tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff));
+    else {
+        tcg_out_movi (s, TCG_TYPE_I32, 0, offset);
+        tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0));
+    }
+}
+
+static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target)
+{
+    tcg_target_long disp;
+
+    disp = target - (tcg_target_long) s->code_ptr;
+    if ((disp << 6) >> 6 == disp)
+        tcg_out32 (s, B | (disp & 0x3fffffc) | mask);
+    else {
+        tcg_out_movi (s, TCG_TYPE_I32, 0, (tcg_target_long) target);
+        tcg_out32 (s, MTSPR | RS (0) | CTR);
+        tcg_out32 (s, BCCTR | BO_ALWAYS | mask);
+    }
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc)
+{
+    int addr_reg, data_reg, data_reg2, r0, mem_index, s_bits, bswap;
+#ifdef CONFIG_SOFTMMU
+    int r1, r2;
+    void *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+    int addr_reg2;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0;
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+    mem_index = *args;
+    s_bits = opc & 3;
+
+#ifdef CONFIG_SOFTMMU
+    r0 = 3;
+    r1 = 4;
+    r2 = 0;
+
+    tcg_out32 (s, (RLWINM
+                   | RA (r0)
+                   | RS (addr_reg)
+                   | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS))
+                   | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS))
+                   | ME (31 - CPU_TLB_ENTRY_BITS)
+                   )
+        );
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0));
+    tcg_out32 (s, (LWZU
+                   | RT (r1)
+                   | RA (r0)
+                   | offsetof (CPUState, tlb_table[mem_index][0].addr_read)
+                   )
+        );
+    tcg_out32 (s, (RLWINM
+                   | RA (r2)
+                   | RS (addr_reg)
+                   | SH (0)
+                   | MB ((32 - s_bits) & 31)
+                   | ME (31 - TARGET_PAGE_BITS)
+                   )
+        );
+
+    tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1));
+#if TARGET_LONG_BITS == 64
+    tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4);
+    tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1));
+    tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ));
+#endif
+
+    label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+    tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+    /* slow path */
+#if TARGET_LONG_BITS == 32
+    tcg_out_mov (s, 3, addr_reg);
+    tcg_out_movi (s, TCG_TYPE_I32, 4, mem_index);
+#else
+    tcg_out_mov (s, 3, addr_reg2);
+    tcg_out_mov (s, 4, addr_reg);
+    tcg_out_movi (s, TCG_TYPE_I32, 5, mem_index);
+#endif
+
+    tcg_out_b (s, LK, (tcg_target_long) qemu_ld_helpers[s_bits]);
+    switch (opc) {
+    case 0|4:
+        tcg_out32 (s, EXTSB | RA (data_reg) | RS (3));
+        break;
+    case 1|4:
+        tcg_out32 (s, EXTSH | RA (data_reg) | RS (3));
+        break;
+    case 0:
+    case 1:
+    case 2:
+        if (data_reg != 3)
+            tcg_out_mov (s, data_reg, 3);
+        break;
+    case 3:
+        if (data_reg == 3) {
+            if (data_reg2 == 4) {
+                tcg_out_mov (s, 0, 4);
+                tcg_out_mov (s, 4, 3);
+                tcg_out_mov (s, 3, 0);
+            }
+            else {
+                tcg_out_mov (s, data_reg2, 3);
+                tcg_out_mov (s, 3, 4);
+            }
+        }
+        else {
+            if (data_reg != 4) tcg_out_mov (s, data_reg, 4);
+            if (data_reg2 != 3) tcg_out_mov (s, data_reg2, 3);
+        }
+        break;
+    }
+    label2_ptr = s->code_ptr;
+    tcg_out32 (s, B);
+
+    /* label1: fast path */
+#ifdef FAST_PATH
+    reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+    /* r0 now contains &env->tlb_table[mem_index][index].addr_read */
+    tcg_out32 (s, (LWZ
+                   | RT (r0)
+                   | RA (r0)
+                   | (ADDEND_OFFSET + offsetof (CPUTLBEntry, addend)
+                      - offsetof (CPUTLBEntry, addr_read))
+                   ));
+    /* r0 = env->tlb_table[mem_index][index].addend */
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+    /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else  /* !CONFIG_SOFTMMU */
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 0;
+#else
+    bswap = 1;
+#endif
+    switch (opc) {
+    default:
+    case 0:
+        tcg_out32 (s, LBZ | RT (data_reg) | RA (r0));
+        break;
+    case 0|4:
+        tcg_out32 (s, LBZ | RT (data_reg) | RA (r0));
+        tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg));
+        break;
+    case 1:
+        if (bswap) tcg_out32 (s, LHBRX | RT (data_reg) | RB (r0));
+        else tcg_out32 (s, LHZ | RT (data_reg) | RA (r0));
+        break;
+    case 1|4:
+        if (bswap) {
+            tcg_out32 (s, LHBRX | RT (data_reg) | RB (r0));
+            tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg));
+        }
+        else tcg_out32 (s, LHA | RT (data_reg) | RA (r0));
+        break;
+    case 2:
+        if (bswap) tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0));
+        else tcg_out32 (s, LWZ | RT (data_reg)| RA (r0));
+        break;
+    case 3:
+        if (bswap) {
+            if (r0 == data_reg) {
+                tcg_out32 (s, LWBRX | RT (0) | RB (r0));
+                tcg_out32 (s, ADDI | RT (r0) | RA (r0) |  4);
+                tcg_out32 (s, LWBRX | RT (data_reg2) | RB (r0));
+                tcg_out_mov (s, data_reg, 0);
+            }
+            else {
+                tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0));
+                tcg_out32 (s, ADDI | RT (r0) | RA (r0) |  4);
+                tcg_out32 (s, LWBRX | RT (data_reg2) | RB (r0));
+            }
+        }
+        else {
+            if (r0 == data_reg2) {
+                tcg_out32 (s, LWZ | RT (0) | RA (r0));
+                tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4);
+                tcg_out_mov (s, data_reg2, 0);
+            }
+            else {
+                tcg_out32 (s, LWZ | RT (data_reg2) | RA (r0));
+                tcg_out32 (s, LWZ | RT (data_reg) | RA (r0) | 4);
+            }
+        }
+        break;
+    }
+
+#ifdef CONFIG_SOFTMMU
+    reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc)
+{
+    int addr_reg, r0, r1, data_reg, data_reg2, mem_index, bswap;
+#ifdef CONFIG_SOFTMMU
+    int r2, ir;
+    void *label1_ptr, *label2_ptr;
+#endif
+#if TARGET_LONG_BITS == 64
+    int addr_reg2;
+#endif
+
+    data_reg = *args++;
+    if (opc == 3)
+        data_reg2 = *args++;
+    else
+        data_reg2 = 0;
+    addr_reg = *args++;
+#if TARGET_LONG_BITS == 64
+    addr_reg2 = *args++;
+#endif
+    mem_index = *args;
+
+#ifdef CONFIG_SOFTMMU
+    r0 = 3;
+    r1 = 4;
+    r2 = 0;
+
+    tcg_out32 (s, (RLWINM
+                   | RA (r0)
+                   | RS (addr_reg)
+                   | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS))
+                   | MB (32 - (CPU_TLB_ENTRY_BITS + CPU_TLB_BITS))
+                   | ME (31 - CPU_TLB_ENTRY_BITS)
+                   )
+        );
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0));
+    tcg_out32 (s, (LWZU
+                   | RT (r1)
+                   | RA (r0)
+                   | offsetof (CPUState, tlb_table[mem_index][0].addr_write)
+                   )
+        );
+    tcg_out32 (s, (RLWINM
+                   | RA (r2)
+                   | RS (addr_reg)
+                   | SH (0)
+                   | MB ((32 - opc) & 31)
+                   | ME (31 - TARGET_PAGE_BITS)
+                   )
+        );
+
+    tcg_out32 (s, CMP | (7 << 23) | RA (r2) | RB (r1));
+#if TARGET_LONG_BITS == 64
+    tcg_out32 (s, LWZ | RT (r1) | RA (r0) | 4);
+    tcg_out32 (s, CMP | BF (6) | RA (addr_reg2) | RB (r1));
+    tcg_out32 (s, CRAND | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ));
+#endif
+
+    label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+    tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+    /* slow path */
+#if TARGET_LONG_BITS == 32
+    tcg_out_mov (s, 3, addr_reg);
+    ir = 4;
+#else
+    tcg_out_mov (s, 3, addr_reg2);
+    tcg_out_mov (s, 4, addr_reg);
+#ifdef TCG_TARGET_CALL_ALIGN_ARGS
+    ir = 5;
+#else
+    ir = 4;
+#endif
+#endif
+
+    switch (opc) {
+    case 0:
+        tcg_out32 (s, (RLWINM
+                       | RA (ir)
+                       | RS (data_reg)
+                       | SH (0)
+                       | MB (24)
+                       | ME (31)));
+        break;
+    case 1:
+        tcg_out32 (s, (RLWINM
+                       | RA (ir)
+                       | RS (data_reg)
+                       | SH (0)
+                       | MB (16)
+                       | ME (31)));
+        break;
+    case 2:
+        tcg_out_mov (s, ir, data_reg);
+        break;
+    case 3:
+#ifdef TCG_TARGET_CALL_ALIGN_ARGS
+        ir = 5;
+#endif
+        tcg_out_mov (s, ir++, data_reg2);
+        tcg_out_mov (s, ir, data_reg);
+        break;
+    }
+    ir++;
+
+    tcg_out_movi (s, TCG_TYPE_I32, ir, mem_index);
+    tcg_out_b (s, LK, (tcg_target_long) qemu_st_helpers[opc]);
+    label2_ptr = s->code_ptr;
+    tcg_out32 (s, B);
+
+    /* label1: fast path */
+#ifdef FAST_PATH
+    reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+    tcg_out32 (s, (LWZ
+                   | RT (r0)
+                   | RA (r0)
+                   | (ADDEND_OFFSET + offsetof (CPUTLBEntry, addend)
+                      - offsetof (CPUTLBEntry, addr_write))
+                   ));
+    /* r0 = env->tlb_table[mem_index][index].addend */
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+    /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else  /* !CONFIG_SOFTMMU */
+    r1 = 3;
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 0;
+#else
+    bswap = 1;
+#endif
+    switch (opc) {
+    case 0:
+        tcg_out32 (s, STB | RS (data_reg) | RA (r0));
+        break;
+    case 1:
+        if (bswap) tcg_out32 (s, STHBRX | RS (data_reg) | RA (0) | RB (r0));
+        else tcg_out32 (s, STH | RS (data_reg) | RA (r0));
+        break;
+    case 2:
+        if (bswap) tcg_out32 (s, STWBRX | RS (data_reg) | RA (0) | RB (r0));
+        else tcg_out32 (s, STW | RS (data_reg) | RA (r0));
+        break;
+    case 3:
+        if (bswap) {
+            tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+            tcg_out32 (s, STWBRX | RS (data_reg) | RA (0) | RB (r0));
+            tcg_out32 (s, STWBRX | RS (data_reg2) | RA (0) | RB (r1));
+        }
+        else {
+            tcg_out32 (s, STW | RS (data_reg2) | RA (r0));
+            tcg_out32 (s, STW | RS (data_reg) | RA (r0) | 4);
+        }
+        break;
+    }
+
+#ifdef CONFIG_SOFTMMU
+    reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+void tcg_target_qemu_prologue (TCGContext *s)
+{
+    int i, frame_size;
+
+    frame_size = 0
+        + LINKAGE_AREA_SIZE
+        + TCG_STATIC_CALL_ARGS_SIZE
+        + ARRAY_SIZE (tcg_target_callee_save_regs) * 4
+        ;
+    frame_size = (frame_size + 15) & ~15;
+
+    tcg_out32 (s, MFSPR | RT (0) | LR);
+    tcg_out32 (s, STWU | RS (1) | RA (1) | (-frame_size & 0xffff));
+    for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+        tcg_out32 (s, (STW
+                       | RS (tcg_target_callee_save_regs[i])
+                       | RA (1)
+                       | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE)
+                       )
+            );
+    tcg_out32 (s, STW | RS (0) | RA (1) | (frame_size + BACK_CHAIN_OFFSET));
+
+    tcg_out32 (s, MTSPR | RS (3) | CTR);
+    tcg_out32 (s, BCCTR | BO_ALWAYS);
+    tb_ret_addr = s->code_ptr;
+
+    for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+        tcg_out32 (s, (LWZ
+                       | RT (tcg_target_callee_save_regs[i])
+                       | RA (1)
+                       | (i * 4 + LINKAGE_AREA_SIZE + TCG_STATIC_CALL_ARGS_SIZE)
+                       )
+            );
+    tcg_out32 (s, LWZ | RT (0) | RA (1) | (frame_size + BACK_CHAIN_OFFSET));
+    tcg_out32 (s, MTSPR | RS (0) | LR);
+    tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size);
+    tcg_out32 (s, BCLR | BO_ALWAYS);
+}
+
+static void tcg_out_ld (TCGContext *s, TCGType type, int ret, int arg1,
+                        tcg_target_long arg2)
+{
+    tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX);
+}
+
+static void tcg_out_st (TCGContext *s, TCGType type, int arg, int arg1,
+                        tcg_target_long arg2)
+{
+    tcg_out_ldst (s, arg, arg1, arg2, STW, STWX);
+}
+
+static void ppc_addi (TCGContext *s, int rt, int ra, tcg_target_long si)
+{
+    if (!si && rt == ra)
+        return;
+
+    if (si == (int16_t) si)
+        tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff));
+    else {
+        uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15);
+        tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h);
+        tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff));
+    }
+}
+
+static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    ppc_addi (s, reg, reg, val);
+}
+
+static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2,
+                         int const_arg2, int cr)
+{
+    int imm;
+    uint32_t op;
+
+    switch (cond) {
+    case TCG_COND_EQ:
+    case TCG_COND_NE:
+        if (const_arg2) {
+            if ((int16_t) arg2 == arg2) {
+                op = CMPI;
+                imm = 1;
+                break;
+            }
+            else if ((uint16_t) arg2 == arg2) {
+                op = CMPLI;
+                imm = 1;
+                break;
+            }
+        }
+        op = CMPL;
+        imm = 0;
+        break;
+
+    case TCG_COND_LT:
+    case TCG_COND_GE:
+    case TCG_COND_LE:
+    case TCG_COND_GT:
+        if (const_arg2) {
+            if ((int16_t) arg2 == arg2) {
+                op = CMPI;
+                imm = 1;
+                break;
+            }
+        }
+        op = CMP;
+        imm = 0;
+        break;
+
+    case TCG_COND_LTU:
+    case TCG_COND_GEU:
+    case TCG_COND_LEU:
+    case TCG_COND_GTU:
+        if (const_arg2) {
+            if ((uint16_t) arg2 == arg2) {
+                op = CMPLI;
+                imm = 1;
+                break;
+            }
+        }
+        op = CMPL;
+        imm = 0;
+        break;
+
+    default:
+        tcg_abort ();
+    }
+    op |= BF (cr);
+
+    if (imm)
+        tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff));
+    else {
+        if (const_arg2) {
+            tcg_out_movi (s, TCG_TYPE_I32, 0, arg2);
+            tcg_out32 (s, op | RA (arg1) | RB (0));
+        }
+        else
+            tcg_out32 (s, op | RA (arg1) | RB (arg2));
+    }
+
+}
+
+static void tcg_out_bc (TCGContext *s, int bc, int label_index)
+{
+    TCGLabel *l = &s->labels[label_index];
+
+    if (l->has_value)
+        tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value));
+    else {
+        uint16_t val = *(uint16_t *) &s->code_ptr[2];
+
+        /* Thanks to Andrzej Zaborowski */
+        tcg_out32 (s, bc | (val & 0xfffc));
+        tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0);
+    }
+}
+
+static void tcg_out_brcond (TCGContext *s, int cond,
+                            TCGArg arg1, TCGArg arg2, int const_arg2,
+                            int label_index)
+{
+    tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7);
+    tcg_out_bc (s, tcg_to_bc[cond], label_index);
+}
+
+/* XXX: we implement it at the target level to avoid having to
+   handle cross basic blocks temporaries */
+static void tcg_out_brcond2 (TCGContext *s, const TCGArg *args,
+                             const int *const_args)
+{
+    int cond = args[4], label_index = args[5], op;
+    struct { int bit1; int bit2; int cond2; } bits[] = {
+        [TCG_COND_LT ] = { CR_LT, CR_LT, TCG_COND_LT  },
+        [TCG_COND_LE ] = { CR_LT, CR_GT, TCG_COND_LT  },
+        [TCG_COND_GT ] = { CR_GT, CR_GT, TCG_COND_GT  },
+        [TCG_COND_GE ] = { CR_GT, CR_LT, TCG_COND_GT  },
+        [TCG_COND_LTU] = { CR_LT, CR_LT, TCG_COND_LTU },
+        [TCG_COND_LEU] = { CR_LT, CR_GT, TCG_COND_LTU },
+        [TCG_COND_GTU] = { CR_GT, CR_GT, TCG_COND_GTU },
+        [TCG_COND_GEU] = { CR_GT, CR_LT, TCG_COND_GTU },
+    }, *b = &bits[cond];
+
+    switch (cond) {
+    case TCG_COND_EQ:
+    case TCG_COND_NE:
+        op = (cond == TCG_COND_EQ) ? CRAND : CRNAND;
+        tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 6);
+        tcg_out_cmp (s, cond, args[1], args[3], const_args[3], 7);
+        tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, CR_EQ));
+        break;
+    case TCG_COND_LT:
+    case TCG_COND_LE:
+    case TCG_COND_GT:
+    case TCG_COND_GE:
+    case TCG_COND_LTU:
+    case TCG_COND_LEU:
+    case TCG_COND_GTU:
+    case TCG_COND_GEU:
+        op = (b->bit1 != b->bit2) ? CRANDC : CRAND;
+        tcg_out_cmp (s, b->cond2, args[1], args[3], const_args[3], 5);
+        tcg_out_cmp (s, TCG_COND_EQ, args[1], args[3], const_args[3], 6);
+        tcg_out_cmp (s, cond, args[0], args[2], const_args[2], 7);
+        tcg_out32 (s, op | BT (7, CR_EQ) | BA (6, CR_EQ) | BB (7, b->bit2));
+        tcg_out32 (s, CROR | BT (7, CR_EQ) | BA (5, b->bit1) | BB (7, CR_EQ));
+        break;
+    default:
+        tcg_abort();
+    }
+
+    tcg_out_bc (s, (BC | BI (7, CR_EQ) | BO_COND_TRUE), label_index);
+}
+
+void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr)
+{
+    uint32_t *ptr;
+    long disp = addr - jmp_addr;
+    unsigned long patch_size;
+
+    ptr = (uint32_t *)jmp_addr;
+
+    if ((disp << 6) >> 6 != disp) {
+        ptr[0] = 0x3c000000 | (addr >> 16);    /* lis 0,addr@ha */
+        ptr[1] = 0x60000000 | (addr & 0xffff); /* la  0,addr@l(0) */
+        ptr[2] = 0x7c0903a6;                   /* mtctr 0 */
+        ptr[3] = 0x4e800420;                   /* brctr */
+        patch_size = 16;
+    } else {
+        /* patch the branch destination */
+        if (disp != 16) {
+            *ptr = 0x48000000 | (disp & 0x03fffffc); /* b disp */
+            patch_size = 4;
+        } else {
+            ptr[0] = 0x60000000; /* nop */
+            ptr[1] = 0x60000000;
+            ptr[2] = 0x60000000;
+            ptr[3] = 0x60000000;
+            patch_size = 16;
+        }
+    }
+    /* flush icache */
+    flush_icache_range(jmp_addr, jmp_addr + patch_size);
+}
+
+static void tcg_out_op(TCGContext *s, int opc, const TCGArg *args,
+                       const int *const_args)
+{
+    switch (opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi (s, TCG_TYPE_I32, TCG_REG_R3, args[0]);
+        tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr);
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            s->code_ptr += 16;
+        }
+        else {
+            tcg_abort ();
+        }
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_br:
+        {
+            TCGLabel *l = &s->labels[args[0]];
+
+            if (l->has_value) {
+                tcg_out_b (s, 0, l->u.value);
+            }
+            else {
+                uint32_t val = *(uint32_t *) s->code_ptr;
+
+                /* Thanks to Andrzej Zaborowski */
+                tcg_out32 (s, B | (val & 0x3fffffc));
+                tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0);
+            }
+        }
+        break;
+    case INDEX_op_call:
+        if (const_args[0]) {
+            tcg_out_b (s, LK, args[0]);
+        }
+        else {
+            tcg_out32 (s, MTSPR | RS (args[0]) | LR);
+            tcg_out32 (s, BCLR | BO_ALWAYS | LK);
+        }
+        break;
+    case INDEX_op_jmp:
+        if (const_args[0]) {
+            tcg_out_b (s, 0, args[0]);
+        }
+        else {
+            tcg_out32 (s, MTSPR | RS (args[0]) | CTR);
+            tcg_out32 (s, BCCTR | BO_ALWAYS);
+        }
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi(s, TCG_TYPE_I32, args[0], args[1]);
+        break;
+    case INDEX_op_ld8u_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+        break;
+    case INDEX_op_ld8s_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+        tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0]));
+        break;
+    case INDEX_op_ld16u_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX);
+        break;
+    case INDEX_op_ld16s_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX);
+        break;
+    case INDEX_op_ld_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX);
+        break;
+    case INDEX_op_st8_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX);
+        break;
+    case INDEX_op_st16_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX);
+        break;
+    case INDEX_op_st_i32:
+        tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX);
+        break;
+
+    case INDEX_op_add_i32:
+        if (const_args[2])
+            ppc_addi (s, args[0], args[1], args[2]);
+        else
+            tcg_out32 (s, ADD | TAB (args[0], args[1], args[2]));
+        break;
+    case INDEX_op_sub_i32:
+        if (const_args[2])
+            ppc_addi (s, args[0], args[1], -args[2]);
+        else
+            tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1]));
+        break;
+
+    case INDEX_op_and_i32:
+        if (const_args[2]) {
+            if ((args[2] & 0xffff) == args[2])
+                tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | args[2]);
+            else if ((args[2] & 0xffff0000) == args[2])
+                tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0])
+                           | ((args[2] >> 16) & 0xffff));
+            else {
+                tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+                tcg_out32 (s, AND | SAB (args[1], args[0], 0));
+            }
+        }
+        else
+            tcg_out32 (s, AND | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_or_i32:
+        if (const_args[2]) {
+            if (args[2] & 0xffff) {
+                tcg_out32 (s, ORI | RS (args[1])  | RA (args[0])
+                           | (args[2] & 0xffff));
+                if (args[2] >> 16)
+                    tcg_out32 (s, ORIS | RS (args[0])  | RA (args[0])
+                               | ((args[2] >> 16) & 0xffff));
+            }
+            else {
+                tcg_out32 (s, ORIS | RS (args[1])  | RA (args[0])
+                           | ((args[2] >> 16) & 0xffff));
+            }
+        }
+        else
+            tcg_out32 (s, OR | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_xor_i32:
+        if (const_args[2]) {
+            if ((args[2] & 0xffff) == args[2])
+                tcg_out32 (s, XORI | RS (args[1])  | RA (args[0])
+                           | (args[2] & 0xffff));
+            else if ((args[2] & 0xffff0000) == args[2])
+                tcg_out32 (s, XORIS | RS (args[1])  | RA (args[0])
+                           | ((args[2] >> 16) & 0xffff));
+            else {
+                tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+                tcg_out32 (s, XOR | SAB (args[1], args[0], 0));
+            }
+        }
+        else
+            tcg_out32 (s, XOR | SAB (args[1], args[0], args[2]));
+        break;
+
+    case INDEX_op_mul_i32:
+        if (const_args[2]) {
+            if (args[2] == (int16_t) args[2])
+                tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1])
+                           | (args[2] & 0xffff));
+            else {
+                tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+                tcg_out32 (s, MULLW | TAB (args[0], args[1], 0));
+            }
+        }
+        else
+            tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2]));
+        break;
+
+    case INDEX_op_div_i32:
+        tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2]));
+        break;
+
+    case INDEX_op_divu_i32:
+        tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2]));
+        break;
+
+    case INDEX_op_rem_i32:
+        tcg_out32 (s, DIVW | TAB (0, args[1], args[2]));
+        tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+        tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+        break;
+
+    case INDEX_op_remu_i32:
+        tcg_out32 (s, DIVWU | TAB (0, args[1], args[2]));
+        tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+        tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+        break;
+
+    case INDEX_op_mulu2_i32:
+        if (args[0] == args[2] || args[0] == args[3]) {
+            tcg_out32 (s, MULLW | TAB (0, args[2], args[3]));
+            tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3]));
+            tcg_out_mov (s, args[0], 0);
+        }
+        else {
+            tcg_out32 (s, MULLW | TAB (args[0], args[2], args[3]));
+            tcg_out32 (s, MULHWU | TAB (args[1], args[2], args[3]));
+        }
+        break;
+
+    case INDEX_op_shl_i32:
+        if (const_args[2]) {
+            tcg_out32 (s, (RLWINM
+                           | RA (args[0])
+                           | RS (args[1])
+                           | SH (args[2])
+                           | MB (0)
+                           | ME (31 - args[2])
+                           )
+                );
+        }
+        else
+            tcg_out32 (s, SLW | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_shr_i32:
+        if (const_args[2]) {
+            tcg_out32 (s, (RLWINM
+                           | RA (args[0])
+                           | RS (args[1])
+                           | SH (32 - args[2])
+                           | MB (args[2])
+                           | ME (31)
+                           )
+                );
+        }
+        else
+            tcg_out32 (s, SRW | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_sar_i32:
+        if (const_args[2])
+            tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2]));
+        else
+            tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2]));
+        break;
+
+    case INDEX_op_add2_i32:
+        if (args[0] == args[3] || args[0] == args[5]) {
+            tcg_out32 (s, ADDC | TAB (0, args[2], args[4]));
+            tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5]));
+            tcg_out_mov (s, args[0], 0);
+        }
+        else {
+            tcg_out32 (s, ADDC | TAB (args[0], args[2], args[4]));
+            tcg_out32 (s, ADDE | TAB (args[1], args[3], args[5]));
+        }
+        break;
+    case INDEX_op_sub2_i32:
+        if (args[0] == args[3] || args[0] == args[5]) {
+            tcg_out32 (s, SUBFC | TAB (0, args[4], args[2]));
+            tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3]));
+            tcg_out_mov (s, args[0], 0);
+        }
+        else {
+            tcg_out32 (s, SUBFC | TAB (args[0], args[4], args[2]));
+            tcg_out32 (s, SUBFE | TAB (args[1], args[5], args[3]));
+        }
+        break;
+
+    case INDEX_op_brcond_i32:
+        /*
+          args[0] = r0
+          args[1] = r1
+          args[2] = cond
+          args[3] = r1 is const
+          args[4] = label_index
+        */
+        tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3]);
+        break;
+    case INDEX_op_brcond2_i32:
+        tcg_out_brcond2(s, args, const_args);
+        break;
+
+    case INDEX_op_neg_i32:
+        tcg_out32 (s, NEG | RT (args[0]) | RA (args[1]));
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, args, 2);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld(s, args, 3);
+        break;
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, args, 2);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st(s, args, 3);
+        break;
+
+    case INDEX_op_ext8s_i32:
+        tcg_out32 (s, EXTSB | RS (args[1]) | RA (args[0]));
+        break;
+    case INDEX_op_ext16s_i32:
+        tcg_out32 (s, EXTSH | RS (args[1]) | RA (args[0]));
+        break;
+
+    default:
+        tcg_dump_ops (s, stderr);
+        tcg_abort ();
+    }
+}
+
+static const TCGTargetOpDef ppc_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "ri" } },
+    { INDEX_op_jmp, { "ri" } },
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "r", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+
+    { INDEX_op_add_i32, { "r", "r", "ri" } },
+    { INDEX_op_mul_i32, { "r", "r", "ri" } },
+    { INDEX_op_div_i32, { "r", "r", "r" } },
+    { INDEX_op_divu_i32, { "r", "r", "r" } },
+    { INDEX_op_rem_i32, { "r", "r", "r" } },
+    { INDEX_op_remu_i32, { "r", "r", "r" } },
+    { INDEX_op_mulu2_i32, { "r", "r", "r", "r" } },
+    { INDEX_op_sub_i32, { "r", "r", "ri" } },
+    { INDEX_op_and_i32, { "r", "r", "ri" } },
+    { INDEX_op_or_i32, { "r", "r", "ri" } },
+    { INDEX_op_xor_i32, { "r", "r", "ri" } },
+
+    { INDEX_op_shl_i32, { "r", "r", "ri" } },
+    { INDEX_op_shr_i32, { "r", "r", "ri" } },
+    { INDEX_op_sar_i32, { "r", "r", "ri" } },
+
+    { INDEX_op_brcond_i32, { "r", "ri" } },
+
+    { INDEX_op_add2_i32, { "r", "r", "r", "r", "r", "r" } },
+    { INDEX_op_sub2_i32, { "r", "r", "r", "r", "r", "r" } },
+    { INDEX_op_brcond2_i32, { "r", "r", "r", "r" } },
+
+    { INDEX_op_neg_i32, { "r", "r" } },
+
+#if TARGET_LONG_BITS == 32
+    { INDEX_op_qemu_ld8u, { "r", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L" } },
+    { INDEX_op_qemu_ld32s, { "r", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "r", "L" } },
+
+    { INDEX_op_qemu_st8, { "K", "K" } },
+    { INDEX_op_qemu_st16, { "K", "K" } },
+    { INDEX_op_qemu_st32, { "K", "K" } },
+    { INDEX_op_qemu_st64, { "M", "M", "M" } },
+#else
+    { INDEX_op_qemu_ld8u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld32s, { "r", "L", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "L", "L", "L" } },
+
+    { INDEX_op_qemu_st8, { "K", "K", "K" } },
+    { INDEX_op_qemu_st16, { "K", "K", "K" } },
+    { INDEX_op_qemu_st32, { "K", "K", "K" } },
+    { INDEX_op_qemu_st64, { "M", "M", "M", "M" } },
+#endif
+
+    { INDEX_op_ext8s_i32, { "r", "r" } },
+    { INDEX_op_ext16s_i32, { "r", "r" } },
+
+    { -1 },
+};
+
+void tcg_target_init(TCGContext *s)
+{
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_R0) |
+#ifdef __APPLE__
+                     (1 << TCG_REG_R2) |
+#endif
+                     (1 << TCG_REG_R3) |
+                     (1 << TCG_REG_R4) |
+                     (1 << TCG_REG_R5) |
+                     (1 << TCG_REG_R6) |
+                     (1 << TCG_REG_R7) |
+                     (1 << TCG_REG_R8) |
+                     (1 << TCG_REG_R9) |
+                     (1 << TCG_REG_R10) |
+                     (1 << TCG_REG_R11) |
+                     (1 << TCG_REG_R12)
+        );
+
+    tcg_regset_clear(s->reserved_regs);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R0);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R1);
+#ifndef __APPLE__
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_R2);
+#endif
+
+    tcg_add_target_add_op_defs(ppc_op_defs);
+}
diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h
new file mode 100644
index 0000000..d46c19d
--- /dev/null
+++ b/tcg/ppc/tcg-target.h
@@ -0,0 +1,105 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_PPC 1
+
+#define TCG_TARGET_REG_BITS 32
+#define TCG_TARGET_WORDS_BIGENDIAN
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+    TCG_REG_R0 = 0,
+    TCG_REG_R1,
+    TCG_REG_R2,
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R24,
+    TCG_REG_R25,
+    TCG_REG_R26,
+    TCG_REG_R27,
+    TCG_REG_R28,
+    TCG_REG_R29,
+    TCG_REG_R30,
+    TCG_REG_R31
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R1
+#define TCG_TARGET_STACK_ALIGN 16
+#ifdef __APPLE__
+#define TCG_TARGET_CALL_STACK_OFFSET 24
+#else
+#define TCG_TARGET_CALL_ALIGN_ARGS 1
+#define TCG_TARGET_CALL_STACK_OFFSET 8
+#endif
+
+/* optional instructions */
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+
+#define TCG_AREG0 TCG_REG_R27
+#define TCG_AREG1 TCG_REG_R24
+#define TCG_AREG2 TCG_REG_R25
+#define TCG_AREG3 TCG_REG_R26
+
+/* taken directly from tcg-dyngen.c */
+#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    unsigned long p;
+
+    start &= ~(MIN_CACHE_LINE_SIZE - 1);
+    stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
+
+    for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+        asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
+    }
+    asm volatile ("sync" : : : "memory");
+    for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+        asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
+    }
+    asm volatile ("sync" : : : "memory");
+    asm volatile ("isync" : : : "memory");
+}
diff --git a/tcg/ppc64/tcg-target.c b/tcg/ppc64/tcg-target.c
new file mode 100644
index 0000000..6b16efa
--- /dev/null
+++ b/tcg/ppc64/tcg-target.c
@@ -0,0 +1,1491 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#define TCG_CT_CONST_U32 0x100
+
+static uint8_t *tb_ret_addr;
+
+#define FAST_PATH
+
+#if TARGET_PHYS_ADDR_BITS == 32
+#define LD_ADDEND LWZ
+#else
+#define LD_ADDEND LD
+#endif
+
+#if TARGET_LONG_BITS == 32
+#define LD_ADDR LWZU
+#define CMP_L 0
+#else
+#define LD_ADDR LDU
+#define CMP_L (1<<21)
+#endif
+
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "r0",
+    "r1",
+    "rp",
+    "r3",
+    "r4",
+    "r5",
+    "r6",
+    "r7",
+    "r8",
+    "r9",
+    "r10",
+    "r11",
+    "r12",
+    "r13",
+    "r14",
+    "r15",
+    "r16",
+    "r17",
+    "r18",
+    "r19",
+    "r20",
+    "r21",
+    "r22",
+    "r23",
+    "r24",
+    "r25",
+    "r26",
+    "r27",
+    "r28",
+    "r29",
+    "r30",
+    "r31"
+};
+
+static const int tcg_target_reg_alloc_order[] = {
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R28,
+    TCG_REG_R29,
+    TCG_REG_R30,
+    TCG_REG_R31,
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R0,
+    TCG_REG_R1,
+    TCG_REG_R2,
+    TCG_REG_R24,
+    TCG_REG_R25,
+    TCG_REG_R26,
+    TCG_REG_R27
+};
+
+static const int tcg_target_call_iarg_regs[] = {
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+    TCG_REG_R3
+};
+
+static const int tcg_target_callee_save_regs[] = {
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R28,
+    TCG_REG_R29,
+    TCG_REG_R30,
+    TCG_REG_R31
+};
+
+static uint32_t reloc_pc24_val (void *pc, tcg_target_long target)
+{
+    tcg_target_long disp;
+
+    disp = target - (tcg_target_long) pc;
+    if ((disp << 38) >> 38 != disp)
+        tcg_abort ();
+
+    return disp & 0x3fffffc;
+}
+
+static void reloc_pc24 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0x3fffffc)
+        | reloc_pc24_val (pc, target);
+}
+
+static uint16_t reloc_pc14_val (void *pc, tcg_target_long target)
+{
+    tcg_target_long disp;
+
+    disp = target - (tcg_target_long) pc;
+    if (disp != (int16_t) disp)
+        tcg_abort ();
+
+    return disp & 0xfffc;
+}
+
+static void reloc_pc14 (void *pc, tcg_target_long target)
+{
+    *(uint32_t *) pc = (*(uint32_t *) pc & ~0xfffc)
+        | reloc_pc14_val (pc, target);
+}
+
+static void patch_reloc (uint8_t *code_ptr, int type,
+                         tcg_target_long value, tcg_target_long addend)
+{
+    value += addend;
+    switch (type) {
+    case R_PPC_REL14:
+        reloc_pc14 (code_ptr, value);
+        break;
+    case R_PPC_REL24:
+        reloc_pc24 (code_ptr, value);
+        break;
+    default:
+        tcg_abort ();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static int tcg_target_get_call_iarg_regs_count (int flags)
+{
+    return sizeof (tcg_target_call_iarg_regs) / sizeof (tcg_target_call_iarg_regs[0]);
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint (TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch (ct_str[0]) {
+    case 'A': case 'B': case 'C': case 'D':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg (ct->u.regs, 3 + ct_str[0] - 'A');
+        break;
+    case 'r':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32 (ct->u.regs, 0, 0xffffffff);
+        break;
+    case 'L':                   /* qemu_ld constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32 (ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3);
+#ifdef CONFIG_SOFTMMU
+        tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4);
+#endif
+        break;
+    case 'S':                   /* qemu_st constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32 (ct->u.regs, 0, 0xffffffff);
+        tcg_regset_reset_reg (ct->u.regs, TCG_REG_R3);
+#ifdef CONFIG_SOFTMMU
+        tcg_regset_reset_reg (ct->u.regs, TCG_REG_R4);
+        tcg_regset_reset_reg (ct->u.regs, TCG_REG_R5);
+#endif
+        break;
+    case 'Z':
+        ct->ct |= TCG_CT_CONST_U32;
+        break;
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static int tcg_target_const_match (tcg_target_long val,
+                                   const TCGArgConstraint *arg_ct)
+{
+    int ct;
+
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    else if ((ct & TCG_CT_CONST_U32) && (val == (uint32_t) val))
+        return 1;
+    return 0;
+}
+
+#define OPCD(opc) ((opc)<<26)
+#define XO19(opc) (OPCD(19)|((opc)<<1))
+#define XO30(opc) (OPCD(30)|((opc)<<2))
+#define XO31(opc) (OPCD(31)|((opc)<<1))
+#define XO58(opc) (OPCD(58)|(opc))
+#define XO62(opc) (OPCD(62)|(opc))
+
+#define B      OPCD( 18)
+#define BC     OPCD( 16)
+#define LBZ    OPCD( 34)
+#define LHZ    OPCD( 40)
+#define LHA    OPCD( 42)
+#define LWZ    OPCD( 32)
+#define STB    OPCD( 38)
+#define STH    OPCD( 44)
+#define STW    OPCD( 36)
+
+#define STD    XO62(  0)
+#define STDU   XO62(  1)
+#define STDX   XO31(149)
+
+#define LD     XO58(  0)
+#define LDX    XO31( 21)
+#define LDU    XO58(  1)
+#define LWA    XO58(  2)
+#define LWAX   XO31(341)
+
+#define ADDI   OPCD( 14)
+#define ADDIS  OPCD( 15)
+#define ORI    OPCD( 24)
+#define ORIS   OPCD( 25)
+#define XORI   OPCD( 26)
+#define XORIS  OPCD( 27)
+#define ANDI   OPCD( 28)
+#define ANDIS  OPCD( 29)
+#define MULLI  OPCD(  7)
+#define CMPLI  OPCD( 10)
+#define CMPI   OPCD( 11)
+
+#define LWZU   OPCD( 33)
+#define STWU   OPCD( 37)
+
+#define RLWINM OPCD( 21)
+
+#define RLDICL XO30(  0)
+#define RLDICR XO30(  1)
+
+#define BCLR   XO19( 16)
+#define BCCTR  XO19(528)
+#define CRAND  XO19(257)
+#define CRANDC XO19(129)
+#define CRNAND XO19(225)
+#define CROR   XO19(449)
+
+#define EXTSB  XO31(954)
+#define EXTSH  XO31(922)
+#define EXTSW  XO31(986)
+#define ADD    XO31(266)
+#define ADDE   XO31(138)
+#define ADDC   XO31( 10)
+#define AND    XO31( 28)
+#define SUBF   XO31( 40)
+#define SUBFC  XO31(  8)
+#define SUBFE  XO31(136)
+#define OR     XO31(444)
+#define XOR    XO31(316)
+#define MULLW  XO31(235)
+#define MULHWU XO31( 11)
+#define DIVW   XO31(491)
+#define DIVWU  XO31(459)
+#define CMP    XO31(  0)
+#define CMPL   XO31( 32)
+#define LHBRX  XO31(790)
+#define LWBRX  XO31(534)
+#define STHBRX XO31(918)
+#define STWBRX XO31(662)
+#define MFSPR  XO31(339)
+#define MTSPR  XO31(467)
+#define SRAWI  XO31(824)
+#define NEG    XO31(104)
+
+#define MULLD  XO31(233)
+#define MULHD  XO31( 73)
+#define MULHDU XO31(  9)
+#define DIVD   XO31(489)
+#define DIVDU  XO31(457)
+
+#define LBZX   XO31( 87)
+#define LHZX   XO31(276)
+#define LHAX   XO31(343)
+#define LWZX   XO31( 23)
+#define STBX   XO31(215)
+#define STHX   XO31(407)
+#define STWX   XO31(151)
+
+#define SPR(a,b) ((((a)<<5)|(b))<<11)
+#define LR     SPR(8, 0)
+#define CTR    SPR(9, 0)
+
+#define SLW    XO31( 24)
+#define SRW    XO31(536)
+#define SRAW   XO31(792)
+
+#define SLD    XO31( 27)
+#define SRD    XO31(539)
+#define SRAD   XO31(794)
+#define SRADI  XO31(413<<1)
+
+#define LMW    OPCD( 46)
+#define STMW   OPCD( 47)
+
+#define TW     XO31( 4)
+#define TRAP   (TW | TO (31))
+
+#define RT(r) ((r)<<21)
+#define RS(r) ((r)<<21)
+#define RA(r) ((r)<<16)
+#define RB(r) ((r)<<11)
+#define TO(t) ((t)<<21)
+#define SH(s) ((s)<<11)
+#define MB(b) ((b)<<6)
+#define ME(e) ((e)<<1)
+#define BO(o) ((o)<<21)
+#define MB64(b) ((b)<<5)
+
+#define LK    1
+
+#define TAB(t,a,b) (RT(t) | RA(a) | RB(b))
+#define SAB(s,a,b) (RS(s) | RA(a) | RB(b))
+
+#define BF(n)    ((n)<<23)
+#define BI(n, c) (((c)+((n)*4))<<16)
+#define BT(n, c) (((c)+((n)*4))<<21)
+#define BA(n, c) (((c)+((n)*4))<<16)
+#define BB(n, c) (((c)+((n)*4))<<11)
+
+#define BO_COND_TRUE  BO (12)
+#define BO_COND_FALSE BO ( 4)
+#define BO_ALWAYS     BO (20)
+
+enum {
+    CR_LT,
+    CR_GT,
+    CR_EQ,
+    CR_SO
+};
+
+static const uint32_t tcg_to_bc[10] = {
+    [TCG_COND_EQ]  = BC | BI (7, CR_EQ) | BO_COND_TRUE,
+    [TCG_COND_NE]  = BC | BI (7, CR_EQ) | BO_COND_FALSE,
+    [TCG_COND_LT]  = BC | BI (7, CR_LT) | BO_COND_TRUE,
+    [TCG_COND_GE]  = BC | BI (7, CR_LT) | BO_COND_FALSE,
+    [TCG_COND_LE]  = BC | BI (7, CR_GT) | BO_COND_FALSE,
+    [TCG_COND_GT]  = BC | BI (7, CR_GT) | BO_COND_TRUE,
+    [TCG_COND_LTU] = BC | BI (7, CR_LT) | BO_COND_TRUE,
+    [TCG_COND_GEU] = BC | BI (7, CR_LT) | BO_COND_FALSE,
+    [TCG_COND_LEU] = BC | BI (7, CR_GT) | BO_COND_FALSE,
+    [TCG_COND_GTU] = BC | BI (7, CR_GT) | BO_COND_TRUE,
+};
+
+static void tcg_out_mov (TCGContext *s, int ret, int arg)
+{
+    tcg_out32 (s, OR | SAB (arg, ret, arg));
+}
+
+static void tcg_out_rld (TCGContext *s, int op, int ra, int rs, int sh, int mb)
+{
+    sh = SH (sh & 0x1f) | (((sh >> 5) & 1) << 1);
+    mb = MB64 ((mb >> 5) | ((mb << 1) & 0x3f));
+    tcg_out32 (s, op | RA (ra) | RS (rs) | sh | mb);
+}
+
+static void tcg_out_movi32 (TCGContext *s, int ret, int32_t arg)
+{
+    if (arg == (int16_t) arg)
+        tcg_out32 (s, ADDI | RT (ret) | RA (0) | (arg & 0xffff));
+    else {
+        tcg_out32 (s, ADDIS | RT (ret) | RA (0) | ((arg >> 16) & 0xffff));
+        if (arg & 0xffff)
+            tcg_out32 (s, ORI | RS (ret) | RA (ret) | (arg & 0xffff));
+    }
+}
+
+static void tcg_out_movi (TCGContext *s, TCGType type,
+                          int ret, tcg_target_long arg)
+{
+    int32_t arg32 = arg;
+
+    if (type == TCG_TYPE_I32 || arg == arg32) {
+        tcg_out_movi32 (s, ret, arg32);
+    }
+    else {
+        if ((uint64_t) arg >> 32) {
+            uint16_t h16 = arg >> 16;
+            uint16_t l16 = arg;
+
+            tcg_out_movi32 (s, ret, arg >> 32);
+            tcg_out_rld (s, RLDICR, ret, ret, 32, 31);
+            if (h16) tcg_out32 (s, ORIS | RS (ret) | RA (ret) | h16);
+            if (l16) tcg_out32 (s, ORI | RS (ret) | RA (ret) | l16);
+        }
+        else {
+            tcg_out_movi32 (s, ret, arg32);
+            if (arg32 < 0)
+                tcg_out_rld (s, RLDICL, ret, ret, 0, 32);
+        }
+    }
+}
+
+static void tcg_out_call (TCGContext *s, tcg_target_long arg, int const_arg)
+{
+    int reg;
+
+    if (const_arg) {
+        reg = 2;
+        tcg_out_movi (s, TCG_TYPE_I64, reg, arg);
+    }
+    else reg = arg;
+
+    tcg_out32 (s, LD | RT (0) | RA (reg));
+    tcg_out32 (s, MTSPR | RA (0) | CTR);
+    tcg_out32 (s, LD | RT (11) | RA (reg) | 16);
+    tcg_out32 (s, LD | RT (2) | RA (reg) | 8);
+    tcg_out32 (s, BCCTR | BO_ALWAYS | LK);
+}
+
+static void tcg_out_ldst (TCGContext *s, int ret, int addr,
+                          int offset, int op1, int op2)
+{
+    if (offset == (int16_t) offset)
+        tcg_out32 (s, op1 | RT (ret) | RA (addr) | (offset & 0xffff));
+    else {
+        tcg_out_movi (s, TCG_TYPE_I64, 0, offset);
+        tcg_out32 (s, op2 | RT (ret) | RA (addr) | RB (0));
+    }
+}
+
+static void tcg_out_b (TCGContext *s, int mask, tcg_target_long target)
+{
+    tcg_target_long disp;
+
+    disp = target - (tcg_target_long) s->code_ptr;
+    if ((disp << 38) >> 38 == disp)
+        tcg_out32 (s, B | (disp & 0x3fffffc) | mask);
+    else {
+        tcg_out_movi (s, TCG_TYPE_I64, 0, (tcg_target_long) target);
+        tcg_out32 (s, MTSPR | RS (0) | CTR);
+        tcg_out32 (s, BCCTR | BO_ALWAYS | mask);
+    }
+}
+
+#if defined (CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+
+static void tcg_out_tlb_read (TCGContext *s, int r0, int r1, int r2,
+                              int addr_reg, int s_bits, int offset)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32);
+
+    tcg_out32 (s, (RLWINM
+                   | RA (r0)
+                   | RS (addr_reg)
+                   | SH (32 - (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS))
+                   | MB (32 - (CPU_TLB_BITS + CPU_TLB_ENTRY_BITS))
+                   | ME (31 - CPU_TLB_ENTRY_BITS)
+                   )
+        );
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (TCG_AREG0));
+    tcg_out32 (s, (LWZU | RT (r1) | RA (r0) | offset));
+    tcg_out32 (s, (RLWINM
+                   | RA (r2)
+                   | RS (addr_reg)
+                   | SH (0)
+                   | MB ((32 - s_bits) & 31)
+                   | ME (31 - TARGET_PAGE_BITS)
+                   )
+        );
+#else
+    tcg_out_rld (s, RLDICL, r0, addr_reg,
+                 64 - TARGET_PAGE_BITS,
+                 64 - CPU_TLB_BITS);
+    tcg_out_rld (s, RLDICR, r0, r0,
+                 CPU_TLB_ENTRY_BITS,
+                 63 - CPU_TLB_ENTRY_BITS);
+
+    tcg_out32 (s, ADD | TAB (r0, r0, TCG_AREG0));
+    tcg_out32 (s, LD_ADDR | RT (r1) | RA (r0) | offset);
+
+    if (!s_bits) {
+        tcg_out_rld (s, RLDICR, r2, addr_reg, 0, 63 - TARGET_PAGE_BITS);
+    }
+    else {
+        tcg_out_rld (s, RLDICL, r2, addr_reg,
+                     64 - TARGET_PAGE_BITS,
+                     TARGET_PAGE_BITS - s_bits);
+        tcg_out_rld (s, RLDICL, r2, r2, TARGET_PAGE_BITS, 0);
+    }
+#endif
+}
+#endif
+
+static void tcg_out_qemu_ld (TCGContext *s, const TCGArg *args, int opc)
+{
+    int addr_reg, data_reg, r0, r1, mem_index, s_bits, bswap;
+#ifdef CONFIG_SOFTMMU
+    int r2;
+    void *label1_ptr, *label2_ptr;
+#endif
+
+    data_reg = *args++;
+    addr_reg = *args++;
+    mem_index = *args;
+    s_bits = opc & 3;
+
+#ifdef CONFIG_SOFTMMU
+    r0 = 3;
+    r1 = 4;
+    r2 = 0;
+
+    tcg_out_tlb_read (s, r0, r1, r2, addr_reg, s_bits,
+                      offsetof (CPUState, tlb_table[mem_index][0].addr_read));
+
+    tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L);
+
+    label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+    tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+    /* slow path */
+    tcg_out_mov (s, 3, addr_reg);
+    tcg_out_movi (s, TCG_TYPE_I64, 4, mem_index);
+
+    tcg_out_call (s, (tcg_target_long) qemu_ld_helpers[s_bits], 1);
+
+    switch (opc) {
+    case 0|4:
+        tcg_out32 (s, EXTSB | RA (data_reg) | RS (3));
+        break;
+    case 1|4:
+        tcg_out32 (s, EXTSH | RA (data_reg) | RS (3));
+        break;
+    case 2|4:
+        tcg_out32 (s, EXTSW | RA (data_reg) | RS (3));
+        break;
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+        if (data_reg != 3)
+            tcg_out_mov (s, data_reg, 3);
+        break;
+    }
+    label2_ptr = s->code_ptr;
+    tcg_out32 (s, B);
+
+    /* label1: fast path */
+#ifdef FAST_PATH
+    reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+    /* r0 now contains &env->tlb_table[mem_index][index].addr_read */
+    tcg_out32 (s, (LD_ADDEND
+                   | RT (r0)
+                   | RA (r0)
+                   | (offsetof (CPUTLBEntry, addend)
+                      - offsetof (CPUTLBEntry, addr_read))
+                   ));
+    /* r0 = env->tlb_table[mem_index][index].addend */
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+    /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else  /* !CONFIG_SOFTMMU */
+#if TARGET_LONG_BITS == 32
+    tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32);
+#endif
+    r0 = addr_reg;
+    r1 = 3;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 0;
+#else
+    bswap = 1;
+#endif
+    switch (opc) {
+    default:
+    case 0:
+        tcg_out32 (s, LBZ | RT (data_reg) | RA (r0));
+        break;
+    case 0|4:
+        tcg_out32 (s, LBZ | RT (data_reg) | RA (r0));
+        tcg_out32 (s, EXTSB | RA (data_reg) | RS (data_reg));
+        break;
+    case 1:
+        if (bswap) tcg_out32 (s, LHBRX | RT (data_reg) | RB (r0));
+        else tcg_out32 (s, LHZ | RT (data_reg) | RA (r0));
+        break;
+    case 1|4:
+        if (bswap) {
+            tcg_out32 (s, LHBRX | RT (data_reg) | RB (r0));
+            tcg_out32 (s, EXTSH | RA (data_reg) | RS (data_reg));
+        }
+        else tcg_out32 (s, LHA | RT (data_reg) | RA (r0));
+        break;
+    case 2:
+        if (bswap) tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0));
+        else tcg_out32 (s, LWZ | RT (data_reg)| RA (r0));
+        break;
+    case 2|4:
+        if (bswap) {
+            tcg_out32 (s, LWBRX | RT (data_reg) | RB (r0));
+            tcg_out32 (s, EXTSW | RA (data_reg) | RS (data_reg));
+        }
+        else tcg_out32 (s, LWA | RT (data_reg)| RA (r0));
+        break;
+    case 3:
+        if (bswap) {
+            tcg_out32 (s, LWBRX | RT (0) | RB (r0));
+            tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+            tcg_out32 (s, LWBRX | RT (data_reg) | RB (r1));
+            tcg_out_rld (s, RLDICR, data_reg, data_reg, 32, 31);
+            tcg_out32 (s, OR | SAB (0, data_reg, data_reg));
+        }
+        else tcg_out32 (s, LD | RT (data_reg) | RA (r0));
+        break;
+    }
+
+#ifdef CONFIG_SOFTMMU
+    reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+static void tcg_out_qemu_st (TCGContext *s, const TCGArg *args, int opc)
+{
+    int addr_reg, r0, r1, data_reg, mem_index, bswap;
+#ifdef CONFIG_SOFTMMU
+    int r2;
+    void *label1_ptr, *label2_ptr;
+#endif
+
+    data_reg = *args++;
+    addr_reg = *args++;
+    mem_index = *args;
+
+#ifdef CONFIG_SOFTMMU
+    r0 = 3;
+    r1 = 4;
+    r2 = 0;
+
+    tcg_out_tlb_read (s, r0, r1, r2, addr_reg, opc,
+                      offsetof (CPUState, tlb_table[mem_index][0].addr_write));
+
+    tcg_out32 (s, CMP | BF (7) | RA (r2) | RB (r1) | CMP_L);
+
+    label1_ptr = s->code_ptr;
+#ifdef FAST_PATH
+    tcg_out32 (s, BC | BI (7, CR_EQ) | BO_COND_TRUE);
+#endif
+
+    /* slow path */
+    tcg_out_mov (s, 3, addr_reg);
+    tcg_out_rld (s, RLDICL, 4, data_reg, 0, 64 - (1 << (3 + opc)));
+    tcg_out_movi (s, TCG_TYPE_I64, 5, mem_index);
+
+    tcg_out_call (s, (tcg_target_long) qemu_st_helpers[opc], 1);
+
+    label2_ptr = s->code_ptr;
+    tcg_out32 (s, B);
+
+    /* label1: fast path */
+#ifdef FAST_PATH
+    reloc_pc14 (label1_ptr, (tcg_target_long) s->code_ptr);
+#endif
+
+    tcg_out32 (s, (LD_ADDEND
+                   | RT (r0)
+                   | RA (r0)
+                   | (offsetof (CPUTLBEntry, addend)
+                      - offsetof (CPUTLBEntry, addr_write))
+                   ));
+    /* r0 = env->tlb_table[mem_index][index].addend */
+    tcg_out32 (s, ADD | RT (r0) | RA (r0) | RB (addr_reg));
+    /* r0 = env->tlb_table[mem_index][index].addend + addr */
+
+#else  /* !CONFIG_SOFTMMU */
+#if TARGET_LONG_BITS == 32
+    tcg_out_rld (s, RLDICL, addr_reg, addr_reg, 0, 32);
+#endif
+    r1 = 3;
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 0;
+#else
+    bswap = 1;
+#endif
+    switch (opc) {
+    case 0:
+        tcg_out32 (s, STB | RS (data_reg) | RA (r0));
+        break;
+    case 1:
+        if (bswap) tcg_out32 (s, STHBRX | RS (data_reg) | RA (0) | RB (r0));
+        else tcg_out32 (s, STH | RS (data_reg) | RA (r0));
+        break;
+    case 2:
+        if (bswap) tcg_out32 (s, STWBRX | RS (data_reg) | RA (0) | RB (r0));
+        else tcg_out32 (s, STW | RS (data_reg) | RA (r0));
+        break;
+    case 3:
+        if (bswap) {
+            tcg_out32 (s, STWBRX | RS (data_reg) | RA (0) | RB (r0));
+            tcg_out32 (s, ADDI | RT (r1) | RA (r0) | 4);
+            tcg_out_rld (s, RLDICL, 0, data_reg, 32, 0);
+            tcg_out32 (s, STWBRX | RS (0) | RA (0) | RB (r1));
+        }
+        else tcg_out32 (s, STD | RS (data_reg) | RA (r0));
+        break;
+    }
+
+#ifdef CONFIG_SOFTMMU
+    reloc_pc24 (label2_ptr, (tcg_target_long) s->code_ptr);
+#endif
+}
+
+void tcg_target_qemu_prologue (TCGContext *s)
+{
+    int i, frame_size;
+    uint64_t addr;
+
+    frame_size = 0
+        + 8                     /* back chain */
+        + 8                     /* CR */
+        + 8                     /* LR */
+        + 8                     /* compiler doubleword */
+        + 8                     /* link editor doubleword */
+        + 8                     /* TOC save area */
+        + TCG_STATIC_CALL_ARGS_SIZE
+        + ARRAY_SIZE (tcg_target_callee_save_regs) * 8
+        ;
+    frame_size = (frame_size + 15) & ~15;
+
+    /* First emit adhoc function descriptor */
+    addr = (uint64_t) s->code_ptr + 24;
+    tcg_out32 (s, addr >> 32); tcg_out32 (s, addr); /* entry point */
+    s->code_ptr += 16;          /* skip TOC and environment pointer */
+
+    /* Prologue */
+    tcg_out32 (s, MFSPR | RT (0) | LR);
+    tcg_out32 (s, STDU | RS (1) | RA (1) | (-frame_size & 0xffff));
+    for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+        tcg_out32 (s, (STD
+                       | RS (tcg_target_callee_save_regs[i])
+                       | RA (1)
+                       | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE)
+                       )
+            );
+    tcg_out32 (s, STD | RS (0) | RA (1) | (frame_size + 16));
+
+    tcg_out32 (s, MTSPR | RS (3) | CTR);
+    tcg_out32 (s, BCCTR | BO_ALWAYS);
+
+    /* Epilogue */
+    tb_ret_addr = s->code_ptr;
+
+    for (i = 0; i < ARRAY_SIZE (tcg_target_callee_save_regs); ++i)
+        tcg_out32 (s, (LD
+                       | RT (tcg_target_callee_save_regs[i])
+                       | RA (1)
+                       | (i * 8 + 48 + TCG_STATIC_CALL_ARGS_SIZE)
+                       )
+            );
+    tcg_out32 (s, LD | RT (0) | RA (1) | (frame_size + 16));
+    tcg_out32 (s, MTSPR | RS (0) | LR);
+    tcg_out32 (s, ADDI | RT (1) | RA (1) | frame_size);
+    tcg_out32 (s, BCLR | BO_ALWAYS);
+}
+
+static void tcg_out_ld (TCGContext *s, TCGType type, int ret, int arg1,
+                        tcg_target_long arg2)
+{
+    if (type == TCG_TYPE_I32)
+        tcg_out_ldst (s, ret, arg1, arg2, LWZ, LWZX);
+    else
+        tcg_out_ldst (s, ret, arg1, arg2, LD, LDX);
+}
+
+static void tcg_out_st (TCGContext *s, TCGType type, int arg, int arg1,
+                        tcg_target_long arg2)
+{
+    if (type == TCG_TYPE_I32)
+        tcg_out_ldst (s, arg, arg1, arg2, STW, STWX);
+    else
+        tcg_out_ldst (s, arg, arg1, arg2, STD, STDX);
+}
+
+static void ppc_addi32 (TCGContext *s, int rt, int ra, tcg_target_long si)
+{
+    if (!si && rt == ra)
+        return;
+
+    if (si == (int16_t) si)
+        tcg_out32 (s, ADDI | RT (rt) | RA (ra) | (si & 0xffff));
+    else {
+        uint16_t h = ((si >> 16) & 0xffff) + ((uint16_t) si >> 15);
+        tcg_out32 (s, ADDIS | RT (rt) | RA (ra) | h);
+        tcg_out32 (s, ADDI | RT (rt) | RA (rt) | (si & 0xffff));
+    }
+}
+
+static void ppc_addi64 (TCGContext *s, int rt, int ra, tcg_target_long si)
+{
+    /* XXX: suboptimal */
+    if (si == (int16_t) si
+        || (((uint64_t) si >> 31) == 0) && (si & 0x8000) == 0)
+        ppc_addi32 (s, rt, ra, si);
+    else {
+        tcg_out_movi (s, TCG_TYPE_I64, 0, si);
+        tcg_out32 (s, ADD | RT (rt) | RA (ra));
+    }
+}
+
+static void tcg_out_addi (TCGContext *s, int reg, tcg_target_long val)
+{
+    ppc_addi64 (s, reg, reg, val);
+}
+
+static void tcg_out_cmp (TCGContext *s, int cond, TCGArg arg1, TCGArg arg2,
+                         int const_arg2, int cr, int arch64)
+{
+    int imm;
+    uint32_t op;
+
+    switch (cond) {
+    case TCG_COND_EQ:
+    case TCG_COND_NE:
+        if (const_arg2) {
+            if ((int16_t) arg2 == arg2) {
+                op = CMPI;
+                imm = 1;
+                break;
+            }
+            else if ((uint16_t) arg2 == arg2) {
+                op = CMPLI;
+                imm = 1;
+                break;
+            }
+        }
+        op = CMPL;
+        imm = 0;
+        break;
+
+    case TCG_COND_LT:
+    case TCG_COND_GE:
+    case TCG_COND_LE:
+    case TCG_COND_GT:
+        if (const_arg2) {
+            if ((int16_t) arg2 == arg2) {
+                op = CMPI;
+                imm = 1;
+                break;
+            }
+        }
+        op = CMP;
+        imm = 0;
+        break;
+
+    case TCG_COND_LTU:
+    case TCG_COND_GEU:
+    case TCG_COND_LEU:
+    case TCG_COND_GTU:
+        if (const_arg2) {
+            if ((uint16_t) arg2 == arg2) {
+                op = CMPLI;
+                imm = 1;
+                break;
+            }
+        }
+        op = CMPL;
+        imm = 0;
+        break;
+
+    default:
+        tcg_abort ();
+    }
+    op |= BF (cr) | (arch64 << 21);
+
+    if (imm)
+        tcg_out32 (s, op | RA (arg1) | (arg2 & 0xffff));
+    else {
+        if (const_arg2) {
+            tcg_out_movi (s, TCG_TYPE_I64, 0, arg2);
+            tcg_out32 (s, op | RA (arg1) | RB (0));
+        }
+        else
+            tcg_out32 (s, op | RA (arg1) | RB (arg2));
+    }
+
+}
+
+static void tcg_out_bc (TCGContext *s, int bc, int label_index)
+{
+    TCGLabel *l = &s->labels[label_index];
+
+    if (l->has_value)
+        tcg_out32 (s, bc | reloc_pc14_val (s->code_ptr, l->u.value));
+    else {
+        uint16_t val = *(uint16_t *) &s->code_ptr[2];
+
+        /* Thanks to Andrzej Zaborowski */
+        tcg_out32 (s, bc | (val & 0xfffc));
+        tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL14, label_index, 0);
+    }
+}
+
+static void tcg_out_brcond (TCGContext *s, int cond,
+                            TCGArg arg1, TCGArg arg2, int const_arg2,
+                            int label_index, int arch64)
+{
+    tcg_out_cmp (s, cond, arg1, arg2, const_arg2, 7, arch64);
+    tcg_out_bc (s, tcg_to_bc[cond], label_index);
+}
+
+void ppc_tb_set_jmp_target (unsigned long jmp_addr, unsigned long addr)
+{
+    TCGContext s;
+    unsigned long patch_size;
+
+    s.code_ptr = (uint8_t *) jmp_addr;
+    tcg_out_b (&s, 0, addr);
+    patch_size = s.code_ptr - (uint8_t *) jmp_addr;
+    flush_icache_range (jmp_addr, jmp_addr + patch_size);
+}
+
+static void tcg_out_op (TCGContext *s, int opc, const TCGArg *args,
+                        const int *const_args)
+{
+    int c;
+
+    switch (opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi (s, TCG_TYPE_I64, TCG_REG_R3, args[0]);
+        tcg_out_b (s, 0, (tcg_target_long) tb_ret_addr);
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            s->code_ptr += 28;
+        }
+        else {
+            tcg_abort ();
+        }
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_br:
+        {
+            TCGLabel *l = &s->labels[args[0]];
+
+            if (l->has_value) {
+                tcg_out_b (s, 0, l->u.value);
+            }
+            else {
+                uint32_t val = *(uint32_t *) s->code_ptr;
+
+                /* Thanks to Andrzej Zaborowski */
+                tcg_out32 (s, B | (val & 0x3fffffc));
+                tcg_out_reloc (s, s->code_ptr - 4, R_PPC_REL24, args[0], 0);
+            }
+        }
+        break;
+    case INDEX_op_call:
+        tcg_out_call (s, args[0], const_args[0]);
+        break;
+    case INDEX_op_jmp:
+        if (const_args[0]) {
+            tcg_out_b (s, 0, args[0]);
+        }
+        else {
+            tcg_out32 (s, MTSPR | RS (args[0]) | CTR);
+            tcg_out32 (s, BCCTR | BO_ALWAYS);
+        }
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi (s, TCG_TYPE_I32, args[0], args[1]);
+        break;
+    case INDEX_op_movi_i64:
+        tcg_out_movi (s, TCG_TYPE_I64, args[0], args[1]);
+        break;
+    case INDEX_op_ld8u_i32:
+    case INDEX_op_ld8u_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+        break;
+    case INDEX_op_ld8s_i32:
+    case INDEX_op_ld8s_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LBZ, LBZX);
+        tcg_out32 (s, EXTSB | RS (args[0]) | RA (args[0]));
+        break;
+    case INDEX_op_ld16u_i32:
+    case INDEX_op_ld16u_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LHZ, LHZX);
+        break;
+    case INDEX_op_ld16s_i32:
+    case INDEX_op_ld16s_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LHA, LHAX);
+        break;
+    case INDEX_op_ld_i32:
+    case INDEX_op_ld32u_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LWZ, LWZX);
+        break;
+    case INDEX_op_ld32s_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LWA, LWAX);
+        break;
+    case INDEX_op_ld_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], LD, LDX);
+        break;
+    case INDEX_op_st8_i32:
+    case INDEX_op_st8_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], STB, STBX);
+        break;
+    case INDEX_op_st16_i32:
+    case INDEX_op_st16_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], STH, STHX);
+        break;
+    case INDEX_op_st_i32:
+    case INDEX_op_st32_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], STW, STWX);
+        break;
+    case INDEX_op_st_i64:
+        tcg_out_ldst (s, args[0], args[1], args[2], STD, STDX);
+        break;
+
+    case INDEX_op_add_i32:
+        if (const_args[2])
+            ppc_addi32 (s, args[0], args[1], args[2]);
+        else
+            tcg_out32 (s, ADD | TAB (args[0], args[1], args[2]));
+        break;
+    case INDEX_op_sub_i32:
+        if (const_args[2])
+            ppc_addi32 (s, args[0], args[1], -args[2]);
+        else
+            tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1]));
+        break;
+
+    case INDEX_op_and_i64:
+    case INDEX_op_and_i32:
+        if (const_args[2]) {
+            if ((args[2] & 0xffff) == args[2])
+                tcg_out32 (s, ANDI | RS (args[1]) | RA (args[0]) | args[2]);
+            else if ((args[2] & 0xffff0000) == args[2])
+                tcg_out32 (s, ANDIS | RS (args[1]) | RA (args[0])
+                           | ((args[2] >> 16) & 0xffff));
+            else {
+                tcg_out_movi (s, (opc == INDEX_op_and_i32
+                                  ? TCG_TYPE_I32
+                                  : TCG_TYPE_I64),
+                              0, args[2]);
+                tcg_out32 (s, AND | SAB (args[1], args[0], 0));
+            }
+        }
+        else
+            tcg_out32 (s, AND | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_or_i64:
+    case INDEX_op_or_i32:
+        if (const_args[2]) {
+            if (args[2] & 0xffff) {
+                tcg_out32 (s, ORI | RS (args[1]) | RA (args[0])
+                           | (args[2] & 0xffff));
+                if (args[2] >> 16)
+                    tcg_out32 (s, ORIS | RS (args[0])  | RA (args[0])
+                               | ((args[2] >> 16) & 0xffff));
+            }
+            else {
+                tcg_out32 (s, ORIS | RS (args[1])  | RA (args[0])
+                           | ((args[2] >> 16) & 0xffff));
+            }
+        }
+        else
+            tcg_out32 (s, OR | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_xor_i64:
+    case INDEX_op_xor_i32:
+        if (const_args[2]) {
+            if ((args[2] & 0xffff) == args[2])
+                tcg_out32 (s, XORI | RS (args[1])  | RA (args[0])
+                           | (args[2] & 0xffff));
+            else if ((args[2] & 0xffff0000) == args[2])
+                tcg_out32 (s, XORIS | RS (args[1])  | RA (args[0])
+                           | ((args[2] >> 16) & 0xffff));
+            else {
+                tcg_out_movi (s, (opc == INDEX_op_and_i32
+                                  ? TCG_TYPE_I32
+                                  : TCG_TYPE_I64),
+                              0, args[2]);
+                tcg_out32 (s, XOR | SAB (args[1], args[0], 0));
+            }
+        }
+        else
+            tcg_out32 (s, XOR | SAB (args[1], args[0], args[2]));
+        break;
+
+    case INDEX_op_mul_i32:
+        if (const_args[2]) {
+            if (args[2] == (int16_t) args[2])
+                tcg_out32 (s, MULLI | RT (args[0]) | RA (args[1])
+                           | (args[2] & 0xffff));
+            else {
+                tcg_out_movi (s, TCG_TYPE_I32, 0, args[2]);
+                tcg_out32 (s, MULLW | TAB (args[0], args[1], 0));
+            }
+        }
+        else
+            tcg_out32 (s, MULLW | TAB (args[0], args[1], args[2]));
+        break;
+
+    case INDEX_op_div_i32:
+        tcg_out32 (s, DIVW | TAB (args[0], args[1], args[2]));
+        break;
+
+    case INDEX_op_divu_i32:
+        tcg_out32 (s, DIVWU | TAB (args[0], args[1], args[2]));
+        break;
+
+    case INDEX_op_rem_i32:
+        tcg_out32 (s, DIVW | TAB (0, args[1], args[2]));
+        tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+        tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+        break;
+
+    case INDEX_op_remu_i32:
+        tcg_out32 (s, DIVWU | TAB (0, args[1], args[2]));
+        tcg_out32 (s, MULLW | TAB (0, 0, args[2]));
+        tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+        break;
+
+    case INDEX_op_shl_i32:
+        if (const_args[2]) {
+            tcg_out32 (s, (RLWINM
+                           | RA (args[0])
+                           | RS (args[1])
+                           | SH (args[2])
+                           | MB (0)
+                           | ME (31 - args[2])
+                           )
+                );
+        }
+        else
+            tcg_out32 (s, SLW | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_shr_i32:
+        if (const_args[2]) {
+            tcg_out32 (s, (RLWINM
+                           | RA (args[0])
+                           | RS (args[1])
+                           | SH (32 - args[2])
+                           | MB (args[2])
+                           | ME (31)
+                           )
+                );
+        }
+        else
+            tcg_out32 (s, SRW | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_sar_i32:
+        if (const_args[2])
+            tcg_out32 (s, SRAWI | RS (args[1]) | RA (args[0]) | SH (args[2]));
+        else
+            tcg_out32 (s, SRAW | SAB (args[1], args[0], args[2]));
+        break;
+
+    case INDEX_op_brcond_i32:
+        tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 0);
+        break;
+
+    case INDEX_op_brcond_i64:
+        tcg_out_brcond (s, args[2], args[0], args[1], const_args[1], args[3], 1);
+        break;
+
+    case INDEX_op_neg_i32:
+    case INDEX_op_neg_i64:
+        tcg_out32 (s, NEG | RT (args[0]) | RA (args[1]));
+        break;
+
+    case INDEX_op_add_i64:
+        if (const_args[2])
+            ppc_addi64 (s, args[0], args[1], args[2]);
+        else
+            tcg_out32 (s, ADD | TAB (args[0], args[1], args[2]));
+        break;
+    case INDEX_op_sub_i64:
+        if (const_args[2])
+            ppc_addi64 (s, args[0], args[1], -args[2]);
+        else
+            tcg_out32 (s, SUBF | TAB (args[0], args[2], args[1]));
+        break;
+
+    case INDEX_op_shl_i64:
+        if (const_args[2])
+            tcg_out_rld (s, RLDICR, args[0], args[1], args[2], 63 - args[2]);
+        else
+            tcg_out32 (s, SLD | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_shr_i64:
+        if (const_args[2])
+            tcg_out_rld (s, RLDICL, args[0], args[1], 64 - args[2], args[2]);
+        else
+            tcg_out32 (s, SRD | SAB (args[1], args[0], args[2]));
+        break;
+    case INDEX_op_sar_i64:
+        if (const_args[2]) {
+            int sh = SH (args[2] & 0x1f) | (((args[2] >> 5) & 1) << 1);
+            tcg_out32 (s, SRADI | RA (args[0]) | RS (args[1]) | sh);
+        }
+        else
+            tcg_out32 (s, SRAD | SAB (args[1], args[0], args[2]));
+        break;
+
+    case INDEX_op_mul_i64:
+        tcg_out32 (s, MULLD | TAB (args[0], args[1], args[2]));
+        break;
+    case INDEX_op_div_i64:
+        tcg_out32 (s, DIVD | TAB (args[0], args[1], args[2]));
+        break;
+    case INDEX_op_divu_i64:
+        tcg_out32 (s, DIVDU | TAB (args[0], args[1], args[2]));
+        break;
+    case INDEX_op_rem_i64:
+        tcg_out32 (s, DIVD | TAB (0, args[1], args[2]));
+        tcg_out32 (s, MULLD | TAB (0, 0, args[2]));
+        tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+        break;
+    case INDEX_op_remu_i64:
+        tcg_out32 (s, DIVDU | TAB (0, args[1], args[2]));
+        tcg_out32 (s, MULLD | TAB (0, 0, args[2]));
+        tcg_out32 (s, SUBF | TAB (args[0], 0, args[1]));
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld (s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld (s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld (s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld (s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld (s, args, 2);
+        break;
+    case INDEX_op_qemu_ld32s:
+        tcg_out_qemu_ld (s, args, 2 | 4);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld (s, args, 3);
+        break;
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st (s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st (s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st (s, args, 2);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st (s, args, 3);
+        break;
+
+    case INDEX_op_ext8s_i32:
+    case INDEX_op_ext8s_i64:
+        c = EXTSB;
+        goto gen_ext;
+    case INDEX_op_ext16s_i32:
+    case INDEX_op_ext16s_i64:
+        c = EXTSH;
+        goto gen_ext;
+    case INDEX_op_ext32s_i64:
+        c = EXTSW;
+        goto gen_ext;
+    gen_ext:
+        tcg_out32 (s, c | RS (args[1]) | RA (args[0]));
+        break;
+
+    default:
+        tcg_dump_ops (s, stderr);
+        tcg_abort ();
+    }
+}
+
+static const TCGTargetOpDef ppc_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "ri" } },
+    { INDEX_op_jmp, { "ri" } },
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_mov_i64, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_movi_i64, { "r" } },
+
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_ld_i64, { "r", "r" } },
+    { INDEX_op_st8_i32, { "r", "r" } },
+    { INDEX_op_st8_i64, { "r", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st16_i64, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+    { INDEX_op_st_i64, { "r", "r" } },
+    { INDEX_op_st32_i64, { "r", "r" } },
+
+    { INDEX_op_ld8u_i64, { "r", "r" } },
+    { INDEX_op_ld8s_i64, { "r", "r" } },
+    { INDEX_op_ld16u_i64, { "r", "r" } },
+    { INDEX_op_ld16s_i64, { "r", "r" } },
+    { INDEX_op_ld32u_i64, { "r", "r" } },
+    { INDEX_op_ld32s_i64, { "r", "r" } },
+    { INDEX_op_ld_i64, { "r", "r" } },
+
+    { INDEX_op_add_i32, { "r", "r", "ri" } },
+    { INDEX_op_mul_i32, { "r", "r", "ri" } },
+    { INDEX_op_div_i32, { "r", "r", "r" } },
+    { INDEX_op_divu_i32, { "r", "r", "r" } },
+    { INDEX_op_rem_i32, { "r", "r", "r" } },
+    { INDEX_op_remu_i32, { "r", "r", "r" } },
+    { INDEX_op_sub_i32, { "r", "r", "ri" } },
+    { INDEX_op_and_i32, { "r", "r", "ri" } },
+    { INDEX_op_or_i32, { "r", "r", "ri" } },
+    { INDEX_op_xor_i32, { "r", "r", "ri" } },
+
+    { INDEX_op_shl_i32, { "r", "r", "ri" } },
+    { INDEX_op_shr_i32, { "r", "r", "ri" } },
+    { INDEX_op_sar_i32, { "r", "r", "ri" } },
+
+    { INDEX_op_brcond_i32, { "r", "ri" } },
+    { INDEX_op_brcond_i64, { "r", "ri" } },
+
+    { INDEX_op_neg_i32, { "r", "r" } },
+
+    { INDEX_op_add_i64, { "r", "r", "ri" } },
+    { INDEX_op_sub_i64, { "r", "r", "ri" } },
+    { INDEX_op_and_i64, { "r", "r", "rZ" } },
+    { INDEX_op_or_i64, { "r", "r", "rZ" } },
+    { INDEX_op_xor_i64, { "r", "r", "rZ" } },
+
+    { INDEX_op_shl_i64, { "r", "r", "ri" } },
+    { INDEX_op_shr_i64, { "r", "r", "ri" } },
+    { INDEX_op_sar_i64, { "r", "r", "ri" } },
+
+    { INDEX_op_mul_i64, { "r", "r", "r" } },
+    { INDEX_op_div_i64, { "r", "r", "r" } },
+    { INDEX_op_divu_i64, { "r", "r", "r" } },
+    { INDEX_op_rem_i64, { "r", "r", "r" } },
+    { INDEX_op_remu_i64, { "r", "r", "r" } },
+
+    { INDEX_op_neg_i64, { "r", "r" } },
+
+    { INDEX_op_qemu_ld8u, { "r", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L" } },
+    { INDEX_op_qemu_ld32s, { "r", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "L" } },
+
+    { INDEX_op_qemu_st8, { "S", "S" } },
+    { INDEX_op_qemu_st16, { "S", "S" } },
+    { INDEX_op_qemu_st32, { "S", "S" } },
+    { INDEX_op_qemu_st64, { "S", "S", "S" } },
+
+    { INDEX_op_ext8s_i32, { "r", "r" } },
+    { INDEX_op_ext16s_i32, { "r", "r" } },
+    { INDEX_op_ext8s_i64, { "r", "r" } },
+    { INDEX_op_ext16s_i64, { "r", "r" } },
+    { INDEX_op_ext32s_i64, { "r", "r" } },
+
+    { -1 },
+};
+
+void tcg_target_init (TCGContext *s)
+{
+    tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+    tcg_regset_set32 (tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff);
+    tcg_regset_set32 (tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_R0) |
+                     (1 << TCG_REG_R3) |
+                     (1 << TCG_REG_R4) |
+                     (1 << TCG_REG_R5) |
+                     (1 << TCG_REG_R6) |
+                     (1 << TCG_REG_R7) |
+                     (1 << TCG_REG_R8) |
+                     (1 << TCG_REG_R9) |
+                     (1 << TCG_REG_R10) |
+                     (1 << TCG_REG_R11) |
+                     (1 << TCG_REG_R12)
+        );
+
+    tcg_regset_clear (s->reserved_regs);
+    tcg_regset_set_reg (s->reserved_regs, TCG_REG_R0);
+    tcg_regset_set_reg (s->reserved_regs, TCG_REG_R1);
+    tcg_regset_set_reg (s->reserved_regs, TCG_REG_R2);
+    tcg_regset_set_reg (s->reserved_regs, TCG_REG_R13);
+
+    tcg_add_target_add_op_defs (ppc_op_defs);
+}
diff --git a/tcg/ppc64/tcg-target.h b/tcg/ppc64/tcg-target.h
new file mode 100644
index 0000000..2174db2
--- /dev/null
+++ b/tcg/ppc64/tcg-target.h
@@ -0,0 +1,105 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_PPC64 1
+
+#define TCG_TARGET_REG_BITS 64
+#define TCG_TARGET_WORDS_BIGENDIAN
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+    TCG_REG_R0 = 0,
+    TCG_REG_R1,
+    TCG_REG_R2,
+    TCG_REG_R3,
+    TCG_REG_R4,
+    TCG_REG_R5,
+    TCG_REG_R6,
+    TCG_REG_R7,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+    TCG_REG_R15,
+    TCG_REG_R16,
+    TCG_REG_R17,
+    TCG_REG_R18,
+    TCG_REG_R19,
+    TCG_REG_R20,
+    TCG_REG_R21,
+    TCG_REG_R22,
+    TCG_REG_R23,
+    TCG_REG_R24,
+    TCG_REG_R25,
+    TCG_REG_R26,
+    TCG_REG_R27,
+    TCG_REG_R28,
+    TCG_REG_R29,
+    TCG_REG_R30,
+    TCG_REG_R31
+};
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_R1
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_CALL_STACK_OFFSET 48
+
+/* optional instructions */
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_div_i32
+#define TCG_TARGET_HAS_neg_i64
+#define TCG_TARGET_HAS_div_i64
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_ext8s_i64
+#define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+
+#define TCG_AREG0 TCG_REG_R27
+#define TCG_AREG1 TCG_REG_R24
+#define TCG_AREG2 TCG_REG_R25
+#define TCG_AREG3 TCG_REG_R26
+
+/* taken directly from tcg-dyngen.c */
+#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    unsigned long p;
+
+    start &= ~(MIN_CACHE_LINE_SIZE - 1);
+    stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
+
+    for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+        asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
+    }
+    asm volatile ("sync" : : : "memory");
+    for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+        asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
+    }
+    asm volatile ("sync" : : : "memory");
+    asm volatile ("isync" : : : "memory");
+}
diff --git a/tcg/sparc/tcg-target.c b/tcg/sparc/tcg-target.c
new file mode 100644
index 0000000..f36796d
--- /dev/null
+++ b/tcg/sparc/tcg-target.c
@@ -0,0 +1,1206 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "%g0",
+    "%g1",
+    "%g2",
+    "%g3",
+    "%g4",
+    "%g5",
+    "%g6",
+    "%g7",
+    "%o0",
+    "%o1",
+    "%o2",
+    "%o3",
+    "%o4",
+    "%o5",
+    "%o6",
+    "%o7",
+    "%l0",
+    "%l1",
+    "%l2",
+    "%l3",
+    "%l4",
+    "%l5",
+    "%l6",
+    "%l7",
+    "%i0",
+    "%i1",
+    "%i2",
+    "%i3",
+    "%i4",
+    "%i5",
+    "%i6",
+    "%i7",
+};
+
+static const int tcg_target_reg_alloc_order[] = {
+    TCG_REG_L0,
+    TCG_REG_L1,
+    TCG_REG_L2,
+    TCG_REG_L3,
+    TCG_REG_L4,
+    TCG_REG_L5,
+    TCG_REG_L6,
+    TCG_REG_L7,
+    TCG_REG_I0,
+    TCG_REG_I1,
+    TCG_REG_I2,
+    TCG_REG_I3,
+    TCG_REG_I4,
+};
+
+static const int tcg_target_call_iarg_regs[6] = {
+    TCG_REG_O0,
+    TCG_REG_O1,
+    TCG_REG_O2,
+    TCG_REG_O3,
+    TCG_REG_O4,
+    TCG_REG_O5,
+};
+
+static const int tcg_target_call_oarg_regs[2] = {
+    TCG_REG_O0,
+    TCG_REG_O1,
+};
+
+static inline int check_fit_tl(tcg_target_long val, unsigned int bits)
+{
+    return (val << ((sizeof(tcg_target_long) * 8 - bits))
+            >> (sizeof(tcg_target_long) * 8 - bits)) == val;
+}
+
+static inline int check_fit_i32(uint32_t val, unsigned int bits)
+{
+    return ((val << (32 - bits)) >> (32 - bits)) == val;
+}
+
+static void patch_reloc(uint8_t *code_ptr, int type,
+                        tcg_target_long value, tcg_target_long addend)
+{
+    value += addend;
+    switch (type) {
+    case R_SPARC_32:
+        if (value != (uint32_t)value)
+            tcg_abort();
+        *(uint32_t *)code_ptr = value;
+        break;
+    case R_SPARC_WDISP22:
+        value -= (long)code_ptr;
+        value >>= 2;
+        if (!check_fit_tl(value, 22))
+            tcg_abort();
+        *(uint32_t *)code_ptr = ((*(uint32_t *)code_ptr) & ~0x3fffff) | value;
+        break;
+    default:
+        tcg_abort();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    return 6;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch (ct_str[0]) {
+    case 'r':
+    case 'L': /* qemu_ld/st constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffffffff);
+        // Helper args
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_O0);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_O1);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_O2);
+        break;
+    case 'I':
+        ct->ct |= TCG_CT_CONST_S11;
+        break;
+    case 'J':
+        ct->ct |= TCG_CT_CONST_S13;
+        break;
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+                                         const TCGArgConstraint *arg_ct)
+{
+    int ct;
+
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    else if ((ct & TCG_CT_CONST_S11) && check_fit_tl(val, 11))
+        return 1;
+    else if ((ct & TCG_CT_CONST_S13) && check_fit_tl(val, 13))
+        return 1;
+    else
+        return 0;
+}
+
+#define INSN_OP(x)  ((x) << 30)
+#define INSN_OP2(x) ((x) << 22)
+#define INSN_OP3(x) ((x) << 19)
+#define INSN_OPF(x) ((x) << 5)
+#define INSN_RD(x)  ((x) << 25)
+#define INSN_RS1(x) ((x) << 14)
+#define INSN_RS2(x) (x)
+#define INSN_ASI(x) ((x) << 5)
+
+#define INSN_IMM13(x) ((1 << 13) | ((x) & 0x1fff))
+#define INSN_OFF22(x) (((x) >> 2) & 0x3fffff)
+
+#define INSN_COND(x, a) (((x) << 25) | ((a) << 29))
+#define COND_N     0x0
+#define COND_E     0x1
+#define COND_LE    0x2
+#define COND_L     0x3
+#define COND_LEU   0x4
+#define COND_CS    0x5
+#define COND_NEG   0x6
+#define COND_VS    0x7
+#define COND_A     0x8
+#define COND_NE    0x9
+#define COND_G     0xa
+#define COND_GE    0xb
+#define COND_GU    0xc
+#define COND_CC    0xd
+#define COND_POS   0xe
+#define COND_VC    0xf
+#define BA         (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2))
+
+#define ARITH_ADD  (INSN_OP(2) | INSN_OP3(0x00))
+#define ARITH_AND  (INSN_OP(2) | INSN_OP3(0x01))
+#define ARITH_OR   (INSN_OP(2) | INSN_OP3(0x02))
+#define ARITH_ORCC (INSN_OP(2) | INSN_OP3(0x12))
+#define ARITH_XOR  (INSN_OP(2) | INSN_OP3(0x03))
+#define ARITH_SUB  (INSN_OP(2) | INSN_OP3(0x04))
+#define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14))
+#define ARITH_ADDX (INSN_OP(2) | INSN_OP3(0x10))
+#define ARITH_SUBX (INSN_OP(2) | INSN_OP3(0x0c))
+#define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a))
+#define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e))
+#define ARITH_SDIV (INSN_OP(2) | INSN_OP3(0x0f))
+#define ARITH_MULX (INSN_OP(2) | INSN_OP3(0x09))
+#define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d))
+#define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d))
+
+#define SHIFT_SLL  (INSN_OP(2) | INSN_OP3(0x25))
+#define SHIFT_SRL  (INSN_OP(2) | INSN_OP3(0x26))
+#define SHIFT_SRA  (INSN_OP(2) | INSN_OP3(0x27))
+
+#define SHIFT_SLLX (INSN_OP(2) | INSN_OP3(0x25) | (1 << 12))
+#define SHIFT_SRLX (INSN_OP(2) | INSN_OP3(0x26) | (1 << 12))
+#define SHIFT_SRAX (INSN_OP(2) | INSN_OP3(0x27) | (1 << 12))
+
+#define WRY        (INSN_OP(2) | INSN_OP3(0x30))
+#define JMPL       (INSN_OP(2) | INSN_OP3(0x38))
+#define SAVE       (INSN_OP(2) | INSN_OP3(0x3c))
+#define RESTORE    (INSN_OP(2) | INSN_OP3(0x3d))
+#define SETHI      (INSN_OP(0) | INSN_OP2(0x4))
+#define CALL       INSN_OP(1)
+#define LDUB       (INSN_OP(3) | INSN_OP3(0x01))
+#define LDSB       (INSN_OP(3) | INSN_OP3(0x09))
+#define LDUH       (INSN_OP(3) | INSN_OP3(0x02))
+#define LDSH       (INSN_OP(3) | INSN_OP3(0x0a))
+#define LDUW       (INSN_OP(3) | INSN_OP3(0x00))
+#define LDSW       (INSN_OP(3) | INSN_OP3(0x08))
+#define LDX        (INSN_OP(3) | INSN_OP3(0x0b))
+#define STB        (INSN_OP(3) | INSN_OP3(0x05))
+#define STH        (INSN_OP(3) | INSN_OP3(0x06))
+#define STW        (INSN_OP(3) | INSN_OP3(0x04))
+#define STX        (INSN_OP(3) | INSN_OP3(0x0e))
+#define LDUBA      (INSN_OP(3) | INSN_OP3(0x11))
+#define LDSBA      (INSN_OP(3) | INSN_OP3(0x19))
+#define LDUHA      (INSN_OP(3) | INSN_OP3(0x12))
+#define LDSHA      (INSN_OP(3) | INSN_OP3(0x1a))
+#define LDUWA      (INSN_OP(3) | INSN_OP3(0x10))
+#define LDSWA      (INSN_OP(3) | INSN_OP3(0x18))
+#define LDXA       (INSN_OP(3) | INSN_OP3(0x1b))
+#define STBA       (INSN_OP(3) | INSN_OP3(0x15))
+#define STHA       (INSN_OP(3) | INSN_OP3(0x16))
+#define STWA       (INSN_OP(3) | INSN_OP3(0x14))
+#define STXA       (INSN_OP(3) | INSN_OP3(0x1e))
+
+#ifndef ASI_PRIMARY_LITTLE
+#define ASI_PRIMARY_LITTLE 0x88
+#endif
+
+static inline void tcg_out_arith(TCGContext *s, int rd, int rs1, int rs2,
+                                 int op)
+{
+    tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) |
+              INSN_RS2(rs2));
+}
+
+static inline void tcg_out_arithi(TCGContext *s, int rd, int rs1,
+                                  uint32_t offset, int op)
+{
+    tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) |
+              INSN_IMM13(offset));
+}
+
+static inline void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    tcg_out_arith(s, ret, arg, TCG_REG_G0, ARITH_OR);
+}
+
+static inline void tcg_out_sethi(TCGContext *s, int ret, uint32_t arg)
+{
+    tcg_out32(s, SETHI | INSN_RD(ret) | ((arg & 0xfffffc00) >> 10));
+}
+
+static inline void tcg_out_movi_imm13(TCGContext *s, int ret, uint32_t arg)
+{
+    tcg_out_arithi(s, ret, TCG_REG_G0, arg, ARITH_OR);
+}
+
+static inline void tcg_out_movi_imm32(TCGContext *s, int ret, uint32_t arg)
+{
+    if (check_fit_tl(arg, 12))
+        tcg_out_movi_imm13(s, ret, arg);
+    else {
+        tcg_out_sethi(s, ret, arg);
+        if (arg & 0x3ff)
+            tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR);
+    }
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type,
+                                int ret, tcg_target_long arg)
+{
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    if (!check_fit_tl(arg, 32) && (arg & ~0xffffffffULL) != 0) {
+        tcg_out_movi_imm32(s, TCG_REG_I4, arg >> 32);
+        tcg_out_arithi(s, TCG_REG_I4, TCG_REG_I4, 32, SHIFT_SLLX);
+        tcg_out_movi_imm32(s, ret, arg);
+        tcg_out_arith(s, ret, ret, TCG_REG_I4, ARITH_OR);
+    } else if (check_fit_tl(arg, 12))
+        tcg_out_movi_imm13(s, ret, arg);
+    else {
+        tcg_out_sethi(s, ret, arg);
+        if (arg & 0x3ff)
+            tcg_out_arithi(s, ret, ret, arg & 0x3ff, ARITH_OR);
+    }
+#else
+    tcg_out_movi_imm32(s, ret, arg);
+#endif
+}
+
+static inline void tcg_out_ld_raw(TCGContext *s, int ret,
+                                  tcg_target_long arg)
+{
+    tcg_out_sethi(s, ret, arg);
+    tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) |
+              INSN_IMM13(arg & 0x3ff));
+}
+
+static inline void tcg_out_ld_ptr(TCGContext *s, int ret,
+                                  tcg_target_long arg)
+{
+    if (!check_fit_tl(arg, 10))
+        tcg_out_movi(s, TCG_TYPE_PTR, ret, arg & ~0x3ffULL);
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(ret) |
+              INSN_IMM13(arg & 0x3ff));
+#else
+    tcg_out32(s, LDUW | INSN_RD(ret) | INSN_RS1(ret) |
+              INSN_IMM13(arg & 0x3ff));
+#endif
+}
+
+static inline void tcg_out_ldst(TCGContext *s, int ret, int addr, int offset, int op)
+{
+    if (check_fit_tl(offset, 13))
+        tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(addr) |
+                  INSN_IMM13(offset));
+    else {
+        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, offset);
+        tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(TCG_REG_I5) |
+                  INSN_RS2(addr));
+    }
+}
+
+static inline void tcg_out_ldst_asi(TCGContext *s, int ret, int addr,
+                                    int offset, int op, int asi)
+{
+    tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, offset);
+    tcg_out32(s, op | INSN_RD(ret) | INSN_RS1(TCG_REG_I5) |
+              INSN_ASI(asi) | INSN_RS2(addr));
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+                              int arg1, tcg_target_long arg2)
+{
+    if (type == TCG_TYPE_I32)
+        tcg_out_ldst(s, ret, arg1, arg2, LDUW);
+    else
+        tcg_out_ldst(s, ret, arg1, arg2, LDX);
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+                              int arg1, tcg_target_long arg2)
+{
+    if (type == TCG_TYPE_I32)
+        tcg_out_ldst(s, arg, arg1, arg2, STW);
+    else
+        tcg_out_ldst(s, arg, arg1, arg2, STX);
+}
+
+static inline void tcg_out_sety(TCGContext *s, tcg_target_long val)
+{
+    if (val == 0 || val == -1)
+        tcg_out32(s, WRY | INSN_IMM13(val));
+    else
+        fprintf(stderr, "unimplemented sety %ld\n", (long)val);
+}
+
+static inline void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    if (val != 0) {
+        if (check_fit_tl(val, 13))
+            tcg_out_arithi(s, reg, reg, val, ARITH_ADD);
+        else {
+            tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I5, val);
+            tcg_out_arith(s, reg, reg, TCG_REG_I5, ARITH_ADD);
+        }
+    }
+}
+
+static inline void tcg_out_andi(TCGContext *s, int reg, tcg_target_long val)
+{
+    if (val != 0) {
+        if (check_fit_tl(val, 13))
+            tcg_out_arithi(s, reg, reg, val, ARITH_AND);
+        else {
+            tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, val);
+            tcg_out_arith(s, reg, reg, TCG_REG_I5, ARITH_AND);
+        }
+    }
+}
+
+static inline void tcg_out_nop(TCGContext *s)
+{
+    tcg_out_sethi(s, TCG_REG_G0, 0);
+}
+
+static void tcg_out_branch(TCGContext *s, int opc, int label_index)
+{
+    int32_t val;
+    TCGLabel *l = &s->labels[label_index];
+
+    if (l->has_value) {
+        val = l->u.value - (tcg_target_long)s->code_ptr;
+        tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x2)
+                      | INSN_OFF22(l->u.value - (unsigned long)s->code_ptr)));
+    } else {
+        tcg_out_reloc(s, s->code_ptr, R_SPARC_WDISP22, label_index, 0);
+        tcg_out32(s, (INSN_OP(0) | INSN_COND(opc, 0) | INSN_OP2(0x2) | 0));
+    }
+}
+
+static const uint8_t tcg_cond_to_bcond[10] = {
+    [TCG_COND_EQ] = COND_E,
+    [TCG_COND_NE] = COND_NE,
+    [TCG_COND_LT] = COND_L,
+    [TCG_COND_GE] = COND_GE,
+    [TCG_COND_LE] = COND_LE,
+    [TCG_COND_GT] = COND_G,
+    [TCG_COND_LTU] = COND_CS,
+    [TCG_COND_GEU] = COND_CC,
+    [TCG_COND_LEU] = COND_LEU,
+    [TCG_COND_GTU] = COND_GU,
+};
+
+static void tcg_out_brcond(TCGContext *s, int cond,
+                           TCGArg arg1, TCGArg arg2, int const_arg2,
+                           int label_index)
+{
+    if (const_arg2 && arg2 == 0)
+        /* orcc %g0, r, %g0 */
+        tcg_out_arith(s, TCG_REG_G0, TCG_REG_G0, arg1, ARITH_ORCC);
+    else
+        /* subcc r1, r2, %g0 */
+        tcg_out_arith(s, TCG_REG_G0, arg1, arg2, ARITH_SUBCC);
+    tcg_out_branch(s, tcg_cond_to_bcond[cond], label_index);
+    tcg_out_nop(s);
+}
+
+/* Generate global QEMU prologue and epilogue code */
+void tcg_target_qemu_prologue(TCGContext *s)
+{
+    tcg_out32(s, SAVE | INSN_RD(TCG_REG_O6) | INSN_RS1(TCG_REG_O6) |
+              INSN_IMM13(-TCG_TARGET_STACK_MINFRAME));
+    tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I0) |
+              INSN_RS2(TCG_REG_G0));
+    tcg_out_nop(s);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static const void * const qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static const void * const qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+#if TARGET_LONG_BITS == 32
+#define TARGET_LD_OP LDUW
+#else
+#define TARGET_LD_OP LDX
+#endif
+
+#if TARGET_PHYS_ADDR_BITS == 32
+#define TARGET_ADDEND_LD_OP LDUW
+#else
+#define TARGET_ADDEND_LD_OP LDX
+#endif
+
+#ifdef __arch64__
+#define HOST_LD_OP LDX
+#define HOST_ST_OP STX
+#define HOST_SLL_OP SHIFT_SLLX
+#define HOST_SRA_OP SHIFT_SRAX
+#else
+#define HOST_LD_OP LDUW
+#define HOST_ST_OP STW
+#define HOST_SLL_OP SHIFT_SLL
+#define HOST_SRA_OP SHIFT_SRA
+#endif
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_reg, data_reg, arg0, arg1, arg2, mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+    uint32_t *label1_ptr, *label2_ptr;
+#endif
+
+    data_reg = *args++;
+    addr_reg = *args++;
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    arg0 = TCG_REG_O0;
+    arg1 = TCG_REG_O1;
+    arg2 = TCG_REG_O2;
+
+#if defined(CONFIG_SOFTMMU)
+    /* srl addr_reg, x, arg1 */
+    tcg_out_arithi(s, arg1, addr_reg, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS,
+                   SHIFT_SRL);
+    /* and addr_reg, x, arg0 */
+    tcg_out_arithi(s, arg0, addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+                   ARITH_AND);
+
+    /* and arg1, x, arg1 */
+    tcg_out_andi(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+    /* add arg1, x, arg1 */
+    tcg_out_addi(s, arg1, offsetof(CPUState,
+                                   tlb_table[mem_index][0].addr_read));
+
+    /* add env, arg1, arg1 */
+    tcg_out_arith(s, arg1, TCG_AREG0, arg1, ARITH_ADD);
+
+    /* ld [arg1], arg2 */
+    tcg_out32(s, TARGET_LD_OP | INSN_RD(arg2) | INSN_RS1(arg1) |
+              INSN_RS2(TCG_REG_G0));
+
+    /* subcc arg0, arg2, %g0 */
+    tcg_out_arith(s, TCG_REG_G0, arg0, arg2, ARITH_SUBCC);
+
+    /* will become:
+       be label1 */
+    label1_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, 0);
+
+    /* mov (delay slot) */
+    tcg_out_mov(s, arg0, addr_reg);
+
+    /* mov */
+    tcg_out_movi(s, TCG_TYPE_I32, arg1, mem_index);
+
+    /* XXX: move that code at the end of the TB */
+    /* qemu_ld_helper[s_bits](arg0, arg1) */
+    tcg_out32(s, CALL | ((((tcg_target_ulong)qemu_ld_helpers[s_bits]
+                           - (tcg_target_ulong)s->code_ptr) >> 2)
+                         & 0x3fffffff));
+    /* Store AREG0 in stack to avoid ugly glibc bugs that mangle
+       global registers */
+    // delay slot
+    tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+                 TCG_TARGET_CALL_STACK_OFFSET - sizeof(long), HOST_ST_OP);
+    tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+                 TCG_TARGET_CALL_STACK_OFFSET - sizeof(long), HOST_LD_OP);
+
+    /* data_reg = sign_extend(arg0) */
+    switch(opc) {
+    case 0 | 4:
+        /* sll arg0, 24/56, data_reg */
+        tcg_out_arithi(s, data_reg, arg0, (int)sizeof(tcg_target_long) * 8 - 8,
+                       HOST_SLL_OP);
+        /* sra data_reg, 24/56, data_reg */
+        tcg_out_arithi(s, data_reg, data_reg,
+                       (int)sizeof(tcg_target_long) * 8 - 8, HOST_SRA_OP);
+        break;
+    case 1 | 4:
+        /* sll arg0, 16/48, data_reg */
+        tcg_out_arithi(s, data_reg, arg0,
+                       (int)sizeof(tcg_target_long) * 8 - 16, HOST_SLL_OP);
+        /* sra data_reg, 16/48, data_reg */
+        tcg_out_arithi(s, data_reg, data_reg,
+                       (int)sizeof(tcg_target_long) * 8 - 16, HOST_SRA_OP);
+        break;
+    case 2 | 4:
+        /* sll arg0, 32, data_reg */
+        tcg_out_arithi(s, data_reg, arg0, 32, HOST_SLL_OP);
+        /* sra data_reg, 32, data_reg */
+        tcg_out_arithi(s, data_reg, data_reg, 32, HOST_SRA_OP);
+        break;
+    case 0:
+    case 1:
+    case 2:
+    case 3:
+    default:
+        /* mov */
+        tcg_out_mov(s, data_reg, arg0);
+        break;
+    }
+
+    /* will become:
+       ba label2 */
+    label2_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, 0);
+
+    /* nop (delay slot */
+    tcg_out_nop(s);
+
+    /* label1: */
+    *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x2) |
+                   INSN_OFF22((unsigned long)s->code_ptr -
+                              (unsigned long)label1_ptr));
+
+    /* ld [arg1 + x], arg1 */
+    tcg_out_ldst(s, arg1, arg1, offsetof(CPUTLBEntry, addend) -
+                 offsetof(CPUTLBEntry, addr_read), TARGET_ADDEND_LD_OP);
+
+#if TARGET_LONG_BITS == 32
+    /* and addr_reg, x, arg0 */
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, 0xffffffff);
+    tcg_out_arith(s, arg0, addr_reg, TCG_REG_I5, ARITH_AND);
+    /* add arg0, arg1, arg0 */
+    tcg_out_arith(s, arg0, arg0, arg1, ARITH_ADD);
+#else
+    /* add addr_reg, arg1, arg0 */
+    tcg_out_arith(s, arg0, addr_reg, arg1, ARITH_ADD);
+#endif
+
+#else
+    arg0 = addr_reg;
+#endif
+
+    switch(opc) {
+    case 0:
+        /* ldub [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDUB);
+        break;
+    case 0 | 4:
+        /* ldsb [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDSB);
+        break;
+    case 1:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* lduh [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDUH);
+#else
+        /* lduha [arg0] ASI_PRIMARY_LITTLE, data_reg */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, LDUHA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    case 1 | 4:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* ldsh [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDSH);
+#else
+        /* ldsha [arg0] ASI_PRIMARY_LITTLE, data_reg */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, LDSHA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    case 2:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* lduw [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDUW);
+#else
+        /* lduwa [arg0] ASI_PRIMARY_LITTLE, data_reg */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, LDUWA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    case 2 | 4:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* ldsw [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDSW);
+#else
+        /* ldswa [arg0] ASI_PRIMARY_LITTLE, data_reg */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, LDSWA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    case 3:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* ldx [arg0], data_reg */
+        tcg_out_ldst(s, data_reg, arg0, 0, LDX);
+#else
+        /* ldxa [arg0] ASI_PRIMARY_LITTLE, data_reg */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, LDXA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr = (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2) |
+                   INSN_OFF22((unsigned long)s->code_ptr -
+                              (unsigned long)label2_ptr));
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_reg, data_reg, arg0, arg1, arg2, mem_index, s_bits;
+#if defined(CONFIG_SOFTMMU)
+    uint32_t *label1_ptr, *label2_ptr;
+#endif
+
+    data_reg = *args++;
+    addr_reg = *args++;
+    mem_index = *args;
+
+    s_bits = opc;
+
+    arg0 = TCG_REG_O0;
+    arg1 = TCG_REG_O1;
+    arg2 = TCG_REG_O2;
+
+#if defined(CONFIG_SOFTMMU)
+    /* srl addr_reg, x, arg1 */
+    tcg_out_arithi(s, arg1, addr_reg, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS,
+                   SHIFT_SRL);
+
+    /* and addr_reg, x, arg0 */
+    tcg_out_arithi(s, arg0, addr_reg, TARGET_PAGE_MASK | ((1 << s_bits) - 1),
+                   ARITH_AND);
+
+    /* and arg1, x, arg1 */
+    tcg_out_andi(s, arg1, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+    /* add arg1, x, arg1 */
+    tcg_out_addi(s, arg1, offsetof(CPUState,
+                                   tlb_table[mem_index][0].addr_write));
+
+    /* add env, arg1, arg1 */
+    tcg_out_arith(s, arg1, TCG_AREG0, arg1, ARITH_ADD);
+
+    /* ld [arg1], arg2 */
+    tcg_out32(s, TARGET_LD_OP | INSN_RD(arg2) | INSN_RS1(arg1) |
+              INSN_RS2(TCG_REG_G0));
+
+    /* subcc arg0, arg2, %g0 */
+    tcg_out_arith(s, TCG_REG_G0, arg0, arg2, ARITH_SUBCC);
+
+    /* will become:
+       be label1 */
+    label1_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, 0);
+
+    /* mov (delay slot) */
+    tcg_out_mov(s, arg0, addr_reg);
+
+    /* mov */
+    tcg_out_mov(s, arg1, data_reg);
+
+    /* mov */
+    tcg_out_movi(s, TCG_TYPE_I32, arg2, mem_index);
+
+    /* XXX: move that code at the end of the TB */
+    /* qemu_st_helper[s_bits](arg0, arg1, arg2) */
+    tcg_out32(s, CALL | ((((tcg_target_ulong)qemu_st_helpers[s_bits]
+                           - (tcg_target_ulong)s->code_ptr) >> 2)
+                         & 0x3fffffff));
+    /* Store AREG0 in stack to avoid ugly glibc bugs that mangle
+       global registers */
+    // delay slot
+    tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+                 TCG_TARGET_CALL_STACK_OFFSET - sizeof(long), HOST_ST_OP);
+    tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+                 TCG_TARGET_CALL_STACK_OFFSET - sizeof(long), HOST_LD_OP);
+
+    /* will become:
+       ba label2 */
+    label2_ptr = (uint32_t *)s->code_ptr;
+    tcg_out32(s, 0);
+
+    /* nop (delay slot) */
+    tcg_out_nop(s);
+
+    /* label1: */
+    *label1_ptr = (INSN_OP(0) | INSN_COND(COND_E, 0) | INSN_OP2(0x2) |
+                   INSN_OFF22((unsigned long)s->code_ptr -
+                              (unsigned long)label1_ptr));
+
+    /* ld [arg1 + x], arg1 */
+    tcg_out_ldst(s, arg1, arg1, offsetof(CPUTLBEntry, addend) -
+                 offsetof(CPUTLBEntry, addr_write), TARGET_ADDEND_LD_OP);
+
+#if TARGET_LONG_BITS == 32
+    /* and addr_reg, x, arg0 */
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_I5, 0xffffffff);
+    tcg_out_arith(s, arg0, addr_reg, TCG_REG_I5, ARITH_AND);
+    /* add arg0, arg1, arg0 */
+    tcg_out_arith(s, arg0, arg0, arg1, ARITH_ADD);
+#else
+    /* add addr_reg, arg1, arg0 */
+    tcg_out_arith(s, arg0, addr_reg, arg1, ARITH_ADD);
+#endif
+
+#else
+    arg0 = addr_reg;
+#endif
+
+    switch(opc) {
+    case 0:
+        /* stb data_reg, [arg0] */
+        tcg_out_ldst(s, data_reg, arg0, 0, STB);
+        break;
+    case 1:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* sth data_reg, [arg0] */
+        tcg_out_ldst(s, data_reg, arg0, 0, STH);
+#else
+        /* stha data_reg, [arg0] ASI_PRIMARY_LITTLE */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, STHA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    case 2:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* stw data_reg, [arg0] */
+        tcg_out_ldst(s, data_reg, arg0, 0, STW);
+#else
+        /* stwa data_reg, [arg0] ASI_PRIMARY_LITTLE */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, STWA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    case 3:
+#ifdef TARGET_WORDS_BIGENDIAN
+        /* stx data_reg, [arg0] */
+        tcg_out_ldst(s, data_reg, arg0, 0, STX);
+#else
+        /* stxa data_reg, [arg0] ASI_PRIMARY_LITTLE */
+        tcg_out_ldst_asi(s, data_reg, arg0, 0, STXA, ASI_PRIMARY_LITTLE);
+#endif
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr = (INSN_OP(0) | INSN_COND(COND_A, 0) | INSN_OP2(0x2) |
+                   INSN_OFF22((unsigned long)s->code_ptr -
+                              (unsigned long)label2_ptr));
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args,
+                              const int *const_args)
+{
+    int c;
+
+    switch (opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_I0, args[0]);
+        tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I7) |
+                  INSN_IMM13(8));
+        tcg_out32(s, RESTORE | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_G0) |
+                      INSN_RS2(TCG_REG_G0));
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+            tcg_out_sethi(s, TCG_REG_I5, args[0] & 0xffffe000);
+            tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I5) |
+                      INSN_IMM13((args[0] & 0x1fff)));
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+        } else {
+            /* indirect jump method */
+            tcg_out_ld_ptr(s, TCG_REG_I5, (tcg_target_long)(s->tb_next + args[0]));
+            tcg_out32(s, JMPL | INSN_RD(TCG_REG_G0) | INSN_RS1(TCG_REG_I5) |
+                      INSN_RS2(TCG_REG_G0));
+        }
+        tcg_out_nop(s);
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_call:
+        if (const_args[0])
+            tcg_out32(s, CALL | ((((tcg_target_ulong)args[0]
+                                   - (tcg_target_ulong)s->code_ptr) >> 2)
+                                 & 0x3fffffff));
+        else {
+            tcg_out_ld_ptr(s, TCG_REG_I5,
+                           (tcg_target_long)(s->tb_next + args[0]));
+            tcg_out32(s, JMPL | INSN_RD(TCG_REG_O7) | INSN_RS1(TCG_REG_I5) |
+                      INSN_RS2(TCG_REG_G0));
+        }
+        /* Store AREG0 in stack to avoid ugly glibc bugs that mangle
+           global registers */
+        // delay slot
+        tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+                     TCG_TARGET_CALL_STACK_OFFSET - sizeof(long), HOST_ST_OP);
+        tcg_out_ldst(s, TCG_AREG0, TCG_REG_CALL_STACK,
+                     TCG_TARGET_CALL_STACK_OFFSET - sizeof(long), HOST_LD_OP);
+        break;
+    case INDEX_op_jmp:
+    case INDEX_op_br:
+        tcg_out_branch(s, COND_A, args[0]);
+        tcg_out_nop(s);
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]);
+        break;
+
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+#define OP_32_64(x)                             \
+        glue(glue(case INDEX_op_, x), _i32:)    \
+        glue(glue(case INDEX_op_, x), _i64:)
+#else
+#define OP_32_64(x)                             \
+        glue(glue(case INDEX_op_, x), _i32:)
+#endif
+        OP_32_64(ld8u);
+        tcg_out_ldst(s, args[0], args[1], args[2], LDUB);
+        break;
+        OP_32_64(ld8s);
+        tcg_out_ldst(s, args[0], args[1], args[2], LDSB);
+        break;
+        OP_32_64(ld16u);
+        tcg_out_ldst(s, args[0], args[1], args[2], LDUH);
+        break;
+        OP_32_64(ld16s);
+        tcg_out_ldst(s, args[0], args[1], args[2], LDSH);
+        break;
+    case INDEX_op_ld_i32:
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    case INDEX_op_ld32u_i64:
+#endif
+        tcg_out_ldst(s, args[0], args[1], args[2], LDUW);
+        break;
+        OP_32_64(st8);
+        tcg_out_ldst(s, args[0], args[1], args[2], STB);
+        break;
+        OP_32_64(st16);
+        tcg_out_ldst(s, args[0], args[1], args[2], STH);
+        break;
+    case INDEX_op_st_i32:
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    case INDEX_op_st32_i64:
+#endif
+        tcg_out_ldst(s, args[0], args[1], args[2], STW);
+        break;
+        OP_32_64(add);
+        c = ARITH_ADD;
+        goto gen_arith32;
+        OP_32_64(sub);
+        c = ARITH_SUB;
+        goto gen_arith32;
+        OP_32_64(and);
+        c = ARITH_AND;
+        goto gen_arith32;
+        OP_32_64(or);
+        c = ARITH_OR;
+        goto gen_arith32;
+        OP_32_64(xor);
+        c = ARITH_XOR;
+        goto gen_arith32;
+    case INDEX_op_shl_i32:
+        c = SHIFT_SLL;
+        goto gen_arith32;
+    case INDEX_op_shr_i32:
+        c = SHIFT_SRL;
+        goto gen_arith32;
+    case INDEX_op_sar_i32:
+        c = SHIFT_SRA;
+        goto gen_arith32;
+    case INDEX_op_mul_i32:
+        c = ARITH_UMUL;
+        goto gen_arith32;
+    case INDEX_op_div2_i32:
+#if defined(__sparc_v9__) || defined(__sparc_v8plus__)
+        c = ARITH_SDIVX;
+        goto gen_arith32;
+#else
+        tcg_out_sety(s, 0);
+        c = ARITH_SDIV;
+        goto gen_arith32;
+#endif
+    case INDEX_op_divu2_i32:
+#if defined(__sparc_v9__) || defined(__sparc_v8plus__)
+        c = ARITH_UDIVX;
+        goto gen_arith32;
+#else
+        tcg_out_sety(s, 0);
+        c = ARITH_UDIV;
+        goto gen_arith32;
+#endif
+
+    case INDEX_op_brcond_i32:
+        tcg_out_brcond(s, args[2], args[0], args[1], const_args[1],
+                       args[3]);
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, args, 2);
+        break;
+    case INDEX_op_qemu_ld32s:
+        tcg_out_qemu_ld(s, args, 2 | 4);
+        break;
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, args, 2);
+        break;
+
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    case INDEX_op_movi_i64:
+        tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]);
+        break;
+    case INDEX_op_ld32s_i64:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDSW);
+        break;
+    case INDEX_op_ld_i64:
+        tcg_out_ldst(s, args[0], args[1], args[2], LDX);
+        break;
+    case INDEX_op_st_i64:
+        tcg_out_ldst(s, args[0], args[1], args[2], STX);
+        break;
+    case INDEX_op_shl_i64:
+        c = SHIFT_SLLX;
+        goto gen_arith32;
+    case INDEX_op_shr_i64:
+        c = SHIFT_SRLX;
+        goto gen_arith32;
+    case INDEX_op_sar_i64:
+        c = SHIFT_SRAX;
+        goto gen_arith32;
+    case INDEX_op_mul_i64:
+        c = ARITH_MULX;
+        goto gen_arith32;
+    case INDEX_op_div2_i64:
+        c = ARITH_SDIVX;
+        goto gen_arith32;
+    case INDEX_op_divu2_i64:
+        c = ARITH_UDIVX;
+        goto gen_arith32;
+
+    case INDEX_op_brcond_i64:
+        tcg_out_brcond(s, args[2], args[0], args[1], const_args[1],
+                       args[3]);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld(s, args, 3);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st(s, args, 3);
+        break;
+
+#endif
+    gen_arith32:
+        if (const_args[2]) {
+            tcg_out_arithi(s, args[0], args[1], args[2], c);
+        } else {
+            tcg_out_arith(s, args[0], args[1], args[2], c);
+        }
+        break;
+
+    default:
+        fprintf(stderr, "unknown opcode 0x%x\n", opc);
+        tcg_abort();
+    }
+}
+
+static const TCGTargetOpDef sparc_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "ri" } },
+    { INDEX_op_jmp, { "ri" } },
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "r", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+
+    { INDEX_op_add_i32, { "r", "r", "rJ" } },
+    { INDEX_op_mul_i32, { "r", "r", "rJ" } },
+    { INDEX_op_div2_i32, { "r", "r", "0", "1", "r" } },
+    { INDEX_op_divu2_i32, { "r", "r", "0", "1", "r" } },
+    { INDEX_op_sub_i32, { "r", "r", "rJ" } },
+    { INDEX_op_and_i32, { "r", "r", "rJ" } },
+    { INDEX_op_or_i32, { "r", "r", "rJ" } },
+    { INDEX_op_xor_i32, { "r", "r", "rJ" } },
+
+    { INDEX_op_shl_i32, { "r", "r", "rJ" } },
+    { INDEX_op_shr_i32, { "r", "r", "rJ" } },
+    { INDEX_op_sar_i32, { "r", "r", "rJ" } },
+
+    { INDEX_op_brcond_i32, { "r", "ri" } },
+
+    { INDEX_op_qemu_ld8u, { "r", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L" } },
+    { INDEX_op_qemu_ld32s, { "r", "L" } },
+
+    { INDEX_op_qemu_st8, { "L", "L" } },
+    { INDEX_op_qemu_st16, { "L", "L" } },
+    { INDEX_op_qemu_st32, { "L", "L" } },
+
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    { INDEX_op_mov_i64, { "r", "r" } },
+    { INDEX_op_movi_i64, { "r" } },
+    { INDEX_op_ld8u_i64, { "r", "r" } },
+    { INDEX_op_ld8s_i64, { "r", "r" } },
+    { INDEX_op_ld16u_i64, { "r", "r" } },
+    { INDEX_op_ld16s_i64, { "r", "r" } },
+    { INDEX_op_ld32u_i64, { "r", "r" } },
+    { INDEX_op_ld32s_i64, { "r", "r" } },
+    { INDEX_op_ld_i64, { "r", "r" } },
+    { INDEX_op_st8_i64, { "r", "r" } },
+    { INDEX_op_st16_i64, { "r", "r" } },
+    { INDEX_op_st32_i64, { "r", "r" } },
+    { INDEX_op_st_i64, { "r", "r" } },
+    { INDEX_op_qemu_ld64, { "L", "L" } },
+    { INDEX_op_qemu_st64, { "L", "L" } },
+
+    { INDEX_op_add_i64, { "r", "r", "rJ" } },
+    { INDEX_op_mul_i64, { "r", "r", "rJ" } },
+    { INDEX_op_div2_i64, { "r", "r", "0", "1", "r" } },
+    { INDEX_op_divu2_i64, { "r", "r", "0", "1", "r" } },
+    { INDEX_op_sub_i64, { "r", "r", "rJ" } },
+    { INDEX_op_and_i64, { "r", "r", "rJ" } },
+    { INDEX_op_or_i64, { "r", "r", "rJ" } },
+    { INDEX_op_xor_i64, { "r", "r", "rJ" } },
+
+    { INDEX_op_shl_i64, { "r", "r", "rJ" } },
+    { INDEX_op_shr_i64, { "r", "r", "rJ" } },
+    { INDEX_op_sar_i64, { "r", "r", "rJ" } },
+
+    { INDEX_op_brcond_i64, { "r", "ri" } },
+#endif
+    { -1 },
+};
+
+void tcg_target_init(TCGContext *s)
+{
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffffffff);
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffffffff);
+#endif
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_G1) |
+                     (1 << TCG_REG_G2) |
+                     (1 << TCG_REG_G3) |
+                     (1 << TCG_REG_G4) |
+                     (1 << TCG_REG_G5) |
+                     (1 << TCG_REG_G6) |
+                     (1 << TCG_REG_G7) |
+                     (1 << TCG_REG_O0) |
+                     (1 << TCG_REG_O1) |
+                     (1 << TCG_REG_O2) |
+                     (1 << TCG_REG_O3) |
+                     (1 << TCG_REG_O4) |
+                     (1 << TCG_REG_O5) |
+                     (1 << TCG_REG_O7));
+
+    tcg_regset_clear(s->reserved_regs);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_G0);
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_I4); // for internal use
+#endif
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_I5); // for internal use
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_I6);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_I7);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_O6);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_O7);
+    tcg_add_target_add_op_defs(sparc_op_defs);
+}
diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h
new file mode 100644
index 0000000..8dc07d3
--- /dev/null
+++ b/tcg/sparc/tcg-target.h
@@ -0,0 +1,122 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_SPARC 1
+
+#if defined(__sparc_v9__) && !defined(__sparc_v8plus__)
+#define TCG_TARGET_REG_BITS 64
+#else
+#define TCG_TARGET_REG_BITS 32
+#endif
+
+#define TCG_TARGET_WORDS_BIGENDIAN
+
+#define TCG_TARGET_NB_REGS 32
+
+enum {
+    TCG_REG_G0 = 0,
+    TCG_REG_G1,
+    TCG_REG_G2,
+    TCG_REG_G3,
+    TCG_REG_G4,
+    TCG_REG_G5,
+    TCG_REG_G6,
+    TCG_REG_G7,
+    TCG_REG_O0,
+    TCG_REG_O1,
+    TCG_REG_O2,
+    TCG_REG_O3,
+    TCG_REG_O4,
+    TCG_REG_O5,
+    TCG_REG_O6,
+    TCG_REG_O7,
+    TCG_REG_L0,
+    TCG_REG_L1,
+    TCG_REG_L2,
+    TCG_REG_L3,
+    TCG_REG_L4,
+    TCG_REG_L5,
+    TCG_REG_L6,
+    TCG_REG_L7,
+    TCG_REG_I0,
+    TCG_REG_I1,
+    TCG_REG_I2,
+    TCG_REG_I3,
+    TCG_REG_I4,
+    TCG_REG_I5,
+    TCG_REG_I6,
+    TCG_REG_I7,
+};
+
+#define TCG_CT_CONST_S11 0x100
+#define TCG_CT_CONST_S13 0x200
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_I6
+#ifdef __arch64__
+// Reserve space for AREG0
+#define TCG_TARGET_STACK_MINFRAME (176 + 2 * (int)sizeof(long))
+#define TCG_TARGET_CALL_STACK_OFFSET (2047 + TCG_TARGET_STACK_MINFRAME)
+#define TCG_TARGET_STACK_ALIGN 16
+#else
+// AREG0 + one word for alignment
+#define TCG_TARGET_STACK_MINFRAME (92 + (2 + 1) * (int)sizeof(long))
+#define TCG_TARGET_CALL_STACK_OFFSET TCG_TARGET_STACK_MINFRAME
+#define TCG_TARGET_STACK_ALIGN 8
+#endif
+
+/* optional instructions */
+//#define TCG_TARGET_HAS_bswap_i32
+//#define TCG_TARGET_HAS_bswap_i64
+//#define TCG_TARGET_HAS_neg_i32
+//#define TCG_TARGET_HAS_neg_i64
+
+
+/* Note: must be synced with dyngen-exec.h and Makefile.target */
+#ifdef HOST_SOLARIS
+#define TCG_AREG0 TCG_REG_G2
+#define TCG_AREG1 TCG_REG_G3
+#define TCG_AREG2 TCG_REG_G4
+#define TCG_AREG3 TCG_REG_G5
+#define TCG_AREG4 TCG_REG_G6
+#elif defined(__sparc_v9__)
+#define TCG_AREG0 TCG_REG_G5
+#define TCG_AREG1 TCG_REG_G6
+#define TCG_AREG2 TCG_REG_G7
+#else
+#define TCG_AREG0 TCG_REG_G6
+#define TCG_AREG1 TCG_REG_G1
+#define TCG_AREG2 TCG_REG_G2
+#define TCG_AREG3 TCG_REG_G3
+#endif
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    unsigned long p;
+
+    p = start & ~(8UL - 1UL);
+    stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
+
+    for (; p < stop; p += 8)
+        __asm__ __volatile__("flush\t%0" : : "r" (p));
+}
diff --git a/tcg/tcg-dyngen.c b/tcg/tcg-dyngen.c
new file mode 100644
index 0000000..b4ceb5e
--- /dev/null
+++ b/tcg/tcg-dyngen.c
@@ -0,0 +1,431 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "osdep.h"
+
+#include "tcg.h"
+
+int __op_param1, __op_param2, __op_param3;
+#if defined(__sparc__) || defined(__arm__)
+  void __op_gen_label1(){}
+  void __op_gen_label2(){}
+  void __op_gen_label3(){}
+#else
+  int __op_gen_label1, __op_gen_label2, __op_gen_label3;
+#endif
+int __op_jmp0, __op_jmp1, __op_jmp2, __op_jmp3;
+
+#if 0
+#if defined(__s390__)
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
+#elif defined(__ia64__)
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    while (start < stop) {
+	asm volatile ("fc %0" :: "r"(start));
+	start += 32;
+    }
+    asm volatile (";;sync.i;;srlz.i;;");
+}
+#elif defined(__powerpc__)
+
+#define MIN_CACHE_LINE_SIZE 8 /* conservative value */
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    unsigned long p;
+
+    start &= ~(MIN_CACHE_LINE_SIZE - 1);
+    stop = (stop + MIN_CACHE_LINE_SIZE - 1) & ~(MIN_CACHE_LINE_SIZE - 1);
+
+    for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+        asm volatile ("dcbst 0,%0" : : "r"(p) : "memory");
+    }
+    asm volatile ("sync" : : : "memory");
+    for (p = start; p < stop; p += MIN_CACHE_LINE_SIZE) {
+        asm volatile ("icbi 0,%0" : : "r"(p) : "memory");
+    }
+    asm volatile ("sync" : : : "memory");
+    asm volatile ("isync" : : : "memory");
+}
+#elif defined(__alpha__)
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    asm ("imb");
+}
+#elif defined(__sparc__)
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+	unsigned long p;
+
+	p = start & ~(8UL - 1UL);
+	stop = (stop + (8UL - 1UL)) & ~(8UL - 1UL);
+
+	for (; p < stop; p += 8)
+		__asm__ __volatile__("flush\t%0" : : "r" (p));
+}
+#elif defined(__arm__)
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    register unsigned long _beg __asm ("a1") = start;
+    register unsigned long _end __asm ("a2") = stop;
+    register unsigned long _flg __asm ("a3") = 0;
+    __asm __volatile__ ("swi 0x9f0002" : : "r" (_beg), "r" (_end), "r" (_flg));
+}
+#elif defined(__mc68000)
+
+# include <asm/cachectl.h>
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    cacheflush(start,FLUSH_SCOPE_LINE,FLUSH_CACHE_BOTH,stop-start+16);
+}
+#elif defined(__mips__)
+
+#include <sys/cachectl.h>
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+    _flush_cache ((void *)start, stop - start, BCACHE);
+}
+#else
+#error unsupported CPU
+#endif
+
+#ifdef __alpha__
+
+register int gp asm("$29");
+
+static inline void immediate_ldah(void *p, int val) {
+    uint32_t *dest = p;
+    long high = ((val >> 16) + ((val >> 15) & 1)) & 0xffff;
+
+    *dest &= ~0xffff;
+    *dest |= high;
+    *dest |= 31 << 16;
+}
+static inline void immediate_lda(void *dest, int val) {
+    *(uint16_t *) dest = val;
+}
+void fix_bsr(void *p, int offset) {
+    uint32_t *dest = p;
+    *dest &= ~((1 << 21) - 1);
+    *dest |= (offset >> 2) & ((1 << 21) - 1);
+}
+
+#endif /* __alpha__ */
+
+#ifdef __ia64
+
+/* Patch instruction with "val" where "mask" has 1 bits. */
+static inline void ia64_patch (uint64_t insn_addr, uint64_t mask, uint64_t val)
+{
+    uint64_t m0, m1, v0, v1, b0, b1, *b = (uint64_t *) (insn_addr & -16);
+#   define insn_mask ((1UL << 41) - 1)
+    unsigned long shift;
+
+    b0 = b[0]; b1 = b[1];
+    shift = 5 + 41 * (insn_addr % 16); /* 5 template, 3 x 41-bit insns */
+    if (shift >= 64) {
+	m1 = mask << (shift - 64);
+	v1 = val << (shift - 64);
+    } else {
+	m0 = mask << shift; m1 = mask >> (64 - shift);
+	v0 = val  << shift; v1 = val >> (64 - shift);
+	b[0] = (b0 & ~m0) | (v0 & m0);
+    }
+    b[1] = (b1 & ~m1) | (v1 & m1);
+}
+
+static inline void ia64_patch_imm60 (uint64_t insn_addr, uint64_t val)
+{
+	ia64_patch(insn_addr,
+		   0x011ffffe000UL,
+		   (  ((val & 0x0800000000000000UL) >> 23) /* bit 59 -> 36 */
+		    | ((val & 0x00000000000fffffUL) << 13) /* bit 0 -> 13 */));
+	ia64_patch(insn_addr - 1, 0x1fffffffffcUL, val >> 18);
+}
+
+static inline void ia64_imm64 (void *insn, uint64_t val)
+{
+    /* Ignore the slot number of the relocation; GCC and Intel
+       toolchains differed for some time on whether IMM64 relocs are
+       against slot 1 (Intel) or slot 2 (GCC).  */
+    uint64_t insn_addr = (uint64_t) insn & ~3UL;
+
+    ia64_patch(insn_addr + 2,
+	       0x01fffefe000UL,
+	       (  ((val & 0x8000000000000000UL) >> 27) /* bit 63 -> 36 */
+		| ((val & 0x0000000000200000UL) <<  0) /* bit 21 -> 21 */
+		| ((val & 0x00000000001f0000UL) <<  6) /* bit 16 -> 22 */
+		| ((val & 0x000000000000ff80UL) << 20) /* bit  7 -> 27 */
+		| ((val & 0x000000000000007fUL) << 13) /* bit  0 -> 13 */)
+	    );
+    ia64_patch(insn_addr + 1, 0x1ffffffffffUL, val >> 22);
+}
+
+static inline void ia64_imm60b (void *insn, uint64_t val)
+{
+    /* Ignore the slot number of the relocation; GCC and Intel
+       toolchains differed for some time on whether IMM64 relocs are
+       against slot 1 (Intel) or slot 2 (GCC).  */
+    uint64_t insn_addr = (uint64_t) insn & ~3UL;
+
+    if (val + ((uint64_t) 1 << 59) >= (1UL << 60))
+	fprintf(stderr, "%s: value %ld out of IMM60 range\n",
+		__FUNCTION__, (int64_t) val);
+    ia64_patch_imm60(insn_addr + 2, val);
+}
+
+static inline void ia64_imm22 (void *insn, uint64_t val)
+{
+    if (val + (1 << 21) >= (1 << 22))
+	fprintf(stderr, "%s: value %li out of IMM22 range\n",
+		__FUNCTION__, (int64_t)val);
+    ia64_patch((uint64_t) insn, 0x01fffcfe000UL,
+	       (  ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
+		| ((val & 0x1f0000UL) <<  6) /* bit 16 -> 22 */
+		| ((val & 0x00ff80UL) << 20) /* bit  7 -> 27 */
+		| ((val & 0x00007fUL) << 13) /* bit  0 -> 13 */));
+}
+
+/* Like ia64_imm22(), but also clear bits 20-21.  For addl, this has
+   the effect of turning "addl rX=imm22,rY" into "addl
+   rX=imm22,r0".  */
+static inline void ia64_imm22_r0 (void *insn, uint64_t val)
+{
+    if (val + (1 << 21) >= (1 << 22))
+	fprintf(stderr, "%s: value %li out of IMM22 range\n",
+		__FUNCTION__, (int64_t)val);
+    ia64_patch((uint64_t) insn, 0x01fffcfe000UL | (0x3UL << 20),
+	       (  ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
+		| ((val & 0x1f0000UL) <<  6) /* bit 16 -> 22 */
+		| ((val & 0x00ff80UL) << 20) /* bit  7 -> 27 */
+		| ((val & 0x00007fUL) << 13) /* bit  0 -> 13 */));
+}
+
+static inline void ia64_imm21b (void *insn, uint64_t val)
+{
+    if (val + (1 << 20) >= (1 << 21))
+	fprintf(stderr, "%s: value %li out of IMM21b range\n",
+		__FUNCTION__, (int64_t)val);
+    ia64_patch((uint64_t) insn, 0x11ffffe000UL,
+	       (  ((val & 0x100000UL) << 16) /* bit 20 -> 36 */
+		| ((val & 0x0fffffUL) << 13) /* bit  0 -> 13 */));
+}
+
+static inline void ia64_nop_b (void *insn)
+{
+    ia64_patch((uint64_t) insn, (1UL << 41) - 1, 2UL << 37);
+}
+
+static inline void ia64_ldxmov(void *insn, uint64_t val)
+{
+    if (val + (1 << 21) < (1 << 22))
+	ia64_patch((uint64_t) insn, 0x1fff80fe000UL, 8UL << 37);
+}
+
+static inline int ia64_patch_ltoff(void *insn, uint64_t val,
+				   int relaxable)
+{
+    if (relaxable && (val + (1 << 21) < (1 << 22))) {
+	ia64_imm22_r0(insn, val);
+	return 0;
+    }
+    return 1;
+}
+
+struct ia64_fixup {
+    struct ia64_fixup *next;
+    void *addr;			/* address that needs to be patched */
+    long value;
+};
+
+#define IA64_PLT(insn, plt_index)			\
+do {							\
+    struct ia64_fixup *fixup = alloca(sizeof(*fixup));	\
+    fixup->next = plt_fixes;				\
+    plt_fixes = fixup;					\
+    fixup->addr = (insn);				\
+    fixup->value = (plt_index);				\
+    plt_offset[(plt_index)] = 1;			\
+} while (0)
+
+#define IA64_LTOFF(insn, val, relaxable)			\
+do {								\
+    if (ia64_patch_ltoff(insn, val, relaxable)) {		\
+	struct ia64_fixup *fixup = alloca(sizeof(*fixup));	\
+	fixup->next = ltoff_fixes;				\
+	ltoff_fixes = fixup;					\
+	fixup->addr = (insn);					\
+	fixup->value = (val);					\
+    }								\
+} while (0)
+
+static inline void ia64_apply_fixes (uint8_t **gen_code_pp,
+				     struct ia64_fixup *ltoff_fixes,
+				     uint64_t gp,
+				     struct ia64_fixup *plt_fixes,
+				     int num_plts,
+				     unsigned long *plt_target,
+				     unsigned int *plt_offset)
+{
+    static const uint8_t plt_bundle[] = {
+	0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	/* nop 0; movl r1=GP */
+	0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x60,
+
+	0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	/* nop 0; brl IP */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0
+    };
+    uint8_t *gen_code_ptr = *gen_code_pp, *plt_start, *got_start;
+    uint64_t *vp;
+    struct ia64_fixup *fixup;
+    unsigned int offset = 0;
+    struct fdesc {
+	long ip;
+	long gp;
+    } *fdesc;
+    int i;
+
+    if (plt_fixes) {
+	plt_start = gen_code_ptr;
+
+	for (i = 0; i < num_plts; ++i) {
+	    if (plt_offset[i]) {
+		plt_offset[i] = offset;
+		offset += sizeof(plt_bundle);
+
+		fdesc = (struct fdesc *) plt_target[i];
+		memcpy(gen_code_ptr, plt_bundle, sizeof(plt_bundle));
+		ia64_imm64 (gen_code_ptr + 0x02, fdesc->gp);
+		ia64_imm60b(gen_code_ptr + 0x12,
+			    (fdesc->ip - (long) (gen_code_ptr + 0x10)) >> 4);
+		gen_code_ptr += sizeof(plt_bundle);
+	    }
+	}
+
+	for (fixup = plt_fixes; fixup; fixup = fixup->next)
+	    ia64_imm21b(fixup->addr,
+			((long) plt_start + plt_offset[fixup->value]
+			 - ((long) fixup->addr & ~0xf)) >> 4);
+    }
+
+    got_start = gen_code_ptr;
+
+    /* First, create the GOT: */
+    for (fixup = ltoff_fixes; fixup; fixup = fixup->next) {
+	/* first check if we already have this value in the GOT: */
+	for (vp = (uint64_t *) got_start; vp < (uint64_t *) gen_code_ptr; ++vp)
+	    if (*vp == fixup->value)
+		break;
+	if (vp == (uint64_t *) gen_code_ptr) {
+	    /* Nope, we need to put the value in the GOT: */
+	    *vp = fixup->value;
+	    gen_code_ptr += 8;
+	}
+	ia64_imm22(fixup->addr, (long) vp - gp);
+    }
+    /* Keep code ptr aligned. */
+    if ((long) gen_code_ptr & 15)
+	gen_code_ptr += 8;
+    *gen_code_pp = gen_code_ptr;
+}
+#endif
+#endif
+
+#ifdef CONFIG_DYNGEN_OP
+
+#if defined __hppa__
+struct hppa_branch_stub {
+    uint32_t *location;
+    long target;
+    struct hppa_branch_stub *next;
+};
+
+#define HPPA_RECORD_BRANCH(LIST, LOC, TARGET) \
+do { \
+    struct hppa_branch_stub *stub = alloca(sizeof(struct hppa_branch_stub)); \
+    stub->location = LOC; \
+    stub->target = TARGET; \
+    stub->next = LIST; \
+    LIST = stub; \
+} while (0)
+
+static inline void hppa_process_stubs(struct hppa_branch_stub *stub,
+                                      uint8_t **gen_code_pp)
+{
+    uint32_t *s = (uint32_t *)*gen_code_pp;
+    uint32_t *p = s + 1;
+
+    if (!stub) return;
+
+    for (; stub != NULL; stub = stub->next) {
+        unsigned long l = (unsigned long)p;
+        /* stub:
+         * ldil L'target, %r1
+         * be,n R'target(%sr4,%r1)
+         */
+        *p++ = 0x20200000 | reassemble_21(lrsel(stub->target, 0));
+        *p++ = 0xe0202002 | (reassemble_17(rrsel(stub->target, 0) >> 2));
+        hppa_patch17f(stub->location, l, 0);
+    }
+    /* b,l,n stub,%r0 */
+    *s = 0xe8000002 | reassemble_17((p - s) - 2);
+    *gen_code_pp = (uint8_t *)p;
+}
+#endif /* __hppa__ */
+
+const TCGArg *dyngen_op(TCGContext *s, int opc, const TCGArg *opparam_ptr)
+{
+    uint8_t *gen_code_ptr;
+
+#ifdef __hppa__
+    struct hppa_branch_stub *hppa_stubs = NULL;
+#endif
+
+    gen_code_ptr = s->code_ptr;
+    switch(opc) {
+
+/* op.h is dynamically generated by dyngen.c from op.c */
+#include "op.h"
+
+    default:
+        tcg_abort();
+    }
+
+#ifdef __hppa__
+    hppa_process_stubs(hppa_stubs, &gen_code_ptr);
+#endif
+
+    s->code_ptr = gen_code_ptr;
+    return opparam_ptr;
+}
+#endif
diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h
new file mode 100644
index 0000000..bc6be85
--- /dev/null
+++ b/tcg/tcg-op.h
@@ -0,0 +1,1713 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "tcg.h"
+
+#ifdef CONFIG_DYNGEN_OP
+/* legacy dyngen operations */
+#include "gen-op.h"
+#endif
+
+int gen_new_label(void);
+
+static inline void tcg_gen_op1(int opc, TCGv arg1)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+}
+
+static inline void tcg_gen_op1i(int opc, TCGArg arg1)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = arg1;
+}
+
+static inline void tcg_gen_op2(int opc, TCGv arg1, TCGv arg2)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+}
+
+static inline void tcg_gen_op2i(int opc, TCGv arg1, TCGArg arg2)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = arg2;
+}
+
+static inline void tcg_gen_op2ii(int opc, TCGArg arg1, TCGArg arg2)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = arg1;
+    *gen_opparam_ptr++ = arg2;
+}
+
+static inline void tcg_gen_op3(int opc, TCGv arg1, TCGv arg2, TCGv arg3)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+}
+
+static inline void tcg_gen_op3i(int opc, TCGv arg1, TCGv arg2, TCGArg arg3)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = arg3;
+}
+
+static inline void tcg_gen_op4(int opc, TCGv arg1, TCGv arg2, TCGv arg3, 
+                               TCGv arg4)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+    *gen_opparam_ptr++ = GET_TCGV(arg4);
+}
+
+static inline void tcg_gen_op4i(int opc, TCGv arg1, TCGv arg2, TCGv arg3, 
+                                TCGArg arg4)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+    *gen_opparam_ptr++ = arg4;
+}
+
+static inline void tcg_gen_op4ii(int opc, TCGv arg1, TCGv arg2, TCGArg arg3, 
+                                 TCGArg arg4)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = arg3;
+    *gen_opparam_ptr++ = arg4;
+}
+
+static inline void tcg_gen_op5(int opc, TCGv arg1, TCGv arg2, 
+                               TCGv arg3, TCGv arg4,
+                               TCGv arg5)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+    *gen_opparam_ptr++ = GET_TCGV(arg4);
+    *gen_opparam_ptr++ = GET_TCGV(arg5);
+}
+
+static inline void tcg_gen_op5i(int opc, TCGv arg1, TCGv arg2, 
+                                TCGv arg3, TCGv arg4,
+                                TCGArg arg5)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+    *gen_opparam_ptr++ = GET_TCGV(arg4);
+    *gen_opparam_ptr++ = arg5;
+}
+
+static inline void tcg_gen_op6(int opc, TCGv arg1, TCGv arg2, 
+                               TCGv arg3, TCGv arg4,
+                               TCGv arg5, TCGv arg6)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+    *gen_opparam_ptr++ = GET_TCGV(arg4);
+    *gen_opparam_ptr++ = GET_TCGV(arg5);
+    *gen_opparam_ptr++ = GET_TCGV(arg6);
+}
+
+static inline void tcg_gen_op6ii(int opc, TCGv arg1, TCGv arg2, 
+                                 TCGv arg3, TCGv arg4,
+                                 TCGArg arg5, TCGArg arg6)
+{
+    *gen_opc_ptr++ = opc;
+    *gen_opparam_ptr++ = GET_TCGV(arg1);
+    *gen_opparam_ptr++ = GET_TCGV(arg2);
+    *gen_opparam_ptr++ = GET_TCGV(arg3);
+    *gen_opparam_ptr++ = GET_TCGV(arg4);
+    *gen_opparam_ptr++ = arg5;
+    *gen_opparam_ptr++ = arg6;
+}
+
+static inline void gen_set_label(int n)
+{
+    tcg_gen_op1i(INDEX_op_set_label, n);
+}
+
+static inline void tcg_gen_br(int label)
+{
+    tcg_gen_op1i(INDEX_op_br, label);
+}
+
+static inline void tcg_gen_mov_i32(TCGv ret, TCGv arg)
+{
+    if (GET_TCGV(ret) != GET_TCGV(arg))
+        tcg_gen_op2(INDEX_op_mov_i32, ret, arg);
+}
+
+static inline void tcg_gen_movi_i32(TCGv ret, int32_t arg)
+{
+    tcg_gen_op2i(INDEX_op_movi_i32, ret, arg);
+}
+
+/* helper calls */
+#define TCG_HELPER_CALL_FLAGS 0
+
+static inline void tcg_gen_helper_0_0(void *func)
+{
+    TCGv t0;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx, 
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 0, NULL, 0, NULL);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_0_1(void *func, TCGv arg)
+{
+    TCGv t0;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 0, NULL, 1, &arg);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_0_2(void *func, TCGv arg1, TCGv arg2)
+{
+    TCGv args[2];
+    TCGv t0;
+    args[0] = arg1;
+    args[1] = arg2;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx, 
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 0, NULL, 2, args);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_0_3(void *func,
+                                      TCGv arg1, TCGv arg2, TCGv arg3)
+{
+    TCGv args[3];
+    TCGv t0;
+    args[0] = arg1;
+    args[1] = arg2;
+    args[2] = arg3;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 0, NULL, 3, args);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_0_4(void *func, TCGv arg1, TCGv arg2,
+                                      TCGv arg3, TCGv arg4)
+{
+    TCGv args[4];
+    TCGv t0;
+    args[0] = arg1;
+    args[1] = arg2;
+    args[2] = arg3;
+    args[3] = arg4;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 0, NULL, 4, args);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_1_0(void *func, TCGv ret)
+{
+    TCGv t0;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 1, &ret, 0, NULL);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_1_1(void *func, TCGv ret, TCGv arg1)
+{
+    TCGv t0;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 1, &ret, 1, &arg1);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_1_2(void *func, TCGv ret, 
+                                      TCGv arg1, TCGv arg2)
+{
+    TCGv args[2];
+    TCGv t0;
+    args[0] = arg1;
+    args[1] = arg2;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx, 
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 1, &ret, 2, args);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_1_3(void *func, TCGv ret,
+                                      TCGv arg1, TCGv arg2, TCGv arg3)
+{
+    TCGv args[3];
+    TCGv t0;
+    args[0] = arg1;
+    args[1] = arg2;
+    args[2] = arg3;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 1, &ret, 3, args);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_helper_1_4(void *func, TCGv ret,
+                                      TCGv arg1, TCGv arg2, TCGv arg3,
+                                      TCGv arg4)
+{
+    TCGv args[4];
+    TCGv t0;
+    args[0] = arg1;
+    args[1] = arg2;
+    args[2] = arg3;
+    args[3] = arg4;
+    t0 = tcg_const_ptr((tcg_target_long)func);
+    tcg_gen_call(&tcg_ctx,
+                 t0, TCG_HELPER_CALL_FLAGS,
+                 1, &ret, 4, args);
+    tcg_temp_free(t0);
+}
+
+/* 32 bit ops */
+
+static inline void tcg_gen_ld8u_i32(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld8u_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld8s_i32(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld8s_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld16u_i32(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld16u_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld16s_i32(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld16s_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld_i32(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld_i32, ret, arg2, offset);
+}
+
+static inline void tcg_gen_st8_i32(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st8_i32, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st16_i32(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st16_i32, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st_i32(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st_i32, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_add_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_add_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_addi_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    /* some cases can be optimized here */
+    if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_add_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_sub_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_sub_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_subi_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    /* some cases can be optimized here */
+    if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_sub_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_and_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_and_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_andi_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    /* some cases can be optimized here */
+    if (arg2 == 0) {
+        tcg_gen_movi_i32(ret, 0);
+    } else if (arg2 == 0xffffffff) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_and_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_or_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_or_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_ori_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    /* some cases can be optimized here */
+    if (arg2 == 0xffffffff) {
+        tcg_gen_movi_i32(ret, 0xffffffff);
+    } else if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_or_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_xor_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_xor_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_xori_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    /* some cases can be optimized here */
+    if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_xor_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_shl_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_shl_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shli_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_shl_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_shr_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_shr_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shri_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_shr_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_sar_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_sar_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sari_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    if (arg2 == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i32(arg2);
+        tcg_gen_sar_i32(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_brcond_i32(int cond, TCGv arg1, TCGv arg2, 
+                                      int label_index)
+{
+    tcg_gen_op4ii(INDEX_op_brcond_i32, arg1, arg2, cond, label_index);
+}
+
+static inline void tcg_gen_brcondi_i32(int cond, TCGv arg1, int32_t arg2, 
+                                       int label_index)
+{
+    TCGv t0 = tcg_const_i32(arg2);
+    tcg_gen_brcond_i32(cond, arg1, t0, label_index);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_mul_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_mul_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_muli_i32(TCGv ret, TCGv arg1, int32_t arg2)
+{
+    TCGv t0 = tcg_const_i32(arg2);
+    tcg_gen_mul_i32(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+#ifdef TCG_TARGET_HAS_div_i32
+static inline void tcg_gen_div_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_div_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_rem_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_divu_i32, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_remu_i32, ret, arg1, arg2);
+}
+#else
+static inline void tcg_gen_div_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    tcg_gen_sari_i32(t0, arg1, 31);
+    tcg_gen_op5(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_rem_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    tcg_gen_sari_i32(t0, arg1, 31);
+    tcg_gen_op5(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_divu_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    tcg_gen_movi_i32(t0, 0);
+    tcg_gen_op5(INDEX_op_divu2_i32, ret, t0, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_remu_i32(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    tcg_gen_movi_i32(t0, 0);
+    tcg_gen_op5(INDEX_op_divu2_i32, t0, ret, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+#endif
+
+#if TCG_TARGET_REG_BITS == 32
+
+static inline void tcg_gen_mov_i64(TCGv ret, TCGv arg)
+{
+    if (GET_TCGV(ret) != GET_TCGV(arg)) {
+        tcg_gen_mov_i32(ret, arg);
+        tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg));
+    }
+}
+
+static inline void tcg_gen_movi_i64(TCGv ret, int64_t arg)
+{
+    tcg_gen_movi_i32(ret, arg);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), arg >> 32);
+}
+
+static inline void tcg_gen_ld8u_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_ld8u_i32(ret, arg2, offset);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ld8s_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_ld8s_i32(ret, arg2, offset);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_ld16u_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_ld16u_i32(ret, arg2, offset);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ld16s_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_ld16s_i32(ret, arg2, offset);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_ld32u_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_ld_i32(ret, arg2, offset);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ld32s_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_ld_i32(ret, arg2, offset);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_ld_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    /* since arg2 and ret have different types, they cannot be the
+       same temporary */
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+    tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset);
+    tcg_gen_ld_i32(ret, arg2, offset + 4);
+#else
+    tcg_gen_ld_i32(ret, arg2, offset);
+    tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset + 4);
+#endif
+}
+
+static inline void tcg_gen_st8_i64(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_st8_i32(arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st16_i64(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_st16_i32(arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st32_i64(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_st_i32(arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st_i64(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+    tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset);
+    tcg_gen_st_i32(arg1, arg2, offset + 4);
+#else
+    tcg_gen_st_i32(arg1, arg2, offset);
+    tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset + 4);
+#endif
+}
+
+static inline void tcg_gen_add_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op6(INDEX_op_add2_i32, ret, TCGV_HIGH(ret), 
+                arg1, TCGV_HIGH(arg1), arg2, TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_addi_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_add_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_sub_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op6(INDEX_op_sub2_i32, ret, TCGV_HIGH(ret), 
+                arg1, TCGV_HIGH(arg1), arg2, TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_subi_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_sub_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_and_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_and_i32(ret, arg1, arg2);
+    tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_andi_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    tcg_gen_andi_i32(ret, arg1, arg2);
+    tcg_gen_andi_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32);
+}
+
+static inline void tcg_gen_or_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_or_i32(ret, arg1, arg2);
+    tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_ori_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    tcg_gen_ori_i32(ret, arg1, arg2);
+    tcg_gen_ori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32);
+}
+
+static inline void tcg_gen_xor_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_xor_i32(ret, arg1, arg2);
+    tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2));
+}
+
+static inline void tcg_gen_xori_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    tcg_gen_xori_i32(ret, arg1, arg2);
+    tcg_gen_xori_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), arg2 >> 32);
+}
+
+/* XXX: use generic code when basic block handling is OK or CPU
+   specific code (x86) */
+static inline void tcg_gen_shl_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_shl_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shli_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    tcg_gen_shifti_i64(ret, arg1, arg2, 0, 0);
+}
+
+static inline void tcg_gen_shr_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_shr_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shri_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    tcg_gen_shifti_i64(ret, arg1, arg2, 1, 0);
+}
+
+static inline void tcg_gen_sar_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_sar_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sari_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    tcg_gen_shifti_i64(ret, arg1, arg2, 1, 1);
+}
+
+static inline void tcg_gen_brcond_i64(int cond, TCGv arg1, TCGv arg2, 
+                                      int label_index)
+{
+    tcg_gen_op6ii(INDEX_op_brcond2_i32, 
+                  arg1, TCGV_HIGH(arg1), arg2, TCGV_HIGH(arg2),
+                  cond, label_index);
+}
+
+static inline void tcg_gen_mul_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0, t1;
+    
+    t0 = tcg_temp_new(TCG_TYPE_I64);
+    t1 = tcg_temp_new(TCG_TYPE_I32);
+
+    tcg_gen_op4(INDEX_op_mulu2_i32, t0, TCGV_HIGH(t0), arg1, arg2);
+    
+    tcg_gen_mul_i32(t1, arg1, TCGV_HIGH(arg2));
+    tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1);
+    tcg_gen_mul_i32(t1, TCGV_HIGH(arg1), arg2);
+    tcg_gen_add_i32(TCGV_HIGH(t0), TCGV_HIGH(t0), t1);
+    
+    tcg_gen_mov_i64(ret, t0);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+
+static inline void tcg_gen_muli_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_mul_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_div_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_div_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_rem_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_divu_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_helper_1_2(tcg_helper_remu_i64, ret, arg1, arg2);
+}
+
+#else
+
+static inline void tcg_gen_mov_i64(TCGv ret, TCGv arg)
+{
+    if (GET_TCGV(ret) != GET_TCGV(arg))
+        tcg_gen_op2(INDEX_op_mov_i64, ret, arg);
+}
+
+static inline void tcg_gen_movi_i64(TCGv ret, int64_t arg)
+{
+    tcg_gen_op2i(INDEX_op_movi_i64, ret, arg);
+}
+
+static inline void tcg_gen_ld8u_i64(TCGv ret, TCGv arg2,
+                                    tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld8u_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld8s_i64(TCGv ret, TCGv arg2,
+                                    tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld8s_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld16u_i64(TCGv ret, TCGv arg2,
+                                     tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld16u_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld16s_i64(TCGv ret, TCGv arg2,
+                                     tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld16s_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld32u_i64(TCGv ret, TCGv arg2,
+                                     tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld32u_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld32s_i64(TCGv ret, TCGv arg2,
+                                     tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld32s_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_ld_i64(TCGv ret, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_ld_i64, ret, arg2, offset);
+}
+
+static inline void tcg_gen_st8_i64(TCGv arg1, TCGv arg2,
+                                   tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st8_i64, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st16_i64(TCGv arg1, TCGv arg2,
+                                    tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st16_i64, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st32_i64(TCGv arg1, TCGv arg2,
+                                    tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st32_i64, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_st_i64(TCGv arg1, TCGv arg2, tcg_target_long offset)
+{
+    tcg_gen_op3i(INDEX_op_st_i64, arg1, arg2, offset);
+}
+
+static inline void tcg_gen_add_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_add_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_addi_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_add_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_sub_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_sub_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_subi_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_sub_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_and_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_and_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_andi_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_and_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_or_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_or_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_ori_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_or_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_xor_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_xor_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_xori_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_xor_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_shl_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_shl_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shli_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    if (arg2 == 0) {
+        tcg_gen_mov_i64(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i64(arg2);
+        tcg_gen_shl_i64(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_shr_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_shr_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_shri_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    if (arg2 == 0) {
+        tcg_gen_mov_i64(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i64(arg2);
+        tcg_gen_shr_i64(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_sar_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_sar_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_sari_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    if (arg2 == 0) {
+        tcg_gen_mov_i64(ret, arg1);
+    } else {
+        TCGv t0 = tcg_const_i64(arg2);
+        tcg_gen_sar_i64(ret, arg1, t0);
+        tcg_temp_free(t0);
+    }
+}
+
+static inline void tcg_gen_brcond_i64(int cond, TCGv arg1, TCGv arg2, 
+                                      int label_index)
+{
+    tcg_gen_op4ii(INDEX_op_brcond_i64, arg1, arg2, cond, label_index);
+}
+
+static inline void tcg_gen_mul_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_mul_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_muli_i64(TCGv ret, TCGv arg1, int64_t arg2)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_mul_i64(ret, arg1, t0);
+    tcg_temp_free(t0);
+}
+
+#ifdef TCG_TARGET_HAS_div_i64
+static inline void tcg_gen_div_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_div_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_rem_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_rem_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_divu_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_divu_i64, ret, arg1, arg2);
+}
+
+static inline void tcg_gen_remu_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    tcg_gen_op3(INDEX_op_remu_i64, ret, arg1, arg2);
+}
+#else
+static inline void tcg_gen_div_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I64);
+    tcg_gen_sari_i64(t0, arg1, 63);
+    tcg_gen_op5(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_rem_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I64);
+    tcg_gen_sari_i64(t0, arg1, 63);
+    tcg_gen_op5(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_divu_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I64);
+    tcg_gen_movi_i64(t0, 0);
+    tcg_gen_op5(INDEX_op_divu2_i64, ret, t0, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+
+static inline void tcg_gen_remu_i64(TCGv ret, TCGv arg1, TCGv arg2)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I64);
+    tcg_gen_movi_i64(t0, 0);
+    tcg_gen_op5(INDEX_op_divu2_i64, t0, ret, arg1, t0, arg2);
+    tcg_temp_free(t0);
+}
+#endif
+
+#endif
+
+static inline void tcg_gen_brcondi_i64(int cond, TCGv arg1, int64_t arg2, 
+                                       int label_index)
+{
+    TCGv t0 = tcg_const_i64(arg2);
+    tcg_gen_brcond_i64(cond, arg1, t0, label_index);
+    tcg_temp_free(t0);
+}
+
+/***************************************/
+/* optional operations */
+
+static inline void tcg_gen_ext8s_i32(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_ext8s_i32
+    tcg_gen_op2(INDEX_op_ext8s_i32, ret, arg);
+#else
+    tcg_gen_shli_i32(ret, arg, 24);
+    tcg_gen_sari_i32(ret, ret, 24);
+#endif
+}
+
+static inline void tcg_gen_ext16s_i32(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_ext16s_i32
+    tcg_gen_op2(INDEX_op_ext16s_i32, ret, arg);
+#else
+    tcg_gen_shli_i32(ret, arg, 16);
+    tcg_gen_sari_i32(ret, ret, 16);
+#endif
+}
+
+/* These are currently just for convenience.
+   We assume a target will recognise these automatically .  */
+static inline void tcg_gen_ext8u_i32(TCGv ret, TCGv arg)
+{
+    tcg_gen_andi_i32(ret, arg, 0xffu);
+}
+
+static inline void tcg_gen_ext16u_i32(TCGv ret, TCGv arg)
+{
+    tcg_gen_andi_i32(ret, arg, 0xffffu);
+}
+
+/* Note: we assume the two high bytes are set to zero */
+static inline void tcg_gen_bswap16_i32(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_bswap16_i32
+    tcg_gen_op2(INDEX_op_bswap16_i32, ret, arg);
+#else
+    TCGv t0, t1;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    t1 = tcg_temp_new(TCG_TYPE_I32);
+    
+    tcg_gen_shri_i32(t0, arg, 8);
+    tcg_gen_andi_i32(t1, arg, 0x000000ff);
+    tcg_gen_shli_i32(t1, t1, 8);
+    tcg_gen_or_i32(ret, t0, t1);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+#endif
+}
+
+static inline void tcg_gen_bswap_i32(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_bswap_i32
+    tcg_gen_op2(INDEX_op_bswap_i32, ret, arg);
+#else
+    TCGv t0, t1;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    t1 = tcg_temp_new(TCG_TYPE_I32);
+    
+    tcg_gen_shli_i32(t0, arg, 24);
+    
+    tcg_gen_andi_i32(t1, arg, 0x0000ff00);
+    tcg_gen_shli_i32(t1, t1, 8);
+    tcg_gen_or_i32(t0, t0, t1);
+    
+    tcg_gen_shri_i32(t1, arg, 8);
+    tcg_gen_andi_i32(t1, t1, 0x0000ff00);
+    tcg_gen_or_i32(t0, t0, t1);
+    
+    tcg_gen_shri_i32(t1, arg, 24);
+    tcg_gen_or_i32(ret, t0, t1);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+#endif
+}
+
+#if TCG_TARGET_REG_BITS == 32
+static inline void tcg_gen_ext8s_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_ext8s_i32(ret, arg);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_ext16s_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_ext16s_i32(ret, arg);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_ext32s_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_mov_i32(ret, arg);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_ext8u_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_ext8u_i32(ret, arg);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ext16u_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_ext16u_i32(ret, arg);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ext32u_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_mov_i32(ret, arg);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_trunc_i64_i32(TCGv ret, TCGv arg)
+{
+    tcg_gen_mov_i32(ret, arg);
+}
+
+static inline void tcg_gen_extu_i32_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_mov_i32(ret, arg);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+}
+
+static inline void tcg_gen_ext_i32_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_mov_i32(ret, arg);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+}
+
+static inline void tcg_gen_bswap_i64(TCGv ret, TCGv arg)
+{
+    TCGv t0, t1;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    t1 = tcg_temp_new(TCG_TYPE_I32);
+
+    tcg_gen_bswap_i32(t0, arg);
+    tcg_gen_bswap_i32(t1, TCGV_HIGH(arg));
+    tcg_gen_mov_i32(ret, t1);
+    tcg_gen_mov_i32(TCGV_HIGH(ret), t0);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+}
+#else
+
+static inline void tcg_gen_ext8s_i64(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_ext8s_i64
+    tcg_gen_op2(INDEX_op_ext8s_i64, ret, arg);
+#else
+    tcg_gen_shli_i64(ret, arg, 56);
+    tcg_gen_sari_i64(ret, ret, 56);
+#endif
+}
+
+static inline void tcg_gen_ext16s_i64(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_ext16s_i64
+    tcg_gen_op2(INDEX_op_ext16s_i64, ret, arg);
+#else
+    tcg_gen_shli_i64(ret, arg, 48);
+    tcg_gen_sari_i64(ret, ret, 48);
+#endif
+}
+
+static inline void tcg_gen_ext32s_i64(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_ext32s_i64
+    tcg_gen_op2(INDEX_op_ext32s_i64, ret, arg);
+#else
+    tcg_gen_shli_i64(ret, arg, 32);
+    tcg_gen_sari_i64(ret, ret, 32);
+#endif
+}
+
+static inline void tcg_gen_ext8u_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_andi_i64(ret, arg, 0xffu);
+}
+
+static inline void tcg_gen_ext16u_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_andi_i64(ret, arg, 0xffffu);
+}
+
+static inline void tcg_gen_ext32u_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_andi_i64(ret, arg, 0xffffffffu);
+}
+
+/* Note: we assume the target supports move between 32 and 64 bit
+   registers.  This will probably break MIPS64 targets.  */
+static inline void tcg_gen_trunc_i64_i32(TCGv ret, TCGv arg)
+{
+    tcg_gen_mov_i32(ret, arg);
+}
+
+/* Note: we assume the target supports move between 32 and 64 bit
+   registers */
+static inline void tcg_gen_extu_i32_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_andi_i64(ret, arg, 0xffffffffu);
+}
+
+/* Note: we assume the target supports move between 32 and 64 bit
+   registers */
+static inline void tcg_gen_ext_i32_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_ext32s_i64(ret, arg);
+}
+
+static inline void tcg_gen_bswap_i64(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_bswap_i64
+    tcg_gen_op2(INDEX_op_bswap_i64, ret, arg);
+#else
+    TCGv t0, t1;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    t1 = tcg_temp_new(TCG_TYPE_I32);
+    
+    tcg_gen_shli_i64(t0, arg, 56);
+    
+    tcg_gen_andi_i64(t1, arg, 0x0000ff00);
+    tcg_gen_shli_i64(t1, t1, 40);
+    tcg_gen_or_i64(t0, t0, t1);
+    
+    tcg_gen_andi_i64(t1, arg, 0x00ff0000);
+    tcg_gen_shli_i64(t1, t1, 24);
+    tcg_gen_or_i64(t0, t0, t1);
+
+    tcg_gen_andi_i64(t1, arg, 0xff000000);
+    tcg_gen_shli_i64(t1, t1, 8);
+    tcg_gen_or_i64(t0, t0, t1);
+
+    tcg_gen_shri_i64(t1, arg, 8);
+    tcg_gen_andi_i64(t1, t1, 0xff000000);
+    tcg_gen_or_i64(t0, t0, t1);
+    
+    tcg_gen_shri_i64(t1, arg, 24);
+    tcg_gen_andi_i64(t1, t1, 0x00ff0000);
+    tcg_gen_or_i64(t0, t0, t1);
+
+    tcg_gen_shri_i64(t1, arg, 40);
+    tcg_gen_andi_i64(t1, t1, 0x0000ff00);
+    tcg_gen_or_i64(t0, t0, t1);
+
+    tcg_gen_shri_i64(t1, arg, 56);
+    tcg_gen_or_i64(ret, t0, t1);
+    tcg_temp_free(t0);
+    tcg_temp_free(t1);
+#endif
+}
+
+#endif
+
+static inline void tcg_gen_neg_i32(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_neg_i32
+    tcg_gen_op2(INDEX_op_neg_i32, ret, arg);
+#else
+    TCGv t0 = tcg_const_i32(0);
+    tcg_gen_sub_i32(ret, t0, arg);
+    tcg_temp_free(t0);
+#endif
+}
+
+static inline void tcg_gen_neg_i64(TCGv ret, TCGv arg)
+{
+#ifdef TCG_TARGET_HAS_neg_i64
+    tcg_gen_op2(INDEX_op_neg_i64, ret, arg);
+#else
+    TCGv t0 = tcg_const_i64(0);
+    tcg_gen_sub_i64(ret, t0, arg);
+    tcg_temp_free(t0);
+#endif
+}
+
+static inline void tcg_gen_not_i32(TCGv ret, TCGv arg)
+{
+    tcg_gen_xori_i32(ret, arg, -1);
+}
+
+static inline void tcg_gen_not_i64(TCGv ret, TCGv arg)
+{
+    tcg_gen_xori_i64(ret, arg, -1);
+}
+
+static inline void tcg_gen_discard_i32(TCGv arg)
+{
+    tcg_gen_op1(INDEX_op_discard, arg);
+}
+
+#if TCG_TARGET_REG_BITS == 32
+static inline void tcg_gen_discard_i64(TCGv arg)
+{
+    tcg_gen_discard_i32(arg);
+    tcg_gen_discard_i32(TCGV_HIGH(arg));
+}
+#else
+static inline void tcg_gen_discard_i64(TCGv arg)
+{
+    tcg_gen_op1(INDEX_op_discard, arg);
+}
+#endif
+
+/***************************************/
+/* QEMU specific operations. Their type depend on the QEMU CPU
+   type. */
+#ifndef TARGET_LONG_BITS
+#error must include QEMU headers
+#endif
+
+/* debug info: write the PC of the corresponding QEMU CPU instruction */
+static inline void tcg_gen_debug_insn_start(uint64_t pc)
+{
+    /* XXX: must really use a 32 bit size for TCGArg in all cases */
+#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
+    tcg_gen_op2ii(INDEX_op_debug_insn_start, 
+                  (uint32_t)(pc), (uint32_t)(pc >> 32));
+#else
+    tcg_gen_op1i(INDEX_op_debug_insn_start, pc);
+#endif
+}
+
+static inline void tcg_gen_exit_tb(tcg_target_long val)
+{
+    tcg_gen_op1i(INDEX_op_exit_tb, val);
+}
+
+static inline void tcg_gen_goto_tb(int idx)
+{
+    tcg_gen_op1i(INDEX_op_goto_tb, idx);
+}
+
+#if TCG_TARGET_REG_BITS == 32
+static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_ld8u, ret, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_ld8u, ret, addr, TCGV_HIGH(addr), mem_index);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_ld8s, ret, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_ld8s, ret, addr, TCGV_HIGH(addr), mem_index);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_ld16u, ret, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_ld16u, ret, addr, TCGV_HIGH(addr), mem_index);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_ld16s, ret, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_ld16s, ret, addr, TCGV_HIGH(addr), mem_index);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_ld32u, ret, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_ld32u, ret, addr, TCGV_HIGH(addr), mem_index);
+    tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_ld32u, ret, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_ld32u, ret, addr, TCGV_HIGH(addr), mem_index);
+    tcg_gen_sari_i32(TCGV_HIGH(ret), ret, 31);
+#endif
+}
+
+static inline void tcg_gen_qemu_ld64(TCGv ret, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op4i(INDEX_op_qemu_ld64, ret, TCGV_HIGH(ret), addr, mem_index);
+#else
+    tcg_gen_op5i(INDEX_op_qemu_ld64, ret, TCGV_HIGH(ret),
+                 addr, TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_st8, arg, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_st8, arg, addr, TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_st16, arg, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_st16, arg, addr, TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op3i(INDEX_op_qemu_st32, arg, addr, mem_index);
+#else
+    tcg_gen_op4i(INDEX_op_qemu_st32, arg, addr, TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+static inline void tcg_gen_qemu_st64(TCGv arg, TCGv addr, int mem_index)
+{
+#if TARGET_LONG_BITS == 32
+    tcg_gen_op4i(INDEX_op_qemu_st64, arg, TCGV_HIGH(arg), addr, mem_index);
+#else
+    tcg_gen_op5i(INDEX_op_qemu_st64, arg, TCGV_HIGH(arg),
+                 addr, TCGV_HIGH(addr), mem_index);
+#endif
+}
+
+#define tcg_gen_ld_ptr tcg_gen_ld_i32
+#define tcg_gen_discard_ptr tcg_gen_discard_i32
+
+#else /* TCG_TARGET_REG_BITS == 32 */
+
+static inline void tcg_gen_qemu_ld8u(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld8u, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld8s(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld8s, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld16u(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld16u, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld16s(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld16s, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld32u(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld32u, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld32s(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld32s, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_ld64(TCGv ret, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_ld64, ret, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st8(TCGv arg, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_st8, arg, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st16(TCGv arg, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_st16, arg, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st32(TCGv arg, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_st32, arg, addr, mem_index);
+}
+
+static inline void tcg_gen_qemu_st64(TCGv arg, TCGv addr, int mem_index)
+{
+    tcg_gen_op3i(INDEX_op_qemu_st64, arg, addr, mem_index);
+}
+
+#define tcg_gen_ld_ptr tcg_gen_ld_i64
+#define tcg_gen_discard_ptr tcg_gen_discard_i64
+
+#endif /* TCG_TARGET_REG_BITS != 32 */
+
+#if TARGET_LONG_BITS == 64
+#define TCG_TYPE_TL TCG_TYPE_I64
+#define tcg_gen_movi_tl tcg_gen_movi_i64
+#define tcg_gen_mov_tl tcg_gen_mov_i64
+#define tcg_gen_ld8u_tl tcg_gen_ld8u_i64
+#define tcg_gen_ld8s_tl tcg_gen_ld8s_i64
+#define tcg_gen_ld16u_tl tcg_gen_ld16u_i64
+#define tcg_gen_ld16s_tl tcg_gen_ld16s_i64
+#define tcg_gen_ld32u_tl tcg_gen_ld32u_i64
+#define tcg_gen_ld32s_tl tcg_gen_ld32s_i64
+#define tcg_gen_ld_tl tcg_gen_ld_i64
+#define tcg_gen_st8_tl tcg_gen_st8_i64
+#define tcg_gen_st16_tl tcg_gen_st16_i64
+#define tcg_gen_st32_tl tcg_gen_st32_i64
+#define tcg_gen_st_tl tcg_gen_st_i64
+#define tcg_gen_add_tl tcg_gen_add_i64
+#define tcg_gen_addi_tl tcg_gen_addi_i64
+#define tcg_gen_sub_tl tcg_gen_sub_i64
+#define tcg_gen_neg_tl tcg_gen_neg_i64
+#define tcg_gen_subi_tl tcg_gen_subi_i64
+#define tcg_gen_and_tl tcg_gen_and_i64
+#define tcg_gen_andi_tl tcg_gen_andi_i64
+#define tcg_gen_or_tl tcg_gen_or_i64
+#define tcg_gen_ori_tl tcg_gen_ori_i64
+#define tcg_gen_xor_tl tcg_gen_xor_i64
+#define tcg_gen_xori_tl tcg_gen_xori_i64
+#define tcg_gen_not_tl tcg_gen_not_i64
+#define tcg_gen_shl_tl tcg_gen_shl_i64
+#define tcg_gen_shli_tl tcg_gen_shli_i64
+#define tcg_gen_shr_tl tcg_gen_shr_i64
+#define tcg_gen_shri_tl tcg_gen_shri_i64
+#define tcg_gen_sar_tl tcg_gen_sar_i64
+#define tcg_gen_sari_tl tcg_gen_sari_i64
+#define tcg_gen_brcond_tl tcg_gen_brcond_i64
+#define tcg_gen_brcondi_tl tcg_gen_brcondi_i64
+#define tcg_gen_mul_tl tcg_gen_mul_i64
+#define tcg_gen_muli_tl tcg_gen_muli_i64
+#define tcg_gen_discard_tl tcg_gen_discard_i64
+#define tcg_gen_trunc_tl_i32 tcg_gen_trunc_i64_i32
+#define tcg_gen_trunc_i64_tl tcg_gen_mov_i64
+#define tcg_gen_extu_i32_tl tcg_gen_extu_i32_i64
+#define tcg_gen_ext_i32_tl tcg_gen_ext_i32_i64
+#define tcg_gen_extu_tl_i64 tcg_gen_mov_i64
+#define tcg_gen_ext_tl_i64 tcg_gen_mov_i64
+#define tcg_gen_ext8u_tl tcg_gen_ext8u_i64
+#define tcg_gen_ext8s_tl tcg_gen_ext8s_i64
+#define tcg_gen_ext16u_tl tcg_gen_ext16u_i64
+#define tcg_gen_ext16s_tl tcg_gen_ext16s_i64
+#define tcg_gen_ext32u_tl tcg_gen_ext32u_i64
+#define tcg_gen_ext32s_tl tcg_gen_ext32s_i64
+#define tcg_const_tl tcg_const_i64
+#else
+#define TCG_TYPE_TL TCG_TYPE_I32
+#define tcg_gen_movi_tl tcg_gen_movi_i32
+#define tcg_gen_mov_tl tcg_gen_mov_i32
+#define tcg_gen_ld8u_tl tcg_gen_ld8u_i32
+#define tcg_gen_ld8s_tl tcg_gen_ld8s_i32
+#define tcg_gen_ld16u_tl tcg_gen_ld16u_i32
+#define tcg_gen_ld16s_tl tcg_gen_ld16s_i32
+#define tcg_gen_ld32u_tl tcg_gen_ld_i32
+#define tcg_gen_ld32s_tl tcg_gen_ld_i32
+#define tcg_gen_ld_tl tcg_gen_ld_i32
+#define tcg_gen_st8_tl tcg_gen_st8_i32
+#define tcg_gen_st16_tl tcg_gen_st16_i32
+#define tcg_gen_st32_tl tcg_gen_st_i32
+#define tcg_gen_st_tl tcg_gen_st_i32
+#define tcg_gen_add_tl tcg_gen_add_i32
+#define tcg_gen_addi_tl tcg_gen_addi_i32
+#define tcg_gen_sub_tl tcg_gen_sub_i32
+#define tcg_gen_neg_tl tcg_gen_neg_i32
+#define tcg_gen_subi_tl tcg_gen_subi_i32
+#define tcg_gen_and_tl tcg_gen_and_i32
+#define tcg_gen_andi_tl tcg_gen_andi_i32
+#define tcg_gen_or_tl tcg_gen_or_i32
+#define tcg_gen_ori_tl tcg_gen_ori_i32
+#define tcg_gen_xor_tl tcg_gen_xor_i32
+#define tcg_gen_xori_tl tcg_gen_xori_i32
+#define tcg_gen_not_tl tcg_gen_not_i32
+#define tcg_gen_shl_tl tcg_gen_shl_i32
+#define tcg_gen_shli_tl tcg_gen_shli_i32
+#define tcg_gen_shr_tl tcg_gen_shr_i32
+#define tcg_gen_shri_tl tcg_gen_shri_i32
+#define tcg_gen_sar_tl tcg_gen_sar_i32
+#define tcg_gen_sari_tl tcg_gen_sari_i32
+#define tcg_gen_brcond_tl tcg_gen_brcond_i32
+#define tcg_gen_brcondi_tl tcg_gen_brcondi_i32
+#define tcg_gen_mul_tl tcg_gen_mul_i32
+#define tcg_gen_muli_tl tcg_gen_muli_i32
+#define tcg_gen_discard_tl tcg_gen_discard_i32
+#define tcg_gen_trunc_tl_i32 tcg_gen_mov_i32
+#define tcg_gen_trunc_i64_tl tcg_gen_trunc_i64_i32
+#define tcg_gen_extu_i32_tl tcg_gen_mov_i32
+#define tcg_gen_ext_i32_tl tcg_gen_mov_i32
+#define tcg_gen_extu_tl_i64 tcg_gen_extu_i32_i64
+#define tcg_gen_ext_tl_i64 tcg_gen_ext_i32_i64
+#define tcg_gen_ext8u_tl tcg_gen_ext8u_i32
+#define tcg_gen_ext8s_tl tcg_gen_ext8s_i32
+#define tcg_gen_ext16u_tl tcg_gen_ext16u_i32
+#define tcg_gen_ext16s_tl tcg_gen_ext16s_i32
+#define tcg_gen_ext32u_tl tcg_gen_mov_i32
+#define tcg_gen_ext32s_tl tcg_gen_mov_i32
+#define tcg_const_tl tcg_const_i32
+#endif
+
+#if TCG_TARGET_REG_BITS == 32
+#define tcg_gen_add_ptr tcg_gen_add_i32
+#define tcg_gen_addi_ptr tcg_gen_addi_i32
+#define tcg_gen_ext_i32_ptr tcg_gen_mov_i32
+#else /* TCG_TARGET_REG_BITS == 32 */
+#define tcg_gen_add_ptr tcg_gen_add_i64
+#define tcg_gen_addi_ptr tcg_gen_addi_i64
+#define tcg_gen_ext_i32_ptr tcg_gen_ext_i32_i64
+#endif /* TCG_TARGET_REG_BITS != 32 */
+
diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h
new file mode 100644
index 0000000..31ae550
--- /dev/null
+++ b/tcg/tcg-opc.h
@@ -0,0 +1,238 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifdef CONFIG_DYNGEN_OP
+#include "dyngen-opc.h"
+#endif
+
+#ifndef DEF2
+#define DEF2(name, oargs, iargs, cargs, flags) DEF(name, oargs + iargs + cargs, 0)
+#endif
+
+/* predefined ops */
+DEF2(end, 0, 0, 0, 0) /* must be kept first */
+DEF2(nop, 0, 0, 0, 0)
+DEF2(nop1, 0, 0, 1, 0)
+DEF2(nop2, 0, 0, 2, 0)
+DEF2(nop3, 0, 0, 3, 0)
+DEF2(nopn, 0, 0, 1, 0) /* variable number of parameters */
+
+DEF2(discard, 1, 0, 0, 0)
+
+DEF2(set_label, 0, 0, 1, 0)
+DEF2(call, 0, 1, 2, TCG_OPF_SIDE_EFFECTS) /* variable number of parameters */
+DEF2(jmp, 0, 1, 0, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF2(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+
+DEF2(mov_i32, 1, 1, 0, 0)
+DEF2(movi_i32, 1, 0, 1, 0)
+/* load/store */
+DEF2(ld8u_i32, 1, 1, 1, 0)
+DEF2(ld8s_i32, 1, 1, 1, 0)
+DEF2(ld16u_i32, 1, 1, 1, 0)
+DEF2(ld16s_i32, 1, 1, 1, 0)
+DEF2(ld_i32, 1, 1, 1, 0)
+DEF2(st8_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF2(st16_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF2(st_i32, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+/* arith */
+DEF2(add_i32, 1, 2, 0, 0)
+DEF2(sub_i32, 1, 2, 0, 0)
+DEF2(mul_i32, 1, 2, 0, 0)
+#ifdef TCG_TARGET_HAS_div_i32
+DEF2(div_i32, 1, 2, 0, 0)
+DEF2(divu_i32, 1, 2, 0, 0)
+DEF2(rem_i32, 1, 2, 0, 0)
+DEF2(remu_i32, 1, 2, 0, 0)
+#else
+DEF2(div2_i32, 2, 3, 0, 0)
+DEF2(divu2_i32, 2, 3, 0, 0)
+#endif
+DEF2(and_i32, 1, 2, 0, 0)
+DEF2(or_i32, 1, 2, 0, 0)
+DEF2(xor_i32, 1, 2, 0, 0)
+/* shifts */
+DEF2(shl_i32, 1, 2, 0, 0)
+DEF2(shr_i32, 1, 2, 0, 0)
+DEF2(sar_i32, 1, 2, 0, 0)
+
+DEF2(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+#if TCG_TARGET_REG_BITS == 32
+DEF2(add2_i32, 2, 4, 0, 0)
+DEF2(sub2_i32, 2, 4, 0, 0)
+DEF2(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF2(mulu2_i32, 2, 2, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext8s_i32
+DEF2(ext8s_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i32
+DEF2(ext16s_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap_i32
+DEF2(bswap_i32, 1, 1, 0, 0)
+#endif
+
+#if TCG_TARGET_REG_BITS == 64
+DEF2(mov_i64, 1, 1, 0, 0)
+DEF2(movi_i64, 1, 0, 1, 0)
+/* load/store */
+DEF2(ld8u_i64, 1, 1, 1, 0)
+DEF2(ld8s_i64, 1, 1, 1, 0)
+DEF2(ld16u_i64, 1, 1, 1, 0)
+DEF2(ld16s_i64, 1, 1, 1, 0)
+DEF2(ld32u_i64, 1, 1, 1, 0)
+DEF2(ld32s_i64, 1, 1, 1, 0)
+DEF2(ld_i64, 1, 1, 1, 0)
+DEF2(st8_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF2(st16_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF2(st32_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+DEF2(st_i64, 0, 2, 1, TCG_OPF_SIDE_EFFECTS)
+/* arith */
+DEF2(add_i64, 1, 2, 0, 0)
+DEF2(sub_i64, 1, 2, 0, 0)
+DEF2(mul_i64, 1, 2, 0, 0)
+#ifdef TCG_TARGET_HAS_div_i64
+DEF2(div_i64, 1, 2, 0, 0)
+DEF2(divu_i64, 1, 2, 0, 0)
+DEF2(rem_i64, 1, 2, 0, 0)
+DEF2(remu_i64, 1, 2, 0, 0)
+#else
+DEF2(div2_i64, 2, 3, 0, 0)
+DEF2(divu2_i64, 2, 3, 0, 0)
+#endif
+DEF2(and_i64, 1, 2, 0, 0)
+DEF2(or_i64, 1, 2, 0, 0)
+DEF2(xor_i64, 1, 2, 0, 0)
+/* shifts */
+DEF2(shl_i64, 1, 2, 0, 0)
+DEF2(shr_i64, 1, 2, 0, 0)
+DEF2(sar_i64, 1, 2, 0, 0)
+
+DEF2(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+#ifdef TCG_TARGET_HAS_ext8s_i64
+DEF2(ext8s_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext16s_i64
+DEF2(ext16s_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_ext32s_i64
+DEF2(ext32s_i64, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_bswap_i64
+DEF2(bswap_i64, 1, 1, 0, 0)
+#endif
+#endif
+#ifdef TCG_TARGET_HAS_neg_i32
+DEF2(neg_i32, 1, 1, 0, 0)
+#endif
+#ifdef TCG_TARGET_HAS_neg_i64
+DEF2(neg_i64, 1, 1, 0, 0)
+#endif
+
+/* QEMU specific */
+#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
+DEF2(debug_insn_start, 0, 0, 2, 0)
+#else
+DEF2(debug_insn_start, 0, 0, 1, 0)
+#endif
+DEF2(exit_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+DEF2(goto_tb, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_SIDE_EFFECTS)
+/* Note: even if TARGET_LONG_BITS is not defined, the INDEX_op
+   constants must be defined */
+#if TCG_TARGET_REG_BITS == 32
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld8u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld8s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld16u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld16s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld32u, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld32s, 1, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_ld64, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_ld64, 2, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_st8, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_st16, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_st32, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+#if TARGET_LONG_BITS == 32
+DEF2(qemu_st64, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#else
+DEF2(qemu_st64, 0, 4, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+#endif
+
+#else /* TCG_TARGET_REG_BITS == 32 */
+
+DEF2(qemu_ld8u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_ld8s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_ld16u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_ld16s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_ld32u, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_ld32s, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_ld64, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+
+DEF2(qemu_st8, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_st16, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_st32, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+DEF2(qemu_st64, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS)
+
+#endif /* TCG_TARGET_REG_BITS != 32 */
+
+#undef DEF2
diff --git a/tcg/tcg-runtime.c b/tcg/tcg-runtime.c
new file mode 100644
index 0000000..575da43
--- /dev/null
+++ b/tcg/tcg-runtime.c
@@ -0,0 +1,68 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+#include "osdep.h"
+#include "tcg.h"
+
+int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2)
+{
+    return arg1 << arg2;
+}
+
+int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2)
+{
+    return (uint64_t)arg1 >> arg2;
+}
+
+int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2)
+{
+    return arg1 >> arg2;
+}
+
+int64_t tcg_helper_div_i64(int64_t arg1, int64_t arg2)
+{
+    return arg1 / arg2;
+}
+
+int64_t tcg_helper_rem_i64(int64_t arg1, int64_t arg2)
+{
+    return arg1 % arg2;
+}
+
+uint64_t tcg_helper_divu_i64(uint64_t arg1, uint64_t arg2)
+{
+    return arg1 / arg2;
+}
+
+uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2)
+{
+    return arg1 % arg2;
+}
+
diff --git a/tcg/tcg.c b/tcg/tcg.c
new file mode 100644
index 0000000..1b7bf5c
--- /dev/null
+++ b/tcg/tcg.c
@@ -0,0 +1,2081 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* define it to suppress various consistency checks (faster) */
+#define NDEBUG
+
+/* define it to use liveness analysis (better code) */
+#define USE_LIVENESS_ANALYSIS
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#ifdef _WIN32
+#include <malloc.h>
+#endif
+
+#include "config.h"
+#include "qemu-common.h"
+
+/* Note: the long term plan is to reduce the dependancies on the QEMU
+   CPU definitions. Currently they are used for qemu_ld/st
+   instructions */
+#define NO_CPU_IO_DEFS
+#include "cpu.h"
+#include "exec-all.h"
+
+#include "tcg-op.h"
+#include "elf.h"
+
+
+static void patch_reloc(uint8_t *code_ptr, int type, 
+                        tcg_target_long value, tcg_target_long addend);
+
+TCGOpDef tcg_op_defs[] = {
+#define DEF(s, n, copy_size) { #s, 0, 0, n, n, 0, copy_size },
+#define DEF2(s, iargs, oargs, cargs, flags) { #s, iargs, oargs, cargs, iargs + oargs + cargs, flags, 0 },
+#include "tcg-opc.h"
+#undef DEF
+#undef DEF2
+};
+
+TCGRegSet tcg_target_available_regs[2];
+TCGRegSet tcg_target_call_clobber_regs;
+
+/* XXX: move that inside the context */
+uint16_t *gen_opc_ptr;
+TCGArg *gen_opparam_ptr;
+
+static inline void tcg_out8(TCGContext *s, uint8_t v)
+{
+    *s->code_ptr++ = v;
+}
+
+static inline void tcg_out16(TCGContext *s, uint16_t v)
+{
+    *(uint16_t *)s->code_ptr = v;
+    s->code_ptr += 2;
+}
+
+static inline void tcg_out32(TCGContext *s, uint32_t v)
+{
+    *(uint32_t *)s->code_ptr = v;
+    s->code_ptr += 4;
+}
+
+/* label relocation processing */
+
+void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, 
+                   int label_index, long addend)
+{
+    TCGLabel *l;
+    TCGRelocation *r;
+
+    l = &s->labels[label_index];
+    if (l->has_value) {
+        /* FIXME: This may break relocations on RISC targets that
+           modify instruction fields in place.  The caller may not have 
+           written the initial value.  */
+        patch_reloc(code_ptr, type, l->u.value, addend);
+    } else {
+        /* add a new relocation entry */
+        r = tcg_malloc(sizeof(TCGRelocation));
+        r->type = type;
+        r->ptr = code_ptr;
+        r->addend = addend;
+        r->next = l->u.first_reloc;
+        l->u.first_reloc = r;
+    }
+}
+
+static void tcg_out_label(TCGContext *s, int label_index, 
+                          tcg_target_long value)
+{
+    TCGLabel *l;
+    TCGRelocation *r;
+
+    l = &s->labels[label_index];
+    if (l->has_value)
+        tcg_abort();
+    r = l->u.first_reloc;
+    while (r != NULL) {
+        patch_reloc(r->ptr, r->type, value, r->addend);
+        r = r->next;
+    }
+    l->has_value = 1;
+    l->u.value = value;
+}
+
+int gen_new_label(void)
+{
+    TCGContext *s = &tcg_ctx;
+    int idx;
+    TCGLabel *l;
+
+    if (s->nb_labels >= TCG_MAX_LABELS)
+        tcg_abort();
+    idx = s->nb_labels++;
+    l = &s->labels[idx];
+    l->has_value = 0;
+    l->u.first_reloc = NULL;
+    return idx;
+}
+
+#include "tcg-target.c"
+
+/* pool based memory allocation */
+void *tcg_malloc_internal(TCGContext *s, int size)
+{
+    TCGPool *p;
+    int pool_size;
+    
+    if (size > TCG_POOL_CHUNK_SIZE) {
+        /* big malloc: insert a new pool (XXX: could optimize) */
+        p = qemu_malloc(sizeof(TCGPool) + size);
+        p->size = size;
+        if (s->pool_current)
+            s->pool_current->next = p;
+        else
+            s->pool_first = p;
+        p->next = s->pool_current;
+    } else {
+        p = s->pool_current;
+        if (!p) {
+            p = s->pool_first;
+            if (!p)
+                goto new_pool;
+        } else {
+            if (!p->next) {
+            new_pool:
+                pool_size = TCG_POOL_CHUNK_SIZE;
+                p = qemu_malloc(sizeof(TCGPool) + pool_size);
+                p->size = pool_size;
+                p->next = NULL;
+                if (s->pool_current) 
+                    s->pool_current->next = p;
+                else
+                    s->pool_first = p;
+            } else {
+                p = p->next;
+            }
+        }
+    }
+    s->pool_current = p;
+    s->pool_cur = p->data + size;
+    s->pool_end = p->data + p->size;
+    return p->data;
+}
+
+void tcg_pool_reset(TCGContext *s)
+{
+    s->pool_cur = s->pool_end = NULL;
+    s->pool_current = NULL;
+}
+
+void tcg_context_init(TCGContext *s)
+{
+    int op, total_args, n;
+    TCGOpDef *def;
+    TCGArgConstraint *args_ct;
+    int *sorted_args;
+
+    memset(s, 0, sizeof(*s));
+    s->temps = s->static_temps;
+    s->nb_globals = 0;
+    
+    /* Count total number of arguments and allocate the corresponding
+       space */
+    total_args = 0;
+    for(op = 0; op < NB_OPS; op++) {
+        def = &tcg_op_defs[op];
+        n = def->nb_iargs + def->nb_oargs;
+        total_args += n;
+    }
+
+    args_ct = qemu_malloc(sizeof(TCGArgConstraint) * total_args);
+    sorted_args = qemu_malloc(sizeof(int) * total_args);
+
+    for(op = 0; op < NB_OPS; op++) {
+        def = &tcg_op_defs[op];
+        def->args_ct = args_ct;
+        def->sorted_args = sorted_args;
+        n = def->nb_iargs + def->nb_oargs;
+        sorted_args += n;
+        args_ct += n;
+    }
+    
+    tcg_target_init(s);
+
+    /* init global prologue and epilogue */
+    s->code_buf = code_gen_prologue;
+    s->code_ptr = s->code_buf;
+    tcg_target_qemu_prologue(s);
+    flush_icache_range((unsigned long)s->code_buf, 
+                       (unsigned long)s->code_ptr);
+}
+
+void tcg_set_frame(TCGContext *s, int reg,
+                   tcg_target_long start, tcg_target_long size)
+{
+    s->frame_start = start;
+    s->frame_end = start + size;
+    s->frame_reg = reg;
+}
+
+void tcg_func_start(TCGContext *s)
+{
+    int i;
+    tcg_pool_reset(s);
+    s->nb_temps = s->nb_globals;
+    for(i = 0; i < (TCG_TYPE_COUNT * 2); i++)
+        s->first_free_temp[i] = -1;
+    s->labels = tcg_malloc(sizeof(TCGLabel) * TCG_MAX_LABELS);
+    s->nb_labels = 0;
+    s->current_frame_offset = s->frame_start;
+
+    gen_opc_ptr = gen_opc_buf;
+    gen_opparam_ptr = gen_opparam_buf;
+}
+
+static inline void tcg_temp_alloc(TCGContext *s, int n)
+{
+    if (n > TCG_MAX_TEMPS)
+        tcg_abort();
+}
+
+TCGv tcg_global_reg_new(TCGType type, int reg, const char *name)
+{
+    TCGContext *s = &tcg_ctx;
+    TCGTemp *ts;
+    int idx;
+
+#if TCG_TARGET_REG_BITS == 32
+    if (type != TCG_TYPE_I32)
+        tcg_abort();
+#endif
+    if (tcg_regset_test_reg(s->reserved_regs, reg))
+        tcg_abort();
+    idx = s->nb_globals;
+    tcg_temp_alloc(s, s->nb_globals + 1);
+    ts = &s->temps[s->nb_globals];
+    ts->base_type = type;
+    ts->type = type;
+    ts->fixed_reg = 1;
+    ts->reg = reg;
+    ts->name = name;
+    s->nb_globals++;
+    tcg_regset_set_reg(s->reserved_regs, reg);
+    return MAKE_TCGV(idx);
+}
+
+#if TCG_TARGET_REG_BITS == 32
+/* temporary hack to avoid register shortage for tcg_qemu_st64() */
+TCGv tcg_global_reg2_new_hack(TCGType type, int reg1, int reg2, 
+                              const char *name)
+{
+    TCGContext *s = &tcg_ctx;
+    TCGTemp *ts;
+    int idx;
+    char buf[64];
+
+    if (type != TCG_TYPE_I64)
+        tcg_abort();
+    idx = s->nb_globals;
+    tcg_temp_alloc(s, s->nb_globals + 2);
+    ts = &s->temps[s->nb_globals];
+    ts->base_type = type;
+    ts->type = TCG_TYPE_I32;
+    ts->fixed_reg = 1;
+    ts->reg = reg1;
+    pstrcpy(buf, sizeof(buf), name);
+    pstrcat(buf, sizeof(buf), "_0");
+    ts->name = strdup(buf);
+
+    ts++;
+    ts->base_type = type;
+    ts->type = TCG_TYPE_I32;
+    ts->fixed_reg = 1;
+    ts->reg = reg2;
+    pstrcpy(buf, sizeof(buf), name);
+    pstrcat(buf, sizeof(buf), "_1");
+    ts->name = strdup(buf);
+
+    s->nb_globals += 2;
+    return MAKE_TCGV(idx);
+}
+#endif
+
+TCGv tcg_global_mem_new(TCGType type, int reg, tcg_target_long offset,
+                        const char *name)
+{
+    TCGContext *s = &tcg_ctx;
+    TCGTemp *ts;
+    int idx;
+
+    idx = s->nb_globals;
+#if TCG_TARGET_REG_BITS == 32
+    if (type == TCG_TYPE_I64) {
+        char buf[64];
+        tcg_temp_alloc(s, s->nb_globals + 2);
+        ts = &s->temps[s->nb_globals];
+        ts->base_type = type;
+        ts->type = TCG_TYPE_I32;
+        ts->fixed_reg = 0;
+        ts->mem_allocated = 1;
+        ts->mem_reg = reg;
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+        ts->mem_offset = offset + 4;
+#else
+        ts->mem_offset = offset;
+#endif
+        pstrcpy(buf, sizeof(buf), name);
+        pstrcat(buf, sizeof(buf), "_0");
+        ts->name = strdup(buf);
+        ts++;
+
+        ts->base_type = type;
+        ts->type = TCG_TYPE_I32;
+        ts->fixed_reg = 0;
+        ts->mem_allocated = 1;
+        ts->mem_reg = reg;
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+        ts->mem_offset = offset;
+#else
+        ts->mem_offset = offset + 4;
+#endif
+        pstrcpy(buf, sizeof(buf), name);
+        pstrcat(buf, sizeof(buf), "_1");
+        ts->name = strdup(buf);
+
+        s->nb_globals += 2;
+    } else
+#endif
+    {
+        tcg_temp_alloc(s, s->nb_globals + 1);
+        ts = &s->temps[s->nb_globals];
+        ts->base_type = type;
+        ts->type = type;
+        ts->fixed_reg = 0;
+        ts->mem_allocated = 1;
+        ts->mem_reg = reg;
+        ts->mem_offset = offset;
+        ts->name = name;
+        s->nb_globals++;
+    }
+    return MAKE_TCGV(idx);
+}
+
+TCGv tcg_temp_new_internal(TCGType type, int temp_local)
+{
+    TCGContext *s = &tcg_ctx;
+    TCGTemp *ts;
+    int idx, k;
+
+    k = type;
+    if (temp_local)
+        k += TCG_TYPE_COUNT;
+    idx = s->first_free_temp[k];
+    if (idx != -1) {
+        /* There is already an available temp with the
+           right type */
+        ts = &s->temps[idx];
+        s->first_free_temp[k] = ts->next_free_temp;
+        ts->temp_allocated = 1;
+        assert(ts->temp_local == temp_local);
+    } else {
+        idx = s->nb_temps;
+#if TCG_TARGET_REG_BITS == 32
+        if (type == TCG_TYPE_I64) {
+            tcg_temp_alloc(s, s->nb_temps + 2);
+            ts = &s->temps[s->nb_temps];
+            ts->base_type = type;
+            ts->type = TCG_TYPE_I32;
+            ts->temp_allocated = 1;
+            ts->temp_local = temp_local;
+            ts->name = NULL;
+            ts++;
+            ts->base_type = TCG_TYPE_I32;
+            ts->type = TCG_TYPE_I32;
+            ts->temp_allocated = 1;
+            ts->temp_local = temp_local;
+            ts->name = NULL;
+            s->nb_temps += 2;
+        } else
+#endif
+        {
+            tcg_temp_alloc(s, s->nb_temps + 1);
+            ts = &s->temps[s->nb_temps];
+            ts->base_type = type;
+            ts->type = type;
+            ts->temp_allocated = 1;
+            ts->temp_local = temp_local;
+            ts->name = NULL;
+            s->nb_temps++;
+        }
+    }
+    return MAKE_TCGV(idx);
+}
+
+void tcg_temp_free(TCGv arg)
+{
+    TCGContext *s = &tcg_ctx;
+    TCGTemp *ts;
+    int idx = GET_TCGV(arg);
+    int k;
+
+    assert(idx >= s->nb_globals && idx < s->nb_temps);
+    ts = &s->temps[idx];
+    assert(ts->temp_allocated != 0);
+    ts->temp_allocated = 0;
+    k = ts->base_type;
+    if (ts->temp_local)
+        k += TCG_TYPE_COUNT;
+    ts->next_free_temp = s->first_free_temp[k];
+    s->first_free_temp[k] = idx;
+}
+
+
+TCGv tcg_const_i32(int32_t val)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I32);
+    tcg_gen_movi_i32(t0, val);
+    return t0;
+}
+
+TCGv tcg_const_i64(int64_t val)
+{
+    TCGv t0;
+    t0 = tcg_temp_new(TCG_TYPE_I64);
+    tcg_gen_movi_i64(t0, val);
+    return t0;
+}
+
+void tcg_register_helper(void *func, const char *name)
+{
+    TCGContext *s = &tcg_ctx;
+    int n;
+    if ((s->nb_helpers + 1) > s->allocated_helpers) {
+        n = s->allocated_helpers;
+        if (n == 0) {
+            n = 4;
+        } else {
+            n *= 2;
+        }
+        s->helpers = realloc(s->helpers, n * sizeof(TCGHelperInfo));
+        s->allocated_helpers = n;
+    }
+    s->helpers[s->nb_helpers].func = (tcg_target_ulong)func;
+    s->helpers[s->nb_helpers].name = name;
+    s->nb_helpers++;
+}
+
+static inline TCGType tcg_get_base_type(TCGContext *s, TCGv arg)
+{
+    return s->temps[GET_TCGV(arg)].base_type;
+}
+
+static void tcg_gen_call_internal(TCGContext *s, TCGv func, 
+                                  unsigned int flags,
+                                  unsigned int nb_rets, const TCGv *rets,
+                                  unsigned int nb_params, const TCGv *params)
+{
+    int i;
+    *gen_opc_ptr++ = INDEX_op_call;
+    *gen_opparam_ptr++ = (nb_rets << 16) | (nb_params + 1);
+    for(i = 0; i < nb_rets; i++) {
+        *gen_opparam_ptr++ = GET_TCGV(rets[i]);
+    }
+    for(i = 0; i < nb_params; i++) {
+        *gen_opparam_ptr++ = GET_TCGV(params[i]);
+    }
+    *gen_opparam_ptr++ = GET_TCGV(func);
+
+    *gen_opparam_ptr++ = flags;
+    /* total parameters, needed to go backward in the instruction stream */
+    *gen_opparam_ptr++ = 1 + nb_rets + nb_params + 3;
+}
+
+
+#if TCG_TARGET_REG_BITS < 64
+/* Note: we convert the 64 bit args to 32 bit and do some alignment
+   and endian swap. Maybe it would be better to do the alignment
+   and endian swap in tcg_reg_alloc_call(). */
+void tcg_gen_call(TCGContext *s, TCGv func, unsigned int flags,
+                  unsigned int nb_rets, const TCGv *rets,
+                  unsigned int nb_params, const TCGv *args1)
+{
+    TCGv ret, *args2, rets_2[2], arg;
+    int j, i, call_type;
+
+    if (nb_rets == 1) {
+        ret = rets[0];
+        if (tcg_get_base_type(s, ret) == TCG_TYPE_I64) {
+            nb_rets = 2;
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+            rets_2[0] = TCGV_HIGH(ret);
+            rets_2[1] = ret;
+#else
+            rets_2[0] = ret;
+            rets_2[1] = TCGV_HIGH(ret);
+#endif
+            rets = rets_2;
+        }
+    }
+    args2 = alloca((nb_params * 3) * sizeof(TCGv));
+    j = 0;
+    call_type = (flags & TCG_CALL_TYPE_MASK);
+    for(i = 0; i < nb_params; i++) {
+        arg = args1[i];
+        if (tcg_get_base_type(s, arg) == TCG_TYPE_I64) {
+#ifdef TCG_TARGET_I386
+            /* REGPARM case: if the third parameter is 64 bit, it is
+               allocated on the stack */
+            if (j == 2 && call_type == TCG_CALL_TYPE_REGPARM) {
+                call_type = TCG_CALL_TYPE_REGPARM_2;
+                flags = (flags & ~TCG_CALL_TYPE_MASK) | call_type;
+            }
+            args2[j++] = arg;
+            args2[j++] = TCGV_HIGH(arg);
+#else
+#ifdef TCG_TARGET_CALL_ALIGN_ARGS
+            /* some targets want aligned 64 bit args */
+            if (j & 1) {
+                args2[j++] = TCG_CALL_DUMMY_ARG;
+            }
+#endif
+#ifdef TCG_TARGET_WORDS_BIGENDIAN
+            args2[j++] = TCGV_HIGH(arg);
+            args2[j++] = arg;
+#else
+            args2[j++] = arg;
+            args2[j++] = TCGV_HIGH(arg);
+#endif
+#endif
+        } else {
+            args2[j++] = arg;
+        }
+    }
+    tcg_gen_call_internal(s, func, flags, 
+                          nb_rets, rets, j, args2);
+}
+#else
+void tcg_gen_call(TCGContext *s, TCGv func, unsigned int flags,
+                  unsigned int nb_rets, const TCGv *rets,
+                  unsigned int nb_params, const TCGv *args1)
+{
+    tcg_gen_call_internal(s, func, flags, 
+                          nb_rets, rets, nb_params, args1);
+}
+#endif
+
+#if TCG_TARGET_REG_BITS == 32
+void tcg_gen_shifti_i64(TCGv ret, TCGv arg1, 
+                        int c, int right, int arith)
+{
+    if (c == 0) {
+        tcg_gen_mov_i32(ret, arg1);
+        tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1));
+    } else if (c >= 32) {
+        c -= 32;
+        if (right) {
+            if (arith) {
+                tcg_gen_sari_i32(ret, TCGV_HIGH(arg1), c);
+                tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), 31);
+            } else {
+                tcg_gen_shri_i32(ret, TCGV_HIGH(arg1), c);
+                tcg_gen_movi_i32(TCGV_HIGH(ret), 0);
+            }
+        } else {
+            tcg_gen_shli_i32(TCGV_HIGH(ret), arg1, c);
+            tcg_gen_movi_i32(ret, 0);
+        }
+    } else {
+        TCGv t0, t1;
+
+        t0 = tcg_temp_new(TCG_TYPE_I32);
+        t1 = tcg_temp_new(TCG_TYPE_I32);
+        if (right) {
+            tcg_gen_shli_i32(t0, TCGV_HIGH(arg1), 32 - c);
+            if (arith)
+                tcg_gen_sari_i32(t1, TCGV_HIGH(arg1), c);
+            else 
+                tcg_gen_shri_i32(t1, TCGV_HIGH(arg1), c);
+            tcg_gen_shri_i32(ret, arg1, c); 
+            tcg_gen_or_i32(ret, ret, t0);
+            tcg_gen_mov_i32(TCGV_HIGH(ret), t1);
+        } else {
+            tcg_gen_shri_i32(t0, arg1, 32 - c);
+            /* Note: ret can be the same as arg1, so we use t1 */
+            tcg_gen_shli_i32(t1, arg1, c); 
+            tcg_gen_shli_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c);
+            tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(ret), t0);
+            tcg_gen_mov_i32(ret, t1);
+        }
+        tcg_temp_free(t0);
+        tcg_temp_free(t1);
+    }
+}
+#endif
+
+static void tcg_reg_alloc_start(TCGContext *s)
+{
+    int i;
+    TCGTemp *ts;
+    for(i = 0; i < s->nb_globals; i++) {
+        ts = &s->temps[i];
+        if (ts->fixed_reg) {
+            ts->val_type = TEMP_VAL_REG;
+        } else {
+            ts->val_type = TEMP_VAL_MEM;
+        }
+    }
+    for(i = s->nb_globals; i < s->nb_temps; i++) {
+        ts = &s->temps[i];
+        ts->val_type = TEMP_VAL_DEAD;
+        ts->mem_allocated = 0;
+        ts->fixed_reg = 0;
+    }
+    for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
+        s->reg_to_temp[i] = -1;
+    }
+}
+
+static char *tcg_get_arg_str_idx(TCGContext *s, char *buf, int buf_size,
+                                 int idx)
+{
+    TCGTemp *ts;
+
+    ts = &s->temps[idx];
+    if (idx < s->nb_globals) {
+        pstrcpy(buf, buf_size, ts->name);
+    } else {
+        if (ts->temp_local) 
+            snprintf(buf, buf_size, "loc%d", idx - s->nb_globals);
+        else
+            snprintf(buf, buf_size, "tmp%d", idx - s->nb_globals);
+    }
+    return buf;
+}
+
+char *tcg_get_arg_str(TCGContext *s, char *buf, int buf_size, TCGv arg)
+{
+    return tcg_get_arg_str_idx(s, buf, buf_size, GET_TCGV(arg));
+}
+
+static int helper_cmp(const void *p1, const void *p2)
+{
+    const TCGHelperInfo *th1 = p1;
+    const TCGHelperInfo *th2 = p2;
+    if (th1->func < th2->func)
+        return -1;
+    else if (th1->func == th2->func)
+        return 0;
+    else
+        return 1;
+}
+
+/* find helper definition (Note: A hash table would be better) */
+static TCGHelperInfo *tcg_find_helper(TCGContext *s, tcg_target_ulong val)
+{
+    int m, m_min, m_max;
+    TCGHelperInfo *th;
+    tcg_target_ulong v;
+
+    if (unlikely(!s->helpers_sorted)) {
+        qsort(s->helpers, s->nb_helpers, sizeof(TCGHelperInfo), 
+              helper_cmp);
+        s->helpers_sorted = 1;
+    }
+
+    /* binary search */
+    m_min = 0;
+    m_max = s->nb_helpers - 1;
+    while (m_min <= m_max) {
+        m = (m_min + m_max) >> 1;
+        th = &s->helpers[m];
+        v = th->func;
+        if (v == val)
+            return th;
+        else if (val < v) {
+            m_max = m - 1;
+        } else {
+            m_min = m + 1;
+        }
+    }
+    return NULL;
+}
+
+static const char * const cond_name[] =
+{
+    [TCG_COND_EQ] = "eq",
+    [TCG_COND_NE] = "ne",
+    [TCG_COND_LT] = "lt",
+    [TCG_COND_GE] = "ge",
+    [TCG_COND_LE] = "le",
+    [TCG_COND_GT] = "gt",
+    [TCG_COND_LTU] = "ltu",
+    [TCG_COND_GEU] = "geu",
+    [TCG_COND_LEU] = "leu",
+    [TCG_COND_GTU] = "gtu"
+};
+
+void tcg_dump_ops(TCGContext *s, FILE *outfile)
+{
+    const uint16_t *opc_ptr;
+    const TCGArg *args;
+    TCGArg arg;
+    int c, i, k, nb_oargs, nb_iargs, nb_cargs, first_insn;
+    const TCGOpDef *def;
+    char buf[128];
+
+    first_insn = 1;
+    opc_ptr = gen_opc_buf;
+    args = gen_opparam_buf;
+    while (opc_ptr < gen_opc_ptr) {
+        c = *opc_ptr++;
+        def = &tcg_op_defs[c];
+        if (c == INDEX_op_debug_insn_start) {
+            uint64_t pc;
+#if TARGET_LONG_BITS > TCG_TARGET_REG_BITS
+            pc = ((uint64_t)args[1] << 32) | args[0];
+#else
+            pc = args[0];
+#endif
+            if (!first_insn) 
+                fprintf(outfile, "\n");
+            fprintf(outfile, " ---- 0x%" PRIx64, pc);
+            first_insn = 0;
+            nb_oargs = def->nb_oargs;
+            nb_iargs = def->nb_iargs;
+            nb_cargs = def->nb_cargs;
+        } else if (c == INDEX_op_call) {
+            TCGArg arg;
+
+            /* variable number of arguments */
+            arg = *args++;
+            nb_oargs = arg >> 16;
+            nb_iargs = arg & 0xffff;
+            nb_cargs = def->nb_cargs;
+
+            fprintf(outfile, " %s ", def->name);
+
+            /* function name */
+            fprintf(outfile, "%s",
+                    tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + nb_iargs - 1]));
+            /* flags */
+            fprintf(outfile, ",$0x%" TCG_PRIlx,
+                    args[nb_oargs + nb_iargs]);
+            /* nb out args */
+            fprintf(outfile, ",$%d", nb_oargs);
+            for(i = 0; i < nb_oargs; i++) {
+                fprintf(outfile, ",");
+                fprintf(outfile, "%s",
+                        tcg_get_arg_str_idx(s, buf, sizeof(buf), args[i]));
+            }
+            for(i = 0; i < (nb_iargs - 1); i++) {
+                fprintf(outfile, ",");
+                if (args[nb_oargs + i] == TCG_CALL_DUMMY_ARG) {
+                    fprintf(outfile, "<dummy>");
+                } else {
+                    fprintf(outfile, "%s",
+                            tcg_get_arg_str_idx(s, buf, sizeof(buf), args[nb_oargs + i]));
+                }
+            }
+        } else if (c == INDEX_op_movi_i32 
+#if TCG_TARGET_REG_BITS == 64
+                   || c == INDEX_op_movi_i64
+#endif
+                   ) {
+            tcg_target_ulong val;
+            TCGHelperInfo *th;
+
+            nb_oargs = def->nb_oargs;
+            nb_iargs = def->nb_iargs;
+            nb_cargs = def->nb_cargs;
+            fprintf(outfile, " %s %s,$", def->name, 
+                    tcg_get_arg_str_idx(s, buf, sizeof(buf), args[0]));
+            val = args[1];
+            th = tcg_find_helper(s, val);
+            if (th) {
+                fprintf(outfile, th->name);
+            } else {
+                if (c == INDEX_op_movi_i32)
+                    fprintf(outfile, "0x%x", (uint32_t)val);
+                else
+                    fprintf(outfile, "0x%" PRIx64 , (uint64_t)val);
+            }
+        } else {
+            fprintf(outfile, " %s ", def->name);
+            if (c == INDEX_op_nopn) {
+                /* variable number of arguments */
+                nb_cargs = *args;
+                nb_oargs = 0;
+                nb_iargs = 0;
+            } else {
+                nb_oargs = def->nb_oargs;
+                nb_iargs = def->nb_iargs;
+                nb_cargs = def->nb_cargs;
+            }
+            
+            k = 0;
+            for(i = 0; i < nb_oargs; i++) {
+                if (k != 0)
+                    fprintf(outfile, ",");
+                fprintf(outfile, "%s",
+                        tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++]));
+            }
+            for(i = 0; i < nb_iargs; i++) {
+                if (k != 0)
+                    fprintf(outfile, ",");
+                fprintf(outfile, "%s",
+                        tcg_get_arg_str_idx(s, buf, sizeof(buf), args[k++]));
+            }
+            if (c == INDEX_op_brcond_i32
+#if TCG_TARGET_REG_BITS == 32
+                || c == INDEX_op_brcond2_i32
+#elif TCG_TARGET_REG_BITS == 64
+                || c == INDEX_op_brcond_i64
+#endif
+                ) {
+                if (args[k] < ARRAY_SIZE(cond_name) && cond_name[args[k]])
+                    fprintf(outfile, ",%s", cond_name[args[k++]]);
+                else
+                    fprintf(outfile, ",$0x%" TCG_PRIlx, args[k++]);
+                i = 1;
+            }
+            else
+                i = 0;
+            for(; i < nb_cargs; i++) {
+                if (k != 0)
+                    fprintf(outfile, ",");
+                arg = args[k++];
+                fprintf(outfile, "$0x%" TCG_PRIlx, arg);
+            }
+        }
+        fprintf(outfile, "\n");
+        args += nb_iargs + nb_oargs + nb_cargs;
+    }
+}
+
+/* we give more priority to constraints with less registers */
+static int get_constraint_priority(const TCGOpDef *def, int k)
+{
+    const TCGArgConstraint *arg_ct;
+
+    int i, n;
+    arg_ct = &def->args_ct[k];
+    if (arg_ct->ct & TCG_CT_ALIAS) {
+        /* an alias is equivalent to a single register */
+        n = 1;
+    } else {
+        if (!(arg_ct->ct & TCG_CT_REG))
+            return 0;
+        n = 0;
+        for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
+            if (tcg_regset_test_reg(arg_ct->u.regs, i))
+                n++;
+        }
+    }
+    return TCG_TARGET_NB_REGS - n + 1;
+}
+
+/* sort from highest priority to lowest */
+static void sort_constraints(TCGOpDef *def, int start, int n)
+{
+    int i, j, p1, p2, tmp;
+
+    for(i = 0; i < n; i++)
+        def->sorted_args[start + i] = start + i;
+    if (n <= 1)
+        return;
+    for(i = 0; i < n - 1; i++) {
+        for(j = i + 1; j < n; j++) {
+            p1 = get_constraint_priority(def, def->sorted_args[start + i]);
+            p2 = get_constraint_priority(def, def->sorted_args[start + j]);
+            if (p1 < p2) {
+                tmp = def->sorted_args[start + i];
+                def->sorted_args[start + i] = def->sorted_args[start + j];
+                def->sorted_args[start + j] = tmp;
+            }
+        }
+    }
+}
+
+void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs)
+{
+    int op;
+    TCGOpDef *def;
+    const char *ct_str;
+    int i, nb_args;
+
+    for(;;) {
+        if (tdefs->op < 0)
+            break;
+        op = tdefs->op;
+        assert(op >= 0 && op < NB_OPS);
+        def = &tcg_op_defs[op];
+        nb_args = def->nb_iargs + def->nb_oargs;
+        for(i = 0; i < nb_args; i++) {
+            ct_str = tdefs->args_ct_str[i];
+            tcg_regset_clear(def->args_ct[i].u.regs);
+            def->args_ct[i].ct = 0;
+            if (ct_str[0] >= '0' && ct_str[0] <= '9') {
+                int oarg;
+                oarg = ct_str[0] - '0';
+                assert(oarg < def->nb_oargs);
+                assert(def->args_ct[oarg].ct & TCG_CT_REG);
+                /* TCG_CT_ALIAS is for the output arguments. The input
+                   argument is tagged with TCG_CT_IALIAS. */
+                def->args_ct[i] = def->args_ct[oarg];
+                def->args_ct[oarg].ct = TCG_CT_ALIAS;
+                def->args_ct[oarg].alias_index = i;
+                def->args_ct[i].ct |= TCG_CT_IALIAS;
+                def->args_ct[i].alias_index = oarg;
+            } else {
+                for(;;) {
+                    if (*ct_str == '\0')
+                        break;
+                    switch(*ct_str) {
+                    case 'i':
+                        def->args_ct[i].ct |= TCG_CT_CONST;
+                        ct_str++;
+                        break;
+                    default:
+                        if (target_parse_constraint(&def->args_ct[i], &ct_str) < 0) {
+                            fprintf(stderr, "Invalid constraint '%s' for arg %d of operation '%s'\n",
+                                    ct_str, i, def->name);
+                            exit(1);
+                        }
+                    }
+                }
+            }
+        }
+
+        /* sort the constraints (XXX: this is just an heuristic) */
+        sort_constraints(def, 0, def->nb_oargs);
+        sort_constraints(def, def->nb_oargs, def->nb_iargs);
+
+#if 0
+        {
+            int i;
+
+            printf("%s: sorted=", def->name);
+            for(i = 0; i < def->nb_oargs + def->nb_iargs; i++)
+                printf(" %d", def->sorted_args[i]);
+            printf("\n");
+        }
+#endif
+        tdefs++;
+    }
+
+}
+
+#ifdef USE_LIVENESS_ANALYSIS
+
+/* set a nop for an operation using 'nb_args' */
+static inline void tcg_set_nop(TCGContext *s, uint16_t *opc_ptr, 
+                               TCGArg *args, int nb_args)
+{
+    if (nb_args == 0) {
+        *opc_ptr = INDEX_op_nop;
+    } else {
+        *opc_ptr = INDEX_op_nopn;
+        args[0] = nb_args;
+        args[nb_args - 1] = nb_args;
+    }
+}
+
+/* liveness analysis: end of function: globals are live, temps are
+   dead. */
+/* XXX: at this stage, not used as there would be little gains because
+   most TBs end with a conditional jump. */
+static inline void tcg_la_func_end(TCGContext *s, uint8_t *dead_temps)
+{
+    memset(dead_temps, 0, s->nb_globals);
+    memset(dead_temps + s->nb_globals, 1, s->nb_temps - s->nb_globals);
+}
+
+/* liveness analysis: end of basic block: globals are live, temps are
+   dead, local temps are live. */
+static inline void tcg_la_bb_end(TCGContext *s, uint8_t *dead_temps)
+{
+    int i;
+    TCGTemp *ts;
+
+    memset(dead_temps, 0, s->nb_globals);
+    ts = &s->temps[s->nb_globals];
+    for(i = s->nb_globals; i < s->nb_temps; i++) {
+        if (ts->temp_local)
+            dead_temps[i] = 0;
+        else
+            dead_temps[i] = 1;
+        ts++;
+    }
+}
+
+/* Liveness analysis : update the opc_dead_iargs array to tell if a
+   given input arguments is dead. Instructions updating dead
+   temporaries are removed. */
+static void tcg_liveness_analysis(TCGContext *s)
+{
+    int i, op_index, op, nb_args, nb_iargs, nb_oargs, arg, nb_ops;
+    TCGArg *args;
+    const TCGOpDef *def;
+    uint8_t *dead_temps;
+    unsigned int dead_iargs;
+    
+    gen_opc_ptr++; /* skip end */
+
+    nb_ops = gen_opc_ptr - gen_opc_buf;
+
+    /* XXX: make it really dynamic */
+    s->op_dead_iargs = tcg_malloc(OPC_BUF_SIZE * sizeof(uint16_t));
+    
+    dead_temps = tcg_malloc(s->nb_temps);
+    memset(dead_temps, 1, s->nb_temps);
+
+    args = gen_opparam_ptr;
+    op_index = nb_ops - 1;
+    while (op_index >= 0) {
+        op = gen_opc_buf[op_index];
+        def = &tcg_op_defs[op];
+        switch(op) {
+        case INDEX_op_call:
+            {
+                int call_flags;
+
+                nb_args = args[-1];
+                args -= nb_args;
+                nb_iargs = args[0] & 0xffff;
+                nb_oargs = args[0] >> 16;
+                args++;
+                call_flags = args[nb_oargs + nb_iargs];
+
+                /* pure functions can be removed if their result is not
+                   used */
+                if (call_flags & TCG_CALL_PURE) {
+                    for(i = 0; i < nb_oargs; i++) {
+                        arg = args[i];
+                        if (!dead_temps[arg])
+                            goto do_not_remove_call;
+                    }
+                    tcg_set_nop(s, gen_opc_buf + op_index, 
+                                args - 1, nb_args);
+                } else {
+                do_not_remove_call:
+
+                    /* output args are dead */
+                    for(i = 0; i < nb_oargs; i++) {
+                        arg = args[i];
+                        dead_temps[arg] = 1;
+                    }
+                    
+                    /* globals are live (they may be used by the call) */
+                    memset(dead_temps, 0, s->nb_globals);
+                    
+                    /* input args are live */
+                    dead_iargs = 0;
+                    for(i = 0; i < nb_iargs; i++) {
+                        arg = args[i + nb_oargs];
+                        if (arg != TCG_CALL_DUMMY_ARG) {
+                            if (dead_temps[arg]) {
+                                dead_iargs |= (1 << i);
+                            }
+                            dead_temps[arg] = 0;
+                        }
+                    }
+                    s->op_dead_iargs[op_index] = dead_iargs;
+                }
+                args--;
+            }
+            break;
+        case INDEX_op_set_label:
+            args--;
+            /* mark end of basic block */
+            tcg_la_bb_end(s, dead_temps);
+            break;
+        case INDEX_op_debug_insn_start:
+            args -= def->nb_args;
+            break;
+        case INDEX_op_nopn:
+            nb_args = args[-1];
+            args -= nb_args;
+            break;
+        case INDEX_op_discard:
+            args--;
+            /* mark the temporary as dead */
+            dead_temps[args[0]] = 1;
+            break;
+        case INDEX_op_end:
+            break;
+            /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */
+        default:
+            if (op > INDEX_op_end) {
+                args -= def->nb_args;
+                nb_iargs = def->nb_iargs;
+                nb_oargs = def->nb_oargs;
+
+                /* Test if the operation can be removed because all
+                   its outputs are dead. We assume that nb_oargs == 0
+                   implies side effects */
+                if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) {
+                    for(i = 0; i < nb_oargs; i++) {
+                        arg = args[i];
+                        if (!dead_temps[arg])
+                            goto do_not_remove;
+                    }
+                    tcg_set_nop(s, gen_opc_buf + op_index, args, def->nb_args);
+#ifdef CONFIG_PROFILER
+                    s->del_op_count++;
+#endif
+                } else {
+                do_not_remove:
+
+                    /* output args are dead */
+                    for(i = 0; i < nb_oargs; i++) {
+                        arg = args[i];
+                        dead_temps[arg] = 1;
+                    }
+                    
+                    /* if end of basic block, update */
+                    if (def->flags & TCG_OPF_BB_END) {
+                        tcg_la_bb_end(s, dead_temps);
+                    } else if (def->flags & TCG_OPF_CALL_CLOBBER) {
+                        /* globals are live */
+                        memset(dead_temps, 0, s->nb_globals);
+                    }
+                    
+                    /* input args are live */
+                    dead_iargs = 0;
+                    for(i = 0; i < nb_iargs; i++) {
+                        arg = args[i + nb_oargs];
+                        if (dead_temps[arg]) {
+                            dead_iargs |= (1 << i);
+                        }
+                        dead_temps[arg] = 0;
+                    }
+                    s->op_dead_iargs[op_index] = dead_iargs;
+                }
+            } else {
+                /* legacy dyngen operations */
+                args -= def->nb_args;
+                /* mark end of basic block */
+                tcg_la_bb_end(s, dead_temps);
+            }
+            break;
+        }
+        op_index--;
+    }
+
+    if (args != gen_opparam_buf)
+        tcg_abort();
+}
+#else
+/* dummy liveness analysis */
+void tcg_liveness_analysis(TCGContext *s)
+{
+    int nb_ops;
+    nb_ops = gen_opc_ptr - gen_opc_buf;
+
+    s->op_dead_iargs = tcg_malloc(nb_ops * sizeof(uint16_t));
+    memset(s->op_dead_iargs, 0, nb_ops * sizeof(uint16_t));
+}
+#endif
+
+#ifndef NDEBUG
+static void dump_regs(TCGContext *s)
+{
+    TCGTemp *ts;
+    int i;
+    char buf[64];
+
+    for(i = 0; i < s->nb_temps; i++) {
+        ts = &s->temps[i];
+        printf("  %10s: ", tcg_get_arg_str_idx(s, buf, sizeof(buf), i));
+        switch(ts->val_type) {
+        case TEMP_VAL_REG:
+            printf("%s", tcg_target_reg_names[ts->reg]);
+            break;
+        case TEMP_VAL_MEM:
+            printf("%d(%s)", (int)ts->mem_offset, tcg_target_reg_names[ts->mem_reg]);
+            break;
+        case TEMP_VAL_CONST:
+            printf("$0x%" TCG_PRIlx, ts->val);
+            break;
+        case TEMP_VAL_DEAD:
+            printf("D");
+            break;
+        default:
+            printf("???");
+            break;
+        }
+        printf("\n");
+    }
+
+    for(i = 0; i < TCG_TARGET_NB_REGS; i++) {
+        if (s->reg_to_temp[i] >= 0) {
+            printf("%s: %s\n", 
+                   tcg_target_reg_names[i], 
+                   tcg_get_arg_str_idx(s, buf, sizeof(buf), s->reg_to_temp[i]));
+        }
+    }
+}
+
+static void check_regs(TCGContext *s)
+{
+    int reg, k;
+    TCGTemp *ts;
+    char buf[64];
+
+    for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
+        k = s->reg_to_temp[reg];
+        if (k >= 0) {
+            ts = &s->temps[k];
+            if (ts->val_type != TEMP_VAL_REG ||
+                ts->reg != reg) {
+                printf("Inconsistency for register %s:\n", 
+                       tcg_target_reg_names[reg]);
+                goto fail;
+            }
+        }
+    }
+    for(k = 0; k < s->nb_temps; k++) {
+        ts = &s->temps[k];
+        if (ts->val_type == TEMP_VAL_REG &&
+            !ts->fixed_reg &&
+            s->reg_to_temp[ts->reg] != k) {
+                printf("Inconsistency for temp %s:\n", 
+                       tcg_get_arg_str_idx(s, buf, sizeof(buf), k));
+        fail:
+                printf("reg state:\n");
+                dump_regs(s);
+                tcg_abort();
+        }
+    }
+}
+#endif
+
+static void temp_allocate_frame(TCGContext *s, int temp)
+{
+    TCGTemp *ts;
+    ts = &s->temps[temp];
+    s->current_frame_offset = (s->current_frame_offset + sizeof(tcg_target_long) - 1) & ~(sizeof(tcg_target_long) - 1);
+    if (s->current_frame_offset + sizeof(tcg_target_long) > s->frame_end)
+        tcg_abort();
+    ts->mem_offset = s->current_frame_offset;
+    ts->mem_reg = s->frame_reg;
+    ts->mem_allocated = 1;
+    s->current_frame_offset += sizeof(tcg_target_long);
+}
+
+/* free register 'reg' by spilling the corresponding temporary if necessary */
+static void tcg_reg_free(TCGContext *s, int reg)
+{
+    TCGTemp *ts;
+    int temp;
+
+    temp = s->reg_to_temp[reg];
+    if (temp != -1) {
+        ts = &s->temps[temp];
+        assert(ts->val_type == TEMP_VAL_REG);
+        if (!ts->mem_coherent) {
+            if (!ts->mem_allocated) 
+                temp_allocate_frame(s, temp);
+            tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+        }
+        ts->val_type = TEMP_VAL_MEM;
+        s->reg_to_temp[reg] = -1;
+    }
+}
+
+/* Allocate a register belonging to reg1 & ~reg2 */
+static int tcg_reg_alloc(TCGContext *s, TCGRegSet reg1, TCGRegSet reg2)
+{
+    int i, reg;
+    TCGRegSet reg_ct;
+
+    tcg_regset_andnot(reg_ct, reg1, reg2);
+
+    /* first try free registers */
+    for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) {
+        reg = tcg_target_reg_alloc_order[i];
+        if (tcg_regset_test_reg(reg_ct, reg) && s->reg_to_temp[reg] == -1)
+            return reg;
+    }
+
+    /* XXX: do better spill choice */
+    for(i = 0; i < ARRAY_SIZE(tcg_target_reg_alloc_order); i++) {
+        reg = tcg_target_reg_alloc_order[i];
+        if (tcg_regset_test_reg(reg_ct, reg)) {
+            tcg_reg_free(s, reg);
+            return reg;
+        }
+    }
+
+    tcg_abort();
+}
+
+/* save a temporary to memory. 'allocated_regs' is used in case a
+   temporary registers needs to be allocated to store a constant. */
+static void temp_save(TCGContext *s, int temp, TCGRegSet allocated_regs)
+{
+    TCGTemp *ts;
+    int reg;
+
+    ts = &s->temps[temp];
+    if (!ts->fixed_reg) {
+        switch(ts->val_type) {
+        case TEMP_VAL_REG:
+            tcg_reg_free(s, ts->reg);
+            break;
+        case TEMP_VAL_DEAD:
+            ts->val_type = TEMP_VAL_MEM;
+            break;
+        case TEMP_VAL_CONST:
+            reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], 
+                                allocated_regs);
+            if (!ts->mem_allocated) 
+                temp_allocate_frame(s, temp);
+            tcg_out_movi(s, ts->type, reg, ts->val);
+            tcg_out_st(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+            ts->val_type = TEMP_VAL_MEM;
+            break;
+        case TEMP_VAL_MEM:
+            break;
+        default:
+            tcg_abort();
+        }
+    }
+}
+
+/* save globals to their cannonical location and assume they can be
+   modified be the following code. 'allocated_regs' is used in case a
+   temporary registers needs to be allocated to store a constant. */
+static void save_globals(TCGContext *s, TCGRegSet allocated_regs)
+{
+    int i;
+
+    for(i = 0; i < s->nb_globals; i++) {
+        temp_save(s, i, allocated_regs);
+    }
+}
+
+/* at the end of a basic block, we assume all temporaries are dead and
+   all globals are stored at their canonical location. */
+static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs)
+{
+    TCGTemp *ts;
+    int i;
+
+    for(i = s->nb_globals; i < s->nb_temps; i++) {
+        ts = &s->temps[i];
+        if (ts->temp_local) {
+            temp_save(s, i, allocated_regs);
+        } else {
+            if (ts->val_type == TEMP_VAL_REG) {
+                s->reg_to_temp[ts->reg] = -1;
+            }
+            ts->val_type = TEMP_VAL_DEAD;
+        }
+    }
+
+    save_globals(s, allocated_regs);
+}
+
+#define IS_DEAD_IARG(n) ((dead_iargs >> (n)) & 1)
+
+static void tcg_reg_alloc_movi(TCGContext *s, const TCGArg *args)
+{
+    TCGTemp *ots;
+    tcg_target_ulong val;
+
+    ots = &s->temps[args[0]];
+    val = args[1];
+
+    if (ots->fixed_reg) {
+        /* for fixed registers, we do not do any constant
+           propagation */
+        tcg_out_movi(s, ots->type, ots->reg, val);
+    } else {
+        /* The movi is not explicitly generated here */
+        if (ots->val_type == TEMP_VAL_REG)
+            s->reg_to_temp[ots->reg] = -1;
+        ots->val_type = TEMP_VAL_CONST;
+        ots->val = val;
+    }
+}
+
+static void tcg_reg_alloc_mov(TCGContext *s, const TCGOpDef *def,
+                              const TCGArg *args,
+                              unsigned int dead_iargs)
+{
+    TCGTemp *ts, *ots;
+    int reg;
+    const TCGArgConstraint *arg_ct;
+
+    ots = &s->temps[args[0]];
+    ts = &s->temps[args[1]];
+    arg_ct = &def->args_ct[0];
+
+    /* XXX: always mark arg dead if IS_DEAD_IARG(0) */
+    if (ts->val_type == TEMP_VAL_REG) {
+        if (IS_DEAD_IARG(0) && !ts->fixed_reg && !ots->fixed_reg) {
+            /* the mov can be suppressed */
+            if (ots->val_type == TEMP_VAL_REG)
+                s->reg_to_temp[ots->reg] = -1;
+            reg = ts->reg;
+            s->reg_to_temp[reg] = -1;
+            ts->val_type = TEMP_VAL_DEAD;
+        } else {
+            if (ots->val_type == TEMP_VAL_REG) {
+                reg = ots->reg;
+            } else {
+                reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
+            }
+            if (ts->reg != reg) {
+                tcg_out_mov(s, reg, ts->reg);
+            }
+        }
+    } else if (ts->val_type == TEMP_VAL_MEM) {
+        if (ots->val_type == TEMP_VAL_REG) {
+            reg = ots->reg;
+        } else {
+            reg = tcg_reg_alloc(s, arg_ct->u.regs, s->reserved_regs);
+        }
+        tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+    } else if (ts->val_type == TEMP_VAL_CONST) {
+        if (ots->fixed_reg) {
+            reg = ots->reg;
+            tcg_out_movi(s, ots->type, reg, ts->val);
+        } else {
+            /* propagate constant */
+            if (ots->val_type == TEMP_VAL_REG)
+                s->reg_to_temp[ots->reg] = -1;
+            ots->val_type = TEMP_VAL_CONST;
+            ots->val = ts->val;
+            return;
+        }
+    } else {
+        tcg_abort();
+    }
+    s->reg_to_temp[reg] = args[0];
+    ots->reg = reg;
+    ots->val_type = TEMP_VAL_REG;
+    ots->mem_coherent = 0;
+}
+
+static void tcg_reg_alloc_op(TCGContext *s, 
+                             const TCGOpDef *def, int opc,
+                             const TCGArg *args,
+                             unsigned int dead_iargs)
+{
+    TCGRegSet allocated_regs;
+    int i, k, nb_iargs, nb_oargs, reg;
+    TCGArg arg;
+    const TCGArgConstraint *arg_ct;
+    TCGTemp *ts;
+    TCGArg new_args[TCG_MAX_OP_ARGS];
+    int const_args[TCG_MAX_OP_ARGS];
+
+    nb_oargs = def->nb_oargs;
+    nb_iargs = def->nb_iargs;
+
+    /* copy constants */
+    memcpy(new_args + nb_oargs + nb_iargs, 
+           args + nb_oargs + nb_iargs, 
+           sizeof(TCGArg) * def->nb_cargs);
+
+    /* satisfy input constraints */ 
+    tcg_regset_set(allocated_regs, s->reserved_regs);
+    for(k = 0; k < nb_iargs; k++) {
+        i = def->sorted_args[nb_oargs + k];
+        arg = args[i];
+        arg_ct = &def->args_ct[i];
+        ts = &s->temps[arg];
+        if (ts->val_type == TEMP_VAL_MEM) {
+            reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+            tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+            ts->val_type = TEMP_VAL_REG;
+            ts->reg = reg;
+            ts->mem_coherent = 1;
+            s->reg_to_temp[reg] = arg;
+        } else if (ts->val_type == TEMP_VAL_CONST) {
+            if (tcg_target_const_match(ts->val, arg_ct)) {
+                /* constant is OK for instruction */
+                const_args[i] = 1;
+                new_args[i] = ts->val;
+                goto iarg_end;
+            } else {
+                /* need to move to a register */
+                reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+                tcg_out_movi(s, ts->type, reg, ts->val);
+                ts->val_type = TEMP_VAL_REG;
+                ts->reg = reg;
+                ts->mem_coherent = 0;
+                s->reg_to_temp[reg] = arg;
+            }
+        }
+        assert(ts->val_type == TEMP_VAL_REG);
+        if (arg_ct->ct & TCG_CT_IALIAS) {
+            if (ts->fixed_reg) {
+                /* if fixed register, we must allocate a new register
+                   if the alias is not the same register */
+                if (arg != args[arg_ct->alias_index])
+                    goto allocate_in_reg;
+            } else {
+                /* if the input is aliased to an output and if it is
+                   not dead after the instruction, we must allocate
+                   a new register and move it */
+                if (!IS_DEAD_IARG(i - nb_oargs)) 
+                    goto allocate_in_reg;
+            }
+        }
+        reg = ts->reg;
+        if (tcg_regset_test_reg(arg_ct->u.regs, reg)) {
+            /* nothing to do : the constraint is satisfied */
+        } else {
+        allocate_in_reg:
+            /* allocate a new register matching the constraint 
+               and move the temporary register into it */
+            reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+            tcg_out_mov(s, reg, ts->reg);
+        }
+        new_args[i] = reg;
+        const_args[i] = 0;
+        tcg_regset_set_reg(allocated_regs, reg);
+    iarg_end: ;
+    }
+    
+    if (def->flags & TCG_OPF_BB_END) {
+        tcg_reg_alloc_bb_end(s, allocated_regs);
+    } else {
+        /* mark dead temporaries and free the associated registers */
+        for(i = 0; i < nb_iargs; i++) {
+            arg = args[nb_oargs + i];
+            if (IS_DEAD_IARG(i)) {
+                ts = &s->temps[arg];
+                if (!ts->fixed_reg) {
+                    if (ts->val_type == TEMP_VAL_REG)
+                        s->reg_to_temp[ts->reg] = -1;
+                    ts->val_type = TEMP_VAL_DEAD;
+                }
+            }
+        }
+        
+        if (def->flags & TCG_OPF_CALL_CLOBBER) {
+            /* XXX: permit generic clobber register list ? */ 
+            for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
+                if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) {
+                    tcg_reg_free(s, reg);
+                }
+            }
+            /* XXX: for load/store we could do that only for the slow path
+               (i.e. when a memory callback is called) */
+            
+            /* store globals and free associated registers (we assume the insn
+               can modify any global. */
+            save_globals(s, allocated_regs);
+        }
+        
+        /* satisfy the output constraints */
+        tcg_regset_set(allocated_regs, s->reserved_regs);
+        for(k = 0; k < nb_oargs; k++) {
+            i = def->sorted_args[k];
+            arg = args[i];
+            arg_ct = &def->args_ct[i];
+            ts = &s->temps[arg];
+            if (arg_ct->ct & TCG_CT_ALIAS) {
+                reg = new_args[arg_ct->alias_index];
+            } else {
+                /* if fixed register, we try to use it */
+                reg = ts->reg;
+                if (ts->fixed_reg &&
+                    tcg_regset_test_reg(arg_ct->u.regs, reg)) {
+                    goto oarg_end;
+                }
+                reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+            }
+            tcg_regset_set_reg(allocated_regs, reg);
+            /* if a fixed register is used, then a move will be done afterwards */
+            if (!ts->fixed_reg) {
+                if (ts->val_type == TEMP_VAL_REG)
+                    s->reg_to_temp[ts->reg] = -1;
+                ts->val_type = TEMP_VAL_REG;
+                ts->reg = reg;
+                /* temp value is modified, so the value kept in memory is
+                   potentially not the same */
+                ts->mem_coherent = 0; 
+                s->reg_to_temp[reg] = arg;
+            }
+        oarg_end:
+            new_args[i] = reg;
+        }
+    }
+
+    /* emit instruction */
+    tcg_out_op(s, opc, new_args, const_args);
+    
+    /* move the outputs in the correct register if needed */
+    for(i = 0; i < nb_oargs; i++) {
+        ts = &s->temps[args[i]];
+        reg = new_args[i];
+        if (ts->fixed_reg && ts->reg != reg) {
+            tcg_out_mov(s, ts->reg, reg);
+        }
+    }
+}
+
+#ifdef TCG_TARGET_STACK_GROWSUP
+#define STACK_DIR(x) (-(x))
+#else
+#define STACK_DIR(x) (x)
+#endif
+
+static int tcg_reg_alloc_call(TCGContext *s, const TCGOpDef *def,
+                              int opc, const TCGArg *args,
+                              unsigned int dead_iargs)
+{
+    int nb_iargs, nb_oargs, flags, nb_regs, i, reg, nb_params;
+    TCGArg arg, func_arg;
+    TCGTemp *ts;
+    tcg_target_long stack_offset, call_stack_size, func_addr;
+    int const_func_arg, allocate_args;
+    TCGRegSet allocated_regs;
+    const TCGArgConstraint *arg_ct;
+
+    arg = *args++;
+
+    nb_oargs = arg >> 16;
+    nb_iargs = arg & 0xffff;
+    nb_params = nb_iargs - 1;
+
+    flags = args[nb_oargs + nb_iargs];
+
+    nb_regs = tcg_target_get_call_iarg_regs_count(flags);
+    if (nb_regs > nb_params)
+        nb_regs = nb_params;
+
+    /* assign stack slots first */
+    /* XXX: preallocate call stack */
+    call_stack_size = (nb_params - nb_regs) * sizeof(tcg_target_long);
+    call_stack_size = (call_stack_size + TCG_TARGET_STACK_ALIGN - 1) & 
+        ~(TCG_TARGET_STACK_ALIGN - 1);
+    allocate_args = (call_stack_size > TCG_STATIC_CALL_ARGS_SIZE);
+    if (allocate_args) {
+        tcg_out_addi(s, TCG_REG_CALL_STACK, -STACK_DIR(call_stack_size));
+    }
+
+    stack_offset = TCG_TARGET_CALL_STACK_OFFSET;
+    for(i = nb_regs; i < nb_params; i++) {
+        arg = args[nb_oargs + i];
+#ifdef TCG_TARGET_STACK_GROWSUP
+        stack_offset -= sizeof(tcg_target_long);
+#endif
+        if (arg != TCG_CALL_DUMMY_ARG) {
+            ts = &s->temps[arg];
+            if (ts->val_type == TEMP_VAL_REG) {
+                tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset);
+            } else if (ts->val_type == TEMP_VAL_MEM) {
+                reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], 
+                                    s->reserved_regs);
+                /* XXX: not correct if reading values from the stack */
+                tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+                tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset);
+            } else if (ts->val_type == TEMP_VAL_CONST) {
+                reg = tcg_reg_alloc(s, tcg_target_available_regs[ts->type], 
+                                    s->reserved_regs);
+                /* XXX: sign extend may be needed on some targets */
+                tcg_out_movi(s, ts->type, reg, ts->val);
+                tcg_out_st(s, ts->type, reg, TCG_REG_CALL_STACK, stack_offset);
+            } else {
+                tcg_abort();
+            }
+        }
+#ifndef TCG_TARGET_STACK_GROWSUP
+        stack_offset += sizeof(tcg_target_long);
+#endif
+    }
+    
+    /* assign input registers */
+    tcg_regset_set(allocated_regs, s->reserved_regs);
+    for(i = 0; i < nb_regs; i++) {
+        arg = args[nb_oargs + i];
+        if (arg != TCG_CALL_DUMMY_ARG) {
+            ts = &s->temps[arg];
+            reg = tcg_target_call_iarg_regs[i];
+            tcg_reg_free(s, reg);
+            if (ts->val_type == TEMP_VAL_REG) {
+                if (ts->reg != reg) {
+                    tcg_out_mov(s, reg, ts->reg);
+                }
+            } else if (ts->val_type == TEMP_VAL_MEM) {
+                tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+            } else if (ts->val_type == TEMP_VAL_CONST) {
+                /* XXX: sign extend ? */
+                tcg_out_movi(s, ts->type, reg, ts->val);
+            } else {
+                tcg_abort();
+            }
+            tcg_regset_set_reg(allocated_regs, reg);
+        }
+    }
+    
+    /* assign function address */
+    func_arg = args[nb_oargs + nb_iargs - 1];
+    arg_ct = &def->args_ct[0];
+    ts = &s->temps[func_arg];
+    func_addr = ts->val;
+    const_func_arg = 0;
+    if (ts->val_type == TEMP_VAL_MEM) {
+        reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+        tcg_out_ld(s, ts->type, reg, ts->mem_reg, ts->mem_offset);
+        func_arg = reg;
+        tcg_regset_set_reg(allocated_regs, reg);
+    } else if (ts->val_type == TEMP_VAL_REG) {
+        reg = ts->reg;
+        if (!tcg_regset_test_reg(arg_ct->u.regs, reg)) {
+            reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+            tcg_out_mov(s, reg, ts->reg);
+        }
+        func_arg = reg;
+        tcg_regset_set_reg(allocated_regs, reg);
+    } else if (ts->val_type == TEMP_VAL_CONST) {
+        if (tcg_target_const_match(func_addr, arg_ct)) {
+            const_func_arg = 1;
+            func_arg = func_addr;
+        } else {
+            reg = tcg_reg_alloc(s, arg_ct->u.regs, allocated_regs);
+            tcg_out_movi(s, ts->type, reg, func_addr);
+            func_arg = reg;
+            tcg_regset_set_reg(allocated_regs, reg);
+        }
+    } else {
+        tcg_abort();
+    }
+        
+    
+    /* mark dead temporaries and free the associated registers */
+    for(i = 0; i < nb_iargs; i++) {
+        arg = args[nb_oargs + i];
+        if (IS_DEAD_IARG(i)) {
+            ts = &s->temps[arg];
+            if (!ts->fixed_reg) {
+                if (ts->val_type == TEMP_VAL_REG)
+                    s->reg_to_temp[ts->reg] = -1;
+                ts->val_type = TEMP_VAL_DEAD;
+            }
+        }
+    }
+    
+    /* clobber call registers */
+    for(reg = 0; reg < TCG_TARGET_NB_REGS; reg++) {
+        if (tcg_regset_test_reg(tcg_target_call_clobber_regs, reg)) {
+            tcg_reg_free(s, reg);
+        }
+    }
+    
+    /* store globals and free associated registers (we assume the call
+       can modify any global. */
+    save_globals(s, allocated_regs);
+
+    tcg_out_op(s, opc, &func_arg, &const_func_arg);
+    
+    if (allocate_args) {
+        tcg_out_addi(s, TCG_REG_CALL_STACK, STACK_DIR(call_stack_size));
+    }
+
+    /* assign output registers and emit moves if needed */
+    for(i = 0; i < nb_oargs; i++) {
+        arg = args[i];
+        ts = &s->temps[arg];
+        reg = tcg_target_call_oarg_regs[i];
+        assert(s->reg_to_temp[reg] == -1);
+        if (ts->fixed_reg) {
+            if (ts->reg != reg) {
+                tcg_out_mov(s, ts->reg, reg);
+            }
+        } else {
+            if (ts->val_type == TEMP_VAL_REG)
+                s->reg_to_temp[ts->reg] = -1;
+            ts->val_type = TEMP_VAL_REG;
+            ts->reg = reg;
+            ts->mem_coherent = 0; 
+            s->reg_to_temp[reg] = arg;
+        }
+    }
+    
+    return nb_iargs + nb_oargs + def->nb_cargs + 1;
+}
+
+#ifdef CONFIG_PROFILER
+
+static int64_t dyngen_table_op_count[NB_OPS];
+
+void dump_op_count(void)
+{
+    int i;
+    FILE *f;
+    f = fopen("/tmp/op1.log", "w");
+    for(i = 0; i < INDEX_op_end; i++) {
+        fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, dyngen_table_op_count[i]);
+    }
+    fclose(f);
+    f = fopen("/tmp/op2.log", "w");
+    for(i = INDEX_op_end; i < NB_OPS; i++) {
+        fprintf(f, "%s %" PRId64 "\n", tcg_op_defs[i].name, dyngen_table_op_count[i]);
+    }
+    fclose(f);
+}
+#endif
+
+
+static inline int tcg_gen_code_common(TCGContext *s, uint8_t *gen_code_buf,
+                                      long search_pc)
+{
+    int opc, op_index;
+    const TCGOpDef *def;
+    unsigned int dead_iargs;
+    const TCGArg *args;
+
+#ifdef DEBUG_DISAS
+    if (unlikely(loglevel & CPU_LOG_TB_OP)) {
+        fprintf(logfile, "OP:\n");
+        tcg_dump_ops(s, logfile);
+        fprintf(logfile, "\n");
+    }
+#endif
+
+#ifdef CONFIG_PROFILER
+    s->la_time -= profile_getclock();
+#endif
+    tcg_liveness_analysis(s);
+#ifdef CONFIG_PROFILER
+    s->la_time += profile_getclock();
+#endif
+
+#ifdef DEBUG_DISAS
+    if (unlikely(loglevel & CPU_LOG_TB_OP_OPT)) {
+        fprintf(logfile, "OP after la:\n");
+        tcg_dump_ops(s, logfile);
+        fprintf(logfile, "\n");
+    }
+#endif
+
+    tcg_reg_alloc_start(s);
+
+    s->code_buf = gen_code_buf;
+    s->code_ptr = gen_code_buf;
+
+    args = gen_opparam_buf;
+    op_index = 0;
+
+    for(;;) {
+        opc = gen_opc_buf[op_index];
+#ifdef CONFIG_PROFILER
+        dyngen_table_op_count[opc]++;
+#endif
+        def = &tcg_op_defs[opc];
+#if 0
+        printf("%s: %d %d %d\n", def->name,
+               def->nb_oargs, def->nb_iargs, def->nb_cargs);
+        //        dump_regs(s);
+#endif
+        switch(opc) {
+        case INDEX_op_mov_i32:
+#if TCG_TARGET_REG_BITS == 64
+        case INDEX_op_mov_i64:
+#endif
+            dead_iargs = s->op_dead_iargs[op_index];
+            tcg_reg_alloc_mov(s, def, args, dead_iargs);
+            break;
+        case INDEX_op_movi_i32:
+#if TCG_TARGET_REG_BITS == 64
+        case INDEX_op_movi_i64:
+#endif
+            tcg_reg_alloc_movi(s, args);
+            break;
+        case INDEX_op_debug_insn_start:
+            /* debug instruction */
+            break;
+        case INDEX_op_nop:
+        case INDEX_op_nop1:
+        case INDEX_op_nop2:
+        case INDEX_op_nop3:
+            break;
+        case INDEX_op_nopn:
+            args += args[0];
+            goto next;
+        case INDEX_op_discard:
+            {
+                TCGTemp *ts;
+                ts = &s->temps[args[0]];
+                /* mark the temporary as dead */
+                if (!ts->fixed_reg) {
+                    if (ts->val_type == TEMP_VAL_REG)
+                        s->reg_to_temp[ts->reg] = -1;
+                    ts->val_type = TEMP_VAL_DEAD;
+                }
+            }
+            break;
+        case INDEX_op_set_label:
+            tcg_reg_alloc_bb_end(s, s->reserved_regs);
+            tcg_out_label(s, args[0], (long)s->code_ptr);
+            break;
+        case INDEX_op_call:
+            dead_iargs = s->op_dead_iargs[op_index];
+            args += tcg_reg_alloc_call(s, def, opc, args, dead_iargs);
+            goto next;
+        case INDEX_op_end:
+            goto the_end;
+
+#ifdef CONFIG_DYNGEN_OP
+        case 0 ... INDEX_op_end - 1:
+            /* legacy dyngen ops */
+#ifdef CONFIG_PROFILER
+            s->old_op_count++;
+#endif
+            tcg_reg_alloc_bb_end(s, s->reserved_regs);
+            if (search_pc >= 0) {
+                s->code_ptr += def->copy_size;
+                args += def->nb_args;
+            } else {
+                args = dyngen_op(s, opc, args);
+            }
+            goto next;
+#endif
+        default:
+            /* Note: in order to speed up the code, it would be much
+               faster to have specialized register allocator functions for
+               some common argument patterns */
+            dead_iargs = s->op_dead_iargs[op_index];
+            tcg_reg_alloc_op(s, def, opc, args, dead_iargs);
+            break;
+        }
+        args += def->nb_args;
+    next:
+        if (search_pc >= 0 && search_pc < s->code_ptr - gen_code_buf) {
+            return op_index;
+        }
+        op_index++;
+#ifndef NDEBUG
+        check_regs(s);
+#endif
+    }
+ the_end:
+    return -1;
+}
+
+int dyngen_code(TCGContext *s, uint8_t *gen_code_buf)
+{
+#ifdef CONFIG_PROFILER
+    {
+        int n;
+        n = (gen_opc_ptr - gen_opc_buf);
+        s->op_count += n;
+        if (n > s->op_count_max)
+            s->op_count_max = n;
+
+        s->temp_count += s->nb_temps;
+        if (s->nb_temps > s->temp_count_max)
+            s->temp_count_max = s->nb_temps;
+    }
+#endif
+
+    tcg_gen_code_common(s, gen_code_buf, -1);
+
+    /* flush instruction cache */
+    flush_icache_range((unsigned long)gen_code_buf, 
+                       (unsigned long)s->code_ptr);
+    return s->code_ptr -  gen_code_buf;
+}
+
+/* Return the index of the micro operation such as the pc after is <
+   offset bytes from the start of the TB.  The contents of gen_code_buf must
+   not be changed, though writing the same values is ok.
+   Return -1 if not found. */
+int dyngen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset)
+{
+    return tcg_gen_code_common(s, gen_code_buf, offset);
+}
+
+#ifdef CONFIG_PROFILER
+void tcg_dump_info(FILE *f,
+                   int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+    TCGContext *s = &tcg_ctx;
+    int64_t tot;
+
+    tot = s->interm_time + s->code_time;
+    cpu_fprintf(f, "JIT cycles          %" PRId64 " (%0.3f s at 2.4 GHz)\n",
+                tot, tot / 2.4e9);
+    cpu_fprintf(f, "translated TBs      %" PRId64 " (aborted=%" PRId64 " %0.1f%%)\n", 
+                s->tb_count, 
+                s->tb_count1 - s->tb_count,
+                s->tb_count1 ? (double)(s->tb_count1 - s->tb_count) / s->tb_count1 * 100.0 : 0);
+    cpu_fprintf(f, "avg ops/TB          %0.1f max=%d\n", 
+                s->tb_count ? (double)s->op_count / s->tb_count : 0, s->op_count_max);
+    cpu_fprintf(f, "old ops/total ops   %0.1f%%\n", 
+                s->op_count ? (double)s->old_op_count / s->op_count * 100.0 : 0);
+    cpu_fprintf(f, "deleted ops/TB      %0.2f\n",
+                s->tb_count ? 
+                (double)s->del_op_count / s->tb_count : 0);
+    cpu_fprintf(f, "avg temps/TB        %0.2f max=%d\n",
+                s->tb_count ? 
+                (double)s->temp_count / s->tb_count : 0,
+                s->temp_count_max);
+    
+    cpu_fprintf(f, "cycles/op           %0.1f\n", 
+                s->op_count ? (double)tot / s->op_count : 0);
+    cpu_fprintf(f, "cycles/in byte      %0.1f\n", 
+                s->code_in_len ? (double)tot / s->code_in_len : 0);
+    cpu_fprintf(f, "cycles/out byte     %0.1f\n", 
+                s->code_out_len ? (double)tot / s->code_out_len : 0);
+    if (tot == 0)
+        tot = 1;
+    cpu_fprintf(f, "  gen_interm time   %0.1f%%\n", 
+                (double)s->interm_time / tot * 100.0);
+    cpu_fprintf(f, "  gen_code time     %0.1f%%\n", 
+                (double)s->code_time / tot * 100.0);
+    cpu_fprintf(f, "liveness/code time  %0.1f%%\n", 
+                (double)s->la_time / (s->code_time ? s->code_time : 1) * 100.0);
+    cpu_fprintf(f, "cpu_restore count   %" PRId64 "\n",
+                s->restore_count);
+    cpu_fprintf(f, "  avg cycles        %0.1f\n",
+                s->restore_count ? (double)s->restore_time / s->restore_count : 0);
+    {
+        extern void dump_op_count(void);
+        dump_op_count();
+    }
+}
+#else
+void tcg_dump_info(FILE *f,
+                   int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+    cpu_fprintf(f, "[TCG profiler not compiled]\n");
+}
+#endif
diff --git a/tcg/tcg.h b/tcg/tcg.h
new file mode 100644
index 0000000..bc5b902
--- /dev/null
+++ b/tcg/tcg.h
@@ -0,0 +1,421 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "tcg-target.h"
+
+#if TCG_TARGET_REG_BITS == 32
+typedef int32_t tcg_target_long;
+typedef uint32_t tcg_target_ulong;
+#define TCG_PRIlx PRIx32
+#define TCG_PRIld PRId32
+#elif TCG_TARGET_REG_BITS == 64
+typedef int64_t tcg_target_long;
+typedef uint64_t tcg_target_ulong;
+#define TCG_PRIlx PRIx64
+#define TCG_PRIld PRId64
+#else
+#error unsupported
+#endif
+
+#if TCG_TARGET_NB_REGS <= 32
+typedef uint32_t TCGRegSet;
+#elif TCG_TARGET_NB_REGS <= 64
+typedef uint64_t TCGRegSet;
+#else
+#error unsupported
+#endif
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "tcg-opc.h"
+#undef DEF
+    NB_OPS,
+};
+
+#define tcg_regset_clear(d) (d) = 0
+#define tcg_regset_set(d, s) (d) = (s)
+#define tcg_regset_set32(d, reg, val32) (d) |= (val32) << (reg)
+#define tcg_regset_set_reg(d, r) (d) |= 1 << (r)
+#define tcg_regset_reset_reg(d, r) (d) &= ~(1 << (r))
+#define tcg_regset_test_reg(d, r) (((d) >> (r)) & 1)
+#define tcg_regset_or(d, a, b) (d) = (a) | (b)
+#define tcg_regset_and(d, a, b) (d) = (a) & (b)
+#define tcg_regset_andnot(d, a, b) (d) = (a) & ~(b)
+#define tcg_regset_not(d, a) (d) = ~(a)
+
+typedef struct TCGRelocation {
+    struct TCGRelocation *next;
+    int type;
+    uint8_t *ptr;
+    tcg_target_long addend;
+} TCGRelocation; 
+
+typedef struct TCGLabel {
+    int has_value;
+    union {
+        tcg_target_ulong value;
+        TCGRelocation *first_reloc;
+    } u;
+} TCGLabel;
+
+typedef struct TCGPool {
+    struct TCGPool *next;
+    int size;
+    uint8_t data[0] __attribute__ ((aligned));
+} TCGPool;
+
+#define TCG_POOL_CHUNK_SIZE 32768
+
+#define TCG_MAX_LABELS 512
+
+#define TCG_MAX_TEMPS 512
+
+/* when the size of the arguments of a called function is smaller than
+   this value, they are statically allocated in the TB stack frame */
+#define TCG_STATIC_CALL_ARGS_SIZE 128
+
+typedef int TCGType;
+
+#define TCG_TYPE_I32 0
+#define TCG_TYPE_I64 1
+#define TCG_TYPE_COUNT 2 /* number of different types */
+
+#if TCG_TARGET_REG_BITS == 32
+#define TCG_TYPE_PTR TCG_TYPE_I32
+#else
+#define TCG_TYPE_PTR TCG_TYPE_I64
+#endif
+
+typedef tcg_target_ulong TCGArg;
+
+/* Define a type and accessor macros for varables.  Using a struct is
+   nice because it gives some level of type safely.  Ideally the compiler
+   be able to see through all this.  However in practice this is not true,
+   expecially on targets with braindamaged ABIs (e.g. i386).
+   We use plain int by default to avoid this runtime overhead.
+   Users of tcg_gen_* don't need to know about any of this, and should
+   treat TCGv as an opaque type.  */
+
+//#define DEBUG_TCGV 1
+
+#ifdef DEBUG_TCGV
+
+typedef struct
+{
+    int n;
+} TCGv;
+
+#define MAKE_TCGV(i) __extension__ \
+  ({ TCGv make_tcgv_tmp = {i}; make_tcgv_tmp;})
+#define GET_TCGV(t) ((t).n)
+#if TCG_TARGET_REG_BITS == 32
+#define TCGV_HIGH(t) MAKE_TCGV(GET_TCGV(t) + 1)
+#endif
+
+#else /* !DEBUG_TCGV */
+
+typedef int TCGv;
+#define MAKE_TCGV(x) (x)
+#define GET_TCGV(t) (t)
+#if TCG_TARGET_REG_BITS == 32
+#define TCGV_HIGH(t) ((t) + 1)
+#endif
+
+#endif /* DEBUG_TCGV */
+
+/* Dummy definition to avoid compiler warnings.  */
+#define TCGV_UNUSED(x) x = MAKE_TCGV(-1)
+
+/* call flags */
+#define TCG_CALL_TYPE_MASK      0x000f
+#define TCG_CALL_TYPE_STD       0x0000 /* standard C call */
+#define TCG_CALL_TYPE_REGPARM_1 0x0001 /* i386 style regparm call (1 reg) */
+#define TCG_CALL_TYPE_REGPARM_2 0x0002 /* i386 style regparm call (2 regs) */
+#define TCG_CALL_TYPE_REGPARM   0x0003 /* i386 style regparm call (3 regs) */
+/* A pure function only reads its arguments and globals variables and
+   cannot raise exceptions. Hence a call to a pure function can be
+   safely suppressed if the return value is not used. */
+#define TCG_CALL_PURE           0x0010 
+
+/* used to align parameters */
+#define TCG_CALL_DUMMY_TCGV     MAKE_TCGV(-1)
+#define TCG_CALL_DUMMY_ARG      ((TCGArg)(-1))
+
+typedef enum {
+    TCG_COND_EQ,
+    TCG_COND_NE,
+    TCG_COND_LT,
+    TCG_COND_GE,
+    TCG_COND_LE,
+    TCG_COND_GT,
+    /* unsigned */
+    TCG_COND_LTU,
+    TCG_COND_GEU,
+    TCG_COND_LEU,
+    TCG_COND_GTU,
+} TCGCond;
+
+#define TEMP_VAL_DEAD  0
+#define TEMP_VAL_REG   1
+#define TEMP_VAL_MEM   2
+#define TEMP_VAL_CONST 3
+
+/* XXX: optimize memory layout */
+typedef struct TCGTemp {
+    TCGType base_type;
+    TCGType type;
+    int val_type;
+    int reg;
+    tcg_target_long val;
+    int mem_reg;
+    tcg_target_long mem_offset;
+    unsigned int fixed_reg:1;
+    unsigned int mem_coherent:1;
+    unsigned int mem_allocated:1;
+    unsigned int temp_local:1; /* If true, the temp is saved accross
+                                  basic blocks. Otherwise, it is not
+                                  preserved accross basic blocks. */
+    unsigned int temp_allocated:1; /* never used for code gen */
+    /* index of next free temp of same base type, -1 if end */
+    int next_free_temp;
+    const char *name;
+} TCGTemp;
+
+typedef struct TCGHelperInfo {
+    tcg_target_ulong func;
+    const char *name;
+} TCGHelperInfo;
+
+typedef struct TCGContext TCGContext;
+
+struct TCGContext {
+    uint8_t *pool_cur, *pool_end;
+    TCGPool *pool_first, *pool_current;
+    TCGLabel *labels;
+    int nb_labels;
+    TCGTemp *temps; /* globals first, temps after */
+    int nb_globals;
+    int nb_temps;
+    /* index of free temps, -1 if none */
+    int first_free_temp[TCG_TYPE_COUNT * 2]; 
+
+    /* goto_tb support */
+    uint8_t *code_buf;
+    unsigned long *tb_next;
+    uint16_t *tb_next_offset;
+    uint16_t *tb_jmp_offset; /* != NULL if USE_DIRECT_JUMP */
+
+    /* liveness analysis */
+    uint16_t *op_dead_iargs; /* for each operation, each bit tells if the
+                                corresponding input argument is dead */
+    
+    /* tells in which temporary a given register is. It does not take
+       into account fixed registers */
+    int reg_to_temp[TCG_TARGET_NB_REGS];
+    TCGRegSet reserved_regs;
+    tcg_target_long current_frame_offset;
+    tcg_target_long frame_start;
+    tcg_target_long frame_end;
+    int frame_reg;
+
+    uint8_t *code_ptr;
+    TCGTemp static_temps[TCG_MAX_TEMPS];
+
+    TCGHelperInfo *helpers;
+    int nb_helpers;
+    int allocated_helpers;
+    int helpers_sorted;
+
+#ifdef CONFIG_PROFILER
+    /* profiling info */
+    int64_t tb_count1;
+    int64_t tb_count;
+    int64_t op_count; /* total insn count */
+    int op_count_max; /* max insn per TB */
+    int64_t temp_count;
+    int temp_count_max;
+    int64_t old_op_count;
+    int64_t del_op_count;
+    int64_t code_in_len;
+    int64_t code_out_len;
+    int64_t interm_time;
+    int64_t code_time;
+    int64_t la_time;
+    int64_t restore_count;
+    int64_t restore_time;
+#endif
+};
+
+extern TCGContext tcg_ctx;
+extern uint16_t *gen_opc_ptr;
+extern TCGArg *gen_opparam_ptr;
+extern uint16_t gen_opc_buf[];
+extern TCGArg gen_opparam_buf[];
+
+/* pool based memory allocation */
+
+void *tcg_malloc_internal(TCGContext *s, int size);
+void tcg_pool_reset(TCGContext *s);
+void tcg_pool_delete(TCGContext *s);
+
+static inline void *tcg_malloc(int size)
+{
+    TCGContext *s = &tcg_ctx;
+    uint8_t *ptr, *ptr_end;
+    size = (size + sizeof(long) - 1) & ~(sizeof(long) - 1);
+    ptr = s->pool_cur;
+    ptr_end = ptr + size;
+    if (unlikely(ptr_end > s->pool_end)) {
+        return tcg_malloc_internal(&tcg_ctx, size);
+    } else {
+        s->pool_cur = ptr_end;
+        return ptr;
+    }
+}
+
+void tcg_context_init(TCGContext *s);
+void tcg_func_start(TCGContext *s);
+
+int dyngen_code(TCGContext *s, uint8_t *gen_code_buf);
+int dyngen_code_search_pc(TCGContext *s, uint8_t *gen_code_buf, long offset);
+
+void tcg_set_frame(TCGContext *s, int reg,
+                   tcg_target_long start, tcg_target_long size);
+TCGv tcg_global_reg_new(TCGType type, int reg, const char *name);
+TCGv tcg_global_reg2_new_hack(TCGType type, int reg1, int reg2, 
+                              const char *name);
+TCGv tcg_global_mem_new(TCGType type, int reg, tcg_target_long offset,
+                        const char *name);
+TCGv tcg_temp_new_internal(TCGType type, int temp_local);
+static inline TCGv tcg_temp_new(TCGType type)
+{
+    return tcg_temp_new_internal(type, 0);
+}
+static inline TCGv tcg_temp_local_new(TCGType type)
+{
+    return tcg_temp_new_internal(type, 1);
+}
+void tcg_temp_free(TCGv arg);
+char *tcg_get_arg_str(TCGContext *s, char *buf, int buf_size, TCGv arg);
+void tcg_dump_info(FILE *f,
+                   int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+
+#define TCG_CT_ALIAS  0x80
+#define TCG_CT_IALIAS 0x40
+#define TCG_CT_REG    0x01
+#define TCG_CT_CONST  0x02 /* any constant of register size */
+
+typedef struct TCGArgConstraint {
+    uint16_t ct;
+    uint8_t alias_index;
+    union {
+        TCGRegSet regs;
+    } u;
+} TCGArgConstraint;
+
+#define TCG_MAX_OP_ARGS 16
+
+#define TCG_OPF_BB_END     0x01 /* instruction defines the end of a basic
+                                   block */
+#define TCG_OPF_CALL_CLOBBER 0x02 /* instruction clobbers call registers 
+                                   and potentially update globals. */
+#define TCG_OPF_SIDE_EFFECTS 0x04 /* instruction has side effects : it
+                                     cannot be removed if its output
+                                     are not used */
+
+typedef struct TCGOpDef {
+    const char *name;
+    uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args;
+    uint8_t flags;
+    uint16_t copy_size;
+    TCGArgConstraint *args_ct;
+    int *sorted_args;
+} TCGOpDef;
+        
+typedef struct TCGTargetOpDef {
+    int op;
+    const char *args_ct_str[TCG_MAX_OP_ARGS];
+} TCGTargetOpDef;
+
+extern TCGOpDef tcg_op_defs[];
+
+void tcg_target_init(TCGContext *s);
+void tcg_target_qemu_prologue(TCGContext *s);
+
+#define tcg_abort() \
+do {\
+    fprintf(stderr, "%s:%d: tcg fatal error\n", __FILE__, __LINE__);\
+    abort();\
+} while (0)
+
+void tcg_add_target_add_op_defs(const TCGTargetOpDef *tdefs);
+
+void tcg_gen_call(TCGContext *s, TCGv func, unsigned int flags,
+                  unsigned int nb_rets, const TCGv *rets,
+                  unsigned int nb_params, const TCGv *args1);
+void tcg_gen_shifti_i64(TCGv ret, TCGv arg1, 
+                        int c, int right, int arith);
+
+/* only used for debugging purposes */
+void tcg_register_helper(void *func, const char *name);
+#define TCG_HELPER(func) tcg_register_helper(func, #func)
+const char *tcg_helper_get_name(TCGContext *s, void *func);
+void tcg_dump_ops(TCGContext *s, FILE *outfile);
+
+void dump_ops(const uint16_t *opc_buf, const TCGArg *opparam_buf);
+TCGv tcg_const_i32(int32_t val);
+TCGv tcg_const_i64(int64_t val);
+
+#if TCG_TARGET_REG_BITS == 32
+#define tcg_const_ptr tcg_const_i32
+#define tcg_add_ptr tcg_add_i32
+#define tcg_sub_ptr tcg_sub_i32
+#else
+#define tcg_const_ptr tcg_const_i64
+#define tcg_add_ptr tcg_add_i64
+#define tcg_sub_ptr tcg_sub_i64
+#endif
+
+void tcg_out_reloc(TCGContext *s, uint8_t *code_ptr, int type, 
+                   int label_index, long addend);
+const TCGArg *tcg_gen_code_op(TCGContext *s, int opc, const TCGArg *args1,
+                              unsigned int dead_iargs);
+
+const TCGArg *dyngen_op(TCGContext *s, int opc, const TCGArg *opparam_ptr);
+
+/* tcg-runtime.c */
+int64_t tcg_helper_shl_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_shr_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_sar_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_div_i64(int64_t arg1, int64_t arg2);
+int64_t tcg_helper_rem_i64(int64_t arg1, int64_t arg2);
+uint64_t tcg_helper_divu_i64(uint64_t arg1, uint64_t arg2);
+uint64_t tcg_helper_remu_i64(uint64_t arg1, uint64_t arg2);
+
+extern uint8_t code_gen_prologue[];
+#if defined(__powerpc__) && !defined(__powerpc64__)
+#define tcg_qemu_tb_exec(tb_ptr) \
+    ((long REGPARM __attribute__ ((longcall)) (*)(void *))code_gen_prologue)(tb_ptr)
+#else
+#define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM (*)(void *))code_gen_prologue)(tb_ptr)
+#endif
diff --git a/tcg/x86_64/tcg-target.c b/tcg/x86_64/tcg-target.c
new file mode 100644
index 0000000..304a0c3
--- /dev/null
+++ b/tcg/x86_64/tcg-target.c
@@ -0,0 +1,1307 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+const char *tcg_target_reg_names[TCG_TARGET_NB_REGS] = {
+    "%rax",
+    "%rcx",
+    "%rdx",
+    "%rbx",
+    "%rsp",
+    "%rbp",
+    "%rsi",
+    "%rdi",
+    "%r8",
+    "%r9",
+    "%r10",
+    "%r11",
+    "%r12",
+    "%r13",
+    "%r14",
+    "%r15",
+};
+
+int tcg_target_reg_alloc_order[] = {
+    TCG_REG_RDI,
+    TCG_REG_RSI,
+    TCG_REG_RDX,
+    TCG_REG_RCX,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_RAX,
+    TCG_REG_R10,
+    TCG_REG_R11,
+
+    TCG_REG_RBP,
+    TCG_REG_RBX,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+    TCG_REG_R15,
+};
+
+const int tcg_target_call_iarg_regs[6] = { 
+    TCG_REG_RDI,
+    TCG_REG_RSI,
+    TCG_REG_RDX,
+    TCG_REG_RCX,
+    TCG_REG_R8,
+    TCG_REG_R9,
+};
+
+const int tcg_target_call_oarg_regs[2] = { 
+    TCG_REG_RAX, 
+    TCG_REG_RDX 
+};
+
+static uint8_t *tb_ret_addr;
+
+static void patch_reloc(uint8_t *code_ptr, int type, 
+                        tcg_target_long value, tcg_target_long addend)
+{
+    value += addend;
+    switch(type) {
+    case R_X86_64_32:
+        if (value != (uint32_t)value)
+            tcg_abort();
+        *(uint32_t *)code_ptr = value;
+        break;
+    case R_X86_64_32S:
+        if (value != (int32_t)value)
+            tcg_abort();
+        *(uint32_t *)code_ptr = value;
+        break;
+    case R_386_PC32:
+        value -= (long)code_ptr;
+        if (value != (int32_t)value)
+            tcg_abort();
+        *(uint32_t *)code_ptr = value;
+        break;
+    default:
+        tcg_abort();
+    }
+}
+
+/* maximum number of register used for input function arguments */
+static inline int tcg_target_get_call_iarg_regs_count(int flags)
+{
+    return 6;
+}
+
+/* parse target specific constraints */
+static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str)
+{
+    const char *ct_str;
+
+    ct_str = *pct_str;
+    switch(ct_str[0]) {
+    case 'a':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_RAX);
+        break;
+    case 'b':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_RBX);
+        break;
+    case 'c':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_RCX);
+        break;
+    case 'd':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_RDX);
+        break;
+    case 'S':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_RSI);
+        break;
+    case 'D':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set_reg(ct->u.regs, TCG_REG_RDI);
+        break;
+    case 'q':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xf);
+        break;
+    case 'r':
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffff);
+        break;
+    case 'L': /* qemu_ld/st constraint */
+        ct->ct |= TCG_CT_REG;
+        tcg_regset_set32(ct->u.regs, 0, 0xffff);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_RSI);
+        tcg_regset_reset_reg(ct->u.regs, TCG_REG_RDI);
+        break;
+    case 'e':
+        ct->ct |= TCG_CT_CONST_S32;
+        break;
+    case 'Z':
+        ct->ct |= TCG_CT_CONST_U32;
+        break;
+    default:
+        return -1;
+    }
+    ct_str++;
+    *pct_str = ct_str;
+    return 0;
+}
+
+/* test if a constant matches the constraint */
+static inline int tcg_target_const_match(tcg_target_long val,
+                                         const TCGArgConstraint *arg_ct)
+{
+    int ct;
+    ct = arg_ct->ct;
+    if (ct & TCG_CT_CONST)
+        return 1;
+    else if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val)
+        return 1;
+    else if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val)
+        return 1;
+    else
+        return 0;
+}
+
+#define ARITH_ADD 0
+#define ARITH_OR  1
+#define ARITH_ADC 2
+#define ARITH_SBB 3
+#define ARITH_AND 4
+#define ARITH_SUB 5
+#define ARITH_XOR 6
+#define ARITH_CMP 7
+
+#define SHIFT_SHL 4
+#define SHIFT_SHR 5
+#define SHIFT_SAR 7
+
+#define JCC_JMP (-1)
+#define JCC_JO  0x0
+#define JCC_JNO 0x1
+#define JCC_JB  0x2
+#define JCC_JAE 0x3
+#define JCC_JE  0x4
+#define JCC_JNE 0x5
+#define JCC_JBE 0x6
+#define JCC_JA  0x7
+#define JCC_JS  0x8
+#define JCC_JNS 0x9
+#define JCC_JP  0xa
+#define JCC_JNP 0xb
+#define JCC_JL  0xc
+#define JCC_JGE 0xd
+#define JCC_JLE 0xe
+#define JCC_JG  0xf
+
+#define P_EXT   0x100 /* 0x0f opcode prefix */
+#define P_REXW  0x200 /* set rex.w = 1 */
+#define P_REXB  0x400 /* force rex use for byte registers */
+                                  
+static const uint8_t tcg_cond_to_jcc[10] = {
+    [TCG_COND_EQ] = JCC_JE,
+    [TCG_COND_NE] = JCC_JNE,
+    [TCG_COND_LT] = JCC_JL,
+    [TCG_COND_GE] = JCC_JGE,
+    [TCG_COND_LE] = JCC_JLE,
+    [TCG_COND_GT] = JCC_JG,
+    [TCG_COND_LTU] = JCC_JB,
+    [TCG_COND_GEU] = JCC_JAE,
+    [TCG_COND_LEU] = JCC_JBE,
+    [TCG_COND_GTU] = JCC_JA,
+};
+
+static inline void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x)
+{
+    int rex;
+    rex = ((opc >> 6) & 0x8) | ((r >> 1) & 0x4) | 
+        ((x >> 2) & 2) | ((rm >> 3) & 1);
+    if (rex || (opc & P_REXB)) {
+        tcg_out8(s, rex | 0x40);
+    }
+    if (opc & P_EXT)
+        tcg_out8(s, 0x0f);
+    tcg_out8(s, opc);
+}
+
+static inline void tcg_out_modrm(TCGContext *s, int opc, int r, int rm)
+{
+    tcg_out_opc(s, opc, r, rm, 0);
+    tcg_out8(s, 0xc0 | ((r & 7) << 3) | (rm & 7));
+}
+
+/* rm < 0 means no register index plus (-rm - 1 immediate bytes) */
+static inline void tcg_out_modrm_offset(TCGContext *s, int opc, int r, int rm, 
+                                        tcg_target_long offset)
+{
+    if (rm < 0) {
+        tcg_target_long val;
+        tcg_out_opc(s, opc, r, 0, 0);
+        val = offset - ((tcg_target_long)s->code_ptr + 5 + (-rm - 1));
+        if (val == (int32_t)val) {
+            /* eip relative */
+            tcg_out8(s, 0x05 | ((r & 7) << 3));
+            tcg_out32(s, val);
+        } else if (offset == (int32_t)offset) {
+            tcg_out8(s, 0x04 | ((r & 7) << 3));
+            tcg_out8(s, 0x25); /* sib */
+            tcg_out32(s, offset);
+        } else {
+            tcg_abort();
+        }
+    } else if (offset == 0 && (rm & 7) != TCG_REG_RBP) {
+        tcg_out_opc(s, opc, r, rm, 0);
+        if ((rm & 7) == TCG_REG_RSP) {
+            tcg_out8(s, 0x04 | ((r & 7) << 3));
+            tcg_out8(s, 0x24);
+        } else {
+            tcg_out8(s, 0x00 | ((r & 7) << 3) | (rm & 7));
+        }
+    } else if ((int8_t)offset == offset) {
+        tcg_out_opc(s, opc, r, rm, 0);
+        if ((rm & 7) == TCG_REG_RSP) {
+            tcg_out8(s, 0x44 | ((r & 7) << 3));
+            tcg_out8(s, 0x24);
+        } else {
+            tcg_out8(s, 0x40 | ((r & 7) << 3) | (rm & 7));
+        }
+        tcg_out8(s, offset);
+    } else {
+        tcg_out_opc(s, opc, r, rm, 0);
+        if ((rm & 7) == TCG_REG_RSP) {
+            tcg_out8(s, 0x84 | ((r & 7) << 3));
+            tcg_out8(s, 0x24);
+        } else {
+            tcg_out8(s, 0x80 | ((r & 7) << 3) | (rm & 7));
+        }
+        tcg_out32(s, offset);
+    }
+}
+
+#if defined(CONFIG_SOFTMMU)
+/* XXX: incomplete. index must be different from ESP */
+static void tcg_out_modrm_offset2(TCGContext *s, int opc, int r, int rm, 
+                                  int index, int shift,
+                                  tcg_target_long offset)
+{
+    int mod;
+    if (rm == -1)
+        tcg_abort();
+    if (offset == 0 && (rm & 7) != TCG_REG_RBP) {
+        mod = 0;
+    } else if (offset == (int8_t)offset) {
+        mod = 0x40;
+    } else if (offset == (int32_t)offset) {
+        mod = 0x80;
+    } else {
+        tcg_abort();
+    }
+    if (index == -1) {
+        tcg_out_opc(s, opc, r, rm, 0);
+        if ((rm & 7) == TCG_REG_RSP) {
+            tcg_out8(s, mod | ((r & 7) << 3) | 0x04);
+            tcg_out8(s, 0x04 | (rm & 7));
+        } else {
+            tcg_out8(s, mod | ((r & 7) << 3) | (rm & 7));
+        }
+    } else {
+        tcg_out_opc(s, opc, r, rm, index);
+        tcg_out8(s, mod | ((r & 7) << 3) | 0x04);
+        tcg_out8(s, (shift << 6) | ((index & 7) << 3) | (rm & 7));
+    }
+    if (mod == 0x40) {
+        tcg_out8(s, offset);
+    } else if (mod == 0x80) {
+        tcg_out32(s, offset);
+    }
+}
+#endif
+
+static inline void tcg_out_mov(TCGContext *s, int ret, int arg)
+{
+    tcg_out_modrm(s, 0x8b | P_REXW, ret, arg);
+}
+
+static inline void tcg_out_movi(TCGContext *s, TCGType type, 
+                                int ret, tcg_target_long arg)
+{
+    if (arg == 0) {
+        tcg_out_modrm(s, 0x01 | (ARITH_XOR << 3), ret, ret); /* xor r0,r0 */
+    } else if (arg == (uint32_t)arg || type == TCG_TYPE_I32) {
+        tcg_out_opc(s, 0xb8 + (ret & 7), 0, ret, 0);
+        tcg_out32(s, arg);
+    } else if (arg == (int32_t)arg) {
+        tcg_out_modrm(s, 0xc7 | P_REXW, 0, ret);
+        tcg_out32(s, arg);
+    } else {
+        tcg_out_opc(s, (0xb8 + (ret & 7)) | P_REXW, 0, ret, 0);
+        tcg_out32(s, arg);
+        tcg_out32(s, arg >> 32);
+    }
+}
+
+static inline void tcg_out_ld(TCGContext *s, TCGType type, int ret,
+                              int arg1, tcg_target_long arg2)
+{
+    if (type == TCG_TYPE_I32)
+        tcg_out_modrm_offset(s, 0x8b, ret, arg1, arg2); /* movl */
+    else
+        tcg_out_modrm_offset(s, 0x8b | P_REXW, ret, arg1, arg2); /* movq */
+}
+
+static inline void tcg_out_st(TCGContext *s, TCGType type, int arg,
+                              int arg1, tcg_target_long arg2)
+{
+    if (type == TCG_TYPE_I32)
+        tcg_out_modrm_offset(s, 0x89, arg, arg1, arg2); /* movl */
+    else
+        tcg_out_modrm_offset(s, 0x89 | P_REXW, arg, arg1, arg2); /* movq */
+}
+
+static inline void tgen_arithi32(TCGContext *s, int c, int r0, int32_t val)
+{
+    if (val == (int8_t)val) {
+        tcg_out_modrm(s, 0x83, c, r0);
+        tcg_out8(s, val);
+    } else if (c == ARITH_AND && val == 0xffu) {
+        /* movzbl */
+        tcg_out_modrm(s, 0xb6 | P_EXT | P_REXB, r0, r0);
+    } else if (c == ARITH_AND && val == 0xffffu) {
+        /* movzwl */
+        tcg_out_modrm(s, 0xb7 | P_EXT, r0, r0);
+    } else {
+        tcg_out_modrm(s, 0x81, c, r0);
+        tcg_out32(s, val);
+    }
+}
+
+static inline void tgen_arithi64(TCGContext *s, int c, int r0, int64_t val)
+{
+    if (val == (int8_t)val) {
+        tcg_out_modrm(s, 0x83 | P_REXW, c, r0);
+        tcg_out8(s, val);
+    } else if (c == ARITH_AND && val == 0xffu) {
+        /* movzbl */
+        tcg_out_modrm(s, 0xb6 | P_EXT | P_REXW, r0, r0);
+    } else if (c == ARITH_AND && val == 0xffffu) {
+        /* movzwl */
+        tcg_out_modrm(s, 0xb7 | P_EXT | P_REXW, r0, r0);
+    } else if (c == ARITH_AND && val == 0xffffffffu) {
+        /* 32-bit mov zero extends */
+        tcg_out_modrm(s, 0x8b, r0, r0);
+    } else if (val == (int32_t)val) {
+        tcg_out_modrm(s, 0x81 | P_REXW, c, r0);
+        tcg_out32(s, val);
+    } else if (c == ARITH_AND && val == (uint32_t)val) {
+        tcg_out_modrm(s, 0x81, c, r0);
+        tcg_out32(s, val);
+    } else {
+        tcg_abort();
+    }
+}
+
+static void tcg_out_addi(TCGContext *s, int reg, tcg_target_long val)
+{
+    if (val != 0)
+        tgen_arithi64(s, ARITH_ADD, reg, val);
+}
+
+static void tcg_out_jxx(TCGContext *s, int opc, int label_index)
+{
+    int32_t val, val1;
+    TCGLabel *l = &s->labels[label_index];
+    
+    if (l->has_value) {
+        val = l->u.value - (tcg_target_long)s->code_ptr;
+        val1 = val - 2;
+        if ((int8_t)val1 == val1) {
+            if (opc == -1)
+                tcg_out8(s, 0xeb);
+            else
+                tcg_out8(s, 0x70 + opc);
+            tcg_out8(s, val1);
+        } else {
+            if (opc == -1) {
+                tcg_out8(s, 0xe9);
+                tcg_out32(s, val - 5);
+            } else {
+                tcg_out8(s, 0x0f);
+                tcg_out8(s, 0x80 + opc);
+                tcg_out32(s, val - 6);
+            }
+        }
+    } else {
+        if (opc == -1) {
+            tcg_out8(s, 0xe9);
+        } else {
+            tcg_out8(s, 0x0f);
+            tcg_out8(s, 0x80 + opc);
+        }
+        tcg_out_reloc(s, s->code_ptr, R_386_PC32, label_index, -4);
+        s->code_ptr += 4;
+    }
+}
+
+static void tcg_out_brcond(TCGContext *s, int cond, 
+                           TCGArg arg1, TCGArg arg2, int const_arg2,
+                           int label_index, int rexw)
+{
+    if (const_arg2) {
+        if (arg2 == 0) {
+            /* test r, r */
+            tcg_out_modrm(s, 0x85 | rexw, arg1, arg1);
+        } else {
+            if (rexw)
+                tgen_arithi64(s, ARITH_CMP, arg1, arg2);
+            else
+                tgen_arithi32(s, ARITH_CMP, arg1, arg2);
+        }
+    } else {
+        tcg_out_modrm(s, 0x01 | (ARITH_CMP << 3) | rexw, arg2, arg1);
+    }
+    tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index);
+}
+
+#if defined(CONFIG_SOFTMMU)
+
+#include "../../softmmu_defs.h"
+
+static void *qemu_ld_helpers[4] = {
+    __ldb_mmu,
+    __ldw_mmu,
+    __ldl_mmu,
+    __ldq_mmu,
+};
+
+static void *qemu_st_helpers[4] = {
+    __stb_mmu,
+    __stw_mmu,
+    __stl_mmu,
+    __stq_mmu,
+};
+#endif
+
+static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_reg, data_reg, r0, r1, mem_index, s_bits, bswap, rexw;
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label1_ptr, *label2_ptr;
+#endif
+
+    data_reg = *args++;
+    addr_reg = *args++;
+    mem_index = *args;
+    s_bits = opc & 3;
+
+    r0 = TCG_REG_RDI;
+    r1 = TCG_REG_RSI;
+
+#if TARGET_LONG_BITS == 32
+    rexw = 0;
+#else
+    rexw = P_REXW;
+#endif
+#if defined(CONFIG_SOFTMMU)
+    /* mov */
+    tcg_out_modrm(s, 0x8b | rexw, r1, addr_reg);
+
+    /* mov */
+    tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg);
+ 
+    tcg_out_modrm(s, 0xc1 | rexw, 5, r1); /* shr $x, r1 */
+    tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); 
+    
+    tcg_out_modrm(s, 0x81 | rexw, 4, r0); /* andl $x, r0 */
+    tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+    
+    tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */
+    tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+    /* lea offset(r1, env), r1 */
+    tcg_out_modrm_offset2(s, 0x8d | P_REXW, r1, r1, TCG_AREG0, 0,
+                          offsetof(CPUState, tlb_table[mem_index][0].addr_read));
+
+    /* cmp 0(r1), r0 */
+    tcg_out_modrm_offset(s, 0x3b | rexw, r0, r1, 0);
+    
+    /* mov */
+    tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg);
+    
+    /* je label1 */
+    tcg_out8(s, 0x70 + JCC_JE);
+    label1_ptr = s->code_ptr;
+    s->code_ptr++;
+
+    /* XXX: move that code at the end of the TB */
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RSI, mem_index);
+    tcg_out8(s, 0xe8);
+    tcg_out32(s, (tcg_target_long)qemu_ld_helpers[s_bits] - 
+              (tcg_target_long)s->code_ptr - 4);
+
+    switch(opc) {
+    case 0 | 4:
+        /* movsbq */
+        tcg_out_modrm(s, 0xbe | P_EXT | P_REXW, data_reg, TCG_REG_RAX);
+        break;
+    case 1 | 4:
+        /* movswq */
+        tcg_out_modrm(s, 0xbf | P_EXT | P_REXW, data_reg, TCG_REG_RAX);
+        break;
+    case 2 | 4:
+        /* movslq */
+        tcg_out_modrm(s, 0x63 | P_REXW, data_reg, TCG_REG_RAX);
+        break;
+    case 0:
+    case 1:
+    case 2:
+    default:
+        /* movl */
+        tcg_out_modrm(s, 0x8b, data_reg, TCG_REG_RAX);
+        break;
+    case 3:
+        tcg_out_mov(s, data_reg, TCG_REG_RAX);
+        break;
+    }
+
+    /* jmp label2 */
+    tcg_out8(s, 0xeb);
+    label2_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* label1: */
+    *label1_ptr = s->code_ptr - label1_ptr - 1;
+
+    /* add x(r1), r0 */
+    tcg_out_modrm_offset(s, 0x03 | P_REXW, r0, r1, offsetof(CPUTLBEntry, addend) - 
+                         offsetof(CPUTLBEntry, addr_read));
+#else
+    r0 = addr_reg;
+#endif    
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 1;
+#else
+    bswap = 0;
+#endif
+    switch(opc) {
+    case 0:
+        /* movzbl */
+        tcg_out_modrm_offset(s, 0xb6 | P_EXT, data_reg, r0, 0);
+        break;
+    case 0 | 4:
+        /* movsbX */
+        tcg_out_modrm_offset(s, 0xbe | P_EXT | rexw, data_reg, r0, 0);
+        break;
+    case 1:
+        /* movzwl */
+        tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, 0);
+        if (bswap) {
+            /* rolw $8, data_reg */
+            tcg_out8(s, 0x66); 
+            tcg_out_modrm(s, 0xc1, 0, data_reg);
+            tcg_out8(s, 8);
+        }
+        break;
+    case 1 | 4:
+        if (bswap) {
+            /* movzwl */
+            tcg_out_modrm_offset(s, 0xb7 | P_EXT, data_reg, r0, 0);
+            /* rolw $8, data_reg */
+            tcg_out8(s, 0x66); 
+            tcg_out_modrm(s, 0xc1, 0, data_reg);
+            tcg_out8(s, 8);
+
+            /* movswX data_reg, data_reg */
+            tcg_out_modrm(s, 0xbf | P_EXT | rexw, data_reg, data_reg);
+        } else {
+            /* movswX */
+            tcg_out_modrm_offset(s, 0xbf | P_EXT | rexw, data_reg, r0, 0);
+        }
+        break;
+    case 2:
+        /* movl (r0), data_reg */
+        tcg_out_modrm_offset(s, 0x8b, data_reg, r0, 0);
+        if (bswap) {
+            /* bswap */
+            tcg_out_opc(s, (0xc8 + (data_reg & 7)) | P_EXT, 0, data_reg, 0);
+        }
+        break;
+    case 2 | 4:
+        if (bswap) {
+            /* movl (r0), data_reg */
+            tcg_out_modrm_offset(s, 0x8b, data_reg, r0, 0);
+            /* bswap */
+            tcg_out_opc(s, (0xc8 + (data_reg & 7)) | P_EXT, 0, data_reg, 0);
+            /* movslq */
+            tcg_out_modrm(s, 0x63 | P_REXW, data_reg, data_reg);
+        } else {
+            /* movslq */
+            tcg_out_modrm_offset(s, 0x63 | P_REXW, data_reg, r0, 0);
+        }
+        break;
+    case 3:
+        /* movq (r0), data_reg */
+        tcg_out_modrm_offset(s, 0x8b | P_REXW, data_reg, r0, 0);
+        if (bswap) {
+            /* bswap */
+            tcg_out_opc(s, (0xc8 + (data_reg & 7)) | P_EXT | P_REXW, 0, data_reg, 0);
+        }
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr = s->code_ptr - label2_ptr - 1;
+#endif
+}
+
+static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
+                            int opc)
+{
+    int addr_reg, data_reg, r0, r1, mem_index, s_bits, bswap, rexw;
+#if defined(CONFIG_SOFTMMU)
+    uint8_t *label1_ptr, *label2_ptr;
+#endif
+
+    data_reg = *args++;
+    addr_reg = *args++;
+    mem_index = *args;
+
+    s_bits = opc;
+
+    r0 = TCG_REG_RDI;
+    r1 = TCG_REG_RSI;
+
+#if TARGET_LONG_BITS == 32
+    rexw = 0;
+#else
+    rexw = P_REXW;
+#endif
+#if defined(CONFIG_SOFTMMU)
+    /* mov */
+    tcg_out_modrm(s, 0x8b | rexw, r1, addr_reg);
+
+    /* mov */
+    tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg);
+ 
+    tcg_out_modrm(s, 0xc1 | rexw, 5, r1); /* shr $x, r1 */
+    tcg_out8(s, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); 
+    
+    tcg_out_modrm(s, 0x81 | rexw, 4, r0); /* andl $x, r0 */
+    tcg_out32(s, TARGET_PAGE_MASK | ((1 << s_bits) - 1));
+    
+    tcg_out_modrm(s, 0x81, 4, r1); /* andl $x, r1 */
+    tcg_out32(s, (CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS);
+
+    /* lea offset(r1, env), r1 */
+    tcg_out_modrm_offset2(s, 0x8d | P_REXW, r1, r1, TCG_AREG0, 0,
+                          offsetof(CPUState, tlb_table[mem_index][0].addr_write));
+
+    /* cmp 0(r1), r0 */
+    tcg_out_modrm_offset(s, 0x3b | rexw, r0, r1, 0);
+    
+    /* mov */
+    tcg_out_modrm(s, 0x8b | rexw, r0, addr_reg);
+    
+    /* je label1 */
+    tcg_out8(s, 0x70 + JCC_JE);
+    label1_ptr = s->code_ptr;
+    s->code_ptr++;
+
+    /* XXX: move that code at the end of the TB */
+    switch(opc) {
+    case 0:
+        /* movzbl */
+        tcg_out_modrm(s, 0xb6 | P_EXT | P_REXB, TCG_REG_RSI, data_reg);
+        break;
+    case 1:
+        /* movzwl */
+        tcg_out_modrm(s, 0xb7 | P_EXT, TCG_REG_RSI, data_reg);
+        break;
+    case 2:
+        /* movl */
+        tcg_out_modrm(s, 0x8b, TCG_REG_RSI, data_reg);
+        break;
+    default:
+    case 3:
+        tcg_out_mov(s, TCG_REG_RSI, data_reg);
+        break;
+    }
+    tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_RDX, mem_index);
+    tcg_out8(s, 0xe8);
+    tcg_out32(s, (tcg_target_long)qemu_st_helpers[s_bits] - 
+              (tcg_target_long)s->code_ptr - 4);
+
+    /* jmp label2 */
+    tcg_out8(s, 0xeb);
+    label2_ptr = s->code_ptr;
+    s->code_ptr++;
+    
+    /* label1: */
+    *label1_ptr = s->code_ptr - label1_ptr - 1;
+
+    /* add x(r1), r0 */
+    tcg_out_modrm_offset(s, 0x03 | P_REXW, r0, r1, offsetof(CPUTLBEntry, addend) - 
+                         offsetof(CPUTLBEntry, addr_write));
+#else
+    r0 = addr_reg;
+#endif
+
+#ifdef TARGET_WORDS_BIGENDIAN
+    bswap = 1;
+#else
+    bswap = 0;
+#endif
+    switch(opc) {
+    case 0:
+        /* movb */
+        tcg_out_modrm_offset(s, 0x88 | P_REXB, data_reg, r0, 0);
+        break;
+    case 1:
+        if (bswap) {
+            tcg_out_modrm(s, 0x8b, r1, data_reg); /* movl */
+            tcg_out8(s, 0x66); /* rolw $8, %ecx */
+            tcg_out_modrm(s, 0xc1, 0, r1);
+            tcg_out8(s, 8);
+            data_reg = r1;
+        }
+        /* movw */
+        tcg_out8(s, 0x66);
+        tcg_out_modrm_offset(s, 0x89, data_reg, r0, 0);
+        break;
+    case 2:
+        if (bswap) {
+            tcg_out_modrm(s, 0x8b, r1, data_reg); /* movl */
+            /* bswap data_reg */
+            tcg_out_opc(s, (0xc8 + r1) | P_EXT, 0, r1, 0);
+            data_reg = r1;
+        }
+        /* movl */
+        tcg_out_modrm_offset(s, 0x89, data_reg, r0, 0);
+        break;
+    case 3:
+        if (bswap) {
+            tcg_out_mov(s, r1, data_reg);
+            /* bswap data_reg */
+            tcg_out_opc(s, (0xc8 + r1) | P_EXT | P_REXW, 0, r1, 0);
+            data_reg = r1;
+        }
+        /* movq */
+        tcg_out_modrm_offset(s, 0x89 | P_REXW, data_reg, r0, 0);
+        break;
+    default:
+        tcg_abort();
+    }
+
+#if defined(CONFIG_SOFTMMU)
+    /* label2: */
+    *label2_ptr = s->code_ptr - label2_ptr - 1;
+#endif
+}
+
+static inline void tcg_out_op(TCGContext *s, int opc, const TCGArg *args,
+                              const int *const_args)
+{
+    int c;
+    
+    switch(opc) {
+    case INDEX_op_exit_tb:
+        tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_RAX, args[0]);
+        tcg_out8(s, 0xe9); /* jmp tb_ret_addr */
+        tcg_out32(s, tb_ret_addr - s->code_ptr - 4);
+        break;
+    case INDEX_op_goto_tb:
+        if (s->tb_jmp_offset) {
+            /* direct jump method */
+            tcg_out8(s, 0xe9); /* jmp im */
+            s->tb_jmp_offset[args[0]] = s->code_ptr - s->code_buf;
+            tcg_out32(s, 0);
+        } else {
+            /* indirect jump method */
+            /* jmp Ev */
+            tcg_out_modrm_offset(s, 0xff, 4, -1, 
+                                 (tcg_target_long)(s->tb_next + 
+                                                   args[0]));
+        }
+        s->tb_next_offset[args[0]] = s->code_ptr - s->code_buf;
+        break;
+    case INDEX_op_call:
+        if (const_args[0]) {
+            tcg_out8(s, 0xe8);
+            tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4);
+        } else {
+            tcg_out_modrm(s, 0xff, 2, args[0]);
+        }
+        break;
+    case INDEX_op_jmp:
+        if (const_args[0]) {
+            tcg_out8(s, 0xe9);
+            tcg_out32(s, args[0] - (tcg_target_long)s->code_ptr - 4);
+        } else {
+            tcg_out_modrm(s, 0xff, 4, args[0]);
+        }
+        break;
+    case INDEX_op_br:
+        tcg_out_jxx(s, JCC_JMP, args[0]);
+        break;
+    case INDEX_op_movi_i32:
+        tcg_out_movi(s, TCG_TYPE_I32, args[0], (uint32_t)args[1]);
+        break;
+    case INDEX_op_movi_i64:
+        tcg_out_movi(s, TCG_TYPE_I64, args[0], args[1]);
+        break;
+    case INDEX_op_ld8u_i32:
+    case INDEX_op_ld8u_i64:
+        /* movzbl */
+        tcg_out_modrm_offset(s, 0xb6 | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld8s_i32:
+        /* movsbl */
+        tcg_out_modrm_offset(s, 0xbe | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld8s_i64:
+        /* movsbq */
+        tcg_out_modrm_offset(s, 0xbe | P_EXT | P_REXW, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16u_i32:
+    case INDEX_op_ld16u_i64:
+        /* movzwl */
+        tcg_out_modrm_offset(s, 0xb7 | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16s_i32:
+        /* movswl */
+        tcg_out_modrm_offset(s, 0xbf | P_EXT, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld16s_i64:
+        /* movswq */
+        tcg_out_modrm_offset(s, 0xbf | P_EXT | P_REXW, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld_i32:
+    case INDEX_op_ld32u_i64:
+        /* movl */
+        tcg_out_modrm_offset(s, 0x8b, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld32s_i64:
+        /* movslq */
+        tcg_out_modrm_offset(s, 0x63 | P_REXW, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_ld_i64:
+        /* movq */
+        tcg_out_modrm_offset(s, 0x8b | P_REXW, args[0], args[1], args[2]);
+        break;
+        
+    case INDEX_op_st8_i32:
+    case INDEX_op_st8_i64:
+        /* movb */
+        tcg_out_modrm_offset(s, 0x88 | P_REXB, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st16_i32:
+    case INDEX_op_st16_i64:
+        /* movw */
+        tcg_out8(s, 0x66);
+        tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st_i32:
+    case INDEX_op_st32_i64:
+        /* movl */
+        tcg_out_modrm_offset(s, 0x89, args[0], args[1], args[2]);
+        break;
+    case INDEX_op_st_i64:
+        /* movq */
+        tcg_out_modrm_offset(s, 0x89 | P_REXW, args[0], args[1], args[2]);
+        break;
+
+    case INDEX_op_sub_i32:
+        c = ARITH_SUB;
+        goto gen_arith32;
+    case INDEX_op_and_i32:
+        c = ARITH_AND;
+        goto gen_arith32;
+    case INDEX_op_or_i32:
+        c = ARITH_OR;
+        goto gen_arith32;
+    case INDEX_op_xor_i32:
+        c = ARITH_XOR;
+        goto gen_arith32;
+    case INDEX_op_add_i32:
+        c = ARITH_ADD;
+    gen_arith32:
+        if (const_args[2]) {
+            tgen_arithi32(s, c, args[0], args[2]);
+        } else {
+            tcg_out_modrm(s, 0x01 | (c << 3), args[2], args[0]);
+        }
+        break;
+
+    case INDEX_op_sub_i64:
+        c = ARITH_SUB;
+        goto gen_arith64;
+    case INDEX_op_and_i64:
+        c = ARITH_AND;
+        goto gen_arith64;
+    case INDEX_op_or_i64:
+        c = ARITH_OR;
+        goto gen_arith64;
+    case INDEX_op_xor_i64:
+        c = ARITH_XOR;
+        goto gen_arith64;
+    case INDEX_op_add_i64:
+        c = ARITH_ADD;
+    gen_arith64:
+        if (const_args[2]) {
+            tgen_arithi64(s, c, args[0], args[2]);
+        } else {
+            tcg_out_modrm(s, 0x01 | (c << 3) | P_REXW, args[2], args[0]);
+        }
+        break;
+
+    case INDEX_op_mul_i32:
+        if (const_args[2]) {
+            int32_t val;
+            val = args[2];
+            if (val == (int8_t)val) {
+                tcg_out_modrm(s, 0x6b, args[0], args[0]);
+                tcg_out8(s, val);
+            } else {
+                tcg_out_modrm(s, 0x69, args[0], args[0]);
+                tcg_out32(s, val);
+            }
+        } else {
+            tcg_out_modrm(s, 0xaf | P_EXT, args[0], args[2]);
+        }
+        break;
+    case INDEX_op_mul_i64:
+        if (const_args[2]) {
+            int32_t val;
+            val = args[2];
+            if (val == (int8_t)val) {
+                tcg_out_modrm(s, 0x6b | P_REXW, args[0], args[0]);
+                tcg_out8(s, val);
+            } else {
+                tcg_out_modrm(s, 0x69 | P_REXW, args[0], args[0]);
+                tcg_out32(s, val);
+            }
+        } else {
+            tcg_out_modrm(s, 0xaf | P_EXT | P_REXW, args[0], args[2]);
+        }
+        break;
+    case INDEX_op_div2_i32:
+        tcg_out_modrm(s, 0xf7, 7, args[4]);
+        break;
+    case INDEX_op_divu2_i32:
+        tcg_out_modrm(s, 0xf7, 6, args[4]);
+        break;
+    case INDEX_op_div2_i64:
+        tcg_out_modrm(s, 0xf7 | P_REXW, 7, args[4]);
+        break;
+    case INDEX_op_divu2_i64:
+        tcg_out_modrm(s, 0xf7 | P_REXW, 6, args[4]);
+        break;
+
+    case INDEX_op_shl_i32:
+        c = SHIFT_SHL;
+    gen_shift32:
+        if (const_args[2]) {
+            if (args[2] == 1) {
+                tcg_out_modrm(s, 0xd1, c, args[0]);
+            } else {
+                tcg_out_modrm(s, 0xc1, c, args[0]);
+                tcg_out8(s, args[2]);
+            }
+        } else {
+            tcg_out_modrm(s, 0xd3, c, args[0]);
+        }
+        break;
+    case INDEX_op_shr_i32:
+        c = SHIFT_SHR;
+        goto gen_shift32;
+    case INDEX_op_sar_i32:
+        c = SHIFT_SAR;
+        goto gen_shift32;
+        
+    case INDEX_op_shl_i64:
+        c = SHIFT_SHL;
+    gen_shift64:
+        if (const_args[2]) {
+            if (args[2] == 1) {
+                tcg_out_modrm(s, 0xd1 | P_REXW, c, args[0]);
+            } else {
+                tcg_out_modrm(s, 0xc1 | P_REXW, c, args[0]);
+                tcg_out8(s, args[2]);
+            }
+        } else {
+            tcg_out_modrm(s, 0xd3 | P_REXW, c, args[0]);
+        }
+        break;
+    case INDEX_op_shr_i64:
+        c = SHIFT_SHR;
+        goto gen_shift64;
+    case INDEX_op_sar_i64:
+        c = SHIFT_SAR;
+        goto gen_shift64;
+        
+    case INDEX_op_brcond_i32:
+        tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], 
+                       args[3], 0);
+        break;
+    case INDEX_op_brcond_i64:
+        tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], 
+                       args[3], P_REXW);
+        break;
+
+    case INDEX_op_bswap_i32:
+        tcg_out_opc(s, (0xc8 + (args[0] & 7)) | P_EXT, 0, args[0], 0);
+        break;
+    case INDEX_op_bswap_i64:
+        tcg_out_opc(s, (0xc8 + (args[0] & 7)) | P_EXT | P_REXW, 0, args[0], 0);
+        break;
+
+    case INDEX_op_neg_i32:
+        tcg_out_modrm(s, 0xf7, 3, args[0]);
+        break;
+    case INDEX_op_neg_i64:
+        tcg_out_modrm(s, 0xf7 | P_REXW, 3, args[0]);
+        break;
+
+    case INDEX_op_ext8s_i32:
+        tcg_out_modrm(s, 0xbe | P_EXT | P_REXB, args[0], args[1]);
+        break;
+    case INDEX_op_ext16s_i32:
+        tcg_out_modrm(s, 0xbf | P_EXT, args[0], args[1]);
+        break;
+    case INDEX_op_ext8s_i64:
+        tcg_out_modrm(s, 0xbe | P_EXT | P_REXW, args[0], args[1]);
+        break;
+    case INDEX_op_ext16s_i64:
+        tcg_out_modrm(s, 0xbf | P_EXT | P_REXW, args[0], args[1]);
+        break;
+    case INDEX_op_ext32s_i64:
+        tcg_out_modrm(s, 0x63 | P_REXW, args[0], args[1]);
+        break;
+
+    case INDEX_op_qemu_ld8u:
+        tcg_out_qemu_ld(s, args, 0);
+        break;
+    case INDEX_op_qemu_ld8s:
+        tcg_out_qemu_ld(s, args, 0 | 4);
+        break;
+    case INDEX_op_qemu_ld16u:
+        tcg_out_qemu_ld(s, args, 1);
+        break;
+    case INDEX_op_qemu_ld16s:
+        tcg_out_qemu_ld(s, args, 1 | 4);
+        break;
+    case INDEX_op_qemu_ld32u:
+        tcg_out_qemu_ld(s, args, 2);
+        break;
+    case INDEX_op_qemu_ld32s:
+        tcg_out_qemu_ld(s, args, 2 | 4);
+        break;
+    case INDEX_op_qemu_ld64:
+        tcg_out_qemu_ld(s, args, 3);
+        break;
+        
+    case INDEX_op_qemu_st8:
+        tcg_out_qemu_st(s, args, 0);
+        break;
+    case INDEX_op_qemu_st16:
+        tcg_out_qemu_st(s, args, 1);
+        break;
+    case INDEX_op_qemu_st32:
+        tcg_out_qemu_st(s, args, 2);
+        break;
+    case INDEX_op_qemu_st64:
+        tcg_out_qemu_st(s, args, 3);
+        break;
+
+    default:
+        tcg_abort();
+    }
+}
+
+static int tcg_target_callee_save_regs[] = {
+    TCG_REG_RBP,
+    TCG_REG_RBX,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    /*    TCG_REG_R14, */ /* currently used for the global env, so no
+                             need to save */
+    TCG_REG_R15,
+};
+
+static inline void tcg_out_push(TCGContext *s, int reg)
+{
+    tcg_out_opc(s, (0x50 + (reg & 7)), 0, reg, 0);
+}
+
+static inline void tcg_out_pop(TCGContext *s, int reg)
+{
+    tcg_out_opc(s, (0x58 + (reg & 7)), 0, reg, 0);
+}
+
+/* Generate global QEMU prologue and epilogue code */
+void tcg_target_qemu_prologue(TCGContext *s)
+{
+    int i, frame_size, push_size, stack_addend;
+
+    /* TB prologue */
+    /* save all callee saved registers */
+    for(i = 0; i < ARRAY_SIZE(tcg_target_callee_save_regs); i++) {
+        tcg_out_push(s, tcg_target_callee_save_regs[i]);
+
+    }
+    /* reserve some stack space */
+    push_size = 8 + ARRAY_SIZE(tcg_target_callee_save_regs) * 8;
+    frame_size = push_size + TCG_STATIC_CALL_ARGS_SIZE;
+    frame_size = (frame_size + TCG_TARGET_STACK_ALIGN - 1) & 
+        ~(TCG_TARGET_STACK_ALIGN - 1);
+    stack_addend = frame_size - push_size;
+    tcg_out_addi(s, TCG_REG_RSP, -stack_addend);
+
+    tcg_out_modrm(s, 0xff, 4, TCG_REG_RDI); /* jmp *%rdi */
+    
+    /* TB epilogue */
+    tb_ret_addr = s->code_ptr;
+    tcg_out_addi(s, TCG_REG_RSP, stack_addend);
+    for(i = ARRAY_SIZE(tcg_target_callee_save_regs) - 1; i >= 0; i--) {
+        tcg_out_pop(s, tcg_target_callee_save_regs[i]);
+    }
+    tcg_out8(s, 0xc3); /* ret */
+}
+
+static const TCGTargetOpDef x86_64_op_defs[] = {
+    { INDEX_op_exit_tb, { } },
+    { INDEX_op_goto_tb, { } },
+    { INDEX_op_call, { "ri" } }, /* XXX: might need a specific constant constraint */
+    { INDEX_op_jmp, { "ri" } }, /* XXX: might need a specific constant constraint */
+    { INDEX_op_br, { } },
+
+    { INDEX_op_mov_i32, { "r", "r" } },
+    { INDEX_op_movi_i32, { "r" } },
+    { INDEX_op_ld8u_i32, { "r", "r" } },
+    { INDEX_op_ld8s_i32, { "r", "r" } },
+    { INDEX_op_ld16u_i32, { "r", "r" } },
+    { INDEX_op_ld16s_i32, { "r", "r" } },
+    { INDEX_op_ld_i32, { "r", "r" } },
+    { INDEX_op_st8_i32, { "r", "r" } },
+    { INDEX_op_st16_i32, { "r", "r" } },
+    { INDEX_op_st_i32, { "r", "r" } },
+
+    { INDEX_op_add_i32, { "r", "0", "ri" } },
+    { INDEX_op_mul_i32, { "r", "0", "ri" } },
+    { INDEX_op_div2_i32, { "a", "d", "0", "1", "r" } },
+    { INDEX_op_divu2_i32, { "a", "d", "0", "1", "r" } },
+    { INDEX_op_sub_i32, { "r", "0", "ri" } },
+    { INDEX_op_and_i32, { "r", "0", "ri" } },
+    { INDEX_op_or_i32, { "r", "0", "ri" } },
+    { INDEX_op_xor_i32, { "r", "0", "ri" } },
+
+    { INDEX_op_shl_i32, { "r", "0", "ci" } },
+    { INDEX_op_shr_i32, { "r", "0", "ci" } },
+    { INDEX_op_sar_i32, { "r", "0", "ci" } },
+
+    { INDEX_op_brcond_i32, { "r", "ri" } },
+
+    { INDEX_op_mov_i64, { "r", "r" } },
+    { INDEX_op_movi_i64, { "r" } },
+    { INDEX_op_ld8u_i64, { "r", "r" } },
+    { INDEX_op_ld8s_i64, { "r", "r" } },
+    { INDEX_op_ld16u_i64, { "r", "r" } },
+    { INDEX_op_ld16s_i64, { "r", "r" } },
+    { INDEX_op_ld32u_i64, { "r", "r" } },
+    { INDEX_op_ld32s_i64, { "r", "r" } },
+    { INDEX_op_ld_i64, { "r", "r" } },
+    { INDEX_op_st8_i64, { "r", "r" } },
+    { INDEX_op_st16_i64, { "r", "r" } },
+    { INDEX_op_st32_i64, { "r", "r" } },
+    { INDEX_op_st_i64, { "r", "r" } },
+
+    { INDEX_op_add_i64, { "r", "0", "re" } },
+    { INDEX_op_mul_i64, { "r", "0", "re" } },
+    { INDEX_op_div2_i64, { "a", "d", "0", "1", "r" } },
+    { INDEX_op_divu2_i64, { "a", "d", "0", "1", "r" } },
+    { INDEX_op_sub_i64, { "r", "0", "re" } },
+    { INDEX_op_and_i64, { "r", "0", "reZ" } },
+    { INDEX_op_or_i64, { "r", "0", "re" } },
+    { INDEX_op_xor_i64, { "r", "0", "re" } },
+
+    { INDEX_op_shl_i64, { "r", "0", "ci" } },
+    { INDEX_op_shr_i64, { "r", "0", "ci" } },
+    { INDEX_op_sar_i64, { "r", "0", "ci" } },
+
+    { INDEX_op_brcond_i64, { "r", "re" } },
+
+    { INDEX_op_bswap_i32, { "r", "0" } },
+    { INDEX_op_bswap_i64, { "r", "0" } },
+
+    { INDEX_op_neg_i32, { "r", "0" } },
+    { INDEX_op_neg_i64, { "r", "0" } },
+
+    { INDEX_op_ext8s_i32, { "r", "r"} },
+    { INDEX_op_ext16s_i32, { "r", "r"} },
+    { INDEX_op_ext8s_i64, { "r", "r"} },
+    { INDEX_op_ext16s_i64, { "r", "r"} },
+    { INDEX_op_ext32s_i64, { "r", "r"} },
+
+    { INDEX_op_qemu_ld8u, { "r", "L" } },
+    { INDEX_op_qemu_ld8s, { "r", "L" } },
+    { INDEX_op_qemu_ld16u, { "r", "L" } },
+    { INDEX_op_qemu_ld16s, { "r", "L" } },
+    { INDEX_op_qemu_ld32u, { "r", "L" } },
+    { INDEX_op_qemu_ld32s, { "r", "L" } },
+    { INDEX_op_qemu_ld64, { "r", "L" } },
+
+    { INDEX_op_qemu_st8, { "L", "L" } },
+    { INDEX_op_qemu_st16, { "L", "L" } },
+    { INDEX_op_qemu_st32, { "L", "L" } },
+    { INDEX_op_qemu_st64, { "L", "L", "L" } },
+
+    { -1 },
+};
+
+void tcg_target_init(TCGContext *s)
+{
+    /* fail safe */
+    if ((1 << CPU_TLB_ENTRY_BITS) != sizeof(CPUTLBEntry))
+        tcg_abort();
+
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I32], 0, 0xffff);
+    tcg_regset_set32(tcg_target_available_regs[TCG_TYPE_I64], 0, 0xffff);
+    tcg_regset_set32(tcg_target_call_clobber_regs, 0,
+                     (1 << TCG_REG_RDI) | 
+                     (1 << TCG_REG_RSI) | 
+                     (1 << TCG_REG_RDX) |
+                     (1 << TCG_REG_RCX) |
+                     (1 << TCG_REG_R8) |
+                     (1 << TCG_REG_R9) |
+                     (1 << TCG_REG_RAX) |
+                     (1 << TCG_REG_R10) |
+                     (1 << TCG_REG_R11));
+    
+    tcg_regset_clear(s->reserved_regs);
+    tcg_regset_set_reg(s->reserved_regs, TCG_REG_RSP);
+
+    tcg_add_target_add_op_defs(x86_64_op_defs);
+}
diff --git a/tcg/x86_64/tcg-target.h b/tcg/x86_64/tcg-target.h
new file mode 100644
index 0000000..9a0cca0
--- /dev/null
+++ b/tcg/x86_64/tcg-target.h
@@ -0,0 +1,77 @@
+/*
+ * Tiny Code Generator for QEMU
+ *
+ * Copyright (c) 2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#define TCG_TARGET_X86_64 1
+
+#define TCG_TARGET_REG_BITS 64
+//#define TCG_TARGET_WORDS_BIGENDIAN
+
+#define TCG_TARGET_NB_REGS 16
+
+enum {
+    TCG_REG_RAX = 0,
+    TCG_REG_RCX,
+    TCG_REG_RDX,
+    TCG_REG_RBX,
+    TCG_REG_RSP,
+    TCG_REG_RBP,
+    TCG_REG_RSI,
+    TCG_REG_RDI,
+    TCG_REG_R8,
+    TCG_REG_R9,
+    TCG_REG_R10,
+    TCG_REG_R11,
+    TCG_REG_R12,
+    TCG_REG_R13,
+    TCG_REG_R14,
+    TCG_REG_R15,
+};
+
+#define TCG_CT_CONST_S32 0x100
+#define TCG_CT_CONST_U32 0x200
+
+/* used for function call generation */
+#define TCG_REG_CALL_STACK TCG_REG_RSP 
+#define TCG_TARGET_STACK_ALIGN 16
+#define TCG_TARGET_CALL_STACK_OFFSET 0
+
+/* optional instructions */
+#define TCG_TARGET_HAS_bswap_i32
+#define TCG_TARGET_HAS_bswap_i64
+#define TCG_TARGET_HAS_neg_i32
+#define TCG_TARGET_HAS_neg_i64
+#define TCG_TARGET_HAS_ext8s_i32
+#define TCG_TARGET_HAS_ext16s_i32
+#define TCG_TARGET_HAS_ext8s_i64
+#define TCG_TARGET_HAS_ext16s_i64
+#define TCG_TARGET_HAS_ext32s_i64
+
+/* Note: must be synced with dyngen-exec.h */
+#define TCG_AREG0 TCG_REG_R14
+#define TCG_AREG1 TCG_REG_R15
+#define TCG_AREG2 TCG_REG_R12
+#define TCG_AREG3 TCG_REG_R13
+
+static inline void flush_icache_range(unsigned long start, unsigned long stop)
+{
+}
diff --git a/tcpdump.c b/tcpdump.c
new file mode 100644
index 0000000..e562253
--- /dev/null
+++ b/tcpdump.c
@@ -0,0 +1,147 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "tcpdump.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+int  qemu_tcpdump_active;
+
+static FILE*     capture_file;
+static uint64_t  capture_count;
+static uint64_t  capture_size;
+static int       capture_init;
+
+static void
+capture_atexit(void)
+{
+    if (qemu_tcpdump_active) {
+        fclose(capture_file);
+        qemu_tcpdump_active = 0;
+    }
+}
+
+/* See http://wiki.wireshark.org/Development/LibpcapFileFormat for
+ * the complete description of the packet capture file format
+ */
+
+#define  PCAP_MAGIC     0xa1b2c3d4
+#define  PCAP_MAJOR     2
+#define  PCAP_MINOR     4
+#define  PCAP_SNAPLEN   65535
+#define  PCAP_ETHERNET  1
+
+static int
+pcap_write_header( FILE*  out )
+{
+    typedef struct {
+        uint32_t   magic;
+        uint16_t   version_major;
+        uint16_t   version_minor;
+        int32_t    this_zone;
+        uint32_t   sigfigs;
+        uint32_t   snaplen;
+        uint32_t   network;
+    } PcapHeader;
+
+    PcapHeader  h;
+
+    h.magic         = PCAP_MAGIC;
+    h.version_major = PCAP_MAJOR;
+    h.version_minor = PCAP_MINOR;
+    h.this_zone     = 0;
+    h.sigfigs       = 0;  /* all tools set it to 0 in practice */
+    h.snaplen       = PCAP_SNAPLEN;
+    h.network       = PCAP_ETHERNET;
+
+    if (fwrite(&h, sizeof(h), 1, out) != 1) {
+        return -1;
+    }
+    return 0;
+}
+
+int
+qemu_tcpdump_start( const char*  filepath )
+{
+    if (!capture_init) {
+        capture_init = 1;
+        atexit(capture_atexit);
+    }
+
+    qemu_tcpdump_stop();
+
+    if (filepath == NULL)
+        return -1;
+
+    capture_file = fopen(filepath, "wb");
+    if (capture_file == NULL)
+        return -1;
+
+    if (pcap_write_header(capture_file) < 0)
+        return -1;
+
+    qemu_tcpdump_active = 1;
+    return 0;
+}
+
+void
+qemu_tcpdump_stop( void )
+{
+    if (!qemu_tcpdump_active)
+        return;
+
+    qemu_tcpdump_active = 0;
+
+    capture_count = 0;
+    capture_size  = 0;
+
+    fclose(capture_file);
+    capture_file = NULL;
+}
+
+void
+qemu_tcpdump_packet( const void*  base, int  len )
+{
+    typedef struct {
+        uint32_t  ts_sec;
+        uint32_t  ts_usec;
+        uint32_t  incl_len;
+        uint32_t  orig_len;
+    } PacketHeader;
+
+    PacketHeader    h;
+    struct timeval  now;
+    int             len2 = len;
+
+    if (len2 > PCAP_SNAPLEN)
+        len2 = PCAP_SNAPLEN;
+
+    gettimeofday(&now, NULL);
+    h.ts_sec   = (uint32_t) now.tv_sec;
+    h.ts_usec  = (uint32_t) now.tv_usec;
+    h.incl_len = (uint32_t) len2;
+    h.orig_len = (uint32_t) len;
+
+    fwrite( &h, sizeof(h), 1, capture_file );
+    fwrite( base, 1, len2, capture_file );
+
+    capture_count += 1;
+    capture_size  += len2;
+}
+
+void
+qemu_tcpdump_stats( uint64_t  *pcount, uint64_t*  psize )
+{
+    *pcount = capture_count;
+    *psize  = capture_size;
+}
+
diff --git a/tcpdump.h b/tcpdump.h
new file mode 100644
index 0000000..fc23d3f
--- /dev/null
+++ b/tcpdump.h
@@ -0,0 +1,36 @@
+/* Copyright (C) 2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _QEMU_TCPDUMP_H
+#define _QEMU_TCPDUMP_H
+
+#include <stdint.h>
+
+/* global flag, set to 1 when packet captupe is active */
+extern int  qemu_tcpdump_active;
+
+/* start a new packet capture, close the current one if any.
+ * returns 0 on success, and -1 on failure (see errno then) */
+extern int  qemu_tcpdump_start( const char*  filepath );
+
+/* stop the current packet capture, if any */
+extern void qemu_tcpdump_stop( void );
+
+/* send an ethernet packet to the packet capture file, if any */
+extern void qemu_tcpdump_packet( const void*  base, int  len );
+
+/* returns interesting stats, like the number of packets captures,
+ * and the total size of these packets. Note: the file will be larger
+ * due to global and packet headers.
+ */
+extern void  qemu_tcpdump_stats( uint64_t  *pcount, uint64_t*  psize );
+
+#endif /* _QEMU_TCPDUMP_H */
diff --git a/telephony/Jamfile b/telephony/Jamfile
new file mode 100644
index 0000000..0f2b7b9
--- /dev/null
+++ b/telephony/Jamfile
@@ -0,0 +1,13 @@
+Main telephony : telephony.c ;
+
+Library  sysdeps : sysdeps_posix.c ;
+Library  android_modem : android_modem.c sim_card.c ;
+
+for prog in test1 test2 {
+    Main          $(prog) : $(prog).c ;
+    LinkLibraries $(prog) : sysdeps ;
+}
+
+Main           simulator : simulator.c ;
+LinkLibraries  simulator : sysdeps android_modem ;
+
diff --git a/telephony/android_modem.c b/telephony/android_modem.c
new file mode 100644
index 0000000..79e93b2
--- /dev/null
+++ b/telephony/android_modem.c
@@ -0,0 +1,1870 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android/android.h"
+#include "android_modem.h"
+#include "android/utils/debug.h"
+#include "android/utils/timezone.h"
+#include "android/utils/system.h"
+#include "sim_card.h"
+#include "sysdeps.h"
+#include <memory.h>
+#include <stdarg.h>
+#include <time.h>
+#include <assert.h>
+#include <stdio.h>
+#include "sms.h"
+#include "remote_call.h"
+
+#define  DEBUG  1
+
+#if  1
+#  define  D_ACTIVE  VERBOSE_CHECK(modem)
+#else
+#  define  D_ACTIVE  DEBUG
+#endif
+
+#if 1
+#  define  R_ACTIVE  VERBOSE_CHECK(radio)
+#else
+#  define  R_ACTIVE  DEBUG
+#endif
+
+#if DEBUG
+#  define  D(...)   do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
+#  define  R(...)   do { if (R_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
+#else
+#  define  D(...)   ((void)0)
+#  define  R(...)   ((void)0)
+#endif
+
+#define  CALL_DELAY_DIAL   1000
+#define  CALL_DELAY_ALERT  1000
+
+/* the Android GSM stack checks that the operator's name has changed
+ * when roaming is on. If not, it will not update the Roaming status icon
+ *
+ * this means that we need to emulate two distinct operators:
+ * - the first one for the 'home' registration state, must also correspond
+ *   to the emulated user's IMEI
+ *
+ * - the second one for the 'roaming' registration state, must have a
+ *   different name and MCC/MNC
+ */
+
+#define  OPERATOR_HOME_INDEX 0
+#define  OPERATOR_HOME_MCC   310
+#define  OPERATOR_HOME_MNC   260
+#define  OPERATOR_HOME_NAME  "Android"
+#define  OPERATOR_HOME_MCCMNC  STRINGIFY(OPERATOR_HOME_MCC) \
+                               STRINGIFY(OPERATOR_HOME_MNC)
+
+#define  OPERATOR_ROAMING_INDEX 1
+#define  OPERATOR_ROAMING_MCC   310
+#define  OPERATOR_ROAMING_MNC   295
+#define  OPERATOR_ROAMING_NAME  "TelKila"
+#define  OPERATOR_ROAMING_MCCMNC  STRINGIFY(OPERATOR_ROAMING_MCC) \
+                                  STRINGIFY(OPERATOR_ROAMING_MNC)
+
+#if DEBUG
+static const char*  quote( const char*  line )
+{
+    static char  temp[1024];
+    const char*  hexdigits = "0123456789abcdef";
+    char*        p = temp;
+    int          c;
+
+    while ((c = *line++) != 0) {
+        c &= 255;
+        if (c >= 32 && c < 127) {
+            *p++ = c;
+        }
+        else if (c == '\r') {
+            memcpy( p, "<CR>", 4 );
+            p += 4;
+        }
+        else if (c == '\n') {
+            memcpy( p, "<LF>", 4 );strcat( p, "<LF>" );
+            p += 4;
+        }
+        else {
+            p[0] = '\\';
+            p[1] = 'x';
+            p[2] = hexdigits[ (c) >> 4 ];
+            p[3] = hexdigits[ (c) & 15 ];
+            p += 4;
+        }
+    }
+    *p = 0;
+    return temp;
+}
+#endif
+
+extern AGprsNetworkType
+android_parse_network_type( const char*  speed )
+{
+    const struct { const char* name; AGprsNetworkType  type; }  types[] = {
+         { "gprs", A_GPRS_NETWORK_GPRS },
+         { "edge", A_GPRS_NETWORK_EDGE },
+         { "umts", A_GPRS_NETWORK_UMTS },
+         { "hsdpa", A_GPRS_NETWORK_UMTS },  /* not handled yet by Android GSM framework */
+         { "full", A_GPRS_NETWORK_UMTS },
+         { NULL, 0 }
+    };
+    int  nn;
+
+    for (nn = 0; types[nn].name; nn++) {
+        if ( !strcmp(speed, types[nn].name) )
+            return types[nn].type;
+    }
+    /* not found, be conservative */
+    return A_GPRS_NETWORK_GPRS;
+}
+
+/* 'mode' for +CREG/+CGREG commands */
+typedef enum {
+    A_REGISTRATION_UNSOL_DISABLED     = 0,
+    A_REGISTRATION_UNSOL_ENABLED      = 1,
+    A_REGISTRATION_UNSOL_ENABLED_FULL = 2
+} ARegistrationUnsolMode;
+
+/* Operator selection mode, see +COPS commands */
+typedef enum {
+    A_SELECTION_AUTOMATIC,
+    A_SELECTION_MANUAL,
+    A_SELECTION_DEREGISTRATION,
+    A_SELECTION_SET_FORMAT,
+    A_SELECTION_MANUAL_AUTOMATIC
+} AOperatorSelection;
+
+/* Operator status, see +COPS commands */
+typedef enum {
+    A_STATUS_UNKNOWN = 0,
+    A_STATUS_AVAILABLE,
+    A_STATUS_CURRENT,
+    A_STATUS_DENIED
+} AOperatorStatus;
+
+typedef struct {
+    AOperatorStatus  status;
+    char             name[3][16];
+} AOperatorRec, *AOperator;
+
+typedef struct AVoiceCallRec {
+    ACallRec    call;
+    SysTimer    timer;
+    AModem      modem;
+    char        is_remote;
+} AVoiceCallRec, *AVoiceCall;
+
+#define  MAX_OPERATORS  4
+
+typedef enum {
+    A_DATA_IP = 0,
+    A_DATA_PPP
+} ADataType;
+
+#define  A_DATA_APN_SIZE  32
+
+typedef struct {
+    int        id;
+    int        active;
+    ADataType  type;
+    char       apn[ A_DATA_APN_SIZE ];
+
+} ADataContextRec, *ADataContext;
+
+/* the spec says that there can only be a max of 4 contexts */
+#define  MAX_DATA_CONTEXTS  4
+#define  MAX_CALLS          4
+
+#define  A_MODEM_SELF_SIZE   3
+
+typedef struct AModemRec_
+{
+    /* Radio state */
+    ARadioState   radio_state;
+    int           area_code;
+    int           cell_id;
+    int           base_port;
+
+    /* SMS */
+    int           wait_sms;
+
+    /* SIM card */
+    ASimCard      sim;
+
+    /* voice and data network registration */
+    ARegistrationUnsolMode   voice_mode;
+    ARegistrationState       voice_state;
+    ARegistrationUnsolMode   data_mode;
+    ARegistrationState       data_state;
+    AGprsNetworkType         data_network;
+
+    /* operator names */
+    AOperatorSelection  oper_selection_mode;
+    ANameIndex          oper_name_index;
+    int                 oper_index;
+    int                 oper_count;
+    AOperatorRec        operators[ MAX_OPERATORS ];
+
+    /* data connection contexts */
+    ADataContextRec     data_contexts[ MAX_DATA_CONTEXTS ];
+
+    /* active calls */
+    AVoiceCallRec       calls[ MAX_CALLS ];
+    int                 call_count;
+
+    /* unsolicited callback */  /* XXX: TODO: use this */
+    AModemUnsolFunc     unsol_func;
+    void*               unsol_opaque;
+
+    SmsReceiver         sms_receiver;
+
+    int                 out_size;
+    char                out_buff[1024];
+
+} AModemRec;
+
+
+static void
+amodem_unsol( AModem  modem, const char* format, ... )
+{
+    if (modem->unsol_func) {
+        va_list  args;
+        va_start(args, format);
+        vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args );
+        va_end(args);
+
+        modem->unsol_func( modem->unsol_opaque, modem->out_buff );
+    }
+}
+
+void
+amodem_receive_sms( AModem  modem, SmsPDU  sms )
+{
+#define  SMS_UNSOL_HEADER  "+CMT: 0\r\n"
+
+    if (modem->unsol_func) {
+        int    len, max;
+        char*  p;
+
+        strcpy( modem->out_buff, SMS_UNSOL_HEADER );
+        p   = modem->out_buff + (sizeof(SMS_UNSOL_HEADER)-1);
+        max = sizeof(modem->out_buff) - 3 - (sizeof(SMS_UNSOL_HEADER)-1);
+        len = smspdu_to_hex( sms, p, max );
+        if (len > max) /* too long */
+            return;
+        p[len]   = '\r';
+        p[len+1] = '\n';
+        p[len+2] = 0;
+
+        R( "SMS>> %s\n", p );
+
+        modem->unsol_func( modem->unsol_opaque, modem->out_buff );
+    }
+}
+
+static const char*
+amodem_printf( AModem  modem, const char*  format, ... )
+{
+    va_list  args;
+    va_start(args, format);
+    vsnprintf( modem->out_buff, sizeof(modem->out_buff), format, args );
+    va_end(args);
+
+    return modem->out_buff;
+}
+
+static void
+amodem_begin_line( AModem  modem )
+{
+    modem->out_size = 0;
+}
+
+static void
+amodem_add_line( AModem  modem, const char*  format, ... )
+{
+    va_list  args;
+    va_start(args, format);
+    modem->out_size += vsnprintf( modem->out_buff + modem->out_size,
+                                  sizeof(modem->out_buff) - modem->out_size,
+                                  format, args );
+    va_end(args);
+}
+
+static const char*
+amodem_end_line( AModem  modem )
+{
+    modem->out_buff[ modem->out_size ] = 0;
+    return modem->out_buff;
+}
+
+static void
+amodem_reset( AModem  modem )
+{
+    modem->radio_state = A_RADIO_STATE_OFF;
+    modem->wait_sms    = 0;
+
+    modem->oper_name_index     = 2;
+    modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
+    modem->oper_index          = 0;
+    modem->oper_count          = 2;
+
+    modem->area_code = -1;
+    modem->cell_id   = -1;
+
+    strcpy( modem->operators[0].name[0], OPERATOR_HOME_NAME );
+    strcpy( modem->operators[0].name[1], OPERATOR_HOME_NAME );
+    strcpy( modem->operators[0].name[2], OPERATOR_HOME_MCCMNC );
+
+    modem->operators[0].status        = A_STATUS_AVAILABLE;
+
+    strcpy( modem->operators[1].name[0], OPERATOR_ROAMING_NAME );
+    strcpy( modem->operators[1].name[1], OPERATOR_ROAMING_NAME );
+    strcpy( modem->operators[1].name[2], OPERATOR_ROAMING_MCCMNC );
+
+    modem->operators[1].status        = A_STATUS_AVAILABLE;
+
+    modem->voice_mode   = A_REGISTRATION_UNSOL_ENABLED_FULL;
+    modem->voice_state  = A_REGISTRATION_HOME;
+    modem->data_mode    = A_REGISTRATION_UNSOL_ENABLED_FULL;
+    modem->data_state   = A_REGISTRATION_HOME;
+    modem->data_network = A_GPRS_NETWORK_UMTS;
+}
+
+static AModemRec   _android_modem[1];
+
+AModem
+amodem_create( int  base_port, AModemUnsolFunc  unsol_func, void*  unsol_opaque )
+{
+    AModem  modem = _android_modem;
+
+    amodem_reset( modem );
+    modem->base_port    = base_port;
+    modem->unsol_func   = unsol_func;
+    modem->unsol_opaque = unsol_opaque;
+
+    modem->sim = asimcard_create();
+
+    return  modem;
+}
+
+void
+amodem_destroy( AModem  modem )
+{
+    asimcard_destroy( modem->sim );
+    modem->sim = NULL;
+}
+
+
+static int
+amodem_has_network( AModem  modem )
+{
+    return !(modem->radio_state == A_RADIO_STATE_OFF   ||
+             modem->oper_index < 0                  ||
+             modem->oper_index >= modem->oper_count ||
+             modem->oper_selection_mode == A_SELECTION_DEREGISTRATION );
+}
+
+
+ARadioState
+amodem_get_radio_state( AModem modem )
+{
+    return modem->radio_state;
+}
+
+void
+amodem_set_radio_state( AModem modem, ARadioState  state )
+{
+    modem->radio_state = state;
+}
+
+ASimCard
+amodem_get_sim( AModem  modem )
+{
+    return  modem->sim;
+}
+
+ARegistrationState
+amodem_get_voice_registration( AModem  modem )
+{
+    return modem->voice_state;
+}
+
+void
+amodem_set_voice_registration( AModem  modem, ARegistrationState  state )
+{
+    modem->voice_state = state;
+
+    if (state == A_REGISTRATION_HOME)
+        modem->oper_index = OPERATOR_HOME_INDEX;
+    else if (state == A_REGISTRATION_ROAMING)
+        modem->oper_index = OPERATOR_ROAMING_INDEX;
+
+    switch (modem->voice_mode) {
+        case A_REGISTRATION_UNSOL_ENABLED:
+            amodem_unsol( modem, "+CREG: %d,%d\r",
+                          modem->voice_mode, modem->voice_state );
+            break;
+
+        case A_REGISTRATION_UNSOL_ENABLED_FULL:
+            amodem_unsol( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"\r",
+                          modem->voice_mode, modem->voice_state,
+                          modem->area_code, modem->cell_id );
+            break;
+        default:
+            ;
+    }
+}
+
+ARegistrationState
+amodem_get_data_registration( AModem  modem )
+{
+    return modem->data_state;
+}
+
+void
+amodem_set_data_registration( AModem  modem, ARegistrationState  state )
+{
+    modem->data_state = state;
+
+    switch (modem->data_mode) {
+        case A_REGISTRATION_UNSOL_ENABLED:
+            amodem_unsol( modem, "+CGREG: %d,%d\r",
+                          modem->data_mode, modem->data_state );
+            break;
+
+        case A_REGISTRATION_UNSOL_ENABLED_FULL:
+            amodem_unsol( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"\r",
+                          modem->data_mode, modem->data_state,
+                          modem->area_code, modem->cell_id,
+                          modem->data_network );
+            break;
+
+        default:
+            ;
+    }
+}
+
+void
+amodem_set_data_network_type( AModem  modem, AGprsNetworkType   type )
+{
+    modem->data_network = type;
+    amodem_set_data_registration( modem, modem->data_state );
+}
+
+int
+amodem_get_operator_name ( AModem  modem, ANameIndex  index, char*  buffer, int  buffer_size )
+{
+    AOperator  oper;
+    int        len;
+
+    if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
+         (unsigned)index > 2 )
+        return 0;
+
+    oper = modem->operators + modem->oper_index;
+    len  = strlen(oper->name[index]) + 1;
+
+    if (buffer_size > len)
+        buffer_size = len;
+
+    if (buffer_size > 0) {
+        memcpy( buffer, oper->name[index], buffer_size-1 );
+        buffer[buffer_size] = 0;
+    }
+    return len;
+}
+
+/* reset one operator name from a user-provided buffer, set buffer_size to -1 for zero-terminated strings */
+void
+amodem_set_operator_name( AModem  modem, ANameIndex  index, const char*  buffer, int  buffer_size )
+{
+    AOperator  oper;
+    int        avail;
+
+    if ( (unsigned)modem->oper_index >= (unsigned)modem->oper_count ||
+         (unsigned)index > 2 )
+        return;
+
+    oper = modem->operators + modem->oper_index;
+
+    avail = sizeof(oper->name[0]);
+    if (buffer_size < 0)
+        buffer_size = strlen(buffer);
+    if (buffer_size > avail-1)
+        buffer_size = avail-1;
+    memcpy( oper->name[index], buffer, buffer_size );
+    oper->name[index][buffer_size] = 0;
+}
+
+/** CALLS
+ **/
+int
+amodem_get_call_count( AModem  modem )
+{
+    return modem->call_count;
+}
+
+ACall
+amodem_get_call( AModem  modem, int  index )
+{
+    if ((unsigned)index >= (unsigned)modem->call_count)
+        return NULL;
+
+    return &modem->calls[index].call;
+}
+
+static AVoiceCall
+amodem_alloc_call( AModem   modem )
+{
+    AVoiceCall  call  = NULL;
+    int         count = modem->call_count;
+
+    if (count < MAX_CALLS) {
+        int  id;
+
+        /* find a valid id for this call */
+        for (id = 0; id < modem->call_count; id++) {
+            int  found = 0;
+            int  nn;
+            for (nn = 0; nn < count; nn++) {
+                if ( modem->calls[nn].call.id == (id+1) ) {
+                    found = 1;
+                    break;
+                }
+            }
+            if (!found)
+                break;
+        }
+        call          = modem->calls + count;
+        call->call.id = id + 1;
+        call->modem   = modem;
+
+        modem->call_count += 1;
+    }
+    return call;
+}
+
+
+static void
+amodem_free_call( AModem  modem, AVoiceCall  call )
+{
+    int  nn;
+
+    if (call->timer) {
+        sys_timer_destroy( call->timer );
+        call->timer = NULL;
+    }
+
+    if (call->is_remote) {
+        remote_call_cancel( call->call.number, modem->base_port );
+        call->is_remote = 0;
+    }
+
+    for (nn = 0; nn < modem->call_count; nn++) {
+        if ( modem->calls + nn == call )
+            break;
+    }
+    assert( nn < modem->call_count );
+
+    memmove( modem->calls + nn,
+             modem->calls + nn + 1,
+             (modem->call_count - 1 - nn)*sizeof(*call) );
+
+    modem->call_count -= 1;
+}
+
+
+static AVoiceCall
+amodem_find_call( AModem  modem, int  id )
+{
+    int  nn;
+
+    for (nn = 0; nn < modem->call_count; nn++) {
+        AVoiceCall call = modem->calls + nn;
+        if (call->call.id == id)
+            return call;
+    }
+    return NULL;
+}
+
+static void
+amodem_send_calls_update( AModem  modem )
+{
+   /* despite its name, this really tells the system that the call
+    * state has changed */
+    amodem_unsol( modem, "RING\r" );
+}
+
+
+int
+amodem_add_inbound_call( AModem  modem, const char*  number )
+{
+    AVoiceCall  vcall = amodem_alloc_call( modem );
+    ACall       call  = &vcall->call;
+    int         len;
+
+    if (call == NULL)
+        return -1;
+
+    call->dir   = A_CALL_INBOUND;
+    call->state = A_CALL_INCOMING;
+    call->mode  = A_CALL_VOICE;
+    call->multi = 0;
+
+    vcall->is_remote = (remote_number_string_to_port(number) > 0);
+
+    len  = strlen(number);
+    if (len >= sizeof(call->number))
+        len = sizeof(call->number)-1;
+
+    memcpy( call->number, number, len );
+    call->number[len] = 0;
+
+    amodem_send_calls_update( modem );
+    return 0;
+}
+
+ACall
+amodem_find_call_by_number( AModem  modem, const char*  number )
+{
+    AVoiceCall  vcall = modem->calls;
+    AVoiceCall  vend  = vcall + modem->call_count;
+
+    if (!number)
+        return NULL;
+
+    for ( ; vcall < vend; vcall++ )
+        if ( !strcmp(vcall->call.number, number) )
+            return &vcall->call;
+
+    return  NULL;
+}
+
+
+static void
+acall_set_state( AVoiceCall    call, ACallState  state )
+{
+    if (state != call->call.state)
+    {
+        if (call->is_remote)
+        {
+            const char*  number = call->call.number;
+            int          port   = call->modem->base_port;
+
+            switch (state) {
+                case A_CALL_HELD:
+                    remote_call_other( number, port, REMOTE_CALL_HOLD );
+                    break;
+
+                case A_CALL_ACTIVE:
+                    remote_call_other( number, port, REMOTE_CALL_ACCEPT );
+                    break;
+
+                default: ;
+            }
+        }
+        call->call.state = state;
+    }
+}
+
+
+int
+amodem_update_call( AModem  modem, const char*  fromNumber, ACallState  state )
+{
+    AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, fromNumber);
+
+    if (vcall == NULL)
+        return -1;
+
+    acall_set_state( vcall, state );
+    amodem_send_calls_update(modem);
+    return 0;
+}
+
+
+int
+amodem_disconnect_call( AModem  modem, const char*  number )
+{
+    AVoiceCall  vcall = (AVoiceCall) amodem_find_call_by_number(modem, number);
+
+    if (!vcall)
+        return -1;
+
+    amodem_free_call( modem, vcall );
+    amodem_send_calls_update(modem);
+    return 0;
+}
+
+/** COMMAND HANDLERS
+ **/
+
+static const char*
+unknownCommand( const char*  cmd, AModem  modem )
+{
+    modem=modem;
+    fprintf(stderr, ">>> unknown command '%s'\n", cmd );
+    return "ERROR: unknown command\r";
+}
+
+static const char*
+handleRadioPower( const char*  cmd, AModem  modem )
+{
+    if ( !strcmp( cmd, "+CFUN=0" ) )
+    {
+        /* turn radio off */
+        modem->radio_state = A_RADIO_STATE_OFF;
+    }
+    else if ( !strcmp( cmd, "+CFUN=1" ) )
+    {
+        /* turn radio on */
+        modem->radio_state = A_RADIO_STATE_ON;
+    }
+    return NULL;
+}
+
+static const char*
+handleRadioPowerReq( const char*  cmd, AModem  modem )
+{
+    if (modem->radio_state != A_RADIO_STATE_OFF)
+        return "+CFUN=1";
+    else
+        return "+CFUN=0";
+}
+
+static const char*
+handleSIMStatusReq( const char*  cmd, AModem  modem )
+{
+    const char*  answer = NULL;
+
+    switch (asimcard_get_status(modem->sim)) {
+        case A_SIM_STATUS_ABSENT:    answer = "+CPIN: ABSENT"; break;
+        case A_SIM_STATUS_READY:     answer = "+CPIN: READY"; break;
+        case A_SIM_STATUS_NOT_READY: answer = "+CMERROR: NOT READY"; break;
+        case A_SIM_STATUS_PIN:       answer = "+CPIN: SIM PIN"; break;
+        case A_SIM_STATUS_PUK:       answer = "+CPIN: SIM PUK"; break;
+        case A_SIM_STATUS_NETWORK_PERSONALIZATION: answer = "+CPIN: PH-NET PIN"; break;
+        default:
+            answer = "ERROR: internal error";
+    }
+    return answer;
+}
+
+static const char*
+handleNetworkRegistration( const char*  cmd, AModem  modem )
+{
+    if ( !memcmp( cmd, "+CREG", 5 ) ) {
+        cmd += 5;
+        if (cmd[0] == '?') {
+            return amodem_printf( modem, "+CREG: %d,%d, \"%04x\", \"%04x\"",
+                                  modem->voice_mode, modem->voice_state,
+                                  modem->area_code, modem->cell_id );
+        } else if (cmd[0] == '=') {
+            switch (cmd[1]) {
+                case '0':
+                    modem->voice_mode  = A_REGISTRATION_UNSOL_DISABLED;
+                    break;
+
+                case '1':
+                    modem->voice_mode  = A_REGISTRATION_UNSOL_ENABLED;
+                    break;
+
+                case '2':
+                    modem->voice_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
+                    break;
+
+                case '?':
+                    return "+CREG: (0-2)";
+
+                default:
+                    return "ERROR: BAD COMMAND";
+            }
+        } else {
+            assert( 0 && "unreachable" );
+        }
+    } else if ( !memcmp( cmd, "+CGREG", 6 ) ) {
+        cmd += 6;
+        if (cmd[0] == '?') {\
+            return amodem_printf( modem, "+CGREG: %d,%d,\"%04x\",\"%04x\",\"%04x\"",
+                                  modem->data_mode, modem->data_state,
+                                  modem->area_code, modem->cell_id,
+                                  modem->data_network );
+        } else if (cmd[0] == '=') {
+            switch (cmd[1]) {
+                case '0':
+                    modem->data_mode  = A_REGISTRATION_UNSOL_DISABLED;
+                    break;
+
+                case '1':
+                    modem->data_mode  = A_REGISTRATION_UNSOL_ENABLED;
+                    break;
+
+                case '2':
+                    modem->data_mode = A_REGISTRATION_UNSOL_ENABLED_FULL;
+                    break;
+
+                case '?':
+                    return "+CGREG: (0-2)";
+
+                default:
+                    return "ERROR: BAD COMMAND";
+            }
+        } else {
+            assert( 0 && "unreachable" );
+        }
+    }
+    return NULL;
+}
+
+static const char*
+handleSetDialTone( const char*  cmd, AModem  modem )
+{
+    /* XXX: TODO */
+    return NULL;
+}
+
+static const char*
+handleDeleteSMSonSIM( const char*  cmd, AModem  modem )
+{
+    /* XXX: TODO */
+    return NULL;
+}
+
+static const char*
+handleSIM_IO( const char*  cmd, AModem  modem )
+{
+    return asimcard_io( modem->sim, cmd );
+}
+
+
+static const char*
+handleOperatorSelection( const char*  cmd, AModem  modem )
+{
+    assert( !memcmp( "+COPS", cmd, 5 ) );
+    cmd += 5;
+    if (cmd[0] == '?') { /* ask for current operator */
+        AOperator  oper = &modem->operators[ modem->oper_index ];
+
+        if ( !amodem_has_network( modem ) )
+        {
+            /* this error code means "no network" */
+            return amodem_printf( modem, "+CME ERROR: 30" );
+        }
+
+        oper = &modem->operators[ modem->oper_index ];
+
+        if ( modem->oper_name_index == 2 )
+            return amodem_printf( modem, "+COPS: %d,2,%s",
+                                  modem->oper_selection_mode,
+                                  oper->name[2] );
+
+        return amodem_printf( modem, "+COPS: %d,%d,\"%s\"",
+                              modem->oper_selection_mode,
+                              modem->oper_name_index,
+                              oper->name[ modem->oper_name_index ] );
+    }
+    else if (cmd[0] == '=' && cmd[1] == '?') {  /* ask for all available operators */
+        const char*  comma = "+COPS: ";
+        int          nn;
+        amodem_begin_line( modem );
+        for (nn = 0; nn < modem->oper_count; nn++) {
+            AOperator  oper = &modem->operators[nn];
+            amodem_add_line( modem, "%s(%d,\"%s\",\"%s\",\"%s\")", comma,
+                             oper->status, oper->name[0], oper->name[1], oper->name[2] );
+            comma = ", ";
+        }
+        return amodem_end_line( modem );
+    }
+    else if (cmd[0] == '=') {
+        switch (cmd[1]) {
+            case '0':
+                modem->oper_selection_mode = A_SELECTION_AUTOMATIC;
+                return NULL;
+
+            case '1':
+                {
+                    int  format, nn, len, found = -1;
+
+                    if (cmd[2] != ',')
+                        goto BadCommand;
+                    format = cmd[3] - '0';
+                    if ( (unsigned)format > 2 )
+                        goto BadCommand;
+                    if (cmd[4] != ',')
+                        goto BadCommand;
+                    cmd += 5;
+                    len  = strlen(cmd);
+                    if (*cmd == '"') {
+                        cmd++;
+                        len -= 2;
+                    }
+                    if (len <= 0)
+                        goto BadCommand;
+
+                    for (nn = 0; nn < modem->oper_count; nn++) {
+                        AOperator    oper = modem->operators + nn;
+                        char*        name = oper->name[ format ];
+
+                        if ( !memcpy( name, cmd, len ) && name[len] == 0 ) {
+                            found = nn;
+                            break;
+                        }
+                    }
+
+                    if (found < 0) {
+                        /* Selection failed */
+                        return "+CME ERROR: 529";
+                    } else if (modem->operators[found].status == A_STATUS_DENIED) {
+                        /* network not allowed */
+                        return "+CME ERROR: 32";
+                    }
+                    modem->oper_index = found;
+
+                    /* set the voice and data registration states to home or roaming
+                     * depending on the operator index
+                     */
+                    if (found == OPERATOR_HOME_INDEX) {
+                        modem->voice_state = A_REGISTRATION_HOME;
+                        modem->data_state  = A_REGISTRATION_HOME;
+                    } else if (found == OPERATOR_ROAMING_INDEX) {
+                        modem->voice_state = A_REGISTRATION_ROAMING;
+                        modem->data_state  = A_REGISTRATION_ROAMING;
+                    }
+                    return NULL;
+                }
+
+            case '2':
+                modem->oper_selection_mode = A_SELECTION_DEREGISTRATION;
+                return NULL;
+
+            case '3':
+                {
+                    int format;
+
+                    if (cmd[2] != ',')
+                        goto BadCommand;
+
+                    format = cmd[3] - '0';
+                    if ( (unsigned)format > 2 )
+                        goto BadCommand;
+
+                    modem->oper_name_index = format;
+                    return NULL;
+                }
+            default:
+                ;
+        }
+    }
+BadCommand:
+    return unknownCommand(cmd,modem);
+}
+
+static const char*
+handleRequestOperator( const char*  cmd, AModem  modem )
+{
+    AOperator  oper;
+    cmd=cmd;
+
+    if ( !amodem_has_network(modem) )
+        return "+CME ERROR: 30";
+
+    oper = modem->operators + modem->oper_index;
+    modem->oper_name_index = 2;
+    return amodem_printf( modem, "+COPS: 0,0,\"%s\"\r"
+                          "+COPS: 0,1,\"%s\"\r"
+                          "+COPS: 0,2,\"%s\"",
+                          oper->name[0], oper->name[1], oper->name[2] );
+}
+
+static const char*
+handleSendSMStoSIM( const char*  cmd, AModem  modem )
+{
+    /* XXX: TODO */
+    return "ERROR: unimplemented";
+}
+
+static const char*
+handleSendSMS( const char*  cmd, AModem  modem )
+{
+    modem->wait_sms = 1;
+    return "> ";
+}
+
+#if 0
+static void
+sms_address_dump( SmsAddress  address, FILE*  out )
+{
+    int  nn, len = address->len;
+
+    if (address->toa == 0x91) {
+        fprintf( out, "+" );
+    }
+    for (nn = 0; nn < len; nn += 2)
+    {
+        static const char  dialdigits[16] = "0123456789*#,N%";
+        int  c = address->data[nn/2];
+
+        fprintf( out, "%c", dialdigits[c & 0xf] );
+        if (nn+1 >= len)
+            break;
+
+        fprintf( out, "%c", dialdigits[(c >> 4) & 0xf] );
+    }
+}
+
+static void
+smspdu_dump( SmsPDU  pdu, FILE*  out )
+{
+    SmsAddressRec    address;
+    unsigned char    temp[256];
+    int              len;
+
+    if (pdu == NULL) {
+        fprintf( out, "SMS PDU is (null)\n" );
+        return;
+    }
+
+    fprintf( out, "SMS PDU type:       " );
+    switch (smspdu_get_type(pdu)) {
+        case SMS_PDU_DELIVER: fprintf(out, "DELIVER"); break;
+        case SMS_PDU_SUBMIT:  fprintf(out, "SUBMIT"); break;
+        case SMS_PDU_STATUS_REPORT: fprintf(out, "STATUS_REPORT"); break;
+        default: fprintf(out, "UNKNOWN");
+    }
+    fprintf( out, "\n        sender:   " );
+    if (smspdu_get_sender_address(pdu, &address) < 0)
+        fprintf( out, "(N/A)" );
+    else
+        sms_address_dump(&address, out);
+    fprintf( out, "\n        receiver: " );
+    if (smspdu_get_receiver_address(pdu, &address) < 0)
+        fprintf(out, "(N/A)");
+    else
+        sms_address_dump(&address, out);
+    fprintf( out, "\n        text:     " );
+    len = smspdu_get_text_message( pdu, temp, sizeof(temp)-1 );
+    if (len > sizeof(temp)-1 )
+        len = sizeof(temp)-1;
+    fprintf( out, "'%.*s'\n", len, temp );
+}
+#endif
+
+static const char*
+handleSendSMSText( const char*  cmd, AModem  modem )
+{
+#if 1
+    SmsAddressRec  address;
+    char           number[16];
+    int            numlen;
+    int            len = strlen(cmd);
+    SmsPDU         pdu;
+
+    /* get rid of trailing escape */
+    if (len > 0 && cmd[len-1] == 0x1a)
+        len -= 1;
+
+    pdu = smspdu_create_from_hex( cmd, len );
+    if (pdu == NULL) {
+        D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
+        return "+CMS ERROR: INVALID SMS PDU";
+    }
+    if (smspdu_get_receiver_address(pdu, &address) < 0) {
+        D("%s: could not get SMS receiver address from '%s'\n",
+          __FUNCTION__, cmd);
+        return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
+    }
+
+    do {
+        int  index;
+
+        numlen = sms_address_to_str( &address, number, sizeof(number) );
+        if (numlen > sizeof(number)-1)
+            break;
+
+        number[numlen] = 0;
+        if ( remote_number_string_to_port( number ) < 0 )
+            break;
+
+        if (modem->sms_receiver == NULL) {
+            modem->sms_receiver = sms_receiver_create();
+            if (modem->sms_receiver == NULL) {
+                D( "%s: could not create SMS receiver\n", __FUNCTION__ );
+                break;
+            }
+        }
+
+        index = sms_receiver_add_submit_pdu( modem->sms_receiver, pdu );
+        if (index < 0) {
+            D( "%s: could not add submit PDU\n", __FUNCTION__ );
+            break;
+        }
+        /* the PDU is now owned by the receiver */
+        pdu = NULL;
+
+        if (index > 0) {
+            SmsAddressRec  from[1];
+            char           temp[10];
+            SmsPDU*        deliver;
+            int            nn;
+
+            sprintf( temp, "%d", modem->base_port );
+            sms_address_from_str( from, temp, strlen(temp) );
+
+            deliver = sms_receiver_create_deliver( modem->sms_receiver, index, from );
+            if (deliver == NULL) {
+                D( "%s: could not create deliver PDUs for SMS index %d\n",
+                   __FUNCTION__, index );
+                break;
+            }
+
+            for (nn = 0; deliver[nn] != NULL; nn++) {
+                if ( remote_call_sms( number, modem->base_port, deliver[nn] ) < 0 ) {
+                    D( "%s: could not send SMS PDU to remote emulator\n",
+                       __FUNCTION__ );
+                    break;
+                }
+            }
+
+            smspdu_free_list(deliver);
+        }
+
+    } while (0);
+
+    if (pdu != NULL)
+        smspdu_free(pdu);
+
+#elif 1
+    SmsAddressRec  address;
+    char           number[16];
+    int            numlen;
+    int            len = strlen(cmd);
+    SmsPDU         pdu;
+
+    /* get rid of trailing escape */
+    if (len > 0 && cmd[len-1] == 0x1a)
+        len -= 1;
+
+    pdu = smspdu_create_from_hex( cmd, len );
+    if (pdu == NULL) {
+        D("%s: invalid SMS PDU ?: '%s'\n", __FUNCTION__, cmd);
+        return "+CMS ERROR: INVALID SMS PDU";
+    }
+    if (smspdu_get_receiver_address(pdu, &address) < 0) {
+        D("%s: could not get SMS receiver address from '%s'\n",
+          __FUNCTION__, cmd);
+        return "+CMS ERROR: BAD SMS RECEIVER ADDRESS";
+    }
+    do {
+        numlen = sms_address_to_str( &address, number, sizeof(number) );
+        if (numlen > sizeof(number)-1)
+            break;
+
+        number[numlen] = 0;
+        if ( remote_number_string_to_port( number ) < 0 )
+            break;
+
+        if ( remote_call_sms( number, modem->base_port, pdu ) < 0 )
+        {
+            D("%s: could not send SMS PDU to remote emulator\n",
+              __FUNCTION__);
+            return "+CMS ERROR: NO EMULATOR RECEIVER";
+        }
+    } while (0);
+#else
+    fprintf(stderr, "SMS<< %s\n", cmd);
+    SmsPDU  pdu = smspdu_create_from_hex( cmd, strlen(cmd) );
+    if (pdu == NULL) {
+        fprintf(stderr, "invalid SMS PDU ?: '%s'\n", cmd);
+    } else {
+        smspdu_dump(pdu, stderr);
+    }
+#endif
+    return "+CMGS: 0\rOK\r";
+}
+
+static const char*
+handleChangeOrEnterPIN( const char*  cmd, AModem  modem )
+{
+    assert( !memcmp( cmd, "+CPIN=", 6 ) );
+    cmd += 6;
+
+    switch (asimcard_get_status(modem->sim)) {
+        case A_SIM_STATUS_ABSENT:
+            return "+CME ERROR: SIM ABSENT";
+
+        case A_SIM_STATUS_NOT_READY:
+            return "+CME ERROR: SIM NOT READY";
+
+        case A_SIM_STATUS_READY:
+            /* this may be a request to change the PIN */
+            {
+                if (strlen(cmd) == 9 && cmd[4] == ',') {
+                    char  pin[5];
+                    memcpy( pin, cmd, 4 ); pin[4] = 0;
+
+                    if ( !asimcard_check_pin( modem->sim, pin ) )
+                        return "+CME ERROR: BAD PIN";
+
+                    memcpy( pin, cmd+5, 4 );
+                    asimcard_set_pin( modem->sim, pin );
+                    return "+CPIN: READY";
+                }
+            }
+            break;
+
+        case A_SIM_STATUS_PIN:   /* waiting for PIN */
+            if ( asimcard_check_pin( modem->sim, cmd ) )
+                return "+CPIN: READY";
+            else
+                return "+CME ERROR: BAD PIN";
+
+        case A_SIM_STATUS_PUK:
+            if (strlen(cmd) == 9 && cmd[4] == ',') {
+                char  puk[5];
+                memcpy( puk, cmd, 4 );
+                puk[4] = 0;
+                if ( asimcard_check_puk( modem->sim, puk, cmd+5 ) )
+                    return "+CPIN: READY";
+                else
+                    return "+CME ERROR: BAD PUK";
+            }
+            return "+CME ERROR: BAD PUK";
+
+        default:
+            return "+CPIN: PH-NET PIN";
+    }
+
+    return "+CME ERROR: BAD FORMAT";
+}
+
+
+static const char*
+handleListCurrentCalls( const char*  cmd, AModem  modem )
+{
+    int  nn;
+    amodem_begin_line( modem );
+    for (nn = 0; nn < modem->call_count; nn++) {
+        AVoiceCall  vcall = modem->calls + nn;
+        ACall       call  = &vcall->call;
+        if (call->mode == A_CALL_VOICE)
+            amodem_add_line( modem, "+CLCC: %d,%d,%d,%d,%d,\"%s\",%d\r\n",
+                             call->id, call->dir, call->state, call->mode,
+                             call->multi, call->number, 129 );
+    }
+    return amodem_end_line( modem );
+}
+
+/* retrieve the current time and zone in a format suitable
+ * for %CTZV: unsolicited message
+ *  "yy/mm/dd,hh:mm:ss(+/-)tz"
+ *   mm is 0-based
+ *   tz is in number of quarter-hours
+ *
+ * it seems reference-ril doesn't parse the comma (,) as anything else than a token
+ * separator, so use a column (:) instead, the Java parsing code won't see a difference
+ *
+ */
+static const char*
+handleEndOfInit( const char*  cmd, AModem  modem )
+{
+    time_t       now = time(NULL);
+    struct tm    utc, local;
+    long         e_local, e_utc;
+    long         tzdiff;
+    char         tzname[64];
+
+    tzset();
+
+    utc   = *gmtime( &now );
+    local = *localtime( &now );
+
+    e_local = local.tm_min + 60*(local.tm_hour + 24*local.tm_yday);
+    e_utc   = utc.tm_min   + 60*(utc.tm_hour   + 24*utc.tm_yday);
+
+    if ( utc.tm_year < local.tm_year )
+        e_local += 24*60;
+    else if ( utc.tm_year > local.tm_year )
+        e_utc += 24*60;
+
+    tzdiff = e_local - e_utc;  /* timezone offset in minutes */
+
+   /* retrieve a zoneinfo-compatible name for the host timezone
+    */
+    {
+        char*  end = tzname + sizeof(tzname);
+        char*  p = bufprint_zoneinfo_timezone( tzname, end );
+        if (p >= end)
+            strcpy(tzname, "Unknown/Unknown");
+
+        /* now replace every / in the timezone name by a "!"
+         * that's because the code that reads the CTZV line is
+         * dumb and treats a / as a field separator...
+         */
+        p = tzname;
+        while (1) {
+            p = strchr(p, '/');
+            if (p == NULL)
+                break;
+            *p = '!';
+            p += 1;
+        }
+    }
+
+   /* as a special extension, we append the name of the host's time zone to the
+    * string returned with %CTZ. the system should contain special code to detect
+    * and deal with this case (since it normally relied on the operator's country code
+    * which is hard to simulate on a general-purpose computer
+    */
+    return amodem_printf( modem, "%%CTZV: %02d/%02d/%02d:%02d:%02d:%02d%c%d:%d:%s",
+             (utc.tm_year + 1900) % 100, utc.tm_mon + 1, utc.tm_mday, utc.tm_hour, utc.tm_min, utc.tm_sec,
+             (tzdiff >= 0) ? '+' : '-', (tzdiff >= 0 ? tzdiff : -tzdiff) / 15,
+             (local.tm_isdst > 0),
+             tzname );
+}
+
+
+static const char*
+handleListPDPContexts( const char*  cmd, AModem  modem )
+{
+    int  nn;
+    assert( !memcmp( cmd, "+CGACT?", 7 ) );
+    amodem_begin_line( modem );
+    for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
+        ADataContext  data = modem->data_contexts + nn;
+        if (!data->active)
+            continue;
+        amodem_add_line( modem, "+CGACT: %d,%d\r", data->id, data->active );
+    }
+    return amodem_end_line( modem );
+}
+
+static const char*
+handleDefinePDPContext( const char*  cmd, AModem  modem )
+{
+    assert( !memcmp( cmd, "+CGDCONT=", 9 ) );
+    cmd += 9;
+    if (cmd[0] == '?') {
+        int  nn;
+        amodem_begin_line(modem);
+        for (nn = 0; nn < MAX_DATA_CONTEXTS; nn++) {
+            ADataContext  data = modem->data_contexts + nn;
+            if (!data->active)
+                continue;
+            amodem_add_line( modem, "+CGDCONT: %d,%s,\"%s\",,0,0\r\n",
+                             data->id,
+                             data->type == A_DATA_IP ? "IP" : "PPP",
+                             data->apn );
+        }
+        return amodem_end_line(modem);
+    } else {
+        /* template is +CGDCONT=<id>,"<type>","<apn>",,0,0 */
+        int              id = cmd[0] - '1';
+        ADataType        type;
+        char             apn[32];
+        ADataContext     data;
+
+        if ((unsigned)id > 3)
+            goto BadCommand;
+
+        if ( !memcmp( cmd+1, ",\"IP\",\"", 7 ) ) {
+            type = A_DATA_IP;
+            cmd += 8;
+        } else if ( !memcmp( cmd+1, ",\"PPP\",\"", 8 ) ) {
+            type = A_DATA_PPP;
+            cmd += 9;
+        } else
+            goto BadCommand;
+
+        {
+            const char*  p = strchr( cmd, '"' );
+            int          len;
+            if (p == NULL)
+                goto BadCommand;
+            len = (int)( p - cmd );
+            if (len > sizeof(apn)-1 )
+                len = sizeof(apn)-1;
+            memcpy( apn, cmd, len );
+            apn[len] = 0;
+        }
+
+        data = modem->data_contexts + id;
+
+        data->id     = id + 1;
+        data->active = 1;
+        data->type   = type;
+        memcpy( data->apn, apn, sizeof(data->apn) );
+    }
+    return NULL;
+BadCommand:
+    return "ERROR: BAD COMMAND";
+}
+
+
+static const char*
+handleStartPDPContext( const char*  cmd, AModem  modem )
+{
+    /* XXX: TODO: handle PDP start appropriately */
+    /* for the moment, always return success */
+#if 0
+    AVoiceCall  vcall = amodem_alloc_call( modem );
+    ACall       call  = (ACall) vcall;
+    if (call == NULL) {
+        return "ERROR: TOO MANY CALLS";
+    }
+    call->id    = 1;
+    call->dir   = A_CALL_OUTBOUND;
+    /* XXX: it would be better to delay this */
+    call->state = A_CALL_ACTIVE;
+    call->mode  = A_CALL_DATA;
+    call->multi = 0;
+    strcpy( call->number, "012345" );
+#endif
+    return NULL;
+}
+
+
+static void
+remote_voice_call_event( void*  _vcall, int  success )
+{
+    AVoiceCall  vcall = _vcall;
+    AModem      modem = vcall->modem;
+
+    /* NOTE: success only means we could send the "gsm in new" command
+     * to the remote emulator, nothing more */
+
+    if (!success) {
+        /* aargh, the remote emulator probably quitted at that point */
+        amodem_free_call(modem, vcall);
+        amodem_send_calls_update(modem);
+    }
+}
+
+
+static void
+voice_call_event( void*  _vcall )
+{
+    AVoiceCall  vcall = _vcall;
+    ACall       call  = &vcall->call;
+
+    switch (call->state) {
+        case A_CALL_DIALING:
+            call->state = A_CALL_ALERTING;
+
+            if (vcall->is_remote) {
+                if ( remote_call_dial( call->number,
+                                       vcall->modem->base_port,
+                                       remote_voice_call_event, vcall ) < 0 )
+                {
+                   /* we could not connect, probably because the corresponding
+                    * emulator is not running, so simply destroy this call.
+                    * XXX: should we send some sort of message to indicate BAD NUMBER ? */
+                    /* it seems the Android code simply waits for changes in the list   */
+                    amodem_free_call( vcall->modem, vcall );
+                }
+            } else {
+               /* this is not a remote emulator number, so just simulate
+                * a small ringing delay */
+                sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_ALERT,
+                               voice_call_event, vcall );
+            }
+            break;
+
+        case A_CALL_ALERTING:
+            call->state = A_CALL_ACTIVE;
+            break;
+
+        default:
+            assert( 0 && "unreachable event call state" );
+    }
+    amodem_send_calls_update(vcall->modem);
+}
+
+
+static const char*
+handleDial( const char*  cmd, AModem  modem )
+{
+    AVoiceCall  vcall = amodem_alloc_call( modem );
+    ACall       call  = &vcall->call;
+    int         len;
+
+    if (call == NULL)
+        return "ERROR: TOO MANY CALLS";
+
+    assert( cmd[0] == 'D' );
+    call->dir   = A_CALL_OUTBOUND;
+    call->state = A_CALL_DIALING;
+    call->mode  = A_CALL_VOICE;
+    call->multi = 0;
+
+    cmd += 1;
+    len  = strlen(cmd);
+    if (len > 0 && cmd[len-1] == ';')
+        len--;
+    if (len >= sizeof(call->number))
+        len = sizeof(call->number)-1;
+
+    memcpy( call->number, cmd, len );
+    call->number[len] = 0;
+
+    vcall->is_remote = (remote_number_string_to_port(call->number) > 0);
+
+    vcall->timer = sys_timer_create();
+    sys_timer_set( vcall->timer, sys_time_ms() + CALL_DELAY_DIAL,
+                   voice_call_event, vcall );
+
+    return NULL;
+}
+
+
+static const char*
+handleAnswer( const char*  cmd, AModem  modem )
+{
+    int  nn;
+    for (nn = 0; nn < modem->call_count; nn++) {
+        AVoiceCall  vcall = modem->calls + nn;
+        ACall       call  = &vcall->call;
+
+        if (cmd[0] == 'A') {
+            if (call->state == A_CALL_INCOMING) {
+                acall_set_state( vcall, A_CALL_ACTIVE );
+            }
+            else if (call->state == A_CALL_ACTIVE) {
+                acall_set_state( vcall, A_CALL_HELD );
+            }
+        } else if (cmd[0] == 'H') {
+            /* ATH: hangup, since user is busy */
+            if (call->state == A_CALL_INCOMING) {
+                amodem_free_call( modem, vcall );
+                break;
+            }
+        }
+    }
+    return NULL;
+}
+
+static const char*
+handleHangup( const char*  cmd, AModem  modem )
+{
+    if ( !memcmp(cmd, "+CHLD=", 6) ) {
+        int  nn;
+        cmd += 6;
+        switch (cmd[0]) {
+            case '0':  /* release all held, and set busy for waiting calls */
+                for (nn = 0; nn < modem->call_count; nn++) {
+                    AVoiceCall  vcall = modem->calls + nn;
+                    ACall       call  = &vcall->call;
+                    if (call->mode != A_CALL_VOICE)
+                        continue;
+                    if (call->state == A_CALL_HELD    ||
+                        call->state == A_CALL_WAITING ||
+                        call->state == A_CALL_INCOMING) {
+                        amodem_free_call(modem, vcall);
+                        nn--;
+                    }
+                }
+                break;
+
+            case '1':
+                if (cmd[1] == 0) { /* release all active, accept held one */
+                    for (nn = 0; nn < modem->call_count; nn++) {
+                        AVoiceCall  vcall = modem->calls + nn;
+                        ACall       call  = &vcall->call;
+                        if (call->mode != A_CALL_VOICE)
+                            continue;
+                        if (call->state == A_CALL_ACTIVE) {
+                            amodem_free_call(modem, vcall);
+                            nn--;
+                        }
+                        else if (call->state == A_CALL_HELD     ||
+                                 call->state == A_CALL_WAITING) {
+                            acall_set_state( vcall, A_CALL_ACTIVE );
+                        }
+                    }
+                } else {  /* release specific call */
+                    int  id = cmd[1] - '0';
+                    AVoiceCall  vcall = amodem_find_call( modem, id );
+                    if (vcall != NULL)
+                        amodem_free_call( modem, vcall );
+                }
+                break;
+
+            case '2':
+                if (cmd[1] == 0) {  /* place all active on hold, accept held or waiting one */
+                    for (nn = 0; nn < modem->call_count; nn++) {
+                        AVoiceCall  vcall = modem->calls + nn;
+                        ACall       call  = &vcall->call;
+                        if (call->mode != A_CALL_VOICE)
+                            continue;
+                        if (call->state == A_CALL_ACTIVE) {
+                            acall_set_state( vcall, A_CALL_HELD );
+                        }
+                        else if (call->state == A_CALL_HELD     ||
+                                 call->state == A_CALL_WAITING) {
+                            acall_set_state( vcall, A_CALL_ACTIVE );
+                        }
+                    }
+                } else {  /* place all active on hold, except a specific one */
+                    int   id = cmd[1] - '0';
+                    for (nn = 0; nn < modem->call_count; nn++) {
+                        AVoiceCall  vcall = modem->calls + nn;
+                        ACall       call  = &vcall->call;
+                        if (call->mode != A_CALL_VOICE)
+                            continue;
+                        if (call->state == A_CALL_ACTIVE && call->id != id) {
+                            acall_set_state( vcall, A_CALL_HELD );
+                        }
+                    }
+                }
+                break;
+
+            case '3':  /* add a held call to the conversation */
+                for (nn = 0; nn < modem->call_count; nn++) {
+                    AVoiceCall  vcall = modem->calls + nn;
+                    ACall       call  = &vcall->call;
+                    if (call->mode != A_CALL_VOICE)
+                        continue;
+                    if (call->state == A_CALL_HELD) {
+                        acall_set_state( vcall, A_CALL_ACTIVE );
+                        break;
+                    }
+                }
+                break;
+
+            case '4':  /* connect the two calls */
+                for (nn = 0; nn < modem->call_count; nn++) {
+                    AVoiceCall  vcall = modem->calls + nn;
+                    ACall       call  = &vcall->call;
+                    if (call->mode != A_CALL_VOICE)
+                        continue;
+                    if (call->state == A_CALL_HELD) {
+                        acall_set_state( vcall, A_CALL_ACTIVE );
+                        break;
+                    }
+                }
+                break;
+        }
+    }
+    else
+        return "ERROR: BAD COMMAND";
+
+    return NULL;
+}
+
+
+/* a function used to deal with a non-trivial request */
+typedef const char*  (*ResponseHandler)(const char*  cmd, AModem  modem);
+
+static const struct {
+    const char*      cmd;     /* command coming from libreference-ril.so, if first
+                                 character is '!', then the rest is a prefix only */
+
+    const char*      answer;  /* default answer, NULL if needs specific handling or
+                                 if OK is good enough */
+
+    ResponseHandler  handler; /* specific handler, ignored if 'answer' is not NULL,
+                                 NULL if OK is good enough */
+} sDefaultResponses[] =
+{
+    /* see onRadioPowerOn() */
+    { "%CPHS=1", NULL, NULL },
+    { "%CTZV=1", NULL, NULL },
+
+    /* see onSIMReady() */
+    { "+CSMS=1", "+CSMS: 1, 1, 1", NULL },
+    { "+CNMI=1,2,2,1,1", NULL, NULL },
+
+    /* see requestRadioPower() */
+    { "+CFUN=0", NULL, handleRadioPower },
+    { "+CFUN=1", NULL, handleRadioPower },
+
+    /* see requestOrSendPDPContextList() */
+    { "+CGACT?", "", handleListPDPContexts },
+
+    /* see requestOperator() */
+    { "+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?", NULL, handleRequestOperator },
+
+    /* see requestQueryNetworkSelectionMode() */
+    { "!+COPS", NULL, handleOperatorSelection },
+
+    /* see requestGetCurrentCalls() */
+    { "+CLCC", NULL, handleListCurrentCalls },
+
+    /* see requestWriteSmsToSim() */
+    { "!+CMGW=", NULL, handleSendSMStoSIM },
+
+    /* see requestHangup() */
+    { "!+CHLD=", NULL, handleHangup },
+
+    /* see requestSignalStrength() */
+    { "+CSQ", "+CSQ: 7,99", NULL },  /* XXX: TODO: implement variable signal strength and error rates */
+
+    /* see requestRegistrationState() */
+    { "!+CREG", NULL, handleNetworkRegistration },
+    { "!+CGREG", NULL, handleNetworkRegistration },
+
+    /* see requestSendSMS() */
+    { "!+CMGS=", NULL, handleSendSMS },
+
+    /* see requestSetupDefaultPDP() */
+    { "%CPRIM=\"GMM\",\"CONFIG MULTISLOT_CLASS=<10>\"", NULL, NULL },
+    { "%DATA=2,\"UART\",1,,\"SER\",\"UART\",0", NULL, NULL },
+
+    { "!+CGDCONT=", NULL, handleDefinePDPContext },
+
+    { "+CGQREQ=1", NULL, NULL },
+    { "+CGQMIN=1", NULL, NULL },
+    { "+CGEREP=1,0", NULL, NULL },
+    { "+CGACT=1,0", NULL, NULL },
+    { "D*99***1#", NULL, handleStartPDPContext },
+
+    /* see requestDial() */
+    { "!D", NULL, handleDial },  /* the code says that success/error is ignored, the call state will
+                              be polled through +CLCC instead */
+
+    /* see requestSMSAcknowledge() */
+    { "+CNMA=1", NULL, NULL },
+    { "+CNMA=2", NULL, NULL },
+
+    /* see requestSIM_IO() */
+    { "!+CRSM=", NULL, handleSIM_IO },
+
+    /* see onRequest() */
+    { "+CHLD=0", NULL, handleHangup },
+    { "+CHLD=1", NULL, handleHangup },
+    { "+CHLD=2", NULL, handleHangup },
+    { "+CHLD=3", NULL, handleHangup },
+    { "A", NULL, handleAnswer },  /* answer the call */
+    { "H", NULL, handleAnswer },  /* user is busy */
+    { "!+VTS=", NULL, handleSetDialTone },
+    { "+CIMI", OPERATOR_HOME_MCCMNC "000000000", NULL },   /* request internation subscriber identification number */
+    { "+CGSN", "000000000000000", NULL },   /* request model version */
+    { "+CUSD=2",NULL, NULL }, /* Cancel USSD */
+    { "+COPS=0", NULL, handleOperatorSelection }, /* set network selection to automatic */
+    { "!+CMGD=", NULL, handleDeleteSMSonSIM }, /* delete SMS on SIM */
+    { "!+CPIN=", NULL, handleChangeOrEnterPIN },
+
+    /* see getSIMStatus() */
+    { "+CPIN?", NULL, handleSIMStatusReq },
+    { "+CNMI?", "+CNMI: 1,2,2,1,1", NULL },
+
+    /* see isRadioOn() */
+    { "+CFUN?", NULL, handleRadioPowerReq },
+
+    /* see initializeCallback() */
+    { "E0Q0V1", NULL, NULL },
+    { "S0=0", NULL, NULL },
+    { "+CMEE=1", NULL, NULL },
+    { "+CREG=2", NULL, handleNetworkRegistration },
+    { "+CREG=1", NULL, handleNetworkRegistration },
+    { "+CGREG=1", NULL, handleNetworkRegistration },
+    { "+CCWA=1", NULL, NULL },
+    { "+CMOD=0", NULL, NULL },
+    { "+CMUT=0", NULL, NULL },
+    { "+CSSN=0,1", NULL, NULL },
+    { "+COLP=0", NULL, NULL },
+    { "+CSCS=\"HEX\"", NULL, NULL },
+    { "+CUSD=1", NULL, NULL },
+    { "+CGEREP=1,0", NULL, NULL },
+    { "+CMGF=0", NULL, handleEndOfInit },  /* now is a goof time to send the current tme and timezone */
+    { "%CPI=3", NULL, NULL },
+    { "%CSTAT=1", NULL, NULL },
+
+    /* end of list */
+    {NULL, NULL, NULL}
+};
+
+
+#define  REPLY(str)  do { const char*  s = (str); R(">> %s\n", quote(s)); return s; } while (0)
+
+const char*  amodem_send( AModem  modem, const char*  cmd )
+{
+    const char*  answer;
+
+    if ( modem->wait_sms != 0 ) {
+        modem->wait_sms = 0;
+        R( "SMS<< %s\n", quote(cmd) );
+        answer = handleSendSMSText( cmd, modem );
+        REPLY(answer);
+    }
+
+    /* everything that doesn't start with 'AT' is not a command, right ? */
+    if ( cmd[0] != 'A' || cmd[1] != 'T' || cmd[2] == 0 ) {
+        /* R( "-- %s\n", quote(cmd) ); */
+        return NULL;
+    }
+    R( "<< %s\n", quote(cmd) );
+
+    cmd += 2;
+
+    /* TODO: implement command handling */
+    {
+        int  nn, found = 0;
+
+        for (nn = 0; ; nn++) {
+            const char*  scmd = sDefaultResponses[nn].cmd;
+
+            if (!scmd) /* end of list */
+                break;
+
+            if (scmd[0] == '!') { /* prefix match */
+                int  len = strlen(++scmd);
+
+                if ( !memcmp( scmd, cmd, len ) ) {
+                    found = 1;
+                    break;
+                }
+            } else { /* full match */
+                if ( !strcmp( scmd, cmd ) ) {
+                    found = 1;
+                    break;
+                }
+            }
+        }
+
+        if ( !found )
+        {
+            D( "** UNSUPPORTED COMMAND **\n" );
+            REPLY( "ERROR: UNSUPPORTED" );
+        }
+        else
+        {
+            const char*      answer  = sDefaultResponses[nn].answer;
+            ResponseHandler  handler = sDefaultResponses[nn].handler;
+
+            if ( answer != NULL ) {
+                REPLY( amodem_printf( modem, "%s\rOK", answer ) );
+            }
+
+            if (handler == NULL) {
+                REPLY( "OK" );
+            }
+
+            answer = handler( cmd, modem );
+            if (answer == NULL)
+                REPLY( "OK" );
+
+            if ( !memcmp( answer, "> ", 2 )     ||
+                 !memcmp( answer, "ERROR", 5 )  ||
+                 !memcmp( answer, "+CME ERROR", 6 ) )
+            {
+                REPLY( answer );
+            }
+
+            if (answer != modem->out_buff)
+                REPLY( amodem_printf( modem, "%s\rOK", answer ) );
+
+            strcat( modem->out_buff, "\rOK" );
+            REPLY( answer );
+        }
+    }
+}
diff --git a/telephony/android_modem.h b/telephony/android_modem.h
new file mode 100644
index 0000000..80d22c5
--- /dev/null
+++ b/telephony/android_modem.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_modem_h_
+#define _android_modem_h_
+
+#include "sim_card.h"
+#include "sms.h"
+
+/** MODEM OBJECT
+ **/
+typedef struct AModemRec_*    AModem;
+
+/* a function used by the modem to send unsolicited messages to the channel controller */
+typedef void (*AModemUnsolFunc)( void*  opaque, const char*  message );
+
+extern AModem      amodem_create( int  base_port, AModemUnsolFunc  unsol_func, void*  unsol_opaque );
+extern void        amodem_destroy( AModem  modem );
+
+/* send a command to the modem */
+extern const char*  amodem_send( AModem  modem, const char*  cmd );
+
+/* simulate the receipt on an incoming SMS message */
+extern void         amodem_receive_sms( AModem  modem, SmsPDU  pdu );
+
+/** RADIO STATE
+ **/
+typedef enum {
+    A_RADIO_STATE_OFF = 0,          /* Radio explictly powered off (eg CFUN=0) */
+    A_RADIO_STATE_ON,               /* Radio on */
+} ARadioState;
+
+extern ARadioState  amodem_get_radio_state( AModem modem );
+extern void         amodem_set_radio_state( AModem modem, ARadioState  state );
+
+/** SIM CARD STATUS
+ **/
+extern ASimCard    amodem_get_sim( AModem  modem );
+
+/** VOICE AND DATA NETWORK REGISTRATION
+ **/
+
+/* 'stat' for +CREG/+CGREG commands */
+typedef enum {
+    A_REGISTRATION_UNREGISTERED = 0,
+    A_REGISTRATION_HOME = 1,
+    A_REGISTRATION_SEARCHING,
+    A_REGISTRATION_DENIED,
+    A_REGISTRATION_UNKNOWN,
+    A_REGISTRATION_ROAMING
+} ARegistrationState;
+
+typedef enum {
+    A_GPRS_NETWORK_UNKNOWN = 0,
+    A_GPRS_NETWORK_GPRS,
+    A_GPRS_NETWORK_EDGE,
+    A_GPRS_NETWORK_UMTS
+} AGprsNetworkType;
+
+extern ARegistrationState  amodem_get_voice_registration( AModem  modem );
+extern void                amodem_set_voice_registration( AModem  modem, ARegistrationState    state );
+
+extern ARegistrationState  amodem_get_data_registration( AModem  modem );
+extern void                amodem_set_data_registration( AModem  modem, ARegistrationState    state );
+extern void                amodem_set_data_network_type( AModem  modem, AGprsNetworkType   type );
+
+extern AGprsNetworkType    android_parse_network_type( const char*  speed );
+
+
+/** OPERATOR NAMES
+ **/
+typedef enum {
+    A_NAME_LONG = 0,
+    A_NAME_SHORT,
+    A_NAME_NUMERIC,
+    A_NAME_MAX  /* don't remove */
+} ANameIndex;
+
+/* retrieve operator name into user-provided buffer. returns number of writes written, including terminating zero */
+extern int   amodem_get_operator_name ( AModem  modem, ANameIndex  index, char*  buffer, int  buffer_size );
+
+/* reset one operator name from a user-provided buffer, set buffer_size to -1 for zero-terminated strings */
+extern void  amodem_set_operator_name( AModem  modem, ANameIndex  index, const char*  buffer, int  buffer_size );
+
+/** CALL STATES
+ **/
+
+typedef enum {
+    A_CALL_OUTBOUND = 0,
+    A_CALL_INBOUND  = 1,
+} ACallDir;
+
+typedef enum {
+    A_CALL_ACTIVE = 0,
+    A_CALL_HELD,
+    A_CALL_DIALING,
+    A_CALL_ALERTING,
+    A_CALL_INCOMING,
+    A_CALL_WAITING
+} ACallState;
+
+typedef enum {
+    A_CALL_VOICE = 0,
+    A_CALL_DATA,
+    A_CALL_FAX,
+    A_CALL_UNKNOWN = 9
+} ACallMode;
+
+#define  A_CALL_NUMBER_MAX_SIZE  16
+
+typedef struct {
+    int         id;
+    ACallDir    dir;
+    ACallState  state;
+    ACallMode   mode;
+    int         multi;
+    char        number[ A_CALL_NUMBER_MAX_SIZE+1 ];
+} ACallRec, *ACall;
+
+extern int    amodem_get_call_count( AModem  modem );
+extern ACall  amodem_get_call( AModem  modem,  int  index );
+extern ACall  amodem_find_call_by_number( AModem  modem, const char*  number );
+extern int    amodem_add_inbound_call( AModem  modem, const char*  number );
+extern int    amodem_update_call( AModem  modem, const char*  number, ACallState  state );
+extern int    amodem_disconnect_call( AModem  modem, const char*  number );
+
+/**/
+
+#endif /* _android_modem_h_ */
diff --git a/telephony/gsm.c b/telephony/gsm.c
new file mode 100644
index 0000000..b55578d
--- /dev/null
+++ b/telephony/gsm.c
@@ -0,0 +1,1220 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "gsm.h"
+#include <stdlib.h>
+#include <string.h>
+
+/** UTILITIES
+ **/
+byte_t
+gsm_int_to_bcdi( int  value )
+{
+    return (byte_t)((value / 10) | ((value % 10) << 4));
+}
+
+int
+gsm_int_from_bcdi( byte_t  val )
+{
+    int  ret = 0;
+
+    if ((val & 0xf0) <= 0x90)
+        ret = (val >> 4);
+
+    if ((val & 0x0f) <= 0x90)
+        ret |= (val % 0xf)*10;
+
+    return ret;
+}
+
+#if 0
+static int
+gsm_bcdi_to_ascii( cbytes_t  bcd, int  bcdlen, bytes_t  dst )
+{
+    static byte_t  bcdichars[14] = "0123456789*#,N";
+
+    int  result = 0;
+    int  shift  = 0;
+
+    while (bcdlen > 0) {
+        int  c = (bcd[0] >> shift) & 0xf;
+
+        if (c == 0xf && bcdlen == 1)
+            break;
+
+        if (c < 14) {
+            if (dst) dst[result] = bcdichars[c];
+            result += 1;
+        }
+        bcdlen --;
+        shift += 4;
+        if (shift == 8) {
+            bcd++;
+            shift = 0;
+        }
+    }
+    return result;
+}
+#endif
+
+#if 0
+static int
+gsm_bcdi_from_ascii( cbytes_t  ascii, int  asciilen, bytes_t  dst )
+{
+    cbytes_t  end    = ascii + asciilen;
+    int       result = 0;
+    int       phase  = 0x01;
+
+    while (ascii < end) {
+        int  c = *ascii++;
+
+        if (c == '*')
+            c = 11;
+        else if (c == '#')
+            c = 12;
+        else if (c == ',')
+            c = 13;
+        else if (c == 'N')
+            c = 14;
+        else {
+            c -= '0';
+            if ((unsigned)c >= 10)
+                break;
+        }
+        phase = (phase << 4) | c;
+        if (phase & 0x100) {
+            if (dst) dst[result] = (byte_t) phase;
+            result += 1;
+            phase   = 0x01;
+        }
+    }
+    if (phase != 0x01) {
+        if (dst) dst[result] = (byte_t)( phase | 0xf0 );
+        result += 1;
+    }
+    return  result;
+}
+#endif
+
+int
+gsm_hexchar_to_int( char  c )
+{
+    if ((unsigned)(c - '0') < 10)
+        return c - '0';
+    if ((unsigned)(c - 'a') < 6)
+        return 10 + (c - 'a');
+    if ((unsigned)(c - 'A') < 6)
+        return 10 + (c - 'A');
+    return -1;
+}
+
+int
+gsm_hexchar_to_int0( char  c )
+{
+    int  ret = gsm_hexchar_to_int(c);
+
+    return (ret < 0) ? 0 : ret;
+}
+
+int
+gsm_hex2_to_byte( const char*  hex )
+{
+    int  hi = gsm_hexchar_to_int(hex[0]);
+    int  lo = gsm_hexchar_to_int(hex[1]);
+
+    if (hi < 0 || lo < 0)
+        return -1;
+
+    return ( (hi << 4) | lo );
+}
+
+int
+gsm_hex4_to_short( const char*  hex )
+{
+    int  hi = gsm_hex2_to_byte(hex);
+    int  lo = gsm_hex2_to_byte(hex+2);
+
+    if (hi < 0 || lo < 0)
+        return -1;
+
+    return ((hi << 8) | lo);
+}
+
+int
+gsm_hex2_to_byte0( const char*  hex )
+{
+    int  hi = gsm_hexchar_to_int0(hex[0]);
+    int  lo = gsm_hexchar_to_int0(hex[1]);
+
+    return (byte_t)( (hi << 4) | lo );
+}
+
+void
+gsm_hex_from_byte( char*  hex, int val )
+{
+    static const char  hexdigits[] = "0123456789abcdef";
+
+    hex[0] = hexdigits[(val >> 4) & 15];
+    hex[1] = hexdigits[val & 15];
+}
+
+void
+gsm_hex_from_short( char*  hex, int  val )
+{
+    gsm_hex_from_byte( hex,   (val >> 8) );
+    gsm_hex_from_byte( hex+2, val );
+}
+
+
+
+/** HEX
+ **/
+void
+gsm_hex_to_bytes0( cbytes_t  hex, int  hexlen, bytes_t  dst )
+{
+    int  nn;
+
+    for (nn = 0; nn < hexlen/2; nn++ ) {
+        dst[nn] = (byte_t) gsm_hex2_to_byte0( (const char*)hex+2*nn );
+    }
+    if (hexlen & 1) {
+        dst[nn] = gsm_hexchar_to_int0( hex[2*nn] ) << 4;
+    }
+}
+
+int
+gsm_hex_to_bytes( cbytes_t  hex, int  hexlen, bytes_t  dst )
+{
+    int  nn;
+
+    if (hexlen & 1)  /* must be even */
+        return -1;
+
+    for (nn = 0; nn < hexlen/2; nn++ ) {
+        int  c = gsm_hex2_to_byte( (const char*)hex+2*nn );
+        if (c < 0) return -1;
+        dst[nn] = (byte_t) c;
+    }
+    return hexlen/2;
+}
+
+void
+gsm_hex_from_bytes( char*  hex, cbytes_t  src, int  srclen )
+{
+    int  nn;
+
+    for (nn = 0; nn < srclen; nn++) {
+        gsm_hex_from_byte( hex + 2*nn, src[nn] );
+    }
+}
+
+/** ROPES
+ **/
+
+void
+gsm_rope_init( GsmRope  rope )
+{
+    rope->data  = NULL;
+    rope->pos   = 0;
+    rope->max   = 0;
+    rope->error = 0;
+}
+
+void
+gsm_rope_init_alloc( GsmRope  rope, int  count )
+{
+    rope->data  = rope->data0;
+    rope->pos   = 0;
+    rope->max   = sizeof(rope->data0);
+    rope->error = 0;
+
+    if (count > 0) {
+        rope->data = calloc( count, 1 );
+        rope->max  = count;
+
+        if (rope->data == NULL) {
+            rope->error = 1;
+            rope->max   = 0;
+        }
+    }
+}
+
+int
+gsm_rope_done( GsmRope  rope )
+{
+    int  result = rope->error;
+
+    if (rope->data && rope->data != rope->data0)
+        free(rope->data);
+
+    rope->data  = NULL;
+    rope->pos   = 0;
+    rope->max   = 0;
+    rope->error = 0;
+
+    return result;
+}
+
+
+bytes_t
+gsm_rope_done_acquire( GsmRope  rope, int  *psize )
+{
+    bytes_t  result = rope->data;
+
+    *psize = rope->pos;
+    if (result == rope->data0) {
+        result = malloc(  rope->pos );
+        if (result != NULL)
+            memcpy( result, rope->data, rope->pos );
+    }
+    return result;
+}
+
+
+int
+gsm_rope_ensure( GsmRope  rope, int  new_count )
+{
+    if (rope->data != NULL) {
+        int       old_max  = rope->max;
+        bytes_t   old_data = rope->data == rope->data0 ? NULL : rope->data;
+        int       new_max  = old_max;
+        bytes_t   new_data;
+
+        while (new_max < new_count) {
+            new_max += (new_max >> 1) + 4;
+        }
+        new_data = realloc( old_data, new_max );
+        if (new_data == NULL) {
+            rope->error = 1;
+            return -1;
+        }
+        rope->data = new_data;
+        rope->max  = new_max;
+    } else {
+        rope->max = new_count;
+    }
+    return 0;
+}
+
+static int
+gsm_rope_can_grow( GsmRope  rope, int  count )
+{
+    if (!rope->data || rope->error)
+        return 0;
+
+    if (rope->pos + count > rope->max)
+    {
+        if (rope->data == NULL)
+            rope->max = rope->pos + count;
+
+        else if (rope->error ||
+                 gsm_rope_ensure( rope, rope->pos + count ) < 0)
+            return 0;
+    }
+    return 1;
+}
+
+void
+gsm_rope_add_c( GsmRope  rope,  char  c )
+{
+    if (gsm_rope_can_grow(rope, 1)) {
+        rope->data[ rope->pos ] = (byte_t) c;
+    }
+    rope->pos += 1;
+}
+
+void
+gsm_rope_add( GsmRope  rope, const void*  buf, int  buflen )
+{
+    if (gsm_rope_can_grow(rope, buflen)) {
+        memcpy( rope->data + rope->pos, (const char*)buf, buflen );
+    }
+    rope->pos += buflen;
+}
+
+void*
+gsm_rope_reserve( GsmRope  rope, int  count )
+{
+    void*  result = NULL;
+
+    if (gsm_rope_can_grow(rope, count))
+    {
+        if (rope->data != NULL)
+            result = rope->data + rope->pos;
+    }
+    rope->pos += count;
+
+    return result;
+}
+
+/* skip a given number of Unicode characters in a utf-8 byte string */
+cbytes_t
+utf8_skip( cbytes_t   utf8,
+           cbytes_t   utf8end,
+           int        count)
+{
+    cbytes_t  p   = utf8;
+    cbytes_t  end = utf8end;
+
+    for ( ; count > 0; count-- ) {
+        int  c;
+
+        if (p >= end)
+            break;
+
+        c = *p++;
+        if (c > 128) {
+            while (p < end && (p[0] & 0xc0) == 0x80)
+                p++;
+        }
+    }
+    return  p;
+}
+
+
+static __inline__ int
+utf8_next( cbytes_t  *pp, cbytes_t  end )
+{
+    cbytes_t  p      = *pp;
+    int       result = -1;
+
+    if (p < end) {
+        int  c= *p++;
+        if (c >= 128) {
+            if ((c & 0xe0) == 0xc0)
+                c &= 0x1f;
+            else if ((c & 0xf0) == 0xe0)
+                c &= 0x0f;
+            else
+                c &= 0x07;
+
+            while (p < end && (p[0] & 0xc0) == 0x80) {
+                c = (c << 6) | (p[0] & 0x3f);
+                p ++;
+            }
+        }
+        result = c;
+        *pp    = p;
+    }
+    return result;
+}
+
+
+__inline__ int
+utf8_write( bytes_t  utf8, int  offset, int  v )
+{
+    int  result;
+
+    if (v < 128) {
+        result = 1;
+        if (utf8)
+            utf8[offset] = (byte_t) v;
+    } else if (v < 0x800) {
+        result = 2;
+        if (utf8) {
+            utf8[offset+0] = (byte_t)( 0xc0 | (v >> 6) );
+            utf8[offset+1] = (byte_t)( 0x80 | (v & 0x3f) );
+        }
+    } else if (v < 0x10000) {
+        result = 3;
+        if (utf8) {
+            utf8[offset+0] = (byte_t)( 0xe0 |  (v >> 12) );
+            utf8[offset+1] = (byte_t)( 0x80 | ((v >> 6) & 0x3f) );
+            utf8[offset+2] = (byte_t)( 0x80 |  (v & 0x3f) );
+        }
+    } else {
+        result = 4;
+        if (utf8) {
+            utf8[offset+0] = (byte_t)( 0xf0 | ((v >> 18) & 0x7) );
+            utf8[offset+1] = (byte_t)( 0x80 | ((v >> 12) & 0x3f) );
+            utf8[offset+2] = (byte_t)( 0x80 | ((v >> 6) & 0x3f) );
+            utf8[offset+3] = (byte_t)( 0x80 |  (v & 0x3f) );
+        }
+    }
+    return  result;
+}
+
+static __inline__ int
+ucs2_write( bytes_t  ucs2, int  offset, int  v )
+{
+    if (ucs2) {
+        ucs2[offset+0] = (byte_t) (v >> 8);
+        ucs2[offset+1] = (byte_t) (v);
+    }
+    return 2;
+}
+
+int
+utf8_check( cbytes_t   p, int  utf8len )
+{
+    cbytes_t  end    = p + utf8len;
+    int       result = 0;
+
+    if (p) {
+        while (p < end) {
+            int  c = *p++;
+            if (c >= 128) {
+                int  len;
+                if ((c & 0xe0) == 0xc0) {
+                    len = 1;
+                }
+                else if ((c & 0xf0) == 0xe0) {
+                    len = 2;
+                }
+                else if ((c & 0xf8) == 0xf0) {
+                    len = 3;
+                }
+                else
+                    goto Exit;  /* malformed utf-8 */
+
+                if (p+len > end) /* string too short */
+                    goto Exit;
+
+                for ( ; len > 0; len--, p++ ) {
+                    if ((p[0] & 0xc0) != 0x80)
+                        goto Exit;
+                }
+            }
+        }
+        result = 1;
+    }
+Exit:
+    return result;
+}
+
+/** UCS2 to UTF8
+ **/
+
+/* convert a UCS2 string into a UTF8 byte string, assumes 'buf' is correctly sized */
+int
+ucs2_to_utf8( cbytes_t  ucs2,
+              int       ucs2len,
+              bytes_t   buf )
+{
+    int  nn;
+    int  result = 0;
+
+    for (nn = 0; nn < ucs2len; ucs2 += 2, nn++) {
+        int  c= (ucs2[0] << 8) | ucs2[1];
+        result += utf8_write(buf, result, c);
+    }
+    return result;
+}
+
+/* count the number of UCS2 chars contained in a utf8 byte string */
+int
+utf8_to_ucs2( cbytes_t  utf8,
+              int       utf8len,
+              bytes_t   ucs2 )
+{
+    cbytes_t  p      = utf8;
+    cbytes_t  end    = p + utf8len;
+    int       result = 0;
+
+    while (p < end) {
+        int  c = utf8_next(&p, end);
+
+        if (c < 0)
+            break;
+
+        result += ucs2_write(ucs2, result, c);
+    }
+    return result/2;
+}
+
+
+
+/** GSM ALPHABET
+ **/
+
+#define  GSM_7BITS_ESCAPE   0x1b
+#define  GSM_7BITS_UNKNOWN  0
+
+static const unsigned short   gsm7bits_to_unicode[128] = {
+  '@', 0xa3,  '$', 0xa5, 0xe8, 0xe9, 0xf9, 0xec, 0xf2, 0xc7, '\n', 0xd8, 0xf8, '\r', 0xc5, 0xe5,
+0x394,  '_',0x3a6,0x393,0x39b,0x3a9,0x3a0,0x3a8,0x3a3,0x398,0x39e,    0, 0xc6, 0xe6, 0xdf, 0xc9,
+  ' ',  '!',  '"',  '#', 0xa4,  '%',  '&', '\'',  '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+  '0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+ 0xa1,  'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+  'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',  'Z', 0xc4, 0xd6,0x147, 0xdc, 0xa7,
+ 0xbf,  'a',  'b',  'c',  'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',  'x',  'y',  'z', 0xe4, 0xf6, 0xf1, 0xfc, 0xe0,
+};
+
+static const unsigned short  gsm7bits_extend_to_unicode[128] = {
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,'\f',   0,   0,   0,   0,   0,
+    0,   0,   0,   0, '^',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0, '{', '}',   0,   0,   0,   0,   0,'\\',
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0, '[', '~', ']',   0,
+  '|',   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,0x20ac, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
+};
+
+
+static int
+unichar_to_gsm7( int  unicode )
+{
+    int  nn;
+    for (nn = 0; nn < 128; nn++) {
+        if (gsm7bits_to_unicode[nn] == unicode) {
+            return nn;
+        }
+    }
+    return -1;
+}
+
+static int
+unichar_to_gsm7_extend( int  unichar )
+{
+    int  nn;
+    for (nn = 0; nn < 128; nn++) {
+        if (gsm7bits_extend_to_unicode[nn] == unichar) {
+            return nn;
+        }
+    }
+    return -1;
+}
+
+
+/* return the number of septets needed to encode a unicode charcode */
+static int
+unichar_to_gsm7_count( int  unicode )
+{
+    int  nn;
+
+    nn = unichar_to_gsm7(unicode);
+    if (nn >= 0)
+        return 1;
+
+    nn = unichar_to_gsm7_extend(unicode);
+    if (nn >= 0)
+        return 2;
+
+    return 0;
+}
+
+
+cbytes_t
+utf8_skip_gsm7( cbytes_t  utf8, cbytes_t  utf8end, int  gsm7len )
+{
+    cbytes_t  p   = utf8;
+    cbytes_t  end = utf8end;
+
+    while (gsm7len >0) {
+        cbytes_t  q = p;
+        int       c = utf8_next( &q, end );
+        int       len;
+
+        if (c < 0)
+            break;
+
+        len = unichar_to_gsm7_count( c );
+        if (len == 0)  /* unknown chars are replaced by spaces */
+            len = 1;
+
+        if (len > gsm7len)
+            break;
+
+        gsm7len -= len;
+        p        = q;
+    }
+    return  p;
+}
+
+
+int
+utf8_check_gsm7( cbytes_t  utf8,
+                 int       utf8len )
+{
+    cbytes_t  utf8end = utf8 + utf8len;
+
+    while (utf8 < utf8end) {
+        int  c = utf8_next( &utf8, utf8end );
+        if (unichar_to_gsm7_count(c) == 0)
+            return 0;
+    }
+    return 1;
+}
+
+
+int
+utf8_from_gsm7( cbytes_t  src,
+                int       septet_offset,
+                int       septet_count,
+                bytes_t   utf8 )
+{
+    int  shift   = (septet_offset & 7);
+    int  escaped = 0;
+    int  result  = 0;
+
+    src += (septet_offset >> 3);
+    for ( ; septet_count > 0; septet_count-- )
+    {
+        int  c = (src[0] >> shift) & 0x7f;
+        int  v;
+
+        if (shift > 1) {
+            c = ((src[1] << (8-shift)) | c) & 0x7f;
+        }
+
+        if (escaped) {
+            v = gsm7bits_extend_to_unicode[c];
+        } else if (c == GSM_7BITS_ESCAPE) {
+            escaped = 1;
+            goto NextSeptet;
+        } else {
+            v = gsm7bits_to_unicode[c];
+        }
+
+        result += utf8_write( utf8, result, v );
+
+    NextSeptet:
+        shift += 7;
+        if (shift >= 8) {
+            shift -= 8;
+            src   += 1;
+        }
+    }
+    return  result;
+}
+
+
+int
+utf8_from_gsm8( cbytes_t  src, int  count, bytes_t  utf8 )
+{
+    int  result  = 0;
+    int  escaped = 0;
+
+
+    for ( ; count > 0; count-- )
+    {
+        int  c = *src++;
+
+        if (c == 0xff)
+            break;
+
+        if (c == GSM_7BITS_ESCAPE) {
+            if (escaped) { /* two escape characters => one space */
+                c = 0x20;
+                escaped = 0;
+            } else {
+                escaped = 1;
+                continue;
+            }
+        }
+        else
+        {
+            if (c >= 0x80) {
+                c       = 0x20;
+                escaped = 0;
+            } else if (escaped) {
+                c = gsm7bits_extend_to_unicode[c];
+            } else
+                c = gsm7bits_to_unicode[c];
+        }
+
+        result += utf8_write( utf8, result, c );
+    }
+    return  result;
+}
+
+/* convert a GSM 7-bit message into a unicode character array
+ * the 'dst' array must contain at least 160 chars. the function
+ * returns the number of characters decoded
+ *
+ * assumes the 'dst' array has at least septet_count items, returns the
+ * number of unichars really written
+ */
+int
+ucs2_from_gsm7( bytes_t   ucs2,
+                cbytes_t  src,
+                int       septet_offset,
+                int       septet_count )
+{
+    const unsigned char*  p     = src + (septet_offset >> 3);
+    int                   shift = (septet_offset & 7);
+    int                   escaped = 0;
+    int                   result  = 0;
+
+    for ( ; septet_count > 0; septet_count-- )
+    {
+        unsigned  val  = (p[0] >> shift) & 0x7f;
+
+        if (shift > 1)
+            val = (val | (p[1] << (8-shift))) & 0x7f;
+
+        if (escaped) {
+            int  c = gsm7bits_to_unicode[val];
+
+            result += ucs2_write(ucs2, result, c);
+            escaped = 0;
+        }
+        else if (val == GSM_7BITS_ESCAPE) {
+            escaped = 1;
+        }
+        else {
+            val = gsm7bits_extend_to_unicode[val];
+            if (val == 0)
+                val = 0x20;
+
+            result += ucs2_write( ucs2, result, val );
+        }
+    }
+    return result/2;
+}
+
+
+/* count the number of septets required to write a utf8 string */
+static int
+utf8_to_gsm7_count( cbytes_t  utf8, int  utf8len )
+{
+    cbytes_t  utf8end = utf8 + utf8len;
+    int       result  = 0;
+
+    while ( utf8 < utf8end ) {
+        int  len;
+        int  c = utf8_next( &utf8, utf8end );
+
+        if (c < 0)
+            break;
+
+        len = unichar_to_gsm7_count(c);
+        if (len == 0)    /* replace non-representables with space */
+            len = 1;
+
+        result += len;
+    }
+    return result;
+}
+
+typedef struct {
+    bytes_t   dst;
+    unsigned  pad;
+    int       bits;
+    int       offset;
+} BWriterRec, *BWriter;
+
+static void
+bwriter_init( BWriter  writer, bytes_t  dst, int  start )
+{
+    int  shift = start & 7;
+
+    writer->dst    = dst + (start >> 3);
+    writer->pad    = 0;
+    writer->bits   = shift;
+    writer->offset = start;
+
+    if (shift > 0) {
+        writer->pad  = writer->dst[0] & ~(0xFF << shift);
+    }
+}
+
+static void
+bwriter_add7( BWriter  writer, unsigned  value )
+{
+    writer->pad  |= (unsigned)(value << writer->bits);
+    writer->bits += 7;
+    if (writer->bits >= 8) {
+        writer->dst[0] = (byte_t)writer->pad;
+        writer->bits  -= 8;
+        writer->pad  >>= 8;
+        writer->dst   += 1;
+    }
+    writer->offset += 7;
+}
+
+static int
+bwriter_done( BWriter  writer )
+{
+    if (writer->bits > 0) {
+        writer->dst[0] = (byte_t)writer->pad;
+        writer->pad    = 0;
+        writer->bits   = 0;
+        writer->dst   += 1;
+    }
+    return writer->offset;
+}
+
+/* convert a utf8 string to a gsm7 byte string - return the number of septets written */
+int
+utf8_to_gsm7( cbytes_t  utf8, int  utf8len, bytes_t  dst, int offset )
+{
+    const unsigned char*  utf8end = utf8 + utf8len;
+    BWriterRec            writer[1];
+
+    if (dst == NULL)
+        return utf8_to_gsm7_count(utf8, utf8len);
+
+    bwriter_init( writer, dst, offset );
+    while ( utf8 < utf8end ) {
+        int  c = utf8_next( &utf8, utf8end );
+        int  nn;
+
+        if (c < 0)
+            break;
+
+        nn = unichar_to_gsm7(c);
+        if (nn >= 0) {
+            bwriter_add7( writer, nn );
+            continue;
+        }
+
+        nn = unichar_to_gsm7_extend(c);
+        if (nn >= 0) {
+            bwriter_add7( writer, GSM_7BITS_ESCAPE );
+            bwriter_add7( writer, nn );
+            continue;
+        }
+
+        /* unknown => replaced by space */
+        bwriter_add7( writer, 0x20 );
+    }
+    return  bwriter_done( writer );
+}
+
+
+int
+utf8_to_gsm8( cbytes_t  utf8, int  utf8len, bytes_t  dst )
+{
+    const unsigned char*  utf8end = utf8 + utf8len;
+    int                   result  = 0;
+
+    while ( utf8 < utf8end ) {
+        int  c = utf8_next( &utf8, utf8end );
+        int  nn;
+
+        if (c < 0)
+            break;
+
+        nn = unichar_to_gsm7(c);
+        if (nn >= 0) {
+            if (dst)
+                dst[result] = (byte_t)nn;
+            result += 1;
+            continue;
+        }
+
+        nn = unichar_to_gsm7_extend(c);
+        if (nn >= 0) {
+            if (dst) {
+                dst[result+0] = (byte_t) GSM_7BITS_ESCAPE;
+                dst[result+1] = (byte_t) nn;
+            }
+            result += 2;
+            continue;
+        }
+
+        /* unknown => space */
+        if (dst)
+            dst[result] = 0x20;
+        result += 1;
+    }
+    return  result;
+}
+
+
+int
+ucs2_to_gsm7( cbytes_t  ucs2, int  ucs2len, bytes_t  dst, int offset )
+{
+    const unsigned char*  ucs2end = ucs2 + ucs2len*2;
+    BWriterRec            writer[1];
+
+    bwriter_init( writer, dst, offset );
+    while ( ucs2 < ucs2end ) {
+        int  c = *ucs2++;
+        int  nn;
+
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_to_unicode[nn] == c ) {
+                bwriter_add7( writer, nn );
+                goto NextUnicode;
+            }
+        }
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_extend_to_unicode[nn] == c ) {
+                bwriter_add7( writer, GSM_7BITS_ESCAPE );
+                bwriter_add7( writer, nn );
+                goto NextUnicode;
+            }
+        }
+
+        /* unknown */
+        bwriter_add7( writer, 0x20 );
+
+    NextUnicode:
+        ;
+    }
+    return  bwriter_done( writer );
+}
+
+
+int
+ucs2_to_gsm8( cbytes_t  ucs2, int  ucs2len, bytes_t  dst )
+{
+    const unsigned char*  ucs2end = ucs2 + ucs2len*2;
+    bytes_t               dst0    = dst;
+
+    while ( ucs2 < ucs2end ) {
+        int  c = *ucs2++;
+        int  nn;
+
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_to_unicode[nn] == c ) {
+                *dst++ = (byte_t)nn;
+                goto NextUnicode;
+            }
+        }
+        for (nn = 0; nn < 128; nn++) {
+            if ( gsm7bits_extend_to_unicode[nn] == c ) {
+                dst[0] = (byte_t) GSM_7BITS_ESCAPE;
+                dst[1] = (byte_t) nn;
+                dst   += 2;
+                goto NextUnicode;
+            }
+        }
+
+        /* unknown */
+        *dst++ = 0x20;
+
+    NextUnicode:
+        ;
+    }
+    return (dst - dst0);
+}
+
+int
+gsm_bcdnum_to_ascii( cbytes_t  bcd, int  count, bytes_t  dst )
+{
+    int  result = 0;
+    int  shift  = 0;
+
+    while (count > 0) {
+        int  c = (bcd[0] >> shift) & 0xf;
+
+        if (c == 15 && count == 1)  /* ignore trailing 0xf */
+            break;
+
+        if (c >= 14)
+            c = 0;
+
+        if (dst) dst[result] = "0123456789*#,N"[c];
+        result += 1;
+
+        shift += 4;
+        if (shift == 8) {
+            shift = 0;
+            bcd += 1;
+        }
+    }
+    return  result;
+}
+
+
+int
+gsm_bcdnum_from_ascii( cbytes_t  ascii, int  asciilen, bytes_t  dst )
+{
+    cbytes_t  end = ascii + asciilen;
+    int  result   = 0;
+    int  phase = 0x01;
+
+    while (ascii < end) {
+        int  c = *ascii++;
+
+        if (c == '*')
+            c = 10;
+        else if (c == '#')
+            c = 11;
+        else if (c == ',')
+            c = 12;
+        else if (c == 'N')
+            c = 13;
+        else {
+            c -= '0';
+            if ((unsigned)c >= 10U)
+                return -1;
+        }
+        phase   = (phase << 4) | c;
+        result += 1;
+        if (phase & 0x100) {
+            if (dst) dst[result/2] = (byte_t) phase;
+            phase   = 0x01;
+        }
+    }
+
+    if (result & 1) {
+        if (dst) dst[result/2] = (byte_t)(phase | 0xf0);
+    }
+    return result;
+}
+
+/** ADN: Abbreviated Dialing Number
+ **/
+
+#define  ADN_FOOTER_SIZE     14
+#define  ADN_OFFSET_NUMBER_LENGTH   0
+#define  ADN_OFFSET_TON_NPI         1
+#define  ADN_OFFSET_NUMBER_START    2
+#define  ADN_OFFSET_NUMBER_END      11
+#define  ADN_OFFSET_CAPABILITY_ID   12
+#define  ADN_OFFSET_EXTENSION_ID    13
+
+/* see 10.5.1 of 3GPP 51.011 */
+static int
+sim_adn_alpha_to_utf8( cbytes_t  alpha, cbytes_t  end, bytes_t  dst )
+{
+    int  result = 0;
+
+    /* ignore trailing 0xff */
+    while (alpha < end && end[-1] == 0xff)
+        end--;
+
+    if (alpha >= end)
+        return 0;
+
+    if (alpha[0] == 0x80) { /* UCS/2 source encoding */
+        alpha += 1;
+        result = ucs2_to_utf8( alpha, (end-alpha)/2, dst );
+    }
+    else
+    {
+        int  is_ucs2 = 0;
+        int  len = 0, base = 0;
+
+        if (alpha+3 <= end && alpha[0] == 0x81) {
+            is_ucs2 = 1;
+            len     = alpha[1];
+            base    = alpha[2] << 7;
+            alpha  += 3;
+            if (len > end-alpha)
+                len = end-alpha;
+        } else if (alpha+4 <= end && alpha[0] == 0x82) {
+            is_ucs2 = 1;
+            len     = alpha[1];
+            base    = (alpha[2] << 8) | alpha[3];
+            alpha  += 4;
+            if (len > end-alpha)
+                len = end-alpha;
+        }
+
+        if (is_ucs2) {
+            end = alpha + len;
+            while (alpha < end) {
+                int  c = alpha[0];
+                if (c >= 0x80) {
+                    result += utf8_write(dst, result, base + (c & 0x7f));
+                    alpha  += 1;
+                } else {
+                    /* GSM character set */
+                    int   count;
+                    for (count = 0; alpha+count < end && alpha[count] < 128; count++)
+                        ;
+                    result += utf8_from_gsm8(alpha, count, (dst ? dst+result : NULL));
+                    alpha  += count;
+                }
+            }
+        }
+        else {
+            result = utf8_from_gsm8(alpha, end-alpha, dst);
+        }
+    }
+    return result;
+}
+
+#if 0
+static int
+sim_adn_alpha_from_utf8( cbytes_t  utf8, int  utf8len, bytes_t  dst )
+{
+    int   result = 0;
+
+    if (utf8_check_gsm7(utf8, utf8len)) {
+        /* GSM 7-bit compatible, encode directly as 8-bit string */
+        result = utf8_to_gsm8(utf8, utf8len, dst);
+    } else {
+        /* otherwise, simply try UCS-2 encoding, nothing more serious at the moment */
+        if (dst) {
+            dst[0] = 0x80;
+        }
+        result = 1 + utf8_to_ucs2(utf8, utf8len, dst ? (dst+1) : NULL)*2;
+    }
+    return  result;
+}
+#endif
+
+int
+sim_adn_record_from_bytes( SimAdnRecord  rec, cbytes_t  data, int  len )
+{
+    cbytes_t  end    = data + len;
+    cbytes_t  footer = end - ADN_FOOTER_SIZE;
+    int       num_len;
+
+    rec->adn.alpha[0]  = 0;
+    rec->adn.number[0] = 0;
+    rec->ext_record    = 0xff;
+
+    if (len < ADN_FOOTER_SIZE)
+        return -1;
+
+    /* alpha is optional */
+    if (len > ADN_FOOTER_SIZE) {
+        cbytes_t  dataend = data + len - ADN_FOOTER_SIZE;
+        int       count   = sim_adn_alpha_to_utf8(data, dataend, NULL);
+
+        if (count > sizeof(rec->adn.alpha)-1)  /* too long */
+            return -1;
+
+        sim_adn_alpha_to_utf8(data, dataend, rec->adn.alpha);
+        rec->adn.alpha[count] = 0;
+    }
+
+    num_len = footer[ADN_OFFSET_NUMBER_LENGTH];
+    if (num_len > 11)
+        return -1;
+
+    /* decode TON and number to ASCII, NOTE: this is lossy !! */
+    {
+        int      ton    = footer[ADN_OFFSET_TON_NPI];
+        bytes_t  number = (bytes_t) rec->adn.number;
+        int      len    = sizeof(rec->adn.number)-1;
+        int      count;
+
+        if (ton != 0x81 && ton != 0x91)
+            return -1;
+
+        if (ton == 0x91) {
+            *number++ = '+';
+            len      -= 1;
+        }
+
+        count = gsm_bcdnum_to_ascii( footer + ADN_OFFSET_NUMBER_START,
+                                     num_len*2, number );
+        number[count] = 0;
+    }
+    return 0;
+}
+
+int
+sim_adn_record_to_bytes( SimAdnRecord  rec, bytes_t   data, int  datalen )
+{
+    bytes_t   end    = data + datalen;
+    bytes_t   footer = end - ADN_FOOTER_SIZE;
+    int       ton    = 0x81;
+    cbytes_t  number = (cbytes_t) rec->adn.number;
+
+    if (number[0] == '+') {
+        ton     = 0x91;
+        number += 1;
+    }
+    footer[0] = (strlen((const char*)number)+1)/2 + 1;
+    /* XXXX: TODO */
+    return 0;
+}
diff --git a/telephony/gsm.h b/telephony/gsm.h
new file mode 100644
index 0000000..f799dea
--- /dev/null
+++ b/telephony/gsm.h
@@ -0,0 +1,196 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_gsm_h
+#define _android_gsm_h
+
+/** USEFUL TYPES
+ **/
+
+typedef unsigned char  byte_t;
+typedef byte_t*        bytes_t;
+typedef const byte_t*  cbytes_t;
+
+/** BCD
+ **/
+
+/* convert a 8-bit value into the corresponding nibble-bcd byte */
+extern byte_t   gsm_int_to_bcdi( int  value );
+
+/* convert a nibble-bcd byte into an int, invalid nibbles are silently converted to 0 */
+extern int      gsm_int_from_bcdi( byte_t  value );
+
+/** HEX
+ **/
+
+/* try to convert a hex string into a byte string, assumes 'dst' is properly sized, and hexlen is even.
+ * returns the number of bytes on exit, or -1 in case of badly formatted data */
+extern int      gsm_hex_to_bytes  ( cbytes_t  hex, int  hexlen, bytes_t  dst );
+
+/* convert a hex string into a byte string, assumes 'dst' is properly sized, and hexlen is even.
+ * no checks are performed */
+extern void     gsm_hex_to_bytes0 ( cbytes_t  hex, int  hexlen, bytes_t  dst );
+
+/* convert a byte string into a hex string, assumes 'hex' is properly sized */
+extern void     gsm_hex_from_bytes( char*  hex, cbytes_t  src, int  srclen );
+
+/* convert a hexchar to an int, returns -1 on error */
+extern int      gsm_hexchar_to_int( char  c );
+
+/* convert a hexchar to an int, returns 0 on error */
+extern int      gsm_hexchar_to_int0( char  c );
+
+/* convert a 2-char hex value into an int, returns -1 on error */
+extern int      gsm_hex2_to_byte( const char*  hex );
+
+/* convert a 2-char hex value into an int, returns 0 on error */
+extern int      gsm_hex2_to_byte0( const char*  hex );
+
+/* convert a 4-char hex value into an int, returns -1 on error */
+extern int      gsm_hex4_to_short( const char*  hex );
+
+/* convert a 4-char hex value into an int, returns 0 on error */
+extern int      gsm_hex4_to_short0( const char*  hex );
+
+/* write a byte to a 2-byte hex string */
+extern void     gsm_hex_from_byte( char*  hex, int  val );
+
+extern void     gsm_hex_from_short( char*  hex, int  val );
+
+/** UTF-8 and GSM Alphabet
+ **/
+
+/* check that a given utf8 string is well-formed, returns 1 on success, 0 otherwise */
+extern int      utf8_check( cbytes_t  utf8, int  utf8len );
+
+/* check that all characters in a given utf8 string can be encoded into the GSM alphabet.
+   returns 1 if TRUE, 0 otherwise */
+extern int      utf8_check_gsm7( cbytes_t  utf8, int  utf8len );
+
+/* try to skip enough utf8 characters to generate gsm7len GSM septets */
+extern cbytes_t utf8_skip_gsm7( cbytes_t  utf8, cbytes_t  utf8end, int  gsm7len );
+
+/* convert a utf-8 string into a GSM septet string, assumes 'dst' is NULL or is properly sized,
+   and that all characters are representable. 'offset' is the starting bit offset in 'dst'.
+   non-representable characters are replaced by spaces.
+   returns the number of septets, */
+extern int      utf8_to_gsm7( cbytes_t  utf8, int  utf8len, bytes_t  dst, int  offset );
+
+/* convert a utf8 string into an array of 8-bit unpacked GSM septets,
+ * assumes 'dst' is NULL or is properly sized, returns the number of GSM bytes */
+extern int      utf8_to_gsm8( cbytes_t  utf8, int  utf8len, bytes_t  dst );
+
+/* convert a GSM septets string into a utf-8 byte string. assumes that 'utf8' is NULL or properly
+   sized. 'offset' is the starting bit offset in 'src', 'count' is the number of input septets.
+   return the number of utf8 bytes. */
+extern int      utf8_from_gsm7( cbytes_t  src, int  offset, int  count, bytes_t  utf8 );
+
+/* convert an unpacked 8-bit GSM septets string into a utf-8 byte string. assumes that 'utf8'
+   is NULL or properly sized. 'count' is the number of input bytes.
+   returns the number of utf8 bytes */
+extern int      utf8_from_gsm8( cbytes_t  src, int  count, bytes_t  utf8 );
+
+
+/** UCS-2 and GSM Alphabet
+ **
+ ** Note that here, 'ucs2' really refers to non-aligned UCS2-BE, as used by the GSM standard
+ **/
+
+/* check that all characters in a given ucs2 string can be encoded into the GSM alphabet.
+   returns 1 if TRUE, 0 otherwise */
+extern int      ucs2_check_gsm7( cbytes_t  ucs2, int  ucs2len );
+
+/* convert a ucs2 string into a GSM septet string, assumes 'dst' is NULL or properly sized,
+   'offset' is the starting bit offset in 'dst'. non-representable characters are replaced
+   by spaces. returns the number of septets */
+extern int      ucs2_to_gsm7( cbytes_t  ucs2, int  ucs2len, bytes_t  dst, int  offset );
+
+/* convert a ucs2 string into a GSM septet string, assumes 'dst' is NULL or properly sized,
+   non-representable characters are replaced by spaces. returns the number of bytes */
+extern int      ucs2_to_gsm8( cbytes_t  ucs2, int  ucs2len, bytes_t  dst );
+
+/* convert a GSM septets string into a ucs2 string. assumes that 'ucs2' is NULL or
+   properly sized. 'offset' is the starting bit offset in 'src', 'count' is the number
+   of input septets. return the number of ucs2 characters (not bytes) */
+extern int      ucs2_from_gsm7( bytes_t   ucs2, cbytes_t  src, int  offset, int  count );
+
+/* convert an 8-bit unpacked GSM septets string into a ucs2 string. assumes that 'ucs2'
+   is NULL or properly sized. 'count' is the number of input septets. return the number
+   of ucs2 characters (not bytes) */
+extern int      ucs2_from_gsm8( bytes_t   ucs2, cbytes_t  src, int  count );
+
+
+/** UCS2 to/from UTF8
+ **/
+
+/* convert a ucs2 string into a utf8 byte string, assumes 'utf8' NULL or properly sized.
+   returns the number of utf8 bytes*/
+extern int      ucs2_to_utf8( cbytes_t  ucs2, int  ucs2len, bytes_t  utf8 );
+
+/* convert a utf8 byte string into a ucs2 string, assumes 'ucs2' NULL or properly sized.
+   returns the number of ucs2 chars */
+extern int      utf8_to_ucs2( cbytes_t  utf8, int  utf8len, bytes_t  ucs2 );
+
+/* try to skip a given number of characters in a utf-8 byte string, return new position */
+extern cbytes_t  utf8_skip( cbytes_t   utf8, cbytes_t   utf8end, int  count);
+
+/** Dial Numbers: TON byte + 'count' bcd numbers
+ **/
+
+/* convert a bcd-coded GSM dial number into an ASCII string (not zero-terminated)
+   assumes 'dst' is NULL or properly sized, returns 0 in case of success, -1 in case of error.
+   'num_digits' is the number of digits, not input bytes. a trailing 0xf0 is ignored automatically
+   return the number of ASCII chars */
+extern int  gsm_bcdnum_to_ascii  ( cbytes_t  bcd, int  num_digits, bytes_t  dst );
+
+/* convert an ASCII dial-number into a bcd-coded string, returns the number of 4-bit nibbles written, */
+extern int  gsm_bcdnum_from_ascii( cbytes_t  ascii, int  asciilen, bytes_t  dst );
+
+/** ADN: Abbreviated Dialing Numbers
+ **/
+#define  SIM_ADN_MAX_ALPHA        20  /* maximum number of characters in ADN alpha tag */
+#define  SIM_ADN_MAX_NUMBER       20  /* maximum digits in ADN number */
+
+typedef struct {
+    byte_t  alpha [ SIM_ADN_MAX_ALPHA*3+1 ];  /* alpha tag in zero-terminated utf-8      */
+    char    number[ SIM_ADN_MAX_NUMBER+1 ];   /* dialing number in zero-terminated ASCII */
+}
+SimAdnRec, *SimAdn;
+
+typedef struct {
+    SimAdnRec       adn;
+    byte_t          ext_record;  /* 0 or 0xFF means no extension */
+}
+SimAdnRecordRec, *SimAdnRecord;
+
+extern int  sim_adn_record_from_bytes( SimAdnRecord  rec, cbytes_t  data, int  datalen );
+extern int  sim_adn_record_to_bytes  ( SimAdnRecord  rec, bytes_t   data, int  datalen );
+
+/** ROPES
+ **/
+
+typedef struct {
+    bytes_t         data;
+    int             max;
+    int             pos;
+    int             error;
+    unsigned char   data0[16];
+} GsmRopeRec, *GsmRope;
+
+extern void      gsm_rope_init( GsmRope  rope );
+extern void      gsm_rope_init_alloc( GsmRope  rope, int  alloc );
+extern int       gsm_rope_done( GsmRope  rope );
+extern bytes_t   gsm_rope_done_acquire( GsmRope  rope, int  *psize );
+extern void      gsm_rope_add_c( GsmRope  rope, char  c );
+extern void      gsm_rope_add( GsmRope  rope, const void*  str, int  len );
+extern void*     gsm_rope_reserve( GsmRope  rope, int  len );
+
+#endif /* _android_gsm_h */
diff --git a/telephony/modem_driver.c b/telephony/modem_driver.c
new file mode 100644
index 0000000..99bbe6c
--- /dev/null
+++ b/telephony/modem_driver.c
@@ -0,0 +1,148 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+/* implement the modem character device for Android within the QEMU event loop.
+ * it communicates through a serial port with "rild" (Radio Interface Layer Daemon)
+ * on the emulated device.
+ */
+#include "modem_driver.h"
+#include "qemu-char.h"
+
+#define  xxDEBUG
+
+#ifdef DEBUG
+#  include <stdio.h>
+#  define  D(...)   ( fprintf( stderr, __VA_ARGS__ ) )
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+AModem            android_modem;
+CharDriverState*  android_modem_cs;
+
+typedef struct {
+    CharDriverState*  cs;
+    AModem            modem;
+    char              in_buff[ 1024 ];
+    int               in_pos;
+    int               in_sms;
+} ModemDriver;
+
+/* send unsollicited messages to the device */
+static void
+modem_driver_unsol( void*  _md, const char*  message)
+{
+    ModemDriver*      md = _md;
+    int               len = strlen(message);
+
+    qemu_chr_write(md->cs, (const uint8_t*)message, len);
+}
+
+static int
+modem_driver_can_read( void*  _md )
+{
+    ModemDriver*  md  = _md;
+    int           ret = sizeof(md->in_buff) - md->in_pos;
+
+    return ret;
+}
+
+/* despite its name, this function is called when the device writes to the modem */
+static void
+modem_driver_read( void*  _md, const uint8_t*  src, int  len )
+{
+    ModemDriver*      md  = _md;
+    const uint8_t*    end = src + len;
+    int               nn;
+
+    D( "%s: reading %d from %p bytes:", __FUNCTION__, len, src );
+    for (nn = 0; nn < len; nn++) {
+        int  c = src[nn];
+        if (c >= 32 && c < 127)
+            D( "%c", c );
+        else if (c == '\n')
+            D( "<LF>" );
+        else if (c == '\r')
+            D( "<CR>" );
+        else
+            D( "\\x%02x", c );
+    }
+    D( "\n" );
+
+    for ( ; src < end; src++ ) {
+        char  c = src[0];
+
+        if (md->in_sms) {
+            if (c != 26)
+                goto AppendChar;
+
+            md->in_buff[ md->in_pos ] = c;
+            md->in_pos++;
+            md->in_sms = 0;
+            c = '\n';
+        }
+
+        if (c == '\n' || c == '\r') {
+            const char*  answer;
+
+            if (md->in_pos == 0)  /* skip empty lines */
+                continue;
+
+            md->in_buff[ md->in_pos ] = 0;
+            md->in_pos                = 0;
+
+            D( "%s: << %s\n", __FUNCTION__, md->in_buff );
+            answer = amodem_send(android_modem, md->in_buff);
+            if (answer != NULL) {
+                D( "%s: >> %s\n", __FUNCTION__, answer );
+                len = strlen(answer);
+                if (len == 2 && answer[0] == '>' && answer[1] == ' ')
+                    md->in_sms = 1;
+
+                qemu_chr_write(md->cs, (const uint8_t*)answer, len);
+                qemu_chr_write(md->cs, (const uint8_t*)"\r", 1);
+            } else
+                D( "%s: -- NO ANSWER\n", __FUNCTION__ );
+
+            continue;
+        }
+    AppendChar:
+        md->in_buff[ md->in_pos++ ] = c;
+        if (md->in_pos == sizeof(md->in_buff)) {
+            /* input is too long !! */
+            md->in_pos = 0;
+        }
+    }
+    D( "%s: done\n", __FUNCTION__ );
+}
+
+
+static void
+modem_driver_init( int  base_port, ModemDriver*  dm, CharDriverState*  cs )
+{
+    dm->cs     = cs;
+    dm->in_pos = 0;
+    dm->in_sms = 0;
+    dm->modem  = amodem_create( base_port, modem_driver_unsol, dm );
+
+    qemu_chr_add_handlers( cs, modem_driver_can_read, modem_driver_read, NULL, dm );
+}
+
+
+void android_modem_init( int  base_port )
+{
+    static ModemDriver  modem_driver[1];
+
+    if (android_modem_cs != NULL) {
+        modem_driver_init( base_port, modem_driver, android_modem_cs );
+        android_modem = modem_driver->modem;
+    }
+}
diff --git a/telephony/modem_driver.h b/telephony/modem_driver.h
new file mode 100644
index 0000000..d03010f
--- /dev/null
+++ b/telephony/modem_driver.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _modem_driver_h
+#define _modem_driver_h
+
+#include "android_modem.h"
+#include "qemu-common.h"
+
+/** in telephony/modem_driver.c */
+/* this is the internal character driver used to communicate with the
+ * emulated GSM modem. see qemu_chr_open() in vl.c */
+extern CharDriverState*  android_modem_cs;
+
+/* the emulated GSM modem itself */
+extern AModem  android_modem;
+
+/* must be called before the VM runs if there is a modem to emulate */
+extern void   android_modem_init( int  base_port );
+
+#endif /* _modem_driver_h */
diff --git a/telephony/remote_call.c b/telephony/remote_call.c
new file mode 100644
index 0000000..927e11d
--- /dev/null
+++ b/telephony/remote_call.c
@@ -0,0 +1,430 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "remote_call.h"
+#include "android/utils/bufprint.h"
+#include "android/utils/debug.h"
+#include "sysdeps.h"
+#include "gsm.h"
+#include "android/android.h"
+#include "sockets.h"
+#include <stdlib.h>
+
+#define  DEBUG  1
+
+#if 1
+#  define  D_ACTIVE  VERBOSE_CHECK(modem)
+#else
+#  define  D_ACTIVE  DEBUG
+#endif
+
+#if 1
+#  define  S_ACTIVE  VERBOSE_CHECK(socket)
+#else
+#  define  S_ACTIVE  DEBUG
+#endif
+
+#if DEBUG
+#  include <stdio.h>
+#  define  D(...)   do { if (D_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
+#  define  S(...)   do { if (S_ACTIVE) fprintf( stderr, __VA_ARGS__ ); } while (0)
+#else
+#  define  D(...)   ((void)0)
+#  define  S(...)   ((void)0)
+#endif
+
+/** By convention, remote numbers are the console ports, i.e. 5554, 5556, etc...
+ **/
+#define  REMOTE_NUMBER_BASE       5554
+#define  REMOTE_NUMBER_MAX        16
+#define  REMOTE_NUMBER_MAX_CHARS  4
+#define  REMOTE_CONSOLE_PORT      5554
+
+int
+remote_number_from_port( int  port )
+{
+    if (port & 1)  /* must be even */
+        return -1;
+
+    port = (port - REMOTE_CONSOLE_PORT) >> 1;
+    if ((unsigned)port >= REMOTE_NUMBER_MAX)
+        return -1;
+
+    return REMOTE_NUMBER_BASE + port*2;
+}
+
+int
+remote_number_to_port( int  number )
+{
+    if (number & 1)  /* must be even */
+        return -1;
+
+    number = (number - REMOTE_NUMBER_BASE) >> 1;
+    if ((unsigned)number >= REMOTE_NUMBER_MAX)
+        return -1;
+
+    return REMOTE_CONSOLE_PORT + number*2;
+}
+
+int
+remote_number_string_to_port( const char*  number )
+{
+    char*  end;
+    long   num = strtol( number, &end, 10 );
+
+    if (end == NULL || *end || (int)num != num )
+        return -1;
+
+    return remote_number_to_port( (int)num );
+}
+
+/** REMOTE CALL OBJECTS
+ **/
+
+typedef struct RemoteCallRec {
+    struct RemoteCallRec*   next;
+    struct RemoteCallRec**  pref;
+    RemoteCallType          type;
+    int                     to_port;
+    int                     from_port;
+    SysChannel              channel;
+    RemoteResultFunc        result_func;
+    void*                   result_opaque;
+
+    char                    quitting;
+
+    /* the output buffer */
+    char*                   buff;
+    int                     buff_pos;
+    int                     buff_len;
+    int                     buff_size;
+    char                    buff0[32];
+
+} RemoteCallRec, *RemoteCall;
+
+static void
+remote_call_done( RemoteCall  call )
+{
+    call->pref[0] = call->next;
+    call->next    = NULL;
+    call->pref    = &call->next;
+
+    if (call->buff && call->buff != call->buff0) {
+        free(call->buff);
+        call->buff      = call->buff0;
+        call->buff_size = (int) sizeof(call->buff0);
+    }
+
+    if ( call->channel ) {
+        sys_channel_close( call->channel );
+        call->channel = NULL;
+    }
+
+    call->buff_pos = 0;
+    call->buff_len = 0;
+}
+
+
+static void
+remote_call_free( RemoteCall  call )
+{
+    if (call) {
+        remote_call_done( call );
+        free(call);
+    }
+}
+
+
+static void  remote_call_event( void*  opaque, int  events );  /* forward */
+
+static RemoteCall
+remote_call_alloc( RemoteCallType  type, int  to_port, int  from_port )
+{
+    RemoteCall  rcall    = calloc( sizeof(*rcall), 1 );
+    int         from_num = remote_number_from_port(from_port);
+
+    if (rcall != NULL) {
+        char  *p, *end;
+
+        rcall->pref      = &rcall->next;
+        rcall->type      = type;
+        rcall->to_port   = to_port;
+        rcall->from_port = from_port;
+        rcall->buff      = rcall->buff0;
+        rcall->buff_size = sizeof(rcall->buff0);
+        rcall->buff_pos  = 0;
+
+        p   = rcall->buff;
+        end = p + rcall->buff_size;
+
+        switch (type) {
+            case REMOTE_CALL_DIAL:
+                p = bufprint(p, end, "gsm call %d\n", from_num );
+                break;
+
+            case REMOTE_CALL_BUSY:
+                p = bufprint(p, end, "gsm busy %d\n", from_num);
+                break;
+
+            case REMOTE_CALL_HOLD:
+                p = bufprint(p, end, "gsm hold %d\n", from_num);
+                break;
+
+            case REMOTE_CALL_ACCEPT:
+                p = bufprint(p, end, "gsm accept %d\n", from_num);
+                break;
+
+            case REMOTE_CALL_HANGUP:
+                p = bufprint(p, end, "gsm cancel %d\n", from_num );
+                break;
+
+            default:
+                ;
+        }
+        if (p >= end) {
+            D("%s: buffer too short\n", __FUNCTION__ );
+            remote_call_free(rcall);
+            return NULL;
+        }
+
+        rcall->buff_len = p - rcall->buff;
+
+        rcall->channel = sys_channel_create_tcp_client( "localhost", to_port );
+        if (rcall->channel == NULL) {
+            D("%s: could not create channel to port %d\n", __FUNCTION__, to_port);
+            remote_call_free(rcall);
+            return NULL;
+        }
+
+        sys_channel_on( rcall->channel, SYS_EVENT_WRITE, remote_call_event, rcall );
+    }
+    return  rcall;
+}
+
+
+static int
+remote_call_set_sms_pdu( RemoteCall  call,
+                         SmsPDU      pdu )
+{
+    char  *p, *end;
+    int    msg2len;
+
+    msg2len = 32 + smspdu_to_hex( pdu, NULL, 0 );
+    if (msg2len > call->buff_size) {
+        char*  old_buff = call->buff == call->buff0 ? NULL : call->buff;
+        char*  new_buff = realloc( old_buff, msg2len );
+        if (new_buff == NULL) {
+            D("%s: not enough memory to alloc %d bytes", __FUNCTION__, msg2len);
+            return -1;
+        }
+        call->buff      = new_buff;
+        call->buff_size = msg2len;
+    }
+
+    p   = call->buff;
+    end = p + call->buff_size;
+
+    p  = bufprint(p, end, "sms pdu ");
+    p += smspdu_to_hex( pdu, p, end-p );
+    *p++ = '\n';
+    *p = 0;
+
+    call->buff_len = p - call->buff;
+    call->buff_pos = 0;
+    return 0;
+}
+
+
+static void
+remote_call_add( RemoteCall   call,
+                 RemoteCall  *plist )
+{
+    RemoteCall  first = *plist;
+
+    call->next = first;
+    call->pref = plist;
+
+    if (first)
+        first->pref = &call->next;
+}
+
+static void
+remote_call_event( void*  opaque, int  events )
+{
+    RemoteCall  call = opaque;
+
+    S("%s: called for call (%d,%d), events=%02x\n", __FUNCTION__,
+       call->from_port, call->to_port, events);
+
+    if (events & SYS_EVENT_READ) {
+        /* simply drain the channel */
+        char  temp[32];
+        int  n = sys_channel_read( call->channel, temp, sizeof(temp) );
+        if (n <= 0) {
+            /* remote emulator probably quitted */
+            //S("%s: emulator %d quitted with %d: %s\n", __FUNCTION__, call->to_port, errno, errno_str);
+            remote_call_free( call );
+            return;
+        }
+    }
+
+    if (events & SYS_EVENT_WRITE) {
+        int  n;
+
+        if (S_ACTIVE) {
+            int  nn;
+            S("%s: call (%d,%d) sending %d bytes '", __FUNCTION__,
+            call->from_port, call->to_port, call->buff_len - call->buff_pos );
+            for (nn = call->buff_pos; nn < call->buff_len; nn++) {
+                int  c = call->buff[nn];
+                if (c < 32) {
+                    if (c == '\n')
+                        S("\\n");
+                    else if (c == '\t')
+                        S("\\t");
+                    else if (c == '\r')
+                        S("\\r");
+                    else
+                        S("\\x%02x", c);
+                } else
+                    S("%c", c);
+            }
+            S("'\n");
+        }
+
+        n = sys_channel_write( call->channel,
+                               call->buff + call->buff_pos,
+                               call->buff_len - call->buff_pos );
+        if (n <= 0) {
+            /* remote emulator probably quitted */
+            S("%s: emulator %d quitted unexpectedly with error %d: %s\n",
+                    __FUNCTION__, call->to_port, errno, errno_str);
+            if (call->result_func)
+                call->result_func( call->result_opaque, 0 );
+            remote_call_free( call );
+            return;
+        }
+        call->buff_pos += n;
+
+        if (call->buff_pos >= call->buff_len) {
+            /* cool, we sent everything */
+            S("%s: finished sending data to %d\n", __FUNCTION__, call->to_port);
+            if (!call->quitting) {
+                    call->quitting = 1;
+                    sprintf( call->buff, "quit\n" );
+                    call->buff_len = strlen(call->buff);
+                    call->buff_pos = 0;
+            } else {
+                call->quitting = 0;
+                if (call->result_func)
+                    call->result_func( call->result_opaque, 1 );
+
+                sys_channel_on( call->channel, SYS_EVENT_READ, remote_call_event, call );
+            }
+        }
+    }
+}
+
+static RemoteCall  _the_remote_calls;
+
+#if 0
+static int
+remote_from_number( const char*  from )
+{
+    char*  end;
+    long   num = strtol( from, &end, 10 );
+
+    if (end == NULL || *end)
+        return -1;
+
+    if ((unsigned)(num - REMOTE_NUMBER_BASE) >= REMOTE_NUMBER_MAX)
+        return -1;
+
+    return (int) num;
+}
+#endif
+
+static RemoteCall
+remote_call_generic( RemoteCallType  type, const char*  to_number, int  from_port )
+{
+    int         to_port = remote_number_string_to_port(to_number);
+    RemoteCall  call;
+
+    if ( remote_number_from_port(from_port) < 0 ) {
+        D("%s: from_port value %d is not valid", __FUNCTION__, from_port);
+        return NULL;
+    }
+    if ( to_port < 0 ) {
+        D("%s: phone number '%s' is not decimal or remote", __FUNCTION__, to_number);
+        return NULL;
+    }
+    if (to_port == from_port) {
+        D("%s: trying to call self\n", __FUNCTION__);
+        return NULL;
+    }
+    call = remote_call_alloc( type, to_port, from_port );
+    if (call == NULL) {
+        return NULL;
+    }
+    remote_call_add( call, &_the_remote_calls );
+    D("%s: adding new call from port %d to port %d\n", __FUNCTION__, from_port, to_port);
+    return call;
+}
+
+
+int
+remote_call_dial( const char*       number,
+                  int               from,
+                  RemoteResultFunc  result_func,
+                  void*             result_opaque )
+{
+    RemoteCall   call = remote_call_generic( REMOTE_CALL_DIAL, number, from );
+
+    if (call != NULL) {
+        call->result_func   = result_func;
+        call->result_opaque = result_opaque;
+    }
+    return call ? 0 : -1;
+}
+
+
+void
+remote_call_other( const char*  to_number, int  from_port, RemoteCallType  type )
+{
+    remote_call_generic( type, to_number, from_port );
+}
+
+/* call this function to send a SMS to a remote emulator */
+int
+remote_call_sms( const char*   number,
+                 int           from,
+                 SmsPDU        pdu )
+{
+    RemoteCall   call = remote_call_generic( REMOTE_CALL_SMS, number, from );
+
+    if (call == NULL)
+        return -1;
+
+    if (call != NULL) {
+        if ( remote_call_set_sms_pdu( call, pdu ) < 0 ) {
+            remote_call_free(call);
+            return -1;
+        }
+    }
+    return call ? 0 : -1;
+}
+
+
+void
+remote_call_cancel( const char*  to_number, int  from_port )
+{
+    remote_call_generic( REMOTE_CALL_HANGUP, to_number, from_port );
+}
diff --git a/telephony/remote_call.h b/telephony/remote_call.h
new file mode 100644
index 0000000..c6891b8
--- /dev/null
+++ b/telephony/remote_call.h
@@ -0,0 +1,55 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _REMOTE_CALL_H
+#define _REMOTE_CALL_H
+
+#include "sms.h"
+
+/* convert a base console port into a remote phone number, -1 on error */
+extern int         remote_number_from_port( int  port );
+
+/* convert a remote phone number into a remote console port, -1 on error */
+extern int         remote_number_to_port( int  number );
+
+extern int         remote_number_string_to_port( const char*  number );
+
+typedef void   (*RemoteResultFunc)( void*  opaque, int  success );
+
+typedef enum {
+    REMOTE_CALL_DIAL = 0,
+    REMOTE_CALL_BUSY,
+    REMOTE_CALL_HANGUP,
+    REMOTE_CALL_HOLD,
+    REMOTE_CALL_ACCEPT,
+    REMOTE_CALL_SMS
+} RemoteCallType;
+
+/* call this function when you need to dial a remote voice call.
+ * this will try to connect to a remote emulator. the result function
+ * is called to indicate success or failure after some time.
+ *
+ * returns 0 if the number is to a remote phone, or -1 otherwise
+ */
+extern  int     remote_call_dial( const char*       to_number,
+                                  int               from_port,
+                                  RemoteResultFunc  result_func,
+                                  void*             result_opaque );
+
+/* call this function to send a SMS to a remote emulator */
+extern int      remote_call_sms( const char*   number, int  from_port, SmsPDU  pdu );
+
+/* call this function to indicate that you're busy to a remote caller */
+extern void     remote_call_other( const char*  to_number, int  from_port, RemoteCallType  type );
+
+extern void     remote_call_cancel( const char*  to_number, int from_port );
+
+#endif /* _REMOTE_CALL_H */
diff --git a/telephony/sim_card.c b/telephony/sim_card.c
new file mode 100644
index 0000000..a5a3249
--- /dev/null
+++ b/telephony/sim_card.c
@@ -0,0 +1,439 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sim_card.h"
+#include <string.h>
+#include <assert.h>
+
+/* set ENABLE_DYNAMIC_RECORDS to 1 to enable dynamic records
+ * for now, this is an experimental feature that needs more testing
+ */
+#define  ENABLE_DYNAMIC_RECORDS  0
+
+#define  A_SIM_PIN_SIZE  4
+#define  A_SIM_PUK_SIZE  8
+
+typedef struct ASimCardRec_ {
+    ASimStatus  status;
+    char        pin[ A_SIM_PIN_SIZE+1 ];
+    char        puk[ A_SIM_PUK_SIZE+1 ];
+    int         pin_retries;
+
+    char        out_buff[ 256 ];
+    int         out_size;
+
+} ASimCardRec;
+
+static ASimCardRec  _s_card[1];
+
+ASimCard
+asimcard_create( void )
+{
+    ASimCard  card    = _s_card;
+    card->status      = A_SIM_STATUS_READY;
+    card->pin_retries = 0;
+    strncpy( card->pin, "0000", sizeof(card->pin) );
+    strncpy( card->puk, "12345678", sizeof(card->puk) );
+    return card;
+}
+
+void
+asimcard_destroy( ASimCard  card )
+{
+    /* nothing really */
+    card=card;
+}
+
+static __inline__ int
+asimcard_ready( ASimCard  card )
+{
+    return card->status == A_SIM_STATUS_READY;
+}
+
+ASimStatus
+asimcard_get_status( ASimCard  sim )
+{
+    return sim->status;
+}
+
+void
+asimcard_set_status( ASimCard  sim, ASimStatus  status )
+{
+    sim->status = status;
+}
+
+const char*
+asimcard_get_pin( ASimCard  sim )
+{
+    return sim->pin;
+}
+
+const char*
+asimcard_get_puk( ASimCard  sim )
+{
+    return sim->puk;
+}
+
+void
+asimcard_set_pin( ASimCard  sim, const char*  pin )
+{
+    strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
+    sim->pin_retries = 0;
+}
+
+void
+asimcard_set_puk( ASimCard  sim, const char*  puk )
+{
+    strncpy( sim->puk, puk, A_SIM_PUK_SIZE );
+    sim->pin_retries = 0;
+}
+
+
+int
+asimcard_check_pin( ASimCard  sim, const char*  pin )
+{
+    if (sim->status != A_SIM_STATUS_PIN   &&
+        sim->status != A_SIM_STATUS_READY )
+        return 0;
+
+    if ( !strcmp( sim->pin, pin ) ) {
+        sim->status      = A_SIM_STATUS_READY;
+        sim->pin_retries = 0;
+        return 1;
+    }
+
+    if (sim->status != A_SIM_STATUS_READY) {
+        if (++sim->pin_retries == 3)
+            sim->status = A_SIM_STATUS_PUK;
+    }
+    return 0;
+}
+
+
+int
+asimcard_check_puk( ASimCard  sim, const char* puk, const char*  pin )
+{
+    if (sim->status != A_SIM_STATUS_PUK)
+        return 0;
+
+    if ( !strcmp( sim->puk, puk ) ) {
+        strncpy( sim->puk, puk, A_SIM_PUK_SIZE );
+        strncpy( sim->pin, pin, A_SIM_PIN_SIZE );
+        sim->status      = A_SIM_STATUS_READY;
+        sim->pin_retries = 0;
+        return 1;
+    }
+
+    if ( ++sim->pin_retries == 6 ) {
+        sim->status = A_SIM_STATUS_ABSENT;
+    }
+    return 0;
+}
+
+typedef enum {
+    SIM_FILE_DM = 0,
+    SIM_FILE_DF,
+    SIM_FILE_EF_DEDICATED,
+    SIM_FILE_EF_LINEAR,
+    SIM_FILE_EF_CYCLIC
+} SimFileType;
+
+typedef enum {
+    SIM_FILE_READ_ONLY       = (1 << 0),
+    SIM_FILE_NEED_PIN = (1 << 1),
+} SimFileFlags;
+
+/* descriptor for a known SIM File */
+#define  SIM_FILE_HEAD       \
+    SimFileType     type;    \
+    unsigned short  id;      \
+    unsigned short  flags;
+
+typedef struct {
+    SIM_FILE_HEAD
+} SimFileAnyRec, *SimFileAny;
+
+typedef struct {
+    SIM_FILE_HEAD
+    cbytes_t   data;
+    int        length;
+} SimFileEFDedicatedRec, *SimFileEFDedicated;
+
+typedef struct {
+    SIM_FILE_HEAD
+    byte_t     rec_count;
+    byte_t     rec_len;
+    cbytes_t   records;
+} SimFileEFLinearRec, *SimFileEFLinear;
+
+typedef SimFileEFLinearRec   SimFileEFCyclicRec;
+typedef SimFileEFCyclicRec*  SimFileEFCyclic;
+
+typedef union {
+    SimFileAnyRec          any;
+    SimFileEFDedicatedRec  dedicated;
+    SimFileEFLinearRec     linear;
+    SimFileEFCyclicRec     cyclic;
+} SimFileRec, *SimFile;
+
+
+#if ENABLE_DYNAMIC_RECORDS
+/* convert a SIM File descriptor into an ASCII string,
+   assumes 'dst' is NULL or properly sized.
+   return the number of chars, or -1 on error */
+static int
+sim_file_to_hex( SimFile  file, bytes_t  dst )
+{
+    SimFileType  type   = file->any.type;
+    int          result = 0;
+
+    /* see 9.2.1 in TS 51.011 */
+    switch (type) {
+        case SIM_FILE_EF_DEDICATED:
+        case SIM_FILE_EF_LINEAR:
+        case SIM_FILE_EF_CYCLIC:
+            {
+                if (dst) {
+                    int  file_size, perm;
+
+                    memcpy(dst, "0000", 4);  /* bytes 1-2 are RFU */
+                    dst += 4;
+
+                    /* bytes 3-4 are the file size */
+                    if (type == SIM_FILE_EF_DEDICATED)
+                        file_size = file->dedicated.length;
+                    else
+                        file_size = file->linear.rec_count * file->linear.rec_len;
+
+                    gsm_hex_from_short( dst, file_size );
+                    dst += 4;
+
+                    /* bytes 5-6 are the file id */
+                    gsm_hex_from_short( dst, file->any.id );
+                    dst += 4;
+
+                    /* byte 7 is the file type - always EF, i.e. 0x04 */
+                    dst[0] = '0';
+                    dst[1] = '4';
+                    dst   += 2;
+
+                    /* byte 8 is RFU, except bit 7 for cyclic files, which indicates
+                       that INCREASE is allowed. Since we don't support this yet... */
+                    dst[0] = '0';
+                    dst[1] = '0';
+                    dst   += 2;
+
+                    /* byte 9-11 are access conditions */
+                    if (file->any.flags & SIM_FILE_READ_ONLY) {
+                        if (file->any.flags & SIM_FILE_NEED_PIN)
+                            perm = 0x1a;
+                        else
+                            perm = 0x0a;
+                    } else {
+                        if (file->any.flags & SIM_FILE_NEED_PIN)
+                            perm = 0x11;
+                        else
+                            perm = 0x00;
+                    }
+                    gsm_hex_from_byte(dst, perm);
+                    memcpy( dst+2, "a0aa", 4 );
+                    dst += 6;
+
+                    /* byte 12 is file status, we don't support invalidation */
+                    dst[0] = '0';
+                    dst[1] = '0';
+                    dst   += 2;
+
+                    /* byte 13 is length of the following data, always 2 */
+                    dst[0] = '0';
+                    dst[1] = '2';
+                    dst   += 2;
+
+                    /* byte 14 is struct of EF */
+                    dst[0] = '0';
+                    if (type == SIM_FILE_EF_DEDICATED)
+                        dst[1] = '0';
+                    else if (type == SIM_FILE_EF_LINEAR)
+                        dst[1] = '1';
+                    else
+                        dst[1] = '3';
+
+                    /* byte 15 is lenght of record, or 0 */
+                    if (type == SIM_FILE_EF_DEDICATED) {
+                        dst[0] = '0';
+                        dst[1] = '0';
+                    } else
+                        gsm_hex_from_byte( dst, file->linear.rec_len );
+                }
+                result = 30;
+            }
+            break;
+
+        default:
+            result = -1;
+    }
+    return result;
+}
+
+
+static const byte_t  _const_spn_cphs[20] = {
+    0x41, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static const byte_t  _const_voicemail_cphs[1] = {
+    0x55
+};
+
+static const byte_t  _const_iccid[10] = {
+    0x98, 0x10, 0x14, 0x30, 0x12, 0x11, 0x81, 0x15, 0x70, 0x02
+};
+
+static const byte_t  _const_cff_cphs[1] = {
+    0x55
+};
+
+static SimFileEFDedicatedRec  _const_files_dedicated[] =
+{
+    { SIM_FILE_EF_DEDICATED, 0x6f14, SIM_FILE_READ_ONLY | SIM_FILE_NEED_PIN,
+      _const_spn_cphs, sizeof(_const_spn_cphs) },
+
+    { SIM_FILE_EF_DEDICATED, 0x6f11, SIM_FILE_NEED_PIN,
+      _const_voicemail_cphs, sizeof(_const_voicemail_cphs) },
+
+    { SIM_FILE_EF_DEDICATED, 0x2fe2, SIM_FILE_READ_ONLY,
+      _const_iccid, sizeof(_const_iccid) },
+
+    { SIM_FILE_EF_DEDICATED, 0x6f13, SIM_FILE_NEED_PIN,
+      _const_cff_cphs, sizeof(_const_cff_cphs) },
+
+    { 0, 0, 0, NULL, 0 }  /* end of list */
+};
+#endif /* ENABLE_DYNAMIC_RECORDS */
+
+const char*
+asimcard_io( ASimCard  sim, const char*  cmd )
+{
+    int  nn;
+#if ENABLE_DYNAMIC_RECORDS
+    int  command, id, p1, p2, p3;
+#endif
+    static const struct { const char*  cmd; const char*  answer; } answers[] =
+    {
+        { "+CRSM=192,28436,0,0,15", "+CRSM: 144,0,000000146f1404001aa0aa01020000" },
+        { "+CRSM=176,28436,0,0,20", "+CRSM: 144,0,416e64726f6964ffffffffffffffffffffffffff" },
+
+        { "+CRSM=192,28433,0,0,15", "+CRSM: 144,0,000000016f11040011a0aa01020000" },
+        { "+CRSM=176,28433,0,0,1", "+CRSM: 144,0,55" },
+
+        { "+CRSM=192,12258,0,0,15", "+CRSM: 144,0,0000000a2fe204000fa0aa01020000" },
+        { "+CRSM=176,12258,0,0,10", "+CRSM: 144,0,98101430121181157002" },
+
+        { "+CRSM=192,28435,0,0,15", "+CRSM: 144,0,000000016f13040011a0aa01020000" },
+        { "+CRSM=176,28435,0,0,1",  "+CRSM: 144,0,55" },
+
+        { "+CRSM=192,28472,0,0,15", "+CRSM: 144,0,0000000f6f3804001aa0aa01020000" },
+        { "+CRSM=176,28472,0,0,15", "+CRSM: 144,0,ff30ffff3c003c03000c0000f03f00" },
+
+        { "+CRSM=192,28617,0,0,15", "+CRSM: 144,0,000000086fc9040011a0aa01020104" },
+        { "+CRSM=178,28617,1,4,4",  "+CRSM: 144,0,01000000" },
+
+        { "+CRSM=192,28618,0,0,15", "+CRSM: 144,0,0000000a6fca040011a0aa01020105" },
+        { "+CRSM=178,28618,1,4,5",  "+CRSM: 144,0,0000000000" },
+
+        { "+CRSM=192,28589,0,0,15", "+CRSM: 144,0,000000046fad04000aa0aa01020000" },
+        { "+CRSM=176,28589,0,0,4",  "+CRSM: 144,0,00000003" },
+
+        { "+CRSM=192,28438,0,0,15", "+CRSM: 144,0,000000026f1604001aa0aa01020000" },
+        { "+CRSM=176,28438,0,0,2",  "+CRSM: 144,0,0233" },
+
+        { "+CRSM=192,28486,0,0,15", "+CRSM: 148,4" },
+        { "+CRSM=192,28621,0,0,15", "+CRSM: 148,4" },
+
+        { "+CRSM=192,28613,0,0,15", "+CRSM: 144,0,000000f06fc504000aa0aa01020118" },
+        { "+CRSM=178,28613,1,4,24", "+CRSM: 144,0,43058441aa890affffffffffffffffffffffffffffffffff" },
+
+        { "+CRSM=192,28480,0,0,15", "+CRSM: 144,0,000000806f40040011a0aa01020120" },
+        { "+CRSM=178,28480,1,4,32", "+CRSM: 144,0,ffffffffffffffffffffffffffffffffffff07815155258131f5ffffffffffff" },
+
+        { "+CRSM=192,28615,0,0,15", "+CRSM: 144,0,000000406fc7040011a0aa01020120" },
+        { "+CRSM=178,28615,1,4,32", "+CRSM: 144,0,566f6963656d61696cffffffffffffffffff07915155125740f9ffffffffffff" },
+
+        { NULL, NULL }
+    };
+
+    assert( memcmp( cmd, "+CRSM=", 6 ) == 0 );
+
+#if ENABLE_DYNAMIC_RECORDS
+    if ( sscanf(cmd, "+CRSM=%d,%d,%d,%d,%d", &command, &id, &p1, &p2, &p3) == 5 ) {
+        switch (command) {
+            case A_SIM_CMD_GET_RESPONSE:
+                {
+                    const SimFileEFDedicatedRec*  file = _const_files_dedicated;
+
+                    assert(p1 == 0 && p2 == 0 && p3 == 15);
+
+                    for ( ; file->id != 0; file++ ) {
+                        if (file->id == id) {
+                            int    count;
+                            char*  out = sim->out_buff;
+                            strcpy( out, "+CRSM: 144,0," );
+                            out  += strlen(out);
+                            count = sim_file_to_hex( (SimFile) file, out );
+                            if (count < 0)
+                                return "ERROR: INTERNAL SIM ERROR";
+                            out[count] = 0;
+                            return sim->out_buff;
+                        }
+                    }
+                    break;
+                }
+
+            case A_SIM_CMD_READ_BINARY:
+                {
+                    const SimFileEFDedicatedRec*  file = _const_files_dedicated;
+
+                    assert(p1 == 0 && p2 == 0);
+
+                    for ( ; file->id != 0; file++ ) {
+                        if (file->id == id) {
+                            char*  out = sim->out_buff;
+
+                            if (p3 > file->length)
+                                return "ERROR: BINARY LENGTH IS TOO LONG";
+
+                            strcpy( out, "+CRSM: 144,0," );
+                            out  += strlen(out);
+                            gsm_hex_from_bytes( out, file->data, p3 );
+                            out[p3*2] = 0;
+                            return sim->out_buff;
+                        }
+                    }
+                    break;
+                }
+
+            case A_SIM_CMD_READ_RECORD:
+                break;
+
+            default:
+                return "ERROR: UNSUPPORTED SIM COMMAND";
+        }
+    }
+#endif
+
+    for (nn = 0; answers[nn].cmd != NULL; nn++) {
+        if ( !strcmp( answers[nn].cmd, cmd ) ) {
+            return answers[nn].answer;
+        }
+    }
+    return "ERROR: BAD COMMAND";
+}
+
diff --git a/telephony/sim_card.h b/telephony/sim_card.h
new file mode 100644
index 0000000..af78237
--- /dev/null
+++ b/telephony/sim_card.h
@@ -0,0 +1,54 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_sim_card_h
+#define _android_sim_card_h
+
+#include "gsm.h"
+
+typedef struct ASimCardRec_*    ASimCard;
+
+extern ASimCard  asimcard_create( void );
+extern void      asimcard_destroy( ASimCard  sim );
+
+typedef enum {
+    A_SIM_STATUS_ABSENT = 0,
+    A_SIM_STATUS_NOT_READY,
+    A_SIM_STATUS_READY,
+    A_SIM_STATUS_PIN,
+    A_SIM_STATUS_PUK,
+    A_SIM_STATUS_NETWORK_PERSONALIZATION
+} ASimStatus;
+
+extern ASimStatus  asimcard_get_status( ASimCard  sim );
+extern void        asimcard_set_status( ASimCard  sim, ASimStatus  status );
+
+extern const char*  asimcard_get_pin( ASimCard  sim );
+extern const char*  asimcard_get_puk( ASimCard  sim );
+extern void         asimcard_set_pin( ASimCard  sim, const char*  pin );
+extern void         asimcard_set_puk( ASimCard  sim, const char*  puk );
+
+extern int         asimcard_check_pin( ASimCard  sim, const char*  pin );
+extern int         asimcard_check_puk( ASimCard  sim, const char*  puk, const char*  pin );
+
+/* Restricted SIM Access command, as defined by 8.18 of 3GPP 27.007 */
+typedef enum {
+    A_SIM_CMD_READ_BINARY = 176,
+    A_SIM_CMD_READ_RECORD = 178,
+    A_SIM_CMD_GET_RESPONSE = 192,
+    A_SIM_CMD_UPDATE_BINARY = 214,
+    A_SIM_CMD_UPDATE_RECORD = 220,
+    A_SIM_CMD_STATUS = 242
+} ASimCommand;
+
+extern const char*  asimcard_io( ASimCard  sim, const char*  cmd );
+
+#endif /* _android_sim_card_h */
diff --git a/telephony/simulator.c b/telephony/simulator.c
new file mode 100644
index 0000000..43f267a
--- /dev/null
+++ b/telephony/simulator.c
@@ -0,0 +1,195 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "android_modem.h"
+#include "sysdeps.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#define  DEFAULT_PORT  6703
+
+static AModem  modem;
+
+typedef struct {
+    SysChannel   channel;
+    char         in_buff[ 128 ];
+    int          in_pos;
+
+    char         out_buff[ 128 ];
+    int          out_pos;
+    int          out_size;
+} ClientRec, *Client;
+
+static Client
+client_alloc( SysChannel  channel )
+{
+    Client  client = calloc( sizeof(*client), 1 );
+
+    client->channel = channel;
+    return client;
+}
+
+static void
+client_free( Client  client )
+{
+    sys_channel_close( client->channel );
+    client->channel = NULL;
+    free( client );
+}
+
+static void
+client_append( Client  client, const char*  str, int len );
+
+static void
+dump_line( const char*  line, const char*  prefix )
+{
+    if (prefix)
+        printf( "%s", prefix );
+
+    for ( ; *line; line++ ) {
+        int  c = line[0];
+
+        if (c >= 32 && c < 127)
+            printf( "%c", c );
+        else if (c == '\r')
+            printf( "<CR>" );
+        else if (c == '\n')
+            printf( "<LF>" );
+        else
+            printf( "\\x%02x", c );
+    }
+    printf( "\n" );
+}
+
+static void
+client_handle_line( Client  client, const char*  cmd )
+{
+    const char*  answer;
+
+    dump_line( cmd, "<< " );
+    answer = amodem_send( modem, cmd );
+    if (answer == NULL)  /* not an AT command, ignored */ {
+        printf( "-- NO ANSWER\n" );
+        return;
+    }
+
+    dump_line( answer, ">> " );
+    client_append( client, answer, -1 );
+    client_append( client, "\r", 1 );
+}
+
+static void
+client_handler( void* _client, int  events )
+{
+    Client  client = _client;
+
+    if (events & SYS_EVENT_READ) {
+        int  ret;
+        /* read into buffer, one character at a time */
+        ret = sys_channel_read( client->channel, client->in_buff + client->in_pos, 1 );
+        if (ret != 1) {
+            fprintf(stderr, "client %p could not read byte, result = %d, error: %s\n",
+                    client, ret, strerror(errno) );
+            goto ExitClient;
+        }
+        if (client->in_buff[client->in_pos] == '\r' ||
+            client->in_buff[client->in_pos] == '\n' ) {
+            const char*  cmd = client->in_buff;
+            client->in_buff[client->in_pos] = 0;
+
+            if (client->in_pos > 0) {
+                client_handle_line( client, cmd );
+                client->in_pos = 0;
+            }
+        } else
+            client->in_pos += 1;
+    }
+
+    if (events & SYS_EVENT_WRITE) {
+        int  ret;
+        /* write from output buffer, one char at a time */
+        ret = sys_channel_write( client->channel, client->out_buff + client->out_pos, 1 );
+        if (ret != 1) {
+            fprintf(stderr, "client %p could not write byte, result = %d, error: %s\n",
+                    client, ret, strerror(errno) );
+            goto ExitClient;
+        }
+        client->out_pos += 1;
+        if (client->out_pos == client->out_size) {
+            client->out_size = 0;
+            client->out_pos  = 0;
+            /* we don't need to write */
+            sys_channel_on( client->channel, SYS_EVENT_READ, client_handler, client );
+        }
+    }
+    return;
+
+ExitClient:
+    printf( "client %p exiting\n", client );
+    client_free( client );
+}
+
+
+static void
+client_append( Client  client, const char*  str, int len )
+{
+    int  avail;
+
+    if (len < 0)
+        len = strlen(str);
+
+    avail = sizeof(client->out_buff) - client->out_size;
+    if (len > avail)
+        len = avail;
+
+    memcpy( client->out_buff + client->out_size, str, len );
+    if (client->out_size == 0) {
+        sys_channel_on( client->channel, SYS_EVENT_READ | SYS_EVENT_WRITE, client_handler, client );
+    }
+    client->out_size += len;
+}
+
+
+static void
+accept_func( void*  _server, int  events )
+{
+    SysChannel  server  = _server;
+    SysChannel  handler;
+    Client      client;
+
+    printf( "connection accepted for server channel, getting handler socket\n" );
+    handler = sys_channel_create_tcp_handler( server );
+    client  = client_alloc( handler );
+    printf( "got one. created client %p\n", client );
+
+    events=events;
+    sys_channel_on( handler, SYS_EVENT_READ, client_handler, client );
+}
+
+
+int  main( void )
+{
+    int  port = DEFAULT_PORT;
+    SysChannel  server;
+
+    sys_main_init();
+    modem = amodem_create( NULL, NULL );
+
+    server = sys_channel_create_tcp_server( port );
+    printf( "GSM simulator listening on local port %d\n", port );
+
+    sys_channel_on( server, SYS_EVENT_READ, accept_func, server );
+    sys_main_loop();
+    printf( "GSM simulator exiting\n" );
+    return 0;
+}
diff --git a/telephony/sms.c b/telephony/sms.c
new file mode 100644
index 0000000..448eab4
--- /dev/null
+++ b/telephony/sms.c
@@ -0,0 +1,1655 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sms.h"
+#include "gsm.h"
+#include <memory.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define  DEBUG  1
+
+#if 1
+#  include "android/utils/debug.h"
+#  define  D_ACTIVE  VERBOSE_CHECK(modem)
+#else
+#  define  D_ACTIVE  DEBUG
+#endif
+
+#if DEBUG
+#  define  D(...)  VERBOSE_PRINT(modem,__VA_ARGS__)
+#else
+#  define  D(...)  ((void)0)
+#endif
+
+/* maximum number of data bytes in a SMS data message */
+#define  MAX_USER_DATA_BYTES   140
+
+/* maximum number of 7-bit septets in a SMS text message */
+#define  MAX_USER_DATA_SEPTETS  160
+
+/* size of the user data header in bytes */
+#define  USER_DATA_HEADER_SIZE   6
+
+/** MESSAGE TEXT
+ **/
+int
+sms_utf8_from_message_str( const char*  str, int  strlen, unsigned char*  utf8, int  utf8len )
+{
+    cbytes_t  p       = (cbytes_t)str;
+    cbytes_t  end     = p + strlen;
+    int       count   = 0;
+    int       escaped = 0;
+
+    while (p < end)
+    {
+        int  c = p[0];
+
+        /* read the value from the string */
+        p += 1;
+        if (c >= 128) {
+            if ((c & 0xe0) == 0xc0)
+                c &= 0x1f;
+            else if ((c & 0xf0) == 0xe0)
+                c &= 0x0f;
+            else
+                c &= 0x07;
+            p++;
+            while (p < end && (p[0] & 0xc0) == 0x80) {
+                c = (c << 6) | (p[0] & 0x3f);
+                p++;
+            }
+        }
+        if (escaped) {
+            switch (c) {
+                case '\\':
+                    break;
+                case 'n':  /* \n is line feed */
+                    c = 10;
+                    break;
+
+                case 'x':  /* \xNN, where NN is a 2-digit hexadecimal value */
+                    if (p+2 > end)
+                        return -1;
+                    c = gsm_hex2_to_byte( (const char*)p );
+                    if (c < 0)
+                        return -1;
+                    p += 2;
+                    break;
+
+                case 'u':  /* \uNNNN where NNNN is a 4-digiti hexadecimal value */
+                    if (p + 4 > end)
+                        return -1;
+                    c = gsm_hex4_to_short( (const char*)p );
+                    if (c < 0)
+                        return -1;
+                    p += 4;
+                    break;
+
+                default:  /* invalid escape, return -1 */
+                    return -1;
+            }
+            escaped = 0;
+        }
+        else if (c == '\\')
+        {
+            escaped = 1;
+            continue;
+        }
+
+        /* now, try to write it to the destination */
+        if (c < 128) {
+            if (count < utf8len)
+                utf8[count] = (byte_t) c;
+            count += 1;
+        }
+        else if (c < 0x800) {
+            if (count < utf8len)
+                utf8[count]   = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
+            if (count+1 < utf8len)
+                utf8[count+1] = (byte_t)(0x80 | (c & 0x3f));
+            count += 2;
+        }
+        else {
+            if (count < utf8len)
+                utf8[count]   = (byte_t)(0xc0 | ((c >> 12) & 0xf));
+            if (count+1 < utf8len)
+                utf8[count+1] = (byte_t)(0x80 | ((c >> 6) & 0x3f));
+            if (count+2 < utf8len)
+                utf8[count+2] = (byte_t)(0x80 | (c & 0x3f));
+            count += 3;
+        }
+    }
+
+    if (escaped)   /* bad final escape */
+        return -1;
+
+    return count;
+}
+
+/* to convert utf-8 to a message string, we only need to deal with control characters
+ * and that's it */
+int  sms_utf8_to_message_str( const unsigned char*  utf8, int  utf8len, char*  str, int  strlen )
+{
+    cbytes_t  p = utf8;
+    cbytes_t  end = p + utf8len;
+    int       count   = 0;
+
+    while (p < end)
+    {
+        int  c      = p[0];
+        int  escape = 0;
+
+        /* read the value from the string */
+        p += 1;
+        if (c >= 128) {
+            if ((c & 0xe0) == 0xc0)
+                c &= 0x1f;
+            else if ((c & 0xf0) == 0xe0)
+                c &= 0x0f;
+            else
+                c &= 0x07;
+            p++;
+            while (p < end && (p[0] & 0xc0) == 0x80) {
+                c = (c << 6) | (p[0] & 0x3f);
+                p++;
+            }
+        }
+
+        if (c < ' ') {
+            escape = 1;
+            if (c == '\n') {
+                c      = 'n';
+                escape = 2;
+            }
+        }
+        else if (c == '\\')
+            escape = 2;
+
+        switch (escape) {
+            case 0:
+                if (c < 128) {
+                    if (count < strlen)
+                        str[count] = (char) c;
+                    count += 1;
+                }
+                else if (c < 0x800) {
+                    if (count < strlen)
+                        str[count]   = (byte_t)(0xc0 | ((c >> 6) & 0x1f));
+                    if (count+1 < strlen)
+                        str[count+1] = (byte_t)(0x80 | (c & 0x3f));
+                    count += 2;
+                }
+                else {
+                    if (count < strlen)
+                        str[count]   = (byte_t)(0xc0 | ((c >> 12) & 0xf));
+                    if (count+1 < strlen)
+                        str[count+1] = (byte_t)(0x80 | ((c >> 6) & 0x3f));
+                    if (count+2 < strlen)
+                        str[count+2] = (byte_t)(0x80 | (c & 0x3f));
+                    count += 3;
+                }
+                break;
+
+            case 1:
+                if (count+3 < strlen) {
+                    str[count+0] = '\\';
+                    str[count+1] = 'x';
+                    gsm_hex_from_byte(str + count + 2, c);
+                }
+                count += 4;
+                break;
+
+            default:
+                if (count+2 < strlen) {
+                    str[count+0] = '\\';
+                    str[count+1] = (char) c;
+                }
+                count += 2;
+        }
+    }
+    return count;
+}
+
+
+/** TIMESTAMPS
+ **/
+void
+sms_timestamp_now( SmsTimeStamp  stamp )
+{
+    time_t     now_time = time(NULL);
+    struct tm  gm       = *(gmtime(&now_time));
+    struct tm  local    = *(localtime(&now_time));
+    int        tzdiff   = 0;
+
+    stamp->data[0] = gsm_int_to_bcdi( local.tm_year % 100 );
+    stamp->data[1] = gsm_int_to_bcdi( local.tm_mon+1 );
+    stamp->data[2] = gsm_int_to_bcdi( local.tm_mday );
+    stamp->data[3] = gsm_int_to_bcdi( local.tm_hour );
+    stamp->data[4] = gsm_int_to_bcdi( local.tm_min );
+    stamp->data[5] = gsm_int_to_bcdi( local.tm_sec );
+
+    tzdiff = (local.tm_hour*4 + local.tm_min/15) - (gm.tm_hour*4 + gm.tm_min/15);
+    if (local.tm_yday > gm.tm_yday)
+        tzdiff += 24*4;
+    else if (local.tm_yday < gm.tm_yday)
+        tzdiff -= 24*4;
+
+    stamp->data[6] = gsm_int_to_bcdi( tzdiff >= 0 ? tzdiff : -tzdiff );
+    if (tzdiff < 0)
+        stamp->data[6] |= 0x08;
+}
+
+int
+sms_timestamp_to_tm( SmsTimeStamp  stamp, struct tm*  tm )
+{
+    int  tzdiff;
+
+    tm->tm_year = gsm_int_from_bcdi( stamp->data[0] );
+    if (tm->tm_year < 50)
+        tm->tm_year += 100;
+    tm->tm_mon  = gsm_int_from_bcdi( stamp->data[1] ) -1;
+    tm->tm_mday = gsm_int_from_bcdi( stamp->data[2] );
+    tm->tm_hour = gsm_int_from_bcdi( stamp->data[3] );
+    tm->tm_min  = gsm_int_from_bcdi( stamp->data[4] );
+    tm->tm_sec  = gsm_int_from_bcdi( stamp->data[5] );
+
+    tm->tm_isdst = -1;
+
+    tzdiff = gsm_int_from_bcdi( stamp->data[6] & 0xf7 );
+    if (stamp->data[6] & 0x8)
+        tzdiff = -tzdiff;
+
+    return tzdiff;
+}
+
+static void
+gsm_rope_add_timestamp( GsmRope  rope, const SmsTimeStampRec*  ts )
+{
+    gsm_rope_add( rope, ts->data, 7 );
+}
+
+
+/** SMS ADDRESSES
+ **/
+
+int
+sms_address_from_str( SmsAddress  address, const char*  src, int  srclen )
+{
+    const char*  end   = src + srclen;
+    int          shift = 0, len = 0;
+    bytes_t      data = address->data;
+
+    address->len = 0;
+    address->toa = 0x81;
+
+    if (src >= end)
+        return -1;
+
+    if ( src[0] == '+' ) {
+        address->toa = 0x91;
+        if (++src == end)
+            goto Fail;
+    }
+
+    memset( address->data, 0, sizeof(address->data) );
+
+    shift = 0;
+
+    while (src < end) {
+        int  c = *src++ - '0';
+
+        if ( (unsigned)c >= 10 ||
+              data >= address->data + sizeof(address->data) )
+            goto Fail;
+
+        data[0] |= c << shift;
+        len   += 1;
+        shift += 4;
+        if (shift == 8) {
+            shift = 0;
+            data += 1;
+        }
+    }
+    if (shift != 0)
+        data[0] |= 0xf0;
+
+    address->len = len;
+    return 0;
+
+Fail:
+    return -1;
+}
+
+int
+sms_address_to_str( SmsAddress address, char*  str, int  strlen )
+{
+    static const char  dialdigits[16] = "0123456789*#,N%";
+    int                n, count = 0;
+
+    if (address->toa == 0x91) {
+        if (count < strlen)
+            str[count] = '+';
+        count++;
+    }
+    for (n = 0; n < address->len; n += 2)
+    {
+        int   c = address->data[n/2];
+
+        if (count < strlen)
+            str[count] = dialdigits[c & 0xf];
+        count += 1;
+
+        if (n+1 > address->len)
+            break;
+
+        if (count < strlen)
+            str[count] = dialdigits[(c >> 4) & 0xf];
+        count += 1;
+    }
+    return count;
+}
+
+int
+sms_address_from_bytes( SmsAddress  address, const unsigned char*  buf, int  buflen )
+{
+    int   len = sizeof(address->data), num_digits;
+
+    if (buflen < 2)
+        return -1;
+
+    address->len = num_digits = buf[0];
+    address->toa = buf[1];
+
+    len = (num_digits+1)/2;
+    if ( len > sizeof(address->data) )
+        return -1;
+
+    memcpy( address->data, buf+2, len );
+    return 0;
+}
+
+int
+sms_address_to_bytes( SmsAddress  address, unsigned char*  buf, int  bufsize )
+{
+    int  len = (address->len + 1)/2 + 2;
+
+    if (buf == NULL)
+        bufsize = 0;
+
+    if (bufsize < 1) goto Exit;
+    buf[0] = address->len;
+
+    if (bufsize < 2) goto Exit;
+    buf[1] = address->toa;
+
+    buf     += 2;
+    bufsize -= 2;
+    if (bufsize > len-2)
+        bufsize = len - 2;
+
+    memcpy( buf, address->data, bufsize );
+Exit:
+    return len;
+}
+
+int
+sms_address_from_hex  ( SmsAddress  address, const char*  hex, int  hexlen )
+{
+    const char*  hexend = hex + hexlen;
+    int          nn, len, num_digits;
+
+    if (hexlen < 4)
+        return -1;
+
+    address->len = num_digits = gsm_hex2_to_byte( hex );
+    address->toa = gsm_hex2_to_byte( hex+2 );
+    hex += 4;
+
+    len = (num_digits + 1)/2;
+    if (hex + len*2 > hexend)
+        return -1;
+
+    for ( nn = 0; nn < len; nn++ )
+        address->data[nn] = gsm_hex2_to_byte( hex + nn*2 );
+
+    return 0;
+}
+
+int
+sms_address_to_hex    ( SmsAddress  address, char*   hex, int  hexlen )
+{
+    int  len = (address->len + 1)/2 + 2;
+    int  nn;
+
+    if (hex == NULL)
+        hexlen = 0;
+
+    if (hexlen < 2) goto Exit;
+    gsm_hex_from_byte( hex, address->len );
+    if (hexlen < 4) goto Exit;
+    gsm_hex_from_byte( hex+2, address->toa );
+    hex    += 4;
+    hexlen -= 4;
+    if ( hexlen > 2*(len - 2) )
+        hexlen = (len - 2)/2;
+
+    for ( nn = 0; nn < hexlen; nn += 2 )
+        gsm_hex_from_byte( hex+nn, address->data[nn/2] );
+
+Exit:
+    return len*2;
+}
+
+static void
+gsm_rope_add_address( GsmRope  rope, const SmsAddressRec*  addr )
+{
+    gsm_rope_add_c( rope, addr->len );
+    gsm_rope_add_c( rope, addr->toa );
+    gsm_rope_add( rope, addr->data, (addr->len+1)/2 );
+    if (addr->len & 1) {
+        if (!rope->error && rope->data != NULL)
+            rope->data[ rope->pos-1 ] |= 0xf0;
+    }
+}
+
+static int
+sms_address_eq( const SmsAddressRec*  addr1, const SmsAddressRec*  addr2 )
+{
+    if ( addr1->toa != addr2->toa ||
+         addr1->len != addr2->len )
+        return 0;
+
+    return ( !memcmp( addr1->data, addr2->data, addr1->len ) );
+}
+
+/** SMS PARSER
+ **/
+static int
+sms_get_byte( cbytes_t  *pcur, cbytes_t  end )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+
+    if (cur < end) {
+        result = cur[0];
+        *pcur  = cur + 1;
+    }
+    return result;
+}
+
+/* parse a service center address, returns -1 in case of error */
+static int
+sms_get_sc_address( cbytes_t   *pcur,
+                    cbytes_t    end,
+                    SmsAddress  address )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+
+    if (cur < end) {
+        int  len = cur[0];
+        int  dlen, adjust = 0;
+
+        cur += 1;
+
+        if (len == 0) {   /* empty address */
+            address->len = 0;
+            address->toa = 0x00;
+            result       = 0;
+            goto Exit;
+        }
+
+        if (cur + len > end) {
+            goto Exit;
+        }
+
+        address->toa = *cur++;
+        len         -= 1;
+        result       = 0;
+
+        for (dlen = 0; dlen < len; dlen+=1)
+        {
+            int  c = cur[dlen];
+            int  v;
+
+            adjust = 0;
+            if (dlen >= sizeof(address->data)) {
+                result = -1;
+                break;
+            }
+
+            v = (c & 0xf);
+            if (v >= 0xe)
+                break;
+
+            adjust              = 1;
+            address->data[dlen] = (byte_t) c;
+
+            v = (c >> 4) & 0xf;
+            if (v >= 0xe) {
+                break;
+            }
+        }
+        address->len = 2*dlen + adjust;
+    }
+Exit:
+    if (!result)
+        *pcur = cur;
+
+    return result;
+}
+
+static int
+sms_skip_sc_address( cbytes_t   *pcur,
+                     cbytes_t    end )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       len;
+
+    if (cur >= end)
+        goto Exit;
+
+    len  = cur[0];
+    cur += 1 + len;
+    if (cur > end)
+        goto Exit;
+
+    *pcur  = cur;
+    result = 0;
+Exit:
+    return result;
+}
+
+/* parse a sender/receiver address, returns -1 in case of error */
+static int
+sms_get_address( cbytes_t   *pcur,
+                 cbytes_t    end,
+                 SmsAddress  address )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       len, dlen;
+
+    if (cur >= end)
+        goto Exit;
+
+    dlen = *cur++;
+
+    if (dlen == 0) {
+        address->len = 0;
+        address->toa = 0;
+        result       = 0;
+        goto Exit;
+    }
+
+    if (cur + 1 + (dlen+1)/2 > end)
+        goto Exit;
+
+    address->len = dlen;
+    address->toa = *cur++;
+
+    len = (dlen + 1)/2;
+    if (len > sizeof(address->data))
+        goto Exit;
+
+    memcpy( address->data, cur, len );
+    cur   += len;
+    result = 0;
+
+Exit:
+    if (!result)
+        *pcur = cur;
+
+    return result;
+}
+
+static int
+sms_skip_address( cbytes_t   *pcur,
+                  cbytes_t    end  )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       dlen;
+
+    if (cur + 2 > end)
+        goto Exit;
+
+    dlen = cur[0];
+    cur += 2 + (dlen + 1)/2;
+    if (cur > end)
+        goto Exit;
+
+    result = 0;
+Exit:
+    return result;
+}
+
+/* parse a service center timestamp */
+static int
+sms_get_timestamp( cbytes_t     *pcur,
+                   cbytes_t      end,
+                   SmsTimeStamp  ts )
+{
+    cbytes_t  cur = *pcur;
+
+    if (cur + 7 > end)
+        return -1;
+
+    memcpy( ts->data, cur, 7 );
+    *pcur = cur + 7;
+    return 0;
+}
+
+static int
+sms_skip_timestamp( cbytes_t  *pcur,
+                    cbytes_t   end )
+{
+    cbytes_t  cur = *pcur;
+
+    if (cur + 7 > end)
+        return -1;
+
+    *pcur = cur + 7;
+    return 0;
+}
+
+
+static int
+sms_skip_validity_period( cbytes_t  *pcur,
+                          cbytes_t   end,
+                          int        mtiByte )
+{
+    cbytes_t  cur = *pcur;
+
+    switch ((mtiByte >> 3) & 3) {
+        case 1:  /* relative format */
+            cur += 1;
+            break;
+
+        case 2:  /* enhanced format */
+        case 3:  /* absolute format */
+            cur += 7;
+    }
+    if (cur > end)
+        return -1;
+
+    *pcur = cur;
+    return 0;
+}
+
+/** SMS PDU
+ **/
+
+typedef struct SmsPDURec {
+    bytes_t  base;
+    bytes_t  end;
+    bytes_t  tpdu;
+} SmsPDURec;
+
+void
+smspdu_free( SmsPDU  pdu )
+{
+    if (pdu) {
+        free( pdu->base );
+        pdu->base = NULL;
+        pdu->end  = NULL;
+        pdu->tpdu = NULL;
+    }
+}
+
+SmsPduType
+smspdu_get_type( SmsPDU  pdu )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte(&data, end);
+
+    switch (mtiByte & 3) {
+        case 0:  return SMS_PDU_DELIVER;
+        case 1:  return SMS_PDU_SUBMIT;
+        case 2:  return SMS_PDU_STATUS_REPORT;
+        default: return SMS_PDU_INVALID;
+    }
+}
+
+int
+smspdu_get_sender_address( SmsPDU  pdu, SmsAddress  address )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte(&data, end);
+
+    switch (mtiByte & 3) {
+        case 0: /* SMS_PDU_DELIVER; */
+            return sms_get_sc_address( &data, end, address );
+
+        default: return -1;
+    }
+}
+
+int
+smspdu_get_sc_timestamp( SmsPDU  pdu, SmsTimeStamp  ts )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+
+    switch (mtiByte & 3) {
+        case 0:  /* SMS_PDU_DELIVER */
+            {
+                SmsAddressRec  address;
+
+                if ( sms_get_sc_address( &data, end, &address ) < 0 )
+                    return -1;
+
+                data += 2;  /* skip protocol identifer + coding scheme */
+
+                return sms_get_timestamp( &data, end, ts );
+            }
+
+        default: return -1;
+    }
+}
+
+int
+smspdu_get_receiver_address( SmsPDU  pdu, SmsAddress  address )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+
+    switch (mtiByte & 3) {
+        case 1:  /* SMS_PDU_SUBMIT */
+            {
+                data += 1;  /* skip message reference */
+                return sms_get_address( &data, end, address );
+            }
+
+        default: return -1;
+    }
+}
+
+typedef enum {
+    SMS_CODING_SCHEME_UNKNOWN = 0,
+    SMS_CODING_SCHEME_GSM7,
+    SMS_CODING_SCHEME_UCS2
+
+} SmsCodingScheme;
+
+/* see TS 23.038 Section 5 for details */
+static SmsCodingScheme
+sms_get_coding_scheme( cbytes_t  *pcur,
+                       cbytes_t   end )
+{
+    cbytes_t  cur = *pcur;
+    int       dataCoding;
+
+    if (cur >= end)
+        return SMS_CODING_SCHEME_UNKNOWN;
+
+    dataCoding = *cur++;
+    *pcur      = cur;
+
+    switch (dataCoding >> 4) {
+        case 0x00:
+        case 0x02:
+        case 0x03:
+            return SMS_CODING_SCHEME_GSM7;
+
+        case 0x01:
+            if (dataCoding == 0x10) return SMS_CODING_SCHEME_GSM7;
+            if (dataCoding == 0x11) return SMS_CODING_SCHEME_UCS2;
+            break;
+
+        case 0x04: case 0x05: case 0x06: case 0x07:
+            if (dataCoding & 0x20)           return SMS_CODING_SCHEME_UNKNOWN; /* compressed 7-bits */
+            if (((dataCoding >> 2) & 3) == 0) return SMS_CODING_SCHEME_GSM7;
+            if (((dataCoding >> 2) & 3) == 2) return SMS_CODING_SCHEME_UCS2;
+            break;
+
+        case 0xF:
+            if (!(dataCoding & 4)) return SMS_CODING_SCHEME_GSM7;
+            break;
+    }
+    return SMS_CODING_SCHEME_UNKNOWN;
+}
+
+
+/* see TS 23.040 section 9.2.3.24 for details */
+static int
+sms_get_text_utf8( cbytes_t        *pcur,
+                   cbytes_t         end,
+                   int              hasUDH,
+                   SmsCodingScheme  coding,
+                   GsmRope          rope )
+{
+    cbytes_t  cur    = *pcur;
+    int       result = -1;
+    int       len;
+
+    if (cur >= end)
+        goto Exit;
+
+    len = *cur++;
+
+    /* skip user data header if any */
+    if ( hasUDH )
+    {
+        int  hlen;
+
+        if (cur >= end)
+            goto Exit;
+
+        hlen = *cur++;
+        if (cur + hlen > end)
+            goto Exit;
+
+        cur += hlen;
+
+        if (coding == SMS_CODING_SCHEME_GSM7)
+            len -= 2*(hlen+1);
+        else
+            len -= hlen+1;
+
+        if (len < 0)
+            goto Exit;
+    }
+
+    /* switch the user data header if any */
+    if (coding == SMS_CODING_SCHEME_GSM7)
+    {
+        int  count = utf8_from_gsm7( cur, 0, len, NULL );
+
+        if (rope != NULL)
+        {
+            bytes_t  dst = gsm_rope_reserve( rope, count );
+            if (dst != NULL)
+                utf8_from_gsm7( cur, 0, len, dst );
+        }
+        cur += (len+1)/2;
+    }
+    else if (coding == SMS_CODING_SCHEME_UCS2)
+    {
+        int  count = ucs2_to_utf8( cur, len/2, NULL );
+
+        if (rope != NULL)
+        {
+            bytes_t  dst = gsm_rope_reserve( rope, count );
+            if (dst != NULL)
+                ucs2_to_utf8( cur, len/2, dst );
+        }
+        cur += len;
+    }
+    result = 0;
+
+Exit:
+    if (!result)
+        *pcur = cur;
+
+    return result;
+}
+
+/* get the message embedded in a SMS PDU as a utf8 byte array, returns the length of the message in bytes */
+/* or -1 in case of error */
+int
+smspdu_get_text_message( SmsPDU  pdu, unsigned char*  utf8, int  utf8len )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+
+    switch (mtiByte & 3) {
+        case 0:  /* SMS_PDU_DELIVER */
+            {
+                SmsAddressRec    address;
+                SmsTimeStampRec  timestamp;
+                SmsCodingScheme  coding;
+                GsmRopeRec       rope[1];
+                int              result;
+
+                if ( sms_get_sc_address( &data, end, &address ) < 0 )
+                    goto Fail;
+
+                data  += 1;  /* skip protocol identifier */
+                coding = sms_get_coding_scheme( &data, end );
+                if (coding == SMS_CODING_SCHEME_UNKNOWN)
+                    goto Fail;
+
+                if ( sms_get_timestamp( &data, end, &timestamp ) < 0 )
+                    goto Fail;
+
+                if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 )
+                    goto Fail;
+
+                result = rope->pos;
+                if (utf8len > result)
+                    utf8len = result;
+
+                if (utf8len > 0)
+                    memcpy( utf8, rope->data, utf8len );
+
+                gsm_rope_done( rope );
+                return result;
+            }
+
+        case 1:  /* SMS_PDU_SUBMIT */
+            {
+                SmsAddressRec    address;
+                SmsCodingScheme  coding;
+                GsmRopeRec       rope[1];
+                int              result;
+
+                data += 1;  /* message reference */
+
+                if ( sms_get_address( &data, end, &address ) < 0 )
+                    goto Fail;
+
+                data  += 1;  /* skip protocol identifier */
+                coding = sms_get_coding_scheme( &data, end );
+                if (coding == SMS_CODING_SCHEME_UNKNOWN)
+                    goto Fail;
+
+                gsm_rope_init_alloc( rope, 0 );
+                if ( sms_get_text_utf8( &data, end, (mtiByte & 0x40), coding, rope ) < 0 ) {
+                    gsm_rope_done( rope );
+                    goto Fail;
+                }
+
+                result = rope->pos;
+                if (utf8len > result)
+                    utf8len = result;
+
+                if (utf8len > 0)
+                    memcpy( utf8, rope->data, utf8len );
+
+                gsm_rope_done( rope );
+                return result;
+            }
+    }
+Fail:
+    return -1;
+}
+
+static cbytes_t
+smspdu_get_user_data_ref( SmsPDU  pdu )
+{
+    cbytes_t  data    = pdu->tpdu;
+    cbytes_t  end     = pdu->end;
+    int       mtiByte = sms_get_byte( &data, end );
+    int       len;
+
+    /* if there is no user-data-header, there is no message reference here */
+    if ((mtiByte & 0x40) == 0)
+        goto Fail;
+
+    switch (mtiByte & 3) {
+        case 0:  /* SMS_PDU_DELIVER */
+            if ( sms_skip_address( &data, end ) < 0 )
+                goto Fail;
+
+            data  += 2;  /* skip protocol identifier + coding scheme */
+
+            if ( sms_skip_timestamp( &data, end ) < 0 )
+                goto Fail;
+
+            break;
+
+        case 1:  /* SMS_PDU_SUBMIT */
+            data += 1;  /* skip message reference */
+
+            if ( sms_skip_address( &data, end ) < 0 )
+                goto Fail;
+
+            data += 2;  /* protocol identifier + oding schene */
+            if ( sms_skip_validity_period( &data, end, mtiByte ) < 0 )
+                goto Fail;
+
+            break;
+
+        default:
+            goto Fail;
+    }
+
+    /* skip user-data length */
+    if (data+1 >= end)
+        goto Fail;
+
+    len   = data[1];
+    data += 2;
+
+    while (len >= 2 && data + 2 <= end) {
+        int  htype = data[0];
+        int  hlen = data[1];
+
+        if (htype == 00 && hlen == 3 && data + 5 <= end) {
+            return data + 2;
+        }
+
+        data += hlen;
+        len  -= hlen - 2;
+    }
+Fail:
+    return NULL;
+}
+
+int
+smspdu_get_ref( SmsPDU  pdu )
+{
+    cbytes_t  user_ref = smspdu_get_user_data_ref( pdu );
+
+    if (user_ref != NULL)
+    {
+        return user_ref[0];
+    }
+    else
+    {
+        cbytes_t  data    = pdu->tpdu;
+        cbytes_t  end     = pdu->end;
+        int       mtiByte = sms_get_byte( &data, end );
+
+        if ((mtiByte & 3) == 1) {
+            /* try to extract directly the reference for a SMS-SUBMIT */
+            if (data < end)
+                return data[0];
+        }
+    }
+    return -1;
+}
+
+int
+smspdu_get_max_index( SmsPDU  pdu )
+{
+    cbytes_t  user_ref = smspdu_get_user_data_ref( pdu );
+
+    if (user_ref != NULL) {
+        return user_ref[1];
+    } else {
+        return 1;
+    }
+}
+
+int
+smspdu_get_cur_index( SmsPDU  pdu )
+{
+    cbytes_t  user_ref = smspdu_get_user_data_ref( pdu );
+
+    if (user_ref != NULL) {
+        return user_ref[2] - 1;
+    } else {
+        return 0;
+    }
+}
+
+
+static void
+gsm_rope_add_sms_user_header( GsmRope  rope,
+                              int      ref_number,
+                              int      pdu_count,
+                              int      pdu_index )
+{
+    gsm_rope_add_c( rope, 0x05 );     /* total header length == 5 bytes */
+    gsm_rope_add_c( rope, 0x00 );     /* element id: concatenated message reference number */
+    gsm_rope_add_c( rope, 0x03 );     /* element len: 3 bytes */
+    gsm_rope_add_c( rope, (byte_t)ref_number );  /* reference number */
+    gsm_rope_add_c( rope, (byte_t)pdu_count );     /* max pdu index */
+    gsm_rope_add_c( rope, (byte_t)pdu_index+1 );   /* current pdu index */
+}
+
+/* write a SMS-DELIVER PDU into a rope */
+static void
+gsm_rope_add_sms_deliver_pdu( GsmRope                 rope,
+                              cbytes_t                utf8,
+                              int                     utf8len,
+                              int                     use_gsm7,
+                              const SmsAddressRec*    sender_address,
+                              const SmsTimeStampRec*  timestamp,
+                              int                     ref_num,
+                              int                     pdu_count,
+                              int                     pdu_index)
+{
+    int  coding;
+    int  mtiByte  = 0x20;  /* message type - SMS DELIVER */
+
+    if (pdu_count > 1)
+        mtiByte |= 0x40;  /* user data header indicator */
+
+    gsm_rope_add_c( rope, 0 );        /* no SC Address */
+    gsm_rope_add_c( rope, mtiByte );     /* message type - SMS-DELIVER */
+    gsm_rope_add_address( rope, sender_address );
+    gsm_rope_add_c( rope, 0 );        /* protocol identifier */
+
+    /* data coding scheme - GSM 7 bits / no class - or - 16-bit UCS2 / class 1 */
+    coding = (use_gsm7 ? 0x00 : 0x09);
+
+    gsm_rope_add_c( rope, coding );               /* data coding scheme       */
+    gsm_rope_add_timestamp( rope, timestamp );    /* service center timestamp */
+
+    if (use_gsm7) {
+        bytes_t  dst;
+        int    count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
+        int    pad   = 0;
+
+        assert( count <= MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE );
+
+        if (pdu_count > 1)
+        {
+            int  headerBits    = 6*8;  /* 6 is size of header in bytes */
+            int  headerSeptets = headerBits / 7;
+            if (headerBits % 7 > 0)
+                headerSeptets += 1;
+
+            pad = headerSeptets*7 - headerBits;
+
+            gsm_rope_add_c( rope, count + headerSeptets );
+            gsm_rope_add_sms_user_header(rope, ref_num, pdu_count, pdu_index);
+        }
+        else
+            gsm_rope_add_c( rope, count );
+
+        count = (count*7+pad+7)/8;  /* convert to byte count */
+
+        dst = gsm_rope_reserve( rope, count );
+        if (dst != NULL) {
+            utf8_to_gsm7( utf8, utf8len, dst, pad );
+        }
+    } else {
+        bytes_t  dst;
+        int      count = utf8_to_ucs2( utf8, utf8len, NULL );
+
+        assert( count*2 <= MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE );
+
+        if (pdu_count > 1)
+        {
+            gsm_rope_add_c( rope, count*2 + 6 );
+            gsm_rope_add_sms_user_header( rope, ref_num, pdu_count, pdu_index );
+        }
+        else
+            gsm_rope_add_c( rope, count*2 );
+
+        gsm_rope_add_c( rope, count*2 );
+        dst = gsm_rope_reserve( rope, count*2 );
+        if (dst != NULL) {
+            utf8_to_ucs2( utf8, utf8len, dst );
+        }
+    }
+}
+
+
+static SmsPDU
+smspdu_create_deliver( cbytes_t               utf8,
+                       int                    utf8len,
+                       int                    use_gsm7,
+                       const SmsAddressRec*   sender_address,
+                       const SmsTimeStampRec* timestamp,
+                       int                    ref_num,
+                       int                    pdu_count,
+                       int                    pdu_index )
+{
+    SmsPDU      p;
+    GsmRopeRec  rope[1];
+    int         size;
+
+    p = calloc( sizeof(*p), 1 );
+    if (!p) goto Exit;
+
+    gsm_rope_init( rope );
+    gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
+                                 sender_address, timestamp,
+                                 ref_num, pdu_count, pdu_index);
+    if (rope->error)
+        goto Fail;
+
+    gsm_rope_init_alloc( rope, rope->pos );
+
+    gsm_rope_add_sms_deliver_pdu( rope, utf8, utf8len, use_gsm7,
+                                 sender_address, timestamp,
+                                 ref_num, pdu_count, pdu_index );
+
+    p->base = gsm_rope_done_acquire( rope, &size );
+    if (p->base == NULL)
+        goto Fail;
+
+    p->end  = p->base + size;
+    p->tpdu = p->base + 1;
+Exit:
+    return p;
+
+Fail:
+    free(p);
+    return NULL;
+}
+
+
+void
+smspdu_free_list( SmsPDU*  pdus )
+{
+    if (pdus) {
+        int  nn;
+        for (nn = 0; pdus[nn] != NULL; nn++)
+            smspdu_free( pdus[nn] );
+
+        free( pdus );
+    }
+}
+
+
+
+SmsPDU*
+smspdu_create_deliver_utf8( const unsigned char*   utf8,
+                            int                    utf8len,
+                            const SmsAddressRec*   sender_address,
+                            const SmsTimeStampRec* timestamp )
+{
+    SmsTimeStampRec  ts0;
+    int              use_gsm7;
+    int              count, block;
+    int              num_pdus = 0;
+    int              leftover = 0;
+    SmsPDU*          list = NULL;
+
+    static unsigned char  ref_num = 0;
+
+    if (timestamp == NULL) {
+        sms_timestamp_now( &ts0 );
+        timestamp = &ts0;
+    }
+
+    /* can we encode the message with the GSM 7-bit alphabet ? */
+    use_gsm7 = utf8_check_gsm7( utf8, utf8len );
+
+    /* count the number of SMS PDUs we'll need */
+    block = MAX_USER_DATA_SEPTETS - USER_DATA_HEADER_SIZE;
+
+    if (use_gsm7) {
+        count = utf8_to_gsm7( utf8, utf8len, NULL, 0 );
+    } else {
+        count = utf8_to_ucs2( utf8, utf8len, NULL );
+        block = MAX_USER_DATA_BYTES - USER_DATA_HEADER_SIZE;
+    }
+
+    num_pdus = count / block;
+    leftover = count - num_pdus*block;
+    if (leftover > 0)
+        num_pdus += 1;
+
+    list = calloc( sizeof(SmsPDU*), num_pdus + 1 );
+    if (list == NULL)
+        return NULL;
+
+    /* now create each SMS PDU */
+    {
+        cbytes_t   src     = utf8;
+        cbytes_t   src_end = utf8 + utf8len;
+        int        nn;
+
+        for (nn = 0; nn < num_pdus; nn++)
+        {
+            int       skip = block;
+            cbytes_t  src_next;
+
+            if (leftover > 0 && nn == num_pdus-1)
+                skip = leftover;
+
+            src_next = utf8_skip_gsm7( src, src_end, skip );
+
+            list[nn] = smspdu_create_deliver( src, src_next - src, use_gsm7, sender_address, timestamp,
+                                              ref_num, num_pdus, nn );
+            if (list[nn] == NULL)
+                goto Fail;
+
+            src = src_next;
+        }
+    }
+
+    ref_num++;
+    return list;
+
+Fail:
+    smspdu_free_list(list);
+    return NULL;
+}
+
+
+SmsPDU
+smspdu_create_from_hex( const char*  hex, int  hexlen )
+{
+    SmsPDU    p;
+    cbytes_t  data;
+
+    p = calloc( sizeof(*p), 1 );
+    if (!p) goto Exit;
+
+    p->base = malloc( (hexlen+1)/2 );
+    if (p->base == NULL) {
+        free(p);
+        p = NULL;
+        goto Exit;
+    }
+
+    if ( gsm_hex_to_bytes( (cbytes_t)hex, hexlen, p->base ) < 0 )
+        goto Fail;
+
+    p->end = p->base + (hexlen+1)/2;
+
+    data = p->base;
+    if ( sms_skip_sc_address( &data, p->end ) < 0 )
+        goto Fail;
+
+    p->tpdu = (bytes_t) data;
+
+Exit:
+    return p;
+
+Fail:
+    free(p->base);
+    free(p);
+    return NULL;
+}
+
+int
+smspdu_to_hex( SmsPDU  pdu, char*  hex, int  hexlen )
+{
+    int  result = (pdu->end - pdu->base)*2;
+    int  nn;
+
+    if (hexlen > result)
+        hexlen = result;
+
+    for (nn = 0; nn*2 < hexlen; nn++) {
+        gsm_hex_from_byte( &hex[nn*2], pdu->base[nn] );
+    }
+    return result;
+}
+
+
+/** SMS SUBMIT RECEIVER
+ ** collects one or more SMS-SUBMIT PDUs to generate a single message to deliver
+ **/
+
+typedef struct SmsFragmentRec {
+    struct SmsFragmentRec*  next;
+    SmsAddressRec           from[1];
+    byte_t                  ref;
+    byte_t                  max;
+    byte_t                  count;
+    int                     index;
+    SmsPDU*                 pdus;
+
+} SmsFragmentRec, *SmsFragment;
+
+
+typedef struct SmsReceiverRec {
+    int           last;
+    SmsFragment   fragments;
+
+} SmsReceiverRec;
+
+
+static void
+sms_fragment_free( SmsFragment  frag )
+{
+    int  nn;
+
+    for (nn = 0; nn < frag->max; nn++) {
+        if (frag->pdus[nn] != NULL) {
+            smspdu_free( frag->pdus[nn] );
+            frag->pdus[nn] = NULL;
+        }
+    }
+    frag->pdus  = NULL;
+    frag->count = 0;
+    frag->max   = 0;
+    frag->index = 0;
+    free( frag );
+}
+
+static SmsFragment
+sms_fragment_alloc( SmsReceiver  rec, const SmsAddressRec*  from, int   ref, int  max )
+{
+    SmsFragment  frag = calloc(sizeof(*frag) + max*sizeof(SmsPDU), 1 );
+
+    if (frag != NULL) {
+        frag->from[0] = from[0];
+        frag->ref     = ref;
+        frag->max     = max;
+        frag->pdus    = (SmsPDU*)(frag + 1);
+        frag->index   = ++rec->last;
+    }
+    return  frag;
+}
+
+
+
+SmsReceiver   sms_receiver_create( void )
+{
+    SmsReceiver  rec = calloc(sizeof(*rec),1);
+    return rec;
+}
+
+void
+sms_receiver_destroy( SmsReceiver  rec )
+{
+    while (rec->fragments) {
+        SmsFragment  frag = rec->fragments;
+        rec->fragments = frag->next;
+        sms_fragment_free(frag);
+    }
+}
+
+static SmsFragment*
+sms_receiver_find_p( SmsReceiver  rec, const SmsAddressRec*  from, int  ref )
+{
+    SmsFragment*  pnode = &rec->fragments;
+    SmsFragment   node;
+
+    for (;;) {
+        node = *pnode;
+        if (node == NULL)
+            break;
+        if (node->ref == ref && sms_address_eq( node->from, from ))
+            break;
+        pnode = &node->next;
+    }
+    return  pnode;
+}
+
+static SmsFragment*
+sms_receiver_find_index_p( SmsReceiver  rec, int  index )
+{
+    SmsFragment*  pnode = &rec->fragments;
+    SmsFragment   node;
+
+    for (;;) {
+        node = *pnode;
+        if (node == NULL)
+            break;
+        if (node->index == index)
+            break;
+        pnode = &node->next;
+    }
+    return  pnode;
+}
+
+int
+sms_receiver_add_submit_pdu( SmsReceiver  rec, SmsPDU       submit_pdu )
+{
+    SmsAddressRec  from[1];
+    int            ref, max, cur;
+    SmsFragment*   pnode;
+    SmsFragment    frag;
+
+    if ( smspdu_get_receiver_address( submit_pdu, from ) < 0 ) {
+        D( "%s: could not extract receiver address\n", __FUNCTION__ );
+        return -1;
+    }
+
+    ref = smspdu_get_ref( submit_pdu );
+    if (ref < 0) {
+        D( "%s: could not extract message reference from pdu\n", __FUNCTION__ );
+        return -1;
+    }
+    max = smspdu_get_max_index( submit_pdu );
+    if (max < 0) {
+        D( "%s: invalid max fragment value: %d should be >= 1\n",
+           __FUNCTION__, max );
+        return -1;
+    }
+    pnode = sms_receiver_find_p( rec, from, ref );
+    frag  = *pnode;
+    if (frag == NULL) {
+        frag = sms_fragment_alloc( rec, from, ref, max );
+        if (frag == NULL) {
+            D("%s: not enough memory to allocate new fragment\n", __FUNCTION__ );
+            return -1;
+        }
+        if (D_ACTIVE) {
+            char  tmp[32];
+            int   len;
+
+            len = sms_address_to_str( from, tmp, sizeof(tmp) );
+            if (len < 0) {
+                strcpy( tmp, "<unknown>" );
+                len = strlen(tmp);
+            }
+            D("%s: created SMS index %d, from %.*s, ref %d, max %d\n", __FUNCTION__,
+               frag->index, len, tmp, frag->ref, frag->max);
+        }
+        *pnode = frag;
+    }
+
+    cur = smspdu_get_cur_index( submit_pdu );
+    if (cur < 0) {
+        D("%s: SMS fragment index is too small: %d should be >= 1\n", __FUNCTION__, cur+1 );
+        return -1;
+    }
+    if (cur >= max) {
+        D("%s: SMS fragment index is too large (%d >= %d)\n", __FUNCTION__, cur, max);
+        return -1;
+    }
+    if ( frag->pdus[cur] != NULL ) {
+        D("%s: receiving duplicate SMS fragment for %d/%d, ref=%d, discarding old one\n",
+          __FUNCTION__, cur+1, max, ref);
+        smspdu_free( frag->pdus[cur] );
+        frag->count -= 1;
+    }
+    frag->pdus[cur] = submit_pdu;
+    frag->count    += 1;
+
+    if (frag->count >= frag->max) {
+        /* yes, we received all fragments for this SMS */
+        D( "%s: SMS index %d, received all %d fragments\n", __FUNCTION__, frag->index, frag->count );
+        return frag->index;
+    }
+    else {
+        /* still waiting for more */
+        D( "%s: SMS index %d, received %d/%d, waiting for %d more\n", __FUNCTION__,
+            frag->index, cur+1, max, frag->max - frag->count );
+        return 0;
+    }
+}
+
+
+int
+sms_receiver_get_text_message( SmsReceiver  rec, int  index, bytes_t  utf8, int  utf8len )
+{
+    SmsFragment*  pnode = sms_receiver_find_index_p( rec, index );
+    SmsFragment   frag  = *pnode;
+    int           nn, total;
+
+    if (frag == NULL) {
+        D( "%s: invalid SMS index %d\n", __FUNCTION__, index );
+        return -1;
+    }
+    if (frag->count != frag->max) {
+        D( "%s: SMS index %d still needs %d fragments\n", __FUNCTION__,
+           frag->index, frag->max - frag->count );
+        return -1;
+    }
+    /* get the size of all combined text */
+    total = 0;
+    for ( nn = 0; nn < frag->count; nn++ ) {
+        int  partial;
+        if (utf8 && utf8len > 0) {
+            partial  = smspdu_get_text_message( frag->pdus[nn], utf8, utf8len );
+            utf8    += partial;
+            utf8len -= partial;
+        } else {
+            partial  = smspdu_get_text_message( frag->pdus[nn], NULL, 0 );
+        }
+        total += partial;
+    }
+    return total;
+}
+
+
+static void
+sms_receiver_remove( SmsReceiver  rec, int  index )
+{
+    SmsFragment*  pnode = sms_receiver_find_index_p( rec, index );
+    SmsFragment   frag  = *pnode;
+    if (frag != NULL) {
+        *pnode = frag->next;
+        sms_fragment_free(frag);
+    }
+}
+
+
+SmsPDU*
+sms_receiver_create_deliver( SmsReceiver  rec, int  index, const SmsAddressRec*  from )
+{
+    SmsPDU*          result = NULL;
+    SmsFragment*     pnode = sms_receiver_find_index_p( rec, index );
+    SmsFragment      frag  = *pnode;
+    SmsTimeStampRec  now[1];
+    int              nn, total;
+    bytes_t          utf8;
+    int              utf8len;
+
+    if (frag == NULL) {
+        D( "%s: invalid SMS index %d\n", __FUNCTION__, index );
+        return NULL;
+    }
+    if (frag->count != frag->max) {
+        D( "%s: SMS index %d still needs %d fragments\n", __FUNCTION__,
+           frag->index, frag->max - frag->count );
+        return NULL;
+    }
+
+    /* get the combined text message */
+    utf8len = sms_receiver_get_text_message( rec, index, NULL, 0 );
+    if (utf8len < 0)
+        goto Exit;
+
+    utf8 = malloc( utf8len + 1 );
+    if (utf8 == NULL) {
+        D( "%s: not enough memory to allocate %d bytes\n",
+           __FUNCTION__, utf8len+1 );
+        goto Exit;
+    }
+
+    total = 0;
+    for ( nn = 0; nn < frag->count; nn++ ) {
+        total += smspdu_get_text_message( frag->pdus[nn], utf8 + total, utf8len - total );
+    }
+
+    sms_timestamp_now( now );
+
+    result = smspdu_create_deliver_utf8( utf8, utf8len, from, now );
+
+    free(utf8);
+
+Exit:
+    sms_receiver_remove( rec, index );
+    return result;
+}
+
diff --git a/telephony/sms.h b/telephony/sms.h
new file mode 100644
index 0000000..7059ee3
--- /dev/null
+++ b/telephony/sms.h
@@ -0,0 +1,117 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef _android_sms_h
+#define _android_sms_h
+
+#include <time.h>
+
+/** MESSAGE TEXT
+ **/
+/* convert a quoted message text into a utf8 string. Note: you can use 'str' as the destination buffer
+ * with the current implementation. always return the number of utf8 bytes corresponding to the original
+ * message string, even if utf8 is NULL and utf8len is 0
+ */
+extern int  sms_utf8_from_message_str( const char*  str, int  strlen, unsigned char*  utf8, int  utf8len );
+
+/* the equivalent in the opposite direction
+ */
+extern int  sms_utf8_to_message_str( const unsigned char*  utf8, int  utf8len, char*  str, int  strlen );
+
+/** TIMESTAMPS
+ **/
+
+/* An SMS timestamp structure */
+typedef struct {
+    unsigned char  data[7];
+} SmsTimeStampRec, *SmsTimeStamp;
+
+extern void  sms_timestamp_now( SmsTimeStamp  stamp );
+extern int   sms_timestamp_to_tm( SmsTimeStamp  stamp, struct tm*  tm );
+
+/** SMS ADDRESSES
+ **/
+
+#define  SMS_ADDRESS_MAX_SIZE  16
+
+typedef struct {
+    unsigned char  len;
+    unsigned char  toa;
+    unsigned char  data[ SMS_ADDRESS_MAX_SIZE ];
+} SmsAddressRec, *SmsAddress;
+
+extern int  sms_address_from_str( SmsAddress  address, const char*  src, int  srclen );
+extern int  sms_address_to_str( SmsAddress  address, char*  src, int  srclen );
+
+extern int  sms_address_from_bytes( SmsAddress  address, const unsigned char*  buf, int  buflen );
+extern int  sms_address_to_bytes  ( SmsAddress  address, unsigned char*  buf, int  bufsize );
+extern int  sms_address_from_hex  ( SmsAddress  address, const char*  hex, int  hexlen );
+extern int  sms_address_to_hex    ( SmsAddress  address, char*   hex, int  hexsize );
+
+/** SMS PROTOCOL DATA UNITS
+ **/
+
+typedef struct SmsPDURec*   SmsPDU;
+
+extern SmsPDU*  smspdu_create_deliver_utf8( const unsigned char*   utf8,
+                                            int                    utf8len,
+                                            const SmsAddressRec*   sender_address,
+                                            const SmsTimeStampRec* timestamp );
+
+extern void     smspdu_free_list( SmsPDU*  pdus );
+
+extern SmsPDU   smspdu_create_from_hex( const char*  hex, int  hexlen );
+
+extern int      smspdu_to_hex( SmsPDU  pdu, char*  hex, int  hexsize );
+
+/* free a given SMS PDU */
+extern void     smspdu_free( SmsPDU  pdu );
+
+typedef enum {
+    SMS_PDU_INVALID = 0,
+    SMS_PDU_DELIVER,
+    SMS_PDU_SUBMIT,
+    SMS_PDU_STATUS_REPORT
+} SmsPduType;
+
+extern SmsPduType    smspdu_get_type( SmsPDU  pdu );
+
+/* retrieve the sender address of a SMS-DELIVER pdu, returns -1 otherwise */
+extern int  smspdu_get_sender_address( SmsPDU  pdu, SmsAddress  address );
+
+/* retrieve the service center timestamp of a SMS-DELIVER pdu, return -1 otherwise */
+extern int  smspdu_get_sc_timestamp( SmsPDU  pdu, SmsTimeStamp  timestamp );
+
+/* retrieve the receiver address of a SMS-SUBMIT pdu, return -1 otherwise */
+extern int  smspdu_get_receiver_address( SmsPDU  pdu, SmsAddress  address );
+
+extern int  smspdu_get_ref      ( SmsPDU  pdu );
+extern int  smspdu_get_max_index( SmsPDU  pdu );
+extern int  smspdu_get_cur_index( SmsPDU  pdu );
+
+/* get the message embedded in a SMS PDU as a utf8 byte array, returns the length of the message in bytes */
+/* or -1 in case of error */
+extern int  smspdu_get_text_message( SmsPDU  pdu, unsigned char*  utf8, int  utf8len );
+
+/** SMS SUBMIT RECEIVER
+ ** collects one or more SMS-SUBMIT PDUs to generate a single message to deliver
+ **/
+
+typedef struct SmsReceiverRec  *SmsReceiver;
+
+extern SmsReceiver   sms_receiver_create( void );
+extern void          sms_receiver_destroy( SmsReceiver  rec );
+
+extern int           sms_receiver_add_submit_pdu( SmsReceiver  rec, SmsPDU       submit_pdu );
+extern int           sms_receiver_get_text_message( SmsReceiver  rec, int  index, unsigned char*  utf8, int  utf8len );
+extern SmsPDU*       sms_receiver_create_deliver( SmsReceiver  rec, int  index, const SmsAddressRec*  from );
+
+#endif /* _android_sms_h */
diff --git a/telephony/sysdeps.h b/telephony/sysdeps.h
new file mode 100644
index 0000000..19ca8d3
--- /dev/null
+++ b/telephony/sysdeps.h
@@ -0,0 +1,80 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#ifndef __sysdeps_h__
+#define __sysdeps_h__
+
+/* system-dependent platform abstraction used by the emulated GSM modem
+ */
+
+/* to be called before anything else */
+
+extern void  sys_main_init( void );
+
+/** callbacks
+ **/
+typedef void  (*SysCallback)( void*  opaque );
+
+/** events
+ **/
+enum {
+    SYS_EVENT_READ  = 0x01,
+    SYS_EVENT_WRITE = 0x02,
+    SYS_EVENT_ERROR = 0x04,
+    SYS_EVENT_ALL   = 0x07
+};
+
+/** channels
+ **/
+typedef struct SysChannelRec_*  SysChannel;
+
+typedef void (*SysChannelCallback)( void*  opaque, int  event_flags );
+
+/* XXX: TODO: channel creation functions */
+extern SysChannel  sys_channel_create_tcp_server( int port );
+extern SysChannel  sys_channel_create_tcp_handler( SysChannel  server_channel );
+extern SysChannel  sys_channel_create_tcp_client( const char*  hostname, int  port );
+extern int         sys_channel_set_non_block( SysChannel  channel );
+
+extern  void   sys_channel_on( SysChannel          channel,
+                               int                 event_flags,
+                               SysChannelCallback  event_callback,
+                               void*               event_opaqe );
+
+extern  int   sys_channel_read( SysChannel  channel, void*  buffer, int  size );
+
+extern  int   sys_channel_write( SysChannel  channel, const void*  buffer, int  size );
+
+extern  void  sys_channel_close( SysChannel  channel );
+
+
+/** time measurement
+ **/
+typedef long long   SysTime;
+
+extern SysTime   sys_time_now( void );
+
+/** timers
+ **/
+typedef struct SysTimerRec_*    SysTimer;
+
+extern SysTimer   sys_timer_create( void );
+extern void       sys_timer_set( SysTimer  timer, SysTime  when, SysCallback   callback, void*  opaque );
+extern void       sys_timer_unset( SysTimer  timer );
+extern void       sys_timer_destroy( SysTimer  timer );
+
+extern long long  sys_time_ms( void );
+
+/** main loop (may return immediately on some platform)
+ **/
+extern int   sys_main_loop( void );
+
+#endif /* __sysdeps_h__ */
diff --git a/telephony/sysdeps_posix.c b/telephony/sysdeps_posix.c
new file mode 100644
index 0000000..8c5eb12
--- /dev/null
+++ b/telephony/sysdeps_posix.c
@@ -0,0 +1,645 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sysdeps.h"
+#include <assert.h>
+#include <unistd.h>
+#include <sys/select.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#ifndef HAVE_WINSOCK
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#endif
+
+/**  QUEUE
+ **/
+#define  SYS_MAX_QUEUE  16
+
+typedef struct {
+    int    start;
+    int    end;
+    void*  pending[ SYS_MAX_QUEUE ];
+}
+SysQueueRec, *SysQueue;
+
+static void
+sys_queue_reset( SysQueue  queue )
+{
+    queue->start = queue->end = 0;
+}
+
+static void
+sys_queue_add( SysQueue  queue, void*  item )
+{
+    assert( queue->end - queue->start < SYS_MAX_QUEUE );
+    assert( queue->start == 0 );
+    assert( item != NULL );
+    queue->pending[ queue->end++ ] = item;
+}
+
+#if 0
+static void
+sys_queue_remove( SysQueue  queue, void*  item )
+{
+    int  nn, count;
+    assert( queue->end > queue->start );
+    assert( item != NULL );
+    count = queue->end - queue->start;
+    for ( nn = queue->start; count > 0; ++nn, --count ) {
+        if ( queue->pending[nn] == item ) {
+            queue->pending[nn] = queue->pending[nn+count-1];
+            queue->end -= 1;
+            break;
+        }
+    }
+    assert( 0 && "sys_queue_remove: item not found" );
+}
+#endif
+
+static void*
+sys_queue_get( SysQueue  queue )
+{
+    if (queue->end > queue->start) {
+        return queue->pending[ queue->start++ ];
+    }
+    return NULL;
+}
+
+/** CHANNELS
+ **/
+typedef struct SysChannelRec_ {
+    SysChannel          next;
+    int                 fd;
+    char                active;
+    char                pending;
+    char                closed;
+    int                 wanted;
+    int                 ready;
+    SysChannelCallback  callback;
+    void*               opaque;
+} SysChannelRec;
+
+
+/*** channel allocation ***/
+#define  SYS_EVENT_MAX     3
+#define  SYS_MAX_CHANNELS  16
+
+static SysChannelRec  _s_channels0[ SYS_MAX_CHANNELS ];
+static SysChannel     _s_free_channels;
+
+static SysChannel
+sys_channel_alloc( void )
+{
+    SysChannel  channel = _s_free_channels;
+    assert( channel != NULL && "out of free channels" );
+    _s_free_channels  = channel->next;
+    channel->next     = NULL;
+    channel->active   = 0;
+    channel->closed   = 0;
+    channel->pending  = 0;
+    channel->wanted   = 0;
+    return channel;
+}
+
+static void
+sys_channel_free( SysChannel  channel )
+{
+    if (channel->fd >= 0) {
+#ifdef _WIN32
+        shutdown( channel->fd, SD_BOTH );
+#else
+        shutdown( channel->fd, SHUT_RDWR );
+#endif
+        close(channel->fd);
+        channel->fd = -1;
+    }
+    channel->wanted   = 0;
+    channel->ready    = 0;
+    channel->callback = NULL;
+
+    channel->next    = _s_free_channels;
+    _s_free_channels = channel;
+}
+
+
+/* list of active channels */
+static SysChannel     _s_channels;
+
+/* used by select to wait on channel events */
+static fd_set         _s_fdsets[SYS_EVENT_MAX];
+static int            _s_maxfd;
+
+static void
+sys_channel_deactivate( SysChannel  channel )
+{
+    assert( channel->active != 0 );
+    SysChannel  *pnode = &_s_channels;
+    for (;;) {
+        SysChannel  node = *pnode;
+        assert( node != NULL );
+        if (node == channel)
+            break;
+        pnode = &node->next;
+    }
+    *pnode          = channel->next;
+    channel->next   = NULL;
+    channel->active = 0;
+}
+
+static void
+sys_channel_activate( SysChannel  channel )
+{
+    assert( channel->active == 0 );
+    channel->next = _s_channels;
+    _s_channels   = channel;
+    channel->active = 1;
+    if (channel->fd > _s_maxfd)
+        _s_maxfd = channel->fd;
+}
+
+
+/* queue of pending channels */
+static SysQueueRec    _s_pending_channels[1];
+
+
+static void
+sys_init_channels( void )
+{
+    int  nn;
+
+    for (nn = 0; nn < SYS_MAX_CHANNELS-1; nn++)
+        _s_channels0[nn].next = &_s_channels0[nn+1];
+    _s_free_channels = &_s_channels0[0];
+
+    for (nn = 0; nn < SYS_EVENT_MAX; nn++)
+        FD_ZERO( &_s_fdsets[nn] );
+
+    _s_maxfd = -1;
+
+    sys_queue_reset( _s_pending_channels );
+}
+
+
+void
+sys_channel_on( SysChannel          channel,
+                int                 events,
+                SysChannelCallback  callback,
+                void*               opaque )
+{
+    int   adds    = events & ~channel->wanted;
+    int   removes = channel->wanted & ~events;
+
+    channel->wanted   = events;
+    channel->callback = callback;
+    channel->opaque   = opaque;
+
+    /* update global fdsets */
+    if (adds) {
+        int  ee;
+        for (ee = 0; ee < SYS_EVENT_MAX; ee++)
+            if (adds & (1 << ee))
+                FD_SET( channel->fd, &_s_fdsets[ee] );
+    }
+    if (removes) {
+        int  ee;
+        for (ee = 0; ee < SYS_EVENT_MAX; ee++)
+            if (removes & (1 << ee))
+                FD_CLR( channel->fd, &_s_fdsets[ee] );
+    }
+    if (events && !channel->active) {
+        sys_channel_activate( channel );
+    }
+    else if (!events && channel->active) {
+        sys_channel_deactivate( channel );
+    }
+}
+
+int
+sys_channel_read( SysChannel  channel, void*  buffer, int  size )
+{
+    char*  buff = buffer;
+    int    count = 0;
+
+    assert( !channel->closed );
+
+    while (size > 0) {
+        int  len = read(channel->fd, buff, size);
+        if (len < 0) {
+            if (errno == EINTR)
+                continue;
+            if (count == 0)
+                count = -1;
+            break;
+        }
+        buff  += len;
+        size  -= len;
+        count += len;
+    }
+    return count;
+}
+
+
+int
+sys_channel_write( SysChannel  channel, const void*  buffer, int  size )
+{
+    const char*  buff = buffer;
+    int          count = 0;
+
+    assert( !channel->closed );
+
+    while (size > 0) {
+        int  len = write(channel->fd, buff, size);
+        if (len < 0) {
+            if (errno == EINTR)
+                continue;
+            if (count == 0)
+                count = -1;
+            break;
+        }
+        buff  += len;
+        size  -= len;
+        count += len;
+    }
+    return count;
+}
+
+
+void
+sys_channel_close( SysChannel  channel )
+{
+    if (channel->active) {
+        sys_channel_on( channel, 0, NULL, NULL );
+    }
+
+    if (channel->pending) {
+        /* we can't free the channel right now because it */
+        /* is in the pending list, set a flag             */
+        channel->closed = 1;
+        return;
+    }
+
+    if (!channel->closed) {
+        channel->closed = 1;
+    }
+
+    sys_channel_free( channel );
+}
+
+/** time measurement
+ **/
+SysTime  sys_time_ms( void )
+{
+    struct timeval  tv;
+    gettimeofday( &tv, NULL );
+    return (SysTime)(tv.tv_usec / 1000) + (SysTime)tv.tv_sec * 1000;
+}
+
+/** timers
+ **/
+typedef struct SysTimerRec_
+{
+    SysTimer     next;
+    SysTime      when;
+    SysCallback  callback;
+    void*        opaque;
+} SysTimerRec;
+
+#define  SYS_MAX_TIMERS  16
+
+static SysTimerRec   _s_timers0[ SYS_MAX_TIMERS ];
+static SysTimer      _s_free_timers;
+static SysTimer      _s_timers;
+
+static SysQueueRec   _s_pending_timers[1];
+
+
+static void
+sys_init_timers( void )
+{
+    int  nn;
+    for (nn = 0; nn < SYS_MAX_TIMERS-1; nn++) {
+        _s_timers0[nn].next = & _s_timers0[nn+1];
+    }
+    _s_free_timers = &_s_timers0[0];
+
+    sys_queue_reset( _s_pending_timers );
+}
+
+
+SysTimer   sys_timer_create( void )
+{
+    SysTimer  timer = _s_free_timers;
+    assert( timer != NULL && "too many timers allocated" );
+    _s_free_timers = timer->next;
+    timer->next    = NULL;
+    return timer;
+}
+
+
+void  sys_timer_unset( SysTimer  timer )
+{
+    if (timer->callback != NULL) {
+        SysTimer  *pnode, node;
+        pnode = &_s_timers;
+        for (;;) {
+            node = *pnode;
+            if (node == NULL)
+                break;
+            if (node == timer) {
+                *pnode = node->next;
+                break;
+            }
+            pnode = &node->next;
+        }
+        timer->next     = NULL;
+        timer->callback = NULL;
+        timer->opaque   = NULL;
+    }
+}
+
+
+void  sys_timer_set( SysTimer      timer,
+                     SysTime       when,
+                     SysCallback   callback,
+                     void*         opaque )
+{
+    if (timer->callback != NULL)
+        sys_timer_unset(timer);
+
+    if (callback != NULL) {
+        SysTime  now = sys_time_ms();
+
+        if (now >= when) {
+            callback( opaque );
+        } else {
+            SysTimer  *pnode, node;
+            pnode = &_s_timers;
+            for (;;) {
+                node = *pnode;
+                if (node == NULL || node->when >= when) {
+                    break;
+                }
+                pnode = &node->next;
+            }
+            timer->next     = *pnode;
+            *pnode          = timer;
+            timer->when     = when;
+            timer->callback = callback;
+            timer->opaque   = opaque;
+        }
+    }
+}
+
+
+void  sys_timer_destroy( SysTimer  timer )
+{
+    assert( timer != NULL && "sys_timer_destroy: bad argument" );
+    if (timer->callback != NULL)
+        sys_timer_unset(timer);
+
+    timer->next    = _s_free_timers;
+    _s_free_timers = timer;
+}
+
+
+static void
+sys_single_loop( void )
+{
+    fd_set rfd, wfd, efd;
+    struct timeval  timeout_tv, *timeout = NULL;
+    int    n;
+
+    memcpy(&rfd, &_s_fdsets[0], sizeof(fd_set));
+    memcpy(&wfd, &_s_fdsets[1], sizeof(fd_set));
+    memcpy(&efd, &_s_fdsets[2], sizeof(fd_set));
+
+    if ( _s_timers != NULL ) {
+        SysTime   now   = sys_time_ms();
+        SysTimer  first = _s_timers;
+
+        timeout = &timeout_tv;
+        if (first->when <= now) {
+            timeout->tv_sec  = 0;
+            timeout->tv_usec = 0;
+        } else {
+            SysTime  diff = first->when - now;
+            timeout->tv_sec =   diff / 1000;
+            timeout->tv_usec = (diff - timeout->tv_sec*1000) * 1000;
+        }
+    }
+
+    n = select( _s_maxfd+1, &rfd, &wfd, &efd, timeout);
+    if(n < 0) {
+        if(errno == EINTR) return;
+        perror("select");
+        return;
+    }
+
+    /* enqueue pending channels */
+    {
+        int  i;
+
+        sys_queue_reset( _s_pending_channels );
+        for(i = 0; (i <= _s_maxfd) && (n > 0); i++)
+        {
+            int  events = 0;
+
+            if(FD_ISSET(i, &rfd)) events |= SYS_EVENT_READ;
+            if(FD_ISSET(i, &wfd)) events |= SYS_EVENT_WRITE;
+            if(FD_ISSET(i, &efd)) events |= SYS_EVENT_ERROR;
+
+            if (events) {
+                SysChannel  channel;
+
+                n--;
+                for (channel = _s_channels; channel; channel = channel->next)
+                {
+                    if (channel->fd != i)
+                        continue;
+
+                    channel->ready   = events;
+                    channel->pending = 1;
+                    sys_queue_add( _s_pending_channels, channel );
+                    break;
+                }
+            }
+        }
+    }
+
+    /* enqueue pending timers */
+    {
+        SysTimer  timer = _s_timers;
+        SysTime   now   = sys_time_ms();
+
+        sys_queue_reset( _s_pending_timers );
+        while (timer != NULL)
+        {
+            if (timer->when > now)
+                break;
+
+            sys_queue_add( _s_pending_timers, timer );
+            _s_timers = timer = timer->next;
+        }
+    }
+}
+
+void  sys_main_init( void )
+{
+    sys_init_channels();
+    sys_init_timers();
+}
+
+
+int   sys_main_loop( void )
+{
+    for (;;) {
+        SysTimer    timer;
+        SysChannel  channel;
+
+        /* exit if we have nothing to do */
+        if (_s_channels == NULL && _s_timers == NULL)
+            break;
+
+        sys_single_loop();
+
+        while ((timer = sys_queue_get( _s_pending_timers )) != NULL) {
+            timer->callback( timer->opaque );
+        }
+
+        while ((channel = sys_queue_get( _s_pending_channels )) != NULL) {
+            int  events;
+
+            channel->pending = 0;
+            if (channel->closed) {
+                /* the channel was closed by a previous callback */
+                sys_channel_close(channel);
+            }
+            events = channel->ready;
+            channel->ready = 0;
+            channel->callback( channel->opaque, events );
+        }
+    }
+    return 0;
+}
+
+
+
+
+SysChannel
+sys_channel_create_tcp_server( int port )
+{
+    SysChannel          channel;
+    int                 on = 1;
+    const int           BACKLOG = 4;
+
+    channel = sys_channel_alloc();
+    if (-1==(channel->fd=socket(AF_INET, SOCK_STREAM, 0))) {
+        perror("socket");
+        sys_channel_free( channel );
+        return NULL;
+    }
+
+    /* Enable address re-use for server mode */
+    if ( -1==setsockopt( channel->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) )) {
+        perror("setsockopt(SO_REUSEADDR)");
+    }
+
+    {
+        struct sockaddr_in  servname;
+        long                in_addr = INADDR_ANY;
+
+        servname.sin_family = AF_INET;
+        servname.sin_port   = htons(port);
+
+        servname.sin_addr.s_addr=in_addr;
+
+        if (-1==bind(channel->fd, (struct sockaddr*)&servname, sizeof(servname))) {
+            perror("bind");
+            sys_channel_close(channel);
+            return NULL;
+        }
+
+        /* Listen but don't accept */
+        if ( listen(channel->fd, BACKLOG) < 0 ) {
+            perror("listen");
+            sys_channel_close(channel);
+            return NULL;
+        }
+    }
+    return channel;
+}
+
+
+SysChannel
+sys_channel_create_tcp_handler( SysChannel  server_channel )
+{
+    int         on      = 1;
+    SysChannel  channel = sys_channel_alloc();
+
+    channel->fd = accept( server_channel->fd, NULL, 0 );
+    if (channel->fd < 0) {
+        perror( "accept" );
+        sys_channel_free( channel );
+        return NULL;
+    }
+
+    /* set to non-blocking and disable TCP Nagle algorithm */
+    fcntl(channel->fd, F_SETFL, O_NONBLOCK);
+    setsockopt(channel->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+    return channel;
+}
+
+
+SysChannel
+sys_channel_create_tcp_client( const char*  hostname, int  port )
+{
+    struct hostent*     hp;
+    struct sockaddr_in  addr;
+    SysChannel          channel = sys_channel_alloc();
+    int                 on = 1;
+
+    hp = gethostbyname(hostname);
+    if(hp == 0) {
+        fprintf(stderr, "unknown host: %s\n", hostname);
+        sys_channel_free(channel);
+        return NULL;
+    };
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = hp->h_addrtype;
+    addr.sin_port   = htons(port);
+    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+
+    channel->fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+    if(channel->fd < 0) {
+        sys_channel_free(channel);
+        return NULL;
+    }
+
+    if(connect( channel->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        perror( "connect" );
+        sys_channel_free(channel);
+        return NULL;
+    }
+
+    /* set to non-blocking and disable Nagle algorithm */
+    fcntl(channel->fd, F_SETFL, O_NONBLOCK);
+    setsockopt( channel->fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) );
+    return channel;
+}
+
diff --git a/telephony/sysdeps_qemu.c b/telephony/sysdeps_qemu.c
new file mode 100644
index 0000000..ec0b3f5
--- /dev/null
+++ b/telephony/sysdeps_qemu.c
@@ -0,0 +1,376 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sockets.h"
+#include "sysdeps.h"
+#include "qemu-timer.h"
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#endif
+
+#define  DEBUG  1
+
+#define  D_ACTIVE  DEBUG
+
+#if DEBUG
+#define  D(...)  do { if (D_ACTIVE) fprintf(stderr, __VA_ARGS__); } while (0)
+#else
+#define  D(...)  ((void)0)
+#endif
+
+/** TIME
+ **/
+
+SysTime
+sys_time_ms( void )
+{
+    return qemu_get_clock( rt_clock );
+}
+
+/** TIMERS
+ **/
+
+typedef struct SysTimerRec_ {
+    QEMUTimer*    timer;
+    QEMUTimerCB*  callback;
+    void*         opaque;
+    SysTimer      next;
+} SysTimerRec;
+
+#define  MAX_TIMERS  32
+
+static SysTimerRec  _s_timers0[ MAX_TIMERS ];
+static SysTimer     _s_free_timers;
+
+static void
+sys_init_timers( void )
+{
+    int  nn;
+    for (nn = 0; nn < MAX_TIMERS-1; nn++)
+        _s_timers0[nn].next = _s_timers0 + (nn+1);
+
+    _s_free_timers = _s_timers0;
+}
+
+static SysTimer
+sys_timer_alloc( void )
+{
+    SysTimer  timer = _s_free_timers;
+
+    if (timer != NULL) {
+        _s_free_timers = timer->next;
+        timer->next    = NULL;
+        timer->timer   = NULL;
+    }
+    return timer;
+}
+
+
+static void
+sys_timer_free( SysTimer  timer )
+{
+    if (timer->timer) {
+        qemu_del_timer( timer->timer );
+        qemu_free_timer( timer->timer );
+        timer->timer = NULL;
+    }
+    timer->next    = _s_free_timers;
+    _s_free_timers = timer;
+}
+
+
+SysTimer   sys_timer_create( void )
+{
+    SysTimer  timer = sys_timer_alloc();
+    return timer;
+}
+
+void
+sys_timer_set( SysTimer  timer, SysTime  when, SysCallback   _callback, void*  opaque )
+{
+    QEMUTimerCB*  callback = (QEMUTimerCB*)_callback;
+
+    if (callback == NULL) {  /* unsetting the timer */
+        if (timer->timer) {
+            qemu_del_timer( timer->timer );
+            qemu_free_timer( timer->timer );
+            timer->timer = NULL;
+        }
+        timer->callback = callback;
+        timer->opaque   = NULL;
+        return;
+    }
+
+    if ( timer->timer ) {
+         if ( timer->callback == callback && timer->opaque == opaque )
+            goto ReuseTimer;
+
+         /* need to replace the timer */
+         qemu_free_timer( timer->timer );
+    }
+
+    timer->timer    = qemu_new_timer( rt_clock, callback, opaque );
+    timer->callback = callback;
+    timer->opaque   = opaque;
+
+ReuseTimer:
+    qemu_mod_timer( timer->timer, when );
+}
+
+void
+sys_timer_unset( SysTimer  timer )
+{
+    if (timer->timer) {
+        qemu_del_timer( timer->timer );
+    }
+}
+
+void
+sys_timer_destroy( SysTimer  timer )
+{
+    sys_timer_free( timer );
+}
+
+
+/** CHANNELS
+ **/
+
+typedef struct SysChannelRec_ {
+    int                 fd;
+    SysChannelCallback  callback;
+    void*               opaque;
+    SysChannel          next;
+} SysChannelRec;
+
+#define  MAX_CHANNELS  16
+
+static SysChannelRec  _s_channels0[ MAX_CHANNELS ];
+static SysChannel     _s_free_channels;
+
+static void
+sys_init_channels( void )
+{
+    int  nn;
+
+    for ( nn = 0; nn < MAX_CHANNELS-1; nn++ ) {
+        _s_channels0[nn].next = _s_channels0 + (nn+1);
+    }
+    _s_free_channels = _s_channels0;
+}
+
+static SysChannel
+sys_channel_alloc( )
+{
+    SysChannel  channel = _s_free_channels;
+    if (channel != NULL) {
+        _s_free_channels  = channel->next;
+        channel->next     = NULL;
+        channel->fd       = -1;
+        channel->callback = NULL;
+        channel->opaque   = NULL;
+    }
+    return channel;
+}
+
+static void
+sys_channel_free( SysChannel  channel )
+{
+    if (channel->fd >= 0) {
+        socket_close( channel->fd );
+        channel->fd = -1;
+    }
+    channel->next    = _s_free_channels;
+    _s_free_channels = channel;
+}
+
+
+static void
+sys_channel_read_handler( void*  _channel )
+{
+    SysChannel  channel = _channel;
+    D( "%s: read event for channel %p:%d\n", __FUNCTION__,
+       channel, channel->fd );
+    channel->callback( channel->opaque, SYS_EVENT_READ );
+}
+
+static void
+sys_channel_write_handler( void*  _channel )
+{
+    SysChannel  channel = _channel;
+    D( "%s: write event for channel %p:%d\n", __FUNCTION__, channel, channel->fd );
+    channel->callback( channel->opaque, SYS_EVENT_WRITE );
+}
+
+void
+sys_channel_on( SysChannel          channel,
+                int                 events,
+                SysChannelCallback  event_callback,
+                void*               event_opaque )
+{
+    IOHandler*  read_handler  = NULL;
+    IOHandler*  write_handler = NULL;
+
+    if (events & SYS_EVENT_READ) {
+        read_handler = sys_channel_read_handler;
+    }
+    if (events & SYS_EVENT_WRITE) {
+        write_handler = sys_channel_write_handler;
+    }
+    channel->callback = event_callback;
+    channel->opaque   = event_opaque;
+    qemu_set_fd_handler( channel->fd, read_handler, write_handler, channel );
+}
+
+int
+sys_channel_read( SysChannel  channel, void*  buffer, int  size )
+{
+    int   len = size;
+    char* buf = (char*) buffer;
+
+    while (len > 0) {
+        int  ret = socket_recv(channel->fd, buf, len);
+        if (ret < 0) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EWOULDBLOCK)
+                break;
+            D( "%s: after reading %d bytes, recv() returned error %d: %s\n",
+                __FUNCTION__, size - len, errno, errno_str);
+            return -1;
+        } else if (ret == 0) {
+            break;
+        } else {
+            buf += ret;
+            len -= ret;
+        }
+    }
+    return size - len;
+}
+
+
+int
+sys_channel_write( SysChannel  channel, const void*  buffer, int  size )
+{
+    int         len = size;
+    const char* buf = (const char*) buffer;
+
+    while (len > 0) {
+        int  ret = socket_send(channel->fd, buf, len);
+        if (ret < 0) {
+            if (errno == EINTR)
+                continue;
+            if (errno == EWOULDBLOCK)
+                break;
+            D( "%s: send() returned error %d: %s\n",
+                __FUNCTION__, errno, errno_str);
+            return -1;
+        } else if (ret == 0) {
+            break;
+        } else {
+            buf += ret;
+            len -= ret;
+        }
+    }
+    return size - len;
+}
+
+void  sys_channel_close( SysChannel  channel )
+{
+    qemu_set_fd_handler( channel->fd, NULL, NULL, NULL );
+    sys_channel_free( channel );
+}
+
+void  sys_main_init( void )
+{
+    sys_init_channels();
+    sys_init_timers();
+}
+
+
+int   sys_main_loop( void )
+{
+    /* no looping, qemu has its own event loop */
+    return 0;
+}
+
+
+
+
+SysChannel
+sys_channel_create_tcp_server( int port )
+{
+    SysChannel  channel = sys_channel_alloc();
+
+    channel->fd = socket_anyaddr_server( port, SOCKET_STREAM );
+    if (channel->fd < 0) {
+        D( "%s: failed to created network socket on TCP:%d\n", 
+            __FUNCTION__, port );
+        sys_channel_free( channel );
+        return NULL;
+    }
+
+    D( "%s: server channel %p:%d now listening on port %d\n",
+       __FUNCTION__, channel, channel->fd, port );
+
+    return channel;
+}
+
+
+SysChannel
+sys_channel_create_tcp_handler( SysChannel  server_channel )
+{
+    SysChannel  channel = sys_channel_alloc();
+
+    D( "%s: creating handler from server channel %p:%d\n", __FUNCTION__,
+       server_channel, server_channel->fd );
+
+    channel->fd = socket_accept_any( server_channel->fd );
+    if (channel->fd < 0) {
+        perror( "accept" );
+        sys_channel_free( channel );
+        return NULL;
+    }
+
+    /* disable Nagle algorithm */
+    socket_set_nodelay( channel->fd );
+
+    D( "%s: handler %p:%d created from server %p:%d\n", __FUNCTION__,
+        server_channel, server_channel->fd, channel, channel->fd );
+
+     return channel;
+}
+
+
+SysChannel
+sys_channel_create_tcp_client( const char*  hostname, int  port )
+{
+    SysChannel  channel = sys_channel_alloc();
+
+    channel->fd = socket_network_client( hostname, port, SOCKET_STREAM );
+    if (channel->fd < 0) {
+        sys_channel_free(channel);
+        return NULL;
+    };
+
+    /* set to non-blocking and disable Nagle algorithm */
+    socket_set_nonblock( channel->fd );
+    socket_set_nodelay( channel->fd );
+
+    return channel;
+}
+
diff --git a/telephony/test1.c b/telephony/test1.c
new file mode 100644
index 0000000..52701b9
--- /dev/null
+++ b/telephony/test1.c
@@ -0,0 +1,49 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sysdeps.h"
+#include <stdio.h>
+
+#define  MAX_COUNTER  10
+
+static int  counter = 0;
+
+static void
+timer_func( void*  _timer )
+{
+    SysTimer  timer = _timer;
+    SysTime   now   = sys_time_ms();
+
+    ++counter;
+    printf( "tick %d/%d a %.2fs\n", counter, MAX_COUNTER, now/1000. );
+    if (counter < MAX_COUNTER)
+        sys_timer_set( timer, now + 2000, timer_func, timer );
+    else
+        sys_timer_destroy( timer );
+}
+
+
+int  main( void )
+{
+    SysTimer  timer;
+
+    /* initialize event subsystem */
+    sys_main_init();
+
+    /* create timer and register it */
+    timer = sys_timer_create();
+    sys_timer_set( timer, sys_time_ms() + 1000, timer_func, timer );
+
+    printf("entering event loop\n");
+    sys_main_loop();
+    printf("exiting event loop\n" );
+    return 0;
+}
diff --git a/telephony/test2.c b/telephony/test2.c
new file mode 100644
index 0000000..a0cd66f
--- /dev/null
+++ b/telephony/test2.c
@@ -0,0 +1,215 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include "sysdeps.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#define  PORT          8000
+#define  MAX_COUNTER   30
+#define  INITIAL_DELAY 1000
+#define  DELAY         5000
+
+static int  counter = 0;
+
+static void
+timer_func( void*  _timer )
+{
+    SysTimer  timer = _timer;
+    SysTime   now   = sys_time_ms();
+
+    ++counter;
+    printf( "tick %d/%d a %.2fs\n", counter, MAX_COUNTER, now/1000. );
+    if (counter < MAX_COUNTER)
+        sys_timer_set( timer, now + DELAY, timer_func, timer );
+    else
+        sys_timer_destroy( timer );
+}
+
+typedef struct {
+    SysChannel   channel;
+    char         in_buff[ 128 ];
+    int          in_pos;
+
+    char         out_buff[ 128 ];
+    int          out_pos;
+    int          out_size;
+} ClientRec, *Client;
+
+static Client
+client_alloc( SysChannel  channel )
+{
+    Client  client = calloc( sizeof(*client), 1 );
+
+    client->channel = channel;
+    return client;
+}
+
+static void
+client_free( Client  client )
+{
+    sys_channel_close( client->channel );
+    client->channel = NULL;
+    free( client );
+}
+
+static void
+client_append( Client  client, const char*  str, int len );
+
+static void
+client_handle_line( Client  client, const char*  cmd )
+{
+    char temp[256];
+    int  nn, mm = 0;
+
+    for (nn = 0; cmd[nn] != 0; nn++) {
+        int  c = cmd[nn];
+        if (c >= 32 && c <= 127)
+            temp[mm++] = c;
+        else if (c == '\n') {
+            strcat( temp+mm, "<LF>" );
+            mm += 4;
+        }
+        else if (c == '\r') {
+            strcat( temp+mm, "<CR>" );
+            mm += 4;
+        }
+        else {
+            sprintf( temp+mm, "\\x%02x", c );
+            mm += strlen( temp+mm );
+        }
+    }
+    temp[mm] = 0;
+    printf( "%p: << %s\n", client, temp );
+
+    if ( !strcmp( cmd, "quit" ) ) {
+        printf( "client %p quitting\n", client );
+        client_free( client );
+        return;
+    }
+    client_append( client, "type 'quit' to quit\n", -1 );
+}
+
+static void
+client_handler( void* _client, int  events )
+{
+    Client  client = _client;
+
+    if (events & SYS_EVENT_READ) {
+        int  ret;
+        /* read into buffer, one character at a time */
+        ret = sys_channel_read( client->channel, client->in_buff + client->in_pos, 1 );
+        if (ret != 1) {
+            fprintf(stderr, "client %p could not read byte, result = %d, error: %s\n",
+                    client, ret, strerror(errno) );
+            goto ExitClient;
+        }
+        if (client->in_buff[client->in_pos] == '\r' ||
+            client->in_buff[client->in_pos] == '\n' ) {
+            const char*  cmd = client->in_buff;
+            client->in_buff[client->in_pos] = 0;
+
+            /* eat leading cr and lf, maybe left-overs from previous line */
+            while (*cmd == '\r' || *cmd =='\n')
+                cmd++;
+
+            client_handle_line( client, cmd );
+            client->in_pos = 0;
+        } else
+            client->in_pos += 1;
+    }
+
+    if (events & SYS_EVENT_WRITE) {
+        int  ret;
+        /* write from output buffer, one char at a time */
+        ret = sys_channel_write( client->channel, client->out_buff + client->out_pos, 1 );
+        if (ret != 1) {
+            fprintf(stderr, "client %p could not write byte, result = %d, error: %s\n",
+                    client, ret, strerror(errno) );
+            goto ExitClient;
+        }
+        client->out_pos += 1;
+        if (client->out_pos == client->out_size) {
+            client->out_size = 0;
+            client->out_pos  = 0;
+            /* we don't need to write */
+            sys_channel_on( client->channel, SYS_EVENT_READ, client_handler, client );
+        }
+    }
+    return;
+
+ExitClient:
+    printf( "client %p exiting\n", client );
+    client_free( client );
+}
+
+static void
+client_append( Client  client, const char*  str, int len )
+{
+    int  avail;
+
+    if (len < 0)
+        len = strlen(str);
+
+    avail = sizeof(client->out_buff) - client->out_size;
+    if (len > avail)
+        len = avail;
+
+    memcpy( client->out_buff + client->out_size, str, len );
+    if (client->out_size == 0) {
+        sys_channel_on( client->channel, SYS_EVENT_READ | SYS_EVENT_WRITE, client_handler, client );
+    }
+    client->out_size += len;
+}
+
+
+static void
+accept_func( void*  _server, int  events )
+{
+    SysChannel  server  = _server;
+    SysChannel  handler;
+    Client      client;
+
+    printf( "connection accepted for server channel, getting handler socket\n" );
+    handler = sys_channel_create_tcp_handler( server );
+    printf( "got one. creating client\n" );
+    client  = client_alloc( handler );
+
+    events=events;
+    sys_channel_on( handler, SYS_EVENT_READ, client_handler, client );
+    client_append( client, "Welcome !\n", -1 );
+}
+
+
+int  main( void )
+{
+    SysTimer    timer;
+    SysChannel  server_channel;
+
+    /* initialize event subsystem */
+    sys_main_init();
+
+    /* create timer and register it */
+    timer = sys_timer_create();
+    sys_timer_set( timer, sys_time_ms() + INITIAL_DELAY, timer_func, timer );
+
+    server_channel = sys_channel_create_tcp_server( PORT );
+    printf( "listening on port %d with %p\n", PORT, server_channel );
+
+    sys_channel_on( server_channel, SYS_EVENT_READ, accept_func, server_channel );
+
+    printf("entering event loop\n");
+    sys_main_loop();
+    printf("exiting event loop\n" );
+    return 0;
+}
diff --git a/thunk.c b/thunk.c
new file mode 100644
index 0000000..7331aeb
--- /dev/null
+++ b/thunk.c
@@ -0,0 +1,288 @@
+/*
+ *  Generic thunking code to convert data between host and target CPU
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "qemu.h"
+#include "thunk.h"
+
+//#define DEBUG
+
+#define MAX_STRUCTS 128
+
+/* XXX: make it dynamic */
+StructEntry struct_entries[MAX_STRUCTS];
+
+static const argtype *thunk_type_next_ptr(const argtype *type_ptr);
+
+static inline const argtype *thunk_type_next(const argtype *type_ptr)
+{
+    int type;
+
+    type = *type_ptr++;
+    switch(type) {
+    case TYPE_CHAR:
+    case TYPE_SHORT:
+    case TYPE_INT:
+    case TYPE_LONGLONG:
+    case TYPE_ULONGLONG:
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+        return type_ptr;
+    case TYPE_PTR:
+        return thunk_type_next_ptr(type_ptr);
+    case TYPE_ARRAY:
+        return thunk_type_next_ptr(type_ptr + 1);
+    case TYPE_STRUCT:
+        return type_ptr + 1;
+    default:
+        return NULL;
+    }
+}
+
+static const argtype *thunk_type_next_ptr(const argtype *type_ptr)
+{
+    return thunk_type_next(type_ptr);
+}
+
+void thunk_register_struct(int id, const char *name, const argtype *types)
+{
+    const argtype *type_ptr;
+    StructEntry *se;
+    int nb_fields, offset, max_align, align, size, i, j;
+
+    se = struct_entries + id;
+
+    /* first we count the number of fields */
+    type_ptr = types;
+    nb_fields = 0;
+    while (*type_ptr != TYPE_NULL) {
+        type_ptr = thunk_type_next(type_ptr);
+        nb_fields++;
+    }
+    se->field_types = types;
+    se->nb_fields = nb_fields;
+    se->name = name;
+#ifdef DEBUG
+    printf("struct %s: id=%d nb_fields=%d\n",
+           se->name, id, se->nb_fields);
+#endif
+    /* now we can alloc the data */
+
+    for(i = 0;i < 2; i++) {
+        offset = 0;
+        max_align = 1;
+        se->field_offsets[i] = malloc(nb_fields * sizeof(int));
+        type_ptr = se->field_types;
+        for(j = 0;j < nb_fields; j++) {
+            size = thunk_type_size(type_ptr, i);
+            align = thunk_type_align(type_ptr, i);
+            offset = (offset + align - 1) & ~(align - 1);
+            se->field_offsets[i][j] = offset;
+            offset += size;
+            if (align > max_align)
+                max_align = align;
+            type_ptr = thunk_type_next(type_ptr);
+        }
+        offset = (offset + max_align - 1) & ~(max_align - 1);
+        se->size[i] = offset;
+        se->align[i] = max_align;
+#ifdef DEBUG
+        printf("%s: size=%d align=%d\n",
+               i == THUNK_HOST ? "host" : "target", offset, max_align);
+#endif
+    }
+}
+
+void thunk_register_struct_direct(int id, const char *name, StructEntry *se1)
+{
+    StructEntry *se;
+    se = struct_entries + id;
+    *se = *se1;
+    se->name = name;
+}
+
+
+/* now we can define the main conversion functions */
+const argtype *thunk_convert(void *dst, const void *src,
+                             const argtype *type_ptr, int to_host)
+{
+    int type;
+
+    type = *type_ptr++;
+    switch(type) {
+    case TYPE_CHAR:
+        *(uint8_t *)dst = *(uint8_t *)src;
+        break;
+    case TYPE_SHORT:
+        *(uint16_t *)dst = tswap16(*(uint16_t *)src);
+        break;
+    case TYPE_INT:
+        *(uint32_t *)dst = tswap32(*(uint32_t *)src);
+        break;
+    case TYPE_LONGLONG:
+    case TYPE_ULONGLONG:
+        *(uint64_t *)dst = tswap64(*(uint64_t *)src);
+        break;
+#if HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 32
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+        *(uint32_t *)dst = tswap32(*(uint32_t *)src);
+        break;
+#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 32
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+        if (to_host) {
+            if (type == TYPE_LONG) {
+                /* sign extension */
+                *(uint64_t *)dst = (int32_t)tswap32(*(uint32_t *)src);
+            } else {
+                *(uint64_t *)dst = tswap32(*(uint32_t *)src);
+            }
+        } else {
+            *(uint32_t *)dst = tswap32(*(uint64_t *)src & 0xffffffff);
+        }
+        break;
+#elif HOST_LONG_BITS == 64 && TARGET_ABI_BITS == 64
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+        *(uint64_t *)dst = tswap64(*(uint64_t *)src);
+        break;
+#elif HOST_LONG_BITS == 32 && TARGET_ABI_BITS == 64
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+        if (to_host) {
+            *(uint32_t *)dst = tswap64(*(uint64_t *)src);
+        } else {
+            if (type == TYPE_LONG) {
+                /* sign extension */
+                *(uint64_t *)dst = tswap64(*(int32_t *)src);
+            } else {
+                *(uint64_t *)dst = tswap64(*(uint32_t *)src);
+            }
+        }
+        break;
+#else
+#warning unsupported conversion
+#endif
+    case TYPE_ARRAY:
+        {
+            int array_length, i, dst_size, src_size;
+            const uint8_t *s;
+            uint8_t  *d;
+
+            array_length = *type_ptr++;
+            dst_size = thunk_type_size(type_ptr, to_host);
+            src_size = thunk_type_size(type_ptr, 1 - to_host);
+            d = dst;
+            s = src;
+            for(i = 0;i < array_length; i++) {
+                thunk_convert(d, s, type_ptr, to_host);
+                d += dst_size;
+                s += src_size;
+            }
+            type_ptr = thunk_type_next(type_ptr);
+        }
+        break;
+    case TYPE_STRUCT:
+        {
+            int i;
+            const StructEntry *se;
+            const uint8_t *s;
+            uint8_t  *d;
+            const argtype *field_types;
+            const int *dst_offsets, *src_offsets;
+
+            se = struct_entries + *type_ptr++;
+            if (se->convert[0] != NULL) {
+                /* specific conversion is needed */
+                (*se->convert[to_host])(dst, src);
+            } else {
+                /* standard struct conversion */
+                field_types = se->field_types;
+                dst_offsets = se->field_offsets[to_host];
+                src_offsets = se->field_offsets[1 - to_host];
+                d = dst;
+                s = src;
+                for(i = 0;i < se->nb_fields; i++) {
+                    field_types = thunk_convert(d + dst_offsets[i],
+                                                s + src_offsets[i],
+                                                field_types, to_host);
+                }
+            }
+        }
+        break;
+    default:
+        fprintf(stderr, "Invalid type 0x%x\n", type);
+        break;
+    }
+    return type_ptr;
+}
+
+/* from em86 */
+
+/* Utility function: Table-driven functions to translate bitmasks
+ * between X86 and Alpha formats...
+ */
+unsigned int target_to_host_bitmask(unsigned int x86_mask,
+                                    bitmask_transtbl * trans_tbl)
+{
+    bitmask_transtbl *	btp;
+    unsigned int	alpha_mask = 0;
+
+    for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
+	if((x86_mask & btp->x86_mask) == btp->x86_bits) {
+	    alpha_mask |= btp->alpha_bits;
+	}
+    }
+    return(alpha_mask);
+}
+
+unsigned int host_to_target_bitmask(unsigned int alpha_mask,
+                                    bitmask_transtbl * trans_tbl)
+{
+    bitmask_transtbl *	btp;
+    unsigned int	x86_mask = 0;
+
+    for(btp = trans_tbl; btp->x86_mask && btp->alpha_mask; btp++) {
+	if((alpha_mask & btp->alpha_mask) == btp->alpha_bits) {
+	    x86_mask |= btp->x86_bits;
+	}
+    }
+    return(x86_mask);
+}
+
+#ifndef NO_THUNK_TYPE_SIZE
+int thunk_type_size_array(const argtype *type_ptr, int is_host)
+{
+    return thunk_type_size(type_ptr, is_host);
+}
+
+int thunk_type_align_array(const argtype *type_ptr, int is_host)
+{
+    return thunk_type_align(type_ptr, is_host);
+}
+#endif /* ndef NO_THUNK_TYPE_SIZE */
diff --git a/thunk.h b/thunk.h
new file mode 100644
index 0000000..d650fa4
--- /dev/null
+++ b/thunk.h
@@ -0,0 +1,161 @@
+/*
+ *  Generic thunking code to convert data between host and target CPU
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef THUNK_H
+#define THUNK_H
+
+#include <inttypes.h>
+#include "cpu.h"
+
+/* types enums definitions */
+
+typedef enum argtype {
+    TYPE_NULL,
+    TYPE_CHAR,
+    TYPE_SHORT,
+    TYPE_INT,
+    TYPE_LONG,
+    TYPE_ULONG,
+    TYPE_PTRVOID, /* pointer on unknown data */
+    TYPE_LONGLONG,
+    TYPE_ULONGLONG,
+    TYPE_PTR,
+    TYPE_ARRAY,
+    TYPE_STRUCT,
+} argtype;
+
+#define MK_PTR(type) TYPE_PTR, type
+#define MK_ARRAY(type, size) TYPE_ARRAY, size, type
+#define MK_STRUCT(id) TYPE_STRUCT, id
+
+#define THUNK_TARGET 0
+#define THUNK_HOST   1
+
+typedef struct {
+    /* standard struct handling */
+    const argtype *field_types;
+    int nb_fields;
+    int *field_offsets[2];
+    /* special handling */
+    void (*convert[2])(void *dst, const void *src);
+    int size[2];
+    int align[2];
+    const char *name;
+} StructEntry;
+
+/* Translation table for bitmasks... */
+typedef struct bitmask_transtbl {
+	unsigned int	x86_mask;
+	unsigned int	x86_bits;
+	unsigned int	alpha_mask;
+	unsigned int	alpha_bits;
+} bitmask_transtbl;
+
+void thunk_register_struct(int id, const char *name, const argtype *types);
+void thunk_register_struct_direct(int id, const char *name, StructEntry *se1);
+const argtype *thunk_convert(void *dst, const void *src,
+                             const argtype *type_ptr, int to_host);
+#ifndef NO_THUNK_TYPE_SIZE
+
+extern StructEntry struct_entries[];
+
+int thunk_type_size_array(const argtype *type_ptr, int is_host);
+int thunk_type_align_array(const argtype *type_ptr, int is_host);
+
+static inline int thunk_type_size(const argtype *type_ptr, int is_host)
+{
+    int type, size;
+    const StructEntry *se;
+
+    type = *type_ptr;
+    switch(type) {
+    case TYPE_CHAR:
+        return 1;
+    case TYPE_SHORT:
+        return 2;
+    case TYPE_INT:
+        return 4;
+    case TYPE_LONGLONG:
+    case TYPE_ULONGLONG:
+        return 8;
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+    case TYPE_PTR:
+        if (is_host) {
+            return HOST_LONG_SIZE;
+        } else {
+            return TARGET_ABI_BITS / 8;
+        }
+        break;
+    case TYPE_ARRAY:
+        size = type_ptr[1];
+        return size * thunk_type_size_array(type_ptr + 2, is_host);
+    case TYPE_STRUCT:
+        se = struct_entries + type_ptr[1];
+        return se->size[is_host];
+    default:
+        return -1;
+    }
+}
+
+static inline int thunk_type_align(const argtype *type_ptr, int is_host)
+{
+    int type;
+    const StructEntry *se;
+
+    type = *type_ptr;
+    switch(type) {
+    case TYPE_CHAR:
+        return 1;
+    case TYPE_SHORT:
+        return 2;
+    case TYPE_INT:
+        return 4;
+    case TYPE_LONGLONG:
+    case TYPE_ULONGLONG:
+        return 8;
+    case TYPE_LONG:
+    case TYPE_ULONG:
+    case TYPE_PTRVOID:
+    case TYPE_PTR:
+        if (is_host) {
+            return HOST_LONG_SIZE;
+        } else {
+            return TARGET_ABI_BITS / 8;
+        }
+        break;
+    case TYPE_ARRAY:
+        return thunk_type_align_array(type_ptr + 2, is_host);
+    case TYPE_STRUCT:
+        se = struct_entries + type_ptr[1];
+        return se->align[is_host];
+    default:
+        return -1;
+    }
+}
+
+#endif /* NO_THUNK_TYPE_SIZE */
+
+unsigned int target_to_host_bitmask(unsigned int x86_mask,
+                                    bitmask_transtbl * trans_tbl);
+unsigned int host_to_target_bitmask(unsigned int alpha_mask,
+                                    bitmask_transtbl * trans_tbl);
+
+#endif
diff --git a/trace.c b/trace.c
new file mode 100644
index 0000000..a96f87f
--- /dev/null
+++ b/trace.c
@@ -0,0 +1,1879 @@
+/* Copyright (C) 2006-2007 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <time.h>
+#include "cpu.h"
+#include "exec-all.h"
+#include "trace.h"
+#include "varint.h"
+
+TraceBB trace_bb;
+TraceInsn trace_insn;
+TraceStatic trace_static;
+TraceAddr trace_load;
+TraceAddr trace_store;
+TraceExc trace_exc;
+TracePid trace_pid;
+TraceMethod trace_method;
+static TraceHeader header;
+
+const char *trace_filename;
+int tracing;
+int trace_cache_miss;
+int trace_all_addr;
+
+// The simulation time in cpu clock cycles
+uint64_t sim_time = 1;
+
+// The current process id
+int current_pid;
+
+// The start and end (wall-clock) time in microseconds
+uint64_t start_time, end_time;
+uint64_t elapsed_usecs;
+
+// For debugging output
+FILE *ftrace_debug;
+
+// The maximum number of bytes consumed by an InsnRec after compression.
+// This is very conservative but needed to ensure no buffer overflows.
+#define kMaxInsnCompressed 14
+
+// The maximum number of bytes consumed by an BBRec after compression.
+// This is very conservative but needed to ensure no buffer overflows.
+#define kMaxBBCompressed 32
+
+// The maximum number of bytes consumed by an AddrRec after compression.
+// This is very conservative but needed to ensure no buffer overflows.
+#define kMaxAddrCompressed 14
+
+// The maximum number of bytes consumed by a MethodRec after compression.
+// This is very conservative but needed to ensure no buffer overflows.
+#define kMaxMethodCompressed 18
+
+// The maximum number of bytes consumed by an exception record after
+// compression.
+#define kMaxExcCompressed 38
+
+// The maximum number of bytes consumed by a pid record for
+// kPidSwitch, or kPidExit after compression.
+#define kMaxPidCompressed 15
+
+// The maximum number of bytes consumed by a pid record for kPidFork,
+// or kPidClone after compression.
+#define kMaxPid2Compressed 20
+
+// The maximum number of bytes consumed by a pid record for kPidExecArgs
+// after compression, not counting the bytes for the args.
+#define kMaxExecArgsCompressed 15
+
+// The maximum number of bytes consumed by a pid record for kPidName
+// after compression, not counting the bytes for the name.
+#define kMaxNameCompressed 20
+
+// The maximum number of bytes consumed by a pid record for kPidMmap
+// after compression, not counting the bytes for the pathname.
+#define kMaxMmapCompressed 33
+
+// The maximum number of bytes consumed by a pid record for kPidMunmap,
+// after compression.
+#define kMaxMunmapCompressed 28
+
+// The maximum number of bytes consumed by a pid record for kPidSymbol
+// after compression, not counting the bytes for the symbol name.
+#define kMaxSymbolCompressed 24
+
+// The maximum number of bytes consumed by a pid record for kPidKthreadName
+// after compression, not counting the bytes for the name.
+#define kMaxKthreadNameCompressed 25
+
+void trace_cleanup();
+
+// Return current time in microseconds as a 64-bit integer.
+uint64 Now() {
+    struct timeval        tv;
+
+    gettimeofday(&tv, NULL);
+    uint64 val = tv.tv_sec;
+    val = val * 1000000ull + tv.tv_usec;
+    return val;
+}
+
+static void create_trace_dir(const char *dirname)
+{
+    int err;
+
+    err = path_mkdir(dirname, 0755);
+    if (err != 0 && errno != EEXIST) {
+        printf("err: %d\n", err);
+        perror(dirname);
+        exit(1);
+    }
+}
+
+static char *create_trace_path(const char *filename, const char *ext)
+{
+    char *fname;
+    const char *base_start, *base_end;
+    int ii, len, base_len, dir_len, path_len, qtrace_len;
+
+    // Handle error cases
+    if (filename == NULL || *filename == 0 || strcmp(filename, "/") == 0)
+        return NULL;
+
+    // Ignore a trailing slash, if any
+    len = strlen(filename);
+    if (filename[len - 1] == '/')
+        len -= 1;
+
+    // Find the basename.  We don't use basename(3) because there are
+    // different behaviors for GNU and Posix in the case where the
+    // last character is a slash.
+    base_start = base_end = &filename[len];
+    for (ii = 0; ii < len; ++ii) {
+        base_start -= 1;
+        if (*base_start == '/') {
+            base_start += 1;
+            break;
+        }
+    }
+    base_len = base_end - base_start;
+    dir_len = len - base_len;
+    qtrace_len = strlen("/qtrace");
+
+    // Create space for the pathname: "/dir/basename/qtrace.ext"
+    // The "ext" string already contains the dot, so just add a byte
+    // for the terminating zero.
+    path_len = dir_len + base_len + qtrace_len + strlen(ext) + 1;
+    fname = malloc(path_len);
+    if (dir_len > 0)
+        strncpy(fname, filename, dir_len);
+    fname[dir_len] = 0;
+    strncat(fname, base_start, base_len);
+    strcat(fname, "/qtrace");
+    strcat(fname, ext);
+    return fname;
+}
+
+void convert_secs_to_date_time(time_t secs, uint32_t *pdate, uint32_t *ptime)
+{
+    struct tm *tm = localtime(&secs);
+    uint32_t year = tm->tm_year + 1900;
+    uint32_t thousands = year / 1000;
+    year -= thousands * 1000;
+    uint32_t hundreds = year / 100;
+    year -= hundreds * 100;
+    uint32_t tens = year / 10;
+    year -= tens * 10;
+    uint32_t ones = year;
+    year = (thousands << 12) | (hundreds << 8) | (tens << 4) | ones;
+
+    uint32_t mon = tm->tm_mon + 1;
+    tens = mon / 10;
+    ones = (mon - tens * 10);
+    mon = (tens << 4) | ones;
+
+    uint32_t day = tm->tm_mday;
+    tens = day / 10;
+    ones = (day - tens * 10);
+    day = (tens << 4) | ones;
+
+    *pdate = (year << 16) | (mon << 8) | day;
+
+    uint32_t hour = tm->tm_hour;
+    tens = hour / 10;
+    ones = (hour - tens * 10);
+    hour = (tens << 4) | ones;
+
+    uint32_t min = tm->tm_min;
+    tens = min / 10;
+    ones = (min - tens * 10);
+    min = (tens << 4) | ones;
+
+    uint32_t sec = tm->tm_sec;
+    tens = sec / 10;
+    ones = (sec - tens * 10);
+    sec = (tens << 4) | ones;
+
+    *ptime = (hour << 16) | (min << 8) | sec;
+}
+
+void write_trace_header(TraceHeader *header)
+{
+    TraceHeader swappedHeader;
+
+    memcpy(&swappedHeader, header, sizeof(TraceHeader));
+
+    convert32(swappedHeader.version);
+    convert32(swappedHeader.start_sec);
+    convert32(swappedHeader.start_usec);
+    convert32(swappedHeader.pdate);
+    convert32(swappedHeader.ptime);
+    convert32(swappedHeader.num_used_pids);
+    convert32(swappedHeader.first_unused_pid);
+    convert64(swappedHeader.num_static_bb);
+    convert64(swappedHeader.num_static_insn);
+    convert64(swappedHeader.num_dynamic_bb);
+    convert64(swappedHeader.num_dynamic_insn);
+    convert64(swappedHeader.elapsed_usecs);
+
+    fwrite(&swappedHeader, sizeof(TraceHeader), 1, trace_static.fstream);
+}
+
+void create_trace_bb(const char *filename)
+{
+    char *fname = create_trace_path(filename, ".bb");
+    trace_bb.filename = fname;
+
+    FILE *fstream = fopen(fname, "wb");
+    if (fstream == NULL) {
+        perror(fname);
+        exit(1);
+    }
+    trace_bb.fstream = fstream;
+    trace_bb.next = &trace_bb.buffer[0];
+    trace_bb.flush_time = 0;
+    trace_bb.compressed_ptr = trace_bb.compressed;
+    trace_bb.high_water_ptr = &trace_bb.compressed[kCompressedSize] - kMaxBBCompressed;
+    trace_bb.prev_bb_num = 0;
+    trace_bb.prev_bb_time = 0;
+    trace_bb.num_insns = 0;
+    trace_bb.recnum = 0;
+}
+
+void create_trace_insn(const char *filename)
+{
+    // Create the instruction time trace file
+    char *fname = create_trace_path(filename, ".insn");
+    trace_insn.filename = fname;
+
+    FILE *fstream = fopen(fname, "wb");
+    if (fstream == NULL) {
+        perror(fname);
+        exit(1);
+    }
+    trace_insn.fstream = fstream;
+    trace_insn.current = &trace_insn.dummy;
+    trace_insn.dummy.time_diff = 0;
+    trace_insn.dummy.repeat = 0;
+    trace_insn.prev_time = 0;
+    trace_insn.compressed_ptr = trace_insn.compressed;
+    trace_insn.high_water_ptr = &trace_insn.compressed[kCompressedSize] - kMaxInsnCompressed;
+}
+
+void create_trace_static(const char *filename)
+{
+    // Create the static basic block trace file
+    char *fname = create_trace_path(filename, ".static");
+    trace_static.filename = fname;
+
+    FILE *fstream = fopen(fname, "wb");
+    if (fstream == NULL) {
+        perror(fname);
+        exit(1);
+    }
+    trace_static.fstream = fstream;
+    trace_static.next_insn = 0;
+    trace_static.bb_num = 1;
+    trace_static.bb_addr = 0;
+
+    // Write an empty header to reserve space for it in the file.
+    // The header will be filled in later when post-processing the
+    // trace file.
+    memset(&header, 0, sizeof(TraceHeader));
+
+    // Write out the version number so that tools can detect if the trace
+    // file format is the same as what they expect.
+    header.version = TRACE_VERSION;
+
+    // Record the start time in the header now.
+    struct timeval tv;
+    struct timezone tz;
+    gettimeofday(&tv, &tz);
+    header.start_sec = tv.tv_sec;
+    header.start_usec = tv.tv_usec;
+    convert_secs_to_date_time(header.start_sec, &header.pdate, &header.ptime);
+    write_trace_header(&header);
+
+    // Write out the record for the unused basic block number 0.
+    uint64_t zero = 0;
+    fwrite(&zero, sizeof(uint64_t), 1, trace_static.fstream);	// bb_num
+    fwrite(&zero, sizeof(uint32_t), 1, trace_static.fstream);	// bb_addr
+    fwrite(&zero, sizeof(uint32_t), 1, trace_static.fstream);	// num_insns
+}
+
+void create_trace_addr(const char *filename)
+{
+    // The "qtrace.load" and "qtrace.store" files are optional
+    trace_load.fstream = NULL;
+    trace_store.fstream = NULL;
+    if (trace_all_addr || trace_cache_miss) {
+        // Create the "qtrace.load" file
+        char *fname = create_trace_path(filename, ".load");
+        trace_load.filename = fname;
+
+        FILE *fstream = fopen(fname, "wb");
+        if (fstream == NULL) {
+            perror(fname);
+            exit(1);
+        }
+        trace_load.fstream = fstream;
+        trace_load.next = &trace_load.buffer[0];
+        trace_load.compressed_ptr = trace_load.compressed;
+        trace_load.high_water_ptr = &trace_load.compressed[kCompressedSize] - kMaxAddrCompressed;
+        trace_load.prev_addr = 0;
+        trace_load.prev_time = 0;
+
+        // Create the "qtrace.store" file
+        fname = create_trace_path(filename, ".store");
+        trace_store.filename = fname;
+
+        fstream = fopen(fname, "wb");
+        if (fstream == NULL) {
+            perror(fname);
+            exit(1);
+        }
+        trace_store.fstream = fstream;
+        trace_store.next = &trace_store.buffer[0];
+        trace_store.compressed_ptr = trace_store.compressed;
+        trace_store.high_water_ptr = &trace_store.compressed[kCompressedSize] - kMaxAddrCompressed;
+        trace_store.prev_addr = 0;
+        trace_store.prev_time = 0;
+    }
+}
+
+void create_trace_exc(const char *filename)
+{
+    // Create the exception trace file
+    char *fname = create_trace_path(filename, ".exc");
+    trace_exc.filename = fname;
+
+    FILE *fstream = fopen(fname, "wb");
+    if (fstream == NULL) {
+        perror(fname);
+        exit(1);
+    }
+    trace_exc.fstream = fstream;
+    trace_exc.compressed_ptr = trace_exc.compressed;
+    trace_exc.high_water_ptr = &trace_exc.compressed[kCompressedSize] - kMaxExcCompressed;
+    trace_exc.prev_time = 0;
+    trace_exc.prev_bb_recnum = 0;
+}
+
+void create_trace_pid(const char *filename)
+{
+    // Create the pid trace file
+    char *fname = create_trace_path(filename, ".pid");
+    trace_pid.filename = fname;
+
+    FILE *fstream = fopen(fname, "wb");
+    if (fstream == NULL) {
+        perror(fname);
+        exit(1);
+    }
+    trace_pid.fstream = fstream;
+    trace_pid.compressed_ptr = trace_pid.compressed;
+    trace_pid.prev_time = 0;
+}
+
+void create_trace_method(const char *filename)
+{
+    // Create the method trace file
+    char *fname = create_trace_path(filename, ".method");
+    trace_method.filename = fname;
+
+    FILE *fstream = fopen(fname, "wb");
+    if (fstream == NULL) {
+        perror(fname);
+        exit(1);
+    }
+    trace_method.fstream = fstream;
+    trace_method.compressed_ptr = trace_method.compressed;
+    trace_method.prev_time = 0;
+    trace_method.prev_addr = 0;
+    trace_method.prev_pid = 0;
+}
+
+void trace_init(const char *filename)
+{
+    // Create the trace files
+    create_trace_dir(filename);
+    create_trace_bb(filename);
+    create_trace_insn(filename);
+    create_trace_static(filename);
+    create_trace_addr(filename);
+    create_trace_exc(filename);
+    create_trace_pid(filename);
+    create_trace_method(filename);
+
+#if 0
+    char *fname = create_trace_path(filename, ".debug");
+    ftrace_debug = fopen(fname, "wb");
+    if (ftrace_debug == NULL) {
+        perror(fname);
+        exit(1);
+    }
+#else
+    ftrace_debug = NULL;
+#endif
+    atexit(trace_cleanup);
+
+    // If tracing is on, then start timing the simulator
+    if (tracing)
+        start_time = Now();
+}
+
+/* the following array is used to deal with def-use register interlocks, which we
+ * can compute statically (ignoring conditions), very fortunately.
+ *
+ * the idea is that interlock_base contains the number of cycles "executed" from
+ * the start of a basic block. It is set to 0 in trace_bb_start, and incremented
+ * in each call to get_insn_ticks_arm.
+ *
+ * interlocks[N] correspond to the value of interlock_base after which a register N
+ * can be used by another operation, it is set each time an instruction writes to
+ * the register in get_insn_ticks()
+ */
+
+static int   interlocks[16];
+static int   interlock_base;
+
+static void
+_interlock_def(int  reg, int  delay)
+{
+    if (reg >= 0)
+        interlocks[reg] = interlock_base + delay;
+}
+
+static int
+_interlock_use(int  reg)
+{
+    int  delay = 0;
+
+    if (reg >= 0)
+    {
+        delay = interlocks[reg] - interlock_base;
+        if (delay < 0)
+            delay = 0;
+    }
+    return delay;
+}
+
+void trace_bb_start(uint32_t bb_addr)
+{
+    int  nn;
+
+    trace_static.bb_addr = bb_addr;
+    trace_static.is_thumb = 0;
+
+    interlock_base = 0;
+    for (nn = 0; nn < 16; nn++)
+        interlocks[nn] = 0;
+}
+
+void trace_add_insn(uint32_t insn, int is_thumb)
+{
+    trace_static.insns[trace_static.next_insn++] = insn;
+    // This relies on the fact that a basic block does not contain a mix
+    // of ARM and Thumb instructions.  If that is not true, then many
+    // software tools that read the trace will have to change.
+    trace_static.is_thumb = is_thumb;
+}
+
+void trace_bb_end()
+{
+    int		ii, num_insns;
+    uint32_t	insn;
+
+    uint64_t bb_num = hostToLE64(trace_static.bb_num);
+    // If these are Thumb instructions, then encode that fact by setting
+    // the low bit of the basic-block address to 1.
+    uint32_t bb_addr = trace_static.bb_addr | trace_static.is_thumb;
+    bb_addr = hostToLE32(bb_addr);
+    num_insns = hostToLE32(trace_static.next_insn);
+    fwrite(&bb_num, sizeof(bb_num), 1, trace_static.fstream);
+    fwrite(&bb_addr, sizeof(bb_addr), 1, trace_static.fstream);
+    fwrite(&num_insns, sizeof(num_insns), 1, trace_static.fstream);
+    for (ii = 0; ii < trace_static.next_insn; ++ii) {
+        insn = hostToLE32(trace_static.insns[ii]);
+        fwrite(&insn, sizeof(insn), 1, trace_static.fstream);
+    }
+
+    trace_static.bb_num += 1;
+    trace_static.next_insn = 0;
+}
+
+void trace_cleanup()
+{
+    if (tracing) {
+        end_time = Now();
+        elapsed_usecs += end_time - start_time;
+    }
+    header.elapsed_usecs = elapsed_usecs;
+    double elapsed_secs = elapsed_usecs / 1000000.0;
+    double cycles_per_sec = 0;
+    if (elapsed_secs != 0)
+        cycles_per_sec = sim_time / elapsed_secs;
+    char *suffix = "";
+    if (cycles_per_sec >= 1000000) {
+        cycles_per_sec /= 1000000.0;
+        suffix = "M";
+    } else if (cycles_per_sec > 1000) {
+        cycles_per_sec /= 1000.0;
+        suffix = "K";
+    }
+    printf("Elapsed seconds: %.2f, simulated cycles/sec: %.1f%s\n",
+           elapsed_secs, cycles_per_sec, suffix);
+    if (trace_bb.fstream) {
+        BBRec *ptr;
+        BBRec *next = trace_bb.next;
+        char *comp_ptr = trace_bb.compressed_ptr;
+        int64_t prev_bb_num = trace_bb.prev_bb_num;
+        uint64_t prev_bb_time = trace_bb.prev_bb_time;
+        for (ptr = trace_bb.buffer; ptr != next; ++ptr) {
+            if (comp_ptr >= trace_bb.high_water_ptr) {
+                uint32_t size = comp_ptr - trace_bb.compressed;
+                fwrite(trace_bb.compressed, sizeof(char), size,
+                       trace_bb.fstream);
+                comp_ptr = trace_bb.compressed;
+            }
+            int64_t bb_diff = ptr->bb_num - prev_bb_num;
+            prev_bb_num = ptr->bb_num;
+            uint64_t time_diff = ptr->start_time - prev_bb_time;
+            prev_bb_time = ptr->start_time;
+            comp_ptr = varint_encode_signed(bb_diff, comp_ptr);
+            comp_ptr = varint_encode(time_diff, comp_ptr);
+            comp_ptr = varint_encode(ptr->repeat, comp_ptr);
+            if (ptr->repeat)
+                comp_ptr = varint_encode(ptr->time_diff, comp_ptr);
+        }
+
+        // Add an extra record at the end containing the ending simulation
+        // time and a basic block number of 0.
+        uint64_t time_diff = sim_time - prev_bb_time;
+        if (time_diff > 0) {
+            int64_t bb_diff = -prev_bb_num;
+            comp_ptr = varint_encode_signed(bb_diff, comp_ptr);
+            comp_ptr = varint_encode(time_diff, comp_ptr);
+            comp_ptr = varint_encode(0, comp_ptr);
+        }
+
+        uint32_t size = comp_ptr - trace_bb.compressed;
+        if (size)
+            fwrite(trace_bb.compressed, sizeof(char), size, trace_bb.fstream);
+
+        // Terminate the file with three zeros so that we can detect
+        // the end of file quickly.
+        uint32_t zeros = 0;
+        fwrite(&zeros, 3, 1, trace_bb.fstream);
+        fclose(trace_bb.fstream);
+    }
+
+    if (trace_insn.fstream) {
+        InsnRec *ptr;
+        InsnRec *current = trace_insn.current + 1;
+        char *comp_ptr = trace_insn.compressed_ptr;
+        for (ptr = trace_insn.buffer; ptr != current; ++ptr) {
+            if (comp_ptr >= trace_insn.high_water_ptr) {
+                uint32_t size = comp_ptr - trace_insn.compressed;
+                uint32_t rval = fwrite(trace_insn.compressed, sizeof(char),
+                                       size, trace_insn.fstream);
+                if (rval != size) {
+                    fprintf(stderr, "fwrite() failed\n");
+                    perror(trace_insn.filename);
+                    exit(1);
+                }
+                comp_ptr = trace_insn.compressed;
+            }
+            comp_ptr = varint_encode(ptr->time_diff, comp_ptr);
+            comp_ptr = varint_encode(ptr->repeat, comp_ptr);
+        }
+
+        uint32_t size = comp_ptr - trace_insn.compressed;
+        if (size) {
+            uint32_t rval = fwrite(trace_insn.compressed, sizeof(char), size,
+                                   trace_insn.fstream);
+            if (rval != size) {
+                fprintf(stderr, "fwrite() failed\n");
+                perror(trace_insn.filename);
+                exit(1);
+            }
+        }
+        fclose(trace_insn.fstream);
+    }
+
+    if (trace_static.fstream) {
+        fseek(trace_static.fstream, 0, SEEK_SET);
+        write_trace_header(&header);
+        fclose(trace_static.fstream);
+    }
+
+    if (trace_load.fstream) {
+        AddrRec *ptr;
+        char *comp_ptr = trace_load.compressed_ptr;
+        AddrRec *next = trace_load.next;
+        uint32_t prev_addr = trace_load.prev_addr;
+        uint64_t prev_time = trace_load.prev_time;
+        for (ptr = trace_load.buffer; ptr != next; ++ptr) {
+            if (comp_ptr >= trace_load.high_water_ptr) {
+                uint32_t size = comp_ptr - trace_load.compressed;
+                fwrite(trace_load.compressed, sizeof(char), size,
+                       trace_load.fstream);
+                comp_ptr = trace_load.compressed;
+            }
+
+            int addr_diff = ptr->addr - prev_addr;
+            uint64_t time_diff = ptr->time - prev_time;
+            prev_addr = ptr->addr;
+            prev_time = ptr->time;
+
+            comp_ptr = varint_encode_signed(addr_diff, comp_ptr);
+            comp_ptr = varint_encode(time_diff, comp_ptr);
+        }
+
+        uint32_t size = comp_ptr - trace_load.compressed;
+        if (size) {
+            fwrite(trace_load.compressed, sizeof(char), size,
+                   trace_load.fstream);
+        }
+
+        // Terminate the file with two zeros so that we can detect
+        // the end of file quickly.
+        uint32_t zeros = 0;
+        fwrite(&zeros, 2, 1, trace_load.fstream);
+        fclose(trace_load.fstream);
+    }
+
+    if (trace_store.fstream) {
+        AddrRec *ptr;
+        char *comp_ptr = trace_store.compressed_ptr;
+        AddrRec *next = trace_store.next;
+        uint32_t prev_addr = trace_store.prev_addr;
+        uint64_t prev_time = trace_store.prev_time;
+        for (ptr = trace_store.buffer; ptr != next; ++ptr) {
+            if (comp_ptr >= trace_store.high_water_ptr) {
+                uint32_t size = comp_ptr - trace_store.compressed;
+                fwrite(trace_store.compressed, sizeof(char), size,
+                       trace_store.fstream);
+                comp_ptr = trace_store.compressed;
+            }
+
+            int addr_diff = ptr->addr - prev_addr;
+            uint64_t time_diff = ptr->time - prev_time;
+            prev_addr = ptr->addr;
+            prev_time = ptr->time;
+
+            comp_ptr = varint_encode_signed(addr_diff, comp_ptr);
+            comp_ptr = varint_encode(time_diff, comp_ptr);
+        }
+
+        uint32_t size = comp_ptr - trace_store.compressed;
+        if (size) {
+            fwrite(trace_store.compressed, sizeof(char), size,
+                   trace_store.fstream);
+        }
+
+        // Terminate the file with two zeros so that we can detect
+        // the end of file quickly.
+        uint32_t zeros = 0;
+        fwrite(&zeros, 2, 1, trace_store.fstream);
+        fclose(trace_store.fstream);
+    }
+
+    if (trace_exc.fstream) {
+        uint32_t size = trace_exc.compressed_ptr - trace_exc.compressed;
+        if (size) {
+            fwrite(trace_exc.compressed, sizeof(char), size,
+                   trace_exc.fstream);
+        }
+
+        // Terminate the file with 7 zeros so that we can detect
+        // the end of file quickly.
+        uint64_t zeros = 0;
+        fwrite(&zeros, 7, 1, trace_exc.fstream);
+        fclose(trace_exc.fstream);
+    }
+    if (trace_pid.fstream) {
+        uint32_t size = trace_pid.compressed_ptr - trace_pid.compressed;
+        if (size) {
+            fwrite(trace_pid.compressed, sizeof(char), size,
+                   trace_pid.fstream);
+        }
+
+        // Terminate the file with 2 zeros so that we can detect
+        // the end of file quickly.
+        uint64_t zeros = 0;
+        fwrite(&zeros, 2, 1, trace_pid.fstream);
+        fclose(trace_pid.fstream);
+    }
+    if (trace_method.fstream) {
+        uint32_t size = trace_method.compressed_ptr - trace_method.compressed;
+        if (size) {
+            fwrite(trace_method.compressed, sizeof(char), size,
+                   trace_method.fstream);
+        }
+
+        // Terminate the file with 2 zeros so that we can detect
+        // the end of file quickly.
+        uint64_t zeros = 0;
+        fwrite(&zeros, 2, 1, trace_method.fstream);
+        fclose(trace_method.fstream);
+    }
+    if (ftrace_debug)
+        fclose(ftrace_debug);
+}
+
+// Define the number of clock ticks for some instructions.  Add one to these
+// (in some cases) if there is an interlock.  We currently do not check for
+// interlocks.
+#define TICKS_OTHER	1
+#define TICKS_SMULxy	1
+#define TICKS_SMLAWy	1
+#define TICKS_SMLALxy	2
+#define TICKS_MUL	2
+#define TICKS_MLA	2
+#define TICKS_MULS	4	// no interlock penalty
+#define TICKS_MLAS	4	// no interlock penalty
+#define TICKS_UMULL	3
+#define TICKS_UMLAL	3
+#define TICKS_SMULL	3
+#define TICKS_SMLAL	3
+#define TICKS_UMULLS	5	// no interlock penalty
+#define TICKS_UMLALS	5	// no interlock penalty
+#define TICKS_SMULLS	5	// no interlock penalty
+#define TICKS_SMLALS	5	// no interlock penalty
+
+// Compute the number of cycles that this instruction will take,
+// not including any I-cache or D-cache misses.  This function
+// is called for each instruction in a basic block when that
+// block is being translated.
+int get_insn_ticks_arm(uint32_t insn)
+{
+#if 1
+    int   result   =  1;   /* by default, use 1 cycle */
+
+    /* See Chapter 12 of the ARM920T Reference Manual for details about clock cycles */
+
+    /* first check for invalid condition codes */
+    if ((insn >> 28) == 0xf)
+    {
+        if ((insn >> 25) == 0x7d) {  /* BLX */
+            result = 3;
+            goto Exit;
+        }
+        /* XXX: if we get there, we're either in an UNDEFINED instruction     */
+        /*      or in co-processor related ones. For now, only return 1 cycle */
+        goto Exit;
+    }
+
+    /* other cases */
+    switch ((insn >> 25) & 7)
+    {
+        case 0:
+            if ((insn & 0x00000090) == 0x00000090)  /* Multiplies, extra load/store, Table 3-2 */
+            {
+                /* XXX: TODO: Add support for multiplier operand content penalties in the translator */
+
+                if ((insn & 0x0fc000f0) == 0x00000090)   /* 3-2: Multiply (accumulate) */
+                {
+                    int  Rm = (insn & 15);
+                    int  Rs = (insn >> 8) & 15;
+                    int  Rn = (insn >> 12) & 15;
+
+                    if ((insn & 0x00200000) != 0) {  /* MLA */
+                        result += _interlock_use(Rn);
+                    } else {   /* MLU */
+                        if (Rn != 0)      /* UNDEFINED */
+                            goto Exit;
+                    }
+                    /* cycles=2+m, assume m=1, this should be adjusted at interpretation time */
+                    result += 2 + _interlock_use(Rm) + _interlock_use(Rs);
+                }
+                else if ((insn & 0x0f8000f0) == 0x00800090)  /* 3-2: Multiply (accumulate) long */
+                {
+                    int  Rm   = (insn & 15);
+                    int  Rs   = (insn >> 8) & 15;
+                    int  RdLo = (insn >> 12) & 15;
+                    int  RdHi = (insn >> 16) & 15;
+
+                    if ((insn & 0x00200000) != 0) { /* SMLAL & UMLAL */
+                        result += _interlock_use(RdLo) + _interlock_use(RdHi);
+                    }
+                    /* else SMLL and UMLL */
+
+                    /* cucles=3+m, assume m=1, this should be adjusted at interpretation time */
+                    result += 3 + _interlock_use(Rm) + _interlock_use(Rs);
+                }
+                else if ((insn & 0x0fd00ff0) == 0x01000090)  /* 3-2: Swap/swap byte */
+                {
+                    int  Rm = (insn & 15);
+                    int  Rd = (insn >> 8) & 15;
+
+                    result = 2 + _interlock_use(Rm);
+                    _interlock_def(Rd, result+1);
+                }
+                else if ((insn & 0x0e400ff0) == 0x00000090)  /* 3-2: load/store halfword, reg offset */
+                {
+                    int  Rm = (insn & 15);
+                    int  Rd = (insn >> 12) & 15;
+                    int  Rn = (insn >> 16) & 15;
+
+                    result += _interlock_use(Rn) + _interlock_use(Rm);
+                    if ((insn & 0x00100000) != 0)  /* it's a load, there's a 2-cycle interlock */
+                        _interlock_def(Rd, result+2);
+                }
+                else if ((insn & 0x0e400ff0) == 0x00400090)  /* 3-2: load/store halfword, imm offset */
+                {
+                    int  Rd = (insn >> 12) & 15;
+                    int  Rn = (insn >> 16) & 15;
+
+                    result += _interlock_use(Rn);
+                    if ((insn & 0x00100000) != 0)  /* it's a load, there's a 2-cycle interlock */
+                        _interlock_def(Rd, result+2);
+                }
+                else if ((insn & 0x0e500fd0) == 0x000000d0) /* 3-2: load/store two words, reg offset */
+                {
+                    /* XXX: TODO: Enhanced DSP instructions */
+                }
+                else if ((insn & 0x0e500fd0) == 0x001000d0) /* 3-2: load/store half/byte, reg offset */
+                {
+                    int  Rm = (insn & 15);
+                    int  Rd = (insn >> 12) & 15;
+                    int  Rn = (insn >> 16) & 15;
+
+                    result += _interlock_use(Rn) + _interlock_use(Rm);
+                    if ((insn & 0x00100000) != 0)  /* load, 2-cycle interlock */
+                        _interlock_def(Rd, result+2);
+                }
+                else if ((insn & 0x0e5000d0) == 0x004000d0) /* 3-2: load/store two words, imm offset */
+                {
+                    /* XXX: TODO: Enhanced DSP instructions */
+                }
+                else if ((insn & 0x0e5000d0) == 0x005000d0) /* 3-2: load/store half/byte, imm offset */
+                {
+                    int  Rd = (insn >> 12) & 15;
+                    int  Rn = (insn >> 16) & 15;
+
+                    result += _interlock_use(Rn);
+                    if ((insn & 0x00100000) != 0)  /* load, 2-cycle interlock */
+                        _interlock_def(Rd, result+2);
+                }
+                else
+                {
+                    /* UNDEFINED */
+                }
+            }
+            else if ((insn & 0x0f900000) == 0x01000000)  /* Misc. instructions, table 3-3 */
+            {
+                switch ((insn >> 4) & 15)
+                {
+                    case 0:
+                        if ((insn & 0x0fb0fff0) == 0x0120f000) /* move register to status register */
+                        {
+                            int  Rm = (insn & 15);
+                            result += _interlock_use(Rm);
+                        }
+                        break;
+
+                    case 1:
+                        if ( ((insn & 0x0ffffff0) == 0x01200010) ||  /* branch/exchange */
+                             ((insn & 0x0fff0ff0) == 0x01600010) )   /* count leading zeroes */
+                        {
+                            int  Rm = (insn & 15);
+                            result += _interlock_use(Rm);
+                        }
+                        break;
+
+                    case 3:
+                        if ((insn & 0x0ffffff0) == 0x01200030)   /* link/exchange */
+                        {
+                            int  Rm = (insn & 15);
+                            result += _interlock_use(Rm);
+                        }
+                        break;
+
+                    default:
+                        /* TODO: Enhanced DSP instructions */
+                        ;
+                }
+            }
+            else  /* Data processing */
+            {
+                int  Rm = (insn & 15);
+                int  Rn = (insn >> 16) & 15;
+
+                result += _interlock_use(Rn) + _interlock_use(Rm);
+                if ((insn & 0x10)) {   /* register-controlled shift => 1 cycle penalty */
+                    int  Rs = (insn >> 8) & 15;
+                    result += 1 + _interlock_use(Rs);
+                }
+            }
+            break;
+
+        case 1:
+            if ((insn & 0x01900000) == 0x01900000)
+            {
+                /* either UNDEFINED or move immediate to CPSR */
+            }
+            else  /* Data processing immediate */
+            {
+                int  Rn = (insn >> 12) & 15;
+                result += _interlock_use(Rn);
+            }
+            break;
+
+        case 2:  /* load/store immediate */
+            {
+                int  Rn = (insn >> 16) & 15;
+
+                result += _interlock_use(Rn);
+                if (insn & 0x00100000) {  /* LDR */
+                    int  Rd = (insn >> 12) & 15;
+
+                    if (Rd == 15)  /* loading PC */
+                        result = 5;
+                    else
+                        _interlock_def(Rd,result+1);
+                }
+            }
+            break;
+
+        case 3:
+            if ((insn & 0x10) == 0)  /* load/store register offset */
+            {
+                int  Rm = (insn & 15);
+                int  Rn = (insn >> 16) & 15;
+
+                result += _interlock_use(Rm) + _interlock_use(Rn);
+
+                if (insn & 0x00100000) {  /* LDR */
+                    int  Rd = (insn >> 12) & 15;
+                    if (Rd == 15)
+                        result = 5;
+                    else
+                        _interlock_def(Rd,result+1);
+                }
+            }
+            /* else UNDEFINED */
+            break;
+
+        case 4:  /* load/store multiple */
+            {
+                int       Rn   = (insn >> 16) & 15;
+                uint32_t  mask = (insn & 0xffff);
+                int       count;
+
+                for (count = 0; mask; count++)
+                    mask &= (mask-1);
+
+                result += _interlock_use(Rn);
+
+                if (insn & 0x00100000)  /* LDM */
+                {
+                    int  nn;
+
+                    if (insn & 0x8000) {  /* loading PC */
+                        result = count+4;
+                    } else {  /* not loading PC */
+                        result = (count < 2) ? 2 : count;
+                    }
+                    /* create defs, all registers locked until the end of the load */
+                    for (nn = 0; nn < 15; nn++)
+                        if ((insn & (1U << nn)) != 0)
+                            _interlock_def(nn,result);
+                }
+                else  /* STM */
+                    result = (count < 2) ? 2 : count;
+            }
+            break;
+
+        case 5:  /* branch and branch+link */
+            break;
+
+        case 6:  /* coprocessor load/store */
+            {
+                int  Rn = (insn >> 16) & 15;
+
+                if (insn & 0x00100000)
+                    result += _interlock_use(Rn);
+
+                /* XXX: other things to do ? */
+            }
+            break;
+
+        default: /* i.e. 7 */
+            /* XXX: TODO: co-processor related things */
+            ;
+    }
+Exit:
+    interlock_base += result;
+    return result;
+#else /* old code - this seems to be completely buggy ?? */
+    if ((insn & 0x0ff0f090) == 0x01600080) {
+        return TICKS_SMULxy;
+    } else if ((insn & 0x0ff00090) == 0x01200080) {
+        return TICKS_SMLAWy;
+    } else if ((insn & 0x0ff00090) == 0x01400080) {
+        return TICKS_SMLALxy;
+    } else if ((insn & 0x0f0000f0) == 0x00000090) {
+        // multiply
+        uint8_t bit23 = (insn >> 23) & 0x1;
+        uint8_t bit22_U = (insn >> 22) & 0x1;
+        uint8_t bit21_A = (insn >> 21) & 0x1;
+        uint8_t bit20_S = (insn >> 20) & 0x1;
+
+        if (bit23 == 0) {
+            // 32-bit multiply
+            if (bit22_U != 0) {
+                // This is an unexpected bit pattern.
+                return TICKS_OTHER;
+            }
+            if (bit21_A == 0) {
+                if (bit20_S)
+                    return TICKS_MULS;
+                return TICKS_MUL;
+            }
+            if (bit20_S)
+                return TICKS_MLAS;
+            return TICKS_MLA;
+        }
+        // 64-bit multiply
+        if (bit22_U == 0) {
+            // Unsigned multiply long
+            if (bit21_A == 0) {
+                if (bit20_S)
+                    return TICKS_UMULLS;
+                return TICKS_UMULL;
+            }
+            if (bit20_S)
+                return TICKS_UMLALS;
+            return TICKS_UMLAL;
+        }
+        // Signed multiply long
+        if (bit21_A == 0) {
+            if (bit20_S)
+                return TICKS_SMULLS;
+            return TICKS_SMULL;
+        }
+        if (bit20_S)
+            return TICKS_SMLALS;
+        return TICKS_SMLAL;
+    }
+    return TICKS_OTHER;
+#endif
+}
+
+int  get_insn_ticks_thumb(uint32_t  insn)
+{
+#if 1
+    int  result = 1;
+
+    switch ((insn >> 11) & 31)
+    {
+        case 0:
+        case 1:
+        case 2:   /* Shift by immediate */
+            {
+                int  Rm = (insn >> 3) & 7;
+                result += _interlock_use(Rm);
+            }
+            break;
+
+        case 3:  /* Add/Substract */
+            {
+                int  Rn = (insn >> 3) & 7;
+                result += _interlock_use(Rn);
+
+                if ((insn & 0x0400) == 0) {  /* register value */
+                    int  Rm = (insn >> 6) & 7;
+                    result += _interlock_use(Rm);
+                }
+            }
+            break;
+
+        case 4:  /* move immediate */
+            break;
+
+        case 5:
+        case 6:
+        case 7:  /* add/substract/compare immediate */
+            {
+                int  Rd = (insn >> 8) & 7;
+                result += _interlock_use(Rd);
+            }
+            break;
+
+        case 8:
+            {
+                if ((insn & 0x0400) == 0)  /* data processing register */
+                {
+                    /* the registers can also be Rs and Rn in some cases */
+                    /* but they're always read anyway and located at the */
+                    /* same place, so we don't check the opcode          */
+                    int  Rm = (insn >> 3) & 7;
+                    int  Rd = (insn >> 3) & 7;
+
+                    result += _interlock_use(Rm) + _interlock_use(Rd);
+                }
+                else switch ((insn >> 8) & 3)
+                {
+                    case 0:
+                    case 1:
+                    case 2:  /* special data processing */
+                        {
+                            int  Rn = (insn & 7) | ((insn >> 4) & 0x8);
+                            int  Rm = ((insn >> 3) & 15);
+
+                            result += _interlock_use(Rn) + _interlock_use(Rm);
+                        }
+                        break;
+
+                    case 3:
+                        if ((insn & 0xff07) == 0x4700)  /* branch/exchange */
+                        {
+                            int  Rm = (insn >> 3) & 15;
+
+                            result = 3 + _interlock_use(Rm);
+                        }
+                        /* else UNDEFINED */
+                        break;
+                }
+            }
+            break;
+
+        case 9:  /* load from literal pool */
+            {
+                int  Rd = (insn >> 8) & 7;
+                _interlock_def(Rd,result+1);
+            }
+            break;
+
+        case 10:
+        case 11:  /* load/store register offset */
+            {
+                int  Rd = (insn & 7);
+                int  Rn = (insn >> 3) & 7;
+                int  Rm = (insn >> 6) & 7;
+
+                result += _interlock_use(Rn) + _interlock_use(Rm);
+
+                switch ((insn >> 9) & 7)
+                {
+                    case 0: /* STR  */
+                    case 1: /* STRH */
+                    case 2: /* STRB */
+                        result += _interlock_use(Rd);
+                        break;
+
+                    case 3: /* LDRSB */
+                    case 5: /* LDRH */
+                    case 6: /* LDRB */
+                    case 7: /* LDRSH */
+                        _interlock_def(Rd,result+2);
+                        break;
+
+                    case 4: /* LDR */
+                        _interlock_def(Rd,result+1);
+                }
+            }
+            break;
+
+        case 12:  /* store word immediate offset */
+        case 14:  /* store byte immediate offset */
+            {
+                int  Rd = (insn & 7);
+                int  Rn = (insn >> 3) & 7;
+
+                result += _interlock_use(Rd) + _interlock_use(Rn);
+            }
+            break;
+
+        case 13:  /* load word immediate offset */
+            {
+                int  Rd = (insn & 7);
+                int  Rn = (insn >> 3) & 7;
+
+                result += _interlock_use(Rn);
+                _interlock_def(Rd,result+1);
+            }
+            break;
+
+        case 15:  /* load byte immediate offset */
+            {
+                int  Rd = (insn & 7);
+                int  Rn = (insn >> 3) & 7;
+
+                result += _interlock_use(Rn);
+                _interlock_def(Rd,result+2);
+            }
+            break;
+
+        case 16:  /* store halfword immediate offset */
+            {
+                int  Rd = (insn & 7);
+                int  Rn = (insn >> 3) & 7;
+
+                result += _interlock_use(Rn) + _interlock_use(Rd);
+            }
+            break;
+
+        case 17:  /* load halfword immediate offset */
+            {
+                int  Rd = (insn & 7);
+                int  Rn = (insn >> 3) & 7;
+
+                result += _interlock_use(Rn);
+                _interlock_def(Rd,result+2);
+            }
+            break;
+
+        case 18:  /* store to stack */
+            {
+                int  Rd = (insn >> 8) & 3;
+                result += _interlock_use(Rd);
+            }
+            break;
+
+        case 19:  /* load from stack */
+            {
+                int  Rd = (insn >> 8) & 3;
+                _interlock_def(Rd,result+1);
+            }
+            break;
+
+        case 20:  /* add to PC */
+        case 21:  /* add to SP */
+            {
+                int  Rd = (insn >> 8) & 3;
+                result += _interlock_use(Rd);
+            }
+            break;
+
+        case 22:
+        case 23:  /* misc. instructions, table 6-2 */
+            {
+                if ((insn & 0xff00) == 0xb000)  /* adjust stack pointer */
+                {
+                    result += _interlock_use(14);
+                }
+                else if ((insn & 0x0600) == 0x0400)  /* push pop register list */
+                {
+                    uint32_t  mask = insn & 0x01ff;
+                    int       count, nn;
+
+                    for (count = 0; mask; count++)
+                        mask &= (mask-1);
+
+                    result = (count < 2) ? 2 : count;
+
+                    if (insn & 0x0800)  /* pop register list */
+                    {
+                        for (nn = 0; nn < 9; nn++)
+                            if (insn & (1 << nn))
+                                _interlock_def(nn, result);
+                    }
+                    else  /* push register list */
+                    {
+                        for (nn = 0; nn < 9; nn++)
+                            if (insn & (1 << nn))
+                                result += _interlock_use(nn);
+                    }
+                }
+                /* else  software breakpoint */
+            }
+            break;
+
+        case 24:  /* store multiple */
+            {
+                int  Rd = (insn >> 8) & 7;
+                uint32_t  mask = insn & 255;
+                int       count, nn;
+
+                for (count = 0; mask; count++)
+                    mask &= (mask-1);
+
+                result = (count < 2) ? 2 : count;
+                result += _interlock_use(Rd);
+
+                for (nn = 0; nn < 8; nn++)
+                    if (insn & (1 << nn))
+                        result += _interlock_use(nn);
+            }
+            break;
+
+        case 25:  /* load multiple */
+            {
+                int  Rd = (insn >> 8) & 7;
+                uint32_t  mask = insn & 255;
+                int       count, nn;
+
+                for (count = 0; mask; count++)
+                    mask &= (mask-1);
+
+                result  = (count < 2) ? 2 : count;
+                result += _interlock_use(Rd);
+
+                for (nn = 0; nn < 8; nn++)
+                    if (insn & (1 << nn))
+                        _interlock_def(nn, result);
+            }
+            break;
+
+        case 26:
+        case 27:  /* conditional branch / undefined / software interrupt */
+            switch ((insn >> 8) & 15)
+            {
+                case 14: /* UNDEFINED */
+                case 15: /* SWI */
+                    break;
+
+                default:  /* conditional branch */
+                    result = 3;
+            }
+            break;
+
+        case 28:  /* unconditional branch */
+            result = 3;
+            break;
+
+        case 29:  /* BLX suffix or undefined */
+            if ((insn & 1) == 0)
+                result = 3;
+            break;
+
+        case 30:  /* BLX/BLX prefix */
+            break;
+
+        case 31:  /* BL suffix */
+            result = 3;
+            break;
+    }
+    interlock_base += result;
+    return result;
+#else /* old code */
+    if ((insn & 0xfc00) == 0x4340) /* MUL */
+        return TICKS_SMULxy;
+
+    return TICKS_OTHER;
+#endif
+}
+
+// Adds an exception trace record.
+void trace_exception(uint32 target_pc)
+{
+    if (trace_exc.fstream == NULL)
+        return;
+
+    // Sometimes we get an unexpected exception as the first record.  If the
+    // basic block number is zero, then we know it is bogus.
+    if (trace_bb.current_bb_num == 0)
+        return;
+
+    uint32_t current_pc = trace_bb.current_bb_addr + 4 * (trace_bb.num_insns - 1);
+#if 0
+    if (ftrace_debug) {
+        fprintf(ftrace_debug, "t%llu exc pc: 0x%x bb_addr: 0x%x num_insns: %d current_pc: 0x%x bb_num %llu bb_start_time %llu\n",
+                sim_time, target_pc, trace_bb.current_bb_addr,
+                trace_bb.num_insns, current_pc, trace_bb.current_bb_num,
+                trace_bb.current_bb_start_time);
+    }
+#endif
+    char *comp_ptr = trace_exc.compressed_ptr;
+    if (comp_ptr >= trace_exc.high_water_ptr) {
+        uint32_t size = comp_ptr - trace_exc.compressed;
+        fwrite(trace_exc.compressed, sizeof(char), size, trace_exc.fstream);
+        comp_ptr = trace_exc.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_exc.prev_time;
+    trace_exc.prev_time = sim_time;
+    uint64_t bb_recnum_diff = trace_bb.recnum - trace_exc.prev_bb_recnum;
+    trace_exc.prev_bb_recnum = trace_bb.recnum;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    comp_ptr = varint_encode(current_pc, comp_ptr);
+    comp_ptr = varint_encode(bb_recnum_diff, comp_ptr);
+    comp_ptr = varint_encode(target_pc, comp_ptr);
+    comp_ptr = varint_encode(trace_bb.current_bb_num, comp_ptr);
+    comp_ptr = varint_encode(trace_bb.current_bb_start_time, comp_ptr);
+    comp_ptr = varint_encode(trace_bb.num_insns, comp_ptr);
+    trace_exc.compressed_ptr = comp_ptr;
+}
+
+void trace_pid_1arg(int pid, int rec_type)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + kMaxPidCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(pid, comp_ptr);
+    trace_pid.compressed_ptr = comp_ptr;
+}
+
+void trace_pid_2arg(int tgid, int pid, int rec_type)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + kMaxPid2Compressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(tgid, comp_ptr);
+    comp_ptr = varint_encode(pid, comp_ptr);
+    trace_pid.compressed_ptr = comp_ptr;
+}
+
+void trace_switch(int pid)
+{
+#if 0
+    if (ftrace_debug && trace_pid.fstream)
+        fprintf(ftrace_debug, "t%lld switch %d\n", sim_time, pid);
+#endif
+    trace_pid_1arg(pid, kPidSwitch);
+    current_pid = pid;
+}
+
+void trace_fork(int tgid, int pid)
+{
+#if 0
+    if (ftrace_debug && trace_pid.fstream)
+        fprintf(ftrace_debug, "t%lld fork %d\n", sim_time, pid);
+#endif
+    trace_pid_2arg(tgid, pid, kPidFork);
+}
+
+void trace_clone(int tgid, int pid)
+{
+#if 0
+    if (ftrace_debug && trace_pid.fstream)
+        fprintf(ftrace_debug, "t%lld clone %d\n", sim_time, pid);
+#endif
+    trace_pid_2arg(tgid, pid, kPidClone);
+}
+
+void trace_exit(int exitcode)
+{
+#if 0
+    if (ftrace_debug && trace_pid.fstream)
+        fprintf(ftrace_debug, "t%lld exit %d\n", sim_time, exitcode);
+#endif
+    trace_pid_1arg(exitcode, kPidExit);
+}
+
+void trace_name(char *name)
+{
+#if 0
+    if (ftrace_debug && trace_pid.fstream) {
+        fprintf(ftrace_debug, "t%lld pid %d name %s\n",
+                sim_time, current_pid, name);
+    }
+#endif
+    if (trace_pid.fstream == NULL)
+        return;
+    int len = strlen(name);
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + len + kMaxNameCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidName;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(current_pid, comp_ptr);
+    comp_ptr = varint_encode(len, comp_ptr);
+    strncpy(comp_ptr, name, len);
+    comp_ptr += len;
+    trace_pid.compressed_ptr = comp_ptr;
+}
+
+void trace_execve(const char *argv, int len)
+{
+    int ii;
+
+    if (trace_pid.fstream == NULL)
+        return;
+    // Count the number of args
+    int alen = 0;
+    int sum_len = 0;
+    int argc = 0;
+    const char *ptr = argv;
+    while (sum_len < len) {
+        argc += 1;
+        alen = strlen(ptr);
+        ptr += alen + 1;
+        sum_len += alen + 1;
+    }
+
+#if 0
+    if (ftrace_debug) {
+        fprintf(ftrace_debug, "t%lld argc: %d\n", sim_time, argc);
+        alen = 0;
+        ptr = argv;
+        for (ii = 0; ii < argc; ++ii) {
+            fprintf(ftrace_debug, "  argv[%d]: %s\n", ii, ptr);
+            alen = strlen(ptr);
+            ptr += alen + 1;
+        }
+    }
+#endif
+
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + len + 5 * argc + kMaxExecArgsCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidExec;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(argc, comp_ptr);
+
+    ptr = argv;
+    for (ii = 0; ii < argc; ++ii) {
+        alen = strlen(ptr);
+        comp_ptr = varint_encode(alen, comp_ptr);
+        strncpy(comp_ptr, ptr, alen);
+        comp_ptr += alen;
+        ptr += alen + 1;
+    }
+    trace_pid.compressed_ptr = comp_ptr;
+}
+
+void trace_mmap(unsigned long vstart, unsigned long vend,
+                unsigned long offset, const char *path)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+#if 0
+    if (ftrace_debug)
+        fprintf(ftrace_debug, "t%lld mmap %08lx - %08lx, offset %08lx '%s'\n",
+                sim_time, vstart, vend, offset, path);
+#endif
+    int len = strlen(path);
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + len + kMaxMmapCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidMmap;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(vstart, comp_ptr);
+    comp_ptr = varint_encode(vend, comp_ptr);
+    comp_ptr = varint_encode(offset, comp_ptr);
+    comp_ptr = varint_encode(len, comp_ptr);
+    strncpy(comp_ptr, path, len);
+    trace_pid.compressed_ptr = comp_ptr + len;
+}
+
+void trace_munmap(unsigned long vstart, unsigned long vend)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+#if 0
+    if (ftrace_debug)
+        fprintf(ftrace_debug, "t%lld munmap %08lx - %08lx\n",
+                sim_time, vstart, vend);
+#endif
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + kMaxMunmapCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidMunmap;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(vstart, comp_ptr);
+    comp_ptr = varint_encode(vend, comp_ptr);
+    trace_pid.compressed_ptr = comp_ptr;
+}
+
+void trace_dynamic_symbol_add(unsigned long vaddr, const char *name)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+#if 0
+    if (ftrace_debug)
+        fprintf(ftrace_debug, "t%lld sym %08lx '%s'\n", sim_time, vaddr, name);
+#endif
+    int len = strlen(name);
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + len + kMaxSymbolCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidSymbolAdd;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(vaddr, comp_ptr);
+    comp_ptr = varint_encode(len, comp_ptr);
+    strncpy(comp_ptr, name, len);
+    trace_pid.compressed_ptr = comp_ptr + len;
+}
+
+void trace_dynamic_symbol_remove(unsigned long vaddr)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+#if 0
+    if (ftrace_debug)
+        fprintf(ftrace_debug, "t%lld remove %08lx\n", sim_time, vaddr);
+#endif
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + kMaxSymbolCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidSymbolRemove;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(vaddr, comp_ptr);
+    trace_pid.compressed_ptr = comp_ptr;
+}
+
+void trace_init_name(int tgid, int pid, const char *name)
+{
+    if (trace_pid.fstream == NULL)
+        return;
+#if 0
+    if (ftrace_debug)
+        fprintf(ftrace_debug, "t%lld kthread %d %s\n", sim_time, pid, name);
+#endif
+    int len = strlen(name);
+    char *comp_ptr = trace_pid.compressed_ptr;
+    char *max_end_ptr = comp_ptr + len + kMaxKthreadNameCompressed;
+    if (max_end_ptr >= &trace_pid.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_pid.compressed;
+        fwrite(trace_pid.compressed, sizeof(char), size, trace_pid.fstream);
+        comp_ptr = trace_pid.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_pid.prev_time;
+    trace_pid.prev_time = sim_time;
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    int rec_type = kPidKthreadName;
+    comp_ptr = varint_encode(rec_type, comp_ptr);
+    comp_ptr = varint_encode(tgid, comp_ptr);
+    comp_ptr = varint_encode(pid, comp_ptr);
+    comp_ptr = varint_encode(len, comp_ptr);
+    strncpy(comp_ptr, name, len);
+    trace_pid.compressed_ptr = comp_ptr + len;
+}
+
+void trace_init_exec(unsigned long start, unsigned long end,
+                     unsigned long offset, const char *exe)
+{
+}
+
+// This function is called by the generated code to record the basic
+// block number.
+void trace_bb_helper(uint64_t bb_num, TranslationBlock *tb)
+{
+    BBRec *bb_rec = tb->bb_rec;
+    uint64_t prev_time = tb->prev_time;
+    trace_bb.current_bb_addr = tb->pc;
+    trace_bb.current_bb_num = bb_num;
+    trace_bb.current_bb_start_time = sim_time;
+    trace_bb.num_insns = 0;
+    trace_bb.recnum += 1;
+
+#if 0
+    if (ftrace_debug)
+        fprintf(ftrace_debug, "t%lld %lld\n", sim_time, bb_num);
+#endif
+    if (bb_rec && bb_rec->bb_num == bb_num && prev_time > trace_bb.flush_time) {
+        uint64_t time_diff = sim_time - prev_time;
+        if (bb_rec->repeat == 0) {
+            bb_rec->repeat = 1;
+            bb_rec->time_diff = time_diff;
+            tb->prev_time = sim_time;
+            return;
+        } else if (time_diff == bb_rec->time_diff) {
+            bb_rec->repeat += 1;
+            tb->prev_time = sim_time;
+            return;
+        }
+    }
+
+    BBRec *next = trace_bb.next;
+    if (next == &trace_bb.buffer[kMaxNumBasicBlocks]) {
+        BBRec *ptr;
+        char *comp_ptr = trace_bb.compressed_ptr;
+        int64_t prev_bb_num = trace_bb.prev_bb_num;
+        uint64_t prev_bb_time = trace_bb.prev_bb_time;
+        for (ptr = trace_bb.buffer; ptr != next; ++ptr) {
+            if (comp_ptr >= trace_bb.high_water_ptr) {
+                uint32_t size = comp_ptr - trace_bb.compressed;
+                fwrite(trace_bb.compressed, sizeof(char), size, trace_bb.fstream);
+                comp_ptr = trace_bb.compressed;
+            }
+            int64_t bb_diff = ptr->bb_num - prev_bb_num;
+            prev_bb_num = ptr->bb_num;
+            uint64_t time_diff = ptr->start_time - prev_bb_time;
+            prev_bb_time = ptr->start_time;
+            comp_ptr = varint_encode_signed(bb_diff, comp_ptr);
+            comp_ptr = varint_encode(time_diff, comp_ptr);
+            comp_ptr = varint_encode(ptr->repeat, comp_ptr);
+            if (ptr->repeat)
+                comp_ptr = varint_encode(ptr->time_diff, comp_ptr);
+        }
+        trace_bb.compressed_ptr = comp_ptr;
+        trace_bb.prev_bb_num = prev_bb_num;
+        trace_bb.prev_bb_time = prev_bb_time;
+
+        next = trace_bb.buffer;
+        trace_bb.flush_time = sim_time;
+    }
+    tb->bb_rec = next;
+    next->bb_num = bb_num;
+    next->start_time = sim_time;
+    next->time_diff = 0;
+    next->repeat = 0;
+    tb->prev_time = sim_time;
+    next += 1;
+    trace_bb.next = next;
+}
+
+// This function is called by the generated code to record the simulation
+// time at the start of each instruction.
+void trace_insn_helper()
+{
+    InsnRec *current = trace_insn.current;
+    uint64_t time_diff = sim_time - trace_insn.prev_time;
+    trace_insn.prev_time = sim_time;
+
+    // Keep track of the number of traced instructions so far in this
+    // basic block in case we get an exception in the middle of the bb.
+    trace_bb.num_insns += 1;
+
+#if 0
+    if (ftrace_debug) {
+        uint32_t current_pc = trace_bb.current_bb_addr + 4 * (trace_bb.num_insns - 1);
+        fprintf(ftrace_debug, "%llu %x\n", sim_time, current_pc);
+    }
+#endif
+    if (time_diff == current->time_diff) {
+        current->repeat += 1;
+        if (current->repeat != 0)
+            return;
+
+        // The repeat count wrapped around, so back up one and create
+        // a new record.
+        current->repeat -= 1;
+    }
+    current += 1;
+
+    if (current == &trace_insn.buffer[kInsnBufferSize]) {
+        InsnRec *ptr;
+        char *comp_ptr = trace_insn.compressed_ptr;
+        for (ptr = trace_insn.buffer; ptr != current; ++ptr) {
+            if (comp_ptr >= trace_insn.high_water_ptr) {
+                uint32_t size = comp_ptr - trace_insn.compressed;
+                uint32_t rval = fwrite(trace_insn.compressed, sizeof(char),
+                                       size, trace_insn.fstream);
+                if (rval != size) {
+                    fprintf(stderr, "fwrite() failed\n");
+                    perror(trace_insn.filename);
+                    exit(1);
+                }
+                comp_ptr = trace_insn.compressed;
+            }
+            comp_ptr = varint_encode(ptr->time_diff, comp_ptr);
+            comp_ptr = varint_encode(ptr->repeat, comp_ptr);
+        }
+        trace_insn.compressed_ptr = comp_ptr;
+        current = trace_insn.buffer;
+    }
+    current->time_diff = time_diff;
+    current->repeat = 0;
+    trace_insn.current = current;
+}
+
+// Adds an interpreted method trace record.  Each trace record is a time
+// stamped entry or exit to a method in a language executed by a "virtual
+// machine".  This allows profiling tools to show the method names instead
+// of the core virtual machine interpreter.
+void trace_interpreted_method(uint32_t addr, int call_type)
+{
+    if (trace_method.fstream == NULL)
+        return;
+#if 0
+    fprintf(stderr, "trace_method time: %llu p%d 0x%x %d\n",
+            sim_time, current_pid, addr, call_type);
+#endif
+    char *comp_ptr = trace_method.compressed_ptr;
+    char *max_end_ptr = comp_ptr + kMaxMethodCompressed;
+    if (max_end_ptr >= &trace_method.compressed[kCompressedSize]) {
+        uint32_t size = comp_ptr - trace_method.compressed;
+        fwrite(trace_method.compressed, sizeof(char), size, trace_method.fstream);
+        comp_ptr = trace_method.compressed;
+    }
+    uint64_t time_diff = sim_time - trace_method.prev_time;
+    trace_method.prev_time = sim_time;
+
+    int32_t addr_diff = addr - trace_method.prev_addr;
+    trace_method.prev_addr = addr;
+
+    int32_t pid_diff = current_pid - trace_method.prev_pid;
+    trace_method.prev_pid = current_pid;
+
+    comp_ptr = varint_encode(time_diff, comp_ptr);
+    comp_ptr = varint_encode_signed(addr_diff, comp_ptr);
+    comp_ptr = varint_encode_signed(pid_diff, comp_ptr);
+    comp_ptr = varint_encode(call_type, comp_ptr);
+    trace_method.compressed_ptr = comp_ptr;
+}
diff --git a/trace.h b/trace.h
new file mode 100644
index 0000000..ebb0e8c
--- /dev/null
+++ b/trace.h
@@ -0,0 +1,162 @@
+/* Copyright (C) 2006-2007 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include <inttypes.h>
+#include "trace_common.h"
+
+extern uint64_t start_time, end_time;
+extern uint64_t elapsed_usecs;
+extern uint64 Now();
+
+// Define magic addresses so that the simulated program can interact with the
+// simulator.
+#define kMagicBaseAddr		0x08000000
+#define kMagicBaseMask		0xfffff000
+#define kMagicOffsetMask	0x00000fff
+
+#define kMethodTraceEnterOffset		0x0004
+#define kMethodTraceExitOffset		0x0008
+#define kMethodTraceExceptionOffset	0x000c
+
+struct TranslationBlock;
+
+// For tracing dynamic execution of basic blocks
+typedef struct TraceBB {
+    char	*filename;
+    FILE	*fstream;
+    BBRec	buffer[kMaxNumBasicBlocks];
+    BBRec	*next;		// points to next record in buffer
+    uint64_t	flush_time;	// time of last buffer flush
+    char	compressed[kCompressedSize];
+    char	*compressed_ptr;
+    char	*high_water_ptr;
+    int64_t	prev_bb_num;
+    uint64_t	prev_bb_time;
+    uint64_t	current_bb_num;
+    uint64_t	current_bb_start_time;
+    uint64_t	recnum;		// counts number of trace records
+    uint32_t	current_bb_addr;
+    int		num_insns;
+} TraceBB;
+
+// For tracing simuation start times of instructions
+typedef struct TraceInsn {
+    char	*filename;
+    FILE	*fstream;
+    InsnRec	dummy;		// this is here so we can use buffer[-1]
+    InsnRec	buffer[kInsnBufferSize];
+    InsnRec	*current;
+    uint64_t	prev_time;	// time of last instruction start
+    char	compressed[kCompressedSize];
+    char	*compressed_ptr;
+    char	*high_water_ptr;
+} TraceInsn;
+
+// For tracing the static information about a basic block
+typedef struct TraceStatic {
+    char	*filename;
+    FILE	*fstream;
+    uint32_t	insns[kMaxInsnPerBB];
+    int		next_insn;
+    uint64_t	bb_num;
+    uint32_t	bb_addr;
+    int		is_thumb;
+} TraceStatic;
+
+// For tracing load and store addresses
+typedef struct TraceAddr {
+    char	*filename;
+    FILE	*fstream;
+    AddrRec	buffer[kMaxNumAddrs];
+    AddrRec	*next;
+    char	compressed[kCompressedSize];
+    char	*compressed_ptr;
+    char	*high_water_ptr;
+    uint32_t	prev_addr;
+    uint64_t	prev_time;
+} TraceAddr;
+
+// For tracing exceptions
+typedef struct TraceExc {
+    char	*filename;
+    FILE	*fstream;
+    char	compressed[kCompressedSize];
+    char	*compressed_ptr;
+    char	*high_water_ptr;
+    uint64_t	prev_time;
+    uint64_t	prev_bb_recnum;
+} TraceExc;
+
+// For tracing process id changes
+typedef struct TracePid {
+    char	*filename;
+    FILE	*fstream;
+    char	compressed[kCompressedSize];
+    char	*compressed_ptr;
+    uint64_t	prev_time;
+} TracePid;
+
+// For tracing Dalvik VM method enter and exit
+typedef struct TraceMethod {
+    char	*filename;
+    FILE	*fstream;
+    char	compressed[kCompressedSize];
+    char	*compressed_ptr;
+    uint64_t	prev_time;
+    uint32_t	prev_addr;
+    int32_t	prev_pid;
+} TraceMethod;
+
+extern TraceBB trace_bb;
+extern TraceInsn trace_insn;
+extern TraceStatic trace_static;
+extern TraceAddr trace_load;
+extern TraceAddr trace_store;
+extern TraceExc trace_exc;
+extern TracePid trace_pid;
+extern TraceMethod trace_method;
+
+// The simulated time, in clock ticks, starting with one.
+extern uint64_t sim_time;
+
+// This variable == 1 if we are currently tracing, otherwise == 0.
+extern int tracing;
+extern int trace_all_addr;
+extern int trace_cache_miss;
+
+extern void start_tracing();
+extern void stop_tracing();
+extern void trace_init(const char *filename);
+extern void trace_bb_start(uint32_t bb_addr);
+extern void trace_add_insn_arm(uint32_t insn, int is_thumb);
+extern void trace_bb_end();
+
+extern int get_insn_ticks_arm(uint32_t insn);
+extern int get_insn_ticks_thumb(uint32_t  insn);
+
+extern void trace_exception(uint32 pc);
+extern void trace_bb_helper(uint64_t bb_num, TranslationBlock *tb);
+extern void trace_insn_helper();
+extern void sim_dcache_load(uint32_t addr);
+extern void sim_dcache_store(uint32_t addr, uint32_t val);
+extern void sim_dcache_swp(uint32_t addr);
+extern void trace_interpreted_method(uint32_t addr, int call_type);
+
+extern const char *trace_filename;
+extern int tracing;
+extern int trace_cache_miss;
+extern int trace_all_addr;
+
+#endif /* TRACE_H */
diff --git a/trace_common.h b/trace_common.h
new file mode 100644
index 0000000..3c4440d
--- /dev/null
+++ b/trace_common.h
@@ -0,0 +1,139 @@
+/* Copyright (C) 2006-2007 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+
+#ifndef TRACE_COMMON_H
+#define TRACE_COMMON_H
+
+#include <inttypes.h>
+
+// This should be the same as OPC_BUF_SIZE
+#define kMaxInsnPerBB 512
+
+#define kMaxNumBasicBlocks 1024
+
+#define kMaxNumAddrs 1024
+
+#define kInsnBufferSize 1024
+
+#define kCompressedSize 8192
+
+#define kMethodEnter		0
+#define kMethodExit		1
+#define kMethodException	2
+
+// The trace identifier string must be less than 16 characters.
+#define TRACE_IDENT "qemu_trace_file"
+#define TRACE_VERSION 1
+
+typedef struct TraceHeader {
+    char	ident[16];
+    int		version;
+    uint32_t	start_sec;
+    uint32_t	start_usec;
+    uint32_t	pdate;
+    uint32_t	ptime;
+    uint32_t	num_used_pids;		// number of distinct process ids used
+    int		first_unused_pid;	// -1 if all 32,768 pids are used (unlikely)
+    uint8_t	padding[4];		// next field is 8-byte aligned
+    uint64_t	num_static_bb;
+    uint64_t	num_static_insn;
+    uint64_t	num_dynamic_bb;
+    uint64_t	num_dynamic_insn;
+    uint64_t	elapsed_usecs;
+} TraceHeader;
+
+typedef struct BBRec {
+    uint64_t	start_time;	// time of first occurrence
+    uint64_t	bb_num;		// basic block number
+    uint32_t	repeat;		// repeat count (= 0 if just one occurrence)
+    uint64_t	time_diff;	// diff from previous time (if repeat > 0)
+} BBRec;
+
+// Define a trace record for addresses that miss in the cache
+typedef struct AddrRec {
+    uint64_t	time;
+    uint32_t	addr;
+} AddrRec;
+
+// Define a trace record for the start time of each instruction
+typedef struct InsnRec {
+    uint64_t	time_diff;	// time difference from last instruction
+    uint32_t	repeat;		// repeat count
+} InsnRec;
+
+// Define record types for process id changes.
+#define kPidEndOfFile		0
+#define kPidFork		1
+#define kPidClone		2
+#define kPidSwitch		3
+#define kPidExec		4
+#define kPidMmap		5
+#define kPidExit		6
+#define kPidKthreadName		7
+#define kPidSymbolAdd		8
+#define kPidSymbolRemove	9
+#define kPidMunmap		10
+#define kPidNoAction		11
+#define kPidName		12
+
+#define bswap16(x) ((((x) & 0xff) << 8) | (((x) >> 8) & 0xff))
+
+#define bswap32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) \
+        | (((x) >> 8) & 0xff00) | (((x) >> 24) & 0xff))
+
+#define bswap64(x) (((x) << 56) | (((x) & 0xff00) << 40) \
+        | (((x) & 0xff0000) << 24) | (((x) & 0xff000000ull) << 8) \
+        | (((x) >> 8) & 0xff000000ull) | (((x) >> 24) & 0xff0000) \
+        | (((x) >> 40) & 0xff00) | ((x) >> 56))
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define hostToLE16(x)	(x)
+#define hostToLE32(x)	(x)
+#define hostToLE64(x)	(x)
+#define LE16ToHost(x)	(x)
+#define LE32ToHost(x)	(x)
+#define LE64ToHost(x)	(x)
+#define convert16(x)
+#define convert32(x)
+#define convert64(x)
+#else
+#define hostToLE16(x)	bswap16(x)
+#define hostToLE32(x)	bswap32(x)
+#define hostToLE64(x)	bswap64(x)
+#define LE16ToHost(x)	bswap16(x)
+#define LE32ToHost(x)	bswap32(x)
+#define LE64ToHost(x)	bswap64(x)
+#define convert16(x) (x = bswap16(x))
+#define convert32(x) (x = bswap32(x))
+#define convert64(x) (x = bswap64(x))
+#endif
+
+/* XXX: we wrap 16-bit thumb instructions into 32-bit undefined ARM instructions
+ *      for simplicity reasons. See section 3.13.1 section of the ARM ARM for details
+ *      on the undefined instruction space we're using
+ */
+static __inline__ int   insn_is_thumb(uint32_t   insn)
+{
+    return ((insn & 0xfff000f0) == 0xf7f000f0);
+}
+
+static __inline__ uint32_t  insn_wrap_thumb(uint32_t  insn)
+{
+    return 0xf7f000f0 | ((insn & 0xfff0) << 4) | (insn & 0x000f);
+}
+
+static __inline__ uint32_t  insn_unwrap_thumb(uint32_t  insn)
+{
+    return ((insn >> 4) & 0xfff0) | (insn & 0x000f);
+}
+
+#endif /* TRACE_COMMON_H */
diff --git a/translate-all.c b/translate-all.c
new file mode 100644
index 0000000..1c27fd3
--- /dev/null
+++ b/translate-all.c
@@ -0,0 +1,195 @@
+/*
+ *  Host code generation
+ *
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+
+#define NO_CPU_IO_DEFS
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+#include "tcg.h"
+
+/* code generation context */
+TCGContext tcg_ctx;
+
+uint16_t gen_opc_buf[OPC_BUF_SIZE];
+TCGArg gen_opparam_buf[OPPARAM_BUF_SIZE];
+
+target_ulong gen_opc_pc[OPC_BUF_SIZE];
+uint16_t gen_opc_icount[OPC_BUF_SIZE];
+uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
+#if defined(TARGET_I386)
+uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
+#elif defined(TARGET_SPARC)
+target_ulong gen_opc_npc[OPC_BUF_SIZE];
+target_ulong gen_opc_jump_pc[2];
+#elif defined(TARGET_MIPS) || defined(TARGET_SH4)
+uint32_t gen_opc_hflags[OPC_BUF_SIZE];
+#endif
+
+/* XXX: suppress that */
+unsigned long code_gen_max_block_size(void)
+{
+    static unsigned long max;
+
+    if (max == 0) {
+        max = TCG_MAX_OP_SIZE;
+#define DEF(s, n, copy_size) max = copy_size > max? copy_size : max;
+#include "tcg-opc.h"
+#undef DEF
+        max *= OPC_MAX_SIZE;
+    }
+
+    return max;
+}
+
+void cpu_gen_init(void)
+{
+    tcg_context_init(&tcg_ctx); 
+    tcg_set_frame(&tcg_ctx, TCG_AREG0, offsetof(CPUState, temp_buf),
+                  CPU_TEMP_BUF_NLONGS * sizeof(long));
+}
+
+/* return non zero if the very first instruction is invalid so that
+   the virtual CPU can trigger an exception.
+
+   '*gen_code_size_ptr' contains the size of the generated code (host
+   code).
+*/
+int cpu_gen_code(CPUState *env, TranslationBlock *tb, int *gen_code_size_ptr)
+{
+    TCGContext *s = &tcg_ctx;
+    uint8_t *gen_code_buf;
+    int gen_code_size;
+#ifdef CONFIG_PROFILER
+    int64_t ti;
+#endif
+
+#ifdef CONFIG_PROFILER
+    s->tb_count1++; /* includes aborted translations because of
+                       exceptions */
+    ti = profile_getclock();
+#endif
+    tcg_func_start(s);
+
+    gen_intermediate_code(env, tb);
+
+    /* generate machine code */
+    gen_code_buf = tb->tc_ptr;
+    tb->tb_next_offset[0] = 0xffff;
+    tb->tb_next_offset[1] = 0xffff;
+    s->tb_next_offset = tb->tb_next_offset;
+#ifdef USE_DIRECT_JUMP
+    s->tb_jmp_offset = tb->tb_jmp_offset;
+    s->tb_next = NULL;
+    /* the following two entries are optional (only used for string ops) */
+    /* XXX: not used ? */
+    tb->tb_jmp_offset[2] = 0xffff;
+    tb->tb_jmp_offset[3] = 0xffff;
+#else
+    s->tb_jmp_offset = NULL;
+    s->tb_next = tb->tb_next;
+#endif
+
+#ifdef CONFIG_PROFILER
+    s->tb_count++;
+    s->interm_time += profile_getclock() - ti;
+    s->code_time -= profile_getclock();
+#endif
+    gen_code_size = dyngen_code(s, gen_code_buf);
+    *gen_code_size_ptr = gen_code_size;
+#ifdef CONFIG_PROFILER
+    s->code_time += profile_getclock();
+    s->code_in_len += tb->size;
+    s->code_out_len += gen_code_size;
+#endif
+
+#ifdef DEBUG_DISAS
+    if (loglevel & CPU_LOG_TB_OUT_ASM) {
+        fprintf(logfile, "OUT: [size=%d]\n", *gen_code_size_ptr);
+        disas(logfile, tb->tc_ptr, *gen_code_size_ptr);
+        fprintf(logfile, "\n");
+        fflush(logfile);
+    }
+#endif
+    return 0;
+}
+
+/* The cpu state corresponding to 'searched_pc' is restored.
+ */
+int cpu_restore_state(TranslationBlock *tb,
+                      CPUState *env, unsigned long searched_pc,
+                      void *puc)
+{
+    TCGContext *s = &tcg_ctx;
+    int j;
+    unsigned long tc_ptr;
+#ifdef CONFIG_PROFILER
+    int64_t ti;
+#endif
+
+#ifdef CONFIG_PROFILER
+    ti = profile_getclock();
+#endif
+    tcg_func_start(s);
+
+    gen_intermediate_code_pc(env, tb);
+
+    if (use_icount) {
+        /* Reset the cycle counter to the start of the block.  */
+        env->icount_decr.u16.low += tb->icount;
+        /* Clear the IO flag.  */
+        env->can_do_io = 0;
+    }
+
+    /* find opc index corresponding to search_pc */
+    tc_ptr = (unsigned long)tb->tc_ptr;
+    if (searched_pc < tc_ptr)
+        return -1;
+
+    s->tb_next_offset = tb->tb_next_offset;
+#ifdef USE_DIRECT_JUMP
+    s->tb_jmp_offset = tb->tb_jmp_offset;
+    s->tb_next = NULL;
+#else
+    s->tb_jmp_offset = NULL;
+    s->tb_next = tb->tb_next;
+#endif
+    j = dyngen_code_search_pc(s, (uint8_t *)tc_ptr, searched_pc - tc_ptr);
+    if (j < 0)
+        return -1;
+    /* now find start of instruction before */
+    while (gen_opc_instr_start[j] == 0)
+        j--;
+    env->icount_decr.u16.low -= gen_opc_icount[j];
+
+    gen_pc_load(env, tb, searched_pc, j, puc);
+
+#ifdef CONFIG_PROFILER
+    s->restore_time += profile_getclock() - ti;
+    s->restore_count++;
+#endif
+    return 0;
+}
diff --git a/translate-op.c b/translate-op.c
new file mode 100644
index 0000000..c25a161
--- /dev/null
+++ b/translate-op.c
@@ -0,0 +1,81 @@
+/*
+ *  Host code generation
+ * 
+ *  Copyright (c) 2003 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "config.h"
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#ifdef GEN_TRACE
+#include "opc-trace.h"
+#else
+#include "opc.h"
+#endif
+#undef DEF
+    NB_OPS,
+};
+
+#include "dyngen.h"
+#ifdef GEN_TRACE
+#define   dyngen_code   _trace_dyngen_code
+#include "op-trace.h"
+#else
+#define   dyngen_code  _default_dyngen_code
+#include "op.h"
+#endif
+
+typedef int (*dyngen_code_func)(uint8_t *gen_code_buf,
+                uint16_t *label_offsets, uint16_t *jmp_offsets,
+                const uint16_t *opc_buf, const uint32_t *opparam_buf, const long *gen_labels);
+
+extern dyngen_code_func  _dyngen_code;
+
+#ifdef GEN_TRACE
+
+void  qemu_trace_enable_dyngen( void )
+{
+    _dyngen_code = dyngen_code;
+}
+
+#else
+
+void  qemu_trace_disable_dyngen( void )
+{
+    _dyngen_code = dyngen_code;
+}
+
+dyngen_code_func  _dyngen_code = _default_dyngen_code;
+
+#undef  dyngen_code
+
+int dyngen_code(uint8_t *gen_code_buf,
+                uint16_t *label_offsets, uint16_t *jmp_offsets,
+                const uint16_t *opc_buf, const uint32_t *opparam_buf, const long *gen_labels)
+{
+    return (*_dyngen_code)(gen_code_buf, label_offsets, jmp_offsets, opc_buf, opparam_buf, gen_labels);
+}
+
+#endif
+
+
diff --git a/translate.make b/translate.make
new file mode 100644
index 0000000..cba105f
--- /dev/null
+++ b/translate.make
@@ -0,0 +1,37 @@
+# this sub-Makefile is included to define a dynamic translating library
+#
+EMULATOR_OP_LIBRARIES := $(EMULATOR_OP_LIBRARIES) $(LOCAL_MODULE)
+
+# we need to compile this with GCC-3.3 preferabbly
+#
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_CC                        := $(MY_CC)
+
+LOCAL_LDFLAGS += $(my_32bit_ldflags)
+LOCAL_CFLAGS += $(my_32bit_cflags) $(OP_CFLAGS)
+
+INTERMEDIATE := $(call intermediates-dir-for,STATIC_LIBRARIES,$(LOCAL_MODULE),true)
+OP_OBJ       := $(INTERMEDIATE)/target-arm/op.o
+
+LOCAL_CFLAGS += -I$(INTERMEDIATE)
+
+OP_H     := $(INTERMEDIATE)/op$(OP_SUFFIX).h
+OPC_H    := $(INTERMEDIATE)/opc$(OP_SUFFIX).h
+GEN_OP_H := $(INTERMEDIATE)/gen-op$(OP_SUFFIX).h
+
+$(OP_H): $(OP_OBJ) $(DYNGEN)
+	$(DYNGEN) -o $@ $<
+
+$(OPC_H): $(OP_OBJ) $(DYNGEN)
+	$(DYNGEN) -c -o $@ $<
+
+$(GEN_OP_H): $(OP_OBJ) $(DYNGEN)
+	$(DYNGEN) -g -o $@ $<
+
+TRANSLATE_SOURCES := target-arm/translate.c \
+                     translate-all.c \
+                     translate-op.c
+
+LOCAL_SRC_FILES += target-arm/op.c  $(TRANSLATE_SOURCES)
+
+$(TRANSLATE_SOURCES:%.c=$(INTERMEDIATE)/%.o): $(OP_H) $(OPC_H) $(GEN_OP_H)
diff --git a/uboot_image.h b/uboot_image.h
new file mode 100644
index 0000000..d5a5b30
--- /dev/null
+++ b/uboot_image.h
@@ -0,0 +1,160 @@
+/*
+ * (C) Copyright 2000-2005
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ ********************************************************************
+ * NOTE: This header file defines an interface to U-Boot. Including
+ * this (unmodified) header file in another file is considered normal
+ * use of U-Boot, and does *not* fall under the heading of "derived
+ * work".
+ ********************************************************************
+ */
+
+#ifndef __UBOOT_IMAGE_H__
+#define __UBOOT_IMAGE_H__
+
+/*
+ * Operating System Codes
+ */
+#define IH_OS_INVALID		0	/* Invalid OS	*/
+#define IH_OS_OPENBSD		1	/* OpenBSD	*/
+#define IH_OS_NETBSD		2	/* NetBSD	*/
+#define IH_OS_FREEBSD		3	/* FreeBSD	*/
+#define IH_OS_4_4BSD		4	/* 4.4BSD	*/
+#define IH_OS_LINUX		5	/* Linux	*/
+#define IH_OS_SVR4		6	/* SVR4		*/
+#define IH_OS_ESIX		7	/* Esix		*/
+#define IH_OS_SOLARIS		8	/* Solaris	*/
+#define IH_OS_IRIX		9	/* Irix		*/
+#define IH_OS_SCO		10	/* SCO		*/
+#define IH_OS_DELL		11	/* Dell		*/
+#define IH_OS_NCR		12	/* NCR		*/
+#define IH_OS_LYNXOS		13	/* LynxOS	*/
+#define IH_OS_VXWORKS		14	/* VxWorks	*/
+#define IH_OS_PSOS		15	/* pSOS		*/
+#define IH_OS_QNX		16	/* QNX		*/
+#define IH_OS_U_BOOT		17	/* Firmware	*/
+#define IH_OS_RTEMS		18	/* RTEMS	*/
+#define IH_OS_ARTOS		19	/* ARTOS	*/
+#define IH_OS_UNITY		20	/* Unity OS	*/
+
+/*
+ * CPU Architecture Codes (supported by Linux)
+ */
+#define IH_CPU_INVALID		0	/* Invalid CPU	*/
+#define IH_CPU_ALPHA		1	/* Alpha	*/
+#define IH_CPU_ARM		2	/* ARM		*/
+#define IH_CPU_I386		3	/* Intel x86	*/
+#define IH_CPU_IA64		4	/* IA64		*/
+#define IH_CPU_MIPS		5	/* MIPS		*/
+#define IH_CPU_MIPS64		6	/* MIPS	 64 Bit */
+#define IH_CPU_PPC		7	/* PowerPC	*/
+#define IH_CPU_S390		8	/* IBM S390	*/
+#define IH_CPU_SH		9	/* SuperH	*/
+#define IH_CPU_SPARC		10	/* Sparc	*/
+#define IH_CPU_SPARC64		11	/* Sparc 64 Bit */
+#define IH_CPU_M68K		12	/* M68K		*/
+#define IH_CPU_NIOS		13	/* Nios-32	*/
+#define IH_CPU_MICROBLAZE	14	/* MicroBlaze   */
+#define IH_CPU_NIOS2		15	/* Nios-II	*/
+#define IH_CPU_BLACKFIN		16	/* Blackfin	*/
+#define IH_CPU_AVR32		17	/* AVR32	*/
+
+/*
+ * Image Types
+ *
+ * "Standalone Programs" are directly runnable in the environment
+ *	provided by U-Boot; it is expected that (if they behave
+ *	well) you can continue to work in U-Boot after return from
+ *	the Standalone Program.
+ * "OS Kernel Images" are usually images of some Embedded OS which
+ *	will take over control completely. Usually these programs
+ *	will install their own set of exception handlers, device
+ *	drivers, set up the MMU, etc. - this means, that you cannot
+ *	expect to re-enter U-Boot except by resetting the CPU.
+ * "RAMDisk Images" are more or less just data blocks, and their
+ *	parameters (address, size) are passed to an OS kernel that is
+ *	being started.
+ * "Multi-File Images" contain several images, typically an OS
+ *	(Linux) kernel image and one or more data images like
+ *	RAMDisks. This construct is useful for instance when you want
+ *	to boot over the network using BOOTP etc., where the boot
+ *	server provides just a single image file, but you want to get
+ *	for instance an OS kernel and a RAMDisk image.
+ *
+ *	"Multi-File Images" start with a list of image sizes, each
+ *	image size (in bytes) specified by an "uint32_t" in network
+ *	byte order. This list is terminated by an "(uint32_t)0".
+ *	Immediately after the terminating 0 follow the images, one by
+ *	one, all aligned on "uint32_t" boundaries (size rounded up to
+ *	a multiple of 4 bytes - except for the last file).
+ *
+ * "Firmware Images" are binary images containing firmware (like
+ *	U-Boot or FPGA images) which usually will be programmed to
+ *	flash memory.
+ *
+ * "Script files" are command sequences that will be executed by
+ *	U-Boot's command interpreter; this feature is especially
+ *	useful when you configure U-Boot to use a real shell (hush)
+ *	as command interpreter (=> Shell Scripts).
+ */
+
+#define IH_TYPE_INVALID		0	/* Invalid Image		*/
+#define IH_TYPE_STANDALONE	1	/* Standalone Program		*/
+#define IH_TYPE_KERNEL		2	/* OS Kernel Image		*/
+#define IH_TYPE_RAMDISK		3	/* RAMDisk Image		*/
+#define IH_TYPE_MULTI		4	/* Multi-File Image		*/
+#define IH_TYPE_FIRMWARE	5	/* Firmware Image		*/
+#define IH_TYPE_SCRIPT		6	/* Script file			*/
+#define IH_TYPE_FILESYSTEM	7	/* Filesystem Image (any type)	*/
+#define IH_TYPE_FLATDT		8	/* Binary Flat Device Tree Blob	*/
+
+/*
+ * Compression Types
+ */
+#define IH_COMP_NONE		0	/*  No	 Compression Used	*/
+#define IH_COMP_GZIP		1	/* gzip	 Compression Used	*/
+#define IH_COMP_BZIP2		2	/* bzip2 Compression Used	*/
+
+#define IH_MAGIC	0x27051956	/* Image Magic Number		*/
+#define IH_NMLEN		32	/* Image Name Length		*/
+
+/*
+ * all data in network byte order (aka natural aka bigendian)
+ */
+
+typedef struct uboot_image_header {
+	uint32_t	ih_magic;	/* Image Header Magic Number	*/
+	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/
+	uint32_t	ih_time;	/* Image Creation Timestamp	*/
+	uint32_t	ih_size;	/* Image Data Size		*/
+	uint32_t	ih_load;	/* Data	 Load  Address		*/
+	uint32_t	ih_ep;		/* Entry Point Address		*/
+	uint32_t	ih_dcrc;	/* Image Data CRC Checksum	*/
+	uint8_t		ih_os;		/* Operating System		*/
+	uint8_t		ih_arch;	/* CPU architecture		*/
+	uint8_t		ih_type;	/* Image Type			*/
+	uint8_t		ih_comp;	/* Compression Type		*/
+	uint8_t		ih_name[IH_NMLEN];	/* Image Name		*/
+} uboot_image_header_t;
+
+
+#endif	/* __IMAGE_H__ */
diff --git a/usb-linux.c b/usb-linux.c
new file mode 100644
index 0000000..91acccd
--- /dev/null
+++ b/usb-linux.c
@@ -0,0 +1,1506 @@
+/*
+ * Linux host USB redirector
+ *
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * Copyright (c) 2008 Max Krasnyansky
+ *      Support for host device auto connect & disconnect
+ *      Major rewrite to support fully async operation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "qemu-timer.h"
+#include "console.h"
+
+#if defined(__linux__)
+#include <dirent.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+
+#include <linux/usbdevice_fs.h>
+#include <linux/version.h>
+#include "hw/usb.h"
+
+/* We redefine it to avoid version problems */
+struct usb_ctrltransfer {
+    uint8_t  bRequestType;
+    uint8_t  bRequest;
+    uint16_t wValue;
+    uint16_t wIndex;
+    uint16_t wLength;
+    uint32_t timeout;
+    void *data;
+};
+
+struct usb_ctrlrequest {
+    uint8_t bRequestType;
+    uint8_t bRequest;
+    uint16_t wValue;
+    uint16_t wIndex;
+    uint16_t wLength;
+};
+
+typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
+                        int vendor_id, int product_id,
+                        const char *product_name, int speed);
+static int usb_host_find_device(int *pbus_num, int *paddr,
+                                char *product_name, int product_name_size,
+                                const char *devname);
+//#define DEBUG
+
+#ifdef DEBUG
+#define dprintf printf
+#else
+#define dprintf(...)
+#endif
+
+#define USBDEVFS_PATH "/proc/bus/usb"
+#define PRODUCT_NAME_SZ 32
+#define MAX_ENDPOINTS 16
+
+/* endpoint association data */
+struct endp_data {
+    uint8_t type;
+    uint8_t halted;
+};
+
+enum {
+    CTRL_STATE_IDLE = 0,
+    CTRL_STATE_SETUP,
+    CTRL_STATE_DATA,
+    CTRL_STATE_ACK
+};
+
+/*
+ * Control transfer state.
+ * Note that 'buffer' _must_ follow 'req' field because 
+ * we need contigious buffer when we submit control URB.
+ */ 
+struct ctrl_struct {
+    uint16_t len;
+    uint16_t offset;
+    uint8_t  state;
+    struct   usb_ctrlrequest req;
+    uint8_t  buffer[1024];
+};
+
+typedef struct USBHostDevice {
+    USBDevice dev;
+    int       fd;
+
+    uint8_t   descr[1024];
+    int       descr_len;
+    int       configuration;
+    int       ninterfaces;
+    int       closing;
+
+    struct ctrl_struct ctrl;
+    struct endp_data endp_table[MAX_ENDPOINTS];
+
+    /* Host side address */
+    int bus_num;
+    int addr;
+
+    struct USBHostDevice *next;
+} USBHostDevice;
+
+static int is_isoc(USBHostDevice *s, int ep)
+{
+    return s->endp_table[ep - 1].type == USBDEVFS_URB_TYPE_ISO;
+}
+
+static int is_halted(USBHostDevice *s, int ep)
+{
+    return s->endp_table[ep - 1].halted;
+}
+
+static void clear_halt(USBHostDevice *s, int ep)
+{
+    s->endp_table[ep - 1].halted = 0;
+}
+
+static void set_halt(USBHostDevice *s, int ep)
+{
+    s->endp_table[ep - 1].halted = 1;
+}
+
+static USBHostDevice *hostdev_list;
+
+static void hostdev_link(USBHostDevice *dev)
+{
+    dev->next = hostdev_list;
+    hostdev_list = dev;
+}
+
+static void hostdev_unlink(USBHostDevice *dev)
+{
+    USBHostDevice *pdev = hostdev_list;
+    USBHostDevice **prev = &hostdev_list;
+
+    while (pdev) {
+	if (pdev == dev) {
+            *prev = dev->next;
+            return;
+        }
+
+        prev = &pdev->next;
+        pdev = pdev->next;
+    }
+}
+
+static USBHostDevice *hostdev_find(int bus_num, int addr)
+{
+    USBHostDevice *s = hostdev_list;
+    while (s) {
+        if (s->bus_num == bus_num && s->addr == addr)
+            return s;
+        s = s->next;
+    }
+    return NULL;
+}
+
+/* 
+ * Async URB state.
+ * We always allocate one isoc descriptor even for bulk transfers
+ * to simplify allocation and casts. 
+ */
+typedef struct AsyncURB
+{
+    struct usbdevfs_urb urb;
+    struct usbdevfs_iso_packet_desc isocpd;
+
+    USBPacket     *packet;
+    USBHostDevice *hdev;
+} AsyncURB;
+
+static AsyncURB *async_alloc(void)
+{
+    return (AsyncURB *) qemu_mallocz(sizeof(AsyncURB));
+}
+
+static void async_free(AsyncURB *aurb)
+{
+    qemu_free(aurb);
+}
+
+static void async_complete_ctrl(USBHostDevice *s, USBPacket *p)
+{
+    switch(s->ctrl.state) {
+    case CTRL_STATE_SETUP:
+        if (p->len < s->ctrl.len)
+            s->ctrl.len = p->len;
+        s->ctrl.state = CTRL_STATE_DATA;
+        p->len = 8;
+        break;
+
+    case CTRL_STATE_ACK:
+        s->ctrl.state = CTRL_STATE_IDLE;
+        p->len = 0;
+        break;
+
+    default:
+        break;
+    }
+}
+
+static void async_complete(void *opaque)
+{
+    USBHostDevice *s = opaque;
+    AsyncURB *aurb;
+
+    while (1) {
+    	USBPacket *p;
+
+	int r = ioctl(s->fd, USBDEVFS_REAPURBNDELAY, &aurb);
+        if (r < 0) {
+            if (errno == EAGAIN)
+                return;
+
+            if (errno == ENODEV && !s->closing) {
+                printf("husb: device %d.%d disconnected\n", s->bus_num, s->addr);
+	        usb_device_del_addr(0, s->dev.addr);
+                return;
+            }
+
+            dprintf("husb: async. reap urb failed errno %d\n", errno);
+            return;
+        }
+
+        p = aurb->packet;
+
+	dprintf("husb: async completed. aurb %p status %d alen %d\n", 
+                aurb, aurb->urb.status, aurb->urb.actual_length);
+
+	if (p) {
+            switch (aurb->urb.status) {
+            case 0:
+                p->len = aurb->urb.actual_length;
+                if (aurb->urb.type == USBDEVFS_URB_TYPE_CONTROL)
+                    async_complete_ctrl(s, p);
+                break;
+
+            case -EPIPE:
+                set_halt(s, p->devep);
+                /* fall through */
+            default:
+                p->len = USB_RET_NAK;
+                break;
+            }
+
+            usb_packet_complete(p);
+	}
+
+        async_free(aurb);
+    }
+}
+
+static void async_cancel(USBPacket *unused, void *opaque)
+{
+    AsyncURB *aurb = opaque;
+    USBHostDevice *s = aurb->hdev;
+
+    dprintf("husb: async cancel. aurb %p\n", aurb);
+
+    /* Mark it as dead (see async_complete above) */
+    aurb->packet = NULL;
+
+    int r = ioctl(s->fd, USBDEVFS_DISCARDURB, aurb);
+    if (r < 0) {
+        dprintf("husb: async. discard urb failed errno %d\n", errno);
+    }
+}
+
+static int usb_host_claim_interfaces(USBHostDevice *dev, int configuration)
+{
+    int dev_descr_len, config_descr_len;
+    int interface, nb_interfaces, nb_configurations;
+    int ret, i;
+
+    if (configuration == 0) /* address state - ignore */
+        return 1;
+
+    dprintf("husb: claiming interfaces. config %d\n", configuration);
+
+    i = 0;
+    dev_descr_len = dev->descr[0];
+    if (dev_descr_len > dev->descr_len)
+        goto fail;
+    nb_configurations = dev->descr[17];
+
+    i += dev_descr_len;
+    while (i < dev->descr_len) {
+        dprintf("husb: i is %d, descr_len is %d, dl %d, dt %d\n", i, dev->descr_len,
+               dev->descr[i], dev->descr[i+1]);
+
+        if (dev->descr[i+1] != USB_DT_CONFIG) {
+            i += dev->descr[i];
+            continue;
+        }
+        config_descr_len = dev->descr[i];
+
+	printf("husb: config #%d need %d\n", dev->descr[i + 5], configuration); 
+
+        if (configuration < 0 || configuration == dev->descr[i + 5]) {
+            configuration = dev->descr[i + 5];
+            break;
+        }
+
+        i += config_descr_len;
+    }
+
+    if (i >= dev->descr_len) {
+        fprintf(stderr, "husb: update iface failed. no matching configuration\n");
+        goto fail;
+    }
+    nb_interfaces = dev->descr[i + 4];
+
+#ifdef USBDEVFS_DISCONNECT
+    /* earlier Linux 2.4 do not support that */
+    {
+        struct usbdevfs_ioctl ctrl;
+        for (interface = 0; interface < nb_interfaces; interface++) {
+            ctrl.ioctl_code = USBDEVFS_DISCONNECT;
+            ctrl.ifno = interface;
+            ret = ioctl(dev->fd, USBDEVFS_IOCTL, &ctrl);
+            if (ret < 0 && errno != ENODATA) {
+                perror("USBDEVFS_DISCONNECT");
+                goto fail;
+            }
+        }
+    }
+#endif
+
+    /* XXX: only grab if all interfaces are free */
+    for (interface = 0; interface < nb_interfaces; interface++) {
+        ret = ioctl(dev->fd, USBDEVFS_CLAIMINTERFACE, &interface);
+        if (ret < 0) {
+            if (errno == EBUSY) {
+                printf("husb: update iface. device already grabbed\n");
+            } else {
+                perror("husb: failed to claim interface");
+            }
+        fail:
+            return 0;
+        }
+    }
+
+    printf("husb: %d interfaces claimed for configuration %d\n",
+           nb_interfaces, configuration);
+
+    dev->ninterfaces   = nb_interfaces;
+    dev->configuration = configuration;
+    return 1;
+}
+
+static int usb_host_release_interfaces(USBHostDevice *s)
+{
+    int ret, i;
+
+    dprintf("husb: releasing interfaces\n");
+
+    for (i = 0; i < s->ninterfaces; i++) {
+        ret = ioctl(s->fd, USBDEVFS_RELEASEINTERFACE, &i);
+        if (ret < 0) {
+            perror("husb: failed to release interface");
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static void usb_host_handle_reset(USBDevice *dev)
+{
+    USBHostDevice *s = (USBHostDevice *) dev;
+
+    dprintf("husb: reset device %u.%u\n", s->bus_num, s->addr);
+
+    ioctl(s->fd, USBDEVFS_RESET);
+
+    usb_host_claim_interfaces(s, s->configuration);
+}
+
+static void usb_host_handle_destroy(USBDevice *dev)
+{
+    USBHostDevice *s = (USBHostDevice *)dev;
+
+    s->closing = 1;
+
+    qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+
+    hostdev_unlink(s);
+
+    async_complete(s);
+
+    if (s->fd >= 0)
+        close(s->fd);
+
+    qemu_free(s);
+}
+
+static int usb_linux_update_endp_table(USBHostDevice *s);
+
+static int usb_host_handle_data(USBHostDevice *s, USBPacket *p)
+{
+    struct usbdevfs_urb *urb;
+    AsyncURB *aurb;
+    int ret;
+
+    aurb = async_alloc();
+    if (!aurb) {
+        dprintf("husb: async malloc failed\n");
+        return USB_RET_NAK;
+    }
+    aurb->hdev   = s;
+    aurb->packet = p;
+
+    urb = &aurb->urb;
+
+    if (p->pid == USB_TOKEN_IN)
+    	urb->endpoint = p->devep | 0x80;
+    else
+    	urb->endpoint = p->devep;
+
+    if (is_halted(s, p->devep)) {
+	ret = ioctl(s->fd, USBDEVFS_CLEAR_HALT, &urb->endpoint);
+        if (ret < 0) {
+            dprintf("husb: failed to clear halt. ep 0x%x errno %d\n", 
+                   urb->endpoint, errno);
+            return USB_RET_NAK;
+        }
+        clear_halt(s, p->devep);
+    }
+
+    urb->buffer        = p->data;
+    urb->buffer_length = p->len;
+
+    if (is_isoc(s, p->devep)) {
+        /* Setup ISOC transfer */
+        urb->type     = USBDEVFS_URB_TYPE_ISO;
+        urb->flags    = USBDEVFS_URB_ISO_ASAP;
+        urb->number_of_packets = 1;
+        urb->iso_frame_desc[0].length = p->len;
+    } else {
+        /* Setup bulk transfer */
+        urb->type     = USBDEVFS_URB_TYPE_BULK;
+    }
+
+    urb->usercontext = s;
+
+    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+    dprintf("husb: data submit. ep 0x%x len %u aurb %p\n", urb->endpoint, p->len, aurb);
+
+    if (ret < 0) {
+        dprintf("husb: submit failed. errno %d\n", errno);
+        async_free(aurb);
+
+        switch(errno) {
+        case ETIMEDOUT:
+            return USB_RET_NAK;
+        case EPIPE:
+        default:
+            return USB_RET_STALL;
+        }
+    }
+
+    usb_defer_packet(p, async_cancel, aurb);
+    return USB_RET_ASYNC;
+}
+
+static int ctrl_error(void)
+{
+    if (errno == ETIMEDOUT)
+        return USB_RET_NAK;
+    else 
+        return USB_RET_STALL;
+}
+
+static int usb_host_set_address(USBHostDevice *s, int addr)
+{
+    dprintf("husb: ctrl set addr %u\n", addr);
+    s->dev.addr = addr;
+    return 0;
+}
+
+static int usb_host_set_config(USBHostDevice *s, int config)
+{
+    usb_host_release_interfaces(s);
+
+    int ret = ioctl(s->fd, USBDEVFS_SETCONFIGURATION, &config);
+ 
+    dprintf("husb: ctrl set config %d ret %d errno %d\n", config, ret, errno);
+    
+    if (ret < 0)
+        return ctrl_error();
+ 
+    usb_host_claim_interfaces(s, config);
+    return 0;
+}
+
+static int usb_host_set_interface(USBHostDevice *s, int iface, int alt)
+{
+    struct usbdevfs_setinterface si;
+    int ret;
+
+    si.interface  = iface;
+    si.altsetting = alt;
+    ret = ioctl(s->fd, USBDEVFS_SETINTERFACE, &si);
+    
+    dprintf("husb: ctrl set iface %d altset %d ret %d errno %d\n", 
+    	iface, alt, ret, errno);
+    
+    if (ret < 0)
+        return ctrl_error();
+
+    usb_linux_update_endp_table(s);
+    return 0;
+}
+
+static int usb_host_handle_control(USBHostDevice *s, USBPacket *p)
+{
+    struct usbdevfs_urb *urb;
+    AsyncURB *aurb;
+    int ret, value, index;
+
+    /* 
+     * Process certain standard device requests.
+     * These are infrequent and are processed synchronously.
+     */
+    value = le16_to_cpu(s->ctrl.req.wValue);
+    index = le16_to_cpu(s->ctrl.req.wIndex);
+
+    dprintf("husb: ctrl type 0x%x req 0x%x val 0x%x index %u len %u\n",
+        s->ctrl.req.bRequestType, s->ctrl.req.bRequest, value, index, 
+        s->ctrl.len);
+
+    if (s->ctrl.req.bRequestType == 0) {
+        switch (s->ctrl.req.bRequest) {
+        case USB_REQ_SET_ADDRESS:
+            return usb_host_set_address(s, value);
+
+        case USB_REQ_SET_CONFIGURATION:
+            return usb_host_set_config(s, value & 0xff);
+        }
+    }
+
+    if (s->ctrl.req.bRequestType == 1 &&
+                  s->ctrl.req.bRequest == USB_REQ_SET_INTERFACE)
+        return usb_host_set_interface(s, index, value);
+
+    /* The rest are asynchronous */
+
+    aurb = async_alloc();
+    if (!aurb) {
+        dprintf("husb: async malloc failed\n");
+        return USB_RET_NAK;
+    }
+    aurb->hdev   = s;
+    aurb->packet = p;
+
+    /* 
+     * Setup ctrl transfer.
+     *
+     * s->ctrl is layed out such that data buffer immediately follows
+     * 'req' struct which is exactly what usbdevfs expects.
+     */ 
+    urb = &aurb->urb;
+
+    urb->type     = USBDEVFS_URB_TYPE_CONTROL;
+    urb->endpoint = p->devep;
+
+    urb->buffer        = &s->ctrl.req;
+    urb->buffer_length = 8 + s->ctrl.len;
+
+    urb->usercontext = s;
+
+    ret = ioctl(s->fd, USBDEVFS_SUBMITURB, urb);
+
+    dprintf("husb: submit ctrl. len %u aurb %p\n", urb->buffer_length, aurb);
+
+    if (ret < 0) {
+        dprintf("husb: submit failed. errno %d\n", errno);
+        async_free(aurb);
+
+        switch(errno) {
+        case ETIMEDOUT:
+            return USB_RET_NAK;
+        case EPIPE:
+        default:
+            return USB_RET_STALL;
+        }
+    }
+
+    usb_defer_packet(p, async_cancel, aurb);
+    return USB_RET_ASYNC;
+}
+
+static int do_token_setup(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = (USBHostDevice *) dev;
+    int ret = 0;
+
+    if (p->len != 8)
+        return USB_RET_STALL;
+ 
+    memcpy(&s->ctrl.req, p->data, 8);
+    s->ctrl.len    = le16_to_cpu(s->ctrl.req.wLength);
+    s->ctrl.offset = 0;
+    s->ctrl.state  = CTRL_STATE_SETUP;
+
+    if (s->ctrl.req.bRequestType & USB_DIR_IN) {
+        ret = usb_host_handle_control(s, p);
+        if (ret < 0)
+            return ret;
+
+        if (ret < s->ctrl.len)
+            s->ctrl.len = ret;
+        s->ctrl.state = CTRL_STATE_DATA;
+    } else {
+        if (s->ctrl.len == 0)
+            s->ctrl.state = CTRL_STATE_ACK;
+        else
+            s->ctrl.state = CTRL_STATE_DATA;
+    }
+
+    return ret;
+}
+
+static int do_token_in(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = (USBHostDevice *) dev;
+    int ret = 0;
+
+    if (p->devep != 0)
+        return usb_host_handle_data(s, p);
+
+    switch(s->ctrl.state) {
+    case CTRL_STATE_ACK:
+        if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
+            ret = usb_host_handle_control(s, p);
+            if (ret == USB_RET_ASYNC)
+                return USB_RET_ASYNC;
+
+            s->ctrl.state = CTRL_STATE_IDLE;
+            return ret > 0 ? 0 : ret;
+        }
+
+        return 0;
+
+    case CTRL_STATE_DATA:
+        if (s->ctrl.req.bRequestType & USB_DIR_IN) {
+            int len = s->ctrl.len - s->ctrl.offset;
+            if (len > p->len)
+                len = p->len;
+            memcpy(p->data, s->ctrl.buffer + s->ctrl.offset, len);
+            s->ctrl.offset += len;
+            if (s->ctrl.offset >= s->ctrl.len)
+                s->ctrl.state = CTRL_STATE_ACK;
+            return len;
+        }
+
+        s->ctrl.state = CTRL_STATE_IDLE;
+        return USB_RET_STALL;
+
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+static int do_token_out(USBDevice *dev, USBPacket *p)
+{
+    USBHostDevice *s = (USBHostDevice *) dev;
+
+    if (p->devep != 0)
+        return usb_host_handle_data(s, p);
+
+    switch(s->ctrl.state) {
+    case CTRL_STATE_ACK:
+        if (s->ctrl.req.bRequestType & USB_DIR_IN) {
+            s->ctrl.state = CTRL_STATE_IDLE;
+            /* transfer OK */
+        } else {
+            /* ignore additional output */
+        }
+        return 0;
+
+    case CTRL_STATE_DATA:
+        if (!(s->ctrl.req.bRequestType & USB_DIR_IN)) {
+            int len = s->ctrl.len - s->ctrl.offset;
+            if (len > p->len)
+                len = p->len;
+            memcpy(s->ctrl.buffer + s->ctrl.offset, p->data, len);
+            s->ctrl.offset += len;
+            if (s->ctrl.offset >= s->ctrl.len)
+                s->ctrl.state = CTRL_STATE_ACK;
+            return len;
+        }
+
+        s->ctrl.state = CTRL_STATE_IDLE;
+        return USB_RET_STALL;
+
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+/*
+ * Packet handler.
+ * Called by the HC (host controller).
+ *
+ * Returns length of the transaction or one of the USB_RET_XXX codes.
+ */
+static int usb_host_handle_packet(USBDevice *s, USBPacket *p)
+{
+    switch(p->pid) {
+    case USB_MSG_ATTACH:
+        s->state = USB_STATE_ATTACHED;
+        return 0;
+
+    case USB_MSG_DETACH:
+        s->state = USB_STATE_NOTATTACHED;
+        return 0;
+
+    case USB_MSG_RESET:
+        s->remote_wakeup = 0;
+        s->addr = 0;
+        s->state = USB_STATE_DEFAULT;
+        s->handle_reset(s);
+        return 0;
+    }
+
+    /* Rest of the PIDs must match our address */
+    if (s->state < USB_STATE_DEFAULT || p->devaddr != s->addr)
+        return USB_RET_NODEV;
+
+    switch (p->pid) {
+    case USB_TOKEN_SETUP:
+        return do_token_setup(s, p);
+
+    case USB_TOKEN_IN:
+        return do_token_in(s, p);
+
+    case USB_TOKEN_OUT:
+        return do_token_out(s, p);
+ 
+    default:
+        return USB_RET_STALL;
+    }
+}
+
+/* returns 1 on problem encountered or 0 for success */
+static int usb_linux_update_endp_table(USBHostDevice *s)
+{
+    uint8_t *descriptors;
+    uint8_t devep, type, configuration, alt_interface;
+    struct usbdevfs_ctrltransfer ct;
+    int interface, ret, length, i;
+
+    ct.bRequestType = USB_DIR_IN;
+    ct.bRequest = USB_REQ_GET_CONFIGURATION;
+    ct.wValue = 0;
+    ct.wIndex = 0;
+    ct.wLength = 1;
+    ct.data = &configuration;
+    ct.timeout = 50;
+
+    ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+    if (ret < 0) {
+        perror("usb_linux_update_endp_table");
+        return 1;
+    }
+
+    /* in address state */
+    if (configuration == 0)
+        return 1;
+
+    /* get the desired configuration, interface, and endpoint descriptors
+     * from device description */
+    descriptors = &s->descr[18];
+    length = s->descr_len - 18;
+    i = 0;
+
+    if (descriptors[i + 1] != USB_DT_CONFIG ||
+        descriptors[i + 5] != configuration) {
+        dprintf("invalid descriptor data - configuration\n");
+        return 1;
+    }
+    i += descriptors[i];
+
+    while (i < length) {
+        if (descriptors[i + 1] != USB_DT_INTERFACE ||
+            (descriptors[i + 1] == USB_DT_INTERFACE &&
+             descriptors[i + 4] == 0)) {
+            i += descriptors[i];
+            continue;
+        }
+
+        interface = descriptors[i + 2];
+
+        ct.bRequestType = USB_DIR_IN | USB_RECIP_INTERFACE;
+        ct.bRequest = USB_REQ_GET_INTERFACE;
+        ct.wValue = 0;
+        ct.wIndex = interface;
+        ct.wLength = 1;
+        ct.data = &alt_interface;
+        ct.timeout = 50;
+
+        ret = ioctl(s->fd, USBDEVFS_CONTROL, &ct);
+        if (ret < 0) {
+            perror("usb_linux_update_endp_table");
+            return 1;
+        }
+
+        /* the current interface descriptor is the active interface
+         * and has endpoints */
+        if (descriptors[i + 3] != alt_interface) {
+            i += descriptors[i];
+            continue;
+        }
+
+        /* advance to the endpoints */
+        while (i < length && descriptors[i +1] != USB_DT_ENDPOINT)
+            i += descriptors[i];
+
+        if (i >= length)
+            break;
+
+        while (i < length) {
+            if (descriptors[i + 1] != USB_DT_ENDPOINT)
+                break;
+
+            devep = descriptors[i + 2];
+            switch (descriptors[i + 3] & 0x3) {
+            case 0x00:
+                type = USBDEVFS_URB_TYPE_CONTROL;
+                break;
+            case 0x01:
+                type = USBDEVFS_URB_TYPE_ISO;
+                break;
+            case 0x02:
+                type = USBDEVFS_URB_TYPE_BULK;
+                break;
+            case 0x03:
+                type = USBDEVFS_URB_TYPE_INTERRUPT;
+                break;
+            default:
+                dprintf("usb_host: malformed endpoint type\n");
+                type = USBDEVFS_URB_TYPE_BULK;
+            }
+            s->endp_table[(devep & 0xf) - 1].type = type;
+            s->endp_table[(devep & 0xf) - 1].halted = 0;
+
+            i += descriptors[i];
+        }
+    }
+    return 0;
+}
+
+static USBDevice *usb_host_device_open_addr(int bus_num, int addr, const char *prod_name)
+{
+    int fd = -1, ret;
+    USBHostDevice *dev = NULL;
+    struct usbdevfs_connectinfo ci;
+    char buf[1024];
+
+    dev = qemu_mallocz(sizeof(USBHostDevice));
+    if (!dev)
+        goto fail;
+
+    dev->bus_num = bus_num;
+    dev->addr = addr;
+
+    printf("husb: open device %d.%d\n", bus_num, addr);
+
+    snprintf(buf, sizeof(buf), USBDEVFS_PATH "/%03d/%03d",
+             bus_num, addr);
+    fd = open(buf, O_RDWR | O_NONBLOCK);
+    if (fd < 0) {
+        perror(buf);
+        goto fail;
+    }
+
+    /* read the device description */
+    dev->descr_len = read(fd, dev->descr, sizeof(dev->descr));
+    if (dev->descr_len <= 0) {
+        perror("husb: reading device data failed");
+        goto fail;
+    }
+
+#ifdef DEBUG
+    {
+        int x;
+        printf("=== begin dumping device descriptor data ===\n");
+        for (x = 0; x < dev->descr_len; x++)
+            printf("%02x ", dev->descr[x]);
+        printf("\n=== end dumping device descriptor data ===\n");
+    }
+#endif
+
+    dev->fd = fd;
+
+    /* 
+     * Initial configuration is -1 which makes us claim first 
+     * available config. We used to start with 1, which does not
+     * always work. I've seen devices where first config starts 
+     * with 2.
+     */
+    if (!usb_host_claim_interfaces(dev, -1))
+        goto fail;
+
+    ret = ioctl(fd, USBDEVFS_CONNECTINFO, &ci);
+    if (ret < 0) {
+        perror("usb_host_device_open: USBDEVFS_CONNECTINFO");
+        goto fail;
+    }
+
+    printf("husb: grabbed usb device %d.%d\n", bus_num, addr);
+
+    ret = usb_linux_update_endp_table(dev);
+    if (ret)
+        goto fail;
+
+    if (ci.slow)
+        dev->dev.speed = USB_SPEED_LOW;
+    else
+        dev->dev.speed = USB_SPEED_HIGH;
+
+    dev->dev.handle_packet  = usb_host_handle_packet;
+    dev->dev.handle_reset   = usb_host_handle_reset;
+    dev->dev.handle_destroy = usb_host_handle_destroy;
+
+    if (!prod_name || prod_name[0] == '\0')
+        snprintf(dev->dev.devname, sizeof(dev->dev.devname),
+                 "host:%d.%d", bus_num, addr);
+    else
+        pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
+                prod_name);
+
+    /* USB devio uses 'write' flag to check for async completions */
+    qemu_set_fd_handler(dev->fd, NULL, async_complete, dev);
+
+    hostdev_link(dev);
+
+    return (USBDevice *) dev;
+
+fail:
+    if (dev)
+        qemu_free(dev);
+
+    close(fd);
+    return NULL;
+}
+
+static int usb_host_auto_add(const char *spec);
+static int usb_host_auto_del(const char *spec);
+
+USBDevice *usb_host_device_open(const char *devname)
+{
+    int bus_num, addr;
+    char product_name[PRODUCT_NAME_SZ];
+
+    if (strstr(devname, "auto:")) {
+        usb_host_auto_add(devname);
+        return NULL;
+    }
+
+    if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name),
+                             devname) < 0)
+        return NULL;
+
+    if (hostdev_find(bus_num, addr)) {
+       term_printf("husb: host usb device %d.%d is already open\n", bus_num, addr);
+       return NULL;
+    }
+
+    return usb_host_device_open_addr(bus_num, addr, product_name);
+}
+
+int usb_host_device_close(const char *devname)
+{
+    char product_name[PRODUCT_NAME_SZ];
+    int bus_num, addr;
+    USBHostDevice *s;
+
+    if (strstr(devname, "auto:"))
+        return usb_host_auto_del(devname);
+
+    if (usb_host_find_device(&bus_num, &addr, product_name, sizeof(product_name),
+                             devname) < 0)
+        return -1;
+ 
+    s = hostdev_find(bus_num, addr);
+    if (s) {
+        usb_device_del_addr(0, s->dev.addr);
+        return 0;
+    }
+
+    return -1;
+}
+ 
+static int get_tag_value(char *buf, int buf_size,
+                         const char *str, const char *tag,
+                         const char *stopchars)
+{
+    const char *p;
+    char *q;
+    p = strstr(str, tag);
+    if (!p)
+        return -1;
+    p += strlen(tag);
+    while (isspace(*p))
+        p++;
+    q = buf;
+    while (*p != '\0' && !strchr(stopchars, *p)) {
+        if ((q - buf) < (buf_size - 1))
+            *q++ = *p;
+        p++;
+    }
+    *q = '\0';
+    return q - buf;
+}
+
+static int usb_host_scan(void *opaque, USBScanFunc *func)
+{
+    FILE *f;
+    char line[1024];
+    char buf[1024];
+    int bus_num, addr, speed, device_count, class_id, product_id, vendor_id;
+    int ret;
+    char product_name[512];
+
+    f = fopen(USBDEVFS_PATH "/devices", "r");
+    if (!f) {
+        term_printf("husb: could not open %s\n", USBDEVFS_PATH "/devices");
+        return 0;
+    }
+    device_count = 0;
+    bus_num = addr = speed = class_id = product_id = vendor_id = 0;
+    ret = 0;
+    for(;;) {
+        if (fgets(line, sizeof(line), f) == NULL)
+            break;
+        if (strlen(line) > 0)
+            line[strlen(line) - 1] = '\0';
+        if (line[0] == 'T' && line[1] == ':') {
+            if (device_count && (vendor_id || product_id)) {
+                /* New device.  Add the previously discovered device.  */
+                ret = func(opaque, bus_num, addr, class_id, vendor_id,
+                           product_id, product_name, speed);
+                if (ret)
+                    goto the_end;
+            }
+            if (get_tag_value(buf, sizeof(buf), line, "Bus=", " ") < 0)
+                goto fail;
+            bus_num = atoi(buf);
+            if (get_tag_value(buf, sizeof(buf), line, "Dev#=", " ") < 0)
+                goto fail;
+            addr = atoi(buf);
+            if (get_tag_value(buf, sizeof(buf), line, "Spd=", " ") < 0)
+                goto fail;
+            if (!strcmp(buf, "480"))
+                speed = USB_SPEED_HIGH;
+            else if (!strcmp(buf, "1.5"))
+                speed = USB_SPEED_LOW;
+            else
+                speed = USB_SPEED_FULL;
+            product_name[0] = '\0';
+            class_id = 0xff;
+            device_count++;
+            product_id = 0;
+            vendor_id = 0;
+        } else if (line[0] == 'P' && line[1] == ':') {
+            if (get_tag_value(buf, sizeof(buf), line, "Vendor=", " ") < 0)
+                goto fail;
+            vendor_id = strtoul(buf, NULL, 16);
+            if (get_tag_value(buf, sizeof(buf), line, "ProdID=", " ") < 0)
+                goto fail;
+            product_id = strtoul(buf, NULL, 16);
+        } else if (line[0] == 'S' && line[1] == ':') {
+            if (get_tag_value(buf, sizeof(buf), line, "Product=", "") < 0)
+                goto fail;
+            pstrcpy(product_name, sizeof(product_name), buf);
+        } else if (line[0] == 'D' && line[1] == ':') {
+            if (get_tag_value(buf, sizeof(buf), line, "Cls=", " (") < 0)
+                goto fail;
+            class_id = strtoul(buf, NULL, 16);
+        }
+    fail: ;
+    }
+    if (device_count && (vendor_id || product_id)) {
+        /* Add the last device.  */
+        ret = func(opaque, bus_num, addr, class_id, vendor_id,
+                   product_id, product_name, speed);
+    }
+ the_end:
+    fclose(f);
+    return ret;
+}
+
+struct USBAutoFilter {
+    struct USBAutoFilter *next;
+    int bus_num;
+    int addr;
+    int vendor_id;
+    int product_id;
+};
+
+static QEMUTimer *usb_auto_timer;
+static struct USBAutoFilter *usb_auto_filter;
+
+static int usb_host_auto_scan(void *opaque, int bus_num, int addr,
+                     int class_id, int vendor_id, int product_id,
+                     const char *product_name, int speed)
+{
+    struct USBAutoFilter *f;
+    struct USBDevice *dev;
+
+    /* Ignore hubs */
+    if (class_id == 9)
+        return 0;
+
+    for (f = usb_auto_filter; f; f = f->next) {
+	if (f->bus_num >= 0 && f->bus_num != bus_num)
+            continue;
+
+	if (f->addr >= 0 && f->addr != addr)
+            continue;
+
+	if (f->vendor_id >= 0 && f->vendor_id != vendor_id)
+            continue;
+
+	if (f->product_id >= 0 && f->product_id != product_id)
+            continue;
+
+        /* We got a match */
+
+        /* Allredy attached ? */
+        if (hostdev_find(bus_num, addr))
+            return 0;
+
+        dprintf("husb: auto open: bus_num %d addr %d\n", bus_num, addr);
+
+	dev = usb_host_device_open_addr(bus_num, addr, product_name);
+	if (dev)
+	    usb_device_add_dev(dev);
+    }
+
+    return 0;
+}
+
+static void usb_host_auto_timer(void *unused)
+{
+    usb_host_scan(NULL, usb_host_auto_scan);
+    qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+}
+
+/*
+ * Autoconnect filter
+ * Format:
+ *    auto:bus:dev[:vid:pid]
+ *    auto:bus.dev[:vid:pid]
+ *
+ *    bus  - bus number    (dec, * means any)
+ *    dev  - device number (dec, * means any)
+ *    vid  - vendor id     (hex, * means any)
+ *    pid  - product id    (hex, * means any)
+ *
+ *    See 'lsusb' output.
+ */
+static int parse_filter(const char *spec, struct USBAutoFilter *f)
+{
+    enum { BUS, DEV, VID, PID, DONE };
+    const char *p = spec;
+    int i;
+
+    f->bus_num    = -1;
+    f->addr       = -1;
+    f->vendor_id  = -1;
+    f->product_id = -1;
+
+    for (i = BUS; i < DONE; i++) {
+    	p = strpbrk(p, ":.");
+    	if (!p) break;
+        p++;
+ 
+    	if (*p == '*')
+            continue;
+
+        switch(i) {
+        case BUS: f->bus_num = strtol(p, NULL, 10);    break;
+        case DEV: f->addr    = strtol(p, NULL, 10);    break;
+        case VID: f->vendor_id  = strtol(p, NULL, 16); break;
+        case PID: f->product_id = strtol(p, NULL, 16); break;
+        }
+    }
+
+    if (i < DEV) {
+        fprintf(stderr, "husb: invalid auto filter spec %s\n", spec);
+        return -1;
+    }
+
+    return 0;
+}
+
+static int match_filter(const struct USBAutoFilter *f1, 
+                        const struct USBAutoFilter *f2)
+{
+    return f1->bus_num    == f2->bus_num &&
+           f1->addr       == f2->addr &&
+           f1->vendor_id  == f2->vendor_id &&
+           f1->product_id == f2->product_id;
+}
+
+static int usb_host_auto_add(const char *spec)
+{
+    struct USBAutoFilter filter, *f;
+
+    if (parse_filter(spec, &filter) < 0)
+        return -1;
+
+    f = qemu_mallocz(sizeof(*f));
+    if (!f) {
+        fprintf(stderr, "husb: failed to allocate auto filter\n");
+        return -1;
+    }
+
+    *f = filter; 
+
+    if (!usb_auto_filter) {
+        /*
+         * First entry. Init and start the monitor.
+         * Right now we're using timer to check for new devices.
+         * If this turns out to be too expensive we can move that into a 
+         * separate thread.
+         */
+	usb_auto_timer = qemu_new_timer(rt_clock, usb_host_auto_timer, NULL);
+	if (!usb_auto_timer) {
+            fprintf(stderr, "husb: failed to allocate auto scan timer\n");
+            qemu_free(f);
+            return -1;
+        }
+
+        /* Check for new devices every two seconds */
+        qemu_mod_timer(usb_auto_timer, qemu_get_clock(rt_clock) + 2000);
+    }
+
+    dprintf("husb: added auto filter: bus_num %d addr %d vid %d pid %d\n",
+	f->bus_num, f->addr, f->vendor_id, f->product_id);
+
+    f->next = usb_auto_filter;
+    usb_auto_filter = f;
+
+    return 0;
+}
+
+static int usb_host_auto_del(const char *spec)
+{
+    struct USBAutoFilter *pf = usb_auto_filter;
+    struct USBAutoFilter **prev = &usb_auto_filter;
+    struct USBAutoFilter filter;
+
+    if (parse_filter(spec, &filter) < 0)
+        return -1;
+
+    while (pf) {
+        if (match_filter(pf, &filter)) {
+            dprintf("husb: removed auto filter: bus_num %d addr %d vid %d pid %d\n",
+	             pf->bus_num, pf->addr, pf->vendor_id, pf->product_id);
+
+            *prev = pf->next;
+
+	    if (!usb_auto_filter) {
+                /* No more filters. Stop scanning. */
+                qemu_del_timer(usb_auto_timer);
+                qemu_free_timer(usb_auto_timer);
+            }
+
+            return 0;
+        }
+
+        prev = &pf->next;
+        pf   = pf->next;
+    }
+
+    return -1;
+}
+
+typedef struct FindDeviceState {
+    int vendor_id;
+    int product_id;
+    int bus_num;
+    int addr;
+    char product_name[PRODUCT_NAME_SZ];
+} FindDeviceState;
+
+static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
+                                     int class_id,
+                                     int vendor_id, int product_id,
+                                     const char *product_name, int speed)
+{
+    FindDeviceState *s = opaque;
+    if ((vendor_id == s->vendor_id &&
+        product_id == s->product_id) ||
+        (bus_num == s->bus_num &&
+        addr == s->addr)) {
+        pstrcpy(s->product_name, PRODUCT_NAME_SZ, product_name);
+        s->bus_num = bus_num;
+        s->addr = addr;
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+/* the syntax is :
+   'bus.addr' (decimal numbers) or
+   'vendor_id:product_id' (hexa numbers) */
+static int usb_host_find_device(int *pbus_num, int *paddr,
+                                char *product_name, int product_name_size,
+                                const char *devname)
+{
+    const char *p;
+    int ret;
+    FindDeviceState fs;
+
+    p = strchr(devname, '.');
+    if (p) {
+        *pbus_num = strtoul(devname, NULL, 0);
+        *paddr = strtoul(p + 1, NULL, 0);
+        fs.bus_num = *pbus_num;
+        fs.addr = *paddr;
+        ret = usb_host_scan(&fs, usb_host_find_device_scan);
+        if (ret)
+            pstrcpy(product_name, product_name_size, fs.product_name);
+        return 0;
+    }
+
+    p = strchr(devname, ':');
+    if (p) {
+        fs.vendor_id = strtoul(devname, NULL, 16);
+        fs.product_id = strtoul(p + 1, NULL, 16);
+        ret = usb_host_scan(&fs, usb_host_find_device_scan);
+        if (ret) {
+            *pbus_num = fs.bus_num;
+            *paddr = fs.addr;
+            pstrcpy(product_name, product_name_size, fs.product_name);
+            return 0;
+        }
+    }
+    return -1;
+}
+
+/**********************/
+/* USB host device info */
+
+struct usb_class_info {
+    int class;
+    const char *class_name;
+};
+
+static const struct usb_class_info usb_class_info[] = {
+    { USB_CLASS_AUDIO, "Audio"},
+    { USB_CLASS_COMM, "Communication"},
+    { USB_CLASS_HID, "HID"},
+    { USB_CLASS_HUB, "Hub" },
+    { USB_CLASS_PHYSICAL, "Physical" },
+    { USB_CLASS_PRINTER, "Printer" },
+    { USB_CLASS_MASS_STORAGE, "Storage" },
+    { USB_CLASS_CDC_DATA, "Data" },
+    { USB_CLASS_APP_SPEC, "Application Specific" },
+    { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
+    { USB_CLASS_STILL_IMAGE, "Still Image" },
+    { USB_CLASS_CSCID, "Smart Card" },
+    { USB_CLASS_CONTENT_SEC, "Content Security" },
+    { -1, NULL }
+};
+
+static const char *usb_class_str(uint8_t class)
+{
+    const struct usb_class_info *p;
+    for(p = usb_class_info; p->class != -1; p++) {
+        if (p->class == class)
+            break;
+    }
+    return p->class_name;
+}
+
+static void usb_info_device(int bus_num, int addr, int class_id,
+                            int vendor_id, int product_id,
+                            const char *product_name,
+                            int speed)
+{
+    const char *class_str, *speed_str;
+
+    switch(speed) {
+    case USB_SPEED_LOW:
+        speed_str = "1.5";
+        break;
+    case USB_SPEED_FULL:
+        speed_str = "12";
+        break;
+    case USB_SPEED_HIGH:
+        speed_str = "480";
+        break;
+    default:
+        speed_str = "?";
+        break;
+    }
+
+    term_printf("  Device %d.%d, speed %s Mb/s\n",
+                bus_num, addr, speed_str);
+    class_str = usb_class_str(class_id);
+    if (class_str)
+        term_printf("    %s:", class_str);
+    else
+        term_printf("    Class %02x:", class_id);
+    term_printf(" USB device %04x:%04x", vendor_id, product_id);
+    if (product_name[0] != '\0')
+        term_printf(", %s", product_name);
+    term_printf("\n");
+}
+
+static int usb_host_info_device(void *opaque, int bus_num, int addr,
+                                int class_id,
+                                int vendor_id, int product_id,
+                                const char *product_name,
+                                int speed)
+{
+    usb_info_device(bus_num, addr, class_id, vendor_id, product_id,
+                    product_name, speed);
+    return 0;
+}
+
+static void dec2str(int val, char *str)
+{
+    if (val == -1)
+        strcpy(str, "*");
+    else
+        sprintf(str, "%d", val); 
+}
+
+static void hex2str(int val, char *str)
+{
+    if (val == -1)
+        strcpy(str, "*");
+    else
+        sprintf(str, "%x", val);
+}
+
+void usb_host_info(void)
+{
+    struct USBAutoFilter *f;
+
+    usb_host_scan(NULL, usb_host_info_device);
+
+    if (usb_auto_filter)
+        term_printf("  Auto filters:\n");
+    for (f = usb_auto_filter; f; f = f->next) {
+        char bus[10], addr[10], vid[10], pid[10];
+        dec2str(f->bus_num, bus);
+        dec2str(f->addr, addr);
+        hex2str(f->vendor_id, vid);
+        hex2str(f->product_id, pid);
+    	term_printf("    Device %s.%s ID %s:%s\n", bus, addr, vid, pid);
+    }
+}
+
+#else
+
+#include "hw/usb.h"
+
+void usb_host_info(void)
+{
+    term_printf("USB host devices not supported\n");
+}
+
+/* XXX: modify configure to compile the right host driver */
+USBDevice *usb_host_device_open(const char *devname)
+{
+    return NULL;
+}
+
+int usb_host_device_close(const char *devname)
+{
+    return 0;
+}
+
+#endif
diff --git a/varint.c b/varint.c
new file mode 100644
index 0000000..41f6c67
--- /dev/null
+++ b/varint.c
@@ -0,0 +1,118 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include <inttypes.h>
+#include "varint.h"
+
+// Define some constants for powers of two.
+static const int k2Exp6 = 64;
+static const uint32_t k2Exp7 = 128;
+static const int k2Exp13 = 8192;
+static const uint32_t k2Exp14 = 16384;
+static const int k2Exp20 = (1 * 1024 * 1024);
+static const uint32_t k2Exp21 = (2 * 1024 * 1024);
+static const int k2Exp27 = (128 * 1024 * 1024);
+static const uint32_t k2Exp28 = (256 * 1024 * 1024);
+static const uint64_t k2Exp35 = (32LL * 1024LL * 1024LL * 1024LL);
+static const uint64_t k2Exp42 = (4LL * 1024LL * 1024LL * 1024LL * 1024LL);
+
+// Encodes the 64-bit value "value" using the varint encoding.  The varint
+// encoding uses a prefix followed by some data bits.  The valid prefixes
+// and the number of data bits are given in the table below.
+//
+// Prefix     Bytes  Data bits
+// 0          1      7
+// 10         2      14
+// 110        3      21
+// 1110       4      28
+// 11110      5      35
+// 111110     6      42
+// 11111100   9      64
+// 11111101   reserved
+// 11111110   reserved
+// 11111111   reserved
+char *varint_encode(uint64_t value, char *buf) {
+  if (value < k2Exp7) {
+    *buf++ = value;
+  } else if (value < k2Exp14) {
+    *buf++ = (2 << 6) | (value >> 8);
+    *buf++ = value & 0xff;
+  } else if (value < k2Exp21) {
+    *buf++ = (6 << 5) | (value >> 16);
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  } else if (value < k2Exp28) {
+    *buf++ = (0xe << 4) | (value >> 24);
+    *buf++ = (value >> 16) & 0xff;
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  } else if (value < k2Exp35) {
+    *buf++ = (0x1e << 3) | (value >> 32);
+    *buf++ = (value >> 24) & 0xff;
+    *buf++ = (value >> 16) & 0xff;
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  } else if (value < k2Exp42) {
+    *buf++ = (0x3e << 2) | (value >> 40);
+    *buf++ = (value >> 32) & 0xff;
+    *buf++ = (value >> 24) & 0xff;
+    *buf++ = (value >> 16) & 0xff;
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  } else {
+    *buf++ = (0x7e << 1);
+    *buf++ = (value >> 56) & 0xff;
+    *buf++ = (value >> 48) & 0xff;
+    *buf++ = (value >> 40) & 0xff;
+    *buf++ = (value >> 32) & 0xff;
+    *buf++ = (value >> 24) & 0xff;
+    *buf++ = (value >> 16) & 0xff;
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  }
+  return buf;
+}
+
+// Encodes the 35-bit signed value "value" using the varint encoding.
+// The varint encoding uses a prefix followed by some data bits.  The
+// valid prefixes and the number of data bits is given in the table
+// below.
+//
+// Prefix     Bytes  Data bits
+// 0          1      7
+// 10         2      14
+// 110        3      21
+// 1110       4      28
+// 11110      5      35
+char *varint_encode_signed(int64_t value, char *buf) {
+  if (value < k2Exp6 && value >= -k2Exp6) {
+    *buf++ = value & 0x7f;
+  } else if (value < k2Exp13 && value >= -k2Exp13) {
+    *buf++ = (2 << 6) | ((value >> 8) & 0x3f);
+    *buf++ = value & 0xff;
+  } else if (value < k2Exp20 && value >= -k2Exp20) {
+    *buf++ = (6 << 5) | ((value >> 16) & 0x1f);
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  } else if (value < k2Exp27 && value >= -k2Exp27) {
+    *buf++ = (0xe << 4) | ((value >> 24) & 0xf);
+    *buf++ = (value >> 16) & 0xff;
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  } else {
+    *buf++ = (0x1e << 3);
+    *buf++ = (value >> 24) & 0xff;
+    *buf++ = (value >> 16) & 0xff;
+    *buf++ = (value >> 8) & 0xff;
+    *buf++ = value & 0xff;
+  }
+  return buf;
+}
diff --git a/varint.h b/varint.h
new file mode 100644
index 0000000..7822756
--- /dev/null
+++ b/varint.h
@@ -0,0 +1,15 @@
+/* Copyright (C) 2007-2008 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+*/
+#include <inttypes.h>
+
+extern char *varint_encode(uint64_t value, char *buf);
+extern char *varint_encode_signed(int64_t value, char *buf);
diff --git a/vgafont.h b/vgafont.h
new file mode 100644
index 0000000..3606dd7
--- /dev/null
+++ b/vgafont.h
@@ -0,0 +1,4611 @@
+static const uint8_t vgafont16[256 * 16] = {
+
+	/* 0 0x00 '^@' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 1 0x01 '^A' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x81, /* 10000001 */
+	0xa5, /* 10100101 */
+	0x81, /* 10000001 */
+	0x81, /* 10000001 */
+	0xbd, /* 10111101 */
+	0x99, /* 10011001 */
+	0x81, /* 10000001 */
+	0x81, /* 10000001 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 2 0x02 '^B' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xff, /* 11111111 */
+	0xdb, /* 11011011 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xc3, /* 11000011 */
+	0xe7, /* 11100111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 3 0x03 '^C' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 4 0x04 '^D' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x7c, /* 01111100 */
+	0xfe, /* 11111110 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 5 0x05 '^E' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0xe7, /* 11100111 */
+	0xe7, /* 11100111 */
+	0xe7, /* 11100111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 6 0x06 '^F' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 7 0x07 '^G' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 8 0x08 '^H' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xe7, /* 11100111 */
+	0xc3, /* 11000011 */
+	0xc3, /* 11000011 */
+	0xe7, /* 11100111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 9 0x09 '^I' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x42, /* 01000010 */
+	0x42, /* 01000010 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 10 0x0a '^J' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xc3, /* 11000011 */
+	0x99, /* 10011001 */
+	0xbd, /* 10111101 */
+	0xbd, /* 10111101 */
+	0x99, /* 10011001 */
+	0xc3, /* 11000011 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 11 0x0b '^K' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1e, /* 00011110 */
+	0x0e, /* 00001110 */
+	0x1a, /* 00011010 */
+	0x32, /* 00110010 */
+	0x78, /* 01111000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 12 0x0c '^L' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 13 0x0d '^M' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3f, /* 00111111 */
+	0x33, /* 00110011 */
+	0x3f, /* 00111111 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x70, /* 01110000 */
+	0xf0, /* 11110000 */
+	0xe0, /* 11100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 14 0x0e '^N' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7f, /* 01111111 */
+	0x63, /* 01100011 */
+	0x7f, /* 01111111 */
+	0x63, /* 01100011 */
+	0x63, /* 01100011 */
+	0x63, /* 01100011 */
+	0x63, /* 01100011 */
+	0x67, /* 01100111 */
+	0xe7, /* 11100111 */
+	0xe6, /* 11100110 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 15 0x0f '^O' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xdb, /* 11011011 */
+	0x3c, /* 00111100 */
+	0xe7, /* 11100111 */
+	0x3c, /* 00111100 */
+	0xdb, /* 11011011 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 16 0x10 '^P' */
+	0x00, /* 00000000 */
+	0x80, /* 10000000 */
+	0xc0, /* 11000000 */
+	0xe0, /* 11100000 */
+	0xf0, /* 11110000 */
+	0xf8, /* 11111000 */
+	0xfe, /* 11111110 */
+	0xf8, /* 11111000 */
+	0xf0, /* 11110000 */
+	0xe0, /* 11100000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 17 0x11 '^Q' */
+	0x00, /* 00000000 */
+	0x02, /* 00000010 */
+	0x06, /* 00000110 */
+	0x0e, /* 00001110 */
+	0x1e, /* 00011110 */
+	0x3e, /* 00111110 */
+	0xfe, /* 11111110 */
+	0x3e, /* 00111110 */
+	0x1e, /* 00011110 */
+	0x0e, /* 00001110 */
+	0x06, /* 00000110 */
+	0x02, /* 00000010 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 18 0x12 '^R' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 19 0x13 '^S' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 20 0x14 '^T' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7f, /* 01111111 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0x7b, /* 01111011 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 21 0x15 '^U' */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x60, /* 01100000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x0c, /* 00001100 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 22 0x16 '^V' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 23 0x17 '^W' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 24 0x18 '^X' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 25 0x19 '^Y' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 26 0x1a '^Z' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0xfe, /* 11111110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 27 0x1b '^[' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xfe, /* 11111110 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 28 0x1c '^\' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 29 0x1d '^]' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x28, /* 00101000 */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x28, /* 00101000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 30 0x1e '^^' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x38, /* 00111000 */
+	0x7c, /* 01111100 */
+	0x7c, /* 01111100 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 31 0x1f '^_' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0x7c, /* 01111100 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 32 0x20 ' ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 33 0x21 '!' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 34 0x22 '"' */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x24, /* 00100100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 35 0x23 '#' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 36 0x24 '$' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc2, /* 11000010 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x86, /* 10000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 37 0x25 '%' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc2, /* 11000010 */
+	0xc6, /* 11000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc6, /* 11000110 */
+	0x86, /* 10000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 38 0x26 '&' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 39 0x27 ''' */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 40 0x28 '(' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 41 0x29 ')' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 42 0x2a '*' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0xff, /* 11111111 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 43 0x2b '+' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 44 0x2c ',' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 45 0x2d '-' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 46 0x2e '.' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 47 0x2f '/' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x02, /* 00000010 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0x80, /* 10000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 48 0x30 '0' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 49 0x31 '1' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x38, /* 00111000 */
+	0x78, /* 01111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 50 0x32 '2' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 51 0x33 '3' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x3c, /* 00111100 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 52 0x34 '4' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x0c, /* 00001100 */
+	0x1c, /* 00011100 */
+	0x3c, /* 00111100 */
+	0x6c, /* 01101100 */
+	0xcc, /* 11001100 */
+	0xfe, /* 11111110 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x1e, /* 00011110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 53 0x35 '5' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xfc, /* 11111100 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 54 0x36 '6' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xfc, /* 11111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 55 0x37 '7' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 56 0x38 '8' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 57 0x39 '9' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7e, /* 01111110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 58 0x3a ':' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 59 0x3b ';' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 60 0x3c '<' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 61 0x3d '=' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 62 0x3e '>' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 63 0x3f '?' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 64 0x40 '@' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xde, /* 11011110 */
+	0xde, /* 11011110 */
+	0xde, /* 11011110 */
+	0xdc, /* 11011100 */
+	0xc0, /* 11000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 65 0x41 'A' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 66 0x42 'B' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfc, /* 11111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xfc, /* 11111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 67 0x43 'C' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0xc2, /* 11000010 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc2, /* 11000010 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 68 0x44 'D' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 69 0x45 'E' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x66, /* 01100110 */
+	0x62, /* 01100010 */
+	0x68, /* 01101000 */
+	0x78, /* 01111000 */
+	0x68, /* 01101000 */
+	0x60, /* 01100000 */
+	0x62, /* 01100010 */
+	0x66, /* 01100110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 70 0x46 'F' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x66, /* 01100110 */
+	0x62, /* 01100010 */
+	0x68, /* 01101000 */
+	0x78, /* 01111000 */
+	0x68, /* 01101000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 71 0x47 'G' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0xc2, /* 11000010 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xde, /* 11011110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x66, /* 01100110 */
+	0x3a, /* 00111010 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 72 0x48 'H' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 73 0x49 'I' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 74 0x4a 'J' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1e, /* 00011110 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 75 0x4b 'K' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xe6, /* 11100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0x78, /* 01111000 */
+	0x78, /* 01111000 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 76 0x4c 'L' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf0, /* 11110000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x62, /* 01100010 */
+	0x66, /* 01100110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 77 0x4d 'M' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xee, /* 11101110 */
+	0xfe, /* 11111110 */
+	0xfe, /* 11111110 */
+	0xd6, /* 11010110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 78 0x4e 'N' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xe6, /* 11100110 */
+	0xf6, /* 11110110 */
+	0xfe, /* 11111110 */
+	0xde, /* 11011110 */
+	0xce, /* 11001110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 79 0x4f 'O' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 80 0x50 'P' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfc, /* 11111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 81 0x51 'Q' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xde, /* 11011110 */
+	0x7c, /* 01111100 */
+	0x0c, /* 00001100 */
+	0x0e, /* 00001110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 82 0x52 'R' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfc, /* 11111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 83 0x53 'S' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x60, /* 01100000 */
+	0x38, /* 00111000 */
+	0x0c, /* 00001100 */
+	0x06, /* 00000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 84 0x54 'T' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x5a, /* 01011010 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 85 0x55 'U' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 86 0x56 'V' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 87 0x57 'W' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xfe, /* 11111110 */
+	0xee, /* 11101110 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 88 0x58 'X' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x7c, /* 01111100 */
+	0x38, /* 00111000 */
+	0x38, /* 00111000 */
+	0x7c, /* 01111100 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 89 0x59 'Y' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 90 0x5a 'Z' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x86, /* 10000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc2, /* 11000010 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 91 0x5b '[' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 92 0x5c '\' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x80, /* 10000000 */
+	0xc0, /* 11000000 */
+	0xe0, /* 11100000 */
+	0x70, /* 01110000 */
+	0x38, /* 00111000 */
+	0x1c, /* 00011100 */
+	0x0e, /* 00001110 */
+	0x06, /* 00000110 */
+	0x02, /* 00000010 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 93 0x5d ']' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 94 0x5e '^' */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 95 0x5f '_' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 96 0x60 '`' */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 97 0x61 'a' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 98 0x62 'b' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x78, /* 01111000 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 99 0x63 'c' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 100 0x64 'd' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1c, /* 00011100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x3c, /* 00111100 */
+	0x6c, /* 01101100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 101 0x65 'e' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 102 0x66 'f' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1c, /* 00011100 */
+	0x36, /* 00110110 */
+	0x32, /* 00110010 */
+	0x30, /* 00110000 */
+	0x78, /* 01111000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 103 0x67 'g' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x7c, /* 01111100 */
+	0x0c, /* 00001100 */
+	0xcc, /* 11001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	/* 104 0x68 'h' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x6c, /* 01101100 */
+	0x76, /* 01110110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 105 0x69 'i' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 106 0x6a 'j' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+	0x0e, /* 00001110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+
+	/* 107 0x6b 'k' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xe0, /* 11100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0x78, /* 01111000 */
+	0x78, /* 01111000 */
+	0x6c, /* 01101100 */
+	0x66, /* 01100110 */
+	0xe6, /* 11100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 108 0x6c 'l' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 109 0x6d 'm' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xec, /* 11101100 */
+	0xfe, /* 11111110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 110 0x6e 'n' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 111 0x6f 'o' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 112 0x70 'p' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+
+	/* 113 0x71 'q' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x7c, /* 01111100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x1e, /* 00011110 */
+	0x00, /* 00000000 */
+
+	/* 114 0x72 'r' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x76, /* 01110110 */
+	0x66, /* 01100110 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 115 0x73 's' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0x60, /* 01100000 */
+	0x38, /* 00111000 */
+	0x0c, /* 00001100 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 116 0x74 't' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0xfc, /* 11111100 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x36, /* 00110110 */
+	0x1c, /* 00011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 117 0x75 'u' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 118 0x76 'v' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 119 0x77 'w' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xd6, /* 11010110 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 120 0x78 'x' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x38, /* 00111000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 121 0x79 'y' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7e, /* 01111110 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+
+	/* 122 0x7a 'z' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xcc, /* 11001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 123 0x7b '{' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x0e, /* 00001110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x70, /* 01110000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x0e, /* 00001110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 124 0x7c '|' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 125 0x7d '}' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x70, /* 01110000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x0e, /* 00001110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 126 0x7e '~' */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 127 0x7f '' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 128 0x80 '€' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0xc2, /* 11000010 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc2, /* 11000010 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 129 0x81 '' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 130 0x82 '‚' */
+	0x00, /* 00000000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 131 0x83 'ƒ' */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 132 0x84 '„' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 133 0x85 '…' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 134 0x86 '†' */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 135 0x87 '‡' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x18, /* 00011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 136 0x88 'ˆ' */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 137 0x89 '‰' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 138 0x8a 'Š' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 139 0x8b '‹' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 140 0x8c 'Œ' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 141 0x8d '' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 142 0x8e 'Ž' */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 143 0x8f '' */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 144 0x90 '' */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x66, /* 01100110 */
+	0x62, /* 01100010 */
+	0x68, /* 01101000 */
+	0x78, /* 01111000 */
+	0x68, /* 01101000 */
+	0x62, /* 01100010 */
+	0x66, /* 01100110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 145 0x91 '‘' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xec, /* 11101100 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x7e, /* 01111110 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0x6e, /* 01101110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 146 0x92 '’' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3e, /* 00111110 */
+	0x6c, /* 01101100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xfe, /* 11111110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xce, /* 11001110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 147 0x93 '“' */
+	0x00, /* 00000000 */
+	0x10, /* 00010000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 148 0x94 '”' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 149 0x95 '•' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 150 0x96 '–' */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x78, /* 01111000 */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 151 0x97 '—' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 152 0x98 '˜' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7e, /* 01111110 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x78, /* 01111000 */
+	0x00, /* 00000000 */
+
+	/* 153 0x99 '™' */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 154 0x9a 'š' */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 155 0x9b '›' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 156 0x9c 'œ' */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x64, /* 01100100 */
+	0x60, /* 01100000 */
+	0xf0, /* 11110000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xe6, /* 11100110 */
+	0xfc, /* 11111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 157 0x9d '' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 158 0x9e 'ž' */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xf8, /* 11111000 */
+	0xc4, /* 11000100 */
+	0xcc, /* 11001100 */
+	0xde, /* 11011110 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 159 0x9f 'Ÿ' */
+	0x00, /* 00000000 */
+	0x0e, /* 00001110 */
+	0x1b, /* 00011011 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xd8, /* 11011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 160 0xa0 ' ' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0x0c, /* 00001100 */
+	0x7c, /* 01111100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 161 0xa1 '¡' */
+	0x00, /* 00000000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 162 0xa2 '¢' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 163 0xa3 '£' */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x00, /* 00000000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 164 0xa4 '¤' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0xdc, /* 11011100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 165 0xa5 '¥' */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0xc6, /* 11000110 */
+	0xe6, /* 11100110 */
+	0xf6, /* 11110110 */
+	0xfe, /* 11111110 */
+	0xde, /* 11011110 */
+	0xce, /* 11001110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 166 0xa6 '¦' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x3e, /* 00111110 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 167 0xa7 '§' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 168 0xa8 '¨' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x7c, /* 01111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 169 0xa9 '©' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 170 0xaa 'ª' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 171 0xab '«' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0xe0, /* 11100000 */
+	0x62, /* 01100010 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xdc, /* 11011100 */
+	0x86, /* 10000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x3e, /* 00111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 172 0xac '¬' */
+	0x00, /* 00000000 */
+	0x60, /* 01100000 */
+	0xe0, /* 11100000 */
+	0x62, /* 01100010 */
+	0x66, /* 01100110 */
+	0x6c, /* 01101100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x66, /* 01100110 */
+	0xce, /* 11001110 */
+	0x9a, /* 10011010 */
+	0x3f, /* 00111111 */
+	0x06, /* 00000110 */
+	0x06, /* 00000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 173 0xad '­' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 174 0xae '®' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x36, /* 00110110 */
+	0x6c, /* 01101100 */
+	0xd8, /* 11011000 */
+	0x6c, /* 01101100 */
+	0x36, /* 00110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 175 0xaf '¯' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xd8, /* 11011000 */
+	0x6c, /* 01101100 */
+	0x36, /* 00110110 */
+	0x6c, /* 01101100 */
+	0xd8, /* 11011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 176 0xb0 '°' */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+	0x11, /* 00010001 */
+	0x44, /* 01000100 */
+
+	/* 177 0xb1 '±' */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+	0x55, /* 01010101 */
+	0xaa, /* 10101010 */
+
+	/* 178 0xb2 '²' */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+	0xdd, /* 11011101 */
+	0x77, /* 01110111 */
+
+	/* 179 0xb3 '³' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 180 0xb4 '´' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 181 0xb5 'µ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 182 0xb6 '¶' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf6, /* 11110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 183 0xb7 '·' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 184 0xb8 '¸' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 185 0xb9 '¹' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf6, /* 11110110 */
+	0x06, /* 00000110 */
+	0xf6, /* 11110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 186 0xba 'º' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 187 0xbb '»' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x06, /* 00000110 */
+	0xf6, /* 11110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 188 0xbc '¼' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf6, /* 11110110 */
+	0x06, /* 00000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 189 0xbd '½' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 190 0xbe '¾' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 191 0xbf '¿' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xf8, /* 11111000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 192 0xc0 'À' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 193 0xc1 'Á' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 194 0xc2 'Â' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 195 0xc3 'Ã' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 196 0xc4 'Ä' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 197 0xc5 'Å' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 198 0xc6 'Æ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 199 0xc7 'Ç' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x37, /* 00110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 200 0xc8 'È' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x37, /* 00110111 */
+	0x30, /* 00110000 */
+	0x3f, /* 00111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 201 0xc9 'É' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3f, /* 00111111 */
+	0x30, /* 00110000 */
+	0x37, /* 00110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 202 0xca 'Ê' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf7, /* 11110111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 203 0xcb 'Ë' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xf7, /* 11110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 204 0xcc 'Ì' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x37, /* 00110111 */
+	0x30, /* 00110000 */
+	0x37, /* 00110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 205 0xcd 'Í' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 206 0xce 'Î' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xf7, /* 11110111 */
+	0x00, /* 00000000 */
+	0xf7, /* 11110111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 207 0xcf 'Ï' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 208 0xd0 'Ð' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 209 0xd1 'Ñ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 210 0xd2 'Ò' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 211 0xd3 'Ó' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x3f, /* 00111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 212 0xd4 'Ô' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 213 0xd5 'Õ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 214 0xd6 'Ö' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x3f, /* 00111111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 215 0xd7 '×' */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0xff, /* 11111111 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+
+	/* 216 0xd8 'Ø' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0xff, /* 11111111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 217 0xd9 'Ù' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xf8, /* 11111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 218 0xda 'Ú' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1f, /* 00011111 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 219 0xdb 'Û' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 220 0xdc 'Ü' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+
+	/* 221 0xdd 'Ý' */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+	0xf0, /* 11110000 */
+
+	/* 222 0xde 'Þ' */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+	0x0f, /* 00001111 */
+
+	/* 223 0xdf 'ß' */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0xff, /* 11111111 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 224 0xe0 'à' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xdc, /* 11011100 */
+	0x76, /* 01110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 225 0xe1 'á' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x78, /* 01111000 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xcc, /* 11001100 */
+	0xd8, /* 11011000 */
+	0xcc, /* 11001100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xcc, /* 11001100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 226 0xe2 'â' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 227 0xe3 'ã' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 228 0xe4 'ä' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 229 0xe5 'å' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 230 0xe6 'æ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x7c, /* 01111100 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+
+	/* 231 0xe7 'ç' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 232 0xe8 'è' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 233 0xe9 'é' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xfe, /* 11111110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 234 0xea 'ê' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0xee, /* 11101110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 235 0xeb 'ë' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1e, /* 00011110 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x3e, /* 00111110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x66, /* 01100110 */
+	0x3c, /* 00111100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 236 0xec 'ì' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 237 0xed 'í' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x03, /* 00000011 */
+	0x06, /* 00000110 */
+	0x7e, /* 01111110 */
+	0xdb, /* 11011011 */
+	0xdb, /* 11011011 */
+	0xf3, /* 11110011 */
+	0x7e, /* 01111110 */
+	0x60, /* 01100000 */
+	0xc0, /* 11000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 238 0xee 'î' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x1c, /* 00011100 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x7c, /* 01111100 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x1c, /* 00011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 239 0xef 'ï' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7c, /* 01111100 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0xc6, /* 11000110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 240 0xf0 'ð' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0xfe, /* 11111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 241 0xf1 'ñ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x7e, /* 01111110 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 242 0xf2 'ò' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x06, /* 00000110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 243 0xf3 'ó' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x30, /* 00110000 */
+	0x60, /* 01100000 */
+	0x30, /* 00110000 */
+	0x18, /* 00011000 */
+	0x0c, /* 00001100 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 244 0xf4 'ô' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x0e, /* 00001110 */
+	0x1b, /* 00011011 */
+	0x1b, /* 00011011 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+
+	/* 245 0xf5 'õ' */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0xd8, /* 11011000 */
+	0x70, /* 01110000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 246 0xf6 'ö' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 247 0xf7 '÷' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0x76, /* 01110110 */
+	0xdc, /* 11011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 248 0xf8 'ø' */
+	0x00, /* 00000000 */
+	0x38, /* 00111000 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x38, /* 00111000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 249 0xf9 'ù' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 250 0xfa 'ú' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x18, /* 00011000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 251 0xfb 'û' */
+	0x00, /* 00000000 */
+	0x0f, /* 00001111 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0x0c, /* 00001100 */
+	0xec, /* 11101100 */
+	0x6c, /* 01101100 */
+	0x6c, /* 01101100 */
+	0x3c, /* 00111100 */
+	0x1c, /* 00011100 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 252 0xfc 'ü' */
+	0x00, /* 00000000 */
+	0x6c, /* 01101100 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x36, /* 00110110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 253 0xfd 'ý' */
+	0x00, /* 00000000 */
+	0x3c, /* 00111100 */
+	0x66, /* 01100110 */
+	0x0c, /* 00001100 */
+	0x18, /* 00011000 */
+	0x32, /* 00110010 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 254 0xfe 'þ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x7e, /* 01111110 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+	/* 255 0xff 'ÿ' */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+	0x00, /* 00000000 */
+
+};
diff --git a/vl.c b/vl.c
new file mode 100644
index 0000000..a9c1940
--- /dev/null
+++ b/vl.c
@@ -0,0 +1,9803 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/* the following is needed on Linux to define ptsname() in stdlib.h */
+#if defined(__linux__)
+#define _GNU_SOURCE 1
+#endif
+
+#include "qemu-common.h"
+#include "hw/hw.h"
+#include "hw/boards.h"
+#include "hw/usb.h"
+#include "hw/pcmcia.h"
+#include "hw/pc.h"
+#include "hw/audiodev.h"
+#include "hw/isa.h"
+#include "hw/baum.h"
+#include "net.h"
+#include "console.h"
+#include "sysemu.h"
+#include "gdbstub.h"
+#include "qemu-timer.h"
+#include "qemu-char.h"
+#include "block.h"
+#include "audio/audio.h"
+
+#include "qemu_file.h"
+#include "android/android.h"
+#include "charpipe.h"
+#include "shaper.h"
+#include "modem_driver.h"
+#include "android/gps.h"
+#include "android/qemud.h"
+#include "android/hw-kmsg.h"
+#include "tcpdump.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <zlib.h>
+
+#ifndef _WIN32
+#include <sys/times.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <dirent.h>
+#include <netdb.h>
+#include <sys/select.h>
+#include <arpa/inet.h>
+#ifdef _BSD
+#include <sys/stat.h>
+#if !defined(__APPLE__) && !defined(__OpenBSD__)
+#include <libutil.h>
+#endif
+#ifdef __OpenBSD__
+#include <net/if.h>
+#endif
+#elif defined (__GLIBC__) && defined (__FreeBSD_kernel__)
+#include <freebsd/stdlib.h>
+#else
+#ifndef __sun__
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <pty.h>
+#include <malloc.h>
+#include <linux/rtc.h>
+
+/* For the benefit of older linux systems which don't supply it,
+   we use a local copy of hpet.h. */
+/* #include <linux/hpet.h> */
+#include "hpet.h"
+
+#include <linux/ppdev.h>
+#include <linux/parport.h>
+#else
+#include <sys/stat.h>
+#include <sys/ethernet.h>
+#include <sys/sockio.h>
+#include <netinet/arp.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h> // must come after ip.h
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <net/if.h>
+#include <syslog.h>
+#include <stropts.h>
+#endif
+#endif
+#endif
+
+#include "qemu_socket.h"
+
+#if defined(CONFIG_SLIRP)
+#include "libslirp.h"
+#endif
+
+#if defined(__OpenBSD__)
+#include <util.h>
+#endif
+
+#if defined(CONFIG_VDE)
+#include <libvdeplug.h>
+#endif
+
+#ifdef _WIN32
+#include <malloc.h>
+#include <sys/timeb.h>
+#include <mmsystem.h>
+#define getopt_long_only getopt_long
+#define memalign(align, size) malloc(size)
+#endif
+
+
+#ifdef CONFIG_COCOA
+#undef main
+#define main qemu_main
+#endif /* CONFIG_COCOA */
+
+#ifdef CONFIG_SKINS
+#undef main
+#define main qemu_main
+#endif
+
+#include "disas.h"
+
+#include "exec-all.h"
+
+#ifdef CONFIG_TRACE
+#include "trace.h"
+#include "dcache.h"
+#endif
+
+#ifdef CONFIG_NAND
+#include "hw/goldfish_nand.h"
+#endif
+
+#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
+#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
+#ifdef __sun__
+#define SMBD_COMMAND "/usr/sfw/sbin/smbd"
+#else
+#define SMBD_COMMAND "/usr/sbin/smbd"
+#endif
+
+//#define DEBUG_UNUSED_IOPORT
+//#define DEBUG_IOPORT
+
+#ifdef TARGET_PPC
+#define DEFAULT_RAM_SIZE 144
+#else
+#define DEFAULT_RAM_SIZE 128
+#endif
+
+/* Max number of USB devices that can be specified on the commandline.  */
+#define MAX_USB_CMDLINE 8
+
+/* XXX: use a two level table to limit memory usage */
+#define MAX_IOPORTS 65536
+
+const char *bios_dir = CONFIG_QEMU_SHAREDIR;
+const char *bios_name = NULL;
+void *ioport_opaque[MAX_IOPORTS];
+IOPortReadFunc *ioport_read_table[3][MAX_IOPORTS];
+IOPortWriteFunc *ioport_write_table[3][MAX_IOPORTS];
+/* Note: drives_table[MAX_DRIVES] is a dummy block driver if none available
+   to store the VM snapshots */
+DriveInfo drives_table[MAX_DRIVES+1];
+int nb_drives;
+/* point to the block driver where the snapshots are managed */
+BlockDriverState *bs_snapshots;
+int vga_ram_size;
+static DisplayState display_state;
+int nographic;
+int curses;
+const char* keyboard_layout = NULL;
+int64_t ticks_per_sec;
+ram_addr_t ram_size;
+int pit_min_timer_count = 0;
+int nb_nics;
+NICInfo nd_table[MAX_NICS];
+int vm_running;
+static int rtc_utc = 1;
+static int rtc_date_offset = -1; /* -1 means no change */
+int cirrus_vga_enabled = 1;
+int vmsvga_enabled = 0;
+#ifdef TARGET_SPARC
+int graphic_width = 1024;
+int graphic_height = 768;
+int graphic_depth = 8;
+#else
+int graphic_width = 800;
+int graphic_height = 600;
+int graphic_depth = 15;
+#endif
+int full_screen = 0;
+int no_frame = 0;
+int no_quit = 0;
+CharDriverState *serial_hds[MAX_SERIAL_PORTS];
+CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
+#ifdef TARGET_I386
+int win2k_install_hack = 0;
+#endif
+int usb_enabled = 0;
+static VLANState *first_vlan;
+int smp_cpus = 1;
+const char *vnc_display;
+#if defined(TARGET_SPARC)
+#define MAX_CPUS 16
+#elif defined(TARGET_I386)
+#define MAX_CPUS 255
+#else
+#define MAX_CPUS 1
+#endif
+int acpi_enabled = 1;
+int fd_bootchk = 1;
+int no_reboot = 0;
+int no_shutdown = 0;
+int cursor_hide = 1;
+int graphic_rotate = 0;
+int daemonize = 0;
+const char *option_rom[MAX_OPTION_ROMS];
+int nb_option_roms;
+int semihosting_enabled = 0;
+int autostart = 1;
+#ifdef TARGET_ARM
+int old_param = 0;
+#endif
+const char *qemu_name;
+int alt_grab = 0;
+#ifdef TARGET_SPARC
+unsigned int nb_prom_envs = 0;
+const char *prom_envs[MAX_PROM_ENVS];
+#endif
+int nb_drives_opt;
+struct drive_opt {
+    const char *file;
+    char opt[1024];
+} drives_opt[MAX_DRIVES];
+
+static CPUState *cur_cpu;
+static CPUState *next_cpu;
+static int event_pending = 1;
+/* Conversion factor from emulated instructions to virtual clock ticks.  */
+static int icount_time_shift;
+/* Arbitrarily pick 1MIPS as the minimum allowable speed.  */
+#define MAX_ICOUNT_SHIFT 10
+/* Compensate for varying guest execution speed.  */
+static int64_t qemu_icount_bias;
+QEMUTimer *icount_rt_timer;
+QEMUTimer *icount_vm_timer;
+
+
+extern int   qemu_cpu_delay;
+extern int   android_audio_enabled;
+extern char* audio_input_source;
+
+extern void  dprint( const char* format, ... );
+
+#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
+
+/***********************************************************/
+/* x86 ISA bus support */
+
+target_phys_addr_t isa_mem_base = 0;
+PicState2 *isa_pic;
+
+static IOPortReadFunc default_ioport_readb, default_ioport_readw, default_ioport_readl;
+static IOPortWriteFunc default_ioport_writeb, default_ioport_writew, default_ioport_writel;
+
+static uint32_t ioport_read(int index, uint32_t address)
+{
+    static IOPortReadFunc *default_func[3] = {
+        default_ioport_readb,
+        default_ioport_readw,
+        default_ioport_readl
+    };
+    IOPortReadFunc *func = ioport_read_table[index][address];
+    if (!func)
+        func = default_func[index];
+    return func(ioport_opaque[address], address);
+}
+
+static void ioport_write(int index, uint32_t address, uint32_t data)
+{
+    static IOPortWriteFunc *default_func[3] = {
+        default_ioport_writeb,
+        default_ioport_writew,
+        default_ioport_writel
+    };
+    IOPortWriteFunc *func = ioport_write_table[index][address];
+    if (!func)
+        func = default_func[index];
+    func(ioport_opaque[address], address, data);
+}
+
+static uint32_t default_ioport_readb(void *opaque, uint32_t address)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+    fprintf(stderr, "unused inb: port=0x%04x\n", address);
+#endif
+    return 0xff;
+}
+
+static void default_ioport_writeb(void *opaque, uint32_t address, uint32_t data)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+    fprintf(stderr, "unused outb: port=0x%04x data=0x%02x\n", address, data);
+#endif
+}
+
+/* default is to make two byte accesses */
+static uint32_t default_ioport_readw(void *opaque, uint32_t address)
+{
+    uint32_t data;
+    data = ioport_read(0, address);
+    address = (address + 1) & (MAX_IOPORTS - 1);
+    data |= ioport_read(0, address) << 8;
+    return data;
+}
+
+static void default_ioport_writew(void *opaque, uint32_t address, uint32_t data)
+{
+    ioport_write(0, address, data & 0xff);
+    address = (address + 1) & (MAX_IOPORTS - 1);
+    ioport_write(0, address, (data >> 8) & 0xff);
+}
+
+static uint32_t default_ioport_readl(void *opaque, uint32_t address)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+    fprintf(stderr, "unused inl: port=0x%04x\n", address);
+#endif
+    return 0xffffffff;
+}
+
+static void default_ioport_writel(void *opaque, uint32_t address, uint32_t data)
+{
+#ifdef DEBUG_UNUSED_IOPORT
+    fprintf(stderr, "unused outl: port=0x%04x data=0x%02x\n", address, data);
+#endif
+}
+
+/* size is the word size in byte */
+int register_ioport_read(int start, int length, int size,
+                         IOPortReadFunc *func, void *opaque)
+{
+    int i, bsize;
+
+    if (size == 1) {
+        bsize = 0;
+    } else if (size == 2) {
+        bsize = 1;
+    } else if (size == 4) {
+        bsize = 2;
+    } else {
+        hw_error("register_ioport_read: invalid size");
+        return -1;
+    }
+    for(i = start; i < start + length; i += size) {
+        ioport_read_table[bsize][i] = func;
+        if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
+            hw_error("register_ioport_read: invalid opaque");
+        ioport_opaque[i] = opaque;
+    }
+    return 0;
+}
+
+/* size is the word size in byte */
+int register_ioport_write(int start, int length, int size,
+                          IOPortWriteFunc *func, void *opaque)
+{
+    int i, bsize;
+
+    if (size == 1) {
+        bsize = 0;
+    } else if (size == 2) {
+        bsize = 1;
+    } else if (size == 4) {
+        bsize = 2;
+    } else {
+        hw_error("register_ioport_write: invalid size");
+        return -1;
+    }
+    for(i = start; i < start + length; i += size) {
+        ioport_write_table[bsize][i] = func;
+        if (ioport_opaque[i] != NULL && ioport_opaque[i] != opaque)
+            hw_error("register_ioport_write: invalid opaque");
+        ioport_opaque[i] = opaque;
+    }
+    return 0;
+}
+
+void isa_unassign_ioport(int start, int length)
+{
+    int i;
+
+    for(i = start; i < start + length; i++) {
+        ioport_read_table[0][i] = default_ioport_readb;
+        ioport_read_table[1][i] = default_ioport_readw;
+        ioport_read_table[2][i] = default_ioport_readl;
+
+        ioport_write_table[0][i] = default_ioport_writeb;
+        ioport_write_table[1][i] = default_ioport_writew;
+        ioport_write_table[2][i] = default_ioport_writel;
+    }
+}
+
+/***********************************************************/
+
+void cpu_outb(CPUState *env, int addr, int val)
+{
+#ifdef DEBUG_IOPORT
+    if (loglevel & CPU_LOG_IOPORT)
+        fprintf(logfile, "outb: %04x %02x\n", addr, val);
+#endif
+    ioport_write(0, addr, val);
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+void cpu_outw(CPUState *env, int addr, int val)
+{
+#ifdef DEBUG_IOPORT
+    if (loglevel & CPU_LOG_IOPORT)
+        fprintf(logfile, "outw: %04x %04x\n", addr, val);
+#endif
+    ioport_write(1, addr, val);
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+void cpu_outl(CPUState *env, int addr, int val)
+{
+#ifdef DEBUG_IOPORT
+    if (loglevel & CPU_LOG_IOPORT)
+        fprintf(logfile, "outl: %04x %08x\n", addr, val);
+#endif
+    ioport_write(2, addr, val);
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
+}
+
+int cpu_inb(CPUState *env, int addr)
+{
+    int val;
+    val = ioport_read(0, addr);
+#ifdef DEBUG_IOPORT
+    if (loglevel & CPU_LOG_IOPORT)
+        fprintf(logfile, "inb : %04x %02x\n", addr, val);
+#endif
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
+    return val;
+}
+
+int cpu_inw(CPUState *env, int addr)
+{
+    int val;
+    val = ioport_read(1, addr);
+#ifdef DEBUG_IOPORT
+    if (loglevel & CPU_LOG_IOPORT)
+        fprintf(logfile, "inw : %04x %04x\n", addr, val);
+#endif
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
+    return val;
+}
+
+int cpu_inl(CPUState *env, int addr)
+{
+    int val;
+    val = ioport_read(2, addr);
+#ifdef DEBUG_IOPORT
+    if (loglevel & CPU_LOG_IOPORT)
+        fprintf(logfile, "inl : %04x %08x\n", addr, val);
+#endif
+#ifdef USE_KQEMU
+    if (env)
+        env->last_io_time = cpu_get_time_fast();
+#endif
+    return val;
+}
+
+/***********************************************************/
+void hw_error(const char *fmt, ...)
+{
+    va_list ap;
+    CPUState *env;
+
+    va_start(ap, fmt);
+    fprintf(stderr, "qemu: hardware error: ");
+    vfprintf(stderr, fmt, ap);
+    fprintf(stderr, "\n");
+    for(env = first_cpu; env != NULL; env = env->next_cpu) {
+        fprintf(stderr, "CPU #%d:\n", env->cpu_index);
+#ifdef TARGET_I386
+        cpu_dump_state(env, stderr, fprintf, X86_DUMP_FPU);
+#else
+        cpu_dump_state(env, stderr, fprintf, 0);
+#endif
+    }
+    va_end(ap);
+    abort();
+}
+
+/***********************************************************/
+/* keyboard/mouse */
+
+static QEMUPutKBDEvent*  qemu_put_kbd_event;
+static void*             qemu_put_kbd_event_opaque;
+
+static QEMUPutKBDEventN*  qemu_put_kbd_event_n;
+static void*              qemu_put_kbd_event_n_opaque;
+
+
+static QEMUPutGenericEvent*  qemu_put_generic_event;
+static void*                 qemu_put_generic_event_opaque;
+
+static QEMUPutMouseEntry *qemu_put_mouse_event_head;
+static QEMUPutMouseEntry *qemu_put_mouse_event_current;
+
+void qemu_add_kbd_event_handler(QEMUPutKBDEvent *func, void *opaque)
+{
+    qemu_put_kbd_event_opaque = opaque;
+    qemu_put_kbd_event = func;
+}
+
+void qemu_add_kbd_event_n_handler(QEMUPutKBDEventN *func, void *opaque)
+{
+    qemu_put_kbd_event_n_opaque = opaque;
+    qemu_put_kbd_event_n = func;
+}
+
+#if 0
+void qemu_add_mouse_event_handler(QEMUPutMouseEvent *func, void *opaque, int absolute)
+{
+    qemu_put_mouse_event_opaque = opaque;
+    qemu_put_mouse_event = func;
+    qemu_put_mouse_event_absolute = absolute;
+}
+#else
+QEMUPutMouseEntry *qemu_add_mouse_event_handler(QEMUPutMouseEvent *func,
+                                                void *opaque, int absolute,
+                                                const char *name)
+{
+    QEMUPutMouseEntry *s, *cursor;
+
+    s = qemu_mallocz(sizeof(QEMUPutMouseEntry));
+    if (!s)
+        return NULL;
+
+    s->qemu_put_mouse_event = func;
+    s->qemu_put_mouse_event_opaque = opaque;
+    s->qemu_put_mouse_event_absolute = absolute;
+    s->qemu_put_mouse_event_name = qemu_strdup(name);
+    s->next = NULL;
+
+    if (!qemu_put_mouse_event_head) {
+        qemu_put_mouse_event_head = qemu_put_mouse_event_current = s;
+        return s;
+    }
+
+    cursor = qemu_put_mouse_event_head;
+    while (cursor->next != NULL)
+        cursor = cursor->next;
+
+    cursor->next = s;
+    qemu_put_mouse_event_current = s;
+
+    return s;
+}
+
+void qemu_remove_mouse_event_handler(QEMUPutMouseEntry *entry)
+{
+    QEMUPutMouseEntry *prev = NULL, *cursor;
+
+    if (!qemu_put_mouse_event_head || entry == NULL)
+        return;
+
+    cursor = qemu_put_mouse_event_head;
+    while (cursor != NULL && cursor != entry) {
+        prev = cursor;
+        cursor = cursor->next;
+    }
+
+    if (cursor == NULL) // does not exist or list empty
+        return;
+    else if (prev == NULL) { // entry is head
+        qemu_put_mouse_event_head = cursor->next;
+        if (qemu_put_mouse_event_current == entry)
+            qemu_put_mouse_event_current = cursor->next;
+        qemu_free(entry->qemu_put_mouse_event_name);
+        qemu_free(entry);
+        return;
+    }
+
+    prev->next = entry->next;
+
+    if (qemu_put_mouse_event_current == entry)
+        qemu_put_mouse_event_current = prev;
+
+    qemu_free(entry->qemu_put_mouse_event_name);
+    qemu_free(entry);
+}
+#endif
+
+void qemu_add_generic_event_handler(QEMUPutGenericEvent *func, void*  opaque)
+{
+    qemu_put_generic_event = func;
+    qemu_put_generic_event_opaque = opaque;
+}
+
+void kbd_put_keycode(int keycode)
+{
+    if (qemu_put_kbd_event) {
+        qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycode);
+    }
+}
+
+void kbd_put_keycodes(int*  keycodes, int  count)
+{
+    if (qemu_put_kbd_event_n)
+    {
+        qemu_put_kbd_event_n(qemu_put_kbd_event_n_opaque, keycodes, count);
+    }
+    else if (qemu_put_kbd_event)
+    {
+        int  nn;
+
+        for (nn = 0; nn < count; nn++)
+            qemu_put_kbd_event(qemu_put_kbd_event_opaque, keycodes[nn]);
+    }
+}
+
+
+void  kbd_generic_event(int  type, int code, int  value)
+{
+    if (qemu_put_generic_event)
+        qemu_put_generic_event(qemu_put_generic_event_opaque, type, code, value);
+}
+
+
+void kbd_mouse_event(int dx, int dy, int dz, int buttons_state)
+{
+    QEMUPutMouseEvent *mouse_event;
+    void *mouse_event_opaque;
+    int width;
+
+    if (!qemu_put_mouse_event_current) {
+        return;
+    }
+
+    mouse_event =
+        qemu_put_mouse_event_current->qemu_put_mouse_event;
+    mouse_event_opaque =
+        qemu_put_mouse_event_current->qemu_put_mouse_event_opaque;
+
+    if (mouse_event) {
+        if (graphic_rotate) {
+            if (qemu_put_mouse_event_current->qemu_put_mouse_event_absolute)
+                width = 0x7fff;
+            else
+                width = graphic_width - 1;
+            mouse_event(mouse_event_opaque,
+                                 width - dy, dx, dz, buttons_state);
+        } else
+            mouse_event(mouse_event_opaque,
+                                 dx, dy, dz, buttons_state);
+    }
+}
+
+int kbd_mouse_is_absolute(void)
+{
+    if (!qemu_put_mouse_event_current)
+        return 0;
+
+    return qemu_put_mouse_event_current->qemu_put_mouse_event_absolute;
+}
+
+void do_info_mice(void)
+{
+    QEMUPutMouseEntry *cursor;
+    int index = 0;
+
+    if (!qemu_put_mouse_event_head) {
+        term_printf("No mouse devices connected\n");
+        return;
+    }
+
+    term_printf("Mouse devices available:\n");
+    cursor = qemu_put_mouse_event_head;
+    while (cursor != NULL) {
+        term_printf("%c Mouse #%d: %s\n",
+                    (cursor == qemu_put_mouse_event_current ? '*' : ' '),
+                    index, cursor->qemu_put_mouse_event_name);
+        index++;
+        cursor = cursor->next;
+    }
+}
+
+void do_mouse_set(int index)
+{
+    QEMUPutMouseEntry *cursor;
+    int i = 0;
+
+    if (!qemu_put_mouse_event_head) {
+        term_printf("No mouse devices connected\n");
+        return;
+    }
+
+    cursor = qemu_put_mouse_event_head;
+    while (cursor != NULL && index != i) {
+        i++;
+        cursor = cursor->next;
+    }
+
+    if (cursor != NULL)
+        qemu_put_mouse_event_current = cursor;
+    else
+        term_printf("Mouse at given index not found\n");
+}
+
+/* compute with 96 bit intermediate result: (a*b)/c */
+uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
+{
+    union {
+        uint64_t ll;
+        struct {
+#ifdef WORDS_BIGENDIAN
+            uint32_t high, low;
+#else
+            uint32_t low, high;
+#endif
+        } l;
+    } u, res;
+    uint64_t rl, rh;
+
+    u.ll = a;
+    rl = (uint64_t)u.l.low * (uint64_t)b;
+    rh = (uint64_t)u.l.high * (uint64_t)b;
+    rh += (rl >> 32);
+    res.l.high = rh / c;
+    res.l.low = (((rh % c) << 32) + (rl & 0xffffffff)) / c;
+    return res.ll;
+}
+
+/***********************************************************/
+/* real time host monotonic timer */
+
+#define QEMU_TIMER_BASE 1000000000LL
+
+#ifdef WIN32
+
+static int64_t clock_freq;
+
+static void init_get_clock(void)
+{
+    LARGE_INTEGER freq;
+    int ret;
+    ret = QueryPerformanceFrequency(&freq);
+    if (ret == 0) {
+        fprintf(stderr, "Could not calibrate ticks\n");
+        exit(1);
+    }
+    clock_freq = freq.QuadPart;
+}
+
+static int64_t get_clock(void)
+{
+    LARGE_INTEGER ti;
+    QueryPerformanceCounter(&ti);
+    return muldiv64(ti.QuadPart, QEMU_TIMER_BASE, clock_freq);
+}
+
+#else
+
+static int use_rt_clock;
+
+static void init_get_clock(void)
+{
+    use_rt_clock = 0;
+#if defined(__linux__)
+    {
+        struct timespec ts;
+        if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+            use_rt_clock = 1;
+        }
+    }
+#endif
+}
+
+static int64_t get_clock(void)
+{
+#if defined(__linux__)
+    if (use_rt_clock) {
+        struct timespec ts;
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+        return ts.tv_sec * 1000000000LL + ts.tv_nsec;
+    } else
+#endif
+    {
+        /* XXX: using gettimeofday leads to problems if the date
+           changes, so it should be avoided. */
+        struct timeval tv;
+        gettimeofday(&tv, NULL);
+        return tv.tv_sec * 1000000000LL + (tv.tv_usec * 1000);
+    }
+}
+#endif
+
+/* Return the virtual CPU time, based on the instruction counter.  */
+static int64_t cpu_get_icount(void)
+{
+    int64_t icount;
+    CPUState *env = cpu_single_env;;
+    icount = qemu_icount;
+    if (env) {
+        if (!can_do_io(env))
+            fprintf(stderr, "Bad clock read\n");
+        icount -= (env->icount_decr.u16.low + env->icount_extra);
+    }
+    return qemu_icount_bias + (icount << icount_time_shift);
+}
+
+/***********************************************************/
+/* guest cycle counter */
+
+static int64_t cpu_ticks_prev;
+static int64_t cpu_ticks_offset;
+static int64_t cpu_clock_offset;
+static int cpu_ticks_enabled;
+
+/* return the host CPU cycle counter and handle stop/restart */
+int64_t cpu_get_ticks(void)
+{
+    if (use_icount) {
+        return cpu_get_icount();
+    }
+    if (!cpu_ticks_enabled) {
+        return cpu_ticks_offset;
+    } else {
+        int64_t ticks;
+        ticks = cpu_get_real_ticks();
+        if (cpu_ticks_prev > ticks) {
+            /* Note: non increasing ticks may happen if the host uses
+               software suspend */
+            cpu_ticks_offset += cpu_ticks_prev - ticks;
+        }
+        cpu_ticks_prev = ticks;
+        return ticks + cpu_ticks_offset;
+    }
+}
+
+/* return the host CPU monotonic timer and handle stop/restart */
+static int64_t cpu_get_clock(void)
+{
+    int64_t ti;
+    if (!cpu_ticks_enabled) {
+        return cpu_clock_offset;
+    } else {
+        ti = get_clock();
+        return ti + cpu_clock_offset;
+    }
+}
+
+/* enable cpu_get_ticks() */
+void cpu_enable_ticks(void)
+{
+    if (!cpu_ticks_enabled) {
+        cpu_ticks_offset -= cpu_get_real_ticks();
+        cpu_clock_offset -= get_clock();
+        cpu_ticks_enabled = 1;
+    }
+}
+
+/* disable cpu_get_ticks() : the clock is stopped. You must not call
+   cpu_get_ticks() after that.  */
+void cpu_disable_ticks(void)
+{
+    if (cpu_ticks_enabled) {
+        cpu_ticks_offset = cpu_get_ticks();
+        cpu_clock_offset = cpu_get_clock();
+        cpu_ticks_enabled = 0;
+    }
+}
+
+/***********************************************************/
+/* timers */
+
+#define QEMU_TIMER_REALTIME 0
+#define QEMU_TIMER_VIRTUAL  1
+
+struct QEMUClock {
+    int type;
+    /* XXX: add frequency */
+};
+
+struct QEMUTimer {
+    QEMUClock *clock;
+    int64_t expire_time;
+    QEMUTimerCB *cb;
+    void *opaque;
+    struct QEMUTimer *next;
+};
+
+struct qemu_alarm_timer {
+    char const *name;
+    unsigned int flags;
+
+    int (*start)(struct qemu_alarm_timer *t);
+    void (*stop)(struct qemu_alarm_timer *t);
+    void (*rearm)(struct qemu_alarm_timer *t);
+    void *priv;
+};
+
+#define ALARM_FLAG_DYNTICKS  0x1
+#define ALARM_FLAG_EXPIRED   0x2
+
+static inline int alarm_has_dynticks(struct qemu_alarm_timer *t)
+{
+    return t->flags & ALARM_FLAG_DYNTICKS;
+}
+
+static void qemu_rearm_alarm_timer(struct qemu_alarm_timer *t)
+{
+    if (!alarm_has_dynticks(t))
+        return;
+
+    t->rearm(t);
+}
+
+/* TODO: MIN_TIMER_REARM_US should be optimized */
+#define MIN_TIMER_REARM_US 250
+
+static struct qemu_alarm_timer *alarm_timer;
+
+#ifdef _WIN32
+
+struct qemu_alarm_win32 {
+    MMRESULT timerId;
+    HANDLE host_alarm;
+    unsigned int period;
+} alarm_win32_data = {0, NULL, -1};
+
+static int win32_start_timer(struct qemu_alarm_timer *t);
+static void win32_stop_timer(struct qemu_alarm_timer *t);
+static void win32_rearm_timer(struct qemu_alarm_timer *t);
+
+#else
+
+static int unix_start_timer(struct qemu_alarm_timer *t);
+static void unix_stop_timer(struct qemu_alarm_timer *t);
+
+#ifdef __linux__
+
+static int dynticks_start_timer(struct qemu_alarm_timer *t);
+static void dynticks_stop_timer(struct qemu_alarm_timer *t);
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t);
+
+static int hpet_start_timer(struct qemu_alarm_timer *t);
+static void hpet_stop_timer(struct qemu_alarm_timer *t);
+
+static int rtc_start_timer(struct qemu_alarm_timer *t);
+static void rtc_stop_timer(struct qemu_alarm_timer *t);
+
+#endif /* __linux__ */
+
+#endif /* _WIN32 */
+
+/* Correlation between real and virtual time is always going to be
+   fairly approximate, so ignore small variation.
+   When the guest is idle real and virtual time will be aligned in
+   the IO wait loop.  */
+#define ICOUNT_WOBBLE (QEMU_TIMER_BASE / 10)
+
+static void icount_adjust(void)
+{
+    int64_t cur_time;
+    int64_t cur_icount;
+    int64_t delta;
+    static int64_t last_delta;
+    /* If the VM is not running, then do nothing.  */
+    if (!vm_running)
+        return;
+
+    cur_time = cpu_get_clock();
+    cur_icount = qemu_get_clock(vm_clock);
+    delta = cur_icount - cur_time;
+    /* FIXME: This is a very crude algorithm, somewhat prone to oscillation.  */
+    if (delta > 0
+        && last_delta + ICOUNT_WOBBLE < delta * 2
+        && icount_time_shift > 0) {
+        /* The guest is getting too far ahead.  Slow time down.  */
+        icount_time_shift--;
+    }
+    if (delta < 0
+        && last_delta - ICOUNT_WOBBLE > delta * 2
+        && icount_time_shift < MAX_ICOUNT_SHIFT) {
+        /* The guest is getting too far behind.  Speed time up.  */
+        icount_time_shift++;
+    }
+    last_delta = delta;
+    qemu_icount_bias = cur_icount - (qemu_icount << icount_time_shift);
+}
+
+static void icount_adjust_rt(void * opaque)
+{
+    qemu_mod_timer(icount_rt_timer,
+                   qemu_get_clock(rt_clock) + 1000);
+    icount_adjust();
+}
+
+static void icount_adjust_vm(void * opaque)
+{
+    qemu_mod_timer(icount_vm_timer,
+                   qemu_get_clock(vm_clock) + QEMU_TIMER_BASE / 10);
+    icount_adjust();
+}
+
+static void init_icount_adjust(void)
+{
+    /* Have both realtime and virtual time triggers for speed adjustment.
+       The realtime trigger catches emulated time passing too slowly,
+       the virtual time trigger catches emulated time passing too fast.
+       Realtime triggers occur even when idle, so use them less frequently
+       than VM triggers.  */
+    icount_rt_timer = qemu_new_timer(rt_clock, icount_adjust_rt, NULL);
+    qemu_mod_timer(icount_rt_timer,
+                   qemu_get_clock(rt_clock) + 1000);
+    icount_vm_timer = qemu_new_timer(vm_clock, icount_adjust_vm, NULL);
+    qemu_mod_timer(icount_vm_timer,
+                   qemu_get_clock(vm_clock) + QEMU_TIMER_BASE / 10);
+}
+
+static struct qemu_alarm_timer alarm_timers[] = {
+#ifndef _WIN32
+#ifdef __linux__
+    {"dynticks", ALARM_FLAG_DYNTICKS, dynticks_start_timer,
+     dynticks_stop_timer, dynticks_rearm_timer, NULL},
+    /* HPET - if available - is preferred */
+    {"hpet", 0, hpet_start_timer, hpet_stop_timer, NULL, NULL},
+    /* ...otherwise try RTC */
+    {"rtc", 0, rtc_start_timer, rtc_stop_timer, NULL, NULL},
+#endif
+    {"unix", 0, unix_start_timer, unix_stop_timer, NULL, NULL},
+#else
+    {"dynticks", ALARM_FLAG_DYNTICKS, win32_start_timer,
+     win32_stop_timer, win32_rearm_timer, &alarm_win32_data},
+    {"win32", 0, win32_start_timer,
+     win32_stop_timer, NULL, &alarm_win32_data},
+#endif
+    {NULL, 0, NULL, NULL, NULL, NULL}
+};
+
+static void show_available_alarms(void)
+{
+    int i;
+
+    printf("Available alarm timers, in order of precedence:\n");
+    for (i = 0; alarm_timers[i].name; i++)
+        printf("%s\n", alarm_timers[i].name);
+}
+
+static void configure_alarms(char const *opt)
+{
+    int i;
+    int cur = 0;
+    int count = (sizeof(alarm_timers) / sizeof(*alarm_timers)) - 1;
+    char *arg;
+    char *name;
+    struct qemu_alarm_timer tmp;
+
+    if (!strcmp(opt, "?")) {
+        show_available_alarms();
+        exit(0);
+    }
+
+    arg = strdup(opt);
+
+    /* Reorder the array */
+    name = strtok(arg, ",");
+    while (name) {
+        for (i = 0; i < count && alarm_timers[i].name; i++) {
+            if (!strcmp(alarm_timers[i].name, name))
+                break;
+        }
+
+        if (i == count) {
+            fprintf(stderr, "Unknown clock %s\n", name);
+            goto next;
+        }
+
+        if (i < cur)
+            /* Ignore */
+            goto next;
+
+	/* Swap */
+        tmp = alarm_timers[i];
+        alarm_timers[i] = alarm_timers[cur];
+        alarm_timers[cur] = tmp;
+
+        cur++;
+next:
+        name = strtok(NULL, ",");
+    }
+
+    free(arg);
+
+    if (cur) {
+        /* Disable remaining timers */
+        for (i = cur; i < count; i++)
+            alarm_timers[i].name = NULL;
+    } else {
+        show_available_alarms();
+        exit(1);
+    }
+}
+
+QEMUClock *rt_clock;
+QEMUClock *vm_clock;
+
+static QEMUTimer *active_timers[2];
+
+static QEMUClock *qemu_new_clock(int type)
+{
+    QEMUClock *clock;
+    clock = qemu_mallocz(sizeof(QEMUClock));
+    if (!clock)
+        return NULL;
+    clock->type = type;
+    return clock;
+}
+
+QEMUTimer *qemu_new_timer(QEMUClock *clock, QEMUTimerCB *cb, void *opaque)
+{
+    QEMUTimer *ts;
+
+    ts = qemu_mallocz(sizeof(QEMUTimer));
+    ts->clock = clock;
+    ts->cb = cb;
+    ts->opaque = opaque;
+    return ts;
+}
+
+void qemu_free_timer(QEMUTimer *ts)
+{
+    qemu_free(ts);
+}
+
+/* stop a timer, but do not dealloc it */
+void qemu_del_timer(QEMUTimer *ts)
+{
+    QEMUTimer **pt, *t;
+
+    /* NOTE: this code must be signal safe because
+       qemu_timer_expired() can be called from a signal. */
+    pt = &active_timers[ts->clock->type];
+    for(;;) {
+        t = *pt;
+        if (!t)
+            break;
+        if (t == ts) {
+            *pt = t->next;
+            break;
+        }
+        pt = &t->next;
+    }
+}
+
+/* modify the current timer so that it will be fired when current_time
+   >= expire_time. The corresponding callback will be called. */
+void qemu_mod_timer(QEMUTimer *ts, int64_t expire_time)
+{
+    QEMUTimer **pt, *t;
+
+    qemu_del_timer(ts);
+
+    /* add the timer in the sorted list */
+    /* NOTE: this code must be signal safe because
+       qemu_timer_expired() can be called from a signal. */
+    pt = &active_timers[ts->clock->type];
+    for(;;) {
+        t = *pt;
+        if (!t)
+            break;
+        if (t->expire_time > expire_time)
+            break;
+        pt = &t->next;
+    }
+    ts->expire_time = expire_time;
+    ts->next = *pt;
+    *pt = ts;
+
+    /* Rearm if necessary  */
+    if (pt == &active_timers[ts->clock->type]) {
+        if ((alarm_timer->flags & ALARM_FLAG_EXPIRED) == 0) {
+            qemu_rearm_alarm_timer(alarm_timer);
+        }
+        /* Interrupt execution to force deadline recalculation.  */
+        if (use_icount && cpu_single_env) {
+            cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+        }
+    }
+}
+
+int qemu_timer_pending(QEMUTimer *ts)
+{
+    QEMUTimer *t;
+    for(t = active_timers[ts->clock->type]; t != NULL; t = t->next) {
+        if (t == ts)
+            return 1;
+    }
+    return 0;
+}
+
+static inline int qemu_timer_expired(QEMUTimer *timer_head, int64_t current_time)
+{
+    if (!timer_head)
+        return 0;
+    return (timer_head->expire_time <= current_time);
+}
+
+static void qemu_run_timers(QEMUTimer **ptimer_head, int64_t current_time)
+{
+    QEMUTimer *ts;
+
+    for(;;) {
+        ts = *ptimer_head;
+        if (!ts || ts->expire_time > current_time)
+            break;
+        /* remove timer from the list before calling the callback */
+        *ptimer_head = ts->next;
+        ts->next = NULL;
+
+        /* run the callback (the timer list can be modified) */
+        ts->cb(ts->opaque);
+    }
+}
+
+int64_t qemu_get_clock(QEMUClock *clock)
+{
+    switch(clock->type) {
+    case QEMU_TIMER_REALTIME:
+        return get_clock() / 1000000;
+    default:
+    case QEMU_TIMER_VIRTUAL:
+        if (use_icount) {
+            return cpu_get_icount();
+        } else {
+            return cpu_get_clock();
+        }
+    }
+}
+
+static void init_timers(void)
+{
+    init_get_clock();
+    ticks_per_sec = QEMU_TIMER_BASE;
+    rt_clock = qemu_new_clock(QEMU_TIMER_REALTIME);
+    vm_clock = qemu_new_clock(QEMU_TIMER_VIRTUAL);
+}
+
+/* save a timer */
+void qemu_put_timer(QEMUFile *f, QEMUTimer *ts)
+{
+    uint64_t expire_time;
+
+    if (qemu_timer_pending(ts)) {
+        expire_time = ts->expire_time;
+    } else {
+        expire_time = -1;
+    }
+    qemu_put_be64(f, expire_time);
+}
+
+void qemu_get_timer(QEMUFile *f, QEMUTimer *ts)
+{
+    uint64_t expire_time;
+
+    expire_time = qemu_get_be64(f);
+    if (expire_time != -1) {
+        qemu_mod_timer(ts, expire_time);
+    } else {
+        qemu_del_timer(ts);
+    }
+}
+
+static void timer_save(QEMUFile *f, void *opaque)
+{
+    if (cpu_ticks_enabled) {
+        hw_error("cannot save state if virtual timers are running");
+    }
+    qemu_put_be64(f, cpu_ticks_offset);
+    qemu_put_be64(f, ticks_per_sec);
+    qemu_put_be64(f, cpu_clock_offset);
+}
+
+static int timer_load(QEMUFile *f, void *opaque, int version_id)
+{
+    if (version_id != 1 && version_id != 2)
+        return -EINVAL;
+    if (cpu_ticks_enabled) {
+        return -EINVAL;
+    }
+    cpu_ticks_offset=qemu_get_be64(f);
+    ticks_per_sec=qemu_get_be64(f);
+    if (version_id == 2) {
+        cpu_clock_offset=qemu_get_be64(f);
+    }
+    return 0;
+}
+
+#ifdef _WIN32
+void CALLBACK host_alarm_handler(UINT uTimerID, UINT uMsg,
+                                 DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
+#else
+static void host_alarm_handler(int host_signum)
+#endif
+{
+#if 0
+#define DISP_FREQ 1000
+    {
+        static int64_t delta_min = INT64_MAX;
+        static int64_t delta_max, delta_cum, last_clock, delta, ti;
+        static int count;
+        ti = qemu_get_clock(vm_clock);
+        if (last_clock != 0) {
+            delta = ti - last_clock;
+            if (delta < delta_min)
+                delta_min = delta;
+            if (delta > delta_max)
+                delta_max = delta;
+            delta_cum += delta;
+            if (++count == DISP_FREQ) {
+                printf("timer: min=%" PRId64 " us max=%" PRId64 " us avg=%" PRId64 " us avg_freq=%0.3f Hz\n",
+                       muldiv64(delta_min, 1000000, ticks_per_sec),
+                       muldiv64(delta_max, 1000000, ticks_per_sec),
+                       muldiv64(delta_cum, 1000000 / DISP_FREQ, ticks_per_sec),
+                       (double)ticks_per_sec / ((double)delta_cum / DISP_FREQ));
+                count = 0;
+                delta_min = INT64_MAX;
+                delta_max = 0;
+                delta_cum = 0;
+            }
+        }
+        last_clock = ti;
+    }
+#endif
+    if (alarm_has_dynticks(alarm_timer) ||
+        (!use_icount &&
+            qemu_timer_expired(active_timers[QEMU_TIMER_VIRTUAL],
+                               qemu_get_clock(vm_clock))) ||
+        qemu_timer_expired(active_timers[QEMU_TIMER_REALTIME],
+                           qemu_get_clock(rt_clock))) {
+#ifdef _WIN32
+        struct qemu_alarm_win32 *data = ((struct qemu_alarm_timer*)dwUser)->priv;
+        SetEvent(data->host_alarm);
+#endif
+        CPUState *env = next_cpu;
+
+        alarm_timer->flags |= ALARM_FLAG_EXPIRED;
+
+        if (env) {
+            /* stop the currently executing cpu because a timer occured */
+            cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+#ifdef USE_KQEMU
+            if (env->kqemu_enabled) {
+                kqemu_cpu_interrupt(env);
+            }
+#endif
+        }
+        event_pending = 1;
+    }
+}
+
+static int64_t qemu_next_deadline(void)
+{
+    int64_t delta;
+
+    if (active_timers[QEMU_TIMER_VIRTUAL]) {
+        delta = active_timers[QEMU_TIMER_VIRTUAL]->expire_time -
+                     qemu_get_clock(vm_clock);
+    } else {
+        /* To avoid problems with overflow limit this to 2^32.  */
+        delta = INT32_MAX;
+    }
+
+    if (delta < 0)
+        delta = 0;
+
+    return delta;
+}
+
+#if defined(__linux__) || defined(_WIN32)
+static uint64_t qemu_next_deadline_dyntick(void)
+{
+    int64_t delta;
+    int64_t rtdelta;
+
+    if (use_icount)
+        delta = INT32_MAX;
+    else
+        delta = (qemu_next_deadline() + 999) / 1000;
+
+    if (active_timers[QEMU_TIMER_REALTIME]) {
+        rtdelta = (active_timers[QEMU_TIMER_REALTIME]->expire_time -
+                 qemu_get_clock(rt_clock))*1000;
+        if (rtdelta < delta)
+            delta = rtdelta;
+    }
+
+    if (delta < MIN_TIMER_REARM_US)
+        delta = MIN_TIMER_REARM_US;
+
+    return delta;
+}
+#endif
+
+#ifndef _WIN32
+
+#if defined(__linux__)
+
+#define RTC_FREQ 1024
+
+static void enable_sigio_timer(int fd)
+{
+    struct sigaction act;
+
+    /* timer signal */
+    sigfillset(&act.sa_mask);
+    act.sa_flags = 0;
+    act.sa_handler = host_alarm_handler;
+
+    sigaction(SIGIO, &act, NULL);
+    fcntl(fd, F_SETFL, O_ASYNC);
+    fcntl(fd, F_SETOWN, getpid());
+}
+
+static int hpet_start_timer(struct qemu_alarm_timer *t)
+{
+    struct hpet_info info;
+    int r, fd;
+
+    fd = open("/dev/hpet", O_RDONLY);
+    if (fd < 0)
+        return -1;
+
+    /* Set frequency */
+    r = ioctl(fd, HPET_IRQFREQ, RTC_FREQ);
+    if (r < 0) {
+        fprintf(stderr, "Could not configure '/dev/hpet' to have a 1024Hz timer. This is not a fatal\n"
+                "error, but for better emulation accuracy type:\n"
+                "'echo 1024 > /proc/sys/dev/hpet/max-user-freq' as root.\n");
+        goto fail;
+    }
+
+    /* Check capabilities */
+    r = ioctl(fd, HPET_INFO, &info);
+    if (r < 0)
+        goto fail;
+
+    /* Enable periodic mode */
+    r = ioctl(fd, HPET_EPI, 0);
+    if (info.hi_flags && (r < 0))
+        goto fail;
+
+    /* Enable interrupt */
+    r = ioctl(fd, HPET_IE_ON, 0);
+    if (r < 0)
+        goto fail;
+
+    enable_sigio_timer(fd);
+    t->priv = (void *)(long)fd;
+
+    return 0;
+fail:
+    close(fd);
+    return -1;
+}
+
+static void hpet_stop_timer(struct qemu_alarm_timer *t)
+{
+    int fd = (long)t->priv;
+
+    close(fd);
+}
+
+static int rtc_start_timer(struct qemu_alarm_timer *t)
+{
+    int rtc_fd;
+    unsigned long current_rtc_freq = 0;
+
+    TFR(rtc_fd = open("/dev/rtc", O_RDONLY));
+    if (rtc_fd < 0)
+        return -1;
+    ioctl(rtc_fd, RTC_IRQP_READ, &current_rtc_freq);
+    if (current_rtc_freq != RTC_FREQ &&
+        ioctl(rtc_fd, RTC_IRQP_SET, RTC_FREQ) < 0) {
+        fprintf(stderr, "Could not configure '/dev/rtc' to have a 1024 Hz timer. This is not a fatal\n"
+                "error, but for better emulation accuracy either use a 2.6 host Linux kernel or\n"
+                "type 'echo 1024 > /proc/sys/dev/rtc/max-user-freq' as root.\n");
+        goto fail;
+    }
+    if (ioctl(rtc_fd, RTC_PIE_ON, 0) < 0) {
+    fail:
+        close(rtc_fd);
+        return -1;
+    }
+
+    enable_sigio_timer(rtc_fd);
+
+    t->priv = (void *)(long)rtc_fd;
+
+    return 0;
+}
+
+static void rtc_stop_timer(struct qemu_alarm_timer *t)
+{
+    int rtc_fd = (long)t->priv;
+
+    close(rtc_fd);
+}
+
+static int dynticks_start_timer(struct qemu_alarm_timer *t)
+{
+    struct sigevent ev;
+    timer_t host_timer;
+    struct sigaction act;
+
+    sigfillset(&act.sa_mask);
+    act.sa_flags = 0;
+    act.sa_handler = host_alarm_handler;
+
+    sigaction(SIGALRM, &act, NULL);
+
+    ev.sigev_value.sival_int = 0;
+    ev.sigev_notify = SIGEV_SIGNAL;
+    ev.sigev_signo = SIGALRM;
+
+    if (timer_create(CLOCK_REALTIME, &ev, &host_timer)) {
+        perror("timer_create");
+
+        /* disable dynticks */
+        fprintf(stderr, "Dynamic Ticks disabled\n");
+
+        return -1;
+    }
+
+    t->priv = (void *)host_timer;
+
+    return 0;
+}
+
+static void dynticks_stop_timer(struct qemu_alarm_timer *t)
+{
+    timer_t host_timer = (timer_t)t->priv;
+
+    timer_delete(host_timer);
+}
+
+static void dynticks_rearm_timer(struct qemu_alarm_timer *t)
+{
+    timer_t host_timer = (timer_t)t->priv;
+    struct itimerspec timeout;
+    int64_t nearest_delta_us = INT64_MAX;
+    int64_t current_us;
+
+    if (!active_timers[QEMU_TIMER_REALTIME] &&
+                !active_timers[QEMU_TIMER_VIRTUAL])
+        return;
+
+    nearest_delta_us = qemu_next_deadline_dyntick();
+
+    /* check whether a timer is already running */
+    if (timer_gettime(host_timer, &timeout)) {
+        perror("gettime");
+        fprintf(stderr, "Internal timer error: aborting\n");
+        exit(1);
+    }
+    current_us = timeout.it_value.tv_sec * 1000000 + timeout.it_value.tv_nsec/1000;
+    if (current_us && current_us <= nearest_delta_us)
+        return;
+
+    timeout.it_interval.tv_sec = 0;
+    timeout.it_interval.tv_nsec = 0; /* 0 for one-shot timer */
+    timeout.it_value.tv_sec =  nearest_delta_us / 1000000;
+    timeout.it_value.tv_nsec = (nearest_delta_us % 1000000) * 1000;
+    if (timer_settime(host_timer, 0 /* RELATIVE */, &timeout, NULL)) {
+        perror("settime");
+        fprintf(stderr, "Internal timer error: aborting\n");
+        exit(1);
+    }
+}
+
+#endif /* defined(__linux__) */
+
+static int unix_start_timer(struct qemu_alarm_timer *t)
+{
+    struct sigaction act;
+    struct itimerval itv;
+    int err;
+
+    /* timer signal */
+    sigfillset(&act.sa_mask);
+    act.sa_flags = 0;
+    act.sa_handler = host_alarm_handler;
+
+    sigaction(SIGALRM, &act, NULL);
+
+    itv.it_interval.tv_sec = 0;
+    /* for i386 kernel 2.6 to get 1 ms */
+    itv.it_interval.tv_usec = 999;
+    itv.it_value.tv_sec = 0;
+    itv.it_value.tv_usec = 10 * 1000;
+
+    err = setitimer(ITIMER_REAL, &itv, NULL);
+    if (err)
+        return -1;
+
+    return 0;
+}
+
+static void unix_stop_timer(struct qemu_alarm_timer *t)
+{
+    struct itimerval itv;
+
+    memset(&itv, 0, sizeof(itv));
+    setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+#endif /* !defined(_WIN32) */
+
+#ifdef _WIN32
+
+static int win32_start_timer(struct qemu_alarm_timer *t)
+{
+    TIMECAPS tc;
+    struct qemu_alarm_win32 *data = t->priv;
+    UINT flags;
+
+    data->host_alarm = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if (!data->host_alarm) {
+        perror("Failed CreateEvent");
+        return -1;
+    }
+
+    memset(&tc, 0, sizeof(tc));
+    timeGetDevCaps(&tc, sizeof(tc));
+
+    if (data->period < tc.wPeriodMin)
+        data->period = tc.wPeriodMin;
+
+    timeBeginPeriod(data->period);
+
+    flags = TIME_CALLBACK_FUNCTION;
+    if (alarm_has_dynticks(t))
+        flags |= TIME_ONESHOT;
+    else
+        flags |= TIME_PERIODIC;
+
+    data->timerId = timeSetEvent(1,         // interval (ms)
+                        data->period,       // resolution
+                        host_alarm_handler, // function
+                        (DWORD)t,           // parameter
+                        flags);
+
+    if (!data->timerId) {
+        perror("Failed to initialize win32 alarm timer");
+
+        timeEndPeriod(data->period);
+        CloseHandle(data->host_alarm);
+        return -1;
+    }
+
+    qemu_add_wait_object(data->host_alarm, NULL, NULL);
+
+    return 0;
+}
+
+static void win32_stop_timer(struct qemu_alarm_timer *t)
+{
+    struct qemu_alarm_win32 *data = t->priv;
+
+    timeKillEvent(data->timerId);
+    timeEndPeriod(data->period);
+
+    CloseHandle(data->host_alarm);
+}
+
+static void win32_rearm_timer(struct qemu_alarm_timer *t)
+{
+    struct qemu_alarm_win32 *data = t->priv;
+    uint64_t nearest_delta_us;
+
+    if (!active_timers[QEMU_TIMER_REALTIME] &&
+                !active_timers[QEMU_TIMER_VIRTUAL])
+        return;
+
+    nearest_delta_us = qemu_next_deadline_dyntick();
+    nearest_delta_us /= 1000;
+
+    timeKillEvent(data->timerId);
+
+    data->timerId = timeSetEvent(1,
+                        data->period,
+                        host_alarm_handler,
+                        (DWORD)t,
+                        TIME_ONESHOT | TIME_PERIODIC);
+
+    if (!data->timerId) {
+        perror("Failed to re-arm win32 alarm timer");
+
+        timeEndPeriod(data->period);
+        CloseHandle(data->host_alarm);
+        exit(1);
+    }
+}
+
+#endif /* _WIN32 */
+
+static void init_timer_alarm(void)
+{
+    struct qemu_alarm_timer *t;
+    int i, err = -1;
+
+    for (i = 0; alarm_timers[i].name; i++) {
+        t = &alarm_timers[i];
+
+        err = t->start(t);
+        if (!err)
+            break;
+    }
+
+    if (err) {
+        fprintf(stderr, "Unable to find any suitable alarm timer.\n");
+        fprintf(stderr, "Terminating\n");
+        exit(1);
+    }
+
+    alarm_timer = t;
+}
+
+static void quit_timers(void)
+{
+    alarm_timer->stop(alarm_timer);
+    alarm_timer = NULL;
+}
+
+/***********************************************************/
+/* host time/date access */
+void qemu_get_timedate(struct tm *tm, int offset)
+{
+    time_t ti;
+    struct tm *ret;
+
+    time(&ti);
+    ti += offset;
+    if (rtc_date_offset == -1) {
+        if (rtc_utc)
+            ret = gmtime(&ti);
+        else
+            ret = localtime(&ti);
+    } else {
+        ti -= rtc_date_offset;
+        ret = gmtime(&ti);
+    }
+
+    memcpy(tm, ret, sizeof(struct tm));
+}
+
+int qemu_timedate_diff(struct tm *tm)
+{
+    time_t seconds;
+
+    if (rtc_date_offset == -1)
+        if (rtc_utc)
+            seconds = mktimegm(tm);
+        else
+            seconds = mktime(tm);
+    else
+        seconds = mktimegm(tm) + rtc_date_offset;
+
+    return seconds - time(NULL);
+}
+
+
+#ifdef CONFIG_TRACE
+static int tbflush_requested;
+static int exit_requested;
+
+void start_tracing()
+{
+  if (trace_filename == NULL)
+    return;
+  if (!tracing) {
+    fprintf(stderr,"-- start tracing --\n");
+    start_time = Now();
+  }
+  tracing = 1;
+  tbflush_requested = 1;
+  if (cpu_single_env)
+    cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+void stop_tracing()
+{
+  if (trace_filename == NULL)
+    return;
+  if (tracing) {
+    end_time = Now();
+    elapsed_usecs += end_time - start_time;
+    fprintf(stderr,"-- stop tracing --\n");
+  }
+  tracing = 0;
+  tbflush_requested = 1;
+  if (cpu_single_env)
+    cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+#ifndef _WIN32
+/* This is the handler for the SIGUSR1 and SIGUSR2 signals.
+ * SIGUSR1 turns tracing on.  SIGUSR2 turns tracing off.
+ */
+void sigusr_handler(int sig)
+{
+  if (sig == SIGUSR1)
+    start_tracing();
+  else
+    stop_tracing();
+}
+#endif
+
+/* This is the handler to catch control-C so that we can exit cleanly.
+ * This is needed when tracing to flush the buffers to disk.
+ */
+void sigint_handler(int sig)
+{
+  exit_requested = 1;
+  if (cpu_single_env)
+    cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+#endif /* CONFIG_TRACE */
+
+
+/***********************************************************/
+/* character device */
+
+static void qemu_chr_event(CharDriverState *s, int event)
+{
+    if (!s->chr_event)
+        return;
+    s->chr_event(s->handler_opaque, event);
+}
+
+static void qemu_chr_reset_bh(void *opaque)
+{
+    CharDriverState *s = opaque;
+    qemu_chr_event(s, CHR_EVENT_RESET);
+    qemu_bh_delete(s->bh);
+    s->bh = NULL;
+}
+
+void qemu_chr_reset(CharDriverState *s)
+{
+    if (s->bh == NULL) {
+	s->bh = qemu_bh_new(qemu_chr_reset_bh, s);
+	qemu_bh_schedule(s->bh);
+    }
+}
+
+int qemu_chr_write(CharDriverState *s, const uint8_t *buf, int len)
+{
+    return s->chr_write(s, buf, len);
+}
+
+int qemu_chr_ioctl(CharDriverState *s, int cmd, void *arg)
+{
+    if (!s->chr_ioctl)
+        return -ENOTSUP;
+    return s->chr_ioctl(s, cmd, arg);
+}
+
+int qemu_chr_can_read(CharDriverState *s)
+{
+    if (!s->chr_can_read)
+        return 0;
+    return s->chr_can_read(s->handler_opaque);
+}
+
+void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len)
+{
+    s->chr_read(s->handler_opaque, buf, len);
+}
+
+void qemu_chr_accept_input(CharDriverState *s)
+{
+    if (s->chr_accept_input)
+        s->chr_accept_input(s);
+}
+
+void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
+{
+    char buf[4096];
+    va_list ap;
+    va_start(ap, fmt);
+    vsnprintf(buf, sizeof(buf), fmt, ap);
+    qemu_chr_write(s, (uint8_t *)buf, strlen(buf));
+    va_end(ap);
+}
+
+void qemu_chr_send_event(CharDriverState *s, int event)
+{
+    if (s->chr_send_event)
+        s->chr_send_event(s, event);
+}
+
+void qemu_chr_add_handlers(CharDriverState *s,
+                           IOCanRWHandler *fd_can_read,
+                           IOReadHandler *fd_read,
+                           IOEventHandler *fd_event,
+                           void *opaque)
+{
+    s->chr_can_read = fd_can_read;
+    s->chr_read = fd_read;
+    s->chr_event = fd_event;
+    s->handler_opaque = opaque;
+    if (s->chr_update_read_handler)
+        s->chr_update_read_handler(s);
+}
+
+static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    return len;
+}
+
+static CharDriverState *qemu_chr_open_null(void)
+{
+    CharDriverState *chr;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    chr->chr_write = null_chr_write;
+    return chr;
+}
+
+/* MUX driver for serial I/O splitting */
+static int term_timestamps;
+static int64_t term_timestamps_start;
+#define MAX_MUX 4
+#define MUX_BUFFER_SIZE 32	/* Must be a power of 2.  */
+#define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1)
+typedef struct {
+    IOCanRWHandler *chr_can_read[MAX_MUX];
+    IOReadHandler *chr_read[MAX_MUX];
+    IOEventHandler *chr_event[MAX_MUX];
+    void *ext_opaque[MAX_MUX];
+    CharDriverState *drv;
+    unsigned char buffer[MUX_BUFFER_SIZE];
+    int prod;
+    int cons;
+    int mux_cnt;
+    int term_got_escape;
+    int max_size;
+} MuxDriver;
+
+
+static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    MuxDriver *d = chr->opaque;
+    int ret;
+    if (!term_timestamps) {
+        ret = d->drv->chr_write(d->drv, buf, len);
+    } else {
+        int i;
+
+        ret = 0;
+        for(i = 0; i < len; i++) {
+            ret += d->drv->chr_write(d->drv, buf+i, 1);
+            if (buf[i] == '\n') {
+                char buf1[64];
+                int64_t ti;
+                int secs;
+
+                ti = get_clock();
+                if (term_timestamps_start == -1)
+                    term_timestamps_start = ti;
+                ti -= term_timestamps_start;
+                secs = ti / 1000000000;
+                snprintf(buf1, sizeof(buf1),
+                         "[%02d:%02d:%02d.%03d] ",
+                         secs / 3600,
+                         (secs / 60) % 60,
+                         secs % 60,
+                         (int)((ti / 1000000) % 1000));
+                d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1));
+            }
+        }
+    }
+    return ret;
+}
+
+static const char * const mux_help[] = {
+    "% h    print this help\n\r",
+    "% x    exit emulator\n\r",
+    "% s    save disk data back to file (if -snapshot)\n\r",
+    "% t    toggle console timestamps\n\r"
+    "% b    send break (magic sysrq)\n\r",
+    "% c    switch between console and monitor\n\r",
+    "% %  sends %\n\r",
+    NULL
+};
+
+static int term_escape_char = 0x01; /* ctrl-a is used for escape */
+static void mux_print_help(CharDriverState *chr)
+{
+    int i, j;
+    char ebuf[15] = "Escape-Char";
+    char cbuf[50] = "\n\r";
+
+    if (term_escape_char > 0 && term_escape_char < 26) {
+        snprintf(cbuf, sizeof(cbuf), "\n\r");
+        snprintf(ebuf, sizeof(ebuf), "C-%c", term_escape_char - 1 + 'a');
+    } else {
+        snprintf(cbuf, sizeof(cbuf),
+                 "\n\rEscape-Char set to Ascii: 0x%02x\n\r\n\r",
+                 term_escape_char);
+    }
+    chr->chr_write(chr, (uint8_t *)cbuf, strlen(cbuf));
+    for (i = 0; mux_help[i] != NULL; i++) {
+        for (j=0; mux_help[i][j] != '\0'; j++) {
+            if (mux_help[i][j] == '%')
+                chr->chr_write(chr, (uint8_t *)ebuf, strlen(ebuf));
+            else
+                chr->chr_write(chr, (uint8_t *)&mux_help[i][j], 1);
+        }
+    }
+}
+
+static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
+{
+    if (d->term_got_escape) {
+        d->term_got_escape = 0;
+        if (ch == term_escape_char)
+            goto send_char;
+        switch(ch) {
+        case '?':
+        case 'h':
+            mux_print_help(chr);
+            break;
+        case 'x':
+            {
+                 const char *term =  "QEMU: Terminated\n\r";
+                 chr->chr_write(chr,(uint8_t *)term,strlen(term));
+                 exit(0);
+                 break;
+            }
+        case 's':
+            {
+                int i;
+                for (i = 0; i < nb_drives; i++) {
+                        bdrv_commit(drives_table[i].bdrv);
+                }
+            }
+            break;
+        case 'b':
+            qemu_chr_event(chr, CHR_EVENT_BREAK);
+            break;
+        case 'c':
+            /* Switch to the next registered device */
+            chr->focus++;
+            if (chr->focus >= d->mux_cnt)
+                chr->focus = 0;
+            break;
+       case 't':
+           term_timestamps = !term_timestamps;
+           term_timestamps_start = -1;
+           break;
+        }
+    } else if (ch == term_escape_char) {
+        d->term_got_escape = 1;
+    } else {
+    send_char:
+        return 1;
+    }
+    return 0;
+}
+
+static void mux_chr_accept_input(CharDriverState *chr)
+{
+    int m = chr->focus;
+    MuxDriver *d = chr->opaque;
+
+    while (d->prod != d->cons &&
+           d->chr_can_read[m] &&
+           d->chr_can_read[m](d->ext_opaque[m])) {
+        d->chr_read[m](d->ext_opaque[m],
+                       &d->buffer[d->cons++ & MUX_BUFFER_MASK], 1);
+    }
+}
+
+static int mux_chr_can_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    MuxDriver *d = chr->opaque;
+
+    if ((d->prod - d->cons) < MUX_BUFFER_SIZE)
+        return 1;
+    if (d->chr_can_read[chr->focus])
+        return d->chr_can_read[chr->focus](d->ext_opaque[chr->focus]);
+    return 0;
+}
+
+static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
+{
+    CharDriverState *chr = opaque;
+    MuxDriver *d = chr->opaque;
+    int m = chr->focus;
+    int i;
+
+    mux_chr_accept_input (opaque);
+
+    for(i = 0; i < size; i++)
+        if (mux_proc_byte(chr, d, buf[i])) {
+            if (d->prod == d->cons &&
+                d->chr_can_read[m] &&
+                d->chr_can_read[m](d->ext_opaque[m]))
+                d->chr_read[m](d->ext_opaque[m], &buf[i], 1);
+            else
+                d->buffer[d->prod++ & MUX_BUFFER_MASK] = buf[i];
+        }
+}
+
+static void mux_chr_event(void *opaque, int event)
+{
+    CharDriverState *chr = opaque;
+    MuxDriver *d = chr->opaque;
+    int i;
+
+    /* Send the event to all registered listeners */
+    for (i = 0; i < d->mux_cnt; i++)
+        if (d->chr_event[i])
+            d->chr_event[i](d->ext_opaque[i], event);
+}
+
+static void mux_chr_update_read_handler(CharDriverState *chr)
+{
+    MuxDriver *d = chr->opaque;
+
+    if (d->mux_cnt >= MAX_MUX) {
+        fprintf(stderr, "Cannot add I/O handlers, MUX array is full\n");
+        return;
+    }
+    d->ext_opaque[d->mux_cnt] = chr->handler_opaque;
+    d->chr_can_read[d->mux_cnt] = chr->chr_can_read;
+    d->chr_read[d->mux_cnt] = chr->chr_read;
+    d->chr_event[d->mux_cnt] = chr->chr_event;
+    /* Fix up the real driver with mux routines */
+    if (d->mux_cnt == 0) {
+        qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
+                              mux_chr_event, chr);
+    }
+    chr->focus = d->mux_cnt;
+    d->mux_cnt++;
+}
+
+static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
+{
+    CharDriverState *chr;
+    MuxDriver *d;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    d = qemu_mallocz(sizeof(MuxDriver));
+    if (!d) {
+        free(chr);
+        return NULL;
+    }
+
+    chr->opaque = d;
+    d->drv = drv;
+    chr->focus = -1;
+    chr->chr_write = mux_chr_write;
+    chr->chr_update_read_handler = mux_chr_update_read_handler;
+    chr->chr_accept_input = mux_chr_accept_input;
+    return chr;
+}
+
+
+#ifdef _WIN32
+
+static int send_all(int fd, const uint8_t *buf, int len1)
+{
+    int ret, len;
+
+    len = len1;
+    while (len > 0) {
+        ret = socket_send(fd, buf, len);
+        if (ret < 0) {
+            if (errno != EWOULDBLOCK) {
+                return -1;
+            }
+        } else if (ret == 0) {
+            break;
+        } else {
+            buf += ret;
+            len -= ret;
+        }
+    }
+    return len1 - len;
+}
+
+#else
+
+static int unix_write(int fd, const uint8_t *buf, int len1)
+{
+    int ret, len;
+
+    len = len1;
+    while (len > 0) {
+        ret = write(fd, buf, len);
+        if (ret < 0) {
+            if (errno != EINTR && errno != EAGAIN)
+                return -1;
+        } else if (ret == 0) {
+            break;
+        } else {
+            buf += ret;
+            len -= ret;
+        }
+    }
+    return len1 - len;
+}
+
+static inline int send_all(int fd, const uint8_t *buf, int len1)
+{
+    return unix_write(fd, buf, len1);
+}
+#endif /* !_WIN32 */
+
+#ifndef _WIN32
+
+typedef struct {
+    int fd_in, fd_out;
+    int max_size;
+} FDCharDriver;
+
+#define STDIO_MAX_CLIENTS 1
+static int stdio_nb_clients = 0;
+
+static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    FDCharDriver *s = chr->opaque;
+    return unix_write(s->fd_out, buf, len);
+}
+
+static int fd_chr_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    FDCharDriver *s = chr->opaque;
+
+    s->max_size = qemu_chr_can_read(chr);
+    return s->max_size;
+}
+
+static void fd_chr_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    FDCharDriver *s = chr->opaque;
+    int size, len;
+    uint8_t buf[1024];
+
+    len = sizeof(buf);
+    if (len > s->max_size)
+        len = s->max_size;
+    if (len == 0)
+        return;
+    size = read(s->fd_in, buf, len);
+    if (size == 0) {
+        /* FD has been closed. Remove it from the active list.  */
+        qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+        return;
+    }
+    if (size > 0) {
+        qemu_chr_read(chr, buf, size);
+    }
+}
+
+static void fd_chr_update_read_handler(CharDriverState *chr)
+{
+    FDCharDriver *s = chr->opaque;
+
+    if (s->fd_in >= 0) {
+        if (nographic && s->fd_in == 0) {
+        } else {
+            qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll,
+                                 fd_chr_read, NULL, chr);
+        }
+    }
+}
+
+static void fd_chr_close(struct CharDriverState *chr)
+{
+    FDCharDriver *s = chr->opaque;
+
+    if (s->fd_in >= 0) {
+        if (nographic && s->fd_in == 0) {
+        } else {
+            qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+        }
+    }
+
+    qemu_free(s);
+}
+
+/* open a character device to a unix fd */
+static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
+{
+    CharDriverState *chr;
+    FDCharDriver *s;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(FDCharDriver));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    s->fd_in = fd_in;
+    s->fd_out = fd_out;
+    chr->opaque = s;
+    chr->chr_write = fd_chr_write;
+    chr->chr_update_read_handler = fd_chr_update_read_handler;
+    chr->chr_close = fd_chr_close;
+
+    qemu_chr_reset(chr);
+
+    return chr;
+}
+
+static CharDriverState *qemu_chr_open_file_out(const char *file_out)
+{
+    int fd_out;
+
+    TFR(fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666));
+    if (fd_out < 0)
+        return NULL;
+    return qemu_chr_open_fd(-1, fd_out);
+}
+
+static CharDriverState *qemu_chr_open_pipe(const char *filename)
+{
+    int fd_in, fd_out;
+    char filename_in[256], filename_out[256];
+
+    snprintf(filename_in, 256, "%s.in", filename);
+    snprintf(filename_out, 256, "%s.out", filename);
+    TFR(fd_in = open(filename_in, O_RDWR | O_BINARY));
+    TFR(fd_out = open(filename_out, O_RDWR | O_BINARY));
+    if (fd_in < 0 || fd_out < 0) {
+	if (fd_in >= 0)
+	    close(fd_in);
+	if (fd_out >= 0)
+	    close(fd_out);
+        TFR(fd_in = fd_out = open(filename, O_RDWR | O_BINARY));
+        if (fd_in < 0)
+            return NULL;
+    }
+    return qemu_chr_open_fd(fd_in, fd_out);
+}
+
+CharDriverState *qemu_chr_open_fdpair(const char *fd_pair)
+{
+    int fd_in, fd_out;
+    char *endptr;
+
+    /* fd_pair should contain two decimal fd values, separated by
+     * a colon. */
+    endptr = NULL;
+    fd_in = strtol(fd_pair, &endptr, 10);
+    if (endptr == NULL || endptr == fd_pair || *endptr != ':')
+        return NULL;
+    endptr++;   // skip colon
+    fd_pair = endptr;
+    endptr = NULL;
+    fd_out = strtol(fd_pair, &endptr, 10);
+    if (endptr == NULL || endptr == fd_pair || *endptr != '\0')
+        return NULL;
+
+    return qemu_chr_open_fd(fd_in, fd_out);
+}
+
+
+/* for STDIO, we handle the case where several clients use it
+   (nographic mode) */
+
+#define TERM_FIFO_MAX_SIZE 1
+
+static uint8_t term_fifo[TERM_FIFO_MAX_SIZE];
+static int term_fifo_size;
+
+static int stdio_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+
+    /* try to flush the queue if needed */
+    if (term_fifo_size != 0 && qemu_chr_can_read(chr) > 0) {
+        qemu_chr_read(chr, term_fifo, 1);
+        term_fifo_size = 0;
+    }
+    /* see if we can absorb more chars */
+    if (term_fifo_size == 0)
+        return 1;
+    else
+        return 0;
+}
+
+static void stdio_read(void *opaque)
+{
+    int size;
+    uint8_t buf[1];
+    CharDriverState *chr = opaque;
+
+    size = read(0, buf, 1);
+    if (size == 0) {
+        /* stdin has been closed. Remove it from the active list.  */
+        qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+        return;
+    }
+    if (size > 0) {
+        if (qemu_chr_can_read(chr) > 0) {
+            qemu_chr_read(chr, buf, 1);
+        } else if (term_fifo_size == 0) {
+            term_fifo[term_fifo_size++] = buf[0];
+        }
+    }
+}
+
+/* init terminal so that we can grab keys */
+static struct termios oldtty;
+static int old_fd0_flags;
+static int term_atexit_done;
+
+static void term_exit(void)
+{
+    tcsetattr (0, TCSANOW, &oldtty);
+    fcntl(0, F_SETFL, old_fd0_flags);
+}
+
+static void term_init(void)
+{
+    struct termios tty;
+
+    tcgetattr (0, &tty);
+    oldtty = tty;
+    old_fd0_flags = fcntl(0, F_GETFL);
+
+    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+                          |INLCR|IGNCR|ICRNL|IXON);
+    tty.c_oflag |= OPOST;
+    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
+    /* if graphical mode, we allow Ctrl-C handling */
+    if (nographic)
+        tty.c_lflag &= ~ISIG;
+    tty.c_cflag &= ~(CSIZE|PARENB);
+    tty.c_cflag |= CS8;
+    tty.c_cc[VMIN] = 1;
+    tty.c_cc[VTIME] = 0;
+
+    tcsetattr (0, TCSANOW, &tty);
+
+    if (!term_atexit_done++)
+        atexit(term_exit);
+
+    fcntl(0, F_SETFL, O_NONBLOCK);
+}
+
+static void qemu_chr_close_stdio(struct CharDriverState *chr)
+{
+    term_exit();
+    stdio_nb_clients--;
+    qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+    fd_chr_close(chr);
+}
+
+static CharDriverState *qemu_chr_open_stdio(void)
+{
+    CharDriverState *chr;
+
+    if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
+        return NULL;
+    chr = qemu_chr_open_fd(0, 1);
+    chr->chr_close = qemu_chr_close_stdio;
+    qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
+    stdio_nb_clients++;
+    term_init();
+
+    return chr;
+}
+
+#ifdef __sun__
+/* Once Solaris has openpty(), this is going to be removed. */
+int openpty(int *amaster, int *aslave, char *name,
+            struct termios *termp, struct winsize *winp)
+{
+        const char *slave;
+        int mfd = -1, sfd = -1;
+
+        *amaster = *aslave = -1;
+
+        mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
+        if (mfd < 0)
+                goto err;
+
+        if (grantpt(mfd) == -1 || unlockpt(mfd) == -1)
+                goto err;
+
+        if ((slave = ptsname(mfd)) == NULL)
+                goto err;
+
+        if ((sfd = open(slave, O_RDONLY | O_NOCTTY)) == -1)
+                goto err;
+
+        if (ioctl(sfd, I_PUSH, "ptem") == -1 ||
+            (termp != NULL && tcgetattr(sfd, termp) < 0))
+                goto err;
+
+        if (amaster)
+                *amaster = mfd;
+        if (aslave)
+                *aslave = sfd;
+        if (winp)
+                ioctl(sfd, TIOCSWINSZ, winp);
+
+        return 0;
+
+err:
+        if (sfd != -1)
+                close(sfd);
+        close(mfd);
+        return -1;
+}
+
+void cfmakeraw (struct termios *termios_p)
+{
+        termios_p->c_iflag &=
+                ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
+        termios_p->c_oflag &= ~OPOST;
+        termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
+        termios_p->c_cflag &= ~(CSIZE|PARENB);
+        termios_p->c_cflag |= CS8;
+
+        termios_p->c_cc[VMIN] = 0;
+        termios_p->c_cc[VTIME] = 0;
+}
+#endif  /* __sun__ */
+
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+    || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+
+typedef struct {
+    int fd;
+    int connected;
+    int polling;
+    int read_bytes;
+    QEMUTimer *timer;
+} PtyCharDriver;
+
+static void pty_chr_update_read_handler(CharDriverState *chr);
+static void pty_chr_state(CharDriverState *chr, int connected);
+
+static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    PtyCharDriver *s = chr->opaque;
+
+    if (!s->connected) {
+        /* guest sends data, check for (re-)connect */
+        pty_chr_update_read_handler(chr);
+        return 0;
+    }
+    return unix_write(s->fd, buf, len);
+}
+
+static int pty_chr_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    PtyCharDriver *s = chr->opaque;
+
+    s->read_bytes = qemu_chr_can_read(chr);
+    return s->read_bytes;
+}
+
+static void pty_chr_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    PtyCharDriver *s = chr->opaque;
+    int size, len;
+    uint8_t buf[1024];
+
+    len = sizeof(buf);
+    if (len > s->read_bytes)
+        len = s->read_bytes;
+    if (len == 0)
+        return;
+    size = read(s->fd, buf, len);
+    if ((size == -1 && errno == EIO) ||
+        (size == 0)) {
+        pty_chr_state(chr, 0);
+        return;
+    }
+    if (size > 0) {
+        pty_chr_state(chr, 1);
+        qemu_chr_read(chr, buf, size);
+    }
+}
+
+static void pty_chr_update_read_handler(CharDriverState *chr)
+{
+    PtyCharDriver *s = chr->opaque;
+
+    qemu_set_fd_handler2(s->fd, pty_chr_read_poll,
+                         pty_chr_read, NULL, chr);
+    s->polling = 1;
+    /*
+     * Short timeout here: just need wait long enougth that qemu makes
+     * it through the poll loop once.  When reconnected we want a
+     * short timeout so we notice it almost instantly.  Otherwise
+     * read() gives us -EIO instantly, making pty_chr_state() reset the
+     * timeout to the normal (much longer) poll interval before the
+     * timer triggers.
+     */
+    qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 10);
+}
+
+static void pty_chr_state(CharDriverState *chr, int connected)
+{
+    PtyCharDriver *s = chr->opaque;
+
+    if (!connected) {
+        qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+        s->connected = 0;
+        s->polling = 0;
+        /* (re-)connect poll interval for idle guests: once per second.
+         * We check more frequently in case the guests sends data to
+         * the virtual device linked to our pty. */
+        qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
+    } else {
+        if (!s->connected)
+            qemu_chr_reset(chr);
+        s->connected = 1;
+    }
+}
+
+static void pty_chr_timer(void *opaque)
+{
+    struct CharDriverState *chr = opaque;
+    PtyCharDriver *s = chr->opaque;
+
+    if (s->connected)
+        return;
+    if (s->polling) {
+        /* If we arrive here without polling being cleared due
+         * read returning -EIO, then we are (re-)connected */
+        pty_chr_state(chr, 1);
+        return;
+    }
+
+    /* Next poll ... */
+    pty_chr_update_read_handler(chr);
+}
+
+static void pty_chr_close(struct CharDriverState *chr)
+{
+    PtyCharDriver *s = chr->opaque;
+
+    qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+    close(s->fd);
+    qemu_free(s);
+}
+
+static CharDriverState *qemu_chr_open_pty(void)
+{
+    CharDriverState *chr;
+    PtyCharDriver *s;
+    struct termios tty;
+    int slave_fd;
+#if defined(__OpenBSD__)
+    char pty_name[PATH_MAX];
+#define q_ptsname(x) pty_name
+#else
+    char *pty_name = NULL;
+#define q_ptsname(x) ptsname(x)
+#endif
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(PtyCharDriver));
+    if (!s) {
+        qemu_free(chr);
+        return NULL;
+    }
+
+    if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) {
+        return NULL;
+    }
+
+    /* Set raw attributes on the pty. */
+    cfmakeraw(&tty);
+    tcsetattr(slave_fd, TCSAFLUSH, &tty);
+    close(slave_fd);
+
+    fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd));
+
+    chr->opaque = s;
+    chr->chr_write = pty_chr_write;
+    chr->chr_update_read_handler = pty_chr_update_read_handler;
+    chr->chr_close = pty_chr_close;
+
+    s->timer = qemu_new_timer(rt_clock, pty_chr_timer, chr);
+
+    return chr;
+}
+#endif /* __linux__ || __sun__ || __xxxBSD__ */
+
+static void tty_serial_init(int fd, int speed,
+                            int parity, int data_bits, int stop_bits)
+{
+    struct termios tty;
+    speed_t spd;
+
+#if 0
+    printf("tty_serial_init: speed=%d parity=%c data=%d stop=%d\n",
+           speed, parity, data_bits, stop_bits);
+#endif
+    tcgetattr (fd, &tty);
+
+#define MARGIN 1.1
+    if (speed <= 50 * MARGIN)
+        spd = B50;
+    else if (speed <= 75 * MARGIN)
+        spd = B75;
+    else if (speed <= 300 * MARGIN)
+        spd = B300;
+    else if (speed <= 600 * MARGIN)
+        spd = B600;
+    else if (speed <= 1200 * MARGIN)
+        spd = B1200;
+    else if (speed <= 2400 * MARGIN)
+        spd = B2400;
+    else if (speed <= 4800 * MARGIN)
+        spd = B4800;
+    else if (speed <= 9600 * MARGIN)
+        spd = B9600;
+    else if (speed <= 19200 * MARGIN)
+        spd = B19200;
+    else if (speed <= 38400 * MARGIN)
+        spd = B38400;
+    else if (speed <= 57600 * MARGIN)
+        spd = B57600;
+    else if (speed <= 115200 * MARGIN)
+        spd = B115200;
+    else
+        spd = B115200;
+
+    cfsetispeed(&tty, spd);
+    cfsetospeed(&tty, spd);
+
+    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
+                          |INLCR|IGNCR|ICRNL|IXON);
+    tty.c_oflag |= OPOST;
+    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG);
+    tty.c_cflag &= ~(CSIZE|PARENB|PARODD|CRTSCTS|CSTOPB);
+    switch(data_bits) {
+    default:
+    case 8:
+        tty.c_cflag |= CS8;
+        break;
+    case 7:
+        tty.c_cflag |= CS7;
+        break;
+    case 6:
+        tty.c_cflag |= CS6;
+        break;
+    case 5:
+        tty.c_cflag |= CS5;
+        break;
+    }
+    switch(parity) {
+    default:
+    case 'N':
+        break;
+    case 'E':
+        tty.c_cflag |= PARENB;
+        break;
+    case 'O':
+        tty.c_cflag |= PARENB | PARODD;
+        break;
+    }
+    if (stop_bits == 2)
+        tty.c_cflag |= CSTOPB;
+
+    tcsetattr (fd, TCSANOW, &tty);
+}
+
+static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+    FDCharDriver *s = chr->opaque;
+
+    switch(cmd) {
+    case CHR_IOCTL_SERIAL_SET_PARAMS:
+        {
+            QEMUSerialSetParams *ssp = arg;
+            tty_serial_init(s->fd_in, ssp->speed, ssp->parity,
+                            ssp->data_bits, ssp->stop_bits);
+        }
+        break;
+    case CHR_IOCTL_SERIAL_SET_BREAK:
+        {
+            int enable = *(int *)arg;
+            if (enable)
+                tcsendbreak(s->fd_in, 1);
+        }
+        break;
+    case CHR_IOCTL_SERIAL_GET_TIOCM:
+        {
+            int sarg = 0;
+            int *targ = (int *)arg;
+            ioctl(s->fd_in, TIOCMGET, &sarg);
+            *targ = 0;
+            if (sarg | TIOCM_CTS)
+                *targ |= CHR_TIOCM_CTS;
+            if (sarg | TIOCM_CAR)
+                *targ |= CHR_TIOCM_CAR;
+            if (sarg | TIOCM_DSR)
+                *targ |= CHR_TIOCM_DSR;
+            if (sarg | TIOCM_RI)
+                *targ |= CHR_TIOCM_RI;
+            if (sarg | TIOCM_DTR)
+                *targ |= CHR_TIOCM_DTR;
+            if (sarg | TIOCM_RTS)
+                *targ |= CHR_TIOCM_RTS;
+        }
+        break;
+    case CHR_IOCTL_SERIAL_SET_TIOCM:
+        {
+            int sarg = *(int *)arg;
+            int targ = 0;
+            if (sarg | CHR_TIOCM_DTR)
+                targ |= TIOCM_DTR;
+            if (sarg | CHR_TIOCM_RTS)
+                targ |= TIOCM_RTS;
+            ioctl(s->fd_in, TIOCMSET, &targ);
+        }
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static CharDriverState *qemu_chr_open_tty(const char *filename)
+{
+    CharDriverState *chr;
+    int fd;
+
+    TFR(fd = open(filename, O_RDWR | O_NONBLOCK));
+    tty_serial_init(fd, 115200, 'N', 8, 1);
+    chr = qemu_chr_open_fd(fd, fd);
+    if (!chr) {
+        close(fd);
+        return NULL;
+    }
+    chr->chr_ioctl = tty_serial_ioctl;
+    qemu_chr_reset(chr);
+    return chr;
+}
+
+#if defined(__linux__)
+typedef struct {
+    int fd;
+    int mode;
+} ParallelCharDriver;
+
+static int pp_hw_mode(ParallelCharDriver *s, uint16_t mode)
+{
+    if (s->mode != mode) {
+	int m = mode;
+        if (ioctl(s->fd, PPSETMODE, &m) < 0)
+            return 0;
+	s->mode = mode;
+    }
+    return 1;
+}
+
+static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
+{
+    ParallelCharDriver *drv = chr->opaque;
+    int fd = drv->fd;
+    uint8_t b;
+
+    switch(cmd) {
+    case CHR_IOCTL_PP_READ_DATA:
+        if (ioctl(fd, PPRDATA, &b) < 0)
+            return -ENOTSUP;
+        *(uint8_t *)arg = b;
+        break;
+    case CHR_IOCTL_PP_WRITE_DATA:
+        b = *(uint8_t *)arg;
+        if (ioctl(fd, PPWDATA, &b) < 0)
+            return -ENOTSUP;
+        break;
+    case CHR_IOCTL_PP_READ_CONTROL:
+        if (ioctl(fd, PPRCONTROL, &b) < 0)
+            return -ENOTSUP;
+	/* Linux gives only the lowest bits, and no way to know data
+	   direction! For better compatibility set the fixed upper
+	   bits. */
+        *(uint8_t *)arg = b | 0xc0;
+        break;
+    case CHR_IOCTL_PP_WRITE_CONTROL:
+        b = *(uint8_t *)arg;
+        if (ioctl(fd, PPWCONTROL, &b) < 0)
+            return -ENOTSUP;
+        break;
+    case CHR_IOCTL_PP_READ_STATUS:
+        if (ioctl(fd, PPRSTATUS, &b) < 0)
+            return -ENOTSUP;
+        *(uint8_t *)arg = b;
+        break;
+    case CHR_IOCTL_PP_DATA_DIR:
+        if (ioctl(fd, PPDATADIR, (int *)arg) < 0)
+            return -ENOTSUP;
+        break;
+    case CHR_IOCTL_PP_EPP_READ_ADDR:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = read(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    case CHR_IOCTL_PP_EPP_READ:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = read(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    case CHR_IOCTL_PP_EPP_WRITE_ADDR:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP|IEEE1284_ADDR)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = write(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    case CHR_IOCTL_PP_EPP_WRITE:
+	if (pp_hw_mode(drv, IEEE1284_MODE_EPP)) {
+	    struct ParallelIOArg *parg = arg;
+	    int n = write(fd, parg->buffer, parg->count);
+	    if (n != parg->count) {
+		return -EIO;
+	    }
+	}
+        break;
+    default:
+        return -ENOTSUP;
+    }
+    return 0;
+}
+
+static void pp_close(CharDriverState *chr)
+{
+    ParallelCharDriver *drv = chr->opaque;
+    int fd = drv->fd;
+
+    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
+    ioctl(fd, PPRELEASE);
+    close(fd);
+    qemu_free(drv);
+}
+
+static CharDriverState *qemu_chr_open_pp(const char *filename)
+{
+    CharDriverState *chr;
+    ParallelCharDriver *drv;
+    int fd;
+
+    TFR(fd = open(filename, O_RDWR));
+    if (fd < 0)
+        return NULL;
+
+    if (ioctl(fd, PPCLAIM) < 0) {
+        close(fd);
+        return NULL;
+    }
+
+    drv = qemu_mallocz(sizeof(ParallelCharDriver));
+    if (!drv) {
+        close(fd);
+        return NULL;
+    }
+    drv->fd = fd;
+    drv->mode = IEEE1284_MODE_COMPAT;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr) {
+	qemu_free(drv);
+        close(fd);
+        return NULL;
+    }
+    chr->chr_write = null_chr_write;
+    chr->chr_ioctl = pp_ioctl;
+    chr->chr_close = pp_close;
+    chr->opaque = drv;
+
+    qemu_chr_reset(chr);
+
+    return chr;
+}
+#endif /* __linux__ */
+
+#else /* _WIN32 */
+
+typedef struct {
+    int max_size;
+    HANDLE hcom, hrecv, hsend;
+    OVERLAPPED orecv, osend;
+    BOOL fpipe;
+    DWORD len;
+} WinCharState;
+
+#define NSENDBUF 2048
+#define NRECVBUF 2048
+#define MAXCONNECT 1
+#define NTIMEOUT 5000
+
+static int win_chr_poll(void *opaque);
+static int win_chr_pipe_poll(void *opaque);
+
+static void win_chr_close(CharDriverState *chr)
+{
+    WinCharState *s = chr->opaque;
+
+    if (s->hsend) {
+        CloseHandle(s->hsend);
+        s->hsend = NULL;
+    }
+    if (s->hrecv) {
+        CloseHandle(s->hrecv);
+        s->hrecv = NULL;
+    }
+    if (s->hcom) {
+        CloseHandle(s->hcom);
+        s->hcom = NULL;
+    }
+    if (s->fpipe)
+        qemu_del_polling_cb(win_chr_pipe_poll, chr);
+    else
+        qemu_del_polling_cb(win_chr_poll, chr);
+}
+
+static int win_chr_init(CharDriverState *chr, const char *filename)
+{
+    WinCharState *s = chr->opaque;
+    COMMCONFIG comcfg;
+    COMMTIMEOUTS cto = { 0, 0, 0, 0, 0};
+    COMSTAT comstat;
+    DWORD size;
+    DWORD err;
+
+    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hsend) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hrecv) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+
+    s->hcom = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, NULL,
+                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+    if (s->hcom == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "Failed CreateFile (%lu)\n", GetLastError());
+        s->hcom = NULL;
+        goto fail;
+    }
+
+    if (!SetupComm(s->hcom, NRECVBUF, NSENDBUF)) {
+        fprintf(stderr, "Failed SetupComm\n");
+        goto fail;
+    }
+
+    ZeroMemory(&comcfg, sizeof(COMMCONFIG));
+    size = sizeof(COMMCONFIG);
+    GetDefaultCommConfig(filename, &comcfg, &size);
+    comcfg.dcb.DCBlength = sizeof(DCB);
+    CommConfigDialog(filename, NULL, &comcfg);
+
+    if (!SetCommState(s->hcom, &comcfg.dcb)) {
+        fprintf(stderr, "Failed SetCommState\n");
+        goto fail;
+    }
+
+    if (!SetCommMask(s->hcom, EV_ERR)) {
+        fprintf(stderr, "Failed SetCommMask\n");
+        goto fail;
+    }
+
+    cto.ReadIntervalTimeout = MAXDWORD;
+    if (!SetCommTimeouts(s->hcom, &cto)) {
+        fprintf(stderr, "Failed SetCommTimeouts\n");
+        goto fail;
+    }
+
+    if (!ClearCommError(s->hcom, &err, &comstat)) {
+        fprintf(stderr, "Failed ClearCommError\n");
+        goto fail;
+    }
+    qemu_add_polling_cb(win_chr_poll, chr);
+    return 0;
+
+ fail:
+    win_chr_close(chr);
+    return -1;
+}
+
+static int win_chr_write(CharDriverState *chr, const uint8_t *buf, int len1)
+{
+    WinCharState *s = chr->opaque;
+    DWORD len, ret, size, err;
+
+    len = len1;
+    ZeroMemory(&s->osend, sizeof(s->osend));
+    s->osend.hEvent = s->hsend;
+    while (len > 0) {
+        if (s->hsend)
+            ret = WriteFile(s->hcom, buf, len, &size, &s->osend);
+        else
+            ret = WriteFile(s->hcom, buf, len, &size, NULL);
+        if (!ret) {
+            err = GetLastError();
+            if (err == ERROR_IO_PENDING) {
+                ret = GetOverlappedResult(s->hcom, &s->osend, &size, TRUE);
+                if (ret) {
+                    buf += size;
+                    len -= size;
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            }
+        } else {
+            buf += size;
+            len -= size;
+        }
+    }
+    return len1 - len;
+}
+
+static int win_chr_read_poll(CharDriverState *chr)
+{
+    WinCharState *s = chr->opaque;
+
+    s->max_size = qemu_chr_can_read(chr);
+    return s->max_size;
+}
+
+static void win_chr_readfile(CharDriverState *chr)
+{
+    WinCharState *s = chr->opaque;
+    int ret, err;
+    uint8_t buf[1024];
+    DWORD size;
+
+    ZeroMemory(&s->orecv, sizeof(s->orecv));
+    s->orecv.hEvent = s->hrecv;
+    ret = ReadFile(s->hcom, buf, s->len, &size, &s->orecv);
+    if (!ret) {
+        err = GetLastError();
+        if (err == ERROR_IO_PENDING) {
+            ret = GetOverlappedResult(s->hcom, &s->orecv, &size, TRUE);
+        }
+    }
+
+    if (size > 0) {
+        qemu_chr_read(chr, buf, size);
+    }
+}
+
+static void win_chr_read(CharDriverState *chr)
+{
+    WinCharState *s = chr->opaque;
+
+    if (s->len > s->max_size)
+        s->len = s->max_size;
+    if (s->len == 0)
+        return;
+
+    win_chr_readfile(chr);
+}
+
+static int win_chr_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    WinCharState *s = chr->opaque;
+    COMSTAT status;
+    DWORD comerr;
+
+    ClearCommError(s->hcom, &comerr, &status);
+    if (status.cbInQue > 0) {
+        s->len = status.cbInQue;
+        win_chr_read_poll(chr);
+        win_chr_read(chr);
+        return 1;
+    }
+    return 0;
+}
+
+static CharDriverState *qemu_chr_open_win(const char *filename)
+{
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(WinCharState));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    chr->opaque = s;
+    chr->chr_write = win_chr_write;
+    chr->chr_close = win_chr_close;
+
+    if (win_chr_init(chr, filename) < 0) {
+        free(s);
+        free(chr);
+        return NULL;
+    }
+    qemu_chr_reset(chr);
+    return chr;
+}
+
+static int win_chr_pipe_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    WinCharState *s = chr->opaque;
+    DWORD size;
+
+    PeekNamedPipe(s->hcom, NULL, 0, NULL, &size, NULL);
+    if (size > 0) {
+        s->len = size;
+        win_chr_read_poll(chr);
+        win_chr_read(chr);
+        return 1;
+    }
+    return 0;
+}
+
+static int win_chr_pipe_init(CharDriverState *chr, const char *filename)
+{
+    WinCharState *s = chr->opaque;
+    OVERLAPPED ov;
+    int ret;
+    DWORD size;
+    char openname[256];
+
+    s->fpipe = TRUE;
+
+    s->hsend = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hsend) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+    s->hrecv = CreateEvent(NULL, TRUE, FALSE, NULL);
+    if (!s->hrecv) {
+        fprintf(stderr, "Failed CreateEvent\n");
+        goto fail;
+    }
+
+    snprintf(openname, sizeof(openname), "\\\\.\\pipe\\%s", filename);
+    s->hcom = CreateNamedPipe(openname, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                              PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+                              PIPE_WAIT,
+                              MAXCONNECT, NSENDBUF, NRECVBUF, NTIMEOUT, NULL);
+    if (s->hcom == INVALID_HANDLE_VALUE) {
+        fprintf(stderr, "Failed CreateNamedPipe (%lu)\n", GetLastError());
+        s->hcom = NULL;
+        goto fail;
+    }
+
+    ZeroMemory(&ov, sizeof(ov));
+    ov.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ret = ConnectNamedPipe(s->hcom, &ov);
+    if (ret) {
+        fprintf(stderr, "Failed ConnectNamedPipe\n");
+        goto fail;
+    }
+
+    ret = GetOverlappedResult(s->hcom, &ov, &size, TRUE);
+    if (!ret) {
+        fprintf(stderr, "Failed GetOverlappedResult\n");
+        if (ov.hEvent) {
+            CloseHandle(ov.hEvent);
+            ov.hEvent = NULL;
+        }
+        goto fail;
+    }
+
+    if (ov.hEvent) {
+        CloseHandle(ov.hEvent);
+        ov.hEvent = NULL;
+    }
+    qemu_add_polling_cb(win_chr_pipe_poll, chr);
+    return 0;
+
+ fail:
+    win_chr_close(chr);
+    return -1;
+}
+
+
+static CharDriverState *qemu_chr_open_win_pipe(const char *filename)
+{
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(WinCharState));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    chr->opaque = s;
+    chr->chr_write = win_chr_write;
+    chr->chr_close = win_chr_close;
+
+    if (win_chr_pipe_init(chr, filename) < 0) {
+        free(s);
+        free(chr);
+        return NULL;
+    }
+    qemu_chr_reset(chr);
+    return chr;
+}
+
+static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
+{
+    CharDriverState *chr;
+    WinCharState *s;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        return NULL;
+    s = qemu_mallocz(sizeof(WinCharState));
+    if (!s) {
+        free(chr);
+        return NULL;
+    }
+    s->hcom = fd_out;
+    chr->opaque = s;
+    chr->chr_write = win_chr_write;
+    qemu_chr_reset(chr);
+    return chr;
+}
+
+static CharDriverState *qemu_chr_open_win_con(const char *filename)
+{
+    return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE));
+}
+
+static CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
+{
+    HANDLE fd_out;
+
+    fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+                        OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if (fd_out == INVALID_HANDLE_VALUE)
+        return NULL;
+
+    return qemu_chr_open_win_file(fd_out);
+}
+#endif /* !_WIN32 */
+
+/***********************************************************/
+/* UDP Net console */
+
+typedef struct {
+    int fd;
+    SockAddress daddr;
+    uint8_t buf[1024];
+    int bufcnt;
+    int bufptr;
+    int max_size;
+} NetCharDriver;
+
+static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    NetCharDriver *s = chr->opaque;
+
+    return socket_sendto(s->fd, buf, len, &s->daddr);
+}
+
+static int udp_chr_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    NetCharDriver *s = chr->opaque;
+
+    s->max_size = qemu_chr_can_read(chr);
+
+    /* If there were any stray characters in the queue process them
+     * first
+     */
+    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+        qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+        s->bufptr++;
+        s->max_size = qemu_chr_can_read(chr);
+    }
+    return s->max_size;
+}
+
+static void udp_chr_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    NetCharDriver *s = chr->opaque;
+
+    if (s->max_size == 0)
+        return;
+    s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0);
+    s->bufptr = s->bufcnt;
+    if (s->bufcnt <= 0)
+        return;
+
+    s->bufptr = 0;
+    while (s->max_size > 0 && s->bufptr < s->bufcnt) {
+        qemu_chr_read(chr, &s->buf[s->bufptr], 1);
+        s->bufptr++;
+        s->max_size = qemu_chr_can_read(chr);
+    }
+}
+
+static void udp_chr_update_read_handler(CharDriverState *chr)
+{
+    NetCharDriver *s = chr->opaque;
+
+    if (s->fd >= 0) {
+        qemu_set_fd_handler2(s->fd, udp_chr_read_poll,
+                             udp_chr_read, NULL, chr);
+    }
+}
+
+int parse_host_port(SockAddress *saddr, const char *str);
+int parse_host_src_port(SockAddress *haddr,
+                        SockAddress *saddr,
+                        const char *str);
+#ifndef _WIN32
+static int parse_unix_path(SockAddress  *uaddr, const char*  str);
+#endif
+
+static CharDriverState *qemu_chr_open_udp(const char *def)
+{
+    CharDriverState *chr = NULL;
+    NetCharDriver *s = NULL;
+    int fd = -1;
+    SockAddress saddr;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        goto return_err;
+    s = qemu_mallocz(sizeof(NetCharDriver));
+    if (!s)
+        goto return_err;
+
+    fd = socket(PF_INET, SOCK_DGRAM, 0);
+    if (fd < 0) {
+        perror("socket(PF_INET, SOCK_DGRAM)");
+        goto return_err;
+    }
+
+    if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
+        printf("Could not parse: %s\n", def);
+        goto return_err;
+    }
+
+    if (socket_bind(fd, &saddr) < 0)
+    {
+        perror("bind");
+        goto return_err;
+    }
+
+    s->fd = fd;
+    s->bufcnt = 0;
+    s->bufptr = 0;
+    chr->opaque = s;
+    chr->chr_write = udp_chr_write;
+    chr->chr_update_read_handler = udp_chr_update_read_handler;
+    return chr;
+
+return_err:
+    if (chr)
+        free(chr);
+    if (s)
+        free(s);
+    if (fd >= 0)
+        closesocket(fd);
+    return NULL;
+}
+
+/***********************************************************/
+/* TCP Net console */
+
+typedef struct {
+    int fd, listen_fd;
+    int connected;
+    int max_size;
+    int do_telnetopt;
+    int do_nodelay;
+    int is_unix;
+} TCPCharDriver;
+
+static void tcp_chr_accept(void *opaque);
+
+static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
+{
+    TCPCharDriver *s = chr->opaque;
+    if (s->connected) {
+        return send_all(s->fd, buf, len);
+    } else {
+        /* XXX: indicate an error ? */
+        return len;
+    }
+}
+
+static int tcp_chr_read_poll(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+    if (!s->connected)
+        return 0;
+    s->max_size = qemu_chr_can_read(chr);
+    return s->max_size;
+}
+
+#define IAC 255
+#define IAC_BREAK 243
+static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
+                                      TCPCharDriver *s,
+                                      uint8_t *buf, int *size)
+{
+    /* Handle any telnet client's basic IAC options to satisfy char by
+     * char mode with no echo.  All IAC options will be removed from
+     * the buf and the do_telnetopt variable will be used to track the
+     * state of the width of the IAC information.
+     *
+     * IAC commands come in sets of 3 bytes with the exception of the
+     * "IAC BREAK" command and the double IAC.
+     */
+
+    int i;
+    int j = 0;
+
+    for (i = 0; i < *size; i++) {
+        if (s->do_telnetopt > 1) {
+            if ((unsigned char)buf[i] == IAC && s->do_telnetopt == 2) {
+                /* Double IAC means send an IAC */
+                if (j != i)
+                    buf[j] = buf[i];
+                j++;
+                s->do_telnetopt = 1;
+            } else {
+                if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) {
+                    /* Handle IAC break commands by sending a serial break */
+                    qemu_chr_event(chr, CHR_EVENT_BREAK);
+                    s->do_telnetopt++;
+                }
+                s->do_telnetopt++;
+            }
+            if (s->do_telnetopt >= 4) {
+                s->do_telnetopt = 1;
+            }
+        } else {
+            if ((unsigned char)buf[i] == IAC) {
+                s->do_telnetopt = 2;
+            } else {
+                if (j != i)
+                    buf[j] = buf[i];
+                j++;
+            }
+        }
+    }
+    *size = j;
+}
+
+static void tcp_chr_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+    uint8_t buf[1024];
+    int len, size;
+
+    if (!s->connected || s->max_size <= 0)
+        return;
+    len = sizeof(buf);
+    if (len > s->max_size)
+        len = s->max_size;
+    size = socket_recv(s->fd, buf, len);
+    if (size == 0) {
+        /* connection closed */
+        s->connected = 0;
+        if (s->listen_fd >= 0) {
+            qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+        }
+        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+        socket_close(s->fd);
+        s->fd = -1;
+    } else if (size > 0) {
+        if (s->do_telnetopt)
+            tcp_chr_process_IAC_bytes(chr, s, buf, &size);
+        if (size > 0)
+            qemu_chr_read(chr, buf, size);
+    }
+}
+
+static void tcp_chr_connect(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+
+    s->connected = 1;
+    qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
+                         tcp_chr_read, NULL, chr);
+    qemu_chr_reset(chr);
+}
+
+#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
+static void tcp_chr_telnet_init(int fd)
+{
+    char buf[3];
+    /* Send the telnet negotion to put telnet in binary, no echo, single char mode */
+    IACSET(buf, 0xff, 0xfb, 0x01);  /* IAC WILL ECHO */
+    socket_send(fd, (char *)buf, 3);
+    IACSET(buf, 0xff, 0xfb, 0x03);  /* IAC WILL Suppress go ahead */
+    socket_send(fd, (char *)buf, 3);
+    IACSET(buf, 0xff, 0xfb, 0x00);  /* IAC WILL Binary */
+    socket_send(fd, (char *)buf, 3);
+    IACSET(buf, 0xff, 0xfd, 0x00);  /* IAC DO Binary */
+    socket_send(fd, (char *)buf, 3);
+}
+
+static void tcp_chr_accept(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    TCPCharDriver *s = chr->opaque;
+    int fd;
+
+    for(;;) {
+        fd = socket_accept(s->listen_fd, NULL);
+        if (fd < 0) {
+            return;
+        } else if (fd >= 0) {
+            if (s->do_telnetopt)
+                tcp_chr_telnet_init(fd);
+            break;
+        }
+    }
+    socket_set_nonblock(fd);
+    if (s->do_nodelay)
+        socket_set_nodelay(fd);
+    s->fd = fd;
+    qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL);
+    tcp_chr_connect(chr);
+}
+
+static void tcp_chr_close(CharDriverState *chr)
+{
+    TCPCharDriver *s = chr->opaque;
+    if (s->fd >= 0)
+        closesocket(s->fd);
+    if (s->listen_fd >= 0)
+        closesocket(s->listen_fd);
+    qemu_free(s);
+}
+
+static CharDriverState *qemu_chr_open_tcp(const char *host_str,
+                                          int is_telnet,
+					  int is_unix)
+{
+    CharDriverState *chr = NULL;
+    TCPCharDriver *s = NULL;
+    int fd = -1, ret, err;
+    int is_listen = 0;
+    int is_waitconnect = 1;
+    int do_nodelay = 0;
+    const char *ptr;
+    SockAddress saddr;
+
+#ifndef _WIN32
+    if (is_unix) {
+	if (parse_unix_path(&saddr, host_str) < 0)
+	    goto fail;
+    } else
+#endif
+    {
+	if (parse_host_port(&saddr, host_str) < 0)
+	    goto fail;
+    }
+
+    ptr = host_str;
+    while((ptr = strchr(ptr,','))) {
+        ptr++;
+        if (!strncmp(ptr,"server",6)) {
+            is_listen = 1;
+        } else if (!strncmp(ptr,"nowait",6)) {
+            is_waitconnect = 0;
+        } else if (!strncmp(ptr,"nodelay",6)) {
+            do_nodelay = 1;
+        } else {
+            printf("Unknown option: %s\n", ptr);
+            goto fail;
+        }
+    }
+    if (!is_listen)
+        is_waitconnect = 0;
+
+    chr = qemu_mallocz(sizeof(CharDriverState));
+    if (!chr)
+        goto fail;
+    s = qemu_mallocz(sizeof(TCPCharDriver));
+    if (!s)
+        goto fail;
+
+#ifndef _WIN32
+    if (is_unix)
+        fd = socket_create( SOCKET_UNIX, SOCKET_STREAM );
+    else
+#endif
+        fd = socket_create_inet( SOCKET_STREAM );
+
+    if (fd < 0)
+        goto fail;
+
+    if (!is_waitconnect)
+        socket_set_nonblock(fd);
+
+    s->connected = 0;
+    s->fd = -1;
+    s->listen_fd = -1;
+    s->is_unix = is_unix;
+    s->do_nodelay = do_nodelay && !is_unix;
+
+    chr->opaque = s;
+    chr->chr_write = tcp_chr_write;
+    chr->chr_close = tcp_chr_close;
+
+    if (is_listen) {
+        /* allow fast reuse */
+#ifndef _WIN32
+    if (is_unix) {
+        unlink( sock_address_get_path(&saddr) );
+    } else
+#endif
+        socket_set_xreuseaddr(fd);
+
+        if (socket_bind(fd, &saddr) < 0 ||
+            socket_listen(fd, 0) < 0)
+            goto fail;
+
+        s->listen_fd = fd;
+        qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr);
+        if (is_telnet)
+            s->do_telnetopt = 1;
+    } else {
+        for(;;) {
+            ret = socket_connect(fd, &saddr);
+            if (ret < 0) {
+                err = errno;
+                if (err == EINTR || err == EWOULDBLOCK) {
+                } else if (err == EINPROGRESS) {
+                    break;
+#ifdef _WIN32
+                } else if (err == EALREADY) {
+                    break;
+#endif
+                } else {
+                    goto fail;
+                }
+            } else {
+                s->connected = 1;
+                break;
+            }
+        }
+        s->fd = fd;
+        socket_set_nodelay(fd);
+        if (s->connected)
+            tcp_chr_connect(chr);
+        else
+            qemu_set_fd_handler(s->fd, NULL, tcp_chr_connect, chr);
+    }
+
+    if (is_listen && is_waitconnect) {
+        printf("QEMU waiting for connection on: %s\n", host_str);
+        tcp_chr_accept(chr);
+        socket_set_nonblock(s->listen_fd);
+    }
+
+    return chr;
+ fail:
+    if (fd >= 0)
+        socket_close(fd);
+    qemu_free(s);
+    qemu_free(chr);
+    return NULL;
+}
+
+CharDriverState *qemu_chr_open(const char *filename)
+{
+    const char *p;
+
+    if (!strcmp(filename, "vc")) {
+        return text_console_init(&display_state, 0);
+    } else if (strstart(filename, "vc:", &p)) {
+        return text_console_init(&display_state, p);
+    } else if (!strcmp(filename, "null")) {
+        return qemu_chr_open_null();
+    } else
+    if (strstart(filename, "tcp:", &p)) {
+        return qemu_chr_open_tcp(p, 0, 0);
+    } else
+    if (strstart(filename, "telnet:", &p)) {
+        return qemu_chr_open_tcp(p, 1, 0);
+    } else
+    if (strstart(filename, "udp:", &p)) {
+        return qemu_chr_open_udp(p);
+    } else
+    if (strstart(filename, "mon:", &p)) {
+        CharDriverState *drv = qemu_chr_open(p);
+        if (drv) {
+            drv = qemu_chr_open_mux(drv);
+            monitor_init(drv, !nographic);
+            return drv;
+        }
+        printf("Unable to open driver: %s\n", p);
+        return 0;
+    } else
+#ifndef _WIN32
+    if (strstart(filename, "unix:", &p)) {
+	return qemu_chr_open_tcp(p, 0, 1);
+    } else if (strstart(filename, "file:", &p)) {
+        return qemu_chr_open_file_out(p);
+    } else if (strstart(filename, "pipe:", &p)) {
+        return qemu_chr_open_pipe(p);
+    } else if (!strcmp(filename, "pty")) {
+        return qemu_chr_open_pty();
+    } else if (!strcmp(filename, "stdio")) {
+        return qemu_chr_open_stdio();
+    } else if (strstart(filename, "fdpair:", &p)) {
+        return qemu_chr_open_fdpair(p);
+    } else
+#endif
+#if defined(__linux__)
+    if (strstart(filename, "/dev/parport", NULL)) {
+        return qemu_chr_open_pp(filename);
+    } else
+#endif
+#ifndef _WIN32
+    if (strstart(filename, "/dev/", NULL)) {
+        return qemu_chr_open_tty(filename);
+    } else
+#endif
+    if (!strcmp(filename, "android-modem")) {
+        CharDriverState*  cs;
+        qemu_chr_open_charpipe( &cs, &android_modem_cs );
+        return cs;
+    } else if (!strcmp(filename, "android-gps")) {
+        CharDriverState*  cs;
+        qemu_chr_open_charpipe( &cs, &android_gps_cs );
+        return cs;
+    } else if (!strcmp(filename, "android-kmsg")) {
+        return android_kmsg_get_cs();
+    } else if (!strcmp(filename, "android-qemud")) {
+        return android_qemud_get_cs();
+    } else
+#if defined(__linux__)
+    if (strstart(filename, "/dev/parport", NULL)) {
+        return qemu_chr_open_pp(filename);
+    } else
+#endif
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+    || defined(__NetBSD__) || defined(__OpenBSD__)
+    if (strstart(filename, "/dev/", NULL)) {
+        return qemu_chr_open_tty(filename);
+    } else
+#endif
+#ifdef _WIN32
+    if (strstart(filename, "COM", NULL)) {
+        return qemu_chr_open_win(filename);
+    } else
+    if (strstart(filename, "pipe:", &p)) {
+        return qemu_chr_open_win_pipe(p);
+    } else
+    if (strstart(filename, "con:", NULL)) {
+        return qemu_chr_open_win_con(filename);
+    } else
+    if (strstart(filename, "file:", &p)) {
+        return qemu_chr_open_win_file_out(p);
+    } else
+#endif
+#ifdef CONFIG_BRLAPI
+    if (!strcmp(filename, "braille")) {
+        return chr_baum_init();
+    } else
+#endif
+    {
+        return NULL;
+    }
+}
+
+void qemu_chr_close(CharDriverState *chr)
+{
+    if (chr->chr_close)
+        chr->chr_close(chr);
+    qemu_free(chr);
+}
+
+/***********************************************************/
+/* network device redirectors */
+
+__attribute__ (( unused ))
+static void hex_dump(FILE *f, const uint8_t *buf, int size)
+{
+    int len, i, j, c;
+
+    for(i=0;i<size;i+=16) {
+        len = size - i;
+        if (len > 16)
+            len = 16;
+        fprintf(f, "%08x ", i);
+        for(j=0;j<16;j++) {
+            if (j < len)
+                fprintf(f, " %02x", buf[i+j]);
+            else
+                fprintf(f, "   ");
+        }
+        fprintf(f, " ");
+        for(j=0;j<len;j++) {
+            c = buf[i+j];
+            if (c < ' ' || c > '~')
+                c = '.';
+            fprintf(f, "%c", c);
+        }
+        fprintf(f, "\n");
+    }
+}
+
+static int parse_macaddr(uint8_t *macaddr, const char *p)
+{
+    int i;
+    char *last_char;
+    long int offset;
+
+    errno = 0;
+    offset = strtol(p, &last_char, 0);    
+    if (0 == errno && '\0' == *last_char &&
+            offset >= 0 && offset <= 0xFFFFFF) {
+        macaddr[3] = (offset & 0xFF0000) >> 16;
+        macaddr[4] = (offset & 0xFF00) >> 8;
+        macaddr[5] = offset & 0xFF;
+        return 0;
+    } else {
+        for(i = 0; i < 6; i++) {
+            macaddr[i] = strtol(p, (char **)&p, 16);
+            if (i == 5) {
+                if (*p != '\0')
+                    return -1;
+            } else {
+                if (*p != ':' && *p != '-')
+                    return -1;
+                p++;
+            }
+        }
+        return 0;    
+    }
+
+    return -1;
+}
+
+static int get_str_sep(char *buf, int buf_size, const char **pp, int sep)
+{
+    const char *p, *p1;
+    int len;
+    p = *pp;
+    p1 = strchr(p, sep);
+    if (!p1)
+        return -1;
+    len = p1 - p;
+    p1++;
+    if (buf_size > 0) {
+        if (len > buf_size - 1)
+            len = buf_size - 1;
+        memcpy(buf, p, len);
+        buf[len] = '\0';
+    }
+    *pp = p1;
+    return 0;
+}
+
+int parse_host_src_port(SockAddress *haddr,
+                        SockAddress *saddr,
+                        const char *input_str)
+{
+    char *str = strdup(input_str);
+    char *host_str = str;
+    char *src_str;
+    const char *src_str2;
+    char *ptr;
+
+    /*
+     * Chop off any extra arguments at the end of the string which
+     * would start with a comma, then fill in the src port information
+     * if it was provided else use the "any address" and "any port".
+     */
+    if ((ptr = strchr(str,',')))
+        *ptr = '\0';
+
+    if ((src_str = strchr(input_str,'@'))) {
+        *src_str = '\0';
+        src_str++;
+    }
+
+    if (parse_host_port(haddr, host_str) < 0)
+        goto fail;
+
+    src_str2 = src_str;
+    if (!src_str || *src_str == '\0')
+        src_str2 = ":0";
+
+    if (parse_host_port(saddr, src_str2) < 0)
+        goto fail;
+
+    free(str);
+    return(0);
+
+fail:
+    free(str);
+    return -1;
+}
+
+int parse_host_port(SockAddress  *saddr, const char *str)
+{
+    char buf[512];
+    const char *p, *r;
+    uint16_t  port;
+
+    p = str;
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        return -1;
+
+    port = strtol(p, (char **)&r, 0);
+    if (r == p)
+        return -1;
+
+    if (buf[0] == '\0') {
+        sock_address_init_inet( saddr, SOCK_ADDRESS_INET_ANY, port );
+    } else {
+        if (sock_address_init_resolve( saddr, buf, port, 0 ) < 0)
+            return -1;
+    }
+    return 0;
+}
+
+#ifndef _WIN32
+static int
+parse_unix_path(SockAddress*  uaddr, const char *str)
+{
+    char  temp[109];
+    const char *p;
+    int len;
+
+    len = MIN(108, strlen(str));
+    p = strchr(str, ',');
+    if (p)
+        len = MIN(len, p - str);
+
+    memcpy(temp, str, len);
+    temp[len] = 0;
+
+    sock_address_init_unix( uaddr, temp );
+    return 0;
+}
+#endif
+
+/* find or alloc a new VLAN */
+VLANState *qemu_find_vlan(int id)
+{
+    VLANState **pvlan, *vlan;
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+        if (vlan->id == id)
+            return vlan;
+    }
+    vlan = qemu_mallocz(sizeof(VLANState));
+    if (!vlan)
+        return NULL;
+    vlan->id = id;
+    vlan->next = NULL;
+    pvlan = &first_vlan;
+    while (*pvlan != NULL)
+        pvlan = &(*pvlan)->next;
+    *pvlan = vlan;
+    return vlan;
+}
+
+VLANClientState *qemu_new_vlan_client(VLANState *vlan,
+                                      IOReadHandler *fd_read,
+                                      IOCanRWHandler *fd_can_read,
+                                      void *opaque)
+{
+    VLANClientState *vc, **pvc;
+    vc = qemu_mallocz(sizeof(VLANClientState));
+    if (!vc)
+        return NULL;
+    vc->fd_read = fd_read;
+    vc->fd_can_read = fd_can_read;
+    vc->opaque = opaque;
+    vc->vlan = vlan;
+
+    vc->next = NULL;
+    pvc = &vlan->first_client;
+    while (*pvc != NULL)
+        pvc = &(*pvc)->next;
+    *pvc = vc;
+    return vc;
+}
+
+void qemu_del_vlan_client(VLANClientState *vc)
+{
+    VLANClientState **pvc = &vc->vlan->first_client;
+
+    while (*pvc != NULL)
+        if (*pvc == vc) {
+            *pvc = vc->next;
+            free(vc);
+            break;
+        } else
+            pvc = &(*pvc)->next;
+}
+
+int qemu_can_send_packet(VLANClientState *vc1)
+{
+    VLANState *vlan = vc1->vlan;
+    VLANClientState *vc;
+
+    for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+        if (vc != vc1) {
+            if (vc->fd_can_read && vc->fd_can_read(vc->opaque))
+                return 1;
+        }
+    }
+    return 0;
+}
+
+void qemu_send_packet(VLANClientState *vc1, const uint8_t *buf, int size)
+{
+    VLANState *vlan = vc1->vlan;
+    VLANClientState *vc;
+
+#if 0
+    printf("vlan %d send:\n", vlan->id);
+    hex_dump(stdout, buf, size);
+#endif
+    for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+        if (vc != vc1) {
+            vc->fd_read(vc->opaque, buf, size);
+        }
+    }
+}
+
+#if defined(CONFIG_SLIRP)
+
+/* slirp network adapter */
+
+int slirp_inited;
+static VLANClientState *slirp_vc;
+
+double   qemu_net_upload_speed   = 0.;
+double   qemu_net_download_speed = 0.;
+int      qemu_net_min_latency = 0;
+int      qemu_net_max_latency = 0;
+int      qemu_net_disable = 0;
+
+int
+ip_packet_is_internal( const uint8_t*  data, size_t  size )
+{
+    const uint8_t*  end = data + size;
+
+    /* must have room for Mac + IP header */
+    if (data + 40 > end)
+        return 0;
+
+    if (data[12] != 0x08 || data[13] != 0x00 )
+        return 0;
+
+    /* must have valid IP header */
+    data += 14;
+    if ((data[0] >> 4) != 4 || (data[0] & 15) < 5)
+        return 0;
+
+    /* internal if both source and dest addresses are in 10.x.x.x */
+    return ( data[12] == 10 && data[16] == 10);
+}
+
+#ifdef CONFIG_SHAPER
+
+/* see http://en.wikipedia.org/wiki/List_of_device_bandwidths or a complete list */
+const NetworkSpeed  android_netspeeds[] = {
+    { "gsm", "GSM/CSD", 14400, 14400 },
+    { "hscsd", "HSCSD", 14400, 43200 },
+    { "gprs", "GPRS", 40000, 80000 },
+    { "edge", "EDGE/EGPRS", 118400, 236800 },
+    { "umts", "UMTS/3G", 128000, 1920000 },
+    { "hsdpa", "HSDPA", 348000, 14400000 },
+    { "full", "no limit", 0, 0 },
+    { NULL, NULL, 0, 0 }
+};
+
+const NetworkLatency  android_netdelays[] = {
+    /* FIXME: these numbers are totally imaginary */
+    { "gprs", "GPRS", 150, 550 },
+    { "edge", "EDGE/EGPRS", 80, 400 },
+    { "umts", "UMTS/3G", 35, 200 },
+    { "none", "no latency", 0, 0 },
+    { NULL, NULL, 0, 0 }
+};
+
+
+NetShaper  slirp_shaper_in;
+NetShaper  slirp_shaper_out;
+NetDelay   slirp_delay_in;
+
+static void
+slirp_delay_in_cb( void*   data,
+                   size_t  size,
+                   void*   opaque )
+{
+    slirp_input( (const uint8_t*)data, (int)size );
+    opaque = opaque;
+}
+
+static void
+slirp_shaper_in_cb( void*   data,
+                    size_t  size,
+                    void*   opaque )
+{
+    netdelay_send_aux( slirp_delay_in, data, size, opaque );
+}
+
+static void
+slirp_shaper_out_cb( void*   data,
+                     size_t  size,
+                     void*   opaque )
+{
+    qemu_send_packet( slirp_vc, (const uint8_t*)data, (int)size );
+}
+
+void
+slirp_init_shapers( void )
+{
+    slirp_delay_in   = netdelay_create( slirp_delay_in_cb );
+    slirp_shaper_in  = netshaper_create( 1, slirp_shaper_in_cb );
+    slirp_shaper_out = netshaper_create( 1, slirp_shaper_out_cb );
+
+    netdelay_set_latency( slirp_delay_in, qemu_net_min_latency, qemu_net_max_latency );
+    netshaper_set_rate( slirp_shaper_out, qemu_net_download_speed );
+    netshaper_set_rate( slirp_shaper_in,  qemu_net_upload_speed  );
+}
+
+#endif /* CONFIG_SHAPER */
+
+int slirp_can_output(void)
+{
+#ifdef CONFIG_SHAPER
+    return !slirp_vc                                ||
+           ( netshaper_can_send( slirp_shaper_out ) &&
+             qemu_can_send_packet(slirp_vc) );
+#else
+    return !slirp_vc || qemu_can_send_packet(slirp_vc);
+#endif
+}
+
+
+
+void slirp_output(const uint8_t *pkt, int pkt_len)
+{
+#if 0
+    printf("slirp output:\n");
+    hex_dump(stdout, pkt, pkt_len);
+#endif
+    if (!slirp_vc)
+        return;
+
+    if (qemu_tcpdump_active)
+        qemu_tcpdump_packet(pkt, pkt_len);
+
+    /* always send internal packets */
+    if ( ip_packet_is_internal( pkt, pkt_len ) ) {
+        qemu_send_packet( slirp_vc, pkt, pkt_len );
+        return;
+    }
+
+    if ( qemu_net_disable )
+        return;
+
+#ifdef CONFIG_SHAPER
+    netshaper_send( slirp_shaper_out, (void*)pkt, pkt_len );
+#else
+    qemu_send_packet(slirp_vc, pkt, pkt_len);
+#endif
+}
+
+static void slirp_receive(void *opaque, const uint8_t *buf, int size)
+{
+#if 0
+    printf("slirp input:\n");
+    hex_dump(stdout, buf, size);
+#endif
+    if (qemu_tcpdump_active)
+        qemu_tcpdump_packet(buf, size);
+
+    if ( ip_packet_is_internal( buf, size ) ) {
+        slirp_input(buf, size);
+        return;
+    }
+
+    if (qemu_net_disable)
+        return;
+
+#ifdef CONFIG_SHAPER
+    netshaper_send( slirp_shaper_in, (char*)buf, size );
+#else
+    slirp_input(buf, size);
+#endif
+}
+
+static int net_slirp_init(VLANState *vlan)
+{
+    if (!slirp_inited) {
+        slirp_inited = 1;
+        slirp_init();
+    }
+    slirp_vc = qemu_new_vlan_client(vlan,
+                                    slirp_receive, NULL, NULL);
+    snprintf(slirp_vc->info_str, sizeof(slirp_vc->info_str), "user redirector");
+    return 0;
+}
+
+static void net_slirp_redir(const char *redir_str)
+{
+    int is_udp;
+    char buf[256], *r;
+    const char *p;
+    uint32_t  guest_ip;
+    int host_port, guest_port;
+
+    if (!slirp_inited) {
+        slirp_inited = 1;
+        slirp_init();
+    }
+
+    p = redir_str;
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        goto fail;
+    if (!strcmp(buf, "tcp")) {
+        is_udp = 0;
+    } else if (!strcmp(buf, "udp")) {
+        is_udp = 1;
+    } else {
+        goto fail;
+    }
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        goto fail;
+    host_port = strtol(buf, &r, 0);
+    if (r == buf)
+        goto fail;
+
+    if (get_str_sep(buf, sizeof(buf), &p, ':') < 0)
+        goto fail;
+    if (buf[0] == '\0') {
+        pstrcpy(buf, sizeof(buf), "10.0.2.15");
+    }
+    if (inet_strtoip(buf, &guest_ip) < 0)
+        goto fail;
+
+    guest_port = strtol(p, &r, 0);
+    if (r == p)
+        goto fail;
+
+    if (slirp_redir(is_udp, host_port, guest_ip, guest_port) < 0) {
+        fprintf(stderr, "qemu: could not set up redirection\n");
+        exit(1);
+    }
+    return;
+ fail:
+    fprintf(stderr, "qemu: syntax: -redir [tcp|udp]:host-port:[guest-host]:guest-port\n");
+    exit(1);
+}
+
+#if 0  /* ANDROID disabled */
+
+char smb_dir[1024];
+
+static void erase_dir(char *dir_name)
+{
+    DIR *d;
+    struct dirent *de;
+    char filename[1024];
+
+    /* erase all the files in the directory */
+    if ((d = opendir(dir_name)) != 0) {
+        for(;;) {
+            de = readdir(d);
+            if (!de)
+                break;
+            if (strcmp(de->d_name, ".") != 0 &&
+                strcmp(de->d_name, "..") != 0) {
+                snprintf(filename, sizeof(filename), "%s/%s",
+                         smb_dir, de->d_name);
+                if (unlink(filename) != 0)  /* is it a directory? */
+                    erase_dir(filename);
+            }
+        }
+        closedir(d);
+        rmdir(dir_name);
+    }
+}
+
+/* automatic user mode samba server configuration */
+static void smb_exit(void)
+{
+    erase_dir(smb_dir);
+}
+
+/* automatic user mode samba server configuration */
+static void net_slirp_smb(const char *exported_dir)
+{
+    char smb_conf[1024];
+    char smb_cmdline[1024];
+    FILE *f;
+
+    if (!slirp_inited) {
+        slirp_inited = 1;
+        slirp_init();
+    }
+
+    /* XXX: better tmp dir construction */
+    snprintf(smb_dir, sizeof(smb_dir), "/tmp/qemu-smb.%d", getpid());
+    if (mkdir(smb_dir, 0700) < 0) {
+        fprintf(stderr, "qemu: could not create samba server dir '%s'\n", smb_dir);
+        exit(1);
+    }
+    snprintf(smb_conf, sizeof(smb_conf), "%s/%s", smb_dir, "smb.conf");
+
+    f = fopen(smb_conf, "w");
+    if (!f) {
+        fprintf(stderr, "qemu: could not create samba server configuration file '%s'\n", smb_conf);
+        exit(1);
+    }
+    fprintf(f,
+            "[global]\n"
+            "private dir=%s\n"
+            "smb ports=0\n"
+            "socket address=127.0.0.1\n"
+            "pid directory=%s\n"
+            "lock directory=%s\n"
+            "log file=%s/log.smbd\n"
+            "smb passwd file=%s/smbpasswd\n"
+            "security = share\n"
+            "[qemu]\n"
+            "path=%s\n"
+            "read only=no\n"
+            "guest ok=yes\n",
+            smb_dir,
+            smb_dir,
+            smb_dir,
+            smb_dir,
+            smb_dir,
+            exported_dir
+            );
+    fclose(f);
+    atexit(smb_exit);
+
+    snprintf(smb_cmdline, sizeof(smb_cmdline), "%s -s %s",
+             SMBD_COMMAND, smb_conf);
+
+    slirp_add_exec(0, smb_cmdline, 4, 139);
+}
+
+#endif /* !defined(_WIN32) */
+
+#endif /* CONFIG_SLIRP */
+
+#if !defined(_WIN32)
+
+typedef struct TAPState {
+    VLANClientState *vc;
+    int fd;
+    char down_script[1024];
+} TAPState;
+
+static void tap_receive(void *opaque, const uint8_t *buf, int size)
+{
+    TAPState *s = opaque;
+    int ret;
+    for(;;) {
+        ret = write(s->fd, buf, size);
+        if (ret < 0 && (errno == EINTR || errno == EAGAIN)) {
+        } else {
+            break;
+        }
+    }
+}
+
+static void tap_send(void *opaque)
+{
+    TAPState *s = opaque;
+    uint8_t buf[4096];
+    int size;
+
+#ifdef __sun__
+    struct strbuf sbuf;
+    int f = 0;
+    sbuf.maxlen = sizeof(buf);
+    sbuf.buf = buf;
+    size = getmsg(s->fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
+#else
+    size = read(s->fd, buf, sizeof(buf));
+#endif
+    if (size > 0) {
+        qemu_send_packet(s->vc, buf, size);
+    }
+}
+
+/* fd support */
+
+static TAPState *net_tap_fd_init(VLANState *vlan, int fd)
+{
+    TAPState *s;
+
+    s = qemu_mallocz(sizeof(TAPState));
+    if (!s)
+        return NULL;
+    s->fd = fd;
+    s->vc = qemu_new_vlan_client(vlan, tap_receive, NULL, s);
+    qemu_set_fd_handler(s->fd, tap_send, NULL, s);
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str), "tap: fd=%d", fd);
+    return s;
+}
+
+#if defined (_BSD) || defined (__FreeBSD_kernel__)
+static int tap_open(char *ifname, int ifname_size)
+{
+    int fd;
+    char *dev;
+    struct stat s;
+
+    TFR(fd = open("/dev/tap", O_RDWR));
+    if (fd < 0) {
+        fprintf(stderr, "warning: could not open /dev/tap: no virtual network emulation\n");
+        return -1;
+    }
+
+    fstat(fd, &s);
+    dev = devname(s.st_rdev, S_IFCHR);
+    pstrcpy(ifname, ifname_size, dev);
+
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+    return fd;
+}
+#elif defined(__sun__)
+#define TUNNEWPPA       (('T'<<16) | 0x0001)
+/*
+ * Allocate TAP device, returns opened fd.
+ * Stores dev name in the first arg(must be large enough).
+ */
+int tap_alloc(char *dev, size_t dev_size)
+{
+    int tap_fd, if_fd, ppa = -1;
+    static int ip_fd = 0;
+    char *ptr;
+
+    static int arp_fd = 0;
+    int ip_muxid, arp_muxid;
+    struct strioctl  strioc_if, strioc_ppa;
+    int link_type = I_PLINK;;
+    struct lifreq ifr;
+    char actual_name[32] = "";
+
+    memset(&ifr, 0x0, sizeof(ifr));
+
+    if( *dev ){
+       ptr = dev;
+       while( *ptr && !isdigit((int)*ptr) ) ptr++;
+       ppa = atoi(ptr);
+    }
+
+    /* Check if IP device was opened */
+    if( ip_fd )
+       close(ip_fd);
+
+    TFR(ip_fd = open("/dev/udp", O_RDWR, 0));
+    if (ip_fd < 0) {
+       syslog(LOG_ERR, "Can't open /dev/ip (actually /dev/udp)");
+       return -1;
+    }
+
+    TFR(tap_fd = open("/dev/tap", O_RDWR, 0));
+    if (tap_fd < 0) {
+       syslog(LOG_ERR, "Can't open /dev/tap");
+       return -1;
+    }
+
+    /* Assign a new PPA and get its unit number. */
+    strioc_ppa.ic_cmd = TUNNEWPPA;
+    strioc_ppa.ic_timout = 0;
+    strioc_ppa.ic_len = sizeof(ppa);
+    strioc_ppa.ic_dp = (char *)&ppa;
+    if ((ppa = ioctl (tap_fd, I_STR, &strioc_ppa)) < 0)
+       syslog (LOG_ERR, "Can't assign new interface");
+
+    TFR(if_fd = open("/dev/tap", O_RDWR, 0));
+    if (if_fd < 0) {
+       syslog(LOG_ERR, "Can't open /dev/tap (2)");
+       return -1;
+    }
+    if(ioctl(if_fd, I_PUSH, "ip") < 0){
+       syslog(LOG_ERR, "Can't push IP module");
+       return -1;
+    }
+
+    if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) < 0)
+	syslog(LOG_ERR, "Can't get flags\n");
+
+    snprintf (actual_name, 32, "tap%d", ppa);
+    strncpy (ifr.lifr_name, actual_name, sizeof (ifr.lifr_name));
+
+    ifr.lifr_ppa = ppa;
+    /* Assign ppa according to the unit number returned by tun device */
+
+    if (ioctl (if_fd, SIOCSLIFNAME, &ifr) < 0)
+        syslog (LOG_ERR, "Can't set PPA %d", ppa);
+    if (ioctl(if_fd, SIOCGLIFFLAGS, &ifr) <0)
+        syslog (LOG_ERR, "Can't get flags\n");
+    /* Push arp module to if_fd */
+    if (ioctl (if_fd, I_PUSH, "arp") < 0)
+        syslog (LOG_ERR, "Can't push ARP module (2)");
+
+    /* Push arp module to ip_fd */
+    if (ioctl (ip_fd, I_POP, NULL) < 0)
+        syslog (LOG_ERR, "I_POP failed\n");
+    if (ioctl (ip_fd, I_PUSH, "arp") < 0)
+        syslog (LOG_ERR, "Can't push ARP module (3)\n");
+    /* Open arp_fd */
+    TFR(arp_fd = open ("/dev/tap", O_RDWR, 0));
+    if (arp_fd < 0)
+       syslog (LOG_ERR, "Can't open %s\n", "/dev/tap");
+
+    /* Set ifname to arp */
+    strioc_if.ic_cmd = SIOCSLIFNAME;
+    strioc_if.ic_timout = 0;
+    strioc_if.ic_len = sizeof(ifr);
+    strioc_if.ic_dp = (char *)&ifr;
+    if (ioctl(arp_fd, I_STR, &strioc_if) < 0){
+        syslog (LOG_ERR, "Can't set ifname to arp\n");
+    }
+
+    if((ip_muxid = ioctl(ip_fd, I_LINK, if_fd)) < 0){
+       syslog(LOG_ERR, "Can't link TAP device to IP");
+       return -1;
+    }
+
+    if ((arp_muxid = ioctl (ip_fd, link_type, arp_fd)) < 0)
+        syslog (LOG_ERR, "Can't link TAP device to ARP");
+
+    close (if_fd);
+
+    memset(&ifr, 0x0, sizeof(ifr));
+    strncpy (ifr.lifr_name, actual_name, sizeof (ifr.lifr_name));
+    ifr.lifr_ip_muxid  = ip_muxid;
+    ifr.lifr_arp_muxid = arp_muxid;
+
+    if (ioctl (ip_fd, SIOCSLIFMUXID, &ifr) < 0)
+    {
+      ioctl (ip_fd, I_PUNLINK , arp_muxid);
+      ioctl (ip_fd, I_PUNLINK, ip_muxid);
+      syslog (LOG_ERR, "Can't set multiplexor id");
+    }
+
+    snprintf(dev, dev_size, "tap%d", ppa);
+    return tap_fd;
+}
+
+static int tap_open(char *ifname, int ifname_size)
+{
+    char  dev[10]="";
+    int fd;
+    if( (fd = tap_alloc(dev, sizeof(dev))) < 0 ){
+       fprintf(stderr, "Cannot allocate TAP device\n");
+       return -1;
+    }
+    pstrcpy(ifname, ifname_size, dev);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+    return fd;
+}
+#else
+static int tap_open(char *ifname, int ifname_size)
+{
+    struct ifreq ifr;
+    int fd, ret;
+
+    TFR(fd = open("/dev/net/tun", O_RDWR));
+    if (fd < 0) {
+        fprintf(stderr, "warning: could not open /dev/net/tun: no virtual network emulation\n");
+        return -1;
+    }
+    memset(&ifr, 0, sizeof(ifr));
+    ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+    if (ifname[0] != '\0')
+        pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
+    else
+        pstrcpy(ifr.ifr_name, IFNAMSIZ, "tap%d");
+    ret = ioctl(fd, TUNSETIFF, (void *) &ifr);
+    if (ret != 0) {
+        fprintf(stderr, "warning: could not configure /dev/net/tun: no virtual network emulation\n");
+        close(fd);
+        return -1;
+    }
+    pstrcpy(ifname, ifname_size, ifr.ifr_name);
+    fcntl(fd, F_SETFL, O_NONBLOCK);
+    return fd;
+}
+#endif
+
+static int launch_script(const char *setup_script, const char *ifname, int fd)
+{
+    int pid, status;
+    char *args[3];
+    char **parg;
+
+        /* try to launch network script */
+        pid = fork();
+        if (pid >= 0) {
+            if (pid == 0) {
+                int open_max = sysconf (_SC_OPEN_MAX), i;
+                for (i = 0; i < open_max; i++)
+                    if (i != STDIN_FILENO &&
+                        i != STDOUT_FILENO &&
+                        i != STDERR_FILENO &&
+                        i != fd)
+                        close(i);
+
+                parg = args;
+                *parg++ = (char *)setup_script;
+                *parg++ = (char *)ifname;
+                *parg++ = NULL;
+                execv(setup_script, args);
+                _exit(1);
+            }
+            while (waitpid(pid, &status, 0) != pid);
+            if (!WIFEXITED(status) ||
+                WEXITSTATUS(status) != 0) {
+                fprintf(stderr, "%s: could not launch network script\n",
+                        setup_script);
+                return -1;
+            }
+        }
+    return 0;
+}
+
+static int net_tap_init(VLANState *vlan, const char *ifname1,
+                        const char *setup_script, const char *down_script)
+{
+    TAPState *s;
+    int fd;
+    char ifname[128];
+
+    if (ifname1 != NULL)
+        pstrcpy(ifname, sizeof(ifname), ifname1);
+    else
+        ifname[0] = '\0';
+    TFR(fd = tap_open(ifname, sizeof(ifname)));
+    if (fd < 0)
+        return -1;
+
+    if (!setup_script || !strcmp(setup_script, "no"))
+        setup_script = "";
+    if (setup_script[0] != '\0') {
+	if (launch_script(setup_script, ifname, fd))
+	    return -1;
+    }
+    s = net_tap_fd_init(vlan, fd);
+    if (!s)
+        return -1;
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+             "tap: ifname=%s setup_script=%s", ifname, setup_script);
+    if (down_script && strcmp(down_script, "no"))
+        snprintf(s->down_script, sizeof(s->down_script), "%s", down_script);
+    return 0;
+}
+
+#endif /* !_WIN32 */
+
+#if defined(CONFIG_VDE)
+typedef struct VDEState {
+    VLANClientState *vc;
+    VDECONN *vde;
+} VDEState;
+
+static void vde_to_qemu(void *opaque)
+{
+    VDEState *s = opaque;
+    uint8_t buf[4096];
+    int size;
+
+    size = vde_recv(s->vde, buf, sizeof(buf), 0);
+    if (size > 0) {
+        qemu_send_packet(s->vc, buf, size);
+    }
+}
+
+static void vde_from_qemu(void *opaque, const uint8_t *buf, int size)
+{
+    VDEState *s = opaque;
+    int ret;
+    for(;;) {
+        ret = vde_send(s->vde, buf, size, 0);
+        if (ret < 0 && errno == EINTR) {
+        } else {
+            break;
+        }
+    }
+}
+
+static int net_vde_init(VLANState *vlan, const char *sock, int port,
+                        const char *group, int mode)
+{
+    VDEState *s;
+    char *init_group = strlen(group) ? (char *)group : NULL;
+    char *init_sock = strlen(sock) ? (char *)sock : NULL;
+
+    struct vde_open_args args = {
+        .port = port,
+        .group = init_group,
+        .mode = mode,
+    };
+
+    s = qemu_mallocz(sizeof(VDEState));
+    if (!s)
+        return -1;
+    s->vde = vde_open(init_sock, "QEMU", &args);
+    if (!s->vde){
+        free(s);
+        return -1;
+    }
+    s->vc = qemu_new_vlan_client(vlan, vde_from_qemu, NULL, s);
+    qemu_set_fd_handler(vde_datafd(s->vde), vde_to_qemu, NULL, s);
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str), "vde: sock=%s fd=%d",
+             sock, vde_datafd(s->vde));
+    return 0;
+}
+#endif
+
+/* network connection */
+typedef struct NetSocketState {
+    VLANClientState *vc;
+    int fd;
+    int state; /* 0 = getting length, 1 = getting data */
+    int index;
+    int packet_len;
+    uint8_t buf[4096];
+    SockAddress dgram_dst; /* contains inet host and port destination iff connectionless (SOCK_DGRAM) */
+} NetSocketState;
+
+typedef struct NetSocketListenState {
+    VLANState *vlan;
+    int fd;
+} NetSocketListenState;
+
+/* XXX: we consider we can send the whole packet without blocking */
+static void net_socket_receive(void *opaque, const uint8_t *buf, int size)
+{
+    NetSocketState *s = opaque;
+    uint32_t len;
+    len = htonl(size);
+
+    send_all(s->fd, (const uint8_t *)&len, sizeof(len));
+    send_all(s->fd, buf, size);
+}
+
+static void net_socket_receive_dgram(void *opaque, const uint8_t *buf, int size)
+{
+    NetSocketState *s = opaque;
+    socket_sendto(s->fd, buf, size, &s->dgram_dst);
+}
+
+static void net_socket_send(void *opaque)
+{
+    NetSocketState *s = opaque;
+    int l, size, err;
+    uint8_t buf1[4096];
+    const uint8_t *buf;
+
+    size = socket_recv(s->fd, buf1, sizeof(buf1));
+    if (size < 0) {
+        err = errno;
+        if (err != EWOULDBLOCK)
+            goto eoc;
+    } else if (size == 0) {
+        /* end of connection */
+    eoc:
+        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+        socket_close(s->fd);
+        return;
+    }
+    buf = buf1;
+    while (size > 0) {
+        /* reassemble a packet from the network */
+        switch(s->state) {
+        case 0:
+            l = 4 - s->index;
+            if (l > size)
+                l = size;
+            memcpy(s->buf + s->index, buf, l);
+            buf += l;
+            size -= l;
+            s->index += l;
+            if (s->index == 4) {
+                /* got length */
+                s->packet_len = ntohl(*(uint32_t *)s->buf);
+                s->index = 0;
+                s->state = 1;
+            }
+            break;
+        case 1:
+            l = s->packet_len - s->index;
+            if (l > size)
+                l = size;
+            memcpy(s->buf + s->index, buf, l);
+            s->index += l;
+            buf += l;
+            size -= l;
+            if (s->index >= s->packet_len) {
+                qemu_send_packet(s->vc, s->buf, s->packet_len);
+                s->index = 0;
+                s->state = 0;
+            }
+            break;
+        }
+    }
+}
+
+static void net_socket_send_dgram(void *opaque)
+{
+    NetSocketState *s = opaque;
+    int size;
+
+    size = socket_recv(s->fd, s->buf, sizeof(s->buf));
+    if (size < 0)
+        return;
+    if (size == 0) {
+        /* end of connection */
+        qemu_set_fd_handler(s->fd, NULL, NULL, NULL);
+        return;
+    }
+    qemu_send_packet(s->vc, s->buf, size);
+}
+
+static int net_socket_mcast_create(SockAddress*  mcastaddr)
+{
+    uint32_t   mcast_ip = (uint32_t) sock_address_get_ip(mcastaddr);
+
+    int fd, ret;
+
+    if (!IN_MULTICAST(mcast_ip)) {
+        fprintf(stderr, "qemu: error: specified mcastaddr \"%s\" does not contain a multicast address\n",
+                sock_address_to_string(mcastaddr));
+        return -1;
+
+    }
+    fd = socket_create_inet( SOCKET_DGRAM );
+    if (fd < 0) {
+        perror("socket(PF_INET, SOCK_DGRAM)");
+        return -1;
+    }
+
+#if 0
+    val = 1;
+    ret=setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+                   (const char *)&val, sizeof(val));
+#else
+    ret=socket_set_xreuseaddr(fd);
+#endif
+    if (ret < 0) {
+	perror("setsockopt(SOL_SOCKET, SO_REUSEADDR)");
+	goto fail;
+    }
+
+    if (socket_bind(fd, mcastaddr) < 0) {
+        perror("bind");
+        goto fail;
+    }
+
+    /* Add host to multicast group */
+    if (socket_mcast_inet_add_membership(fd, mcast_ip) < 0) {
+        perror("setsockopt(IP_ADD_MEMBERSHIP)");
+        goto fail;
+    }
+
+    /* Force mcast msgs to loopback (eg. several QEMUs in same host */
+    if (socket_mcast_inet_set_loop(fd, 1) < 0) {
+        perror("setsockopt(SOL_IP, IP_MULTICAST_LOOP)");
+        goto fail;
+    }
+
+    socket_set_nonblock(fd);
+    return fd;
+fail:
+    if (fd >= 0)
+        closesocket(fd);
+    return -1;
+}
+
+static NetSocketState *net_socket_fd_init_dgram(VLANState *vlan, int fd,
+                                          int is_connected)
+{
+    SockAddress  saddr;
+    int newfd;
+    NetSocketState *s;
+
+    /* fd passed: multicast: "learn" dgram_dst address from bound address and save it
+     * Because this may be "shared" socket from a "master" process, datagrams would be recv()
+     * by ONLY ONE process: we must "clone" this dgram socket --jjo
+     */
+
+    if (is_connected) {
+	if (socket_get_address(fd, &saddr) == 0) {
+	    /* must be bound */
+	    if (sock_address_get_ip(&saddr) == 0) {
+		fprintf(stderr, "qemu: error: init_dgram: fd=%d unbound, cannot setup multicast dst addr\n",
+			fd);
+		return NULL;
+	    }
+	    /* clone dgram socket */
+	    newfd = net_socket_mcast_create(&saddr);
+	    if (newfd < 0) {
+		/* error already reported by net_socket_mcast_create() */
+		close(fd);
+		return NULL;
+	    }
+	    /* clone newfd to fd, close newfd */
+	    dup2(newfd, fd);
+	    close(newfd);
+
+	} else {
+	    fprintf(stderr, "qemu: error: init_dgram: fd=%d failed getsockname(): %s\n",
+		    fd, errno_str);
+	    return NULL;
+	}
+    }
+
+    s = qemu_mallocz(sizeof(NetSocketState));
+    if (!s)
+        return NULL;
+    s->fd = fd;
+
+    s->vc = qemu_new_vlan_client(vlan, net_socket_receive_dgram, NULL, s);
+    qemu_set_fd_handler(s->fd, net_socket_send_dgram, NULL, s);
+
+    /* mcast: save bound address as dst */
+    if (is_connected) s->dgram_dst=saddr;
+
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+	    "socket: fd=%d (%s mcast=%s)",
+	    fd, is_connected? "cloned" : "",
+	    sock_address_to_string(&saddr));
+    return s;
+}
+
+static void net_socket_connect(void *opaque)
+{
+    NetSocketState *s = opaque;
+    qemu_set_fd_handler(s->fd, net_socket_send, NULL, s);
+}
+
+static NetSocketState *net_socket_fd_init_stream(VLANState *vlan, int fd,
+                                          int is_connected)
+{
+    NetSocketState *s;
+    s = qemu_mallocz(sizeof(NetSocketState));
+    if (!s)
+        return NULL;
+    s->fd = fd;
+    s->vc = qemu_new_vlan_client(vlan,
+                                 net_socket_receive, NULL, s);
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+             "socket: fd=%d", fd);
+    if (is_connected) {
+        net_socket_connect(s);
+    } else {
+        qemu_set_fd_handler(s->fd, NULL, net_socket_connect, s);
+    }
+    return s;
+}
+
+static NetSocketState *net_socket_fd_init(VLANState *vlan, int fd,
+                                          int is_connected)
+{
+    SocketType  so_type;
+
+    so_type = socket_get_type(fd);
+    switch(so_type) {
+    case SOCKET_DGRAM:
+        return net_socket_fd_init_dgram(vlan, fd, is_connected);
+    case SOCKET_STREAM:
+        return net_socket_fd_init_stream(vlan, fd, is_connected);
+    default:
+        /* who knows ... this could be a eg. a pty, do warn and continue as stream */
+        fprintf(stderr, "qemu: warning: socket type=%d for fd=%d is not SOCK_DGRAM or SOCK_STREAM\n", so_type, fd);
+        return net_socket_fd_init_stream(vlan, fd, is_connected);
+    }
+    return NULL;
+}
+
+static void net_socket_accept(void *opaque)
+{
+    NetSocketListenState *s = opaque;
+    NetSocketState *s1;
+    SockAddress   saddr;
+    int fd;
+
+    fd = socket_accept(s->fd, &saddr);
+    if (fd < 0)
+        return;
+
+    s1 = net_socket_fd_init(s->vlan, fd, 1);
+    if (!s1) {
+        closesocket(fd);
+    } else {
+        snprintf(s1->vc->info_str, sizeof(s1->vc->info_str),
+                 "socket: connection from %s",
+                 sock_address_to_string(&saddr));
+    }
+    sock_address_done(&saddr);
+}
+
+static int net_socket_listen_init(VLANState *vlan, const char *host_str)
+{
+    NetSocketListenState *s;
+    int fd, ret;
+    SockAddress saddr;
+
+    if (parse_host_port(&saddr, host_str) < 0)
+        return -1;
+
+    s = qemu_mallocz(sizeof(NetSocketListenState));
+    if (!s)
+        return -1;
+
+    fd = socket_create_inet( SOCKET_STREAM );
+    if (fd < 0) {
+        perror("socket");
+        return -1;
+    }
+    socket_set_nonblock(fd);
+#if 0
+    /* allow fast reuse */
+    val = 1;
+    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val));
+#else
+    socket_set_xreuseaddr(fd);
+#endif
+
+    ret = socket_bind(fd, &saddr);
+    if (ret < 0) {
+        perror("bind");
+        socket_close(fd);
+        return -1;
+    }
+    ret = socket_listen(fd, 0);
+    if (ret < 0) {
+        perror("listen");
+        socket_close(fd);
+        return -1;
+    }
+    s->vlan = vlan;
+    s->fd   = fd;
+    qemu_set_fd_handler(fd, net_socket_accept, NULL, s);
+    return 0;
+}
+
+static int net_socket_connect_init(VLANState *vlan, const char *host_str)
+{
+    NetSocketState *s;
+    int fd, connected, ret, err;
+    SockAddress  saddr;
+
+    if (parse_host_port(&saddr, host_str) < 0)
+        return -1;
+
+    fd = socket_create_inet( SOCKET_STREAM );
+    if (fd < 0) {
+        perror("socket");
+        return -1;
+    }
+    socket_set_nonblock(fd);
+
+    connected = 0;
+    for(;;) {
+        ret = socket_connect(fd, &saddr);
+        if (ret < 0) {
+            err = errno;
+            if (err == EINTR || err == EWOULDBLOCK) {
+            } else if (err == EINPROGRESS) {
+                break;
+#ifdef _WIN32
+            } else if (err == EALREADY) {
+                break;
+#endif
+            } else {
+                perror("connect");
+                socket_close(fd);
+                return -1;
+            }
+        } else {
+            connected = 1;
+            break;
+        }
+    }
+    s = net_socket_fd_init(vlan, fd, connected);
+    if (!s)
+        return -1;
+
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+             "socket: connect to %s", sock_address_to_string(&saddr));
+    return 0;
+}
+
+static int net_socket_mcast_init(VLANState *vlan, const char *host_str)
+{
+    NetSocketState *s;
+    int fd;
+    SockAddress  saddr;
+
+    if (parse_host_port(&saddr, host_str) < 0)
+        return -1;
+
+
+    fd = net_socket_mcast_create(&saddr);
+    if (fd < 0)
+	return -1;
+
+    s = net_socket_fd_init(vlan, fd, 0);
+    if (!s)
+        return -1;
+
+    s->dgram_dst = saddr;
+
+    snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+             "socket: mcast=%s", sock_address_to_string(&saddr));
+    return 0;
+
+}
+
+static const char *get_opt_name(char *buf, int buf_size, const char *p)
+{
+    char *q;
+
+    q = buf;
+    while (*p != '\0' && *p != '=') {
+        if (q && (q - buf) < buf_size - 1)
+            *q++ = *p;
+        p++;
+    }
+    if (q)
+        *q = '\0';
+
+    return p;
+}
+
+static const char *get_opt_value(char *buf, int buf_size, const char *p)
+{
+    char *q;
+
+    q = buf;
+    while (*p != '\0') {
+        if (*p == ',') {
+            if (*(p + 1) != ',')
+                break;
+            p++;
+        }
+        if (q && (q - buf) < buf_size - 1)
+            *q++ = *p;
+        p++;
+    }
+    if (q)
+        *q = '\0';
+
+    return p;
+}
+
+static int get_param_value(char *buf, int buf_size,
+                           const char *tag, const char *str)
+{
+    const char *p;
+    char option[128];
+
+    p = str;
+    for(;;) {
+        p = get_opt_name(option, sizeof(option), p);
+        if (*p != '=')
+            break;
+        p++;
+        if (!strcmp(tag, option)) {
+            (void)get_opt_value(buf, buf_size, p);
+            return strlen(buf);
+        } else {
+            p = get_opt_value(NULL, 0, p);
+        }
+        if (*p != ',')
+            break;
+        p++;
+    }
+    return 0;
+}
+
+static int check_params(char *buf, int buf_size,
+                        const char * const *params, const char *str)
+{
+    const char *p;
+    int i;
+
+    p = str;
+    for(;;) {
+        p = get_opt_name(buf, buf_size, p);
+        if (*p != '=')
+            return -1;
+        p++;
+        for(i = 0; params[i] != NULL; i++)
+            if (!strcmp(params[i], buf))
+                break;
+        if (params[i] == NULL)
+            return -1;
+        p = get_opt_value(NULL, 0, p);
+        if (*p != ',')
+            break;
+        p++;
+    }
+    return 0;
+}
+
+static int net_client_init(const char *device, const char *p)
+{
+    char buf[1024];
+    int vlan_id, ret;
+    VLANState *vlan;
+
+    vlan_id = 0;
+    if (get_param_value(buf, sizeof(buf), "vlan", p)) {
+        vlan_id = strtol(buf, NULL, 0);
+    }
+    vlan = qemu_find_vlan(vlan_id);
+    if (!vlan) {
+        fprintf(stderr, "Could not create vlan %d\n", vlan_id);
+        return -1;
+    }
+    if (!strcmp(device, "nic")) {
+        NICInfo *nd;
+        uint8_t *macaddr;
+
+        if (nb_nics >= MAX_NICS) {
+            fprintf(stderr, "Too Many NICs\n");
+            return -1;
+        }
+        nd = &nd_table[nb_nics];
+        macaddr = nd->macaddr;
+        macaddr[0] = 0x52;
+        macaddr[1] = 0x54;
+        macaddr[2] = 0x00;
+        macaddr[3] = 0x12;
+        macaddr[4] = 0x34;
+        macaddr[5] = 0x56 + nb_nics;
+
+        if (get_param_value(buf, sizeof(buf), "macaddr", p)) {
+            if (parse_macaddr(macaddr, buf) < 0) {
+                fprintf(stderr, "invalid syntax for ethernet address\n");
+                return -1;
+            }
+        }
+        if (get_param_value(buf, sizeof(buf), "model", p)) {
+            nd->model = strdup(buf);
+        }
+        nd->vlan = vlan;
+        nb_nics++;
+        vlan->nb_guest_devs++;
+        ret = 0;
+    } else
+    if (!strcmp(device, "none")) {
+        /* does nothing. It is needed to signal that no network cards
+           are wanted */
+#if 1 /* ANDROID */
+        fprintf(stderr, "sorry, you need to enable the network to use the Android emulator\n");
+        return -1;
+#else
+        ret = 0;
+#endif
+    } else
+#ifdef CONFIG_SLIRP
+    if (!strcmp(device, "user")) {
+        if (get_param_value(buf, sizeof(buf), "hostname", p)) {
+            pstrcpy(slirp_hostname, sizeof(slirp_hostname), buf);
+        }
+        vlan->nb_host_devs++;
+        ret = net_slirp_init(vlan);
+    } else
+#endif
+#ifdef _WIN32
+#if 0
+    if (!strcmp(device, "tap")) {
+        char ifname[64];
+        if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+            fprintf(stderr, "tap: no interface name\n");
+            return -1;
+        }
+        vlan->nb_host_devs++;
+        ret = tap_win32_init(vlan, ifname);
+    } else
+#endif
+#else
+    if (!strcmp(device, "tap")) {
+        char ifname[64];
+        char setup_script[1024], down_script[1024];
+        int fd;
+        vlan->nb_host_devs++;
+        if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
+            fd = strtol(buf, NULL, 0);
+            socket_set_nonblock(fd);
+            ret = -1;
+            if (net_tap_fd_init(vlan, fd))
+                ret = 0;
+        } else {
+            if (get_param_value(ifname, sizeof(ifname), "ifname", p) <= 0) {
+                ifname[0] = '\0';
+            }
+            if (get_param_value(setup_script, sizeof(setup_script), "script", p) == 0) {
+                pstrcpy(setup_script, sizeof(setup_script), DEFAULT_NETWORK_SCRIPT);
+            }
+            if (get_param_value(down_script, sizeof(down_script), "downscript", p) == 0) {
+                pstrcpy(down_script, sizeof(down_script), DEFAULT_NETWORK_DOWN_SCRIPT);
+            }
+            ret = net_tap_init(vlan, ifname, setup_script, down_script);
+        }
+    } else
+#endif
+    if (!strcmp(device, "socket")) {
+        if (get_param_value(buf, sizeof(buf), "fd", p) > 0) {
+            int fd;
+            fd = strtol(buf, NULL, 0);
+            ret = -1;
+            if (net_socket_fd_init(vlan, fd, 1))
+                ret = 0;
+        } else if (get_param_value(buf, sizeof(buf), "listen", p) > 0) {
+            ret = net_socket_listen_init(vlan, buf);
+        } else if (get_param_value(buf, sizeof(buf), "connect", p) > 0) {
+            ret = net_socket_connect_init(vlan, buf);
+        } else if (get_param_value(buf, sizeof(buf), "mcast", p) > 0) {
+            ret = net_socket_mcast_init(vlan, buf);
+        } else {
+            fprintf(stderr, "Unknown socket options: %s\n", p);
+            return -1;
+        }
+        vlan->nb_host_devs++;
+    } else
+#ifdef CONFIG_VDE
+    if (!strcmp(device, "vde")) {
+        char vde_sock[1024], vde_group[512];
+	int vde_port, vde_mode;
+        vlan->nb_host_devs++;
+        if (get_param_value(vde_sock, sizeof(vde_sock), "sock", p) <= 0) {
+	    vde_sock[0] = '\0';
+	}
+	if (get_param_value(buf, sizeof(buf), "port", p) > 0) {
+	    vde_port = strtol(buf, NULL, 10);
+	} else {
+	    vde_port = 0;
+	}
+	if (get_param_value(vde_group, sizeof(vde_group), "group", p) <= 0) {
+	    vde_group[0] = '\0';
+	}
+	if (get_param_value(buf, sizeof(buf), "mode", p) > 0) {
+	    vde_mode = strtol(buf, NULL, 8);
+	} else {
+	    vde_mode = 0700;
+	}
+	ret = net_vde_init(vlan, vde_sock, vde_port, vde_group, vde_mode);
+    } else
+#endif
+    {
+        fprintf(stderr, "Unknown network device: %s\n", device);
+        return -1;
+    }
+    if (ret < 0) {
+        fprintf(stderr, "Could not initialize device '%s'\n", device);
+    }
+
+    return ret;
+}
+
+static int net_client_parse(const char *str)
+{
+    const char *p;
+    char *q;
+    char device[64];
+
+    p = str;
+    q = device;
+    while (*p != '\0' && *p != ',') {
+        if ((q - device) < sizeof(device) - 1)
+            *q++ = *p;
+        p++;
+    }
+    *q = '\0';
+    if (*p == ',')
+        p++;
+
+    return net_client_init(device, p);
+}
+
+void do_info_network(void)
+{
+    VLANState *vlan;
+    VLANClientState *vc;
+
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+        term_printf("VLAN %d devices:\n", vlan->id);
+        for(vc = vlan->first_client; vc != NULL; vc = vc->next)
+            term_printf("  %s\n", vc->info_str);
+    }
+}
+
+#define HD_ALIAS "index=%d,media=disk"
+#ifdef TARGET_PPC
+#define CDROM_ALIAS "index=1,media=cdrom"
+#else
+#define CDROM_ALIAS "index=2,media=cdrom"
+#endif
+#define FD_ALIAS "index=%d,if=floppy"
+#define PFLASH_ALIAS "if=pflash"
+#define MTD_ALIAS "if=mtd"
+#define SD_ALIAS "index=0,if=sd"
+
+static int drive_add(const char *file, const char *fmt, ...)
+{
+    va_list ap;
+
+    if (nb_drives_opt >= MAX_DRIVES) {
+        fprintf(stderr, "qemu: too many drives\n");
+        exit(1);
+    }
+
+    drives_opt[nb_drives_opt].file = file;
+    va_start(ap, fmt);
+    vsnprintf(drives_opt[nb_drives_opt].opt,
+              sizeof(drives_opt[0].opt), fmt, ap);
+    va_end(ap);
+
+    return nb_drives_opt++;
+}
+
+int drive_get_index(BlockInterfaceType type, int bus, int unit)
+{
+    int index;
+
+    /* seek interface, bus and unit */
+
+    for (index = 0; index < nb_drives; index++)
+        if (drives_table[index].type == type &&
+	    drives_table[index].bus == bus &&
+	    drives_table[index].unit == unit)
+        return index;
+
+    return -1;
+}
+
+int drive_get_max_bus(BlockInterfaceType type)
+{
+    int max_bus;
+    int index;
+
+    max_bus = -1;
+    for (index = 0; index < nb_drives; index++) {
+        if(drives_table[index].type == type &&
+           drives_table[index].bus > max_bus)
+            max_bus = drives_table[index].bus;
+    }
+    return max_bus;
+}
+
+static void bdrv_format_print(void *opaque, const char *name)
+{
+    fprintf(stderr, " %s", name);
+}
+
+static int drive_init(struct drive_opt *arg, int snapshot,
+                      QEMUMachine *machine)
+{
+    char buf[128];
+    char file[1024];
+    char devname[128];
+    const char *mediastr = "";
+    BlockInterfaceType type;
+    enum { MEDIA_DISK, MEDIA_CDROM } media;
+    int bus_id, unit_id;
+    int cyls, heads, secs, translation;
+    BlockDriverState *bdrv;
+    BlockDriver *drv = NULL;
+    int max_devs;
+    int index;
+    int cache;
+    int bdrv_flags;
+    char *str = arg->opt;
+    static const char * const params[] = { "bus", "unit", "if", "index",
+                                           "cyls", "heads", "secs", "trans",
+                                           "media", "snapshot", "file",
+                                           "cache", "format", NULL };
+
+    if (check_params(buf, sizeof(buf), params, str) < 0) {
+         fprintf(stderr, "qemu: unknown parameter '%s' in '%s'\n",
+                         buf, str);
+         return -1;
+    }
+
+    file[0] = 0;
+    cyls = heads = secs = 0;
+    bus_id = 0;
+    unit_id = -1;
+    translation = BIOS_ATA_TRANSLATION_AUTO;
+    index = -1;
+    cache = 1;
+
+    if (!strcmp(machine->name, "realview") ||
+        !strcmp(machine->name, "SS-5") ||
+        !strcmp(machine->name, "SS-10") ||
+        !strcmp(machine->name, "SS-600MP") ||
+        !strcmp(machine->name, "versatilepb") ||
+        !strcmp(machine->name, "versatileab")) {
+        type = IF_SCSI;
+        max_devs = MAX_SCSI_DEVS;
+        pstrcpy(devname, sizeof(devname), "scsi");
+    } else {
+        type = IF_IDE;
+        max_devs = MAX_IDE_DEVS;
+        pstrcpy(devname, sizeof(devname), "ide");
+    }
+    media = MEDIA_DISK;
+
+    /* extract parameters */
+
+    if (get_param_value(buf, sizeof(buf), "bus", str)) {
+        bus_id = strtol(buf, NULL, 0);
+	if (bus_id < 0) {
+	    fprintf(stderr, "qemu: '%s' invalid bus id\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "unit", str)) {
+        unit_id = strtol(buf, NULL, 0);
+	if (unit_id < 0) {
+	    fprintf(stderr, "qemu: '%s' invalid unit id\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "if", str)) {
+        pstrcpy(devname, sizeof(devname), buf);
+        if (!strcmp(buf, "ide")) {
+	    type = IF_IDE;
+            max_devs = MAX_IDE_DEVS;
+        } else if (!strcmp(buf, "scsi")) {
+	    type = IF_SCSI;
+            max_devs = MAX_SCSI_DEVS;
+        } else if (!strcmp(buf, "floppy")) {
+	    type = IF_FLOPPY;
+            max_devs = 0;
+        } else if (!strcmp(buf, "pflash")) {
+	    type = IF_PFLASH;
+            max_devs = 0;
+	} else if (!strcmp(buf, "mtd")) {
+	    type = IF_MTD;
+            max_devs = 0;
+	} else if (!strcmp(buf, "sd")) {
+	    type = IF_SD;
+            max_devs = 0;
+	} else {
+            fprintf(stderr, "qemu: '%s' unsupported bus type '%s'\n", str, buf);
+            return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "index", str)) {
+        index = strtol(buf, NULL, 0);
+	if (index < 0) {
+	    fprintf(stderr, "qemu: '%s' invalid index\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "cyls", str)) {
+        cyls = strtol(buf, NULL, 0);
+    }
+
+    if (get_param_value(buf, sizeof(buf), "heads", str)) {
+        heads = strtol(buf, NULL, 0);
+    }
+
+    if (get_param_value(buf, sizeof(buf), "secs", str)) {
+        secs = strtol(buf, NULL, 0);
+    }
+
+    if (cyls || heads || secs) {
+        if (cyls < 1 || cyls > 16383) {
+            fprintf(stderr, "qemu: '%s' invalid physical cyls number\n", str);
+	    return -1;
+	}
+        if (heads < 1 || heads > 16) {
+            fprintf(stderr, "qemu: '%s' invalid physical heads number\n", str);
+	    return -1;
+	}
+        if (secs < 1 || secs > 63) {
+            fprintf(stderr, "qemu: '%s' invalid physical secs number\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "trans", str)) {
+        if (!cyls) {
+            fprintf(stderr,
+                    "qemu: '%s' trans must be used with cyls,heads and secs\n",
+                    str);
+            return -1;
+        }
+        if (!strcmp(buf, "none"))
+            translation = BIOS_ATA_TRANSLATION_NONE;
+        else if (!strcmp(buf, "lba"))
+            translation = BIOS_ATA_TRANSLATION_LBA;
+        else if (!strcmp(buf, "auto"))
+            translation = BIOS_ATA_TRANSLATION_AUTO;
+	else {
+            fprintf(stderr, "qemu: '%s' invalid translation type\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "media", str)) {
+        if (!strcmp(buf, "disk")) {
+	    media = MEDIA_DISK;
+	} else if (!strcmp(buf, "cdrom")) {
+            if (cyls || secs || heads) {
+                fprintf(stderr,
+                        "qemu: '%s' invalid physical CHS format\n", str);
+	        return -1;
+            }
+	    media = MEDIA_CDROM;
+	} else {
+	    fprintf(stderr, "qemu: '%s' invalid media\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "snapshot", str)) {
+        if (!strcmp(buf, "on"))
+	    snapshot = 1;
+        else if (!strcmp(buf, "off"))
+	    snapshot = 0;
+	else {
+	    fprintf(stderr, "qemu: '%s' invalid snapshot option\n", str);
+	    return -1;
+	}
+    }
+
+    if (get_param_value(buf, sizeof(buf), "cache", str)) {
+        if (!strcmp(buf, "off"))
+            cache = 0;
+        else if (!strcmp(buf, "on"))
+            cache = 1;
+        else {
+           fprintf(stderr, "qemu: invalid cache option\n");
+           return -1;
+        }
+    }
+
+    if (get_param_value(buf, sizeof(buf), "format", str)) {
+       if (strcmp(buf, "?") == 0) {
+            fprintf(stderr, "qemu: Supported formats:");
+            bdrv_iterate_format(bdrv_format_print, NULL);
+            fprintf(stderr, "\n");
+	    return -1;
+        }
+        drv = bdrv_find_format(buf);
+        if (!drv) {
+            fprintf(stderr, "qemu: '%s' invalid format\n", buf);
+            return -1;
+        }
+    }
+
+    if (arg->file == NULL)
+        get_param_value(file, sizeof(file), "file", str);
+    else
+        pstrcpy(file, sizeof(file), arg->file);
+
+    /* compute bus and unit according index */
+
+    if (index != -1) {
+        if (bus_id != 0 || unit_id != -1) {
+            fprintf(stderr,
+                    "qemu: '%s' index cannot be used with bus and unit\n", str);
+            return -1;
+        }
+        if (max_devs == 0)
+        {
+            unit_id = index;
+            bus_id = 0;
+        } else {
+            unit_id = index % max_devs;
+            bus_id = index / max_devs;
+        }
+    }
+
+    /* if user doesn't specify a unit_id,
+     * try to find the first free
+     */
+
+    if (unit_id == -1) {
+       unit_id = 0;
+       while (drive_get_index(type, bus_id, unit_id) != -1) {
+           unit_id++;
+           if (max_devs && unit_id >= max_devs) {
+               unit_id -= max_devs;
+               bus_id++;
+           }
+       }
+    }
+
+    /* check unit id */
+
+    if (max_devs && unit_id >= max_devs) {
+        fprintf(stderr, "qemu: '%s' unit %d too big (max is %d)\n",
+                        str, unit_id, max_devs - 1);
+        return -1;
+    }
+
+    /*
+     * ignore multiple definitions
+     */
+
+    if (drive_get_index(type, bus_id, unit_id) != -1)
+        return 0;
+
+    /* init */
+
+    if (type == IF_IDE || type == IF_SCSI)
+        mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd";
+    if (max_devs)
+        snprintf(buf, sizeof(buf), "%s%i%s%i",
+                 devname, bus_id, mediastr, unit_id);
+    else
+        snprintf(buf, sizeof(buf), "%s%s%i",
+                 devname, mediastr, unit_id);
+    bdrv = bdrv_new(buf);
+    drives_table[nb_drives].bdrv = bdrv;
+    drives_table[nb_drives].type = type;
+    drives_table[nb_drives].bus = bus_id;
+    drives_table[nb_drives].unit = unit_id;
+    nb_drives++;
+
+    switch(type) {
+    case IF_IDE:
+    case IF_SCSI:
+        switch(media) {
+	case MEDIA_DISK:
+            if (cyls != 0) {
+                bdrv_set_geometry_hint(bdrv, cyls, heads, secs);
+                bdrv_set_translation_hint(bdrv, translation);
+            }
+	    break;
+	case MEDIA_CDROM:
+            bdrv_set_type_hint(bdrv, BDRV_TYPE_CDROM);
+	    break;
+	}
+        break;
+    case IF_SD:
+        /* FIXME: This isn't really a floppy, but it's a reasonable
+           approximation.  */
+    case IF_FLOPPY:
+        bdrv_set_type_hint(bdrv, BDRV_TYPE_FLOPPY);
+        break;
+    case IF_PFLASH:
+    case IF_MTD:
+        break;
+    }
+    if (!file[0])
+        return 0;
+    bdrv_flags = 0;
+    if (snapshot)
+        bdrv_flags |= BDRV_O_SNAPSHOT;
+    if (!cache)
+        bdrv_flags |= BDRV_O_DIRECT;
+    if (bdrv_open2(bdrv, file, bdrv_flags, drv) < 0 || qemu_key_check(bdrv, file)) {
+        fprintf(stderr, "qemu: could not open disk image %s\n",
+                        file);
+        return -1;
+    }
+    return 0;
+}
+
+/***********************************************************/
+/* USB devices */
+
+static USBPort *used_usb_ports;
+static USBPort *free_usb_ports;
+
+/* ??? Maybe change this to register a hub to keep track of the topology.  */
+void qemu_register_usb_port(USBPort *port, void *opaque, int index,
+                            usb_attachfn attach)
+{
+    port->opaque = opaque;
+    port->index = index;
+    port->attach = attach;
+    port->next = free_usb_ports;
+    free_usb_ports = port;
+}
+
+int usb_device_add_dev(USBDevice *dev)
+{
+    USBPort *port;
+
+    /* Find a USB port to add the device to.  */
+    port = free_usb_ports;
+    if (!port->next) {
+        USBDevice *hub;
+
+        /* Create a new hub and chain it on.  */
+        free_usb_ports = NULL;
+        port->next = used_usb_ports;
+        used_usb_ports = port;
+
+        hub = usb_hub_init(VM_USB_HUB_SIZE);
+        usb_attach(port, hub);
+        port = free_usb_ports;
+    }
+
+    free_usb_ports = port->next;
+    port->next = used_usb_ports;
+    used_usb_ports = port;
+    usb_attach(port, dev);
+    return 0;
+}
+
+static int usb_device_add(const char *devname)
+{
+    const char *p;
+    USBDevice *dev;
+
+    if (!free_usb_ports)
+        return -1;
+
+    if (strstart(devname, "host:", &p)) {
+        dev = usb_host_device_open(p);
+    } else if (!strcmp(devname, "mouse")) {
+        dev = usb_mouse_init();
+    } else if (!strcmp(devname, "tablet")) {
+        dev = usb_tablet_init();
+    } else if (!strcmp(devname, "keyboard")) {
+        dev = usb_keyboard_init();
+    } else if (strstart(devname, "disk:", &p)) {
+        dev = usb_msd_init(p);
+#if 0
+    } else if (!strcmp(devname, "wacom-tablet")) {
+        dev = usb_wacom_init();
+    } else if (strstart(devname, "serial:", &p)) {
+        dev = usb_serial_init(p);
+#ifdef CONFIG_BRLAPI
+    } else if (!strcmp(devname, "braille")) {
+        dev = usb_baum_init();
+#endif
+    } else if (strstart(devname, "net:", &p)) {
+        int nic = nb_nics;
+
+        if (net_client_init("nic", p) < 0)
+            return -1;
+        nd_table[nic].model = "usb";
+        dev = usb_net_init(&nd_table[nic]);
+#endif
+    } else {
+        return -1;
+    }
+    if (!dev)
+        return -1;
+
+    return usb_device_add_dev(dev);
+}
+
+int usb_device_del_addr(int bus_num, int addr)
+{
+    USBPort *port;
+    USBPort **lastp;
+    USBDevice *dev;
+
+    if (!used_usb_ports)
+        return -1;
+
+    if (bus_num != 0)
+        return -1;
+
+    lastp = &used_usb_ports;
+    port = used_usb_ports;
+    while (port && port->dev->addr != addr) {
+        lastp = &port->next;
+        port = port->next;
+    }
+
+    if (!port)
+        return -1;
+
+    dev = port->dev;
+    *lastp = port->next;
+    usb_attach(port, NULL);
+    dev->handle_destroy(dev);
+    port->next = free_usb_ports;
+    free_usb_ports = port;
+    return 0;
+}
+
+static int usb_device_del(const char *devname)
+{
+    int bus_num, addr;
+    const char *p;
+
+    if (strstart(devname, "host:", &p))
+        return usb_host_device_close(p);
+
+    if (!used_usb_ports)
+        return -1;
+
+    p = strchr(devname, '.');
+    if (!p)
+        return -1;
+    bus_num = strtoul(devname, NULL, 0);
+    addr = strtoul(p + 1, NULL, 0);
+
+    return usb_device_del_addr(bus_num, addr);
+}
+
+void do_usb_add(const char *devname)
+{
+    usb_device_add(devname);
+}
+
+void do_usb_del(const char *devname)
+{
+    usb_device_del(devname);
+}
+
+void usb_info(void)
+{
+    USBDevice *dev;
+    USBPort *port;
+    const char *speed_str;
+
+    if (!usb_enabled) {
+        term_printf("USB support not enabled\n");
+        return;
+    }
+
+    for (port = used_usb_ports; port; port = port->next) {
+        dev = port->dev;
+        if (!dev)
+            continue;
+        switch(dev->speed) {
+        case USB_SPEED_LOW:
+            speed_str = "1.5";
+            break;
+        case USB_SPEED_FULL:
+            speed_str = "12";
+            break;
+        case USB_SPEED_HIGH:
+            speed_str = "480";
+            break;
+        default:
+            speed_str = "?";
+            break;
+        }
+        term_printf("  Device %d.%d, Speed %s Mb/s, Product %s\n",
+                    0, dev->addr, speed_str, dev->devname);
+    }
+}
+
+/***********************************************************/
+/* pid file */
+
+static char *pid_filename;
+
+/* Remove PID file. Called on normal exit */
+
+static void remove_pidfile(void)
+{
+    unlink (pid_filename);
+}
+
+static void create_pidfile(const char *filename)
+{
+    struct stat pidstat;
+    FILE *f;
+
+    /* Try to write our PID to the named file */
+    if (stat(filename, &pidstat) < 0) {
+        if (errno == ENOENT) {
+            if ((f = fopen (filename, "w")) == NULL) {
+                perror("Opening pidfile");
+                exit(1);
+            }
+            fprintf(f, "%d\n", getpid());
+            fclose(f);
+            pid_filename = qemu_strdup(filename);
+            if (!pid_filename) {
+                fprintf(stderr, "Could not save PID filename");
+                exit(1);
+            }
+            atexit(remove_pidfile);
+        }
+    } else {
+        fprintf(stderr, "%s already exists. Remove it and try again.\n",
+                filename);
+        exit(1);
+    }
+}
+
+/***********************************************************/
+/* dumb display */
+
+static void dumb_update(DisplayState *ds, int x, int y, int w, int h)
+{
+}
+
+static void dumb_resize(DisplayState *ds, int w, int h)
+{
+}
+
+static void dumb_refresh(DisplayState *ds)
+{
+#if defined(CONFIG_SDL)
+    vga_hw_update();
+#endif
+}
+
+static void dumb_display_init(DisplayState *ds)
+{
+    ds->data = NULL;
+    ds->linesize = 0;
+    ds->depth = 0;
+    ds->dpy_update = dumb_update;
+    ds->dpy_resize = dumb_resize;
+    ds->dpy_refresh = dumb_refresh;
+    ds->gui_timer_interval = 500;
+    ds->idle = 1;
+}
+
+/***********************************************************/
+/* I/O handling */
+
+#define MAX_IO_HANDLERS 64
+
+typedef struct IOHandlerRecord {
+    int fd;
+    IOCanRWHandler *fd_read_poll;
+    IOHandler *fd_read;
+    IOHandler *fd_write;
+    int deleted;
+    void *opaque;
+    /* temporary data */
+    struct pollfd *ufd;
+    struct IOHandlerRecord *next;
+} IOHandlerRecord;
+
+static IOHandlerRecord *first_io_handler;
+
+/* XXX: fd_read_poll should be suppressed, but an API change is
+   necessary in the character devices to suppress fd_can_read(). */
+int qemu_set_fd_handler2(int fd,
+                         IOCanRWHandler *fd_read_poll,
+                         IOHandler *fd_read,
+                         IOHandler *fd_write,
+                         void *opaque)
+{
+    IOHandlerRecord **pioh, *ioh;
+
+    if (!fd_read && !fd_write) {
+        pioh = &first_io_handler;
+        for(;;) {
+            ioh = *pioh;
+            if (ioh == NULL)
+                break;
+            if (ioh->fd == fd) {
+                ioh->deleted = 1;
+                break;
+            }
+            pioh = &ioh->next;
+        }
+    } else {
+        for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
+            if (ioh->fd == fd)
+                goto found;
+        }
+        ioh = qemu_mallocz(sizeof(IOHandlerRecord));
+        if (!ioh)
+            return -1;
+        ioh->next = first_io_handler;
+        first_io_handler = ioh;
+    found:
+        ioh->fd = fd;
+        ioh->fd_read_poll = fd_read_poll;
+        ioh->fd_read = fd_read;
+        ioh->fd_write = fd_write;
+        ioh->opaque = opaque;
+        ioh->deleted = 0;
+    }
+    return 0;
+}
+
+int qemu_set_fd_handler(int fd,
+                        IOHandler *fd_read,
+                        IOHandler *fd_write,
+                        void *opaque)
+{
+    return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque);
+}
+
+/***********************************************************/
+/* Polling handling */
+
+typedef struct PollingEntry {
+    PollingFunc *func;
+    void *opaque;
+    struct PollingEntry *next;
+} PollingEntry;
+
+static PollingEntry *first_polling_entry;
+
+int qemu_add_polling_cb(PollingFunc *func, void *opaque)
+{
+    PollingEntry **ppe, *pe;
+    pe = qemu_mallocz(sizeof(PollingEntry));
+    if (!pe)
+        return -1;
+    pe->func = func;
+    pe->opaque = opaque;
+    for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next);
+    *ppe = pe;
+    return 0;
+}
+
+void qemu_del_polling_cb(PollingFunc *func, void *opaque)
+{
+    PollingEntry **ppe, *pe;
+    for(ppe = &first_polling_entry; *ppe != NULL; ppe = &(*ppe)->next) {
+        pe = *ppe;
+        if (pe->func == func && pe->opaque == opaque) {
+            *ppe = pe->next;
+            qemu_free(pe);
+            break;
+        }
+    }
+}
+
+#ifdef _WIN32
+/***********************************************************/
+/* Wait objects support */
+typedef struct WaitObjects {
+    int num;
+    HANDLE events[MAXIMUM_WAIT_OBJECTS + 1];
+    WaitObjectFunc *func[MAXIMUM_WAIT_OBJECTS + 1];
+    void *opaque[MAXIMUM_WAIT_OBJECTS + 1];
+} WaitObjects;
+
+static WaitObjects wait_objects = {0};
+
+int qemu_add_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+    WaitObjects *w = &wait_objects;
+
+    if (w->num >= MAXIMUM_WAIT_OBJECTS)
+        return -1;
+    w->events[w->num] = handle;
+    w->func[w->num] = func;
+    w->opaque[w->num] = opaque;
+    w->num++;
+    return 0;
+}
+
+void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque)
+{
+    int i, found;
+    WaitObjects *w = &wait_objects;
+
+    found = 0;
+    for (i = 0; i < w->num; i++) {
+        if (w->events[i] == handle)
+            found = 1;
+        if (found) {
+            w->events[i] = w->events[i + 1];
+            w->func[i] = w->func[i + 1];
+            w->opaque[i] = w->opaque[i + 1];
+        }
+    }
+    if (found)
+        w->num--;
+}
+#endif
+
+/***********************************************************/
+/* savevm/loadvm support */
+
+#define IO_BUF_SIZE 32768
+
+struct QEMUFile {
+    FILE *outfile;
+    BlockDriverState *bs;
+    int is_file;
+    int is_writable;
+    int64_t base_offset;
+    int64_t buf_offset; /* start of buffer when writing, end of buffer
+                           when reading */
+    int buf_index;
+    int buf_size; /* 0 when writing */
+    uint8_t buf[IO_BUF_SIZE];
+};
+
+QEMUFile *qemu_fopen(const char *filename, const char *mode)
+{
+    QEMUFile *f;
+
+    f = qemu_mallocz(sizeof(QEMUFile));
+    if (!f)
+        return NULL;
+    if (!strcmp(mode, "wb")) {
+        f->is_writable = 1;
+    } else if (!strcmp(mode, "rb")) {
+        f->is_writable = 0;
+    } else {
+        goto fail;
+    }
+    f->outfile = fopen(filename, mode);
+    if (!f->outfile)
+        goto fail;
+    f->is_file = 1;
+    return f;
+ fail:
+    if (f->outfile)
+        fclose(f->outfile);
+    qemu_free(f);
+    return NULL;
+}
+
+static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable)
+{
+    QEMUFile *f;
+
+    f = qemu_mallocz(sizeof(QEMUFile));
+    if (!f)
+        return NULL;
+    f->is_file = 0;
+    f->bs = bs;
+    f->is_writable = is_writable;
+    f->base_offset = offset;
+    return f;
+}
+
+void qemu_fflush(QEMUFile *f)
+{
+    if (!f->is_writable)
+        return;
+    if (f->buf_index > 0) {
+        if (f->is_file) {
+            fseek(f->outfile, f->buf_offset, SEEK_SET);
+            fwrite(f->buf, 1, f->buf_index, f->outfile);
+        } else {
+            bdrv_pwrite(f->bs, f->base_offset + f->buf_offset,
+                        f->buf, f->buf_index);
+        }
+        f->buf_offset += f->buf_index;
+        f->buf_index = 0;
+    }
+}
+
+static void qemu_fill_buffer(QEMUFile *f)
+{
+    int len;
+
+    if (f->is_writable)
+        return;
+    if (f->is_file) {
+        fseek(f->outfile, f->buf_offset, SEEK_SET);
+        len = fread(f->buf, 1, IO_BUF_SIZE, f->outfile);
+        if (len < 0)
+            len = 0;
+    } else {
+        len = bdrv_pread(f->bs, f->base_offset + f->buf_offset,
+                         f->buf, IO_BUF_SIZE);
+        if (len < 0)
+            len = 0;
+    }
+    f->buf_index = 0;
+    f->buf_size = len;
+    f->buf_offset += len;
+}
+
+void qemu_fclose(QEMUFile *f)
+{
+    if (f->is_writable)
+        qemu_fflush(f);
+    if (f->is_file) {
+        fclose(f->outfile);
+    }
+    qemu_free(f);
+}
+
+void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size)
+{
+    int l;
+    while (size > 0) {
+        l = IO_BUF_SIZE - f->buf_index;
+        if (l > size)
+            l = size;
+        memcpy(f->buf + f->buf_index, buf, l);
+        f->buf_index += l;
+        buf += l;
+        size -= l;
+        if (f->buf_index >= IO_BUF_SIZE)
+            qemu_fflush(f);
+    }
+}
+
+void qemu_put_byte(QEMUFile *f, int v)
+{
+    f->buf[f->buf_index++] = v;
+    if (f->buf_index >= IO_BUF_SIZE)
+        qemu_fflush(f);
+}
+
+int qemu_get_buffer(QEMUFile *f, uint8_t *buf, int size1)
+{
+    int size, l;
+
+    size = size1;
+    while (size > 0) {
+        l = f->buf_size - f->buf_index;
+        if (l == 0) {
+            qemu_fill_buffer(f);
+            l = f->buf_size - f->buf_index;
+            if (l == 0)
+                break;
+        }
+        if (l > size)
+            l = size;
+        memcpy(buf, f->buf + f->buf_index, l);
+        f->buf_index += l;
+        buf += l;
+        size -= l;
+    }
+    return size1 - size;
+}
+
+int qemu_get_byte(QEMUFile *f)
+{
+    if (f->buf_index >= f->buf_size) {
+        qemu_fill_buffer(f);
+        if (f->buf_index >= f->buf_size)
+            return 0;
+    }
+    return f->buf[f->buf_index++];
+}
+
+int64_t qemu_ftell(QEMUFile *f)
+{
+    return f->buf_offset - f->buf_size + f->buf_index;
+}
+
+int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence)
+{
+    if (whence == SEEK_SET) {
+        /* nothing to do */
+    } else if (whence == SEEK_CUR) {
+        pos += qemu_ftell(f);
+    } else {
+        /* SEEK_END not supported */
+        return -1;
+    }
+    if (f->is_writable) {
+        qemu_fflush(f);
+        f->buf_offset = pos;
+    } else {
+        f->buf_offset = pos;
+        f->buf_index = 0;
+        f->buf_size = 0;
+    }
+    return pos;
+}
+
+void qemu_put_be16(QEMUFile *f, unsigned int v)
+{
+    qemu_put_byte(f, v >> 8);
+    qemu_put_byte(f, v);
+}
+
+void qemu_put_be32(QEMUFile *f, unsigned int v)
+{
+    qemu_put_byte(f, v >> 24);
+    qemu_put_byte(f, v >> 16);
+    qemu_put_byte(f, v >> 8);
+    qemu_put_byte(f, v);
+}
+
+void qemu_put_be64(QEMUFile *f, uint64_t v)
+{
+    qemu_put_be32(f, v >> 32);
+    qemu_put_be32(f, v);
+}
+
+unsigned int qemu_get_be16(QEMUFile *f)
+{
+    unsigned int v;
+    v = qemu_get_byte(f) << 8;
+    v |= qemu_get_byte(f);
+    return v;
+}
+
+unsigned int qemu_get_be32(QEMUFile *f)
+{
+    unsigned int v;
+    v = qemu_get_byte(f) << 24;
+    v |= qemu_get_byte(f) << 16;
+    v |= qemu_get_byte(f) << 8;
+    v |= qemu_get_byte(f);
+    return v;
+}
+
+uint64_t qemu_get_be64(QEMUFile *f)
+{
+    uint64_t v;
+    v = (uint64_t)qemu_get_be32(f) << 32;
+    v |= qemu_get_be32(f);
+    return v;
+}
+
+void  qemu_put_struct(QEMUFile*  f, const QField*  fields, const void*  s)
+{
+    const QField*  qf = fields;
+
+    for (;;) {
+        uint8_t*  p = (uint8_t*)s + qf->offset;
+
+        switch (qf->type) {
+            case Q_FIELD_END:
+                break;
+            case Q_FIELD_BYTE:
+                qemu_put_byte(f, p[0]);
+                break;
+            case Q_FIELD_INT16:
+                qemu_put_be16(f, ((uint16_t*)p)[0]);
+                break;
+            case Q_FIELD_INT32:
+                qemu_put_be32(f, ((uint32_t*)p)[0]);
+                break;
+            case Q_FIELD_INT64:
+                qemu_put_be64(f, ((uint64_t*)p)[0]);
+                break;
+            case Q_FIELD_BUFFER:
+                if (fields[1].type != Q_FIELD_BUFFER_SIZE ||
+                    fields[2].type != Q_FIELD_BUFFER_SIZE)
+                {
+                    fprintf(stderr, "%s: invalid QFIELD_BUFFER item passed as argument. aborting\n",
+                            __FUNCTION__ );
+                    exit(1);
+                }
+                else
+                {
+                    uint32_t  size = ((uint32_t)fields[1].offset << 16) | (uint32_t)fields[2].offset;
+
+                    qemu_put_buffer(f, p, size);
+                    qf += 2;
+                }
+                break;
+            default:
+                fprintf(stderr, "%s: invalid fields list passed as argument. aborting\n", __FUNCTION__);
+                exit(1);
+        }
+        qf++;
+    }
+}
+
+int   qemu_get_struct(QEMUFile*  f, const QField*  fields, void*  s)
+{
+    const QField*  qf = fields;
+
+    for (;;) {
+        uint8_t*  p = (uint8_t*)s + qf->offset;
+
+        switch (qf->type) {
+            case Q_FIELD_END:
+                break;
+            case Q_FIELD_BYTE:
+                p[0] = qemu_get_byte(f);
+                break;
+            case Q_FIELD_INT16:
+                ((uint16_t*)p)[0] = qemu_get_be16(f);
+                break;
+            case Q_FIELD_INT32:
+                ((uint32_t*)p)[0] = qemu_get_be32(f);
+                break;
+            case Q_FIELD_INT64:
+                ((uint64_t*)p)[0] = qemu_get_be64(f);
+                break;
+            case Q_FIELD_BUFFER:
+                if (fields[1].type != Q_FIELD_BUFFER_SIZE ||
+                    fields[2].type != Q_FIELD_BUFFER_SIZE)
+                {
+                    fprintf(stderr, "%s: invalid QFIELD_BUFFER item passed as argument.\n",
+                            __FUNCTION__ );
+                    return -1;
+                }
+                else
+                {
+                    uint32_t  size = ((uint32_t)fields[1].offset << 16) | (uint32_t)fields[2].offset;
+                    int       ret  = qemu_get_buffer(f, p, size);
+
+                    if (ret != size) {
+                        fprintf(stderr, "%s: not enough bytes to load structure\n", __FUNCTION__);
+                        return -1;
+                    }
+                    qf += 2;
+                }
+                break;
+            default:
+                fprintf(stderr, "%s: invalid fields list passed as argument. aborting\n", __FUNCTION__);
+                exit(1);
+        }
+        qf++;
+    }
+    return 0;
+}
+
+typedef struct SaveStateEntry {
+    char idstr[256];
+    int instance_id;
+    int version_id;
+    SaveStateHandler *save_state;
+    LoadStateHandler *load_state;
+    void *opaque;
+    struct SaveStateEntry *next;
+} SaveStateEntry;
+
+static SaveStateEntry *first_se;
+
+/* TODO: Individual devices generally have very little idea about the rest
+   of the system, so instance_id should be removed/replaced.
+   Meanwhile pass -1 as instance_id if you do not already have a clearly
+   distinguishing id for all instances of your device class. */
+int register_savevm(const char *idstr,
+                    int instance_id,
+                    int version_id,
+                    SaveStateHandler *save_state,
+                    LoadStateHandler *load_state,
+                    void *opaque)
+{
+    SaveStateEntry *se, **pse;
+
+    se = qemu_malloc(sizeof(SaveStateEntry));
+    if (!se)
+        return -1;
+    pstrcpy(se->idstr, sizeof(se->idstr), idstr);
+    se->instance_id = (instance_id == -1) ? 0 : instance_id;
+    se->version_id = version_id;
+    se->save_state = save_state;
+    se->load_state = load_state;
+    se->opaque = opaque;
+    se->next = NULL;
+
+    /* add at the end of list */
+    pse = &first_se;
+    while (*pse != NULL) {
+        if (instance_id == -1
+                && strcmp(se->idstr, (*pse)->idstr) == 0
+                && se->instance_id <= (*pse)->instance_id)
+            se->instance_id = (*pse)->instance_id + 1;
+        pse = &(*pse)->next;
+    }
+    *pse = se;
+    return 0;
+}
+
+#define QEMU_VM_FILE_MAGIC   0x5145564d
+#define QEMU_VM_FILE_VERSION 0x00000002
+
+static int qemu_savevm_state(QEMUFile *f)
+{
+    SaveStateEntry *se;
+    int len, ret;
+    int64_t cur_pos, len_pos, total_len_pos;
+
+    qemu_put_be32(f, QEMU_VM_FILE_MAGIC);
+    qemu_put_be32(f, QEMU_VM_FILE_VERSION);
+    total_len_pos = qemu_ftell(f);
+    qemu_put_be64(f, 0); /* total size */
+
+    for(se = first_se; se != NULL; se = se->next) {
+	if (se->save_state == NULL)
+	    /* this one has a loader only, for backwards compatibility */
+	    continue;
+
+        /* ID string */
+        len = strlen(se->idstr);
+        qemu_put_byte(f, len);
+        qemu_put_buffer(f, (uint8_t *)se->idstr, len);
+
+        qemu_put_be32(f, se->instance_id);
+        qemu_put_be32(f, se->version_id);
+
+        /* record size: filled later */
+        len_pos = qemu_ftell(f);
+        qemu_put_be32(f, 0);
+        se->save_state(f, se->opaque);
+
+        /* fill record size */
+        cur_pos = qemu_ftell(f);
+        len = cur_pos - len_pos - 4;
+        qemu_fseek(f, len_pos, SEEK_SET);
+        qemu_put_be32(f, len);
+        qemu_fseek(f, cur_pos, SEEK_SET);
+    }
+    cur_pos = qemu_ftell(f);
+    qemu_fseek(f, total_len_pos, SEEK_SET);
+    qemu_put_be64(f, cur_pos - total_len_pos - 8);
+    qemu_fseek(f, cur_pos, SEEK_SET);
+
+    ret = 0;
+    return ret;
+}
+
+static SaveStateEntry *find_se(const char *idstr, int instance_id)
+{
+    SaveStateEntry *se;
+
+    for(se = first_se; se != NULL; se = se->next) {
+        if (!strcmp(se->idstr, idstr) &&
+            instance_id == se->instance_id)
+            return se;
+    }
+    return NULL;
+}
+
+static int qemu_loadvm_state(QEMUFile *f)
+{
+    SaveStateEntry *se;
+    int len, ret, instance_id, record_len, version_id;
+    int64_t total_len, end_pos, cur_pos;
+    unsigned int v;
+    char idstr[256];
+
+    v = qemu_get_be32(f);
+    if (v != QEMU_VM_FILE_MAGIC)
+        goto fail;
+    v = qemu_get_be32(f);
+    if (v != QEMU_VM_FILE_VERSION) {
+    fail:
+        ret = -1;
+        goto the_end;
+    }
+    total_len = qemu_get_be64(f);
+    end_pos = total_len + qemu_ftell(f);
+    for(;;) {
+        if (qemu_ftell(f) >= end_pos)
+            break;
+        len = qemu_get_byte(f);
+        qemu_get_buffer(f, (uint8_t *)idstr, len);
+        idstr[len] = '\0';
+        instance_id = qemu_get_be32(f);
+        version_id = qemu_get_be32(f);
+        record_len = qemu_get_be32(f);
+#if 0
+        printf("idstr=%s instance=0x%x version=%d len=%d\n",
+               idstr, instance_id, version_id, record_len);
+#endif
+        cur_pos = qemu_ftell(f);
+        se = find_se(idstr, instance_id);
+        if (!se) {
+            fprintf(stderr, "qemu: warning: instance 0x%x of device '%s' not present in current VM\n",
+                    instance_id, idstr);
+        } else {
+            ret = se->load_state(f, se->opaque, version_id);
+            if (ret < 0) {
+                fprintf(stderr, "qemu: warning: error while loading state for instance 0x%x of device '%s'\n",
+                        instance_id, idstr);
+            }
+        }
+        /* always seek to exact end of record */
+        qemu_fseek(f, cur_pos + record_len, SEEK_SET);
+    }
+    ret = 0;
+ the_end:
+    return ret;
+}
+
+/* device can contain snapshots */
+static int bdrv_can_snapshot(BlockDriverState *bs)
+{
+    return (bs &&
+            !bdrv_is_removable(bs) &&
+            !bdrv_is_read_only(bs));
+}
+
+/* device must be snapshots in order to have a reliable snapshot */
+static int bdrv_has_snapshot(BlockDriverState *bs)
+{
+    return (bs &&
+            !bdrv_is_removable(bs) &&
+            !bdrv_is_read_only(bs));
+}
+
+static BlockDriverState *get_bs_snapshots(void)
+{
+    BlockDriverState *bs;
+    int i;
+
+    if (bs_snapshots)
+        return bs_snapshots;
+    for(i = 0; i <= nb_drives; i++) {
+        bs = drives_table[i].bdrv;
+        if (bdrv_can_snapshot(bs))
+            goto ok;
+    }
+    return NULL;
+ ok:
+    bs_snapshots = bs;
+    return bs;
+}
+
+static int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
+                              const char *name)
+{
+    QEMUSnapshotInfo *sn_tab, *sn;
+    int nb_sns, i, ret;
+
+    ret = -ENOENT;
+    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+    if (nb_sns < 0)
+        return ret;
+    for(i = 0; i < nb_sns; i++) {
+        sn = &sn_tab[i];
+        if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) {
+            *sn_info = *sn;
+            ret = 0;
+            break;
+        }
+    }
+    qemu_free(sn_tab);
+    return ret;
+}
+
+void do_savevm(const char *name)
+{
+    BlockDriverState *bs, *bs1;
+    QEMUSnapshotInfo sn1, *sn = &sn1, old_sn1, *old_sn = &old_sn1;
+    int must_delete, ret, i;
+    BlockDriverInfo bdi1, *bdi = &bdi1;
+    QEMUFile *f;
+    int saved_vm_running;
+#ifdef _WIN32
+    struct _timeb tb;
+#else
+    struct timeval tv;
+#endif
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No block device can accept snapshots\n");
+        return;
+    }
+
+    /* ??? Should this occur after vm_stop?  */
+    qemu_aio_flush();
+
+    saved_vm_running = vm_running;
+    vm_stop(0);
+
+    must_delete = 0;
+    if (name) {
+        ret = bdrv_snapshot_find(bs, old_sn, name);
+        if (ret >= 0) {
+            must_delete = 1;
+        }
+    }
+    memset(sn, 0, sizeof(*sn));
+    if (must_delete) {
+        pstrcpy(sn->name, sizeof(sn->name), old_sn->name);
+        pstrcpy(sn->id_str, sizeof(sn->id_str), old_sn->id_str);
+    } else {
+        if (name)
+            pstrcpy(sn->name, sizeof(sn->name), name);
+    }
+
+    /* fill auxiliary fields */
+#ifdef _WIN32
+    _ftime(&tb);
+    sn->date_sec = tb.time;
+    sn->date_nsec = tb.millitm * 1000000;
+#else
+    gettimeofday(&tv, NULL);
+    sn->date_sec = tv.tv_sec;
+    sn->date_nsec = tv.tv_usec * 1000;
+#endif
+    sn->vm_clock_nsec = qemu_get_clock(vm_clock);
+
+    if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
+        term_printf("Device %s does not support VM state snapshots\n",
+                    bdrv_get_device_name(bs));
+        goto the_end;
+    }
+
+    /* save the VM state */
+    f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 1);
+    if (!f) {
+        term_printf("Could not open VM state file\n");
+        goto the_end;
+    }
+    ret = qemu_savevm_state(f);
+    sn->vm_state_size = qemu_ftell(f);
+    qemu_fclose(f);
+    if (ret < 0) {
+        term_printf("Error %d while writing VM\n", ret);
+        goto the_end;
+    }
+
+    /* create the snapshots */
+
+    for(i = 0; i < nb_drives; i++) {
+        bs1 = drives_table[i].bdrv;
+        if (bdrv_has_snapshot(bs1)) {
+            if (must_delete) {
+                ret = bdrv_snapshot_delete(bs1, old_sn->id_str);
+                if (ret < 0) {
+                    term_printf("Error while deleting snapshot on '%s'\n",
+                                bdrv_get_device_name(bs1));
+                }
+            }
+            ret = bdrv_snapshot_create(bs1, sn);
+            if (ret < 0) {
+                term_printf("Error while creating snapshot on '%s'\n",
+                            bdrv_get_device_name(bs1));
+            }
+        }
+    }
+
+ the_end:
+    if (saved_vm_running)
+        vm_start();
+}
+
+void do_loadvm(const char *name)
+{
+    BlockDriverState *bs, *bs1;
+    BlockDriverInfo bdi1, *bdi = &bdi1;
+    QEMUFile *f;
+    int i, ret;
+    int saved_vm_running;
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No block device supports snapshots\n");
+        return;
+    }
+
+    /* Flush all IO requests so they don't interfere with the new state.  */
+    qemu_aio_flush();
+
+    saved_vm_running = vm_running;
+    vm_stop(0);
+
+    for(i = 0; i <= nb_drives; i++) {
+        bs1 = drives_table[i].bdrv;
+        if (bdrv_has_snapshot(bs1)) {
+            ret = bdrv_snapshot_goto(bs1, name);
+            if (ret < 0) {
+                if (bs != bs1)
+                    term_printf("Warning: ");
+                switch(ret) {
+                case -ENOTSUP:
+                    term_printf("Snapshots not supported on device '%s'\n",
+                                bdrv_get_device_name(bs1));
+                    break;
+                case -ENOENT:
+                    term_printf("Could not find snapshot '%s' on device '%s'\n",
+                                name, bdrv_get_device_name(bs1));
+                    break;
+                default:
+                    term_printf("Error %d while activating snapshot on '%s'\n",
+                                ret, bdrv_get_device_name(bs1));
+                    break;
+                }
+                /* fatal on snapshot block device */
+                if (bs == bs1)
+                    goto the_end;
+            }
+        }
+    }
+
+    if (bdrv_get_info(bs, bdi) < 0 || bdi->vm_state_offset <= 0) {
+        term_printf("Device %s does not support VM state snapshots\n",
+                    bdrv_get_device_name(bs));
+        return;
+    }
+
+    /* restore the VM state */
+    f = qemu_fopen_bdrv(bs, bdi->vm_state_offset, 0);
+    if (!f) {
+        term_printf("Could not open VM state file\n");
+        goto the_end;
+    }
+    ret = qemu_loadvm_state(f);
+    qemu_fclose(f);
+    if (ret < 0) {
+        term_printf("Error %d while loading VM state\n", ret);
+    }
+ the_end:
+    if (saved_vm_running)
+        vm_start();
+}
+
+void do_delvm(const char *name)
+{
+    BlockDriverState *bs, *bs1;
+    int i, ret;
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No block device supports snapshots\n");
+        return;
+    }
+
+    for(i = 0; i <= nb_drives; i++) {
+        bs1 = drives_table[i].bdrv;
+        if (bdrv_has_snapshot(bs1)) {
+            ret = bdrv_snapshot_delete(bs1, name);
+            if (ret < 0) {
+                if (ret == -ENOTSUP)
+                    term_printf("Snapshots not supported on device '%s'\n",
+                                bdrv_get_device_name(bs1));
+                else
+                    term_printf("Error %d while deleting snapshot on '%s'\n",
+                                ret, bdrv_get_device_name(bs1));
+            }
+        }
+    }
+}
+
+void do_info_snapshots(void)
+{
+    BlockDriverState *bs, *bs1;
+    QEMUSnapshotInfo *sn_tab, *sn;
+    int nb_sns, i;
+    char buf[256];
+
+    bs = get_bs_snapshots();
+    if (!bs) {
+        term_printf("No available block device supports snapshots\n");
+        return;
+    }
+    term_printf("Snapshot devices:");
+    for(i = 0; i <= nb_drives; i++) {
+        bs1 = drives_table[i].bdrv;
+        if (bdrv_has_snapshot(bs1)) {
+            if (bs == bs1)
+                term_printf(" %s", bdrv_get_device_name(bs1));
+        }
+    }
+    term_printf("\n");
+
+    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
+    if (nb_sns < 0) {
+        term_printf("bdrv_snapshot_list: error %d\n", nb_sns);
+        return;
+    }
+    term_printf("Snapshot list (from %s):\n", bdrv_get_device_name(bs));
+    term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
+    for(i = 0; i < nb_sns; i++) {
+        sn = &sn_tab[i];
+        term_printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
+    }
+    qemu_free(sn_tab);
+}
+
+/***********************************************************/
+/* ram save/restore */
+/* we just avoid storing empty pages */
+static void ram_put_page(QEMUFile *f, const uint8_t *buf, int len)
+{
+    int i, v;
+
+    v = buf[0];
+    for(i = 1; i < len; i++) {
+        if (buf[i] != v)
+            goto normal_save;
+    }
+    qemu_put_byte(f, 1);
+    qemu_put_byte(f, v);
+    return;
+ normal_save:
+    qemu_put_byte(f, 0);
+    qemu_put_buffer(f, buf, len);
+}
+
+static int ram_get_page(QEMUFile *f, uint8_t *buf, int len)
+{
+    int v;
+
+    v = qemu_get_byte(f);
+    switch(v) {
+    case 0:
+        if (qemu_get_buffer(f, buf, len) != len)
+            return -EIO;
+        break;
+    case 1:
+        v = qemu_get_byte(f);
+        memset(buf, v, len);
+        break;
+    default:
+        return -EINVAL;
+    }
+    return 0;
+}
+
+static int ram_load_v1(QEMUFile *f, void *opaque)
+{
+    int ret;
+    ram_addr_t i;
+
+    if (qemu_get_be32(f) != phys_ram_size)
+        return -EINVAL;
+    for(i = 0; i < phys_ram_size; i+= TARGET_PAGE_SIZE) {
+        ret = ram_get_page(f, phys_ram_base + i, TARGET_PAGE_SIZE);
+        if (ret)
+            return ret;
+    }
+    return 0;
+}
+
+#define BDRV_HASH_BLOCK_SIZE 1024
+#define IOBUF_SIZE 4096
+#define RAM_CBLOCK_MAGIC 0xfabe
+
+typedef struct RamCompressState {
+    z_stream zstream;
+    QEMUFile *f;
+    uint8_t buf[IOBUF_SIZE];
+} RamCompressState;
+
+static int ram_compress_open(RamCompressState *s, QEMUFile *f)
+{
+    int ret;
+    memset(s, 0, sizeof(*s));
+    s->f = f;
+    ret = deflateInit2(&s->zstream, 1,
+                       Z_DEFLATED, 15,
+                       9, Z_DEFAULT_STRATEGY);
+    if (ret != Z_OK)
+        return -1;
+    s->zstream.avail_out = IOBUF_SIZE;
+    s->zstream.next_out = s->buf;
+    return 0;
+}
+
+static void ram_put_cblock(RamCompressState *s, const uint8_t *buf, int len)
+{
+    qemu_put_be16(s->f, RAM_CBLOCK_MAGIC);
+    qemu_put_be16(s->f, len);
+    qemu_put_buffer(s->f, buf, len);
+}
+
+static int ram_compress_buf(RamCompressState *s, const uint8_t *buf, int len)
+{
+    int ret;
+
+    s->zstream.avail_in = len;
+    s->zstream.next_in = (uint8_t *)buf;
+    while (s->zstream.avail_in > 0) {
+        ret = deflate(&s->zstream, Z_NO_FLUSH);
+        if (ret != Z_OK)
+            return -1;
+        if (s->zstream.avail_out == 0) {
+            ram_put_cblock(s, s->buf, IOBUF_SIZE);
+            s->zstream.avail_out = IOBUF_SIZE;
+            s->zstream.next_out = s->buf;
+        }
+    }
+    return 0;
+}
+
+static void ram_compress_close(RamCompressState *s)
+{
+    int len, ret;
+
+    /* compress last bytes */
+    for(;;) {
+        ret = deflate(&s->zstream, Z_FINISH);
+        if (ret == Z_OK || ret == Z_STREAM_END) {
+            len = IOBUF_SIZE - s->zstream.avail_out;
+            if (len > 0) {
+                ram_put_cblock(s, s->buf, len);
+            }
+            s->zstream.avail_out = IOBUF_SIZE;
+            s->zstream.next_out = s->buf;
+            if (ret == Z_STREAM_END)
+                break;
+        } else {
+            goto fail;
+        }
+    }
+fail:
+    deflateEnd(&s->zstream);
+}
+
+typedef struct RamDecompressState {
+    z_stream zstream;
+    QEMUFile *f;
+    uint8_t buf[IOBUF_SIZE];
+} RamDecompressState;
+
+static int ram_decompress_open(RamDecompressState *s, QEMUFile *f)
+{
+    int ret;
+    memset(s, 0, sizeof(*s));
+    s->f = f;
+    ret = inflateInit(&s->zstream);
+    if (ret != Z_OK)
+        return -1;
+    return 0;
+}
+
+static int ram_decompress_buf(RamDecompressState *s, uint8_t *buf, int len)
+{
+    int ret, clen;
+
+    s->zstream.avail_out = len;
+    s->zstream.next_out = buf;
+    while (s->zstream.avail_out > 0) {
+        if (s->zstream.avail_in == 0) {
+            if (qemu_get_be16(s->f) != RAM_CBLOCK_MAGIC)
+                return -1;
+            clen = qemu_get_be16(s->f);
+            if (clen > IOBUF_SIZE)
+                return -1;
+            qemu_get_buffer(s->f, s->buf, clen);
+            s->zstream.avail_in = clen;
+            s->zstream.next_in = s->buf;
+        }
+        ret = inflate(&s->zstream, Z_PARTIAL_FLUSH);
+        if (ret != Z_OK && ret != Z_STREAM_END) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static void ram_decompress_close(RamDecompressState *s)
+{
+    inflateEnd(&s->zstream);
+}
+
+static void ram_save(QEMUFile *f, void *opaque)
+{
+    ram_addr_t i;
+    RamCompressState s1, *s = &s1;
+    uint8_t buf[10];
+
+    qemu_put_be32(f, phys_ram_size);
+    if (ram_compress_open(s, f) < 0)
+        return;
+    for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+#if 0
+        if (tight_savevm_enabled) {
+            int64_t sector_num;
+            int j;
+
+            /* find if the memory block is available on a virtual
+               block device */
+            sector_num = -1;
+            for(j = 0; j < nb_drives; j++) {
+                sector_num = bdrv_hash_find(drives_table[j].bdrv,
+                                            phys_ram_base + i,
+					    BDRV_HASH_BLOCK_SIZE);
+                if (sector_num >= 0)
+                    break;
+            }
+            if (j == nb_drives)
+                goto normal_compress;
+            buf[0] = 1;
+            buf[1] = j;
+            cpu_to_be64wu((uint64_t *)(buf + 2), sector_num);
+            ram_compress_buf(s, buf, 10);
+        } else
+#endif
+        {
+            //        normal_compress:
+            buf[0] = 0;
+            ram_compress_buf(s, buf, 1);
+            ram_compress_buf(s, phys_ram_base + i, BDRV_HASH_BLOCK_SIZE);
+        }
+    }
+    ram_compress_close(s);
+}
+
+static int ram_load(QEMUFile *f, void *opaque, int version_id)
+{
+    RamDecompressState s1, *s = &s1;
+    uint8_t buf[10];
+    ram_addr_t i;
+
+    if (version_id == 1)
+        return ram_load_v1(f, opaque);
+    if (version_id != 2)
+        return -EINVAL;
+    if (qemu_get_be32(f) != phys_ram_size)
+        return -EINVAL;
+    if (ram_decompress_open(s, f) < 0)
+        return -EINVAL;
+    for(i = 0; i < phys_ram_size; i+= BDRV_HASH_BLOCK_SIZE) {
+        if (ram_decompress_buf(s, buf, 1) < 0) {
+            fprintf(stderr, "Error while reading ram block header\n");
+            goto error;
+        }
+        if (buf[0] == 0) {
+            if (ram_decompress_buf(s, phys_ram_base + i, BDRV_HASH_BLOCK_SIZE) < 0) {
+                fprintf(stderr, "Error while reading ram block address=0x%08" PRIx64, (uint64_t)i);
+                goto error;
+            }
+        } else
+#if 0
+        if (buf[0] == 1) {
+            int bs_index;
+            int64_t sector_num;
+
+            ram_decompress_buf(s, buf + 1, 9);
+            bs_index = buf[1];
+            sector_num = be64_to_cpupu((const uint64_t *)(buf + 2));
+            if (bs_index >= nb_drives) {
+                fprintf(stderr, "Invalid block device index %d\n", bs_index);
+                goto error;
+            }
+            if (bdrv_read(drives_table[bs_index].bdrv, sector_num,
+	                  phys_ram_base + i,
+                          BDRV_HASH_BLOCK_SIZE / 512) < 0) {
+                fprintf(stderr, "Error while reading sector %d:%" PRId64 "\n",
+                        bs_index, sector_num);
+                goto error;
+            }
+        } else
+#endif
+        {
+        error:
+            printf("Error block header\n");
+            return -EINVAL;
+        }
+    }
+    ram_decompress_close(s);
+    return 0;
+}
+
+/***********************************************************/
+/* bottom halves (can be seen as timers which expire ASAP) */
+
+struct QEMUBH {
+    QEMUBHFunc *cb;
+    void *opaque;
+    int scheduled;
+    QEMUBH *next;
+};
+
+static QEMUBH *first_bh = NULL;
+
+QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
+{
+    QEMUBH *bh;
+    bh = qemu_mallocz(sizeof(QEMUBH));
+    if (!bh)
+        return NULL;
+    bh->cb = cb;
+    bh->opaque = opaque;
+    return bh;
+}
+
+int qemu_bh_poll(void)
+{
+    QEMUBH *bh, **pbh;
+    int ret;
+
+    ret = 0;
+    for(;;) {
+        pbh = &first_bh;
+        bh = *pbh;
+        if (!bh)
+            break;
+        ret = 1;
+        *pbh = bh->next;
+        bh->scheduled = 0;
+        bh->cb(bh->opaque);
+    }
+    return ret;
+}
+
+void qemu_bh_schedule(QEMUBH *bh)
+{
+    CPUState *env = cpu_single_env;
+    if (bh->scheduled)
+        return;
+    bh->scheduled = 1;
+    bh->next = first_bh;
+    first_bh = bh;
+
+    /* stop the currently executing CPU to execute the BH ASAP */
+    if (env) {
+        cpu_interrupt(env, CPU_INTERRUPT_EXIT);
+    }
+}
+
+void qemu_bh_cancel(QEMUBH *bh)
+{
+    QEMUBH **pbh;
+    if (bh->scheduled) {
+        pbh = &first_bh;
+        while (*pbh != bh)
+            pbh = &(*pbh)->next;
+        *pbh = bh->next;
+        bh->scheduled = 0;
+    }
+}
+
+void qemu_bh_delete(QEMUBH *bh)
+{
+    qemu_bh_cancel(bh);
+    qemu_free(bh);
+}
+
+/***********************************************************/
+/* machine registration */
+
+QEMUMachine *first_machine = NULL;
+
+int qemu_register_machine(QEMUMachine *m)
+{
+    QEMUMachine **pm;
+    pm = &first_machine;
+    while (*pm != NULL)
+        pm = &(*pm)->next;
+    m->next = NULL;
+    *pm = m;
+    return 0;
+}
+
+static QEMUMachine *find_machine(const char *name)
+{
+    QEMUMachine *m;
+
+    for(m = first_machine; m != NULL; m = m->next) {
+        if (!strcmp(m->name, name))
+            return m;
+    }
+    return NULL;
+}
+
+/***********************************************************/
+/* main execution loop */
+
+static void gui_update(void *opaque)
+{
+    DisplayState *ds = opaque;
+    ds->dpy_refresh(ds);
+    qemu_mod_timer(ds->gui_timer,
+        (ds->gui_timer_interval ?
+	    ds->gui_timer_interval :
+	    GUI_REFRESH_INTERVAL)
+	+ qemu_get_clock(rt_clock));
+}
+
+struct vm_change_state_entry {
+    VMChangeStateHandler *cb;
+    void *opaque;
+    LIST_ENTRY (vm_change_state_entry) entries;
+};
+
+static LIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head;
+
+VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb,
+                                                     void *opaque)
+{
+    VMChangeStateEntry *e;
+
+    e = qemu_mallocz(sizeof (*e));
+    if (!e)
+        return NULL;
+
+    e->cb = cb;
+    e->opaque = opaque;
+    LIST_INSERT_HEAD(&vm_change_state_head, e, entries);
+    return e;
+}
+
+void qemu_del_vm_change_state_handler(VMChangeStateEntry *e)
+{
+    LIST_REMOVE (e, entries);
+    qemu_free (e);
+}
+
+static void vm_state_notify(int running)
+{
+    VMChangeStateEntry *e;
+
+    for (e = vm_change_state_head.lh_first; e; e = e->entries.le_next) {
+        e->cb(e->opaque, running);
+    }
+}
+
+/* XXX: support several handlers */
+static VMStopHandler *vm_stop_cb;
+static void *vm_stop_opaque;
+
+int qemu_add_vm_stop_handler(VMStopHandler *cb, void *opaque)
+{
+    vm_stop_cb = cb;
+    vm_stop_opaque = opaque;
+    return 0;
+}
+
+void qemu_del_vm_stop_handler(VMStopHandler *cb, void *opaque)
+{
+    vm_stop_cb = NULL;
+}
+
+void vm_start(void)
+{
+    if (!vm_running) {
+        cpu_enable_ticks();
+        vm_running = 1;
+        vm_state_notify(1);
+        qemu_rearm_alarm_timer(alarm_timer);
+    }
+}
+
+void vm_stop(int reason)
+{
+    if (vm_running) {
+        cpu_disable_ticks();
+        vm_running = 0;
+        if (reason != 0) {
+            if (vm_stop_cb) {
+                vm_stop_cb(vm_stop_opaque, reason);
+            }
+        }
+        vm_state_notify(0);
+    }
+}
+
+/* reset/shutdown handler */
+
+typedef struct QEMUResetEntry {
+    QEMUResetHandler *func;
+    void *opaque;
+    struct QEMUResetEntry *next;
+} QEMUResetEntry;
+
+static QEMUResetEntry *first_reset_entry;
+static int reset_requested;
+static int shutdown_requested;
+static int powerdown_requested;
+
+int qemu_shutdown_requested(void)
+{
+    int r = shutdown_requested;
+    shutdown_requested = 0;
+    return r;
+}
+
+int qemu_reset_requested(void)
+{
+    int r = reset_requested;
+    reset_requested = 0;
+    return r;
+}
+
+int qemu_powerdown_requested(void)
+{
+    int r = powerdown_requested;
+    powerdown_requested = 0;
+    return r;
+}
+
+void qemu_register_reset(QEMUResetHandler *func, void *opaque)
+{
+    QEMUResetEntry **pre, *re;
+
+    pre = &first_reset_entry;
+    while (*pre != NULL)
+        pre = &(*pre)->next;
+    re = qemu_mallocz(sizeof(QEMUResetEntry));
+    re->func = func;
+    re->opaque = opaque;
+    re->next = NULL;
+    *pre = re;
+}
+
+void qemu_system_reset(void)
+{
+    QEMUResetEntry *re;
+
+    /* reset all devices */
+    for(re = first_reset_entry; re != NULL; re = re->next) {
+        re->func(re->opaque);
+    }
+}
+
+void qemu_system_reset_request(void)
+{
+    if (no_reboot) {
+        shutdown_requested = 1;
+    } else {
+        reset_requested = 1;
+    }
+    if (cpu_single_env)
+        cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+#ifdef HAS_AUDIO
+extern void  AUD_cleanup();
+#endif
+
+void qemu_system_shutdown_request(void)
+{
+    shutdown_requested = 1;
+    if (cpu_single_env)
+        cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+void qemu_system_powerdown_request(void)
+{
+    powerdown_requested = 1;
+    if (cpu_single_env)
+        cpu_interrupt(cpu_single_env, CPU_INTERRUPT_EXIT);
+}
+
+#define  MAIN_LOOP_STATS  0
+
+#if  MAIN_LOOP_STATS
+typedef struct {
+    int         counter;
+	int64_t     reset_time;       // time when counter is reset
+    int64_t     spent_time_total; // total time spent since last counter reset
+	int64_t     spent_time_min;   // minimum time spent in call
+	int64_t     spent_time_max;   // maximum time spent in call
+	int64_t     wait_time_total;  // total time spent waiting for select()
+} MainLoopStats;
+
+static __inline__ int64_t
+mainloopstats_now( void )
+{
+	return  qemu_get_clock( vm_clock );
+}
+
+static __inline__ double
+mainloopstats_to_ms( int64_t  duration )
+{
+	return duration / 1000000.;
+}
+
+static void
+mainloopstats_reset( MainLoopStats*  s )
+{
+	int64_t   now = qemu_get_clock( vm_clock );
+
+	s->counter          = 0;
+	s->reset_time       = now;
+	s->spent_time_total = 0;
+	s->wait_time_total  = 0;
+	s->spent_time_min   = INT_MAX;
+	s->spent_time_max   = 0;
+}
+
+static MainLoopStats   main_loop_stats;
+#endif  /* MAIN_LOOP_STATS */
+
+void main_loop_wait(int timeout)
+{
+    IOHandlerRecord *ioh;
+    fd_set rfds, wfds, xfds;
+    int ret, nfds;
+#ifdef _WIN32
+    int ret2, i;
+#endif
+    struct timeval tv;
+    PollingEntry *pe;
+
+#if MAIN_LOOP_STATS
+	int64   time_before = mainloopstats_now();
+	int64   time_after_select;
+#endif
+
+    /* XXX: need to suppress polling by better using win32 events */
+    ret = 0;
+    for(pe = first_polling_entry; pe != NULL; pe = pe->next) {
+        ret |= pe->func(pe->opaque);
+    }
+#ifdef _WIN32
+    if (ret == 0) {
+        int err;
+        WaitObjects *w = &wait_objects;
+
+        ret = WaitForMultipleObjects(w->num, w->events, FALSE, timeout);
+        if (WAIT_OBJECT_0 + 0 <= ret && ret <= WAIT_OBJECT_0 + w->num - 1) {
+            if (w->func[ret - WAIT_OBJECT_0])
+                w->func[ret - WAIT_OBJECT_0](w->opaque[ret - WAIT_OBJECT_0]);
+
+            /* Check for additional signaled events */
+            for(i = (ret - WAIT_OBJECT_0 + 1); i < w->num; i++) {
+
+                /* Check if event is signaled */
+                ret2 = WaitForSingleObject(w->events[i], 0);
+                if(ret2 == WAIT_OBJECT_0) {
+                    if (w->func[i])
+                        w->func[i](w->opaque[i]);
+                } else if (ret2 == WAIT_TIMEOUT) {
+                } else {
+                    err = GetLastError();
+                    fprintf(stderr, "WaitForSingleObject error %d %d\n", i, err);
+                }
+            }
+        } else if (ret == WAIT_TIMEOUT) {
+        } else {
+            err = GetLastError();
+            fprintf(stderr, "WaitForMultipleObjects error %d %d\n", ret, err);
+        }
+    }
+#endif
+    /* poll any events */
+    /* XXX: separate device handlers from system ones */
+    nfds = -1;
+    FD_ZERO(&rfds);
+    FD_ZERO(&wfds);
+    FD_ZERO(&xfds);
+    for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
+        if (ioh->deleted)
+            continue;
+        if (ioh->fd_read &&
+            (!ioh->fd_read_poll ||
+             ioh->fd_read_poll(ioh->opaque) != 0)) {
+            FD_SET(ioh->fd, &rfds);
+            if (ioh->fd > nfds)
+                nfds = ioh->fd;
+        }
+        if (ioh->fd_write) {
+            FD_SET(ioh->fd, &wfds);
+            if (ioh->fd > nfds)
+                nfds = ioh->fd;
+        }
+    }
+
+    tv.tv_sec = 0;
+#ifdef _WIN32
+    tv.tv_usec = 0;
+#else
+    tv.tv_usec = timeout * 1000;
+#endif
+#if defined(CONFIG_SLIRP)
+    if (slirp_inited) {
+        slirp_select_fill(&nfds, &rfds, &wfds, &xfds);
+    }
+#endif
+    ret = select(nfds + 1, &rfds, &wfds, &xfds, &tv);
+#if MAIN_LOOP_STATS
+	time_after_select = mainloopstats_now();
+#endif
+    if (ret > 0) {
+        IOHandlerRecord **pioh;
+
+        for(ioh = first_io_handler; ioh != NULL; ioh = ioh->next) {
+            if (!ioh->deleted && ioh->fd_read && FD_ISSET(ioh->fd, &rfds)) {
+                ioh->fd_read(ioh->opaque);
+            }
+            if (!ioh->deleted && ioh->fd_write && FD_ISSET(ioh->fd, &wfds)) {
+                ioh->fd_write(ioh->opaque);
+            }
+        }
+
+	/* remove deleted IO handlers */
+	pioh = &first_io_handler;
+	while (*pioh) {
+            ioh = *pioh;
+            if (ioh->deleted) {
+                *pioh = ioh->next;
+                qemu_free(ioh);
+            } else
+                pioh = &ioh->next;
+        }
+    }
+#if defined(CONFIG_SLIRP)
+    if (slirp_inited) {
+        if (ret < 0) {
+            FD_ZERO(&rfds);
+            FD_ZERO(&wfds);
+            FD_ZERO(&xfds);
+        }
+        slirp_select_poll(&rfds, &wfds, &xfds);
+    }
+#endif
+    charpipe_poll();
+
+    if (vm_running) {
+        if (likely(!(cur_cpu->singlestep_enabled & SSTEP_NOTIMER)))
+        qemu_run_timers(&active_timers[QEMU_TIMER_VIRTUAL],
+                        qemu_get_clock(vm_clock));
+        /* run dma transfers, if any */
+        DMA_run();
+    }
+
+    /* real time timers */
+    qemu_run_timers(&active_timers[QEMU_TIMER_REALTIME],
+                    qemu_get_clock(rt_clock));
+
+    if (alarm_timer->flags & ALARM_FLAG_EXPIRED) {
+        alarm_timer->flags &= ~(ALARM_FLAG_EXPIRED);
+        qemu_rearm_alarm_timer(alarm_timer);
+    }
+
+    /* Check bottom-halves last in case any of the earlier events triggered
+       them.  */
+    qemu_bh_poll();
+
+#if MAIN_LOOP_STATS
+	{
+		MainLoopStats*  s = &main_loop_stats;
+		int64_t         time_after = mainloopstats_now();
+		int64_t         time_diff  = time_after - time_before;
+
+		s->spent_time_total += time_diff;
+		if (time_diff < s->spent_time_min)
+			s->spent_time_min = time_diff;
+	    if (time_diff > s->spent_time_max)
+		    s->spent_time_max = time_diff;
+
+		time_diff = time_after_select - time_before;
+		s->wait_time_total += time_diff;
+
+		if (++s->counter == 1000) {
+			double  period        = time_after - s->reset_time;
+			double  average_spent = s->spent_time_total * 1. / s->counter;
+			double  average_wait  = s->wait_time_total * 1. / s->counter;
+
+			printf( "main loop stats:  iterations: %8ld,  period: %10.2f ms, avg wait time: %10.2f ms  (%.3f %%), avg exec time %10.2f ms (%.3f %%)\n",
+			        s->counter,
+					mainloopstats_to_ms(period),
+					mainloopstats_to_ms(average_wait),
+					s->wait_time_total * 100. / period,
+					mainloopstats_to_ms(average_spent),
+					s->spent_time_total * 100. / period );
+
+			mainloopstats_reset( s );
+		}
+	}
+#endif  /* MAIN_LOOP_STATS */
+}
+
+static int main_loop(void)
+{
+    int ret, timeout;
+#ifdef CONFIG_PROFILER
+    int64_t ti;
+#endif
+    CPUState *env;
+
+    cur_cpu = first_cpu;
+    next_cpu = cur_cpu->next_cpu ?: first_cpu;
+    for(;;) {
+        if (vm_running) {
+
+            for(;;) {
+                /* get next cpu */
+                env = next_cpu;
+#ifdef CONFIG_PROFILER
+                ti = profile_getclock();
+#endif
+                if (use_icount) {
+                    int64_t count;
+                    int decr;
+                    qemu_icount -= (env->icount_decr.u16.low + env->icount_extra);
+                    env->icount_decr.u16.low = 0;
+                    env->icount_extra = 0;
+                    count = qemu_next_deadline();
+                    count = (count + (1 << icount_time_shift) - 1)
+                            >> icount_time_shift;
+                    qemu_icount += count;
+                    decr = (count > 0xffff) ? 0xffff : count;
+                    count -= decr;
+                    env->icount_decr.u16.low = decr;
+                    env->icount_extra = count;
+                }
+                ret = cpu_exec(env);
+#ifdef CONFIG_PROFILER
+                qemu_time += profile_getclock() - ti;
+#endif
+                if (use_icount) {
+                    /* Fold pending instructions back into the
+                       instruction counter, and clear the interrupt flag.  */
+                    qemu_icount -= (env->icount_decr.u16.low
+                                    + env->icount_extra);
+                    env->icount_decr.u32 = 0;
+                    env->icount_extra = 0;
+                }
+                next_cpu = env->next_cpu ?: first_cpu;
+                if (event_pending && likely(ret != EXCP_DEBUG)) {
+                    ret = EXCP_INTERRUPT;
+                    event_pending = 0;
+                    break;
+                }
+                if (ret == EXCP_HLT) {
+                    /* Give the next CPU a chance to run.  */
+                    cur_cpu = env;
+                    continue;
+                }
+                if (ret != EXCP_HALTED)
+                    break;
+                /* all CPUs are halted ? */
+                if (env == cur_cpu)
+                    break;
+            }
+            cur_cpu = env;
+
+#ifdef CONFIG_TRACE
+            if (tbflush_requested) {
+                tbflush_requested = 0;
+                tb_flush(env);
+                ret = EXCP_INTERRUPT;
+            } else if (exit_requested)
+				goto ExitRequested;
+#endif
+
+            if (shutdown_requested) {
+                ret = EXCP_INTERRUPT;
+                if (no_shutdown) {
+                    vm_stop(0);
+                    no_shutdown = 0;
+                }
+                else
+                    break;
+            }
+            if (reset_requested) {
+                reset_requested = 0;
+                qemu_system_reset();
+                ret = EXCP_INTERRUPT;
+            }
+            if (powerdown_requested) {
+                powerdown_requested = 0;
+		qemu_system_powerdown();
+                ret = EXCP_INTERRUPT;
+            }
+            if (unlikely(ret == EXCP_DEBUG)) {
+                vm_stop(EXCP_DEBUG);
+            }
+            /* If all cpus are halted then wait until the next IRQ */
+            /* XXX: use timeout computed from timers */
+            if (ret == EXCP_HALTED) {
+                if (use_icount) {
+                    int64_t add;
+                    int64_t delta;
+                    /* Advance virtual time to the next event.  */
+                    if (use_icount == 1) {
+                        /* When not using an adaptive execution frequency
+                           we tend to get badly out of sync with real time,
+                           so just delay for a reasonable amount of time.  */
+                        delta = 0;
+                    } else {
+                        delta = cpu_get_icount() - cpu_get_clock();
+                    }
+                    if (delta > 0) {
+                        /* If virtual time is ahead of real time then just
+                           wait for IO.  */
+                        timeout = (delta / 1000000) + 1;
+                    } else {
+                        /* Wait for either IO to occur or the next
+                           timer event.  */
+                        add = qemu_next_deadline();
+                        /* We advance the timer before checking for IO.
+                           Limit the amount we advance so that early IO
+                           activity won't get the guest too far ahead.  */
+                        if (add > 10000000)
+                            add = 10000000;
+                        delta += add;
+                        add = (add + (1 << icount_time_shift) - 1)
+                              >> icount_time_shift;
+                        qemu_icount += add;
+                        timeout = delta / 1000000;
+                        if (timeout < 0)
+                            timeout = 0;
+                    }
+                } else {
+                    timeout = 10;
+                }
+            } else {
+                timeout = 0;
+            }
+        } else {
+            if (shutdown_requested)
+                break;
+            timeout = 10;
+        }
+#ifdef CONFIG_PROFILER
+        ti = profile_getclock();
+#endif
+        main_loop_wait(timeout);
+#ifdef CONFIG_PROFILER
+        dev_time += profile_getclock() - ti;
+#endif
+    }
+    cpu_disable_ticks();
+    return ret;
+
+#ifdef CONFIG_TRACE
+ExitRequested:
+#  ifdef HAS_AUDIO
+    AUD_cleanup();
+#  endif
+    exit(1);
+	return 0;
+#endif
+}
+
+void qemu_help(int exitcode)
+{
+    printf("QEMU PC emulator version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
+           "usage: %s [options] [disk_image]\n"
+           "\n"
+           "'disk_image' is a raw hard image image for IDE hard disk 0\n"
+           "\n"
+           "Standard options:\n"
+           "-M machine      select emulated machine (-M ? for list)\n"
+           "-cpu cpu        select CPU (-cpu ? for list)\n"
+           "-fda/-fdb file  use 'file' as floppy disk 0/1 image\n"
+           "-hda/-hdb file  use 'file' as IDE hard disk 0/1 image\n"
+           "-hdc/-hdd file  use 'file' as IDE hard disk 2/3 image\n"
+           "-cdrom file     use 'file' as IDE cdrom image (cdrom is ide1 master)\n"
+	   "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n"
+           "       [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n"
+           "       [,cache=on|off][,format=f]\n"
+	   "                use 'file' as a drive image\n"
+           "-mtdblock file  use 'file' as on-board Flash memory image\n"
+           "-sd file        use 'file' as SecureDigital card image\n"
+           "-pflash file    use 'file' as a parallel flash image\n"
+           "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)\n"
+           "-snapshot       write to temporary files instead of disk image files\n"
+#ifdef CONFIG_SDL
+           "-no-frame       open SDL window without a frame and window decorations\n"
+           "-alt-grab       use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n"
+           "-no-quit        disable SDL window close capability\n"
+#endif
+#ifdef TARGET_I386
+           "-no-fd-bootchk  disable boot signature checking for floppy disks\n"
+#endif
+           "-m megs         set virtual RAM size to megs MB [default=%d]\n"
+           "-smp n          set the number of CPUs to 'n' [default=1]\n"
+           "-nographic      disable graphical output and redirect serial I/Os to console\n"
+           "-portrait       rotate graphical output 90 deg left (only PXA LCD)\n"
+#ifndef _WIN32
+           "-k language     use keyboard layout (for example \"fr\" for French)\n"
+#endif
+#ifdef HAS_AUDIO
+           "-audio-help     print list of audio drivers and their options\n"
+           "-soundhw c1,... enable audio support\n"
+           "                and only specified sound cards (comma separated list)\n"
+           "                use -soundhw ? to get the list of supported cards\n"
+           "                use -soundhw all to enable all of them\n"
+#endif
+           "-localtime      set the real time clock to local time [default=utc]\n"
+           "-full-screen    start in full screen\n"
+#ifdef TARGET_I386
+           "-win2k-hack     use it when installing Windows 2000 to avoid a disk full bug\n"
+#endif
+           "-usb            enable the USB driver (will be the default soon)\n"
+           "-usbdevice name add the host or guest USB device 'name'\n"
+#if defined(TARGET_PPC) || defined(TARGET_SPARC)
+           "-g WxH[xDEPTH]  Set the initial graphical resolution and depth\n"
+#endif
+           "-name string    set the name of the guest\n"
+           "\n"
+           "Network options:\n"
+           "-net nic[,vlan=n][,macaddr=addr][,model=type]\n"
+           "                create a new Network Interface Card and connect it to VLAN 'n'\n"
+#ifdef CONFIG_SLIRP
+           "-net user[,vlan=n][,hostname=host]\n"
+           "                connect the user mode network stack to VLAN 'n' and send\n"
+           "                hostname 'host' to DHCP clients\n"
+#endif
+#ifdef _WIN32
+           "-net tap[,vlan=n],ifname=name\n"
+           "                connect the host TAP network interface to VLAN 'n'\n"
+#else
+           "-net tap[,vlan=n][,fd=h][,ifname=name][,script=file][,downscript=dfile]\n"
+           "                connect the host TAP network interface to VLAN 'n' and use the\n"
+           "                network scripts 'file' (default=%s)\n"
+           "                and 'dfile' (default=%s);\n"
+           "                use '[down]script=no' to disable script execution;\n"
+           "                use 'fd=h' to connect to an already opened TAP interface\n"
+#endif
+           "-net socket[,vlan=n][,fd=h][,listen=[host]:port][,connect=host:port]\n"
+           "                connect the vlan 'n' to another VLAN using a socket connection\n"
+           "-net socket[,vlan=n][,fd=h][,mcast=maddr:port]\n"
+           "                connect the vlan 'n' to multicast maddr and port\n"
+           "-net none       use it alone to have zero network devices; if no -net option\n"
+           "                is provided, the default is '-net nic -net user'\n"
+           "\n"
+#ifdef CONFIG_SLIRP
+           "-tftp dir       allow tftp access to files in dir [-net user]\n"
+           "-bootp file     advertise file in BOOTP replies\n"
+#ifndef _WIN32
+           "-smb dir        allow SMB access to files in 'dir' [-net user]\n"
+#endif
+           "-redir [tcp|udp]:host-port:[guest-host]:guest-port\n"
+           "                redirect TCP or UDP connections from host to guest [-net user]\n"
+#endif
+           "\n"
+           "Linux boot specific:\n"
+           "-kernel bzImage use 'bzImage' as kernel image\n"
+           "-append cmdline use 'cmdline' as kernel command line\n"
+           "-initrd file    use 'file' as initial ram disk\n"
+           "\n"
+           "Debug/Expert options:\n"
+           "-monitor dev    redirect the monitor to char device 'dev'\n"
+           "-serial dev     redirect the serial port to char device 'dev'\n"
+           "-parallel dev   redirect the parallel port to char device 'dev'\n"
+           "-pidfile file   Write PID to 'file'\n"
+           "-S              freeze CPU at startup (use 'c' to start execution)\n"
+           "-s              wait gdb connection to port\n"
+           "-p port         set gdb connection port [default=%s]\n"
+           "-d item1,...    output log to %s (use -d ? for a list of log items)\n"
+           "-hdachs c,h,s[,t]  force hard disk 0 physical geometry and the optional BIOS\n"
+           "                translation (t=none or lba) (usually qemu can guess them)\n"
+           "-L path         set the directory for the BIOS, VGA BIOS and keymaps\n"
+#ifdef USE_KQEMU
+           "-kernel-kqemu   enable KQEMU full virtualization (default is user mode only)\n"
+           "-no-kqemu       disable KQEMU kernel module usage\n"
+#endif
+#ifdef TARGET_I386
+           "-std-vga        simulate a standard VGA card with VESA Bochs Extensions\n"
+           "                (default is CL-GD5446 PCI VGA)\n"
+           "-no-acpi        disable ACPI\n"
+#endif
+#ifdef CONFIG_CURSES
+           "-curses         use a curses/ncurses interface instead of SDL\n"
+#endif
+           "-no-reboot      exit instead of rebooting\n"
+           "-no-shutdown    stop before shutdown\n"
+           "-loadvm [tag|id]  start right away with a saved state (loadvm in monitor)\n"
+	   "-vnc display    start a VNC server on display\n"
+#ifdef CONFIG_TRACE
+	   "-trace file     create an execution trace in 'file' (implies -tracing on)\n"
+	   "-tracing off    start with tracing off\n"
+	   "-trace_miss     include tracing of cache miss addresses\n"
+	   "-trace_addr     include tracing of all load/store addresses\n"
+	   "-dcache_load_miss cycles\n"
+           "                set the dcache load miss penalty to 'cycles'\n"
+	   "-dcache_store_miss cycles\n"
+           "                set the dcache store miss penalty to 'cycles'\n"
+#endif
+#ifdef CONFIG_NAND
+           "-nand name[,readonly][,size=size][,pagesize=size][,extrasize=size][,erasepages=pages][,initfile=file][,file=file]"
+#endif
+#ifndef _WIN32
+	   "-daemonize      daemonize QEMU after initializing\n"
+#endif
+	   "-option-rom rom load a file, rom, into the option ROM space\n"
+#ifdef TARGET_SPARC
+           "-prom-env variable=value  set OpenBIOS nvram variables\n"
+#endif
+           "-clock          force the use of the given methods for timer alarm.\n"
+           "                To see what timers are available use -clock ?\n"
+           "-startdate      select initial date of the clock\n"
+           "-icount [N|auto]\n"
+           "                Enable virtual instruction counter with 2^N clock ticks per instruction\n"
+           "\n"
+           "During emulation, the following keys are useful:\n"
+           "ctrl-alt-f      toggle full screen\n"
+           "ctrl-alt-n      switch to virtual console 'n'\n"
+           "ctrl-alt        toggle mouse and keyboard grab\n"
+           "\n"
+           "When using -nographic, press 'ctrl-a h' to get some help.\n"
+           ,
+           "qemu",
+           DEFAULT_RAM_SIZE,
+#ifndef _WIN32
+           DEFAULT_NETWORK_SCRIPT,
+           DEFAULT_NETWORK_DOWN_SCRIPT,
+#endif
+           DEFAULT_GDBSTUB_PORT,
+           "/tmp/qemu.log");
+    exit(exitcode);
+}
+
+#define HAS_ARG 0x0001
+
+enum {
+    QEMU_OPTION_h,
+
+    QEMU_OPTION_M,
+    QEMU_OPTION_cpu,
+    QEMU_OPTION_fda,
+    QEMU_OPTION_fdb,
+    QEMU_OPTION_hda,
+    QEMU_OPTION_hdb,
+    QEMU_OPTION_hdc,
+    QEMU_OPTION_hdd,
+    QEMU_OPTION_drive,
+    QEMU_OPTION_cdrom,
+    QEMU_OPTION_mtdblock,
+    QEMU_OPTION_sd,
+    QEMU_OPTION_pflash,
+    QEMU_OPTION_boot,
+    QEMU_OPTION_snapshot,
+#ifdef TARGET_I386
+    QEMU_OPTION_no_fd_bootchk,
+#endif
+    QEMU_OPTION_m,
+    QEMU_OPTION_nographic,
+    QEMU_OPTION_portrait,
+#ifdef HAS_AUDIO
+    QEMU_OPTION_audio_help,
+    QEMU_OPTION_soundhw,
+#endif
+
+    QEMU_OPTION_net,
+    QEMU_OPTION_tftp,
+    QEMU_OPTION_bootp,
+    QEMU_OPTION_smb,
+    QEMU_OPTION_redir,
+
+    QEMU_OPTION_kernel,
+    QEMU_OPTION_append,
+    QEMU_OPTION_initrd,
+
+    QEMU_OPTION_S,
+    QEMU_OPTION_s,
+    QEMU_OPTION_p,
+    QEMU_OPTION_d,
+    QEMU_OPTION_hdachs,
+    QEMU_OPTION_L,
+    QEMU_OPTION_bios,
+    QEMU_OPTION_k,
+    QEMU_OPTION_localtime,
+    QEMU_OPTION_cirrusvga,
+    QEMU_OPTION_vmsvga,
+    QEMU_OPTION_g,
+    QEMU_OPTION_std_vga,
+    QEMU_OPTION_echr,
+    QEMU_OPTION_monitor,
+    QEMU_OPTION_serial,
+    QEMU_OPTION_parallel,
+    QEMU_OPTION_loadvm,
+    QEMU_OPTION_full_screen,
+    QEMU_OPTION_no_frame,
+    QEMU_OPTION_alt_grab,
+    QEMU_OPTION_no_quit,
+    QEMU_OPTION_pidfile,
+    QEMU_OPTION_no_kqemu,
+    QEMU_OPTION_kernel_kqemu,
+    QEMU_OPTION_win2k_hack,
+    QEMU_OPTION_usb,
+    QEMU_OPTION_usbdevice,
+    QEMU_OPTION_smp,
+    QEMU_OPTION_vnc,
+    QEMU_OPTION_no_acpi,
+    QEMU_OPTION_curses,
+    QEMU_OPTION_no_reboot,
+    QEMU_OPTION_no_shutdown,
+    QEMU_OPTION_show_cursor,
+    QEMU_OPTION_daemonize,
+    QEMU_OPTION_option_rom,
+    QEMU_OPTION_semihosting,
+    QEMU_OPTION_name,
+    QEMU_OPTION_prom_env,
+    QEMU_OPTION_old_param,
+    QEMU_OPTION_noaudio,
+    QEMU_OPTION_mic,
+#ifdef CONFIG_TRACE
+    QEMU_OPTION_trace_file,
+    QEMU_OPTION_tracing,
+    QEMU_OPTION_trace_miss,
+    QEMU_OPTION_trace_addr,
+    QEMU_OPTION_dcache_load_miss,
+    QEMU_OPTION_dcache_store_miss,
+#endif
+#ifdef CONFIG_NAND
+    QEMU_OPTION_nand,
+#endif
+    QEMU_OPTION_clock,
+    QEMU_OPTION_startdate,
+    QEMU_OPTION_tb_size,
+    QEMU_OPTION_icount,
+};
+
+typedef struct QEMUOption {
+    const char *name;
+    int flags;
+    int index;
+} QEMUOption;
+
+const QEMUOption qemu_options[] = {
+    { "h", 0, QEMU_OPTION_h },
+    { "help", 0, QEMU_OPTION_h },
+
+    { "M", HAS_ARG, QEMU_OPTION_M },
+    { "cpu", HAS_ARG, QEMU_OPTION_cpu },
+    { "fda", HAS_ARG, QEMU_OPTION_fda },
+    { "fdb", HAS_ARG, QEMU_OPTION_fdb },
+    { "hda", HAS_ARG, QEMU_OPTION_hda },
+    { "hdb", HAS_ARG, QEMU_OPTION_hdb },
+    { "hdc", HAS_ARG, QEMU_OPTION_hdc },
+    { "hdd", HAS_ARG, QEMU_OPTION_hdd },
+    { "drive", HAS_ARG, QEMU_OPTION_drive },
+    { "cdrom", HAS_ARG, QEMU_OPTION_cdrom },
+    { "mtdblock", HAS_ARG, QEMU_OPTION_mtdblock },
+    { "sd", HAS_ARG, QEMU_OPTION_sd },
+    { "pflash", HAS_ARG, QEMU_OPTION_pflash },
+    { "boot", HAS_ARG, QEMU_OPTION_boot },
+    { "snapshot", 0, QEMU_OPTION_snapshot },
+#ifdef TARGET_I386
+    { "no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk },
+#endif
+    { "m", HAS_ARG, QEMU_OPTION_m },
+    { "nographic", 0, QEMU_OPTION_nographic },
+    { "portrait", 0, QEMU_OPTION_portrait },
+    { "k", HAS_ARG, QEMU_OPTION_k },
+#ifdef HAS_AUDIO
+    { "audio-help", 0, QEMU_OPTION_audio_help },
+    { "soundhw", HAS_ARG, QEMU_OPTION_soundhw },
+#endif
+
+    { "net", HAS_ARG, QEMU_OPTION_net},
+#ifdef CONFIG_SLIRP
+    { "tftp", HAS_ARG, QEMU_OPTION_tftp },
+    { "bootp", HAS_ARG, QEMU_OPTION_bootp },
+#ifndef _WIN32
+    { "smb", HAS_ARG, QEMU_OPTION_smb },
+#endif
+    { "redir", HAS_ARG, QEMU_OPTION_redir },
+#endif
+
+    { "kernel", HAS_ARG, QEMU_OPTION_kernel },
+    { "append", HAS_ARG, QEMU_OPTION_append },
+    { "initrd", HAS_ARG, QEMU_OPTION_initrd },
+
+    { "S", 0, QEMU_OPTION_S },
+    { "s", 0, QEMU_OPTION_s },
+    { "p", HAS_ARG, QEMU_OPTION_p },
+    { "d", HAS_ARG, QEMU_OPTION_d },
+    { "hdachs", HAS_ARG, QEMU_OPTION_hdachs },
+    { "L", HAS_ARG, QEMU_OPTION_L },
+    { "bios", HAS_ARG, QEMU_OPTION_bios },
+#ifdef USE_KQEMU
+    { "no-kqemu", 0, QEMU_OPTION_no_kqemu },
+    { "kernel-kqemu", 0, QEMU_OPTION_kernel_kqemu },
+#endif
+#if defined(TARGET_PPC) || defined(TARGET_SPARC)
+    { "g", HAS_ARG, QEMU_OPTION_g },
+#endif
+    { "localtime", 0, QEMU_OPTION_localtime },
+    { "std-vga", 0, QEMU_OPTION_std_vga },
+    { "echr", HAS_ARG, QEMU_OPTION_echr },
+    { "monitor", HAS_ARG, QEMU_OPTION_monitor },
+    { "serial", HAS_ARG, QEMU_OPTION_serial },
+    { "parallel", HAS_ARG, QEMU_OPTION_parallel },
+    { "loadvm", HAS_ARG, QEMU_OPTION_loadvm },
+    { "full-screen", 0, QEMU_OPTION_full_screen },
+#ifdef CONFIG_SDL
+    { "no-frame", 0, QEMU_OPTION_no_frame },
+    { "alt-grab", 0, QEMU_OPTION_alt_grab },
+    { "no-quit", 0, QEMU_OPTION_no_quit },
+#endif
+    { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
+    { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
+    { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
+    { "smp", HAS_ARG, QEMU_OPTION_smp },
+    { "vnc", HAS_ARG, QEMU_OPTION_vnc },
+#ifdef CONFIG_CURSES
+    { "curses", 0, QEMU_OPTION_curses },
+#endif
+
+    /* temporary options */
+    { "usb", 0, QEMU_OPTION_usb },
+    { "cirrusvga", 0, QEMU_OPTION_cirrusvga },
+    { "vmwarevga", 0, QEMU_OPTION_vmsvga },
+    { "no-acpi", 0, QEMU_OPTION_no_acpi },
+    { "no-reboot", 0, QEMU_OPTION_no_reboot },
+    { "no-shutdown", 0, QEMU_OPTION_no_shutdown },
+    { "show-cursor", 0, QEMU_OPTION_show_cursor },
+    { "daemonize", 0, QEMU_OPTION_daemonize },
+    { "option-rom", HAS_ARG, QEMU_OPTION_option_rom },
+#if defined(TARGET_ARM) || defined(TARGET_M68K)
+    { "semihosting", 0, QEMU_OPTION_semihosting },
+#endif
+    { "name", HAS_ARG, QEMU_OPTION_name },
+#if defined(TARGET_SPARC)
+    { "prom-env", HAS_ARG, QEMU_OPTION_prom_env },
+#endif
+#if defined(TARGET_ARM)
+    { "old-param", 0, QEMU_OPTION_old_param },
+#endif
+
+    /* android stuff */
+    { "noaudio", 0, QEMU_OPTION_noaudio },
+    { "mic", HAS_ARG, QEMU_OPTION_mic },
+#ifdef CONFIG_TRACE
+    { "trace", HAS_ARG, QEMU_OPTION_trace_file },
+    { "tracing", HAS_ARG, QEMU_OPTION_tracing },
+    { "trace_miss", 0, QEMU_OPTION_trace_miss },
+    { "trace_addr", 0, QEMU_OPTION_trace_addr },
+    { "dcache_load_miss", HAS_ARG, QEMU_OPTION_dcache_load_miss },
+    { "dcache_store_miss", HAS_ARG, QEMU_OPTION_dcache_store_miss },
+#endif
+#ifdef CONFIG_NAND
+    { "nand", HAS_ARG, QEMU_OPTION_nand },
+#endif
+    { "clock", HAS_ARG, QEMU_OPTION_clock },
+    { NULL, 0, 0 },
+};
+
+/* password input */
+
+int qemu_key_check(BlockDriverState *bs, const char *name)
+{
+    char password[256];
+    int i;
+
+    if (!bdrv_is_encrypted(bs))
+        return 0;
+
+    term_printf("%s is encrypted.\n", name);
+    for(i = 0; i < 3; i++) {
+        monitor_readline("Password: ", 1, password, sizeof(password));
+        if (bdrv_set_key(bs, password) == 0)
+            return 0;
+        term_printf("invalid password\n");
+    }
+    return -EPERM;
+}
+
+static BlockDriverState *get_bdrv(int index)
+{
+    if (index > nb_drives)
+        return NULL;
+    return drives_table[index].bdrv;
+}
+
+static void read_passwords(void)
+{
+    BlockDriverState *bs;
+    int i;
+
+    for(i = 0; i < 6; i++) {
+        bs = get_bdrv(i);
+        if (bs)
+            qemu_key_check(bs, bdrv_get_device_name(bs));
+    }
+}
+
+#ifdef HAS_AUDIO
+struct soundhw soundhw[] = {
+#if 0  /* ANDROID */
+#ifdef TARGET_I386
+    {
+        "pcspk",
+        "PC speaker",
+        0,
+        1,
+        { .init_isa = pcspk_audio_init }
+    },
+#endif
+    {
+        "sb16",
+        "Creative Sound Blaster 16",
+        0,
+        1,
+        { .init_isa = SB16_init }
+    },
+
+#ifdef CONFIG_CS4231A
+    {
+        "cs4231a",
+        "CS4231A",
+        0,
+        1,
+        { .init_isa = cs4231a_init }
+    },
+#endif
+
+#ifdef CONFIG_ADLIB
+    {
+        "adlib",
+#ifdef HAS_YMF262
+        "Yamaha YMF262 (OPL3)",
+#else
+        "Yamaha YM3812 (OPL2)",
+#endif
+        0,
+        1,
+        { .init_isa = Adlib_init }
+    },
+#endif
+
+#ifdef CONFIG_GUS
+    {
+        "gus",
+        "Gravis Ultrasound GF1",
+        0,
+        1,
+        { .init_isa = GUS_init }
+    },
+#endif
+
+#ifdef CONFIG_AC97
+    {
+        "ac97",
+        "Intel 82801AA AC97 Audio",
+        0,
+        0,
+        { .init_pci = ac97_init }
+    },
+#endif
+
+    {
+        "es1370",
+        "ENSONIQ AudioPCI ES1370",
+        0,
+        0,
+        { .init_pci = es1370_init }
+    },
+#endif /* ANDROID */
+
+    { NULL, NULL, 0, 0, { NULL } }
+};
+
+static void select_soundhw (const char *optarg)
+{
+    struct soundhw *c;
+
+    if (*optarg == '?') {
+    show_valid_cards:
+
+        printf ("Valid sound card names (comma separated):\n");
+        for (c = soundhw; c->name; ++c) {
+            printf ("%-11s %s\n", c->name, c->descr);
+        }
+        printf ("\n-soundhw all will enable all of the above\n");
+        exit (*optarg != '?');
+    }
+    else {
+        size_t l;
+        const char *p;
+        char *e;
+        int bad_card = 0;
+
+        if (!strcmp (optarg, "all")) {
+            for (c = soundhw; c->name; ++c) {
+                c->enabled = 1;
+            }
+            return;
+        }
+
+        p = optarg;
+        while (*p) {
+            e = strchr (p, ',');
+            l = !e ? strlen (p) : (size_t) (e - p);
+
+            for (c = soundhw; c->name; ++c) {
+                if (!strncmp (c->name, p, l)) {
+                    c->enabled = 1;
+                    break;
+                }
+            }
+
+            if (!c->name) {
+                if (l > 80) {
+                    fprintf (stderr,
+                             "Unknown sound card name (too big to show)\n");
+                }
+                else {
+                    fprintf (stderr, "Unknown sound card name `%.*s'\n",
+                             (int) l, p);
+                }
+                bad_card = 1;
+            }
+            p += l + (e != NULL);
+        }
+
+        if (bad_card)
+            goto show_valid_cards;
+    }
+}
+#endif
+
+#ifdef _WIN32
+static BOOL WINAPI qemu_ctrl_handler(DWORD type)
+{
+    exit(STATUS_CONTROL_C_EXIT);
+    return TRUE;
+}
+#endif
+
+#define MAX_NET_CLIENTS 32
+
+#ifndef _WIN32
+
+static void termsig_handler(int signal)
+{
+    qemu_system_shutdown_request();
+}
+
+static void termsig_setup(void)
+{
+    struct sigaction act;
+
+    memset(&act, 0, sizeof(act));
+    act.sa_handler = termsig_handler;
+    sigaction(SIGINT,  &act, NULL);
+    sigaction(SIGHUP,  &act, NULL);
+    sigaction(SIGTERM, &act, NULL);
+}
+
+#endif
+
+int main(int argc, char **argv)
+{
+#ifdef CONFIG_GDBSTUB
+    int use_gdbstub;
+    const char *gdbstub_port;
+#endif
+    uint32_t boot_devices_bitmap = 0;
+    int i;
+    int snapshot, linux_boot, net_boot;
+    const char *initrd_filename;
+    const char *kernel_filename, *kernel_cmdline;
+    const char *boot_devices = "";
+    DisplayState *ds = &display_state;
+    int cyls, heads, secs, translation;
+    const char *net_clients[MAX_NET_CLIENTS];
+    int nb_net_clients;
+    int hda_index;
+    int optind;
+    const char *r, *optarg;
+    CharDriverState *monitor_hd;
+    const char *monitor_device;
+    const char *serial_devices[MAX_SERIAL_PORTS];
+    int serial_device_index;
+    const char *parallel_devices[MAX_PARALLEL_PORTS];
+    int parallel_device_index;
+    const char *loadvm = NULL;
+    QEMUMachine *machine;
+    const char *cpu_model;
+    const char *usb_devices[MAX_USB_CMDLINE];
+    int usb_devices_index;
+    int fds[2];
+    int tb_size;
+    const char *pid_file = NULL;
+    VLANState *vlan;
+
+    LIST_INIT (&vm_change_state_head);
+#ifndef _WIN32
+    {
+        struct sigaction act;
+        sigfillset(&act.sa_mask);
+        act.sa_flags = 0;
+        act.sa_handler = SIG_IGN;
+        sigaction(SIGPIPE, &act, NULL);
+    }
+#else
+    SetConsoleCtrlHandler(qemu_ctrl_handler, TRUE);
+    /* Note: cpu_interrupt() is currently not SMP safe, so we force
+       QEMU to run on a single CPU */
+    {
+        HANDLE h;
+        DWORD mask, smask;
+        int i;
+        h = GetCurrentProcess();
+        if (GetProcessAffinityMask(h, &mask, &smask)) {
+            for(i = 0; i < 32; i++) {
+                if (mask & (1 << i))
+                    break;
+            }
+            if (i != 32) {
+                mask = 1 << i;
+                SetProcessAffinityMask(h, mask);
+            }
+        }
+    }
+#endif
+
+    register_machines();
+    machine = first_machine;
+    cpu_model = NULL;
+    initrd_filename = NULL;
+    ram_size = 0;
+    vga_ram_size = VGA_RAM_SIZE;
+#ifdef CONFIG_GDBSTUB
+    use_gdbstub = 0;
+    gdbstub_port = DEFAULT_GDBSTUB_PORT;
+#endif
+    snapshot = 0;
+    nographic = 0;
+    curses = 0;
+    kernel_filename = NULL;
+    kernel_cmdline = "";
+    cyls = heads = secs = 0;
+    translation = BIOS_ATA_TRANSLATION_AUTO;
+    monitor_device = "vc";
+
+    serial_devices[0] = "vc:80Cx24C";
+    for(i = 1; i < MAX_SERIAL_PORTS; i++)
+        serial_devices[i] = NULL;
+    serial_device_index = 0;
+
+    parallel_devices[0] = "vc:640x480";
+    for(i = 1; i < MAX_PARALLEL_PORTS; i++)
+        parallel_devices[i] = NULL;
+    parallel_device_index = 0;
+
+    usb_devices_index = 0;
+
+    nb_net_clients = 0;
+    nb_drives = 0;
+    nb_drives_opt = 0;
+    hda_index = -1;
+
+    nb_nics = 0;
+
+    tb_size = 0;
+	android_audio_enabled = 1;
+
+    optind = 1;
+    for(;;) {
+        if (optind >= argc)
+            break;
+        r = argv[optind];
+        if (r[0] != '-') {
+	    hda_index = drive_add(argv[optind++], HD_ALIAS, 0);
+        } else {
+            const QEMUOption *popt;
+
+            optind++;
+            /* Treat --foo the same as -foo.  */
+            if (r[1] == '-')
+                r++;
+            popt = qemu_options;
+            for(;;) {
+                if (!popt->name) {
+                    fprintf(stderr, "%s: invalid option -- '%s'\n",
+                            argv[0], r);
+                    exit(1);
+                }
+                if (!strcmp(popt->name, r + 1))
+                    break;
+                popt++;
+            }
+            if (popt->flags & HAS_ARG) {
+                if (optind >= argc) {
+                    fprintf(stderr, "%s: option '%s' requires an argument\n",
+                            argv[0], r);
+                    exit(1);
+                }
+                optarg = argv[optind++];
+            } else {
+                optarg = NULL;
+            }
+
+            switch(popt->index) {
+            case QEMU_OPTION_M:
+                machine = find_machine(optarg);
+                if (!machine) {
+                    QEMUMachine *m;
+                    printf("Supported machines are:\n");
+                    for(m = first_machine; m != NULL; m = m->next) {
+                        printf("%-10s %s%s\n",
+                               m->name, m->desc,
+                               m == first_machine ? " (default)" : "");
+                    }
+                    exit(*optarg != '?');
+                }
+                break;
+            case QEMU_OPTION_cpu:
+                /* hw initialization will check this */
+                if (*optarg == '?') {
+/* XXX: implement xxx_cpu_list for targets that still miss it */
+#if defined(cpu_list)
+                    cpu_list(stdout, &fprintf);
+#endif
+                    exit(0);
+                } else {
+                    cpu_model = optarg;
+                }
+                break;
+            case QEMU_OPTION_initrd:
+                initrd_filename = optarg;
+                break;
+            case QEMU_OPTION_hda:
+                if (cyls == 0)
+                    hda_index = drive_add(optarg, HD_ALIAS, 0);
+                else
+                    hda_index = drive_add(optarg, HD_ALIAS
+			     ",cyls=%d,heads=%d,secs=%d%s",
+                             0, cyls, heads, secs,
+                             translation == BIOS_ATA_TRANSLATION_LBA ?
+                                 ",trans=lba" :
+                             translation == BIOS_ATA_TRANSLATION_NONE ?
+                                 ",trans=none" : "");
+                 break;
+            case QEMU_OPTION_hdb:
+            case QEMU_OPTION_hdc:
+            case QEMU_OPTION_hdd:
+                drive_add(optarg, HD_ALIAS, popt->index - QEMU_OPTION_hda);
+                break;
+            case QEMU_OPTION_drive:
+                drive_add(NULL, "%s", optarg);
+	        break;
+            case QEMU_OPTION_mtdblock:
+                drive_add(optarg, MTD_ALIAS);
+                break;
+            case QEMU_OPTION_sd:
+                drive_add(optarg, SD_ALIAS);
+                break;
+            case QEMU_OPTION_pflash:
+                drive_add(optarg, PFLASH_ALIAS);
+                break;
+            case QEMU_OPTION_snapshot:
+                snapshot = 1;
+                break;
+            case QEMU_OPTION_hdachs:
+                {
+                    const char *p;
+                    p = optarg;
+                    cyls = strtol(p, (char **)&p, 0);
+                    if (cyls < 1 || cyls > 16383)
+                        goto chs_fail;
+                    if (*p != ',')
+                        goto chs_fail;
+                    p++;
+                    heads = strtol(p, (char **)&p, 0);
+                    if (heads < 1 || heads > 16)
+                        goto chs_fail;
+                    if (*p != ',')
+                        goto chs_fail;
+                    p++;
+                    secs = strtol(p, (char **)&p, 0);
+                    if (secs < 1 || secs > 63)
+                        goto chs_fail;
+                    if (*p == ',') {
+                        p++;
+                        if (!strcmp(p, "none"))
+                            translation = BIOS_ATA_TRANSLATION_NONE;
+                        else if (!strcmp(p, "lba"))
+                            translation = BIOS_ATA_TRANSLATION_LBA;
+                        else if (!strcmp(p, "auto"))
+                            translation = BIOS_ATA_TRANSLATION_AUTO;
+                        else
+                            goto chs_fail;
+                    } else if (*p != '\0') {
+                    chs_fail:
+                        fprintf(stderr, "qemu: invalid physical CHS format\n");
+                        exit(1);
+                    }
+		    if (hda_index != -1)
+                        snprintf(drives_opt[hda_index].opt,
+                                 sizeof(drives_opt[hda_index].opt),
+                                 HD_ALIAS ",cyls=%d,heads=%d,secs=%d%s",
+                                 0, cyls, heads, secs,
+			         translation == BIOS_ATA_TRANSLATION_LBA ?
+			     	    ",trans=lba" :
+			         translation == BIOS_ATA_TRANSLATION_NONE ?
+			             ",trans=none" : "");
+                }
+                break;
+            case QEMU_OPTION_nographic:
+                nographic = 1;
+                break;
+#ifdef CONFIG_CURSES
+            case QEMU_OPTION_curses:
+                curses = 1;
+                break;
+#endif
+            case QEMU_OPTION_portrait:
+                graphic_rotate = 1;
+                break;
+            case QEMU_OPTION_kernel:
+                kernel_filename = optarg;
+                break;
+            case QEMU_OPTION_append:
+                kernel_cmdline = optarg;
+                break;
+            case QEMU_OPTION_cdrom:
+                drive_add(optarg, CDROM_ALIAS);
+                break;
+            case QEMU_OPTION_boot:
+                boot_devices = optarg;
+                /* We just do some generic consistency checks */
+                {
+                    /* Could easily be extended to 64 devices if needed */
+                    const char *p;
+                    
+                    boot_devices_bitmap = 0;
+                    for (p = boot_devices; *p != '\0'; p++) {
+                        /* Allowed boot devices are:
+                         * a b     : floppy disk drives
+                         * c ... f : IDE disk drives
+                         * g ... m : machine implementation dependant drives
+                         * n ... p : network devices
+                         * It's up to each machine implementation to check
+                         * if the given boot devices match the actual hardware
+                         * implementation and firmware features.
+                         */
+                        if (*p < 'a' || *p > 'q') {
+                            fprintf(stderr, "Invalid boot device '%c'\n", *p);
+                            exit(1);
+                        }
+                        if (boot_devices_bitmap & (1 << (*p - 'a'))) {
+                            fprintf(stderr,
+                                    "Boot device '%c' was given twice\n",*p);
+                            exit(1);
+                        }
+                        boot_devices_bitmap |= 1 << (*p - 'a');
+                    }
+                }
+                break;
+            case QEMU_OPTION_fda:
+            case QEMU_OPTION_fdb:
+                drive_add(optarg, FD_ALIAS, popt->index - QEMU_OPTION_fda);
+                break;
+#ifdef TARGET_I386
+            case QEMU_OPTION_no_fd_bootchk:
+                fd_bootchk = 0;
+                break;
+#endif
+            case QEMU_OPTION_net:
+                if (nb_net_clients >= MAX_NET_CLIENTS) {
+                    fprintf(stderr, "qemu: too many network clients\n");
+                    exit(1);
+                }
+                net_clients[nb_net_clients] = optarg;
+                nb_net_clients++;
+                break;
+#ifdef CONFIG_SLIRP
+            case QEMU_OPTION_tftp:
+		tftp_prefix = optarg;
+                break;
+            case QEMU_OPTION_bootp:
+                bootp_filename = optarg;
+                break;
+#if 0  /* ANDROID disabled */
+            case QEMU_OPTION_smb:
+		net_slirp_smb(optarg);
+                break;
+#endif
+            case QEMU_OPTION_redir:
+                net_slirp_redir(optarg);
+                break;
+#endif
+#ifdef HAS_AUDIO
+            case QEMU_OPTION_audio_help:
+                AUD_help ();
+                exit (0);
+                break;
+            case QEMU_OPTION_soundhw:
+                select_soundhw (optarg);
+                break;
+#endif
+            case QEMU_OPTION_h:
+                qemu_help(0);
+                break;
+            case QEMU_OPTION_m: {
+                uint64_t value;
+                char *ptr;
+
+                value = strtoul(optarg, &ptr, 10);
+                switch (*ptr) {
+                case 0: case 'M': case 'm':
+                    value <<= 20;
+                    break;
+                case 'G': case 'g':
+                    value <<= 30;
+                    break;
+                default:
+                    fprintf(stderr, "qemu: invalid ram size: %s\n", optarg);
+                    exit(1);
+                }
+
+                /* On 32-bit hosts, QEMU is limited by virtual address space */
+                if (value > (2047 << 20)
+#ifndef USE_KQEMU
+                    && HOST_LONG_BITS == 32
+#endif
+                    ) {
+                    fprintf(stderr, "qemu: at most 2047 MB RAM can be simulated\n");
+                    exit(1);
+                }
+                if (value != (uint64_t)(ram_addr_t)value) {
+                    fprintf(stderr, "qemu: ram size too large\n");
+                    exit(1);
+                }
+                ram_size = value;
+                break;
+            }
+            case QEMU_OPTION_d:
+                {
+                    int mask;
+                    CPULogItem *item;
+
+                    mask = cpu_str_to_log_mask(optarg);
+                    if (!mask) {
+                        printf("Log items (comma separated):\n");
+                    for(item = cpu_log_items; item->mask != 0; item++) {
+                        printf("%-10s %s\n", item->name, item->help);
+                    }
+                    exit(1);
+                    }
+                    cpu_set_log(mask);
+                }
+                break;
+#ifdef CONFIG_GDBSTUB
+            case QEMU_OPTION_s:
+                use_gdbstub = 1;
+                break;
+            case QEMU_OPTION_p:
+                gdbstub_port = optarg;
+                break;
+#endif
+            case QEMU_OPTION_L:
+                bios_dir = optarg;
+                break;
+            case QEMU_OPTION_S:
+#if 1  /* ANDROID */
+                fprintf(stderr, "Sorry, stopped launch is not supported in the Android emulator\n" );
+                exit(1);
+#else
+                autostart = 0;
+                break;
+#endif
+	    case QEMU_OPTION_k:
+		keyboard_layout = optarg;
+		break;
+            case QEMU_OPTION_localtime:
+                rtc_utc = 0;
+                break;
+            case QEMU_OPTION_cirrusvga:
+                cirrus_vga_enabled = 1;
+                vmsvga_enabled = 0;
+                break;
+            case QEMU_OPTION_vmsvga:
+                cirrus_vga_enabled = 0;
+                vmsvga_enabled = 1;
+                break;
+            case QEMU_OPTION_std_vga:
+                cirrus_vga_enabled = 0;
+                vmsvga_enabled = 0;
+                break;
+            case QEMU_OPTION_g:
+                {
+                    const char *p;
+                    int w, h, depth;
+                    p = optarg;
+                    w = strtol(p, (char **)&p, 10);
+                    if (w <= 0) {
+                    graphic_error:
+                        fprintf(stderr, "qemu: invalid resolution or depth\n");
+                        exit(1);
+                    }
+                    if (*p != 'x')
+                        goto graphic_error;
+                    p++;
+                    h = strtol(p, (char **)&p, 10);
+                    if (h <= 0)
+                        goto graphic_error;
+                    if (*p == 'x') {
+                        p++;
+                        depth = strtol(p, (char **)&p, 10);
+                        if (depth != 8 && depth != 15 && depth != 16 &&
+                            depth != 24 && depth != 32)
+                            goto graphic_error;
+                    } else if (*p == '\0') {
+                        depth = graphic_depth;
+                    } else {
+                        goto graphic_error;
+                    }
+
+                    graphic_width = w;
+                    graphic_height = h;
+                    graphic_depth = depth;
+                }
+                break;
+            case QEMU_OPTION_echr:
+                {
+                    char *r;
+                    term_escape_char = strtol(optarg, &r, 0);
+                    if (r == optarg)
+                        printf("Bad argument to echr\n");
+                    break;
+                }
+            case QEMU_OPTION_monitor:
+                monitor_device = optarg;
+                break;
+            case QEMU_OPTION_serial:
+                if (serial_device_index >= MAX_SERIAL_PORTS) {
+                    fprintf(stderr, "qemu: too many serial ports\n");
+                    exit(1);
+                }
+                serial_devices[serial_device_index] = optarg;
+                serial_device_index++;
+                break;
+            case QEMU_OPTION_parallel:
+                if (parallel_device_index >= MAX_PARALLEL_PORTS) {
+                    fprintf(stderr, "qemu: too many parallel ports\n");
+                    exit(1);
+                }
+                parallel_devices[parallel_device_index] = optarg;
+                parallel_device_index++;
+                break;
+	    case QEMU_OPTION_loadvm:
+		loadvm = optarg;
+		break;
+            case QEMU_OPTION_full_screen:
+                full_screen = 1;
+                break;
+#ifdef CONFIG_SDL
+            case QEMU_OPTION_no_frame:
+                no_frame = 1;
+                break;
+            case QEMU_OPTION_alt_grab:
+                alt_grab = 1;
+                break;
+            case QEMU_OPTION_no_quit:
+                no_quit = 1;
+                break;
+#endif
+            case QEMU_OPTION_pidfile:
+                pid_file = optarg;
+                break;
+#ifdef TARGET_I386
+            case QEMU_OPTION_win2k_hack:
+                win2k_install_hack = 1;
+                break;
+#endif
+#ifdef USE_KQEMU
+            case QEMU_OPTION_no_kqemu:
+                kqemu_allowed = 0;
+                break;
+            case QEMU_OPTION_kernel_kqemu:
+                kqemu_allowed = 2;
+                break;
+#endif
+            case QEMU_OPTION_usb:
+                usb_enabled = 1;
+                break;
+            case QEMU_OPTION_usbdevice:
+                usb_enabled = 1;
+                if (usb_devices_index >= MAX_USB_CMDLINE) {
+                    fprintf(stderr, "Too many USB devices\n");
+                    exit(1);
+                }
+                usb_devices[usb_devices_index] = optarg;
+                usb_devices_index++;
+                break;
+            case QEMU_OPTION_smp:
+                smp_cpus = atoi(optarg);
+                if (smp_cpus < 1 || smp_cpus > MAX_CPUS) {
+                    fprintf(stderr, "Invalid number of CPUs\n");
+                    exit(1);
+                }
+                break;
+	    case QEMU_OPTION_vnc:
+		vnc_display = optarg;
+		break;
+            case QEMU_OPTION_no_acpi:
+                acpi_enabled = 0;
+                break;
+            case QEMU_OPTION_no_reboot:
+                no_reboot = 1;
+                break;
+            case QEMU_OPTION_no_shutdown:
+                no_shutdown = 1;
+                break;
+            case QEMU_OPTION_show_cursor:
+                cursor_hide = 0;
+                break;
+	    case QEMU_OPTION_daemonize:
+		daemonize = 1;
+		break;
+	    case QEMU_OPTION_option_rom:
+		if (nb_option_roms >= MAX_OPTION_ROMS) {
+		    fprintf(stderr, "Too many option ROMs\n");
+		    exit(1);
+		}
+		option_rom[nb_option_roms] = optarg;
+		nb_option_roms++;
+		break;
+            case QEMU_OPTION_semihosting:
+                semihosting_enabled = 1;
+                break;
+            case QEMU_OPTION_name:
+                qemu_name = optarg;
+                break;
+#ifdef TARGET_SPARC
+            case QEMU_OPTION_prom_env:
+                if (nb_prom_envs >= MAX_PROM_ENVS) {
+                    fprintf(stderr, "Too many prom variables\n");
+                    exit(1);
+                }
+                prom_envs[nb_prom_envs] = optarg;
+                nb_prom_envs++;
+                break;
+#endif
+#ifdef TARGET_ARM
+            case QEMU_OPTION_old_param:
+                old_param = 1;
+                break;
+#endif
+            case QEMU_OPTION_clock:
+                configure_alarms(optarg);
+                break;
+            case QEMU_OPTION_startdate:
+                {
+                    struct tm tm;
+                    time_t rtc_start_date;
+                    if (!strcmp(optarg, "now")) {
+                        rtc_date_offset = -1;
+                    } else {
+                        if (sscanf(optarg, "%d-%d-%dT%d:%d:%d",
+                               &tm.tm_year,
+                               &tm.tm_mon,
+                               &tm.tm_mday,
+                               &tm.tm_hour,
+                               &tm.tm_min,
+                               &tm.tm_sec) == 6) {
+                            /* OK */
+                        } else if (sscanf(optarg, "%d-%d-%d",
+                                          &tm.tm_year,
+                                          &tm.tm_mon,
+                                          &tm.tm_mday) == 3) {
+                            tm.tm_hour = 0;
+                            tm.tm_min = 0;
+                            tm.tm_sec = 0;
+                        } else {
+                            goto date_fail;
+                        }
+                        tm.tm_year -= 1900;
+                        tm.tm_mon--;
+                        rtc_start_date = mktimegm(&tm);
+                        if (rtc_start_date == -1) {
+                        date_fail:
+                            fprintf(stderr, "Invalid date format. Valid format are:\n"
+                                    "'now' or '2006-06-17T16:01:21' or '2006-06-17'\n");
+                            exit(1);
+                        }
+                        rtc_date_offset = time(NULL) - rtc_start_date;
+                    }
+                }
+                break;
+            case QEMU_OPTION_tb_size:
+                tb_size = strtol(optarg, NULL, 0);
+                if (tb_size < 0)
+                    tb_size = 0;
+                break;
+            case QEMU_OPTION_icount:
+                use_icount = 1;
+                if (strcmp(optarg, "auto") == 0) {
+                    icount_time_shift = -1;
+                } else {
+                    icount_time_shift = strtol(optarg, NULL, 0);
+                }
+                break;
+
+            case QEMU_OPTION_noaudio:
+                android_audio_enabled = 0;
+                break;
+            case QEMU_OPTION_mic:
+                audio_input_source = (char*)optarg;
+                break;
+#ifdef CONFIG_TRACE
+            case QEMU_OPTION_trace_file:
+                trace_filename = optarg;
+                tracing = 1;
+                break;
+            case QEMU_OPTION_trace_miss:
+                trace_cache_miss = 1;
+                break;
+            case QEMU_OPTION_trace_addr:
+                trace_all_addr = 1;
+                break;
+            case QEMU_OPTION_tracing:
+                if (strcmp(optarg, "off") == 0)
+                    tracing = 0;
+                else if (strcmp(optarg, "on") == 0 && trace_filename)
+                    tracing = 1;
+                else {
+                    fprintf(stderr, "Unexpected option to -tracing ('%s')\n",
+                            optarg);
+                    exit(1);
+                }
+                break;
+            case QEMU_OPTION_dcache_load_miss:
+                dcache_load_miss_penalty = atoi(optarg);
+                break;
+            case QEMU_OPTION_dcache_store_miss:
+                dcache_store_miss_penalty = atoi(optarg);
+                break;
+#endif
+#ifdef CONFIG_NAND
+            case QEMU_OPTION_nand:
+                nand_add_dev(optarg);
+                break;
+#endif
+            }
+        }
+    }
+
+    if (nographic) {
+       if (serial_device_index == 0)
+           serial_devices[0] = "stdio";
+       if (parallel_device_index == 0)
+           parallel_devices[0] = "null";
+       if (strncmp(monitor_device, "vc", 2) == 0)
+           monitor_device = "stdio";
+    }
+
+#ifndef _WIN32
+    if (daemonize) {
+	pid_t pid;
+
+	if (pipe(fds) == -1)
+	    exit(1);
+
+	pid = fork();
+	if (pid > 0) {
+	    uint8_t status;
+	    ssize_t len;
+
+	    close(fds[1]);
+
+	again:
+            len = read(fds[0], &status, 1);
+            if (len == -1 && (errno == EINTR))
+                goto again;
+
+            if (len != 1)
+                exit(1);
+            else if (status == 1) {
+                fprintf(stderr, "Could not acquire pidfile\n");
+                exit(1);
+            } else
+                exit(0);
+	} else if (pid < 0)
+            exit(1);
+
+	setsid();
+
+	pid = fork();
+	if (pid > 0)
+	    exit(0);
+	else if (pid < 0)
+	    exit(1);
+
+	umask(027);
+
+        signal(SIGTSTP, SIG_IGN);
+        signal(SIGTTOU, SIG_IGN);
+        signal(SIGTTIN, SIG_IGN);
+    }
+#endif
+
+    if (pid_file && qemu_create_pidfile(pid_file) != 0) {
+        if (daemonize) {
+            uint8_t status = 1;
+            write(fds[1], &status, 1);
+        } else
+            fprintf(stderr, "Could not acquire pid file\n");
+        exit(1);
+    }
+
+#ifdef USE_KQEMU
+    if (smp_cpus > 1)
+        kqemu_allowed = 0;
+#endif
+    linux_boot = (kernel_filename != NULL);
+    net_boot = (boot_devices_bitmap >> ('n' - 'a')) & 0xF;
+
+    if (!linux_boot && net_boot == 0 &&
+        !machine->nodisk_ok && nb_drives_opt == 0)
+        qemu_help(1);
+
+    if (!linux_boot && *kernel_cmdline != '\0') {
+        fprintf(stderr, "-append only allowed with -kernel option\n");
+        exit(1);
+    }
+
+    if (!linux_boot && initrd_filename != NULL) {
+        fprintf(stderr, "-initrd only allowed with -kernel option\n");
+        exit(1);
+    }
+
+    /* boot to floppy or the default cd if no hard disk defined yet */
+    if (!boot_devices[0]) {
+        boot_devices = "cad";
+    }
+    setvbuf(stdout, NULL, _IOLBF, 0);
+
+    init_timers();
+    init_timer_alarm();
+    qemu_aio_init();
+    if (use_icount && icount_time_shift < 0) {
+        use_icount = 2;
+        /* 125MIPS seems a reasonable initial guess at the guest speed.
+           It will be corrected fairly quickly anyway.  */
+        icount_time_shift = 3;
+        init_icount_adjust();
+    }
+
+#ifdef _WIN32
+    socket_init();
+#endif
+
+    /* init network clients */
+    if (nb_net_clients == 0) {
+        /* if no clients, we use a default config */
+        net_clients[nb_net_clients++] = "nic";
+#ifdef CONFIG_SLIRP
+        net_clients[nb_net_clients++] = "user";
+#endif
+    }
+
+    for(i = 0;i < nb_net_clients; i++) {
+        if (net_client_parse(net_clients[i]) < 0)
+            exit(1);
+    }
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+        if (vlan->nb_guest_devs == 0 && vlan->nb_host_devs == 0)
+            continue;
+        if (vlan->nb_guest_devs == 0)
+            fprintf(stderr, "Warning: vlan %d with no nics\n", vlan->id);
+        if (vlan->nb_host_devs == 0)
+            fprintf(stderr,
+                    "Warning: vlan %d is not connected to host network\n",
+                    vlan->id);
+    }
+
+#ifdef TARGET_I386
+    /* XXX: this should be moved in the PC machine instantiation code */
+    if (net_boot != 0) {
+        int netroms = 0;
+	for (i = 0; i < nb_nics && i < 4; i++) {
+	    const char *model = nd_table[i].model;
+	    char buf[1024];
+            if (net_boot & (1 << i)) {
+                if (model == NULL)
+                    model = "ne2k_pci";
+                snprintf(buf, sizeof(buf), "%s/pxe-%s.bin", bios_dir, model);
+                if (get_image_size(buf) > 0) {
+                    if (nb_option_roms >= MAX_OPTION_ROMS) {
+                        fprintf(stderr, "Too many option ROMs\n");
+                        exit(1);
+                    }
+                    option_rom[nb_option_roms] = strdup(buf);
+                    nb_option_roms++;
+                    netroms++;
+                }
+            }
+	}
+	if (netroms == 0) {
+	    fprintf(stderr, "No valid PXE rom found for network device\n");
+	    exit(1);
+	}
+    }
+#endif
+
+    /* init the memory */
+    phys_ram_size = machine->ram_require & ~RAMSIZE_FIXED;
+
+    if (machine->ram_require & RAMSIZE_FIXED) {
+        if (ram_size > 0) {
+            if (ram_size < phys_ram_size) {
+                fprintf(stderr, "Machine `%s' requires %llu bytes of memory\n",
+                                machine->name, (unsigned long long) phys_ram_size);
+                exit(-1);
+            }
+
+            phys_ram_size = ram_size;
+        } else
+            ram_size = phys_ram_size;
+    } else {
+        if (ram_size == 0)
+            ram_size = DEFAULT_RAM_SIZE * 1024 * 1024;
+
+        phys_ram_size += ram_size;
+    }
+
+    phys_ram_base = qemu_vmalloc(phys_ram_size);
+    if (!phys_ram_base) {
+        fprintf(stderr, "Could not allocate physical memory\n");
+        exit(1);
+    }
+
+    /* init the dynamic translator */
+    cpu_exec_init_all(tb_size * 1024 * 1024);
+
+    bdrv_init();
+
+    /* we always create the cdrom drive, even if no disk is there */
+
+    if (nb_drives_opt < MAX_DRIVES)
+        drive_add(NULL, CDROM_ALIAS);
+
+    /* we always create at least one floppy */
+
+    if (nb_drives_opt < MAX_DRIVES)
+        drive_add(NULL, FD_ALIAS, 0);
+
+    /* we always create one sd slot, even if no card is in it */
+
+    if (nb_drives_opt < MAX_DRIVES)
+        drive_add(NULL, SD_ALIAS);
+
+    /* open the virtual block devices */
+
+    for(i = 0; i < nb_drives_opt; i++)
+        if (drive_init(&drives_opt[i], snapshot, machine) == -1)
+	    exit(1);
+
+    register_savevm("timer", 0, 2, timer_save, timer_load, NULL);
+    register_savevm("ram", 0, 2, ram_save, ram_load, NULL);
+
+    /* terminal init */
+    memset(&display_state, 0, sizeof(display_state));
+    if (nographic) {
+        if (curses) {
+            fprintf(stderr, "fatal: -nographic can't be used with -curses\n");
+            exit(1);
+        }
+        /* nearly nothing to do */
+        dumb_display_init(ds);
+    } else if (vnc_display != NULL) {
+        vnc_display_init(ds);
+        if (vnc_display_open(ds, vnc_display) < 0)
+            exit(1);
+    } else
+#if defined(CONFIG_CURSES)
+    if (curses) {
+        curses_display_init(ds, full_screen);
+    } else
+#endif
+    {
+#if defined(CONFIG_SDL)
+        sdl_display_init(ds, full_screen, no_frame);
+#elif defined(CONFIG_COCOA)
+        cocoa_display_init(ds, full_screen);
+#else
+        dumb_display_init(ds);
+#endif
+    }
+
+#ifndef _WIN32
+    /* must be after terminal init, SDL library changes signal handlers */
+    termsig_setup();
+#endif
+
+    /* Maintain compatibility with multiple stdio monitors */
+    if (!strcmp(monitor_device,"stdio")) {
+        for (i = 0; i < MAX_SERIAL_PORTS; i++) {
+            const char *devname = serial_devices[i];
+            if (devname && !strcmp(devname,"mon:stdio")) {
+                monitor_device = NULL;
+                break;
+            } else if (devname && !strcmp(devname,"stdio")) {
+                monitor_device = NULL;
+                serial_devices[i] = "mon:stdio";
+                break;
+            }
+        }
+    }
+    if (monitor_device) {
+        monitor_hd = qemu_chr_open(monitor_device);
+        if (!monitor_hd) {
+            fprintf(stderr, "qemu: could not open monitor device '%s'\n", monitor_device);
+            exit(1);
+        }
+        monitor_init(monitor_hd, !nographic);
+    }
+
+    for(i = 0; i < MAX_SERIAL_PORTS; i++) {
+        const char *devname = serial_devices[i];
+        if (devname && strcmp(devname, "none")) {
+            serial_hds[i] = qemu_chr_open(devname);
+            if (!serial_hds[i]) {
+                fprintf(stderr, "qemu: could not open serial device '%s'\n",
+                        devname);
+                exit(1);
+            }
+            if (strstart(devname, "vc", 0))
+                qemu_chr_printf(serial_hds[i], "serial%d console\r\n", i);
+        }
+    }
+
+    for(i = 0; i < MAX_PARALLEL_PORTS; i++) {
+        const char *devname = parallel_devices[i];
+        if (devname && strcmp(devname, "none")) {
+            parallel_hds[i] = qemu_chr_open(devname);
+            if (!parallel_hds[i]) {
+                fprintf(stderr, "qemu: could not open parallel device '%s'\n",
+                        devname);
+                exit(1);
+            }
+            if (strstart(devname, "vc", 0))
+                qemu_chr_printf(parallel_hds[i], "parallel%d console\r\n", i);
+        }
+    }
+
+#ifdef CONFIG_TRACE
+    if (trace_filename) {
+        trace_init(trace_filename);
+        dcache_init(dcache_size, dcache_ways, dcache_line_size,
+                    dcache_replace_policy, dcache_load_miss_penalty,
+                    dcache_store_miss_penalty);
+        fprintf(stderr, "-- When done tracing, exit the emulator. --\n");
+    }
+#endif
+
+    machine->init(ram_size, vga_ram_size, boot_devices, ds,
+                  kernel_filename, kernel_cmdline, initrd_filename, cpu_model);
+
+    /* init USB devices */
+    if (usb_enabled) {
+        for(i = 0; i < usb_devices_index; i++) {
+            if (usb_device_add(usb_devices[i]) < 0) {
+                fprintf(stderr, "Warning: could not add USB device %s\n",
+                        usb_devices[i]);
+            }
+        }
+    }
+
+    if (display_state.dpy_refresh) {
+        display_state.gui_timer = qemu_new_timer(rt_clock, gui_update, &display_state);
+        qemu_mod_timer(display_state.gui_timer, qemu_get_clock(rt_clock));
+    }
+
+#ifdef CONFIG_GDBSTUB
+    if (use_gdbstub) {
+        /* XXX: use standard host:port notation and modify options
+           accordingly. */
+        if (gdbserver_start(gdbstub_port) < 0) {
+            fprintf(stderr, "qemu: could not open gdbstub device on port '%s'\n",
+                    gdbstub_port);
+            exit(1);
+        }
+    }
+#endif
+
+    if (loadvm)
+        do_loadvm(loadvm);
+
+    /* call android-specific setup function */
+    android_emulation_setup();
+
+    {
+        /* XXX: simplify init */
+        read_passwords();
+        if (autostart) {
+            vm_start();
+        }
+    }
+
+    if (daemonize) {
+	uint8_t status = 0;
+	ssize_t len;
+	int fd;
+
+    again1:
+	len = write(fds[1], &status, 1);
+	if (len == -1 && (errno == EINTR))
+	    goto again1;
+
+	if (len != 1)
+	    exit(1);
+
+	chdir("/");
+	TFR(fd = open("/dev/null", O_RDWR));
+	if (fd == -1)
+	    exit(1);
+
+	dup2(fd, 0);
+	dup2(fd, 1);
+	dup2(fd, 2);
+
+	close(fd);
+    }
+
+    main_loop();
+    quit_timers();
+
+#if !defined(_WIN32)
+    /* close network clients */
+    for(vlan = first_vlan; vlan != NULL; vlan = vlan->next) {
+        VLANClientState *vc;
+
+        for(vc = vlan->first_client; vc != NULL; vc = vc->next) {
+            if (vc->fd_read == tap_receive) {
+                char ifname[64];
+                TAPState *s = vc->opaque;
+
+                if (sscanf(vc->info_str, "tap: ifname=%63s ", ifname) == 1 &&
+                    s->down_script[0])
+                    launch_script(s->down_script, ifname, s->fd);
+            }
+#if defined(CONFIG_VDE)
+            if (vc->fd_read == vde_from_qemu) {
+                VDEState *s = vc->opaque;
+                vde_close(s->vde);
+            }
+#endif
+        }
+    }
+#endif
+
+    android_emulation_teardown();
+    return 0;
+}
diff --git a/vnc.c b/vnc.c
new file mode 100644
index 0000000..8b7d19f
--- /dev/null
+++ b/vnc.c
@@ -0,0 +1,2446 @@
+/*
+ * QEMU VNC display driver
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2006 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu-common.h"
+#include "console.h"
+#include "sysemu.h"
+#include "qemu_socket.h"
+#include "qemu-timer.h"
+
+#define VNC_REFRESH_INTERVAL (1000 / 30)
+
+#include "vnc_keysym.h"
+#include "keymaps.c"
+#include "d3des.h"
+
+#ifdef CONFIG_VNC_TLS
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#endif /* CONFIG_VNC_TLS */
+
+// #define _VNC_DEBUG 1
+
+#ifdef _VNC_DEBUG
+#define VNC_DEBUG(fmt, ...) do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
+
+#if CONFIG_VNC_TLS && _VNC_DEBUG >= 2
+/* Very verbose, so only enabled for _VNC_DEBUG >= 2 */
+static void vnc_debug_gnutls_log(int level, const char* str) {
+    VNC_DEBUG("%d %s", level, str);
+}
+#endif /* CONFIG_VNC_TLS && _VNC_DEBUG */
+#else
+#define VNC_DEBUG(fmt, ...) do { } while (0)
+#endif
+
+
+typedef struct Buffer
+{
+    size_t capacity;
+    size_t offset;
+    uint8_t *buffer;
+} Buffer;
+
+typedef struct VncState VncState;
+
+typedef int VncReadEvent(VncState *vs, uint8_t *data, size_t len);
+
+typedef void VncWritePixels(VncState *vs, void *data, int size);
+
+typedef void VncSendHextileTile(VncState *vs,
+                                int x, int y, int w, int h,
+                                void *last_bg,
+                                void *last_fg,
+                                int *has_bg, int *has_fg);
+
+#define VNC_MAX_WIDTH 2048
+#define VNC_MAX_HEIGHT 2048
+#define VNC_DIRTY_WORDS (VNC_MAX_WIDTH / (16 * 32))
+
+#define VNC_AUTH_CHALLENGE_SIZE 16
+
+enum {
+    VNC_AUTH_INVALID = 0,
+    VNC_AUTH_NONE = 1,
+    VNC_AUTH_VNC = 2,
+    VNC_AUTH_RA2 = 5,
+    VNC_AUTH_RA2NE = 6,
+    VNC_AUTH_TIGHT = 16,
+    VNC_AUTH_ULTRA = 17,
+    VNC_AUTH_TLS = 18,
+    VNC_AUTH_VENCRYPT = 19
+};
+
+#ifdef CONFIG_VNC_TLS
+enum {
+    VNC_WIREMODE_CLEAR,
+    VNC_WIREMODE_TLS,
+};
+
+enum {
+    VNC_AUTH_VENCRYPT_PLAIN = 256,
+    VNC_AUTH_VENCRYPT_TLSNONE = 257,
+    VNC_AUTH_VENCRYPT_TLSVNC = 258,
+    VNC_AUTH_VENCRYPT_TLSPLAIN = 259,
+    VNC_AUTH_VENCRYPT_X509NONE = 260,
+    VNC_AUTH_VENCRYPT_X509VNC = 261,
+    VNC_AUTH_VENCRYPT_X509PLAIN = 262,
+};
+
+#define X509_CA_CERT_FILE "ca-cert.pem"
+#define X509_CA_CRL_FILE "ca-crl.pem"
+#define X509_SERVER_KEY_FILE "server-key.pem"
+#define X509_SERVER_CERT_FILE "server-cert.pem"
+
+#endif /* CONFIG_VNC_TLS */
+
+struct VncState
+{
+    QEMUTimer *timer;
+    int lsock;
+    int csock;
+    DisplayState *ds;
+    int need_update;
+    int width;
+    int height;
+    uint32_t dirty_row[VNC_MAX_HEIGHT][VNC_DIRTY_WORDS];
+    char *old_data;
+    int depth; /* internal VNC frame buffer byte per pixel */
+    int has_resize;
+    int has_hextile;
+    int has_pointer_type_change;
+    int has_WMVi;
+    int absolute;
+    int last_x;
+    int last_y;
+
+    int major;
+    int minor;
+
+    char *display;
+    char *password;
+    int auth;
+#ifdef CONFIG_VNC_TLS
+    int subauth;
+    int x509verify;
+
+    char *x509cacert;
+    char *x509cacrl;
+    char *x509cert;
+    char *x509key;
+#endif
+    char challenge[VNC_AUTH_CHALLENGE_SIZE];
+
+#ifdef CONFIG_VNC_TLS
+    int wiremode;
+    gnutls_session_t tls_session;
+#endif
+
+    Buffer output;
+    Buffer input;
+    kbd_layout_t *kbd_layout;
+    /* current output mode information */
+    VncWritePixels *write_pixels;
+    VncSendHextileTile *send_hextile_tile;
+    int pix_bpp, pix_big_endian;
+    int client_red_shift, client_red_max, server_red_shift, server_red_max;
+    int client_green_shift, client_green_max, server_green_shift, server_green_max;
+    int client_blue_shift, client_blue_max, server_blue_shift, server_blue_max;
+
+    VncReadEvent *read_handler;
+    size_t read_handler_expect;
+    /* input */
+    uint8_t modifiers_state[256];
+};
+
+static VncState *vnc_state; /* needed for info vnc */
+
+void do_info_vnc(void)
+{
+    if (vnc_state == NULL)
+	term_printf("VNC server disabled\n");
+    else {
+	term_printf("VNC server active on: ");
+	term_print_filename(vnc_state->display);
+	term_printf("\n");
+
+	if (vnc_state->csock == -1)
+	    term_printf("No client connected\n");
+	else
+	    term_printf("Client connected\n");
+    }
+}
+
+/* TODO
+   1) Get the queue working for IO.
+   2) there is some weirdness when using the -S option (the screen is grey
+      and not totally invalidated
+   3) resolutions > 1024
+*/
+
+static void vnc_write(VncState *vs, const void *data, size_t len);
+static void vnc_write_u32(VncState *vs, uint32_t value);
+static void vnc_write_s32(VncState *vs, int32_t value);
+static void vnc_write_u16(VncState *vs, uint16_t value);
+static void vnc_write_u8(VncState *vs, uint8_t value);
+static void vnc_flush(VncState *vs);
+static void vnc_update_client(void *opaque);
+static void vnc_client_read(void *opaque);
+
+static void vnc_colordepth(DisplayState *ds, int depth);
+
+static inline void vnc_set_bit(uint32_t *d, int k)
+{
+    d[k >> 5] |= 1 << (k & 0x1f);
+}
+
+static inline void vnc_clear_bit(uint32_t *d, int k)
+{
+    d[k >> 5] &= ~(1 << (k & 0x1f));
+}
+
+static inline void vnc_set_bits(uint32_t *d, int n, int nb_words)
+{
+    int j;
+
+    j = 0;
+    while (n >= 32) {
+        d[j++] = -1;
+        n -= 32;
+    }
+    if (n > 0)
+        d[j++] = (1 << n) - 1;
+    while (j < nb_words)
+        d[j++] = 0;
+}
+
+static inline int vnc_get_bit(const uint32_t *d, int k)
+{
+    return (d[k >> 5] >> (k & 0x1f)) & 1;
+}
+
+static inline int vnc_and_bits(const uint32_t *d1, const uint32_t *d2,
+                               int nb_words)
+{
+    int i;
+    for(i = 0; i < nb_words; i++) {
+        if ((d1[i] & d2[i]) != 0)
+            return 1;
+    }
+    return 0;
+}
+
+static void vnc_dpy_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    VncState *vs = ds->opaque;
+    int i;
+
+    h += y;
+
+    /* round x down to ensure the loop only spans one 16-pixel block per,
+       iteration.  otherwise, if (x % 16) != 0, the last iteration may span
+       two 16-pixel blocks but we only mark the first as dirty
+    */
+    w += (x % 16);
+    x -= (x % 16);
+
+    x = MIN(x, vs->width);
+    y = MIN(y, vs->height);
+    w = MIN(x + w, vs->width) - x;
+    h = MIN(h, vs->height);
+
+    for (; y < h; y++)
+	for (i = 0; i < w; i += 16)
+	    vnc_set_bit(vs->dirty_row[y], (x + i) / 16);
+}
+
+static void vnc_framebuffer_update(VncState *vs, int x, int y, int w, int h,
+				   int32_t encoding)
+{
+    vnc_write_u16(vs, x);
+    vnc_write_u16(vs, y);
+    vnc_write_u16(vs, w);
+    vnc_write_u16(vs, h);
+
+    vnc_write_s32(vs, encoding);
+}
+
+static void vnc_dpy_resize(DisplayState *ds, int w, int h)
+{
+    int size_changed;
+    VncState *vs = ds->opaque;
+
+    ds->data = qemu_realloc(ds->data, w * h * vs->depth);
+    vs->old_data = qemu_realloc(vs->old_data, w * h * vs->depth);
+
+    if (ds->data == NULL || vs->old_data == NULL) {
+	fprintf(stderr, "vnc: memory allocation failed\n");
+	exit(1);
+    }
+
+    if (ds->depth != vs->depth * 8) {
+        ds->depth = vs->depth * 8;
+        console_color_init(ds);
+    }
+    size_changed = ds->width != w || ds->height != h;
+    ds->width = w;
+    ds->height = h;
+    ds->linesize = w * vs->depth;
+    if (size_changed) {
+        vs->width = ds->width;
+        vs->height = ds->height;
+        if (vs->csock != -1 && vs->has_resize) {
+            vnc_write_u8(vs, 0);  /* msg id */
+            vnc_write_u8(vs, 0);
+            vnc_write_u16(vs, 1); /* number of rects */
+            vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, -223);
+            vnc_flush(vs);
+        }
+    }
+
+    memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+    memset(vs->old_data, 42, vs->ds->linesize * vs->ds->height);
+}
+
+/* fastest code */
+static void vnc_write_pixels_copy(VncState *vs, void *pixels, int size)
+{
+    vnc_write(vs, pixels, size);
+}
+
+/* slowest but generic code. */
+static void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v)
+{
+    uint8_t r, g, b;
+
+    r = ((v >> vs->server_red_shift) & vs->server_red_max) * (vs->client_red_max + 1) /
+        (vs->server_red_max + 1);
+    g = ((v >> vs->server_green_shift) & vs->server_green_max) * (vs->client_green_max + 1) /
+        (vs->server_green_max + 1);
+    b = ((v >> vs->server_blue_shift) & vs->server_blue_max) * (vs->client_blue_max + 1) /
+        (vs->server_blue_max + 1);
+    v = (r << vs->client_red_shift) |
+        (g << vs->client_green_shift) |
+        (b << vs->client_blue_shift);
+    switch(vs->pix_bpp) {
+    case 1:
+        buf[0] = v;
+        break;
+    case 2:
+        if (vs->pix_big_endian) {
+            buf[0] = v >> 8;
+            buf[1] = v;
+        } else {
+            buf[1] = v >> 8;
+            buf[0] = v;
+        }
+        break;
+    default:
+    case 4:
+        if (vs->pix_big_endian) {
+            buf[0] = v >> 24;
+            buf[1] = v >> 16;
+            buf[2] = v >> 8;
+            buf[3] = v;
+        } else {
+            buf[3] = v >> 24;
+            buf[2] = v >> 16;
+            buf[1] = v >> 8;
+            buf[0] = v;
+        }
+        break;
+    }
+}
+
+static void vnc_write_pixels_generic(VncState *vs, void *pixels1, int size)
+{
+    uint8_t buf[4];
+
+    if (vs->depth == 4) {
+        uint32_t *pixels = pixels1;
+        int n, i;
+        n = size >> 2;
+        for(i = 0; i < n; i++) {
+            vnc_convert_pixel(vs, buf, pixels[i]);
+            vnc_write(vs, buf, vs->pix_bpp);
+        }
+    } else if (vs->depth == 2) {
+        uint16_t *pixels = pixels1;
+        int n, i;
+        n = size >> 1;
+        for(i = 0; i < n; i++) {
+            vnc_convert_pixel(vs, buf, pixels[i]);
+            vnc_write(vs, buf, vs->pix_bpp);
+        }
+    } else if (vs->depth == 1) {
+        uint8_t *pixels = pixels1;
+        int n, i;
+        n = size;
+        for(i = 0; i < n; i++) {
+            vnc_convert_pixel(vs, buf, pixels[i]);
+            vnc_write(vs, buf, vs->pix_bpp);
+        }
+    } else {
+        fprintf(stderr, "vnc_write_pixels_generic: VncState color depth not supported\n");
+    }
+}
+
+static void send_framebuffer_update_raw(VncState *vs, int x, int y, int w, int h)
+{
+    int i;
+    uint8_t *row;
+
+    vnc_framebuffer_update(vs, x, y, w, h, 0);
+
+    row = vs->ds->data + y * vs->ds->linesize + x * vs->depth;
+    for (i = 0; i < h; i++) {
+	vs->write_pixels(vs, row, w * vs->depth);
+	row += vs->ds->linesize;
+    }
+}
+
+static void hextile_enc_cord(uint8_t *ptr, int x, int y, int w, int h)
+{
+    ptr[0] = ((x & 0x0F) << 4) | (y & 0x0F);
+    ptr[1] = (((w - 1) & 0x0F) << 4) | ((h - 1) & 0x0F);
+}
+
+#define BPP 8
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 16
+#include "vnchextile.h"
+#undef BPP
+
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+
+#define GENERIC
+#define BPP 8
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+#define GENERIC
+#define BPP 16
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+#define GENERIC
+#define BPP 32
+#include "vnchextile.h"
+#undef BPP
+#undef GENERIC
+
+static void send_framebuffer_update_hextile(VncState *vs, int x, int y, int w, int h)
+{
+    int i, j;
+    int has_fg, has_bg;
+    uint8_t *last_fg, *last_bg;
+
+    vnc_framebuffer_update(vs, x, y, w, h, 5);
+
+    last_fg = (uint8_t *) malloc(vs->depth);
+    last_bg = (uint8_t *) malloc(vs->depth);
+    has_fg = has_bg = 0;
+    for (j = y; j < (y + h); j += 16) {
+	for (i = x; i < (x + w); i += 16) {
+            vs->send_hextile_tile(vs, i, j,
+                                  MIN(16, x + w - i), MIN(16, y + h - j),
+                                  last_bg, last_fg, &has_bg, &has_fg);
+	}
+    }
+    free(last_fg);
+    free(last_bg);
+
+}
+
+static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h)
+{
+	if (vs->has_hextile)
+	    send_framebuffer_update_hextile(vs, x, y, w, h);
+	else
+	    send_framebuffer_update_raw(vs, x, y, w, h);
+}
+
+static void vnc_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h)
+{
+    int src, dst;
+    uint8_t *src_row;
+    uint8_t *dst_row;
+    char *old_row;
+    int y = 0;
+    int pitch = ds->linesize;
+    VncState *vs = ds->opaque;
+
+    vnc_update_client(vs);
+
+    if (dst_y > src_y) {
+	y = h - 1;
+	pitch = -pitch;
+    }
+
+    src = (ds->linesize * (src_y + y) + vs->depth * src_x);
+    dst = (ds->linesize * (dst_y + y) + vs->depth * dst_x);
+
+    src_row = ds->data + src;
+    dst_row = ds->data + dst;
+    old_row = vs->old_data + dst;
+
+    for (y = 0; y < h; y++) {
+	memmove(old_row, src_row, w * vs->depth);
+	memmove(dst_row, src_row, w * vs->depth);
+	src_row += pitch;
+	dst_row += pitch;
+	old_row += pitch;
+    }
+
+    vnc_write_u8(vs, 0);  /* msg id */
+    vnc_write_u8(vs, 0);
+    vnc_write_u16(vs, 1); /* number of rects */
+    vnc_framebuffer_update(vs, dst_x, dst_y, w, h, 1);
+    vnc_write_u16(vs, src_x);
+    vnc_write_u16(vs, src_y);
+    vnc_flush(vs);
+}
+
+static int find_dirty_height(VncState *vs, int y, int last_x, int x)
+{
+    int h;
+
+    for (h = 1; h < (vs->height - y); h++) {
+	int tmp_x;
+	if (!vnc_get_bit(vs->dirty_row[y + h], last_x))
+	    break;
+	for (tmp_x = last_x; tmp_x < x; tmp_x++)
+	    vnc_clear_bit(vs->dirty_row[y + h], tmp_x);
+    }
+
+    return h;
+}
+
+static void vnc_update_client(void *opaque)
+{
+    VncState *vs = opaque;
+
+    if (vs->need_update && vs->csock != -1) {
+	int y;
+	uint8_t *row;
+	char *old_row;
+	uint32_t width_mask[VNC_DIRTY_WORDS];
+	int n_rectangles;
+	int saved_offset;
+	int has_dirty = 0;
+
+        vga_hw_update();
+
+        vnc_set_bits(width_mask, (vs->width / 16), VNC_DIRTY_WORDS);
+
+	/* Walk through the dirty map and eliminate tiles that
+	   really aren't dirty */
+	row = vs->ds->data;
+	old_row = vs->old_data;
+
+	for (y = 0; y < vs->height; y++) {
+	    if (vnc_and_bits(vs->dirty_row[y], width_mask, VNC_DIRTY_WORDS)) {
+		int x;
+		uint8_t *ptr;
+		char *old_ptr;
+
+		ptr = row;
+		old_ptr = (char*)old_row;
+
+		for (x = 0; x < vs->ds->width; x += 16) {
+		    if (memcmp(old_ptr, ptr, 16 * vs->depth) == 0) {
+			vnc_clear_bit(vs->dirty_row[y], (x / 16));
+		    } else {
+			has_dirty = 1;
+			memcpy(old_ptr, ptr, 16 * vs->depth);
+		    }
+
+		    ptr += 16 * vs->depth;
+		    old_ptr += 16 * vs->depth;
+		}
+	    }
+
+	    row += vs->ds->linesize;
+	    old_row += vs->ds->linesize;
+	}
+
+	if (!has_dirty) {
+	    qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+	    return;
+	}
+
+	/* Count rectangles */
+	n_rectangles = 0;
+	vnc_write_u8(vs, 0);  /* msg id */
+	vnc_write_u8(vs, 0);
+	saved_offset = vs->output.offset;
+	vnc_write_u16(vs, 0);
+
+	for (y = 0; y < vs->height; y++) {
+	    int x;
+	    int last_x = -1;
+	    for (x = 0; x < vs->width / 16; x++) {
+		if (vnc_get_bit(vs->dirty_row[y], x)) {
+		    if (last_x == -1) {
+			last_x = x;
+		    }
+		    vnc_clear_bit(vs->dirty_row[y], x);
+		} else {
+		    if (last_x != -1) {
+			int h = find_dirty_height(vs, y, last_x, x);
+			send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
+			n_rectangles++;
+		    }
+		    last_x = -1;
+		}
+	    }
+	    if (last_x != -1) {
+		int h = find_dirty_height(vs, y, last_x, x);
+		send_framebuffer_update(vs, last_x * 16, y, (x - last_x) * 16, h);
+		n_rectangles++;
+	    }
+	}
+	vs->output.buffer[saved_offset] = (n_rectangles >> 8) & 0xFF;
+	vs->output.buffer[saved_offset + 1] = n_rectangles & 0xFF;
+	vnc_flush(vs);
+
+    }
+
+    if (vs->csock != -1) {
+        qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
+    }
+
+}
+
+static int vnc_listen_poll(void *opaque)
+{
+    VncState *vs = opaque;
+    if (vs->csock == -1)
+	return 1;
+    return 0;
+}
+
+static void buffer_reserve(Buffer *buffer, size_t len)
+{
+    if ((buffer->capacity - buffer->offset) < len) {
+	buffer->capacity += (len + 1024);
+	buffer->buffer = qemu_realloc(buffer->buffer, buffer->capacity);
+	if (buffer->buffer == NULL) {
+	    fprintf(stderr, "vnc: out of memory\n");
+	    exit(1);
+	}
+    }
+}
+
+static int buffer_empty(Buffer *buffer)
+{
+    return buffer->offset == 0;
+}
+
+static uint8_t *buffer_end(Buffer *buffer)
+{
+    return buffer->buffer + buffer->offset;
+}
+
+static void buffer_reset(Buffer *buffer)
+{
+	buffer->offset = 0;
+}
+
+static void buffer_append(Buffer *buffer, const void *data, size_t len)
+{
+    memcpy(buffer->buffer + buffer->offset, data, len);
+    buffer->offset += len;
+}
+
+static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
+{
+    if (ret == 0 || ret == -1) {
+        if (ret == -1) {
+            switch (last_errno) {
+                case EINTR:
+                case EAGAIN:
+#ifdef _WIN32
+                case EWOULDBLOCK:
+#endif
+                    return 0;
+                default:
+                    break;
+            }
+        }
+
+	VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0);
+	qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+	socket_close(vs->csock);
+	vs->csock = -1;
+	vs->ds->idle = 1;
+	buffer_reset(&vs->input);
+	buffer_reset(&vs->output);
+	vs->need_update = 0;
+#ifdef CONFIG_VNC_TLS
+	if (vs->tls_session) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	}
+	vs->wiremode = VNC_WIREMODE_CLEAR;
+#endif /* CONFIG_VNC_TLS */
+	return 0;
+    }
+    return ret;
+}
+
+static void vnc_client_error(VncState *vs)
+{
+    vnc_client_io_error(vs, -1, EINVAL);
+}
+
+static void vnc_client_write(void *opaque)
+{
+    long ret;
+    VncState *vs = opaque;
+
+#ifdef CONFIG_VNC_TLS
+    if (vs->tls_session) {
+	ret = gnutls_write(vs->tls_session, vs->output.buffer, vs->output.offset);
+	if (ret < 0) {
+	    if (ret == GNUTLS_E_AGAIN)
+		errno = EAGAIN;
+	    else
+		errno = EIO;
+	    ret = -1;
+	}
+    } else
+#endif /* CONFIG_VNC_TLS */
+	ret = socket_send(vs->csock, vs->output.buffer, vs->output.offset);
+    ret = vnc_client_io_error(vs, ret, errno_str);
+    if (!ret)
+	return;
+
+    memmove(vs->output.buffer, vs->output.buffer + ret, (vs->output.offset - ret));
+    vs->output.offset -= ret;
+
+    if (vs->output.offset == 0) {
+	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+    }
+}
+
+static void vnc_read_when(VncState *vs, VncReadEvent *func, size_t expecting)
+{
+    vs->read_handler = func;
+    vs->read_handler_expect = expecting;
+}
+
+static void vnc_client_read(void *opaque)
+{
+    VncState *vs = opaque;
+    long ret;
+
+    buffer_reserve(&vs->input, 4096);
+
+#ifdef CONFIG_VNC_TLS
+    if (vs->tls_session) {
+	ret = gnutls_read(vs->tls_session, buffer_end(&vs->input), 4096);
+	if (ret < 0) {
+	    if (ret == GNUTLS_E_AGAIN)
+		errno = EAGAIN;
+	    else
+		errno = EIO;
+	    ret = -1;
+	}
+    } else
+#endif /* CONFIG_VNC_TLS */
+	ret = socket_recv(vs->csock, buffer_end(&vs->input), 4096);
+    ret = vnc_client_io_error(vs, ret, errno_str);
+    if (!ret)
+	return;
+
+    vs->input.offset += ret;
+
+    while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) {
+	size_t len = vs->read_handler_expect;
+	int ret;
+
+	ret = vs->read_handler(vs, vs->input.buffer, len);
+	if (vs->csock == -1)
+	    return;
+
+	if (!ret) {
+	    memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len));
+	    vs->input.offset -= len;
+	} else {
+	    vs->read_handler_expect = ret;
+	}
+    }
+}
+
+static void vnc_write(VncState *vs, const void *data, size_t len)
+{
+    buffer_reserve(&vs->output, len);
+
+    if (buffer_empty(&vs->output)) {
+	qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+    }
+
+    buffer_append(&vs->output, data, len);
+}
+
+static void vnc_write_s32(VncState *vs, int32_t value)
+{
+    vnc_write_u32(vs, *(uint32_t *)&value);
+}
+
+static void vnc_write_u32(VncState *vs, uint32_t value)
+{
+    uint8_t buf[4];
+
+    buf[0] = (value >> 24) & 0xFF;
+    buf[1] = (value >> 16) & 0xFF;
+    buf[2] = (value >>  8) & 0xFF;
+    buf[3] = value & 0xFF;
+
+    vnc_write(vs, buf, 4);
+}
+
+static void vnc_write_u16(VncState *vs, uint16_t value)
+{
+    uint8_t buf[2];
+
+    buf[0] = (value >> 8) & 0xFF;
+    buf[1] = value & 0xFF;
+
+    vnc_write(vs, buf, 2);
+}
+
+static void vnc_write_u8(VncState *vs, uint8_t value)
+{
+    vnc_write(vs, (char *)&value, 1);
+}
+
+static void vnc_flush(VncState *vs)
+{
+    if (vs->output.offset)
+	vnc_client_write(vs);
+}
+
+static uint8_t read_u8(uint8_t *data, size_t offset)
+{
+    return data[offset];
+}
+
+static uint16_t read_u16(uint8_t *data, size_t offset)
+{
+    return ((data[offset] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
+}
+
+static int32_t read_s32(uint8_t *data, size_t offset)
+{
+    return (int32_t)((data[offset] << 24) | (data[offset + 1] << 16) |
+		     (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+static uint32_t read_u32(uint8_t *data, size_t offset)
+{
+    return ((data[offset] << 24) | (data[offset + 1] << 16) |
+	    (data[offset + 2] << 8) | data[offset + 3]);
+}
+
+#ifdef CONFIG_VNC_TLS
+static ssize_t vnc_tls_push(gnutls_transport_ptr_t transport,
+                            const void *data,
+                            size_t len) {
+    struct VncState *vs = (struct VncState *)transport;
+
+    return socket_send(vs->csock, data, len);
+}
+
+
+static ssize_t vnc_tls_pull(gnutls_transport_ptr_t transport,
+                            void *data,
+                            size_t len) {
+    struct VncState *vs = (struct VncState *)transport;
+
+    return socket_recv(vs->csock, data, len);
+}
+#endif /* CONFIG_VNC_TLS */
+
+static void client_cut_text(VncState *vs, size_t len, uint8_t *text)
+{
+}
+
+static void check_pointer_type_change(VncState *vs, int absolute)
+{
+    if (vs->has_pointer_type_change && vs->absolute != absolute) {
+	vnc_write_u8(vs, 0);
+	vnc_write_u8(vs, 0);
+	vnc_write_u16(vs, 1);
+	vnc_framebuffer_update(vs, absolute, 0,
+			       vs->ds->width, vs->ds->height, -257);
+	vnc_flush(vs);
+    }
+    vs->absolute = absolute;
+}
+
+static void pointer_event(VncState *vs, int button_mask, int x, int y)
+{
+    int buttons = 0;
+    int dz = 0;
+
+    if (button_mask & 0x01)
+	buttons |= MOUSE_EVENT_LBUTTON;
+    if (button_mask & 0x02)
+	buttons |= MOUSE_EVENT_MBUTTON;
+    if (button_mask & 0x04)
+	buttons |= MOUSE_EVENT_RBUTTON;
+    if (button_mask & 0x08)
+	dz = -1;
+    if (button_mask & 0x10)
+	dz = 1;
+
+    if (vs->absolute) {
+	kbd_mouse_event(x * 0x7FFF / (vs->ds->width - 1),
+			y * 0x7FFF / (vs->ds->height - 1),
+			dz, buttons);
+    } else if (vs->has_pointer_type_change) {
+	x -= 0x7FFF;
+	y -= 0x7FFF;
+
+	kbd_mouse_event(x, y, dz, buttons);
+    } else {
+	if (vs->last_x != -1)
+	    kbd_mouse_event(x - vs->last_x,
+			    y - vs->last_y,
+			    dz, buttons);
+	vs->last_x = x;
+	vs->last_y = y;
+    }
+
+    check_pointer_type_change(vs, kbd_mouse_is_absolute());
+}
+
+static void reset_keys(VncState *vs)
+{
+    int i;
+    for(i = 0; i < 256; i++) {
+        if (vs->modifiers_state[i]) {
+            if (i & 0x80)
+                kbd_put_keycode(0xe0);
+            kbd_put_keycode(i | 0x80);
+            vs->modifiers_state[i] = 0;
+        }
+    }
+}
+
+static void press_key(VncState *vs, int keysym)
+{
+    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) & 0x7f);
+    kbd_put_keycode(keysym2scancode(vs->kbd_layout, keysym) | 0x80);
+}
+
+static void do_key_event(VncState *vs, int down, int keycode, int sym)
+{
+    /* QEMU console switch */
+    switch(keycode) {
+    case 0x2a:                          /* Left Shift */
+    case 0x36:                          /* Right Shift */
+    case 0x1d:                          /* Left CTRL */
+    case 0x9d:                          /* Right CTRL */
+    case 0x38:                          /* Left ALT */
+    case 0xb8:                          /* Right ALT */
+        if (down)
+            vs->modifiers_state[keycode] = 1;
+        else
+            vs->modifiers_state[keycode] = 0;
+        break;
+    case 0x02 ... 0x0a: /* '1' to '9' keys */
+        if (down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) {
+            /* Reset the modifiers sent to the current console */
+            reset_keys(vs);
+            console_select(keycode - 0x02);
+            return;
+        }
+        break;
+    case 0x3a:			/* CapsLock */
+    case 0x45:			/* NumLock */
+        if (!down)
+            vs->modifiers_state[keycode] ^= 1;
+        break;
+    }
+
+    if (keycode_is_keypad(vs->kbd_layout, keycode)) {
+        /* If the numlock state needs to change then simulate an additional
+           keypress before sending this one.  This will happen if the user
+           toggles numlock away from the VNC window.
+        */
+        if (keysym_is_numlock(vs->kbd_layout, sym & 0xFFFF)) {
+            if (!vs->modifiers_state[0x45]) {
+                vs->modifiers_state[0x45] = 1;
+                press_key(vs, 0xff7f);
+            }
+        } else {
+            if (vs->modifiers_state[0x45]) {
+                vs->modifiers_state[0x45] = 0;
+                press_key(vs, 0xff7f);
+            }
+        }
+    }
+
+    if (is_graphic_console()) {
+        if (keycode & 0x80)
+            kbd_put_keycode(0xe0);
+        if (down)
+            kbd_put_keycode(keycode & 0x7f);
+        else
+            kbd_put_keycode(keycode | 0x80);
+    } else {
+        /* QEMU console emulation */
+        if (down) {
+            switch (keycode) {
+            case 0x2a:                          /* Left Shift */
+            case 0x36:                          /* Right Shift */
+            case 0x1d:                          /* Left CTRL */
+            case 0x9d:                          /* Right CTRL */
+            case 0x38:                          /* Left ALT */
+            case 0xb8:                          /* Right ALT */
+                break;
+            case 0xc8:
+                kbd_put_keysym(QEMU_KEY_UP);
+                break;
+            case 0xd0:
+                kbd_put_keysym(QEMU_KEY_DOWN);
+                break;
+            case 0xcb:
+                kbd_put_keysym(QEMU_KEY_LEFT);
+                break;
+            case 0xcd:
+                kbd_put_keysym(QEMU_KEY_RIGHT);
+                break;
+            case 0xd3:
+                kbd_put_keysym(QEMU_KEY_DELETE);
+                break;
+            case 0xc7:
+                kbd_put_keysym(QEMU_KEY_HOME);
+                break;
+            case 0xcf:
+                kbd_put_keysym(QEMU_KEY_END);
+                break;
+            case 0xc9:
+                kbd_put_keysym(QEMU_KEY_PAGEUP);
+                break;
+            case 0xd1:
+                kbd_put_keysym(QEMU_KEY_PAGEDOWN);
+                break;
+            default:
+                kbd_put_keysym(sym);
+                break;
+            }
+        }
+    }
+}
+
+static void key_event(VncState *vs, int down, uint32_t sym)
+{
+    int keycode;
+
+    if (sym >= 'A' && sym <= 'Z' && is_graphic_console())
+	sym = sym - 'A' + 'a';
+
+    keycode = keysym2scancode(vs->kbd_layout, sym & 0xFFFF);
+    do_key_event(vs, down, keycode, sym);
+}
+
+static void ext_key_event(VncState *vs, int down,
+                          uint32_t sym, uint16_t keycode)
+{
+    /* if the user specifies a keyboard layout, always use it */
+    if (keyboard_layout)
+        key_event(vs, down, sym);
+    else
+        do_key_event(vs, down, keycode, sym);
+}
+
+static void framebuffer_update_request(VncState *vs, int incremental,
+				       int x_position, int y_position,
+				       int w, int h)
+{
+    if (x_position > vs->ds->width)
+        x_position = vs->ds->width;
+    if (y_position > vs->ds->height)
+        y_position = vs->ds->height;
+    if (x_position + w >= vs->ds->width)
+        w = vs->ds->width  - x_position;
+    if (y_position + h >= vs->ds->height)
+        h = vs->ds->height - y_position;
+
+    int i;
+    vs->need_update = 1;
+    if (!incremental) {
+	char *old_row = vs->old_data + y_position * vs->ds->linesize;
+
+	for (i = 0; i < h; i++) {
+            vnc_set_bits(vs->dirty_row[y_position + i],
+                         (vs->ds->width / 16), VNC_DIRTY_WORDS);
+	    memset(old_row, 42, vs->ds->width * vs->depth);
+	    old_row += vs->ds->linesize;
+	}
+    }
+}
+
+static void send_ext_key_event_ack(VncState *vs)
+{
+    vnc_write_u8(vs, 0);
+    vnc_write_u8(vs, 0);
+    vnc_write_u16(vs, 1);
+    vnc_framebuffer_update(vs, 0, 0, vs->ds->width, vs->ds->height, -258);
+    vnc_flush(vs);
+}
+
+static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
+{
+    int i;
+
+    vs->has_hextile = 0;
+    vs->has_resize = 0;
+    vs->has_pointer_type_change = 0;
+    vs->has_WMVi = 0;
+    vs->absolute = -1;
+    vs->ds->dpy_copy = NULL;
+
+    for (i = n_encodings - 1; i >= 0; i--) {
+	switch (encodings[i]) {
+	case 0: /* Raw */
+	    vs->has_hextile = 0;
+	    break;
+	case 1: /* CopyRect */
+	    vs->ds->dpy_copy = vnc_copy;
+	    break;
+	case 5: /* Hextile */
+	    vs->has_hextile = 1;
+	    break;
+	case -223: /* DesktopResize */
+	    vs->has_resize = 1;
+	    break;
+	case -257:
+	    vs->has_pointer_type_change = 1;
+	    break;
+        case -258:
+            send_ext_key_event_ack(vs);
+            break;
+        case 0x574D5669:
+            vs->has_WMVi = 1;
+            break;
+	default:
+	    break;
+	}
+    }
+
+    check_pointer_type_change(vs, kbd_mouse_is_absolute());
+}
+
+static void set_pixel_format(VncState *vs,
+			     int bits_per_pixel, int depth,
+			     int big_endian_flag, int true_color_flag,
+			     int red_max, int green_max, int blue_max,
+			     int red_shift, int green_shift, int blue_shift)
+{
+    int host_big_endian_flag;
+
+#ifdef WORDS_BIGENDIAN
+    host_big_endian_flag = 1;
+#else
+    host_big_endian_flag = 0;
+#endif
+    if (!true_color_flag) {
+    fail:
+	vnc_client_error(vs);
+        return;
+    }
+    if (bits_per_pixel == 32 &&
+        bits_per_pixel == vs->depth * 8 &&
+        host_big_endian_flag == big_endian_flag &&
+        red_max == 0xff && green_max == 0xff && blue_max == 0xff &&
+        red_shift == 16 && green_shift == 8 && blue_shift == 0) {
+        vs->depth = 4;
+        vs->write_pixels = vnc_write_pixels_copy;
+        vs->send_hextile_tile = send_hextile_tile_32;
+    } else
+    if (bits_per_pixel == 16 &&
+        bits_per_pixel == vs->depth * 8 && 
+        host_big_endian_flag == big_endian_flag &&
+        red_max == 31 && green_max == 63 && blue_max == 31 &&
+        red_shift == 11 && green_shift == 5 && blue_shift == 0) {
+        vs->depth = 2;
+        vs->write_pixels = vnc_write_pixels_copy;
+        vs->send_hextile_tile = send_hextile_tile_16;
+    } else
+    if (bits_per_pixel == 8 &&
+        bits_per_pixel == vs->depth * 8 &&
+        red_max == 7 && green_max == 7 && blue_max == 3 &&
+        red_shift == 5 && green_shift == 2 && blue_shift == 0) {
+        vs->depth = 1;
+        vs->write_pixels = vnc_write_pixels_copy;
+        vs->send_hextile_tile = send_hextile_tile_8;
+    } else
+    {
+        /* generic and slower case */
+        if (bits_per_pixel != 8 &&
+            bits_per_pixel != 16 &&
+            bits_per_pixel != 32)
+            goto fail;
+        if (vs->depth == 4) {
+            vs->send_hextile_tile = send_hextile_tile_generic_32;
+        } else if (vs->depth == 2) {
+           vs->send_hextile_tile = send_hextile_tile_generic_16;
+        } else {
+            vs->send_hextile_tile = send_hextile_tile_generic_8;
+        }
+
+        vs->pix_big_endian = big_endian_flag;
+        vs->write_pixels = vnc_write_pixels_generic;
+    }
+
+    vs->client_red_shift = red_shift;
+    vs->client_red_max = red_max;
+    vs->client_green_shift = green_shift;
+    vs->client_green_max = green_max;
+    vs->client_blue_shift = blue_shift;
+    vs->client_blue_max = blue_max;
+    vs->pix_bpp = bits_per_pixel / 8;
+
+    vga_hw_invalidate();
+    vga_hw_update();
+}
+
+static void pixel_format_message (VncState *vs) {
+    char pad[3] = { 0, 0, 0 };
+
+    vnc_write_u8(vs, vs->depth * 8); /* bits-per-pixel */
+    if (vs->depth == 4) vnc_write_u8(vs, 24); /* depth */
+    else vnc_write_u8(vs, vs->depth * 8); /* depth */
+
+#ifdef WORDS_BIGENDIAN
+    vnc_write_u8(vs, 1);             /* big-endian-flag */
+#else
+    vnc_write_u8(vs, 0);             /* big-endian-flag */
+#endif
+    vnc_write_u8(vs, 1);             /* true-color-flag */
+    if (vs->depth == 4) {
+        vnc_write_u16(vs, 0xFF);     /* red-max */
+        vnc_write_u16(vs, 0xFF);     /* green-max */
+        vnc_write_u16(vs, 0xFF);     /* blue-max */
+        vnc_write_u8(vs, 16);        /* red-shift */
+        vnc_write_u8(vs, 8);         /* green-shift */
+        vnc_write_u8(vs, 0);         /* blue-shift */
+        vs->send_hextile_tile = send_hextile_tile_32;
+    } else if (vs->depth == 2) {
+        vnc_write_u16(vs, 31);       /* red-max */
+        vnc_write_u16(vs, 63);       /* green-max */
+        vnc_write_u16(vs, 31);       /* blue-max */
+        vnc_write_u8(vs, 11);        /* red-shift */
+        vnc_write_u8(vs, 5);         /* green-shift */
+        vnc_write_u8(vs, 0);         /* blue-shift */
+        vs->send_hextile_tile = send_hextile_tile_16;
+    } else if (vs->depth == 1) {
+        /* XXX: change QEMU pixel 8 bit pixel format to match the VNC one ? */
+        vnc_write_u16(vs, 7);        /* red-max */
+        vnc_write_u16(vs, 7);        /* green-max */
+        vnc_write_u16(vs, 3);        /* blue-max */
+        vnc_write_u8(vs, 5);         /* red-shift */
+        vnc_write_u8(vs, 2);         /* green-shift */
+        vnc_write_u8(vs, 0);         /* blue-shift */
+        vs->send_hextile_tile = send_hextile_tile_8;
+    }
+    vs->client_red_max = vs->server_red_max;
+    vs->client_green_max = vs->server_green_max;
+    vs->client_blue_max = vs->server_blue_max;
+    vs->client_red_shift = vs->server_red_shift;
+    vs->client_green_shift = vs->server_green_shift;
+    vs->client_blue_shift = vs->server_blue_shift;
+    vs->pix_bpp = vs->depth * 8;
+    vs->write_pixels = vnc_write_pixels_copy;
+
+    vnc_write(vs, pad, 3);           /* padding */
+}
+
+static void vnc_colordepth(DisplayState *ds, int depth)
+{
+    int host_big_endian_flag;
+    struct VncState *vs = ds->opaque;
+
+    switch (depth) {
+        case 24:
+            if (ds->depth == 32) return;
+            depth = 32;
+            break;
+        case 15:
+        case 8:
+        case 0:
+            return;
+        default:
+            break;
+    }
+
+#ifdef WORDS_BIGENDIAN
+    host_big_endian_flag = 1;
+#else
+    host_big_endian_flag = 0;
+#endif   
+    
+    switch (depth) {
+        case 8:
+            vs->depth = depth / 8;
+            vs->server_red_max = 7;
+            vs->server_green_max = 7;
+            vs->server_blue_max = 3;
+            vs->server_red_shift = 5;
+            vs->server_green_shift = 2;
+            vs->server_blue_shift = 0;
+            break;
+        case 16:
+            vs->depth = depth / 8;
+            vs->server_red_max = 31;
+            vs->server_green_max = 63;
+            vs->server_blue_max = 31;
+            vs->server_red_shift = 11;
+            vs->server_green_shift = 5;
+            vs->server_blue_shift = 0;
+            break;
+        case 32:
+            vs->depth = 4;
+            vs->server_red_max = 255;
+            vs->server_green_max = 255;
+            vs->server_blue_max = 255;
+            vs->server_red_shift = 16;
+            vs->server_green_shift = 8;
+            vs->server_blue_shift = 0;
+            break;
+        default:
+            return;
+    }
+
+    if (vs->csock != -1 && vs->has_WMVi) {
+        /* Sending a WMVi message to notify the client*/
+        vnc_write_u8(vs, 0);  /* msg id */
+        vnc_write_u8(vs, 0);
+        vnc_write_u16(vs, 1); /* number of rects */
+        vnc_framebuffer_update(vs, 0, 0, ds->width, ds->height, 0x574D5669);
+        pixel_format_message(vs);
+        vnc_flush(vs);
+    } else {
+        if (vs->pix_bpp == 4 && vs->depth == 4 &&
+                host_big_endian_flag == vs->pix_big_endian &&
+                vs->client_red_max == 0xff && vs->client_green_max == 0xff && vs->client_blue_max == 0xff &&
+                vs->client_red_shift == 16 && vs->client_green_shift == 8 && vs->client_blue_shift == 0) {
+            vs->write_pixels = vnc_write_pixels_copy;
+            vs->send_hextile_tile = send_hextile_tile_32;
+        } else if (vs->pix_bpp == 2 && vs->depth == 2 &&
+                host_big_endian_flag == vs->pix_big_endian &&
+                vs->client_red_max == 31 && vs->client_green_max == 63 && vs->client_blue_max == 31 &&
+                vs->client_red_shift == 11 && vs->client_green_shift == 5 && vs->client_blue_shift == 0) {
+            vs->write_pixels = vnc_write_pixels_copy;
+            vs->send_hextile_tile = send_hextile_tile_16;
+        } else if (vs->pix_bpp == 1 && vs->depth == 1 &&
+                host_big_endian_flag == vs->pix_big_endian &&
+                vs->client_red_max == 7 && vs->client_green_max == 7 && vs->client_blue_max == 3 &&
+                vs->client_red_shift == 5 && vs->client_green_shift == 2 && vs->client_blue_shift == 0) {
+            vs->write_pixels = vnc_write_pixels_copy;
+            vs->send_hextile_tile = send_hextile_tile_8;
+        } else {
+            if (vs->depth == 4) {
+                vs->send_hextile_tile = send_hextile_tile_generic_32;
+            } else if (vs->depth == 2) {
+                vs->send_hextile_tile = send_hextile_tile_generic_16;
+            } else {
+                vs->send_hextile_tile = send_hextile_tile_generic_8;
+            }
+            vs->write_pixels = vnc_write_pixels_generic;
+        }
+    }
+}
+
+static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
+{
+    int i;
+    uint16_t limit;
+
+    switch (data[0]) {
+    case 0:
+	if (len == 1)
+	    return 20;
+
+	set_pixel_format(vs, read_u8(data, 4), read_u8(data, 5),
+			 read_u8(data, 6), read_u8(data, 7),
+			 read_u16(data, 8), read_u16(data, 10),
+			 read_u16(data, 12), read_u8(data, 14),
+			 read_u8(data, 15), read_u8(data, 16));
+	break;
+    case 2:
+	if (len == 1)
+	    return 4;
+
+	if (len == 4)
+	    return 4 + (read_u16(data, 2) * 4);
+
+	limit = read_u16(data, 2);
+	for (i = 0; i < limit; i++) {
+	    int32_t val = read_s32(data, 4 + (i * 4));
+	    memcpy(data + 4 + (i * 4), &val, sizeof(val));
+	}
+
+	set_encodings(vs, (int32_t *)(data + 4), limit);
+	break;
+    case 3:
+	if (len == 1)
+	    return 10;
+
+	framebuffer_update_request(vs,
+				   read_u8(data, 1), read_u16(data, 2), read_u16(data, 4),
+				   read_u16(data, 6), read_u16(data, 8));
+	break;
+    case 4:
+	if (len == 1)
+	    return 8;
+
+	key_event(vs, read_u8(data, 1), read_u32(data, 4));
+	break;
+    case 5:
+	if (len == 1)
+	    return 6;
+
+	pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4));
+	break;
+    case 6:
+	if (len == 1)
+	    return 8;
+
+	if (len == 8) {
+            uint32_t dlen = read_u32(data, 4);
+            if (dlen > 0)
+                return 8 + dlen;
+        }
+
+	client_cut_text(vs, read_u32(data, 4), data + 8);
+	break;
+    case 255:
+        if (len == 1)
+            return 2;
+
+        switch (read_u8(data, 1)) {
+        case 0:
+            if (len == 2)
+                return 12;
+
+            ext_key_event(vs, read_u16(data, 2),
+                          read_u32(data, 4), read_u32(data, 8));
+            break;
+        default:
+            printf("Msg: %d\n", read_u16(data, 0));
+            vnc_client_error(vs);
+            break;
+        }
+        break;
+    default:
+	printf("Msg: %d\n", data[0]);
+	vnc_client_error(vs);
+	break;
+    }
+
+    vnc_read_when(vs, protocol_client_msg, 1);
+    return 0;
+}
+
+static int protocol_client_init(VncState *vs, uint8_t *data, size_t len)
+{
+    char buf[1024];
+    int size;
+
+    vs->width = vs->ds->width;
+    vs->height = vs->ds->height;
+    vnc_write_u16(vs, vs->ds->width);
+    vnc_write_u16(vs, vs->ds->height);
+
+    pixel_format_message(vs);
+
+    if (qemu_name)
+        size = snprintf(buf, sizeof(buf), "QEMU (%s)", qemu_name);
+    else
+        size = snprintf(buf, sizeof(buf), "QEMU");
+
+    vnc_write_u32(vs, size);
+    vnc_write(vs, buf, size);
+    vnc_flush(vs);
+
+    vnc_read_when(vs, protocol_client_msg, 1);
+
+    return 0;
+}
+
+static void make_challenge(VncState *vs)
+{
+    int i;
+
+    srand(time(NULL)+getpid()+getpid()*987654+rand());
+
+    for (i = 0 ; i < sizeof(vs->challenge) ; i++)
+        vs->challenge[i] = (int) (256.0*rand()/(RAND_MAX+1.0));
+}
+
+static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
+{
+    unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
+    int i, j, pwlen;
+    unsigned char key[8];
+
+    if (!vs->password || !vs->password[0]) {
+	VNC_DEBUG("No password configured on server");
+	vnc_write_u32(vs, 1); /* Reject auth */
+	if (vs->minor >= 8) {
+	    static const char err[] = "Authentication failed";
+	    vnc_write_u32(vs, sizeof(err));
+	    vnc_write(vs, err, sizeof(err));
+	}
+	vnc_flush(vs);
+	vnc_client_error(vs);
+	return 0;
+    }
+
+    memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
+
+    /* Calculate the expected challenge response */
+    pwlen = strlen(vs->password);
+    for (i=0; i<sizeof(key); i++)
+        key[i] = i<pwlen ? vs->password[i] : 0;
+    deskey(key, EN0);
+    for (j = 0; j < VNC_AUTH_CHALLENGE_SIZE; j += 8)
+        des(response+j, response+j);
+
+    /* Compare expected vs actual challenge response */
+    if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
+	VNC_DEBUG("Client challenge reponse did not match\n");
+	vnc_write_u32(vs, 1); /* Reject auth */
+	if (vs->minor >= 8) {
+	    static const char err[] = "Authentication failed";
+	    vnc_write_u32(vs, sizeof(err));
+	    vnc_write(vs, err, sizeof(err));
+	}
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	VNC_DEBUG("Accepting VNC challenge response\n");
+	vnc_write_u32(vs, 0); /* Accept auth */
+	vnc_flush(vs);
+
+	vnc_read_when(vs, protocol_client_init, 1);
+    }
+    return 0;
+}
+
+static int start_auth_vnc(VncState *vs)
+{
+    make_challenge(vs);
+    /* Send client a 'random' challenge */
+    vnc_write(vs, vs->challenge, sizeof(vs->challenge));
+    vnc_flush(vs);
+
+    vnc_read_when(vs, protocol_client_auth_vnc, sizeof(vs->challenge));
+    return 0;
+}
+
+
+#ifdef CONFIG_VNC_TLS
+#define DH_BITS 1024
+static gnutls_dh_params_t dh_params;
+
+static int vnc_tls_initialize(void)
+{
+    static int tlsinitialized = 0;
+
+    if (tlsinitialized)
+	return 1;
+
+    if (gnutls_global_init () < 0)
+	return 0;
+
+    /* XXX ought to re-generate diffie-hellmen params periodically */
+    if (gnutls_dh_params_init (&dh_params) < 0)
+	return 0;
+    if (gnutls_dh_params_generate2 (dh_params, DH_BITS) < 0)
+	return 0;
+
+#if _VNC_DEBUG == 2
+    gnutls_global_set_log_level(10);
+    gnutls_global_set_log_function(vnc_debug_gnutls_log);
+#endif
+
+    tlsinitialized = 1;
+
+    return 1;
+}
+
+static gnutls_anon_server_credentials vnc_tls_initialize_anon_cred(void)
+{
+    gnutls_anon_server_credentials anon_cred;
+    int ret;
+
+    if ((ret = gnutls_anon_allocate_server_credentials(&anon_cred)) < 0) {
+	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
+	return NULL;
+    }
+
+    gnutls_anon_set_server_dh_params(anon_cred, dh_params);
+
+    return anon_cred;
+}
+
+
+static gnutls_certificate_credentials_t vnc_tls_initialize_x509_cred(VncState *vs)
+{
+    gnutls_certificate_credentials_t x509_cred;
+    int ret;
+
+    if (!vs->x509cacert) {
+	VNC_DEBUG("No CA x509 certificate specified\n");
+	return NULL;
+    }
+    if (!vs->x509cert) {
+	VNC_DEBUG("No server x509 certificate specified\n");
+	return NULL;
+    }
+    if (!vs->x509key) {
+	VNC_DEBUG("No server private key specified\n");
+	return NULL;
+    }
+
+    if ((ret = gnutls_certificate_allocate_credentials(&x509_cred)) < 0) {
+	VNC_DEBUG("Cannot allocate credentials %s\n", gnutls_strerror(ret));
+	return NULL;
+    }
+    if ((ret = gnutls_certificate_set_x509_trust_file(x509_cred,
+						      vs->x509cacert,
+						      GNUTLS_X509_FMT_PEM)) < 0) {
+	VNC_DEBUG("Cannot load CA certificate %s\n", gnutls_strerror(ret));
+	gnutls_certificate_free_credentials(x509_cred);
+	return NULL;
+    }
+
+    if ((ret = gnutls_certificate_set_x509_key_file (x509_cred,
+						     vs->x509cert,
+						     vs->x509key,
+						     GNUTLS_X509_FMT_PEM)) < 0) {
+	VNC_DEBUG("Cannot load certificate & key %s\n", gnutls_strerror(ret));
+	gnutls_certificate_free_credentials(x509_cred);
+	return NULL;
+    }
+
+    if (vs->x509cacrl) {
+	if ((ret = gnutls_certificate_set_x509_crl_file(x509_cred,
+							vs->x509cacrl,
+							GNUTLS_X509_FMT_PEM)) < 0) {
+	    VNC_DEBUG("Cannot load CRL %s\n", gnutls_strerror(ret));
+	    gnutls_certificate_free_credentials(x509_cred);
+	    return NULL;
+	}
+    }
+
+    gnutls_certificate_set_dh_params (x509_cred, dh_params);
+
+    return x509_cred;
+}
+
+static int vnc_validate_certificate(struct VncState *vs)
+{
+    int ret;
+    unsigned int status;
+    const gnutls_datum_t *certs;
+    unsigned int nCerts, i;
+    time_t now;
+
+    VNC_DEBUG("Validating client certificate\n");
+    if ((ret = gnutls_certificate_verify_peers2 (vs->tls_session, &status)) < 0) {
+	VNC_DEBUG("Verify failed %s\n", gnutls_strerror(ret));
+	return -1;
+    }
+
+    if ((now = time(NULL)) == ((time_t)-1)) {
+	return -1;
+    }
+
+    if (status != 0) {
+	if (status & GNUTLS_CERT_INVALID)
+	    VNC_DEBUG("The certificate is not trusted.\n");
+
+	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+	    VNC_DEBUG("The certificate hasn't got a known issuer.\n");
+
+	if (status & GNUTLS_CERT_REVOKED)
+	    VNC_DEBUG("The certificate has been revoked.\n");
+
+	if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+	    VNC_DEBUG("The certificate uses an insecure algorithm\n");
+
+	return -1;
+    } else {
+	VNC_DEBUG("Certificate is valid!\n");
+    }
+
+    /* Only support x509 for now */
+    if (gnutls_certificate_type_get(vs->tls_session) != GNUTLS_CRT_X509)
+	return -1;
+
+    if (!(certs = gnutls_certificate_get_peers(vs->tls_session, &nCerts)))
+	return -1;
+
+    for (i = 0 ; i < nCerts ; i++) {
+	gnutls_x509_crt_t cert;
+	VNC_DEBUG ("Checking certificate chain %d\n", i);
+	if (gnutls_x509_crt_init (&cert) < 0)
+	    return -1;
+
+	if (gnutls_x509_crt_import(cert, &certs[i], GNUTLS_X509_FMT_DER) < 0) {
+	    gnutls_x509_crt_deinit (cert);
+	    return -1;
+	}
+
+	if (gnutls_x509_crt_get_expiration_time (cert) < now) {
+	    VNC_DEBUG("The certificate has expired\n");
+	    gnutls_x509_crt_deinit (cert);
+	    return -1;
+	}
+
+	if (gnutls_x509_crt_get_activation_time (cert) > now) {
+	    VNC_DEBUG("The certificate is not yet activated\n");
+	    gnutls_x509_crt_deinit (cert);
+	    return -1;
+	}
+
+	if (gnutls_x509_crt_get_activation_time (cert) > now) {
+	    VNC_DEBUG("The certificate is not yet activated\n");
+	    gnutls_x509_crt_deinit (cert);
+	    return -1;
+	}
+
+	gnutls_x509_crt_deinit (cert);
+    }
+
+    return 0;
+}
+
+
+static int start_auth_vencrypt_subauth(VncState *vs)
+{
+    switch (vs->subauth) {
+    case VNC_AUTH_VENCRYPT_TLSNONE:
+    case VNC_AUTH_VENCRYPT_X509NONE:
+       VNC_DEBUG("Accept TLS auth none\n");
+       vnc_write_u32(vs, 0); /* Accept auth completion */
+       vnc_read_when(vs, protocol_client_init, 1);
+       break;
+
+    case VNC_AUTH_VENCRYPT_TLSVNC:
+    case VNC_AUTH_VENCRYPT_X509VNC:
+       VNC_DEBUG("Start TLS auth VNC\n");
+       return start_auth_vnc(vs);
+
+    default: /* Should not be possible, but just in case */
+       VNC_DEBUG("Reject auth %d\n", vs->auth);
+       vnc_write_u8(vs, 1);
+       if (vs->minor >= 8) {
+           static const char err[] = "Unsupported authentication type";
+           vnc_write_u32(vs, sizeof(err));
+           vnc_write(vs, err, sizeof(err));
+       }
+       vnc_client_error(vs);
+    }
+
+    return 0;
+}
+
+static void vnc_handshake_io(void *opaque);
+
+static int vnc_continue_handshake(struct VncState *vs) {
+    int ret;
+
+    if ((ret = gnutls_handshake(vs->tls_session)) < 0) {
+       if (!gnutls_error_is_fatal(ret)) {
+           VNC_DEBUG("Handshake interrupted (blocking)\n");
+           if (!gnutls_record_get_direction(vs->tls_session))
+               qemu_set_fd_handler(vs->csock, vnc_handshake_io, NULL, vs);
+           else
+               qemu_set_fd_handler(vs->csock, NULL, vnc_handshake_io, vs);
+           return 0;
+       }
+       VNC_DEBUG("Handshake failed %s\n", gnutls_strerror(ret));
+       vnc_client_error(vs);
+       return -1;
+    }
+
+    if (vs->x509verify) {
+	if (vnc_validate_certificate(vs) < 0) {
+	    VNC_DEBUG("Client verification failed\n");
+	    vnc_client_error(vs);
+	    return -1;
+	} else {
+	    VNC_DEBUG("Client verification passed\n");
+	}
+    }
+
+    VNC_DEBUG("Handshake done, switching to TLS data mode\n");
+    vs->wiremode = VNC_WIREMODE_TLS;
+    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs);
+
+    return start_auth_vencrypt_subauth(vs);
+}
+
+static void vnc_handshake_io(void *opaque) {
+    struct VncState *vs = (struct VncState *)opaque;
+
+    VNC_DEBUG("Handshake IO continue\n");
+    vnc_continue_handshake(vs);
+}
+
+#define NEED_X509_AUTH(vs)			      \
+    ((vs)->subauth == VNC_AUTH_VENCRYPT_X509NONE ||   \
+     (vs)->subauth == VNC_AUTH_VENCRYPT_X509VNC ||    \
+     (vs)->subauth == VNC_AUTH_VENCRYPT_X509PLAIN)
+
+
+static int vnc_start_tls(struct VncState *vs) {
+    static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 };
+    static const int protocol_priority[]= { GNUTLS_TLS1_1, GNUTLS_TLS1_0, GNUTLS_SSL3, 0 };
+    static const int kx_anon[] = {GNUTLS_KX_ANON_DH, 0};
+    static const int kx_x509[] = {GNUTLS_KX_DHE_DSS, GNUTLS_KX_RSA, GNUTLS_KX_DHE_RSA, GNUTLS_KX_SRP, 0};
+
+    VNC_DEBUG("Do TLS setup\n");
+    if (vnc_tls_initialize() < 0) {
+	VNC_DEBUG("Failed to init TLS\n");
+	vnc_client_error(vs);
+	return -1;
+    }
+    if (vs->tls_session == NULL) {
+	if (gnutls_init(&vs->tls_session, GNUTLS_SERVER) < 0) {
+	    vnc_client_error(vs);
+	    return -1;
+	}
+
+	if (gnutls_set_default_priority(vs->tls_session) < 0) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	    vnc_client_error(vs);
+	    return -1;
+	}
+
+	if (gnutls_kx_set_priority(vs->tls_session, NEED_X509_AUTH(vs) ? kx_x509 : kx_anon) < 0) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	    vnc_client_error(vs);
+	    return -1;
+	}
+
+	if (gnutls_certificate_type_set_priority(vs->tls_session, cert_type_priority) < 0) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	    vnc_client_error(vs);
+	    return -1;
+	}
+
+	if (gnutls_protocol_set_priority(vs->tls_session, protocol_priority) < 0) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	    vnc_client_error(vs);
+	    return -1;
+	}
+
+	if (NEED_X509_AUTH(vs)) {
+	    gnutls_certificate_server_credentials x509_cred = vnc_tls_initialize_x509_cred(vs);
+	    if (!x509_cred) {
+		gnutls_deinit(vs->tls_session);
+		vs->tls_session = NULL;
+		vnc_client_error(vs);
+		return -1;
+	    }
+	    if (gnutls_credentials_set(vs->tls_session, GNUTLS_CRD_CERTIFICATE, x509_cred) < 0) {
+		gnutls_deinit(vs->tls_session);
+		vs->tls_session = NULL;
+		gnutls_certificate_free_credentials(x509_cred);
+		vnc_client_error(vs);
+		return -1;
+	    }
+	    if (vs->x509verify) {
+		VNC_DEBUG("Requesting a client certificate\n");
+		gnutls_certificate_server_set_request (vs->tls_session, GNUTLS_CERT_REQUEST);
+	    }
+
+	} else {
+	    gnutls_anon_server_credentials anon_cred = vnc_tls_initialize_anon_cred();
+	    if (!anon_cred) {
+		gnutls_deinit(vs->tls_session);
+		vs->tls_session = NULL;
+		vnc_client_error(vs);
+		return -1;
+	    }
+	    if (gnutls_credentials_set(vs->tls_session, GNUTLS_CRD_ANON, anon_cred) < 0) {
+		gnutls_deinit(vs->tls_session);
+		vs->tls_session = NULL;
+		gnutls_anon_free_server_credentials(anon_cred);
+		vnc_client_error(vs);
+		return -1;
+	    }
+	}
+
+	gnutls_transport_set_ptr(vs->tls_session, (gnutls_transport_ptr_t)vs);
+	gnutls_transport_set_push_function(vs->tls_session, vnc_tls_push);
+	gnutls_transport_set_pull_function(vs->tls_session, vnc_tls_pull);
+    }
+
+    VNC_DEBUG("Start TLS handshake process\n");
+    return vnc_continue_handshake(vs);
+}
+
+static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len)
+{
+    int auth = read_u32(data, 0);
+
+    if (auth != vs->subauth) {
+	VNC_DEBUG("Rejecting auth %d\n", auth);
+	vnc_write_u8(vs, 0); /* Reject auth */
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	VNC_DEBUG("Accepting auth %d, starting handshake\n", auth);
+	vnc_write_u8(vs, 1); /* Accept auth */
+	vnc_flush(vs);
+
+	if (vnc_start_tls(vs) < 0) {
+	    VNC_DEBUG("Failed to complete TLS\n");
+	    return 0;
+	}
+
+	if (vs->wiremode == VNC_WIREMODE_TLS) {
+	    VNC_DEBUG("Starting VeNCrypt subauth\n");
+	    return start_auth_vencrypt_subauth(vs);
+	} else {
+	    VNC_DEBUG("TLS handshake blocked\n");
+	    return 0;
+	}
+    }
+    return 0;
+}
+
+static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len)
+{
+    if (data[0] != 0 ||
+	data[1] != 2) {
+	VNC_DEBUG("Unsupported VeNCrypt protocol %d.%d\n", (int)data[0], (int)data[1]);
+	vnc_write_u8(vs, 1); /* Reject version */
+	vnc_flush(vs);
+	vnc_client_error(vs);
+    } else {
+	VNC_DEBUG("Sending allowed auth %d\n", vs->subauth);
+	vnc_write_u8(vs, 0); /* Accept version */
+	vnc_write_u8(vs, 1); /* Number of sub-auths */
+	vnc_write_u32(vs, vs->subauth); /* The supported auth */
+	vnc_flush(vs);
+	vnc_read_when(vs, protocol_client_vencrypt_auth, 4);
+    }
+    return 0;
+}
+
+static int start_auth_vencrypt(VncState *vs)
+{
+    /* Send VeNCrypt version 0.2 */
+    vnc_write_u8(vs, 0);
+    vnc_write_u8(vs, 2);
+
+    vnc_read_when(vs, protocol_client_vencrypt_init, 2);
+    return 0;
+}
+#endif /* CONFIG_VNC_TLS */
+
+static int protocol_client_auth(VncState *vs, uint8_t *data, size_t len)
+{
+    /* We only advertise 1 auth scheme at a time, so client
+     * must pick the one we sent. Verify this */
+    if (data[0] != vs->auth) { /* Reject auth */
+       VNC_DEBUG("Reject auth %d\n", (int)data[0]);
+       vnc_write_u32(vs, 1);
+       if (vs->minor >= 8) {
+           static const char err[] = "Authentication failed";
+           vnc_write_u32(vs, sizeof(err));
+           vnc_write(vs, err, sizeof(err));
+       }
+       vnc_client_error(vs);
+    } else { /* Accept requested auth */
+       VNC_DEBUG("Client requested auth %d\n", (int)data[0]);
+       switch (vs->auth) {
+       case VNC_AUTH_NONE:
+           VNC_DEBUG("Accept auth none\n");
+           if (vs->minor >= 8) {
+               vnc_write_u32(vs, 0); /* Accept auth completion */
+               vnc_flush(vs);
+           }
+           vnc_read_when(vs, protocol_client_init, 1);
+           break;
+
+       case VNC_AUTH_VNC:
+           VNC_DEBUG("Start VNC auth\n");
+           return start_auth_vnc(vs);
+
+#ifdef CONFIG_VNC_TLS
+       case VNC_AUTH_VENCRYPT:
+           VNC_DEBUG("Accept VeNCrypt auth\n");;
+           return start_auth_vencrypt(vs);
+#endif /* CONFIG_VNC_TLS */
+
+       default: /* Should not be possible, but just in case */
+           VNC_DEBUG("Reject auth %d\n", vs->auth);
+           vnc_write_u8(vs, 1);
+           if (vs->minor >= 8) {
+               static const char err[] = "Authentication failed";
+               vnc_write_u32(vs, sizeof(err));
+               vnc_write(vs, err, sizeof(err));
+           }
+           vnc_client_error(vs);
+       }
+    }
+    return 0;
+}
+
+static int protocol_version(VncState *vs, uint8_t *version, size_t len)
+{
+    char local[13];
+
+    memcpy(local, version, 12);
+    local[12] = 0;
+
+    if (sscanf(local, "RFB %03d.%03d\n", &vs->major, &vs->minor) != 2) {
+	VNC_DEBUG("Malformed protocol version %s\n", local);
+	vnc_client_error(vs);
+	return 0;
+    }
+    VNC_DEBUG("Client request protocol version %d.%d\n", vs->major, vs->minor);
+    if (vs->major != 3 ||
+	(vs->minor != 3 &&
+	 vs->minor != 4 &&
+	 vs->minor != 5 &&
+	 vs->minor != 7 &&
+	 vs->minor != 8)) {
+	VNC_DEBUG("Unsupported client version\n");
+	vnc_write_u32(vs, VNC_AUTH_INVALID);
+	vnc_flush(vs);
+	vnc_client_error(vs);
+	return 0;
+    }
+    /* Some broken clients report v3.4 or v3.5, which spec requires to be treated
+     * as equivalent to v3.3 by servers
+     */
+    if (vs->minor == 4 || vs->minor == 5)
+	vs->minor = 3;
+
+    if (vs->minor == 3) {
+	if (vs->auth == VNC_AUTH_NONE) {
+            VNC_DEBUG("Tell client auth none\n");
+            vnc_write_u32(vs, vs->auth);
+            vnc_flush(vs);
+            vnc_read_when(vs, protocol_client_init, 1);
+       } else if (vs->auth == VNC_AUTH_VNC) {
+            VNC_DEBUG("Tell client VNC auth\n");
+            vnc_write_u32(vs, vs->auth);
+            vnc_flush(vs);
+            start_auth_vnc(vs);
+       } else {
+            VNC_DEBUG("Unsupported auth %d for protocol 3.3\n", vs->auth);
+            vnc_write_u32(vs, VNC_AUTH_INVALID);
+            vnc_flush(vs);
+            vnc_client_error(vs);
+       }
+    } else {
+	VNC_DEBUG("Telling client we support auth %d\n", vs->auth);
+	vnc_write_u8(vs, 1); /* num auth */
+	vnc_write_u8(vs, vs->auth);
+	vnc_read_when(vs, protocol_client_auth, 1);
+	vnc_flush(vs);
+    }
+
+    return 0;
+}
+
+static void vnc_connect(VncState *vs)
+{
+    VNC_DEBUG("New client on socket %d\n", vs->csock);
+    vs->ds->idle = 0;
+    socket_set_nonblock(vs->csock);
+    qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs);
+    vnc_write(vs, "RFB 003.008\n", 12);
+    vnc_flush(vs);
+    vnc_read_when(vs, protocol_version, 12);
+    memset(vs->old_data, 0, vs->ds->linesize * vs->ds->height);
+    memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
+    vs->has_resize = 0;
+    vs->has_hextile = 0;
+    vs->ds->dpy_copy = NULL;
+    vnc_update_client(vs);
+}
+
+static void vnc_listen_read(void *opaque)
+{
+    VncState *vs = opaque;
+
+    /* Catch-up */
+    vga_hw_update();
+
+    vs->csock = socket_accept(vs->lsock, NULL);
+    if (vs->csock != -1) {
+        vnc_connect(vs);
+    }
+}
+
+extern int parse_host_port(SockAddress* saddr, const char *str);
+
+void vnc_display_init(DisplayState *ds)
+{
+    VncState *vs;
+
+    vs = qemu_mallocz(sizeof(VncState));
+    if (!vs)
+	exit(1);
+
+    ds->opaque = vs;
+    ds->idle = 1;
+    vnc_state = vs;
+    vs->display = NULL;
+    vs->password = NULL;
+
+    vs->lsock = -1;
+    vs->csock = -1;
+    vs->last_x = -1;
+    vs->last_y = -1;
+
+    vs->ds = ds;
+
+    if (keyboard_layout)
+        vs->kbd_layout = init_keyboard_layout(keyboard_layout);
+    else
+        vs->kbd_layout = init_keyboard_layout("en-us");
+
+    if (!vs->kbd_layout)
+	exit(1);
+
+    vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+
+    vs->ds->data = NULL;
+    vs->ds->dpy_update = vnc_dpy_update;
+    vs->ds->dpy_resize = vnc_dpy_resize;
+    vs->ds->dpy_refresh = NULL;
+
+    vnc_colordepth(vs->ds, 32);
+    vnc_dpy_resize(vs->ds, 640, 400);
+}
+
+#ifdef CONFIG_VNC_TLS
+static int vnc_set_x509_credential(VncState *vs,
+				   const char *certdir,
+				   const char *filename,
+				   char **cred,
+				   int ignoreMissing)
+{
+    struct stat sb;
+
+    if (*cred) {
+	qemu_free(*cred);
+	*cred = NULL;
+    }
+
+    if (!(*cred = qemu_malloc(strlen(certdir) + strlen(filename) + 2)))
+	return -1;
+
+    strcpy(*cred, certdir);
+    strcat(*cred, "/");
+    strcat(*cred, filename);
+
+    VNC_DEBUG("Check %s\n", *cred);
+    if (stat(*cred, &sb) < 0) {
+	qemu_free(*cred);
+	*cred = NULL;
+	if (ignoreMissing && errno == ENOENT)
+	    return 0;
+	return -1;
+    }
+
+    return 0;
+}
+
+static int vnc_set_x509_credential_dir(VncState *vs,
+				       const char *certdir)
+{
+    if (vnc_set_x509_credential(vs, certdir, X509_CA_CERT_FILE, &vs->x509cacert, 0) < 0)
+	goto cleanup;
+    if (vnc_set_x509_credential(vs, certdir, X509_CA_CRL_FILE, &vs->x509cacrl, 1) < 0)
+	goto cleanup;
+    if (vnc_set_x509_credential(vs, certdir, X509_SERVER_CERT_FILE, &vs->x509cert, 0) < 0)
+	goto cleanup;
+    if (vnc_set_x509_credential(vs, certdir, X509_SERVER_KEY_FILE, &vs->x509key, 0) < 0)
+	goto cleanup;
+
+    return 0;
+
+ cleanup:
+    qemu_free(vs->x509cacert);
+    qemu_free(vs->x509cacrl);
+    qemu_free(vs->x509cert);
+    qemu_free(vs->x509key);
+    vs->x509cacert = vs->x509cacrl = vs->x509cert = vs->x509key = NULL;
+    return -1;
+}
+#endif /* CONFIG_VNC_TLS */
+
+void vnc_display_close(DisplayState *ds)
+{
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+
+    if (vs->display) {
+	qemu_free(vs->display);
+	vs->display = NULL;
+    }
+    if (vs->lsock != -1) {
+	qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL);
+	close(vs->lsock);
+	vs->lsock = -1;
+    }
+    if (vs->csock != -1) {
+	qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL);
+	socket_close(vs->csock);
+	vs->csock = -1;
+	buffer_reset(&vs->input);
+	buffer_reset(&vs->output);
+	vs->need_update = 0;
+#ifdef CONFIG_VNC_TLS
+	if (vs->tls_session) {
+	    gnutls_deinit(vs->tls_session);
+	    vs->tls_session = NULL;
+	}
+	vs->wiremode = VNC_WIREMODE_CLEAR;
+#endif /* CONFIG_VNC_TLS */
+    }
+    vs->auth = VNC_AUTH_INVALID;
+#ifdef CONFIG_VNC_TLS
+    vs->subauth = VNC_AUTH_INVALID;
+    vs->x509verify = 0;
+#endif
+}
+
+int vnc_display_password(DisplayState *ds, const char *password)
+{
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+
+    if (vs->password) {
+	qemu_free(vs->password);
+	vs->password = NULL;
+    }
+    if (password && password[0]) {
+	if (!(vs->password = qemu_strdup(password)))
+	    return -1;
+    }
+
+    return 0;
+}
+
+int vnc_display_open(DisplayState *ds, const char *display)
+{
+    SockAddress  addr;
+#ifndef _WIN32
+    const char *p;
+#endif
+    int ret;
+    VncState *vs = ds ? (VncState *)ds->opaque : vnc_state;
+    const char *options;
+    int password = 0;
+    int reverse = 0;
+#ifdef CONFIG_VNC_TLS
+    int tls = 0, x509 = 0;
+#endif
+
+    sock_address_init_inet( &addr, SOCK_ADDRESS_INET_ANY, 0 );
+    vs->lsock = -1;
+
+    vnc_display_close(ds);
+    if (strcmp(display, "none") == 0)
+	return 0;
+
+    if (!(vs->display = strdup(display)))
+	return -1;
+
+    options = display;
+    while ((options = strchr(options, ','))) {
+	options++;
+	if (strncmp(options, "password", 8) == 0) {
+	    password = 1; /* Require password auth */
+	} else if (strncmp(options, "reverse", 7) == 0) {
+	    reverse = 1;
+#ifdef CONFIG_VNC_TLS
+	} else if (strncmp(options, "tls", 3) == 0) {
+	    tls = 1; /* Require TLS */
+	} else if (strncmp(options, "x509", 4) == 0) {
+	    char *start, *end;
+	    x509 = 1; /* Require x509 certificates */
+	    if (strncmp(options, "x509verify", 10) == 0)
+	        vs->x509verify = 1; /* ...and verify client certs */
+
+	    /* Now check for 'x509=/some/path' postfix
+	     * and use that to setup x509 certificate/key paths */
+	    start = strchr(options, '=');
+	    end = strchr(options, ',');
+	    if (start && (!end || (start < end))) {
+		int len = end ? end-(start+1) : strlen(start+1);
+		char *path = qemu_malloc(len+1);
+		strncpy(path, start+1, len);
+		path[len] = '\0';
+		VNC_DEBUG("Trying certificate path '%s'\n", path);
+		ret = vnc_set_x509_credential_dir(vs, path);
+                qemu_free(path);
+                if (ret < 0) {
+		    fprintf(stderr, "Failed to find x509 certificates/keys in %s\n", path);
+		    goto FAIL;
+		}
+	    } else {
+		fprintf(stderr, "No certificate path provided\n");
+		goto FAIL;
+	    }
+#endif
+	}
+    }
+
+    if (password) {
+#ifdef CONFIG_VNC_TLS
+	if (tls) {
+	    vs->auth = VNC_AUTH_VENCRYPT;
+	    if (x509) {
+		VNC_DEBUG("Initializing VNC server with x509 password auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_X509VNC;
+	    } else {
+		VNC_DEBUG("Initializing VNC server with TLS password auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_TLSVNC;
+	    }
+	} else {
+#endif
+	    VNC_DEBUG("Initializing VNC server with password auth\n");
+	    vs->auth = VNC_AUTH_VNC;
+#ifdef CONFIG_VNC_TLS
+	    vs->subauth = VNC_AUTH_INVALID;
+	}
+#endif
+    } else {
+#ifdef CONFIG_VNC_TLS
+	if (tls) {
+	    vs->auth = VNC_AUTH_VENCRYPT;
+	    if (x509) {
+		VNC_DEBUG("Initializing VNC server with x509 no auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_X509NONE;
+	    } else {
+		VNC_DEBUG("Initializing VNC server with TLS no auth\n");
+		vs->subauth = VNC_AUTH_VENCRYPT_TLSNONE;
+	    }
+	} else {
+#endif
+	    VNC_DEBUG("Initializing VNC server with no auth\n");
+	    vs->auth = VNC_AUTH_NONE;
+#ifdef CONFIG_VNC_TLS
+	    vs->subauth = VNC_AUTH_INVALID;
+	}
+#endif
+    }
+#ifndef _WIN32
+    if (strstart(display, "unix:", &p)) {
+	sock_address_init_unix( &addr, p);
+	vs->lsock = socket_create( SOCKET_UNIX, SOCKET_STREAM );
+	if (vs->lsock == -1) {
+	    fprintf(stderr, "Could not create socket\n");
+	    goto FAIL;
+	}
+
+	if (!reverse) {
+	    unlink(p);
+	}
+    } else
+#endif
+    {
+	if (parse_host_port(&addr, display) < 0) {
+	    fprintf(stderr, "Could not parse VNC address\n");
+	    goto FAIL;
+	}
+
+	sock_address_set_port(&addr, reverse ? 0 : 5900);
+
+	vs->lsock = socket_create_inet( SOCKET_STREAM );
+	if (vs->lsock == -1) {
+	    fprintf(stderr, "Could not create socket\n");
+	    goto FAIL;
+	}
+
+	ret = socket_set_xreuseaddr(vs->lsock);
+	if (ret == -1) {
+	    fprintf(stderr, "setsockopt() failed\n");
+	    goto FAIL;
+	}
+    }
+
+    if (reverse) {
+        if (socket_connect(vs->lsock, &addr) == -1) {
+            fprintf(stderr, "Connection to VNC client failed\n");
+            goto FAIL;
+        }
+        vs->csock = vs->lsock;
+        vs->lsock = -1;
+        vnc_connect(vs);
+        return 0;
+    }
+
+    if (socket_bind(vs->lsock, &addr) == -1) {
+	fprintf(stderr, "bind() failed\n");
+	goto FAIL;
+    }
+
+    if (socket_listen(vs->lsock, 1) == -1) {
+	fprintf(stderr, "listen() failed\n");
+	goto FAIL;
+    }
+
+    return qemu_set_fd_handler2(vs->lsock, vnc_listen_poll, vnc_listen_read, NULL, vs);
+
+FAIL:
+    sock_address_done(&addr);
+
+    if (vs->lsock >= 0) {
+        socket_close(vs->lsock);
+        vs->lsock = -1;
+    }
+    free(vs->display);
+    vs->display = NULL;
+    return -1;
+}
diff --git a/vnc_keysym.h b/vnc_keysym.h
new file mode 100644
index 0000000..b6a172d
--- /dev/null
+++ b/vnc_keysym.h
@@ -0,0 +1,302 @@
+typedef struct {
+	const char* name;
+	int keysym;
+} name2keysym_t;
+static name2keysym_t name2keysym[]={
+/* ascii */
+    { "space",                0x020},
+    { "exclam",               0x021},
+    { "quotedbl",             0x022},
+    { "numbersign",           0x023},
+    { "dollar",               0x024},
+    { "percent",              0x025},
+    { "ampersand",            0x026},
+    { "apostrophe",           0x027},
+    { "parenleft",            0x028},
+    { "parenright",           0x029},
+    { "asterisk",             0x02a},
+    { "plus",                 0x02b},
+    { "comma",                0x02c},
+    { "minus",                0x02d},
+    { "period",               0x02e},
+    { "slash",                0x02f},
+    { "0",                    0x030},
+    { "1",                    0x031},
+    { "2",                    0x032},
+    { "3",                    0x033},
+    { "4",                    0x034},
+    { "5",                    0x035},
+    { "6",                    0x036},
+    { "7",                    0x037},
+    { "8",                    0x038},
+    { "9",                    0x039},
+    { "colon",                0x03a},
+    { "semicolon",            0x03b},
+    { "less",                 0x03c},
+    { "equal",                0x03d},
+    { "greater",              0x03e},
+    { "question",             0x03f},
+    { "at",                   0x040},
+    { "A",                    0x041},
+    { "B",                    0x042},
+    { "C",                    0x043},
+    { "D",                    0x044},
+    { "E",                    0x045},
+    { "F",                    0x046},
+    { "G",                    0x047},
+    { "H",                    0x048},
+    { "I",                    0x049},
+    { "J",                    0x04a},
+    { "K",                    0x04b},
+    { "L",                    0x04c},
+    { "M",                    0x04d},
+    { "N",                    0x04e},
+    { "O",                    0x04f},
+    { "P",                    0x050},
+    { "Q",                    0x051},
+    { "R",                    0x052},
+    { "S",                    0x053},
+    { "T",                    0x054},
+    { "U",                    0x055},
+    { "V",                    0x056},
+    { "W",                    0x057},
+    { "X",                    0x058},
+    { "Y",                    0x059},
+    { "Z",                    0x05a},
+    { "bracketleft",          0x05b},
+    { "backslash",            0x05c},
+    { "bracketright",         0x05d},
+    { "asciicircum",          0x05e},
+    { "underscore",           0x05f},
+    { "grave",                0x060},
+    { "a",                    0x061},
+    { "b",                    0x062},
+    { "c",                    0x063},
+    { "d",                    0x064},
+    { "e",                    0x065},
+    { "f",                    0x066},
+    { "g",                    0x067},
+    { "h",                    0x068},
+    { "i",                    0x069},
+    { "j",                    0x06a},
+    { "k",                    0x06b},
+    { "l",                    0x06c},
+    { "m",                    0x06d},
+    { "n",                    0x06e},
+    { "o",                    0x06f},
+    { "p",                    0x070},
+    { "q",                    0x071},
+    { "r",                    0x072},
+    { "s",                    0x073},
+    { "t",                    0x074},
+    { "u",                    0x075},
+    { "v",                    0x076},
+    { "w",                    0x077},
+    { "x",                    0x078},
+    { "y",                    0x079},
+    { "z",                    0x07a},
+    { "braceleft",            0x07b},
+    { "bar",                  0x07c},
+    { "braceright",           0x07d},
+    { "asciitilde",           0x07e},
+
+/* latin 1 extensions */
+{ "nobreakspace",         0x0a0},
+{ "exclamdown",           0x0a1},
+{ "cent",         	  0x0a2},
+{ "sterling",             0x0a3},
+{ "currency",             0x0a4},
+{ "yen",                  0x0a5},
+{ "brokenbar",            0x0a6},
+{ "section",              0x0a7},
+{ "diaeresis",            0x0a8},
+{ "copyright",            0x0a9},
+{ "ordfeminine",          0x0aa},
+{ "guillemotleft",        0x0ab},
+{ "notsign",              0x0ac},
+{ "hyphen",               0x0ad},
+{ "registered",           0x0ae},
+{ "macron",               0x0af},
+{ "degree",               0x0b0},
+{ "plusminus",            0x0b1},
+{ "twosuperior",          0x0b2},
+{ "threesuperior",        0x0b3},
+{ "acute",                0x0b4},
+{ "mu",                   0x0b5},
+{ "paragraph",            0x0b6},
+{ "periodcentered",       0x0b7},
+{ "cedilla",              0x0b8},
+{ "onesuperior",          0x0b9},
+{ "masculine",            0x0ba},
+{ "guillemotright",       0x0bb},
+{ "onequarter",           0x0bc},
+{ "onehalf",              0x0bd},
+{ "threequarters",        0x0be},
+{ "questiondown",         0x0bf},
+{ "Agrave",               0x0c0},
+{ "Aacute",               0x0c1},
+{ "Acircumflex",          0x0c2},
+{ "Atilde",               0x0c3},
+{ "Adiaeresis",           0x0c4},
+{ "Aring",                0x0c5},
+{ "AE",                   0x0c6},
+{ "Ccedilla",             0x0c7},
+{ "Egrave",               0x0c8},
+{ "Eacute",               0x0c9},
+{ "Ecircumflex",          0x0ca},
+{ "Ediaeresis",           0x0cb},
+{ "Igrave",               0x0cc},
+{ "Iacute",               0x0cd},
+{ "Icircumflex",          0x0ce},
+{ "Idiaeresis",           0x0cf},
+{ "ETH",                  0x0d0},
+{ "Eth",                  0x0d0},
+{ "Ntilde",               0x0d1},
+{ "Ograve",               0x0d2},
+{ "Oacute",               0x0d3},
+{ "Ocircumflex",          0x0d4},
+{ "Otilde",               0x0d5},
+{ "Odiaeresis",           0x0d6},
+{ "multiply",             0x0d7},
+{ "Ooblique",             0x0d8},
+{ "Oslash",               0x0d8},
+{ "Ugrave",               0x0d9},
+{ "Uacute",               0x0da},
+{ "Ucircumflex",          0x0db},
+{ "Udiaeresis",           0x0dc},
+{ "Yacute",               0x0dd},
+{ "THORN",                0x0de},
+{ "Thorn",                0x0de},
+{ "ssharp",               0x0df},
+{ "agrave",               0x0e0},
+{ "aacute",               0x0e1},
+{ "acircumflex",          0x0e2},
+{ "atilde",               0x0e3},
+{ "adiaeresis",           0x0e4},
+{ "aring",                0x0e5},
+{ "ae",                   0x0e6},
+{ "ccedilla",             0x0e7},
+{ "egrave",               0x0e8},
+{ "eacute",               0x0e9},
+{ "ecircumflex",          0x0ea},
+{ "ediaeresis",           0x0eb},
+{ "igrave",               0x0ec},
+{ "iacute",               0x0ed},
+{ "icircumflex",          0x0ee},
+{ "idiaeresis",           0x0ef},
+{ "eth",                  0x0f0},
+{ "ntilde",               0x0f1},
+{ "ograve",               0x0f2},
+{ "oacute",               0x0f3},
+{ "ocircumflex",          0x0f4},
+{ "otilde",               0x0f5},
+{ "odiaeresis",           0x0f6},
+{ "division",             0x0f7},
+{ "oslash",               0x0f8},
+{ "ooblique",             0x0f8},
+{ "ugrave",               0x0f9},
+{ "uacute",               0x0fa},
+{ "ucircumflex",          0x0fb},
+{ "udiaeresis",           0x0fc},
+{ "yacute",               0x0fd},
+{ "thorn",                0x0fe},
+{ "ydiaeresis",           0x0ff},
+{"EuroSign", 0x20ac},  /* XK_EuroSign */
+
+    /* modifiers */
+{"Control_L", 0xffe3}, /* XK_Control_L */
+{"Control_R", 0xffe4}, /* XK_Control_R */
+{"Alt_L", 0xffe9},     /* XK_Alt_L */
+{"Alt_R", 0xffea},     /* XK_Alt_R */
+{"Caps_Lock", 0xffe5}, /* XK_Caps_Lock */
+{"Meta_L", 0xffe7},    /* XK_Meta_L */
+{"Meta_R", 0xffe8},    /* XK_Meta_R */
+{"Shift_L", 0xffe1},   /* XK_Shift_L */
+{"Shift_R", 0xffe2},   /* XK_Shift_R */
+{"Super_L", 0xffeb},   /* XK_Super_L */
+{"Super_R", 0xffec},   /* XK_Super_R */
+
+    /* special keys */
+{"BackSpace", 0xff08}, /* XK_BackSpace */
+{"Tab", 0xff09},       /* XK_Tab */
+{"Return", 0xff0d},    /* XK_Return */
+{"Right", 0xff53},     /* XK_Right */
+{"Left", 0xff51},      /* XK_Left */
+{"Up", 0xff52},        /* XK_Up */
+{"Down", 0xff54},      /* XK_Down */
+{"Page_Down", 0xff56}, /* XK_Page_Down */
+{"Page_Up", 0xff55},   /* XK_Page_Up */
+{"Insert", 0xff63},    /* XK_Insert */
+{"Delete", 0xffff},    /* XK_Delete */
+{"Home", 0xff50},      /* XK_Home */
+{"End", 0xff57},       /* XK_End */
+{"Scroll_Lock", 0xff14}, /* XK_Scroll_Lock */
+{"KP_Home", 0xff95},
+{"KP_Left", 0xff96},
+{"KP_Up", 0xff97},
+{"KP_Right", 0xff98},
+{"KP_Down", 0xff99},
+{"KP_Prior", 0xff9a},
+{"KP_Page_Up", 0xff9a},
+{"KP_Next", 0xff9b},
+{"KP_Page_Down", 0xff9b},
+{"KP_End", 0xff9c},
+{"KP_Begin", 0xff9d},
+{"KP_Insert", 0xff9e},
+{"KP_Delete", 0xff9f},
+{"F1", 0xffbe},        /* XK_F1 */
+{"F2", 0xffbf},        /* XK_F2 */
+{"F3", 0xffc0},        /* XK_F3 */
+{"F4", 0xffc1},        /* XK_F4 */
+{"F5", 0xffc2},        /* XK_F5 */
+{"F6", 0xffc3},        /* XK_F6 */
+{"F7", 0xffc4},        /* XK_F7 */
+{"F8", 0xffc5},        /* XK_F8 */
+{"F9", 0xffc6},        /* XK_F9 */
+{"F10", 0xffc7},       /* XK_F10 */
+{"F11", 0xffc8},       /* XK_F11 */
+{"F12", 0xffc9},       /* XK_F12 */
+{"F13", 0xffca},       /* XK_F13 */
+{"F14", 0xffcb},       /* XK_F14 */
+{"F15", 0xffcc},       /* XK_F15 */
+{"Sys_Req", 0xff15},   /* XK_Sys_Req */
+{"KP_0", 0xffb0},      /* XK_KP_0 */
+{"KP_1", 0xffb1},      /* XK_KP_1 */
+{"KP_2", 0xffb2},      /* XK_KP_2 */
+{"KP_3", 0xffb3},      /* XK_KP_3 */
+{"KP_4", 0xffb4},      /* XK_KP_4 */
+{"KP_5", 0xffb5},      /* XK_KP_5 */
+{"KP_6", 0xffb6},      /* XK_KP_6 */
+{"KP_7", 0xffb7},      /* XK_KP_7 */
+{"KP_8", 0xffb8},      /* XK_KP_8 */
+{"KP_9", 0xffb9},      /* XK_KP_9 */
+{"KP_Add", 0xffab},    /* XK_KP_Add */
+{"KP_Separator", 0xffac},/* XK_KP_Separator */
+{"KP_Decimal", 0xffae},  /* XK_KP_Decimal */
+{"KP_Divide", 0xffaf},   /* XK_KP_Divide */
+{"KP_Enter", 0xff8d},    /* XK_KP_Enter */
+{"KP_Equal", 0xffbd},    /* XK_KP_Equal */
+{"KP_Multiply", 0xffaa}, /* XK_KP_Multiply */
+{"KP_Subtract", 0xffad}, /* XK_KP_Subtract */
+{"help", 0xff6a},        /* XK_Help */
+{"Menu", 0xff67},        /* XK_Menu */
+{"Print", 0xff61},       /* XK_Print */
+{"Mode_switch", 0xff7e}, /* XK_Mode_switch */
+{"Num_Lock", 0xff7f},    /* XK_Num_Lock */
+{"Pause", 0xff13},       /* XK_Pause */
+{"Escape", 0xff1b},      /* XK_Escape */
+
+    /* localized keys */
+{"BackApostrophe", 0xff21},
+{"Muhenkan", 0xff22},
+{"Katakana", 0xff27},
+{"Hankaku", 0xff29},
+{"Zenkaku_Hankaku", 0xff2a},
+{"Henkan_Mode_Real", 0xff23},
+{"Henkan_Mode_Ultra", 0xff3e},
+{"backslash_ja", 0xffa5},
+{"Katakana_Real", 0xff25},
+{"Eisu_toggle", 0xff30},
+
+{0,0},
+};
diff --git a/vnchextile.h b/vnchextile.h
new file mode 100644
index 0000000..eb05feb
--- /dev/null
+++ b/vnchextile.h
@@ -0,0 +1,209 @@
+#define CONCAT_I(a, b) a ## b
+#define CONCAT(a, b) CONCAT_I(a, b)
+#define pixel_t CONCAT(uint, CONCAT(BPP, _t))
+#ifdef GENERIC
+#define NAME CONCAT(generic_, BPP)
+#else
+#define NAME BPP
+#endif
+
+static void CONCAT(send_hextile_tile_, NAME)(VncState *vs,
+                                             int x, int y, int w, int h,
+                                             void *last_bg_,
+                                             void *last_fg_,
+                                             int *has_bg, int *has_fg)
+{
+    uint8_t *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth);
+    pixel_t *irow = (pixel_t *)row;
+    int j, i;
+    pixel_t *last_bg = (pixel_t *)last_bg_;
+    pixel_t *last_fg = (pixel_t *)last_fg_;
+    pixel_t bg = 0;
+    pixel_t fg = 0;
+    int n_colors = 0;
+    int bg_count = 0;
+    int fg_count = 0;
+    int flags = 0;
+    uint8_t data[(vs->pix_bpp + 2) * 16 * 16];
+    int n_data = 0;
+    int n_subtiles = 0;
+
+    for (j = 0; j < h; j++) {
+	for (i = 0; i < w; i++) {
+	    switch (n_colors) {
+	    case 0:
+		bg = irow[i];
+		n_colors = 1;
+		break;
+	    case 1:
+		if (irow[i] != bg) {
+		    fg = irow[i];
+		    n_colors = 2;
+		}
+		break;
+	    case 2:
+		if (irow[i] != bg && irow[i] != fg) {
+		    n_colors = 3;
+		} else {
+		    if (irow[i] == bg)
+			bg_count++;
+		    else if (irow[i] == fg)
+			fg_count++;
+		}
+		break;
+	    default:
+		break;
+	    }
+	}
+	if (n_colors > 2)
+	    break;
+	irow += vs->ds->linesize / sizeof(pixel_t);
+    }
+
+    if (n_colors > 1 && fg_count > bg_count) {
+	pixel_t tmp = fg;
+	fg = bg;
+	bg = tmp;
+    }
+
+    if (!*has_bg || *last_bg != bg) {
+	flags |= 0x02;
+	*has_bg = 1;
+	*last_bg = bg;
+    }
+
+    if (!*has_fg || *last_fg != fg) {
+	flags |= 0x04;
+	*has_fg = 1;
+	*last_fg = fg;
+    }
+
+    switch (n_colors) {
+    case 1:
+	n_data = 0;
+	break;
+    case 2:
+	flags |= 0x08;
+
+	irow = (pixel_t *)row;
+
+	for (j = 0; j < h; j++) {
+	    int min_x = -1;
+	    for (i = 0; i < w; i++) {
+		if (irow[i] == fg) {
+		    if (min_x == -1)
+			min_x = i;
+		} else if (min_x != -1) {
+		    hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+		    n_data += 2;
+		    n_subtiles++;
+		    min_x = -1;
+		}
+	    }
+	    if (min_x != -1) {
+		hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+		n_data += 2;
+		n_subtiles++;
+	    }
+	    irow += vs->ds->linesize / sizeof(pixel_t);
+	}
+	break;
+    case 3:
+	flags |= 0x18;
+
+	irow = (pixel_t *)row;
+
+	if (!*has_bg || *last_bg != bg)
+	    flags |= 0x02;
+
+	for (j = 0; j < h; j++) {
+	    int has_color = 0;
+	    int min_x = -1;
+	    pixel_t color = 0; /* shut up gcc */
+
+	    for (i = 0; i < w; i++) {
+		if (!has_color) {
+		    if (irow[i] == bg)
+			continue;
+		    color = irow[i];
+		    min_x = i;
+		    has_color = 1;
+		} else if (irow[i] != color) {
+		    has_color = 0;
+#ifdef GENERIC
+                    vnc_convert_pixel(vs, data + n_data, color);
+                    n_data += vs->pix_bpp;
+#else
+		    memcpy(data + n_data, &color, sizeof(color));
+                    n_data += sizeof(pixel_t);
+#endif
+		    hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+		    n_data += 2;
+		    n_subtiles++;
+
+		    min_x = -1;
+		    if (irow[i] != bg) {
+			color = irow[i];
+			min_x = i;
+			has_color = 1;
+		    }
+		}
+	    }
+	    if (has_color) {
+#ifdef GENERIC
+                vnc_convert_pixel(vs, data + n_data, color);
+                n_data += vs->pix_bpp;
+#else
+                memcpy(data + n_data, &color, sizeof(color));
+                n_data += sizeof(pixel_t);
+#endif
+		hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
+		n_data += 2;
+		n_subtiles++;
+	    }
+	    irow += vs->ds->linesize / sizeof(pixel_t);
+	}
+
+	/* A SubrectsColoured subtile invalidates the foreground color */
+	*has_fg = 0;
+	if (n_data > (w * h * sizeof(pixel_t))) {
+	    n_colors = 4;
+	    flags = 0x01;
+	    *has_bg = 0;
+
+	    /* we really don't have to invalidate either the bg or fg
+	       but we've lost the old values.  oh well. */
+	}
+    default:
+	break;
+    }
+
+    if (n_colors > 3) {
+	flags = 0x01;
+	*has_fg = 0;
+	*has_bg = 0;
+	n_colors = 4;
+    }
+
+    vnc_write_u8(vs, flags);
+    if (n_colors < 4) {
+	if (flags & 0x02)
+	    vs->write_pixels(vs, last_bg, sizeof(pixel_t));
+	if (flags & 0x04)
+	    vs->write_pixels(vs, last_fg, sizeof(pixel_t));
+	if (n_subtiles) {
+	    vnc_write_u8(vs, n_subtiles);
+	    vnc_write(vs, data, n_data);
+	}
+    } else {
+	for (j = 0; j < h; j++) {
+	    vs->write_pixels(vs, row, w * vs->depth);
+	    row += vs->ds->linesize;
+	}
+    }
+}
+
+#undef NAME
+#undef pixel_t
+#undef CONCAT_I
+#undef CONCAT
diff --git a/x86_64.ld b/x86_64.ld
new file mode 100644
index 0000000..878dafb
--- /dev/null
+++ b/x86_64.ld
@@ -0,0 +1,171 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
+OUTPUT_ARCH(i386:x86-64)
+ENTRY(_start)
+SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/lib64");
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  . = 0x60000000 + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .hash           : { *(.hash) }
+  .dynsym         : { *(.dynsym) }
+  .dynstr         : { *(.dynstr) }
+  .gnu.version    : { *(.gnu.version) }
+  .gnu.version_d  : { *(.gnu.version_d) }
+  .gnu.version_r  : { *(.gnu.version_r) }
+  .rel.init       : { *(.rel.init) }
+  .rela.init      : { *(.rela.init) }
+  .rel.text       : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+  .rela.text      : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+  .rel.fini       : { *(.rel.fini) }
+  .rela.fini      : { *(.rela.fini) }
+  .rel.rodata     : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+  .rela.rodata    : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+  .rel.data       : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+  .rela.data      : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+  .rel.tdata	  : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+  .rela.tdata	  : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+  .rel.tbss	  : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+  .rela.tbss	  : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+  .rel.ctors      : { *(.rel.ctors) }
+  .rela.ctors     : { *(.rela.ctors) }
+  .rel.dtors      : { *(.rel.dtors) }
+  .rela.dtors     : { *(.rela.dtors) }
+  .rel.got        : { *(.rel.got) }
+  .rela.got       : { *(.rela.got) }
+  .rel.bss        : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+  .rela.bss       : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+  .rel.plt        : { *(.rel.plt) }
+  .rela.plt       : { *(.rela.plt) }
+  .init           :
+  {
+    KEEP (*(.init))
+  } =0x90909090
+  .plt            : { *(.plt) }
+  .text           :
+  {
+    *(.text .stub .text.* .gnu.linkonce.t.*)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0x90909090
+  .fini           :
+  {
+    KEEP (*(.fini))
+  } =0x90909090
+  PROVIDE (__etext = .);
+  PROVIDE (_etext = .);
+  PROVIDE (etext = .);
+  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+  .rodata1        : { *(.rodata1) }
+  .eh_frame_hdr : { *(.eh_frame_hdr) }
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN (0x100000) - ((0x100000 - .) & (0x100000 - 1)); . = DATA_SEGMENT_ALIGN (0x100000, 0x1000);
+  /* Ensure the __preinit_array_start label is properly aligned.  We
+     could instead move the label definition inside the section, but
+     the linker would then create the section even if it turns out to
+     be empty, which isn't pretty.  */
+  . = ALIGN(64 / 8);
+  PROVIDE (__preinit_array_start = .);
+  .preinit_array     : { *(.preinit_array) }
+  PROVIDE (__preinit_array_end = .);
+  PROVIDE (__init_array_start = .);
+  .init_array     : { *(.init_array) }
+  PROVIDE (__init_array_end = .);
+  PROVIDE (__fini_array_start = .);
+  .fini_array     : { *(.fini_array) }
+  PROVIDE (__fini_array_end = .);
+  .data           :
+  {
+    *(.data .data.* .gnu.linkonce.d.*)
+    SORT(CONSTRUCTORS)
+  }
+  .data1          : { *(.data1) }
+  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table) }
+  .dynamic        : { *(.dynamic) }
+  .ctors          :
+  {
+    /* gcc uses crtbegin.o to find the start of
+       the constructors, so we make sure it is
+       first.  Because this is a wildcard, it
+       doesn't matter if the user does not
+       actually link against crtbegin.o; the
+       linker won't look for a file to match a
+       wildcard.  The wildcard also means that it
+       doesn't matter which directory crtbegin.o
+       is in.  */
+    KEEP (*crtbegin.o(.ctors))
+    /* We don't want to include the .ctor section from
+       from the crtend.o file until after the sorted ctors.
+       The .ctor section from the crtend file contains the
+       end of ctors marker and it must be last */
+    KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  .jcr            : { KEEP (*(.jcr)) }
+  .got            : { *(.got.plt) *(.got) }
+  _edata = .;
+  PROVIDE (edata = .);
+  __bss_start = .;
+  .bss            :
+  {
+   *(.dynbss)
+   *(.bss .bss.* .gnu.linkonce.b.*)
+   *(COMMON)
+   /* Align here to ensure that the .bss section occupies space up to
+      _end.  Align after .bss to ensure correct alignment even if the
+      .bss section disappears because there are no input sections.  */
+   . = ALIGN(64 / 8);
+  }
+  . = ALIGN(64 / 8);
+  _end = .;
+  PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+  /* DWARF debug sections.
+     Symbols in the DWARF debugging sections are relative to the beginning
+     of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+}